会话与自动压缩¶
消息结构¶
Conversation.messages 直接保存 OpenAI 官方类型字典(ChatCompletionMessageParam),角色为 system / user / assistant / tool,不额外包装:
conv = agent.conversation
conv.messages[0] # {"role": "system", "content": "..."}
| 方法 | 说明 |
|---|---|
set_system_message_content(content) |
替换或插入第 0 条系统消息(agent.system() 的落点) |
add_user_message(content, images=None) |
有图片时 content 变成 text/image_url 分块列表,PIL 图像转 data URL |
append_user_message(extra) |
把文本追加到最后一条用户消息上(结构化输出的 schema 就是这样注入的) |
add_agent_message(msg) |
写入 assistant 消息,自动剔除空的 tool_calls: [] |
add_tool_result(tool_call_id, result) |
写入 role="tool" 消息,内容为 result.value_str() |
pop_last_message_if_user() |
取消时回退最后一条用户消息 |
pop_from_last_user_message(inclusive) |
/retry(不含)与 /revise(含)的实现 |
clear() |
清空但保留系统消息,并重置 token、压缩计数与被压缩工具结果的原文 |
to_history() / content_to_html() / render_history_as_html() |
界面渲染与 /render 导出 |
dumps() / dump(path) / loads() / load(path) |
序列化,落盘键 tokens_used 为历史兼容保留 |
工具调用保存在 assistant 消息的 tool_calls 数组里(含参数字符串),工具结果单独作为 role="tool" 消息按 tool_call_id 关联。推理内容按 model.reasoning_field 自动探测(reasoning 或 reasoning_content)并原样回写,以便多轮保持。
两种长度度量¶
| 度量 | 来源 | 用途 |
|---|---|---|
total_tokens |
模型返回的 usage.total_tokens |
自动压缩的唯一判据(压缩后重置为 None,避免立刻再触发) |
estimated_message_length() |
对 content / text / reasoning / reasoning_content 字段做字符数累加(递归进入嵌套 dict/list) |
估算回收比例,并据此决定「继续廉价回收」还是「升级为摘要」 |
分级压缩¶
flowchart TD
S["每轮调用前检查"] --> A["回收旧工具结果"]
A --> B{"仍超标?"}
B -->|否| E["本轮结束"]
B -->|是| C["摘要压缩"]
C --> D{"仍超标?"}
D -->|否| E
D -->|是| A
第一级:回收旧工具结果¶
超过 keep_max(默认 12)条的旧 role="tool" 消息被就地替换成占位符:
[Compacted, ID: <toolcall_id>. If this content is still needed, call extract_compacted_tool_result with this ID or re-run the tool.]
原文仍留在内存里,模型可用 extract_compacted_tool_result(toolcall_id) 取回——这是「先廉价回收、不丢信息」的关键。只在替换后确实更短时才替换。
第二级:摘要压缩¶
切点选择保证尾部不以孤立的工具结果开头,且必要时把最后一条用户消息强行保留在尾部(部分服务端要求请求中必须存在用户消息)。摘要么由一个子智能体产出:
Agent.inherit(agent, share_display=False, copy_toolbox=False, copy_command=False)
# 关闭 auto_compact / enable_extensions,开启 auto_confirm,复制当前会话作为上下文
# execute(max_iterations=1)
摘要要求以 markdown 分节输出(system_context、overview、key_facts、user_preferences、decisions、pending_tasks、open_questions、tone_context),总量控制在 1024 token 内。成功后系统消息被替换为「摘要 + 压缩说明」,total_tokens 置空,compaction_counter.summary_rounds 加一(工具轮次计数归零)。
无内容可压缩或摘要失败时,会话保持原样,返回状态分别为 NOTHING_TO_CONDENSE / SUMMARIZE_FAILED。
参数一览¶
策略参数是 AutoCompactor 的字段(代码层),开关与阈值是配置(用户层):
| 位置 | 名称 | 默认 | 说明 |
|---|---|---|---|
AutoCompactor |
toolcall_keep_max |
12 | 保留最近多少条工具结果 |
AutoCompactor |
summary_keep_max |
16 | 摘要时保留的尾部消息数 |
AutoCompactor |
escalation_rounds |
2 | 连续多少轮廉价回收后升级为摘要;回收后估算仍超过阈值 × ESCALATION_RATIO 时当轮立即升级 |
AutoCompactor |
max_retries |
2 | 摘要后仍超标时允许的额外压缩次数;每再压一轮,toolcall_keep_max 与 summary_keep_max 各自减半 |
| 模块常量 | ESCALATION_RATIO |
0.95 | 估算值需低于阈值的 95% 才算有效进展;它是 compact 模块级常量,不能按 AutoCompactor 实例调整 |
| 配置 | auto_compact.enabled |
true |
开关 |
| 配置 | auto_compact.token_threshold |
192000 | 触发阈值 |
自定义策略只需继承 CompactorAbstract 并实现 auto_compact(agent),然后 Agent(compactor=MyCompactor())。
手动触发¶
| 方式 | 行为 |
|---|---|
/compact |
只做第二级摘要压缩(等价于 compact_conversation) |
/compact toolcall |
只做第一级回收 |
agent.conversation.compact_toolcall(keep_max=12) |
编程调用第一级(Conversation 的方法),返回 ToolCallCompactResult(reclaimed_count, reclaimed_fraction) |
xun.compact.compact_conversation(agent, keep_recent=16) |
编程调用第二级(模块函数,未在顶层导出),返回 SummaryCompactResult |