从记忆到技能:让长周期 AI Agent 真正学会复用经验

2026-08-25 39 预计阅读时间: 1 分钟
来源: oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:15 分钟

长周期 AI Agent 的难点,不只是保存更多历史记录,而是把历史中可复用的做法提炼出来,让 Agent 在下一次任务中能够稳定执行。Memory 解决的是“过去发生过什么”,Skill 解决的则是“遇到类似问题时应该怎么做”。

这正是 MSCE 试图补上的空白:在记忆和行动之间增加一层可验证、可调用、可持续改进的能力表示。

Agent 能记住,不等于下次会做

传统 Memory 通常保存几类信息:

  • 用户偏好,例如项目使用 Python 还是 TypeScript。
  • 对话摘要,例如上一次排查了哪个错误。
  • 任务结果,例如某个 API 调用成功或失败。
  • 外部事实,例如代码库中某个模块的职责。

这些信息对恢复上下文很有帮助,但它们往往不能直接指导行动。

例如,Agent 可能记得“上次部署因为环境变量缺失而失败”,却不一定会在下一次部署前主动执行环境变量检查。它记住了事件,却没有形成一个稳定的操作流程。

可以把两者区分为:

Memory: 发生了什么?
Skill:  面对类似情况,应该如何判断、执行和验证?

一个合格的 Skill 通常至少包含四部分:

  1. 触发条件:什么任务或状态下应该使用它。
  2. 执行步骤:按什么顺序调用工具或处理信息。
  3. 验证规则:如何判断操作真的成功。
  4. 失败处理:遇到异常时,应该重试、回滚还是请求人工确认。

如果只有一段摘要,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

这里的 preconditionsverificationfailure_policy 很关键。它们把“建议怎么做”变成了“在什么条件下执行,以及如何证明完成”。对于涉及生产环境、权限或数据修改的 Skill,还应该加入人工审批和最大操作范围。

持续进化需要评估闭环

如果系统只会新增 Skill,却不会评估和删除 Skill,最终得到的可能是一个庞杂、冲突、过时的能力库。

建议至少记录这些指标:

  • 触发次数:Skill 是否真的被检索和使用。
  • 适用率:被触发后,是否适合当前任务。
  • 成功率:执行后是否达到目标,而不是仅仅完成工具调用。
  • 回退率:是否频繁需要人工接管或重新规划。
  • 节省量:是否减少了规划时间、工具调用次数或错误恢复成本。
  • 新鲜度:Skill 依赖的 API、命令和项目结构是否仍然有效。

可以给 Skill 建立简单的生命周期:

候选 Candidate
    -> 评估 Evaluated
    -> 发布 Active
    -> 降级 Deprecated
    -> 删除 Retired

一个经验只有在多个独立任务中稳定有效,才适合提升为共享 Skill。对于低频、高风险操作,样本量不是唯一标准,还应加入人工审查和隔离环境验证。

Memory 和 Skill 应该如何分工

两者不需要互相替代,而应承担不同职责:

类型 主要回答的问题 典型内容 生命周期
Memory 过去发生了什么 对话、事件、结果、用户偏好 高频写入,按相关性检索
Skill 类似任务应该怎么做 触发条件、步骤、验证、失败处理 经过评估后发布,持续版本化
Policy 什么不能做 权限、合规、操作边界 稳定、优先级高、不可被普通经验覆盖
Trace Agent 实际做了什么 工具调用、参数、日志、耗时 用于审计、调试和训练评估

实际执行时,可以采用这样的顺序:

  1. 从 Memory 和当前任务中提取上下文。
  2. 检索候选 Skill。
  3. 使用 Policy 过滤不允许的操作。
  4. 由 Agent 选择或组合 Skill。
  5. 执行工具调用并进行验证。
  6. 保存 Trace,并把高质量经验送入评估流程。

这个分层能避免一个常见错误:让模型把一次偶然成功的操作直接当成通用规则。Memory 可以保留事实,Skill 才能进入执行层,而 Policy 始终拥有更高优先级。

落地时的检查清单

开始建设 Memory-to-Skill 系统时,可以从一个具体工作流切入,例如代码修复、测试失败恢复、文档发布或服务部署:

  • 先定义可观察的成功标准,而不是只记录“任务完成”。
  • 保留完整工具轨迹,包括失败步骤和人工修正。
  • 只从重复出现、边界清晰的模式中提炼 Skill。
  • 为每个 Skill 增加触发条件、前置条件和验证方式。
  • 对涉及生产数据和权限的 Skill 设置审批或沙箱。
  • 为 Skill 建立版本、评分和淘汰机制。
  • 将 Skill 的实际效果与没有使用 Skill 的基线进行比较。
  • 让 Agent 在不确定时回退到普通规划,而不是强行套用 Skill。

长周期 Agent 的持续进化,核心不是让它无限保存过去,而是让过去的经验经过提炼、验证和版本化,最终成为下一次可以可靠调用的能力。Memory 让 Agent 不会忘记,Skill 则让它有机会真正做得更好。


相关推荐