动态 instructions:人设不是常量
对应 11.1.1 · 01_core_persona_tips.py · 能跑 ✓
from datetime import date
from agents import Agent, Runner, set_default_openai_api, set_tracing_disabled
from dotenv import load_dotenv
from pydantic import BaseModel, Field
# 仓库原文没有这三行,是为了接第三方模型加的
load_dotenv()
set_default_openai_api("chat_completions")
set_tracing_disabled(True)
class Answer(BaseModel):
"""机器可读的回答格式。"""
answer: str = Field(..., description="简洁的、给用户看的回答")
citations: list[str] = Field(default_factory=list)
confidence: float = Field(..., ge=0, le=1)
def core_instructions(ctx, agent) -> str:
# ↓ 每次运行时才算,所以「今天」永远是对的
today = date.today().isoformat()
return (
f"你是 SupportMentor,一个乐于助人的领域助手。今天是 {today}。\n"
"始终要:\n"
"1) 简洁作答;2) 优先使用检索到的上下文;3) 上下文里没有就说你不知道。\n"
"输出必须是符合 Answer schema 的合法 JSON。\n"
"绝不要:破例修改政策,或者编造事实。"
)
core_agent = Agent(
name="SupportMentor", instructions=core_instructions, output_type=Answer
)
input = "我们配件的退款窗口期是多久?"
result = Runner.run_sync(core_agent, input)
print(result.final_output)
instructions=core_instructions——传的是函数本身,不是调用它。
SDK 会在每次运行时调用它,现算出一段人设。
为什么重要?因为字符串是在 import 时就定死的。你要是写
instructions=f"今天是 {date.today()}",
这个「今天」就永远停在你启动服务那一天——
服务跑一个月,它还以为是一个月前。这是个真实的线上事故来源。
第五章 Reflexion 那节用过同一个机制(把上一轮的批评拼进人设), 这里给出了它更日常的用途:任何随时间变化的东西,都必须动态注入。
身份 → 始终要(编号列表)→ 绝不要。「绝不要」单独成段, 而且只放最关键的两条(不破例、不编造)。
对比第二章那个随手写的「你是一个研究规划助手」——这才是生产级人设该有的样子。
另外注意 confidence: float = Field(..., ge=0, le=1) 的
ge/le 约束:不光要模型给个数,还要在 schema 层面卡死范围。