长周期 AI Agent 的难点,不只是保存更多历史记录,而是把历史中可复用的做法提炼出来,让 Agent 在下一次任务中能够稳定执行。Memory 解决的是“过去发生过什么”,Skill 解决的则是“遇到类似问题时应该怎么做”。
这正是 MSCE 试图补上的空白:在记忆和行动之间增加一层可验证、可调用、可持续改进的能力表示。
Agent 能记住,不等于下次会做
传统 Memory 通常保存几类信息:
- 用户偏好,例如项目使用 Python 还是 TypeScript。
- 对话摘要,例如上一次排查了哪个错误。
- 任务结果,例如某个 API 调用成功或失败。
- 外部事实,例如代码库中某个模块的职责。
这些信息对恢复上下文很有帮助,但它们往往不能直接指导行动。
例如,Agent 可能记得“上次部署因为环境变量缺失而失败”,却不一定会在下一次部署前主动执行环境变量检查。它记住了事件,却没有形成一个稳定的操作流程。
可以把两者区分为:
Memory: 发生了什么?
Skill: 面对类似情况,应该如何判断、执行和验证?
一个合格的 Skill 通常至少包含四部分:
- 触发条件:什么任务或状态下应该使用它。
- 执行步骤:按什么顺序调用工具或处理信息。
- 验证规则:如何判断操作真的成功。
- 失败处理:遇到异常时,应该重试、回滚还是请求人工确认。
如果只有一段摘要,Agent 仍然需要临场推理;如果有结构化 Skill,它就可以把经验转化为下一次行动的约束。
长周期任务为什么更需要 Skill
短对话中的错误通常可以通过下一轮追问纠正。长周期任务则不同:Agent 可能要修改多个文件、运行测试、访问浏览器、调用外部服务,甚至跨越数小时或数天继续工作。
在这样的流程中,几个问题会被放大:
- 上下文窗口有限,早期决策可能被截断。
- 同类任务反复出现,但每次都从零开始规划。
- 工具调用成功不代表业务目标达成。
- 错误经验可能被重复使用,造成稳定的错误模式。
- 多个 Agent 之间难以共享隐含的操作知识。
因此,长周期 Agent 需要的不只是更大的历史数据库,还需要一种能够脱离原始对话、独立指导任务执行的知识单元。
Skill 可以是一个 Markdown 文件、一段结构化 JSON、一个可执行函数,或者它们的组合。关键不在于文件格式,而在于它是否具备明确的使用边界和验证标准。
一个可落地的 Memory-to-Skill 流程
可以把经验转化为 Skill 的过程拆成四步:
1. 收集任务轨迹
记录 Agent 的目标、工具调用、输入输出、错误、最终结果以及人工反馈。不要只保存最终答案,因为真正有价值的经验经常出现在中间步骤和失败分支里。
2. 识别可重复模式
并不是每次成功都值得沉淀。适合提炼为 Skill 的经验通常满足以下条件:
- 在多个任务中重复出现。
- 能够描述清晰的触发条件。
- 有相对稳定的执行步骤。
- 可以通过测试或检查结果验证。
- 复用后能够降低错误率或减少工具调用。
3. 生成并评估 Skill
让模型从轨迹中提出候选 Skill,但不要直接把模型生成的内容放入生产环境。需要检查它是否引用了具体项目中的偶然细节,是否遗漏权限、超时和回滚处理,是否会在不适用的任务中被错误触发。
4. 在下一次任务中检索和执行
新任务到来时,系统先根据目标、仓库、工具和环境检索候选 Skill,再由 Agent 判断是否采用。执行过程中记录 Skill 的调用结果,用于后续升级、降级或废弃。
下面是一个最小化的 Python 示例。它不依赖外部服务,演示如何把任务记录提炼成 Skill,并在新任务中匹配和执行。示例中的规则是简化实现,实际项目可以替换为向量检索、LLM 评估器或工作流引擎。
运行方式:将代码保存为 memory_to_skill.py,使用 Python 3.10 或更高版本执行 python memory_to_skill.py。
from dataclasses import dataclass, field
from typing import Callable, List
@dataclass
class TaskTrace:
goal: str
actions: List[str]
result: str
success: bool
feedback: str = ""
@dataclass
class Skill:
name: str
triggers: List[str]
steps: List[str]
verify: Callable[[TaskTrace], bool]
uses: int = 0
failures: int = 0
metadata: dict = field(default_factory=dict)
@property
def score(self) -> float:
return (self.uses - self.failures * 2) / max(self.uses, 1)
def matches(self, goal: str) -> bool:
text = goal.lower()
return any(trigger.lower() in text for trigger in self.triggers)
def verify_deployment(trace: TaskTrace) -> bool:
return trace.success and "health check" in trace.result.lower()
def extract_skill(traces: List[TaskTrace]) -> Skill | None:
successful = [trace for trace in traces if trace.success]
if len(successful) < 2:
return None
common_steps = [
"检查目标环境变量是否完整",
"执行部署命令并保存日志",
"等待服务就绪",
"执行 health check",
]
return Skill(
name="部署前检查与健康验证",
triggers=["部署", "发布", "上线"],
steps=common_steps,
verify=verify_deployment,
metadata={"source_traces": len(successful)},
)
def run_skill(skill: Skill, goal: str) -> TaskTrace:
print(f"使用 Skill: {skill.name}")
for index, step in enumerate(skill.steps, start=1):
print(f"{index}. {step}")
# 真实系统中,这里应调用 shell、CI/CD API 或其他工具。
trace = TaskTrace(
goal=goal,
actions=skill.steps,
result="Deployment completed; health check passed",
success=True,
)
skill.uses += 1
if not skill.verify(trace):
skill.failures += 1
return trace
if __name__ == "__main__":
history = [
TaskTrace("部署支付服务", ["检查变量", "部署", "health check"], "health check passed", True),
TaskTrace("发布支付服务", ["检查变量", "部署", "health check"], "health check passed", True),
]
skill = extract_skill(history)
new_goal = "把订单服务上线到 staging 环境"
if skill and skill.matches(new_goal):
result = run_skill(skill, new_goal)
print(f"验证结果: {'通过' if skill.verify(result) else '失败'}")
print(f"当前 Skill 分数: {skill.score:.2f}")
else:
print("没有匹配的 Skill,需要重新规划任务。")
这个例子包含几个重要约束:Skill 不仅保存步骤,还保存触发条件和验证函数;执行结果会反馈到 Skill 的评分中;当历史样本不足时,系统不会急于生成新能力。
Skill 不是静态提示词
把 Skill 设计成一段很长的系统提示词,短期内容易实现,但长期维护会遇到几个问题:
- 所有任务都可能加载同一批内容,增加上下文成本。
- Skill 之间的适用边界不清晰,容易互相冲突。
- 提示词写得很详细,却没有机器可验证的成功标准。
- 失败经验会被保留,但缺少版本、质量和淘汰机制。
更实用的做法是把 Skill 当成一种可管理的能力资产。可以使用类似下面的 YAML 描述它的边界:
name: deploy-with-health-check
version: 1.2.0
triggers:
- deploy
- release
- production rollout
preconditions:
- repository_has_ci: true
- environment_is_known: true
steps:
- inspect_required_environment_variables
- run_deployment_command
- wait_for_readiness
- call_health_check
verification:
type: http
url: /health
expected_status: 200
failure_policy:
missing_variable: stop_and_report
readiness_timeout: rollback
health_check_failed: rollback_and_attach_logs
quality:
min_success_rate: 0.95
min_sample_count: 10
这里的 preconditions、verification 和 failure_policy 很关键。它们把“建议怎么做”变成了“在什么条件下执行,以及如何证明完成”。对于涉及生产环境、权限或数据修改的 Skill,还应该加入人工审批和最大操作范围。
持续进化需要评估闭环
如果系统只会新增 Skill,却不会评估和删除 Skill,最终得到的可能是一个庞杂、冲突、过时的能力库。
建议至少记录这些指标:
- 触发次数:Skill 是否真的被检索和使用。
- 适用率:被触发后,是否适合当前任务。
- 成功率:执行后是否达到目标,而不是仅仅完成工具调用。
- 回退率:是否频繁需要人工接管或重新规划。
- 节省量:是否减少了规划时间、工具调用次数或错误恢复成本。
- 新鲜度:Skill 依赖的 API、命令和项目结构是否仍然有效。
可以给 Skill 建立简单的生命周期:
候选 Candidate
-> 评估 Evaluated
-> 发布 Active
-> 降级 Deprecated
-> 删除 Retired
一个经验只有在多个独立任务中稳定有效,才适合提升为共享 Skill。对于低频、高风险操作,样本量不是唯一标准,还应加入人工审查和隔离环境验证。
Memory 和 Skill 应该如何分工
两者不需要互相替代,而应承担不同职责:
| 类型 | 主要回答的问题 | 典型内容 | 生命周期 |
|---|---|---|---|
| Memory | 过去发生了什么 | 对话、事件、结果、用户偏好 | 高频写入,按相关性检索 |
| Skill | 类似任务应该怎么做 | 触发条件、步骤、验证、失败处理 | 经过评估后发布,持续版本化 |
| Policy | 什么不能做 | 权限、合规、操作边界 | 稳定、优先级高、不可被普通经验覆盖 |
| Trace | Agent 实际做了什么 | 工具调用、参数、日志、耗时 | 用于审计、调试和训练评估 |
实际执行时,可以采用这样的顺序:
- 从 Memory 和当前任务中提取上下文。
- 检索候选 Skill。
- 使用 Policy 过滤不允许的操作。
- 由 Agent 选择或组合 Skill。
- 执行工具调用并进行验证。
- 保存 Trace,并把高质量经验送入评估流程。
这个分层能避免一个常见错误:让模型把一次偶然成功的操作直接当成通用规则。Memory 可以保留事实,Skill 才能进入执行层,而 Policy 始终拥有更高优先级。
落地时的检查清单
开始建设 Memory-to-Skill 系统时,可以从一个具体工作流切入,例如代码修复、测试失败恢复、文档发布或服务部署:
- 先定义可观察的成功标准,而不是只记录“任务完成”。
- 保留完整工具轨迹,包括失败步骤和人工修正。
- 只从重复出现、边界清晰的模式中提炼 Skill。
- 为每个 Skill 增加触发条件、前置条件和验证方式。
- 对涉及生产数据和权限的 Skill 设置审批或沙箱。
- 为 Skill 建立版本、评分和淘汰机制。
- 将 Skill 的实际效果与没有使用 Skill 的基线进行比较。
- 让 Agent 在不确定时回退到普通规划,而不是强行套用 Skill。
长周期 Agent 的持续进化,核心不是让它无限保存过去,而是让过去的经验经过提炼、验证和版本化,最终成为下一次可以可靠调用的能力。Memory 让 Agent 不会忘记,Skill 则让它有机会真正做得更好。