跳转至

会话与自动压缩

消息结构

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