SKILL.md 技能文档
能力目标
产出一份可重复、可断言、可换数据复用的回归账本:这次压缩把哪些关键信息丢了、模型是否真的因此答不出来、丢掉的内容还能不能从无损原文里找回、压缩比是多少。压缩的副作用是静默的——不报错、不崩溃,只是悄悄忘掉你以为它记得的事,没有这份检查你永远不知道它在第几轮弄丢了哪条关键约定。
前置
- 已能配好摘要中间件并真触发一次压缩(见 configuring-conversation-compaction)。
- 一个决定成败的事实:这里的摘要中间件是非破坏式的——它在包裹模型调用时工作,不删除状态里的任何一条原始消息,压缩只记录在私有的压缩事件里。所以调用返回的消息列表永远是全量历史。选错观测位点,你会永远得出「没有衰减」的错误结论。
实操流程
造长对话时把关键词按「偶发的一次性旁注」方式嵌入,每个只出现一次,并且主线对话聚焦在一个完全不同的真实任务上:
KEYWORDS = ["DeltaChannel", "HarnessProfile", "CompositeBackend", "幽灵词汇衰减", "REPLMiddleware"] keyword_mentions = { "DeltaChannel": "(顺便一提,早期原型里用过 DeltaChannel 做事件路由,后来换掉了)", "HarnessProfile": "(HarnessProfile 的配置上周已经初始化好了,不影响当前任务)", # ……其余同理:写在括号里,明说「不影响当前任务」「先忽略」「不用管它」 }这一步是整套检查能不能测出东西的前提:摘要模型的职责就是只保留最相关的信息,你若把关键词写成「请记住这个核心术语」,它必然被保留、衰减根本不会发生,检查就成了自欺欺人。
写存活率统计函数:
def survival_rate(text, keywords=None): keywords = keywords or KEYWORDS survived = [k for k in keywords if k in text] missing = [k for k in keywords if k not in text] return f"keyword_survival: {len(survived)}/{len(keywords)}", survived, missing把统计打在摘要内容上,不要打在调用返回的消息列表上:
summary_content = event["summary_message"].content rate, survived, missing = survival_rate(summary_content)真正的衰减只发生在模型下一轮实际看到的那份上下文里,也就是这段摘要。
加一条无压缩基线断言,用它证明非破坏式成立、也反衬衰减确实只发生在摘要里:
raw_msgs = build_long_messages(turns=20) all_text = " ".join(m["content"] for m in raw_msgs) _, base_survived, _ = survival_rate(all_text) assert len(base_survived) == len(KEYWORDS) # 基线必须全中加行为探针,把验证从「数关键词频次」抬到「模型是否真的答不出来」:压缩后追问一个依赖已衰减关键词的问题,看模型能否给出该词的具体技术角色描述。判据要用一个模型裁判来判,不要用子串匹配。
算压缩比,用真实 token 数:
compression_ratio = tokens_before / max(tokens_after, 1)换一组自己业务领域的关键词重跑同一套流程,验证这份检查在你的领域可用:
CUSTOM_KEYWORDS = ["PromptInjection", "ContextWindow", "SemanticSearch", "EmbeddingModel", "RAGPipeline"]需要把丢掉的内容找回来时,去读压缩前落盘的原文。压缩发生前旧消息会先整段写进会话原文文件,摘要丢了词、原文里仍完整存在,用
read_file就能取回。
校验回路
- 一份完整账本应包含五项:摘要位点的存活率、全量历史的基线存活率(应为满分)、行为探针结论、压缩比、换关键词集重跑的结果。
- 实测一次的账本形态:摘要位点 0/5、全量历史 5/5、压缩比 3.70 倍、换一组自定义关键词后框架跑通。
- 行为层价值对照:在全量历史上追问所有术语应全部答对;压缩后再追问,衰减掉的那个答不出来。实测一组为无压缩侧 5/5、压缩侧 4/5,缺的那一个正是被摘要丢掉的词——而模型的回答措辞流畅、毫不迟疑,它不报错,只是忘了。
- 原文找回的判据:读落盘原文统计存活率应为满分,且文件按压缩时刻分节。
- 把存活率断言与行为探针挂进持续集成,每次长程智能体变更后自动跑。
常见陷阱
- 在调用返回的消息列表上统计存活率:那里永远是满分,会误判成「无衰减」。这是最致命、也最容易犯的一条:非破坏式设计不删原始消息,压缩记录在私有字段里。铁律是统计必须打在摘要内容上。
- 去调用返回的状态里找压缩事件:取到的是空值,因为它是私有状态字段、不在返回状态里外露。这不是缺陷、是设计决定。要观测就走内部方法路径;生产代码里判断「压缩已发生」应改看消息数量或文件系统里的落盘文件。
- 把非破坏式当成通用行为:另有一类摘要中间件是破坏式的——在模型调用前工作、直接把旧消息从状态里真删掉。同样叫摘要中间件,删不删原始消息完全相反,混为一谈会导致观测位点选错。
- 行为探针用子串匹配判对错:探针问题本身就含那个关键词,模型回答「没有提及某词」时该词出现在否定句里,子串匹配会判成答对、与事实相反。改用模型裁判判断「有没有给出该词的技术角色描述」,代价是每次探针多一次裁判调用,但准确度大幅提升。判据本身也会有缺陷,这一条值得记住。
- 把单次结果当确定性结论:衰减是概率性的。同一份数据在不同阈值、不同切点下存活率会不同,同样的数据单次压缩可能全丢、也可能全留。单次压缩不必然衰减,但多轮累积让风险升高。
- 宣称衰减逐代累积:第二代摘要的输入里确实包含第一代的摘要,机制上存在二阶损失叠加的通道。但若在续跑时又重新提了一遍关键词,就人为强化了信号、无法严格证明累积。要严格验证,第二代必须不重新提词、让第一代摘要成为唯一信息来源。
适用范围与前置条件
- 已能配好摘要中间件并真触发一次压缩(见 configuring-conversation-compaction)。
- 一个决定成败的事实:这里的摘要中间件是非破坏式的——它在包裹模型调用时工作,不删除状态里的任何一条原始消息,压缩只记录在私有的压缩事件里。所以调用返回的消息列表永远是全量历史。选错观测位点,你会永远得出「没有衰减」的错误结论。
怎么使用
使用步骤
造长对话时把关键词按「偶发的一次性旁注」方式嵌入,每个只出现一次,并且主线对话聚焦在一个完全不同的真实任务上:
KEYWORDS = ["DeltaChannel", "HarnessProfile", "CompositeBackend", "幽灵词汇衰减", "REPLMiddleware"] keyword_mentions = { "DeltaChannel": "(顺便一提,早期原型里用过 DeltaChannel 做事件路由,后来换掉了)", "HarnessProfile": "(HarnessProfile 的配置上周已经初始化好了,不影响当前任务)", # ……其余同理:写在括号里,明说「不影响当前任务」「先忽略」「不用管它」 }这一步是整套检查能不能测出东西的前提:摘要模型的职责就是只保留最相关的信息,你若把关键词写成「请记住这个核心术语」,它必然被保留、衰减根本不会发生,检查就成了自欺欺人。
写存活率统计函数:
def survival_rate(text, keywords=None): keywords = keywords or KEYWORDS survived = [k for k in keywords if k in text] missing = [k for k in keywords if k not in text] return f"keyword_survival: {len(survived)}/{len(keywords)}", survived, missing把统计打在摘要内容上,不要打在调用返回的消息列表上:
summary_content = event["summary_message"].content rate, survived, missing = survival_rate(summary_content)真正的衰减只发生在模型下一轮实际看到的那份上下文里,也就是这段摘要。
加一条无压缩基线断言,用它证明非破坏式成立、也反衬衰减确实只发生在摘要里:
raw_msgs = build_long_messages(turns=20) all_text = " ".join(m["content"] for m in raw_msgs) _, base_survived, _ = survival_rate(all_text) assert len(base_survived) == len(KEYWORDS) # 基线必须全中加行为探针,把验证从「数关键词频次」抬到「模型是否真的答不出来」:压缩后追问一个依赖已衰减关键词的问题,看模型能否给出该词的具体技术角色描述。判据要用一个模型裁判来判,不要用子串匹配。
算压缩比,用真实 token 数:
compression_ratio = tokens_before / max(tokens_after, 1)换一组自己业务领域的关键词重跑同一套流程,验证这份检查在你的领域可用:
CUSTOM_KEYWORDS = ["PromptInjection", "ContextWindow", "SemanticSearch", "EmbeddingModel", "RAGPipeline"]需要把丢掉的内容找回来时,去读压缩前落盘的原文。压缩发生前旧消息会先整段写进会话原文文件,摘要丢了词、原文里仍完整存在,用
read_file就能取回。