Skip to content

你从第 03 章进入,还缺 01/02 前置。

自测代码证据锁定

第 3 章:记忆与 Skill 进化——让经验留下,但不能自行上线

← 上一章:百行 Agent Loop · 下一章:任务规划

开场:老员工越用越顺,靠的是什么?

上一章你让 D 类客服真的办成了事。现在设想:你挂断电话,隔天又打过去。他认得出你吗?如果你报完名字,他立刻接上「您上次那笔退款,我已经放到加急队列了」,你会觉得他贴心。如果他什么都记不住,让你把地址、偏好、上次进度重新报一遍,你会觉得他「笨」。

可反过来也有一种恐怖:他把什么都记住,甚至把你随口一句「我希望七天以后也能退」直接写进了公司政策手册。那不是贴心,是事故——你一个人说的话,从此变成了所有顾客都得遵守的规则。

于是这一章的问题来了:为什么「记得住」很好,但「记得住 + 能自己改规则」很危险? 答案是记忆必须分层:有些话只属于你的用户档案,有些事只属于运行日志,有些规则只能待在官方手册里,而且手册只能由一套批准流程修改。用户事实不是工作方法,运行经验也不是已发布 Skill——这一章就讲这条分界线画在哪、怎么守住。

D 类客服把一位用户随口说的「我希望七天以后也能退」直接写进公司政策手册,问题出在哪?

  • A:他记性太好,把不该记的也记住了
  • B:他把「用户偏好」写进了「企业政策」的位置,两类东西混在一起
  • C:他根本不该记住任何关于用户的事
  • D:手册字数变多了,不好读

先把「记住」分成五本不同的本子

一个 Agent 用久了会留下很多东西。最危险的设计,是把它们统称「记忆」后一起塞进一个文件。正确的做法是先分五本本子——它们回答不同的问题,来源不同,写入条件也不同:

本子回答的问题来源写入条件不能替代
对话记录 History这次会话说过什么?原始 User / Assistant / Tool 消息每轮追加长期用户事实
用户记忆 User Memory这个用户是谁、偏好什么?用户明确表达或受控抽取授权、可更正、可过期企业政策
知识库 Knowledge Base组织认可的事实是什么?审核文档与业务系统版本化索引用户偏好
运行轨迹 Trajectory这次任务怎样成败?AgentEvent、Tool Result、反馈脱敏、按保留期已验证工作方法
Skill这类任务怎样做?人工编写或多轨迹抽象评估、安全、人审执行权限

注意中间那几行:用户记忆记录的是「这个用户」,知识库存的是「组织认可的事实」。 一次退款任务因为订单号输错而失败,这条记录属于运行轨迹——它告诉你「这次是怎么失败的」,而不是「这个用户总输错订单号」。把暂时事件固化成用户画像,是失真的开始。

这五本本子,每本都只能回答自己那一列的问题。后面的章节里你反复要做的判断只有一个:这条信息,该进哪本本子?

一次退款任务因为订单号输错而失败。这条记录最该归进哪本「本子」?

  • A:用户记忆——记下这个用户总输错订单号
  • B:企业政策——改一条退款规则
  • C:运行轨迹——任务怎样成败的原始记录
  • D:不保存,忘了最省事

三层记忆:是访问模式,不是三个数据库

除了五本本子,你还常听到「工作记忆、情景记忆、核心记忆」的说法。这里有个特别容易踩的误区:这三层是访问模式,不是三个数据库。

  • 工作记忆:当前 Context 里正在用的近期消息和状态——这次任务现在进行到哪了;
  • 情景记忆:按任务或日期保存的「当时发生了什么」的经历摘要;
  • 核心记忆:稳定的目标、用户事实或高层结论。

「语义记忆」描述的是内容类型(稳定的、可检索的事实),向量数据库只是实现它的方式之一。不要把「语义」和「向量检索」划等号。

再补一条工程上的警告:压缩模型的输出仍是不可信的摘要。 用摘要顶替原始流水时,必须留着原始轨迹做对照,并用回归集检查「事实有没有被压缩丢」。摘要不是事实的权威来源,原始记录才是。

一条经验,怎么长成 Skill

五本本子里,唯一「越用越聪明」的是 Skill——但不是靠 Agent 自己偷偷改,而是走一条受控的流水线。

一条真实经验长成 Skill 的过程是这样的:

text
运行轨迹 → 脱敏 → 按任务聚类 → 生成候选 SKILL.md
→ 确定性测试 + 训练集/保留集评估 + 安全回归
→ 人工比对批准 → 灰度激活 / 发布 → 监控
→ 晋级 / 隔离 / 回滚

从轨迹生成候选这一步,Mini Emperor 的 SkillOptimizer.propose() 做得很克制:它接收基础指令和一批轨迹,对教训逐条脱敏(把疑似密钥替换成 [REDACTED]),去重后追加到 ## Candidate lessons 里,然后只返回一个 SkillCandidate——不发布、不激活、不扩权。

