可信 Agent 怎么写:微软 AI Agent 入门课第 6 课的系统消息框架
写 agent 的人多半都碰到过这个场景:三个人各写了一版 system message,谁也说不清哪版更好,改一个字要重新读三百行提示词,新加一个 agent 又得从零抄一遍。ai-agents-for-beginners 第 6 课 06-building-trustworthy-agents/README.md 里,最能直接搬走的不是后面那张威胁清单,而是开头 Safety 一节的 Building a System Message Framework——它给的是一套「用 LLM 批量生产 system message」的流程,配套代码在 06-building-trustworthy-agents/code_samples/06-system-message-framework.ipynb。
这篇就沿着这个 README 小节和那个 notebook 走一遍,看清每一格在改什么。
框架的四步:把提示词当产物,而不是当手稿
README 把这个框架拆成四步(Step 1 到 Step 4),核心是把「写提示词」变成「用一段元提示词去生成提示词」。
Step 1 是 meta system message。 README 给的原文是这样一段 plaintext:
You are an expert at creating AI agent assistants.
You will be provided a company name, role, responsibilities and other
information that you will use to provide a system prompt for.
To create the system prompt, be descriptive as possible and provide a structure that a system using an LLM can better understand the role and responsibilities of the AI assistant.
注意它的输入契约写得很死:company name、role、responsibilities。这三样后面会原样变成 notebook 里的三个变量,不是随口举例。
Step 2 是 basic prompt,也就是你手写的那一句粗描述。README 的示例是一个 Contoso Travel 的订票 agent,把 lookup available flights、book flights、ask for preferences、cancel、alert 这些任务平铺在一句话里。
Step 3 把前两者拼起来发给 LLM:meta system message 当 system,basic prompt 当输入。README 直接贴出了产出示例,这一段才是「结构化写法」的正题——它产出的不是一段散文,而是一份带固定骨架的文档:Company Name、Role、Objective、Key Responsibilities(下面再按 Flight Lookup / Flight Booking / Customer Preference Inquiry / Flight Cancellation / Flight Monitoring 分条,每条各带若干行动作描述)、Tone and Style、User Interaction Instructions、Additional Notes。
这个骨架的价值在于它可 diff。散文式提示词改一个词,你只能整段重读;这种分节结构改动落在哪一节一目了然,多个 agent 之间还能按节比对。
Step 4 是迭代。README 自述这套框架的价值在于既方便批量生成,也方便随时间改进,并且明说「很少有一版 system message 第一次就适配完整用例」,做法是改 basic prompt 再跑一遍去对比评估。请留意:这一步在仓库里是人工动作,没有配套代码。
notebook 逐格:请求是怎么发出去的
06-system-message-framework.ipynb 只有四个 code cell,没有 markdown 说明格。
第一格是环境。它 load_dotenv(),然后读两个变量:
endpoint = os.environ["AZURE_OPENAI_ENDPOINT"]
deployment = os.environ["AZURE_OPENAI_DEPLOYMENT"]
这一格的注释值得单独看一眼,原文写明这个示例走 Azure OpenAI 的 Responses API(稳定的 /openai/v1/ 端点),并注明 GitHub Models 已弃用(retiring July 2026)且不支持 Responses API,所以改为直接调 Azure OpenAI。00-course-setup/README.md 里也有对应说明:第 6 课和第 8 课的部分 notebook 之前用的是 GitHub Models。如果你手上还留着老版本的 GitHub Models 写法,这一课是已经换过路线的,别照着旧笔记配。
第二格是鉴权,走的是无密钥路线:
token_provider = get_bearer_token_provider(
DefaultAzureCredential(),
"https://cognitiveservices.azure.com/.default",
)
client = OpenAI(
base_url=f"{endpoint.rstrip('/')}/openai/v1/",
api_key=token_provider,
)
这里有两处容易踩:一是它把 get_bearer_token_provider 返回的东西直接放进 api_key;二是这一格完全没有读 AZURE_OPENAI_API_KEY 的分支。.env.example 里 AZURE_OPENAI_API_KEY 是有的,但 00-course-setup/README.md 的变量表把它标为 Optional——只在你用 key 方式而非 az login / Entra ID 时才需要。也就是说:光在 .env 里填了 key,这个 notebook 的代码路径也不会去用它,仓库文档给的前置动作是先跑 az login。
第三格就是那三个变量,与 meta 提示词的输入契约一一对应:
role = "travel agent"
company = "contoso travel"
responsibility = "booking flights"
第四格发请求,meta 提示词进 system,三个变量拼进 user:
response = client.responses.create(
model=deployment,
input=[
{"role": "system", "content": """..."""},
{"role": "user", "content": f"You are {role} at {company} that is responsible for {responsibility}."},
],
# Optional parameters
temperature=1.0,
max_output_tokens=1000,
top_p=1.0,
store=False,
)
print(response.output_text)
这里有个和 README 对不上的地方,值得知道:README 的 Step 2 basic prompt 是一整句带任务枚举的描述,而 notebook 把它压成了模板句 You are {role} at {company} that is responsible for {responsibility}.,任务枚举没了。两处不一致本身不影响你用,但如果你照 notebook 的模板句去生成,输入信息量比 README 示例少一截,产出自然也会更薄——想复现 README 那份骨架,得把 responsibilities 写细。
那几个「Optional parameters」不是随手填的
那一格里 # Optional parameters 下面的几个参数,写的都是仓库示例里的取值,但它们各有出处,改之前最好知道:
store=False。仓库.agents/skills/azure-openai-to-responses/references/cheat-sheet.md里写明,用previous_response_id做多轮串联需要store=True(默认),会话会存在服务端;手动维护input数组这条路则不需要服务端存储,配store=False。放在「可信 Agent」这一课里,这就是一个明摆着的数据边界选择。max_output_tokens。同一份 cheat-sheet 与references/troubleshooting.md都写明:reasoning 模型的内部 reasoning token 也会计入这个上限,按老习惯设一个很小的值,症状是output_text空掉或者输出被截断。notebook 里那个具体取值只是仓库示例里的写法,别当成推荐配置抄走。temperature=1.0。同一目录下的SKILL.md与references/troubleshooting.md写明:GPT-5 与 o 系列模型的temperature必须省略或设为 1,给别的值会报参数错误。而.env.example里AZURE_OPENAI_DEPLOYMENT的示例值恰好是gpt-5-mini。所以这个 1.0 是跟着模型走的,不是随手写的中间值——照着老习惯调低去「让输出更稳定」,按仓库自己的排错表就是一条报错。top_p=1.0。这一条要和上一条一起看:SKILL.md的 O-series 一节写明top_p在 o 系列模型上不受支持、应当移除。notebook 把它显式写在了参数里,所以你要是把 deployment 换成 o 系列,按仓库文档这一行就该删掉,而不是照抄。两处说法我们只并列,不替作者解释它为什么留在示例里。
以上参数含义均来自仓库文档,未经实测,以仓库最新代码为准。另外 troubleshooting.md 还提到 output_text 是 SDK 的便捷属性,直接打 REST 的话原始 JSON 里没有这个字段,文本在 output[0].content[0].text——你要把生成的 system message 落盘做版本管理时会用到这一点。
「校验」这一环,仓库里在哪
按这一课的名字,你可能期待 notebook 里有一格拿产出去做安全检查。没有。 这个 notebook 的最后一个动作就是 print(response.output_text),既没有断言,也没有对生成的 system message 做过滤或评分。README 威胁一节讲的那些缓解手段(输入校验与过滤、限制对话轮数、按需最小授权、在 Docker 容器这类受限环境里执行、失败回退与重试)在这个 notebook 里都没有对应代码。
真正带判定逻辑的是同一课的另一个 notebook code_samples/06-human-in-the-loop.ipynb:它自己的 markdown 说明写明 README 那段 APPROVE / REJECT 片段属于 post-action(agent 已经跑完才问人),而它实现的是 pre-action gate,代码里有 classify_risk() 做低/中/高三档分级、gate_action() 做闸口、log_decision() 把每次判定按 JSONL 追加成审计日志。它的 classify_risk 注释还明说未识别的动作默认落到 medium 而不是 low。这两个 notebook 的分工要分清:一个负责产出提示词,一个负责运行期的人工闸口。
另一层「校验」是 notebook 本身能不能跑通,这一层仓库是有工具的:scripts/validate-notebooks.ps1,说明在 .agents/skills/testing-course-samples/SKILL.md。它用 nbconvert 把每个 Python notebook 无头执行一遍,打出 PASS/FAIL 矩阵,并把执行后的副本、逐 notebook 日志和 results.json 写到输出目录。
pwsh scripts/validate-notebooks.ps1 -List
pwsh scripts/validate-notebooks.ps1 -Filter '08-*' -Timeout 600
上面两条是仓库文档里的原文示例,未经实测;-Filter 的语义按文档是对 notebook 的仓库相对路径做通配,换成本课的目录前缀即可,具体以仓库最新内容为准。
对 Windows 用户有两处仓库明写的细节:默认输出目录写作 $env:TEMP\aiab-nbval;-Python 参数的文档说明就是给「python 不在 PATH(例如 Windows Store 别名)」这种情况用的。另外文档提到,人工介入那格挂住时会以 StdinNotImplementedError 的形式暴露,而不是一直卡着,这靠的是按格计时的 -Timeout(文档写的默认值是 300 秒,这是仓库当前文档里的默认值,随版本可能变动)。
前置条件方面,00-course-setup/README.md 要求 Python 3.12+,虚拟环境激活在 zsh/bash 下是 source venv/bin/activate,Windows 命令提示符下是 venv\Scripts\activate;跑校验脚本还要额外装 nbconvert 与 ipykernel;.env 至少要有 AZURE_OPENAI_ENDPOINT 与 AZURE_OPENAI_DEPLOYMENT,并先完成 az login。
拿走之后怎么用
如果只搬一件事,我会搬 Step 1 那段 meta system message 和它的三变量契约:把它存成仓库里的一个文件,把 role / company / responsibility 做成一张表,产出的分节文档按 agent 落成独立文件进版本库。这样每次调整都变成一次可 diff 的改动,而不是又一版谁也说不清差在哪的提示词。
至于校验,仓库给的现状要认清:生成端没有自动校验,评估靠 Step 4 的人工迭代;运行期的把关代码在人工介入那个 notebook 里;notebook 能否跑通则交给那个 PowerShell 脚本。这三层是分开的,别指望其中一个覆盖另外两个。这套课程持续更新,上面涉及的文件路径、变量名与参数写法都可能随版本变动,动手前请以仓库最新内容为准。
本文依据 github.com/microsoft/ai-agents-for-beginners 仓库于 2026-08-18 的公开内容整理,
事实来自仓库内的课程正文与代码示例。我们没有跑过文中涉及的代码,
因此不涉及运行结果、耗时与 Agent 实际表现的任何描述。
该课程持续更新,文中涉及的文件路径、依赖与接口写法随版本变动,请以仓库最新内容为准。
文中涉及的云端服务调用会产生费用并可能上传数据,请自行评估密钥与数据边界。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。