20 min · roles 语义、聊天模板本质、多模态、位置效应、历史管理三模式
🎒 预备知识:完成上一课「L3」(零基础入口:第 0 课)
你发 messages=[...] 给 API,到模型真正"阅读"之间发生了什么:
provider 把你的数组用聊天模板拼成一段带特殊标记的纯文本(见下节),
加上采样参数(L3)发给模型,模型逐 token 生成直到停止。理解这条流水线的好处:
messages 数组的每个设计决定,最终都变成"模型看到的文本顺序和标记"——
顺序、角色、内容类型全部有后果。
| role | 语义 | 工程要点 |
|---|---|---|
| system | 任务设定与规则,优先级最高 | 是"强烈偏好"不是安全边界(A12);须稳定(缓存,A5) |
| user | 用户输入 | 不可信输入的第一来源(直接注入面,A12) |
| assistant | 模型的历史输出 | 含 tool_calls 声明时必须原样回填(L7 协议) |
| tool | 工具执行结果 | 第二个不可信来源(外部内容=间接注入面) |
协议红线:assistant 的 tool_calls 和对应 tool 结果必须成对,缺一边 API 直接 400; 且顺序敏感——tool 消息必须紧跟在带 tool_calls 的 assistant 之后,不能隔着别的消息。 也不要把工具结果塞进 user 消息偷懒:模型对 tool 角色有"这是事实数据"的先验, 对 user 有"这是指令"的先验,混用会悄悄改变模型对内容的解读方式 (把数据当指令读 = 注入面扩大)。
底层模型只会文本补全。chat API 是聊天模板把 messages 拼成带特殊标记的文本:
<|system|>你是助手<|end|>
<|user|>你好<|end|>
<|assistant|>
两个推论:① 模型是"被训练成"认这些标记的——如果你的用户输入里恰好包含类似标记的文本, 就可能越权变成"指令"(提示注入的技术根源,A12 的攻击剧本即此); ② 不同家模板不同——同一组 messages 换 provider 行为会漂移,跨 provider 迁移要重测。
{"role": "user", "content": [
{"type": "text", "text": "这张图里是什么?"},
{"type": "image_url", "image_url": {"url": "https://... 或 base64:data:image/png;base64,..."}}
]}
图像占 token 的粗略手感:一张图几百到一千多 token(按分辨率计)。 agent 里传图给模型判断("这张封面合规吗")完全可行;但批量传图烧钱快—— 能用代码判断的(尺寸/格式/清晰度)先代码判断,图只留给真正需要视觉理解的决策。
长上下文里模型对信息的检索能力不均匀:开头和结尾最"显眼",中段容易漏 ("Lost in the Middle",Liu et al. 2023)。工程对策三条:① 最重要的规则放开头(system) 并在末尾重申(Easel 的 TURN_REMINDER 就是这个原理,L05/A3 都会再遇到); ② 长文档的关键信息用标记/标题提高显著性;③ 真正关键的契约别埋在 50K 历史的中段。
messages 数组每轮怎么组装?三种基本模式,复杂度递增:
| 模式 | 做法 | 代价 | 适用 |
|---|---|---|---|
| 全量重发 | 历史全部保留,每轮原样发送 | 成本复利增长(L2),终会爆窗 | 短会话默认 |
| 滑动窗口 | 只保留最近 N 轮 | 早期约束静默丢失 | 闲聊类,约束少的场景 |
| 摘要+窗口 | 远期压成摘要条目 + 近期保原文 | 要管理压缩时机与边界 | 长任务 agent(OpenClaw compaction 的思路,A6/L18) |
注意滑动窗口的坑:用户第 2 轮说"以后都用中文回复",第 15 轮被滑出窗口——约束丢了。 对策:持久约束进 system 或长期记忆(A6),不靠历史轮次"活着"。
坑 1:手拼聊天模板。直接往文本里拼 "User:" "Assistant:"——绕过模板等于绕过训练分布。永远走 chat API。
坑 2:role 语义混用。系统规则写进 user、工具数据写进 user——模型先验全乱。
坑 3:关键契约埋中段。第 8 轮的约定到第 20 轮就忘——要么进 system,要么每轮末尾重申。
坑 4:tool 结果塞 user 角色。省事的代价是模型把数据当指令读。
① 说出四种 role 各自的"可信度地位"和对应的注入面;② 解释为什么绕过 chat API 手拼模板是坏主意; ③ 你的 agent 有一条"发布前必须人确认"的铁律,用户在第 3 轮说的、第 15 轮才执行发布—— 用本课和 L2 的知识给出保证它不丢的两层方案。发到对话里我批改。