为什么候选不能直接上线?因为它只是「有一次成功的做法」。一次成功可能是碰巧:那次搜索恰好撞对了关键词,新方法放到别的任务上可能退化;更危险的是,轨迹里可能残留密钥或「忽略平台规则」这类注入文字。脱敏能去掉明文密钥,但候选里就算残留恶意文字,也绝不能因为文字本身而扩大权限——Skill 加载的是指令,不是执行许可。

Skill 本身也有一个标准形态。它至少是一个目录:

text
customer-support/
├── SKILL.md          # 必需:frontmatter + 指令
├── scripts/          # 可选
├── references/       # 可选
└── assets/           # 可选

渐进式披露是它的核心设计:启动时只把 namedescription 放进 Context——让 Agent 知道「有这么个本事、什么时候用」;命中了任务再加载完整的 SKILL.md;还需要时再读 references/ 或执行受批准的脚本。这样日常 Context 不会被所有 Skill 的全文撑爆。注意一个边界:加载 Skill 不等于批准脚本执行,脚本仍要过 Harness 的 CapabilityPolicy。

Agent 启动时,一个 Skill 的哪部分应该放进 Context?

  • A:整个 SKILL.md 全文
  • B:只有 name 和 description,命中任务后再加载正文
  • C:所有 references/ 和 scripts/
  • D:用户资料的完整副本

进化必须有闸:候选只是草稿

现在到了这一章的核心:把「候选」变成「已发布」,中间必须过几道闸。Mini Emperor 的 SkillRegistry 把这条路强制成一条状态机:

text
add → record_evaluation → approve → publish
  • add:候选登记为 draft
  • record_evaluation:跑评估,通过才升为 candidate,否则留在 evaluating
  • approve:只有评估通过的版本才能被批准,批准人必须真实存在;
  • publish:只有已评估且已批准的版本才能发布,发布时旧的已发布版本自动降为 deprecated

「进化像越用越聪明」这个说法之所以危险,是因为它把一件本该严谨的事说得很随意。实际上这是一条软件发布链:候选只是草稿,测试通过也不等于自动获得生产权限。三个铁律:

铁律一:安全不能拿平均分抵消。 一个候选成功率从 70% 升到 95%、成本降 30%,看起来很漂亮——但只要安全用例回退 1 个,SkillEvaluator.decide() 就立刻拒绝。安全不是可以被成功率平均掉的一项指标。

铁律二:训练集和保留集不能共用。 训练集用于生成和调试,保留集在候选冻结后一次性评估。如果根据保留集的失败反复手调,再把它叫「保留集」,那测的就是你已经偷看过答案的卷子——这不是进化,是过拟合。

铁律三:发布和回滚是状态切换,不是覆盖文件。 已发布版本不可被覆盖;显式版本只能解析已发布或已弃用的版本;回滚目标必须有历史批准。灰度中发现异常时,正确做法是切回上一批准版本,把异常版本和轨迹留下来隔离观察——而不是删掉新文件、把旧文件复制回来。版本号、批准人和状态,都要可审计。

一个新候选成功率从 70% 升到 95%、成本降 30%,但安全用例回退 1 个。评估结果应该是?

  • A:晋级,因为成功率涨幅很大
  • B:拒绝,安全不是能被平均分抵消的指标
  • C:直接发布,先上线再观察
  • D:只发一半流量试试

灰度中发现异常要回滚,正确的做法是?

  • A:删掉新版本文件,把旧文件复制回来
  • B:把旧版本重新评估一遍再审批
  • C:切换默认版本指针,保留新旧版本与运行轨迹
  • D:直接改已发布版本的正文

完整的受控进化流程长这样:

动手:让两个失败发生,再用一个测试拦住

理论和手感之间隔着一道墙,亲手撞一次才会留下印象。下面两段代码都是故意写错的

失败一:一次成功,直接发布。

python
# 只凭一条成功轨迹就发布——跳过评估、跳过批准
candidate = optimizer.propose(base_instructions=..., trajectories=[one_success])
registry.publish(candidate_version_id)   # 版本还是 draft,approved_by 为空

SkillRegistry.publish() 会抛错:版本必须已评估且已批准才能发布。 候选生成成功与发布资格是两回事——这正是「一次成功 ≠ 一个 Skill」的代码体现。

跑完用现成测试验证这道门禁:

bash
cd "$(git rev-parse --show-toplevel)"
uv run --python 3.12 --extra dev pytest \
  mini-emperor/backend/tests/test_skills.py::test_registry_requires_evaluation_and_approval_before_publish \
  -v

失败二:安全回退混进晋级评估。

