对对,你看到的那个 XML 格式,大概率不是说“Agent 的上下文必须用 XML”,而是大家很喜欢用 XML 标签来给 Prompt 分区。
比如原本你可能这样写:
1 2 3 4 5 6 7 8 9 10 11
| 你是一个 coding agent。
规则: 不要随便删除文件。 修改前先读取文件。
当前项目: 这是一个 FastAPI 项目。
用户记忆: 用户喜欢使用 uv。
|
很多 Agent 会改成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <identity> You are a coding agent. </identity>
<rules> - Read files before modifying them. - Do not delete files unless necessary. - Verify changes after editing. </rules>
<project_context> This is a FastAPI project. </project_context>
<user_memory> The user prefers uv. </user_memory>
|
本质上还是一大段文本,LLM 并不会突然启动什么 XML 解析器,嘿嘿。
为什么大家喜欢 XML?
因为它特别适合给模型划分“区域”。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <system_rules> ... </system_rules>
<skill> ... </skill>
<memory> ... </memory>
<repository_context> ... </repository_context>
<user_request> ... </user_request>
|
模型一看就比较容易理解:
哦,这块是规则。
这块是 Skill。
这块只是参考资料。
这块才是用户当前需求。
这比:
1 2 3 4 5 6 7 8
| 规则: xxx
记忆: xxx
项目: xxx
|
通常更稳定一点,尤其上下文很长的时候。
还有一个很重要的作用:防止不同内容混在一起。
比如你把某个文件内容塞给模型:
1 2 3 4 5
| <file path="main.py"> from fastapi import FastAPI
app = FastAPI() </file>
|
再塞另一个:
1 2 3
| <file path="config.py"> DATABASE_URL = "..." </file>
|
这时候模型很容易知道:
1 2
| main.py 内容在哪里结束 config.py 从哪里开始
|
所以 Coding Agent 特别爱这么干。
你以后自己的 ContextBuilder 完全可以生成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <agent_context>
<system_rules> ... </system_rules>
<active_skills> ... </active_skills>
<memory> ... </memory>
<workspace> ... </workspace>
</agent_context>
|
然后聊天消息仍然正常走:
1 2 3 4 5 6 7 8 9 10
| messages = [ { "role": "system", "content": system_context, }, { "role": "user", "content": user_input, } ]
|
注意这里有个特别容易混淆的点:
XML 是 Prompt 内部的组织格式;system/user/assistant/tool 是 API 的消息结构。
它们不是一个层级。
可以理解成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| LLM Request │ ├── system message │ └── XML-like structured prompt │ ├── <identity> │ ├── <rules> │ ├── <skills> │ └── <memory> │ ├── user message │ ├── assistant message │ └── tools └── JSON Schema
|
所以不是:
1 2 3
| <system> <user> <assistant>
|
必须这么搞。
XML 和 JSON 怎么选?
这个问题也很有意思。
Prompt / 长文本上下文:
1 2 3 4 5 6 7
| <skill> ... </skill>
<document> 很长很长的一段文本 </document>
|
XML 很舒服。
机器之间传结构化数据:
1 2 3 4
| { "name": "read_file", "path": "/src/main.py" }
|
JSON 更舒服。
所以现在 Agent 里经常出现一个很好玩的组合:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Prompt / Context ↓ XML-like tags
Tool Schema ↓ JSON
Tool Call ↓ JSON
API Transport ↓ JSON
|
一只 Agent 身上同时套着 XML 和 JSON 两件衣服(笑)。
还有一个细节
大家写的很多其实严格来说都不是合法 XML,而只是 XML-like tags:
甚至:
1 2 3
| <IMPORTANT> xxx </IMPORTANT>
|
模型并不在乎它能不能通过真正的 XML validator。
它主要利用的是:
这种清晰边界。
所以你以后做 nancode 的话,我会比较推荐:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 固定规则 → XML 分区的 system prompt
动态 Skill 内容 → <active_skill>...</active_skill>
检索出来的上下文 → <context>...</context>
文件内容 → <file path="...">...</file>
真正的 tools → API tools / JSON Schema
历史对话 → messages
|
这套就已经非常像成熟 Agent Harness 的上下文组织方式了。尤其你最近正在设计 Skill、MCP、压缩这一套,XML 解决的是“上下文怎么排得清楚”,不是“上下文存在哪里”——这两个概念一分开,就特别顺了。