模型看到什么#
“上下文”就是随一次请求发送出去的消息列表。理解它就能解释大多数令人意外的行为:为什么某个轮次被遗忘了、压缩为什么改变了提示词,以及为什么 UI 里的记录会比模型收到的内容更长。
消息角色#
角色 |
含义 |
|---|---|
system |
应用指令,每次请求都会重新构建。 |
user |
用户文本,或按顺序排列的文本和图像。 |
assistant |
模型的回答、它的推理,以及它请求的所有工具调用。 |
tool |
通过 |
agent |
internal continuation;模型提供商会把它当作用户输入。 |
系统指令每次都会重建#
历史记录扩展恢复的是对话,不是提示词。指令会在每次请求时重新添加,因此修改系统提示词或工具会在下一个轮次生效,不会让过期指令越堆越多。
这也是 extensions=[] 会丢掉对话的原因:没有扩展恢复消息。参见保存并恢复对话。
记录与模型输入#
压缩之后两者会分岔,而两者都有用:
raw log 1 2 3 4 5 6 7 8 what the user saw
└──── checkpoint ────┘
model input [checkpoint] 6 7 8 what the model receives
在 UI 中展示原始日志:
await storage.list_raw_messages(session_id)。发送给模型的内容:最新的检查点,加上它之后的原始消息。
检查点会记录它覆盖到的最后一条原始消息,因此恢复从该位置之后开始,已摘要的消息不会被重复发送。
把模型上下文和展示用的历史记录分开#
你可以独立决定持久化哪些内容、回传给模型哪些内容。Message.persist=False 请求持久化订阅者不要存储某条消息,但不会把这条消息从当前请求中隐藏。include_in_messages=False 让某条消息不进入之后恢复的上下文,但不一定把它排除在原始记录之外。应用数据请放在消息属性里,不要把私有的路由信息写进提示词文本。
不要用拼接可见聊天气泡的方式重建模型提供商的上下文。即使 UI 把它们隐藏起来,工具调用和对应的工具结果依然是对话的一部分。恢复出来的上下文必须保留它们的 ID 和结构;持久化扩展会替你处理这种投影。参见保存并恢复对话。
上下文不够用时#
CompactionExtension 会监视请求的大小,一旦超过阈值,就把较早的轮次替换成一条摘要。
最近的轮次会完整保留,包括其中的工具调用和工具结果。
当前轮次绝不会被拆开,所以单个过大的轮次会原样保留。
如果摘要并不比它替换掉的内容更小,那就什么都不改。
用 max_tokens 和 keep_recent_tokens 配置它,并观察 COMPACTION_* 事件。参见压缩而不丢失历史记录。