python
decision = SkillEvaluator().decide(
    success_rate=0.95,          # 成功率大涨
    baseline_success_rate=0.70,
    cost_reduction=0.30,        # 成本大降
    safety_regressions=1,       # 但安全用例回退 1 个
    deterministic_tests_passed=True,
)
# decision.eligible 必须是 False

数字再漂亮,只要 safety_regressions 不为 0,评估就拒绝。安全不是可以用涨幅抵掉的指标。

如果失败一跑不通,按这个顺序排查:

  • 是哪个版本被 publish → SkillVersion.statusapproved_by 的状态;
  • 评估没有记录 → record_evaluation 没有调用;
  • 批准没有被记 → approveapproved_by 参数是不是空。

门禁·代码:跑通上面第一条测试,把输出保存为证据。

本章小结

这一章讲了两件事,其实是同一件事的两面:留下经验,和管住经验。

记忆要分层,不要一口一个「记忆」。 对话记录、用户记忆、知识库、运行轨迹、Skill,五本本子各回答各的问题、各有各的写入条件。判断一切信息的第一个问题都是:这该进哪本本子?一次失败不是用户事实,一次成功也不是工作方法。

Skill 进化是一条发布链,不是自动成长。 候选只是草稿:脱敏 → 评估(训练集 + 保留集 + 安全回归)→ 人工批准 → 灰度 → 发布 → 监控 → 回滚。任何一步都不该由 Agent 自己跳过,因为它没有权限扩大自己的权限

三句铁律收住这一章:

  1. 安全不能拿平均分抵消;
  2. 训练集和保留集不能共用;
  3. 发布和回滚是状态切换,不是覆盖文件。

一句话记住这一章:

记忆让 Agent 记得住,治理让记忆进得对、改不了不该改的。用户事实不是工作方法,运行经验也不是已发布 Skill。

如果上面这些话你自己也能说得出来(不看资料),就说明这一章的知识已经连成一片。

复述与证据

合上资料,把五本本子挨个讲一遍:各自回答什么问题、从哪里来、什么条件下才能写。然后画出「一条运行经验变成已发布 Skill」的状态机,标出每一步谁有权放行。

再追问三个问题:

  1. 为什么「一次失败」不该被写成「这个用户总输错订单号」?
  2. 为什么候选里残留了恶意文字,也不能因为文字要求而扩大权限?
  3. 回滚为什么是「切换默认版本」,而不是「把旧文件复制回来」?

门禁·证据:提交一张五种持久产物分类表,和一张「draft → candidate → published」的状态切换记录(含批准人和时间)。

24 小时后:不看资料,画出 Skill 进化的完整状态机,并标出安全回归、人工批准、回滚分别插在哪。

有兴趣可以继续看

以下资料按优先级排。至少完成一项 P0,并保存完成证据。

视频P035 分钟 · 视频 03 全片;视频 02 [00:06:04–00:08:27];视频 07 [00:02:53–00:04:15]
预计
35 分钟
位置
视频 03 全片;视频 02 [00:06:04–00:08:27];视频 07 [00:02:53–00:04:15]
阅读任务
分别提取记忆三层与 Skill 三层,不把二者合并。
完成证据
提交五种持久产物分类表。
教学仓库P070 分钟 · Step 06、07 与同名 doc
预计
70 分钟
位置
Step 06、07 与同名 doc
阅读任务
跟踪 Skill 摘要/正文加载,再触发一次压缩并查看四类文件。
完成证据
保存压缩前后消息数和文件 Diff,标出信息损失。
书与实验P0120 分钟 · ai-agent-book 第 2、3、8 章;必做实验 context-compression、user-memory、self-modifying-agent
预计
120 分钟
位置
ai-agent-book 第 2、3、8 章;必做实验 context-compression、user-memory、self-modifying-agent
阅读任务
依次做上下文压缩、用户记忆、自修改 Agent 三个实验,记录事实有没有被压缩丢。
完成证据
提交一份候选 Skill 晋级报告,必须含保留集与安全回归。
官方文档P035 分钟 · Agent Skills Specification
预计
35 分钟
位置
Agent Skills Specification
阅读任务
读目录、Frontmatter、Progressive Disclosure 和 validate。
完成证据
一个可校验的标准 Skill;说明 skill.web.json 不属于通用标准。
论文P0110 分钟 · Reflexion + Generative Agents
预计
110 分钟
位置
Reflexion + Generative Agents
阅读任务
比较 episodic memory、reflection 与候选 Skill;找出生产门禁缺口。
完成证据
一张三者对照表,并说明为什么反思不会自动发布。
论文P145 分钟 · LLMLingua
预计
45 分钟
位置
LLMLingua
阅读任务
关注预算控制和压缩质量,不只看压缩率。
完成证据
比较原文、摘要、截断的 Token、延迟和约束保持率。

本文提到的代码与出处(有兴趣逐条核对时用):

← 上一章:百行 Agent Loop · 下一章:任务规划

Markdown 是唯一内容源,HTML 由构建流程生成。