结构化摘要:从 6 字段到 10 字段的信息保全术
专栏信息
《从零到一构建跨平台 AI 助手:WeClaw 实战指南》专栏
本文是模块八第 6 篇,讲解摘要模板设计与信息保留策略。
作者与项目
作者简介:翁勇刚 WENG YONGGANG 新概念龙虾-WeClaw 开发团队负责人,一群专注于跨平台 AI 应用的实践者 理念:"再复杂的技术,也能用代码讲清楚"
- 项目地址:https://github.com/wyg5208/weclaw.git
- 官网地址:https://weclaw.link
- 作者 CSDN:https://blog.csdn.net/yweng18
摘要
本文结构概览: 本文从"摘要丢失关键信息"的痛点出发,对比 6 字段和 10 字段模板的差异,详解每个字段的设计意图,重点讲解双语约束前缀和 System Prompt 注解的双重保险机制,最后介绍 focus_topic 引导式压缩。
背景:上下文压缩的核心是"用更少的 token 保留更多的信息"。摘要模板的字段设计直接决定了压缩后模型能否"记住"之前做了什么、用户还需要什么。
核心问题:如何设计一个摘要模板,既能被 LLM 正确理解,又能在压缩后保持对话连贯性?
解决方案:10 字段结构化摘要 + 双语约束前缀 + SP 注解 + focus_topic 引导
关键成果:
- 压缩后模型不再重复已完成的工作
- 用户最新请求被精确保留("活跃任务"字段)
- 摘要中的提问不会被模型误执行
适合读者:LLM Agent 开发者,关注摘要质量和对话连贯性
阅读时长:约 10 分钟
关键词:结构化摘要、Prompt Engineering、上下文压缩、信息保留、双语约束
一、摘要丢失了什么?
1.1 一个典型的失败场景
[压缩前] 47 条消息,包含:
- 用户让 AI 分析半导体板块
- AI 调用 stock_query 查询了 3 只股票
- 讨论了量化策略的参数调整
- 用户最后说"帮我生成一份投资报告"
[压缩后] 旧版 6 字段摘要:
"""
用户目标: 分析半导体板块走势
已完成操作: 查询了3只股票的行情
当前状态: 讨论量化策略中
关键决策: 采用均线交叉策略
待处理事项: 生成投资报告
关键上下文: 讨论了A股和港股
"""
问题:摘要中提到了"生成投资报告",但没有保留用户的原始请求文本。模型可能知道"要生成报告",但不知道用户的具体要求(格式、内容范围、重点等)。
1.2 6 字段 vs 10 字段
| 字段 | 6 字段模板 | 10 字段模板 | 差异 |
|---|---|---|---|
| 用户目标 | 有 | 有 | - |
| 已完成操作 | 有 | 有 | - |
| 当前状态 | 有 | 有 | - |
| 关键决策 | 有 | 有 | - |
| 待处理事项 | 有 | 有 | - |
| 关键上下文 | 有 | 有 | - |
| 活跃任务 | 无 | 有 | 保留用户最后请求原文 |
| 已解答问题 | 无 | 有 | 防止模型重复回答 |
| 待回应请求 | 无 | 有 | 明确列出未完成项 |
| 相关文件 | 无 | 有 | 保留操作过的文件路径 |
二、10 字段模板设计
[图片: 10 字段模板结构图 | 生成方式: 文生图 PROMPT: "A structured template card with 10 labeled fields for LLM conversation summary, arranged in a clean card UI layout, with 'Active Task' field highlighted in gold at the top, other fields in white/light gray, each field showing its name and a brief one-line description, light theme, professional design"]
2.1 模板结构
SUMMARY_TEMPLATE = """
## Conversation Summary
### User Goal
{user_goal}
### Completed Operations
{completed_ops}
### Current Status
{current_status}
### Key Decisions
{key_decisions}
### Pending Items
{pending_items}
### Key Context
{key_context}
### Active Task (IMPORTANT - user's most recent request)
{active_task}
### Answered Questions (DO NOT re-answer these)
{answered_questions}
### Unresponded Requests
{unresponded_requests}
### Related Files
{related_files}
"""
2.2 为什么"活跃任务"最重要
"活跃任务"字段保留了用户最后一条消息的原文,这是模型在压缩后恢复对话连贯性的关键:
# 活跃任务示例
active_task = """
用户最新请求(原文):
"帮我生成一份关于半导体板块的投资报告,要求:
1. 包含中芯国际、北方华创、韦尔股份三家公司的对比分析
2. 使用 Markdown 格式
3. 重点分析 PE/PB 估值水平
4. 给出明确的买入/持有/卖出建议"
"""
如果没有这个字段,摘要中可能只保留了"生成投资报告"这个概括性描述,模型不知道具体要求,生成的报告可能不符合用户期望。
2.3 "已解答问题"防止重复
answered_questions = """
- "中芯国际的 PE 是多少?" → 已回答: PE(TTM) 约 35.2
- "北方华创的营收增速如何?" → 已回答: 2025Q3 同比增长 28%
- "半导体板块整体估值水平?" → 已回答: 板块平均 PE 约 45x
"""
有了这个字段,模型不会在压缩后重新去查询已经查过的数据。
三、双语约束前缀:防止摘要被"误执行"
3.1 问题:摘要中的提问
当摘要包含用户之前提出的问题(如"中芯国际的 PE 是多少?"),模型可能会把这些问题当成新的请求去回答:
[压缩后的对话]
System: [摘要] ... 已解答问题: "中芯国际的 PE 是多少?" ...
User: 好的,继续
AI: 让我查询一下中芯国际的 PE...(又去查了一遍!)
3.2 双语约束前缀
在摘要开头注入一段双语约束指令:
CONSTRAINT_PREFIX = """[IMPORTANT INSTRUCTIONS FOR THE MODEL]
The following is a SUMMARY of previous conversation history.
- Do NOT answer questions mentioned in this summary — they have already been answered.
- Do NOT re-execute operations mentioned in this summary — they have already been completed.
- Treat this as background context, NOT as active instructions.
[重要提示]
以下是之前对话的摘要。
- 不要回答摘要中提到的问题——它们已经被回答过了。
- 不要重新执行摘要中提到的操作——它们已经完成了。
- 将此视为背景上下文,而非当前指令。
[/IMPORTANT INSTRUCTIONS]
"""
3.3 为什么需要双语?
[图片: 双语约束效果对比 | 生成方式: 文生图 PROMPT: "Split panel comparison showing AI assistant behavior: Left panel shows AI re-answering old questions from summary (highlighted in red as incorrect behavior), Right panel shows AI correctly treating summary as context only and responding to new user request (highlighted in green as correct behavior), clean UI mockup style"]
| 语言 | 原因 |
|---|---|
| 英文 | 大多数 LLM 对英文指令的遵循度更高 |
| 中文 | 用户的原始对话语言,确保中文模型也能理解 |
双语覆盖确保了无论模型使用什么语言进行推理,都能正确理解约束。
四、System Prompt 注解:双重保险
4.1 为什么需要双重保险
摘要中的约束前缀可能被模型的推理过程"淹没"——尤其在长上下文中,模型对早期 system 指令的注意力可能衰减。
4.2 SP 注解机制
当压缩发生时,自动在 System Prompt 末尾追加一段简短的注解:
def inject_compression_annotation(self, system_prompt, compression_info):
"""在 System Prompt 中注入压缩注解"""
annotation = f"""
[CONTEXT COMPRESSION NOTICE]
The conversation history has been compressed. A summary of earlier messages
is provided above. Focus on the user's most recent request and continue
from where the conversation left off.
Compression timestamp: {compression_info['timestamp']}
Messages compressed: {compression_info['count']} → 1 summary
[/CONTEXT COMPRESSION NOTICE]"""
return system_prompt + annotation
4.3 双重保险的效果
System Prompt 末尾: [压缩通知] 历史已压缩,聚焦最新请求
摘要开头: [约束指令] 不要重新回答已解答的问题
摘要内容: [10 字段结构化信息]
两层约束确保模型无论从哪个角度处理上下文,都能意识到"这是一段摘要"。
五、Focus Topic 引导式压缩
5.1 场景:用户主动触发压缩
用户: /compress 量化交易
意图: 压缩上下文,但优先保留与"量化交易"相关的信息
5.2 focus_topic 如何影响摘要
def build_summary_prompt(self, messages, focus_topic=None):
"""构建摘要生成的 prompt"""
base_prompt = SUMMARY_TEMPLATE
if focus_topic:
focus_instruction = f"""
IMPORTANT: The user has requested compression with focus on: "{focus_topic}"
Prioritize preserving information related to this topic.
Other information can be more aggressively summarized.
"""
base_prompt = focus_instruction + base_prompt
return base_prompt
5.3 效果对比
无 focus_topic:
"用户讨论了半导体分析和量化策略。已查询了3只股票行情。"
有 focus_topic="量化交易":
"用户正在构建量化交易策略:
- 采用均线交叉信号(MA5/MA20)
- 回测周期:2024-01 至 2025-06
- 当前夏普比率:1.85
- 待优化:止损参数和仓位管理
- 半导体分析作为选股候选池的一部分被讨论"
focus_topic 引导模型在压缩时优先保留相关细节,其他内容可以更激进地摘要。
六、摘要质量评估
6.1 评估指标
| 指标 | 定义 | 如何测试 |
|---|---|---|
| 活跃任务保留 | 用户最后请求是否被完整保留 | 检查摘要中 active_task 字段 |
| 不重复执行 | 模型是否重复已完成的工具调用 | 压缩后发送"继续",观察行为 |
| 文件路径保留 | 操作过的文件路径是否保留 | 检查 related_files 字段 |
| 配对完整性 | tool_call/tool_result 配对是否完整 | 检查消息结构验证 |
6.2 自动化测试
@pytest.mark.asyncio
async def test_summary_preserves_active_task():
"""摘要应保留用户最新请求"""
messages = build_test_conversation() # 47 条消息
summary = await engine.compress(messages)
# 活跃任务应包含用户最后请求的关键词
assert "投资报告" in summary
assert "PE/PB 估值" in summary
@pytest.mark.asyncio
async def test_no_duplicate_execution():
"""压缩后模型不应重复执行已完成操作"""
messages = build_test_conversation()
compressed = await engine.compress(messages)
# 模拟压缩后的对话
follow_up = [{"role": "user", "content": "好的,继续"}]
response = await model.chat(compressed + follow_up)
# 模型不应再次调用 stock_query(已查询过的股票)
assert not any("stock_query" in str(tc) for tc in response.tool_calls)
七、总结与展望
7.1 核心要点回顾
- "活跃任务"是摘要的灵魂:保留用户最新请求的原文,防止模型"失忆"
- 双语约束前缀是防线:防止模型把摘要中的提问当成新请求
- SP 注解是双重保险:在 System Prompt 层面强化压缩感知
- Focus topic 引导精度:用户主动指定主题时,优先保留相关信息
下期预告:《Tool 配对完整性:ReAct Agent 最隐蔽的 Bug》
- 一个在 7 个 ReAct 步骤中反复出现的"缺失结果"警告
- 占位消息累积泄漏的根因分析
- 原子批量写入和孤儿剥离策略
敬请期待!
版权声明:本文为 CSDN 博主「翁勇刚」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。