AI Agent 是当前以大模型为核心的工程热点,而斯坦福 CS329A 这门课更进一步,把目标从“会调用工具”提升到“能从自身经验中改进能力”。自改进不是给模型多调几次 prompt,而是让 Agent 在完成任务的循环里,记录轨迹、检验结果、修正策略,最终让同样的任务做得更好。本文围绕 CS329A 的课程主线,先讲清楚自改进 Agent 与传统程序的区别,再给出可复现的最小实现思路,最后补充排查路径和实战建议。
这门课适合三类人:已经在做 RAG、工具调用或自动化流程的工程师,想从“调接口”升级到“设计闭环系统”的研究者,以及准备系统学习 Agent 工程化的学生。读完本文,你会得到一张完整的自改进 Agent 技术地图,包括模块划分、代码骨架、评估方法、常见坑和可落地的检查清单。
1. 先理解“自改进 AI Agent”到底在解决什么问题
1.1 自改进 Agent 和普通程序有什么不同
普通程序是“输入 -> 固定逻辑 -> 输出”。无论运行多少次,只要输入相同,输出就几乎不变。Agent 的本质区别在于,它会根据当前环境状态选择动作,而自改进 Agent 还会在动作结束之后反思:这次为什么做对了,为什么做错了,下次怎样更少走弯路。
CS329A 这门课程把“自改进”拆成了三层:
- 第一层是记忆。Agent 需要保存历史轨迹,而不是每次任务都从零开始。
- 第二层是反思。Agent 需要根据结果反馈生成经验总结。
- 第三层是策略更新。Agent 把反思后的经验变成新的 prompt、参数、或工具选择规则,用于下一次任务。
这三层缺一不可。只有记忆没有反思,数据会堆积但不会变成能力;只有反思没有策略更新,总结只是一堆文本;只有策略更新没有验证机制,模型会越改越偏。
1.2 为什么“自改进”不能靠堆 Prompt 实现
很多人会问:自改进是不是把错误案例写进 system prompt 就行?这种做法有效,但不可持续。Prompt 长度有限,错误案例会越来越多,而且不同任务之间的经验可能互相冲突。
更系统的做法是把自改进看成一个闭环:采样轨迹,用评估器打分,从高分和低分轨迹中提炼规则,再通过微调或外部知识库更新策略。CS329A 强调的正是这个闭环。它要求开发者先定义“好”和“坏”,再让 Agent 在试错中逐步靠近“好”的标准。
1.3 CS329A 的课程主线:从能力边界到自我强化
从课程主题看,CS329A 讨论的不只是“怎么让 Agent 能写代码”或“怎么让 Agent 能查文档”,而是“如何让 Agent 在多次执行中自动发现自己的能力边界,并针对短板进行强化”。
一个典型路径是:
- 给出一个目标函数,例如任务成功率。
- Agent 执行一组任务,收集日志和结果。
- 评估器判断哪些步骤失败,并给出失败标签。
- Agent 根据失败标签调整工具使用顺序、prompt 描述或模型参数。
- 再次执行同样任务,对比成功率。
这条路径和机器学习训练流程很像,区别在于 Agent 的状态空间、动作空间和反馈信号都更复杂。这也是为什么学 CS329A 之前,最好已经掌握 Python、基础机器学习和大模型 API 调用。
2. 围绕课程主题搭建可复现的学习环境
2.1 环境准备和依赖版本建议
学习 CS329A 不需要一开始就上生产级分布式系统。一个能在本地跑通的最小环境足够理解核心逻辑。建议使用 Python 3.10 以上版本,并安装以下依赖:
transformers:用于加载本地或远程大模型。torch或tensorflow:作为模型后端。openai或类似 SDK:用于调用云端模型服务。pydantic:用于定义结构化输出和评估结果。pytest:用于测试 Agent 的每个模块。
如果原始课程环境没有明确给出版本,落地前需要确认依赖版本兼容性。示例操作如下:
python -m venv venv source venv/bin/activate pip install --upgrade pip pip install transformers torch openai pydantic pytest这里把transformers和openai同时安装,是因为学习时可能需要在本地小模型和云端大模型之间切换。pydantic用来约束 Agent 返回的数据结构,评估器依靠它判断结果是否有缺失字段。
2.2 最小 Agent 框架:模型、工具、循环
下面用一个非常小的示例说明 Agent 的基本组成。这里假设call_llm是封装好的模型调用函数,get_weather是工具,实际使用时可以换成自己的 API。
# agent_base.py from typing import Callable, Dict, List def call_llm(messages: List[Dict[str, str]]) -> str: # 这里实现模型调用,返回文本 raise NotImplementedError def get_weather(city: str) -> str: # 模拟天气查询工具 return f"{city} 晴,25 度" class MinimalAgent: def __init__(self, tools: Dict[str, Callable]): self.tools = tools self.history = [] def run(self, user_input: str) -> str: messages = [ {"role": "system", "content": "你是一个智能助理,可以调用工具获取实时信息。"}, {"role": "user", "content": user_input}, ] response = call_llm(messages) self.history.append({"input": user_input, "output": response}) return response这个 Agent 只有“调用模型”和“记录历史”两个能力,还没有自改进。自改进要在这个基础上增加评估和优化模块。先跑通这个最小闭环,再逐步扩展,是 CS329A 推荐的实践顺序。
2.3 为什么需要评估器而不是只看“模型回答好不好”
普通聊天场景中,人可以主观判断回答质量。自改进 Agent 面对的是批量任务,不可能每次都人工打分,需要使用评估器自动计算指标。
评估器可以很简单,也可以很复杂。简单评估器检查结构化输出是否包含关键字段;复杂评估器会把答案和标准答案做语义相似度计算,或者用另一个模型做裁判。CS329A 关注的重点之一是“评估信号的质量”,因为自改进算法最终要优化评估器给出的分数,如果分数不可靠,策略更新就会跑偏。
以下是评估器的最小实现:
# evaluator.py from typing import Dict class SimpleEvaluator: def __init__(self, required_fields: list): self.required_fields = required_fields def evaluate(self, output: Dict) -> Dict: missing = [f for f in self.required_fields if f not in output] score = 1.0 if not missing else 0.0 return { "score": score, "missing_fields": missing, "passed": score == 1.0, }这个评估器只检查字段是否完整,适合学习阶段。真实项目还需要检查字段值是否合法、是否和上下文一致、计算过程是否正确等。
3. 自改进 Agent 的三个核心模块:记忆、反思、策略更新
3.1 记忆模块:从“每次从头开始”到“带着经验工作”
自改进 Agent 的记忆可以分为两类。
短期记忆指当前任务执行过程中的上下文,例如已经调用过的工具、已经得到的中间结果。长期记忆指跨任务的经验,例如某个任务类型常见的失败模式、某类 prompt 的改进记录。
实现时,建议用统一的Trajectory结构保存记忆。每一次任务执行都记录:
- 输入意图
- Agent 的每一步动作
- 工具返回结果
- 最终答案
- 评估器打分和错误标签
保存到 JSON 或 SQLite 都可以。CS329A 里强调“轨迹是自改进的燃料”,因为后续所有反思和策略更新都基于轨迹,而不是基于零散印象。
3.2 反思模块:把轨迹变成可执行的修改建议
反思不是让 Agent 写一句“我错了”,而是从错误轨迹中找出可以复用的规律。例如一个 Agent 在查询天气时总是先问用户城市名,再调用工具。如果轨迹显示用户已经说了“北京天气如何”,那 Agent 还反问就显得多余。
反思模块要做的是:
- 从失败轨迹中定位关键步骤。
- 对比成功轨迹和失败轨迹的差异。
- 输出一条可执行规则,例如“当用户输入包含城市名时,不要反问,直接调用天气工具”。
如果用大模型做反思,通常需要把轨迹摘要和失败原因拼接成 prompt。这里要注意,不能让反思结果变成空话,例如“下次要更细心”这种提示没有价值。规则必须落到具体动作。
3.3 策略更新模块:把反思结果应用到下一次决策
策略更新有两条路径。
一条是外部策略。反思生成的新规则写入知识库,Agent 在下一次任务开始时把相关规则注入 system prompt。这条路径成本低,适合快速实验。
另一条是内部策略。把反思后的经验整理成训练数据,对模型进行微调。这条路径更接近课程讨论的“自改进”终极目标,但成本高,而且需要防止过拟合。
下面用一个表格比较两种策略更新方式:
| 维度 | 外部知识库 | 模型微调 |
|---|---|---|
| 更新速度 | 快,写入即生效 | 慢,需要训练和评估流程 |
| 成本 | 低,只需要注意 prompt 长度 | 高,需要算力和数据准备 |
| 可解释性 | 可以直接看到注入的规则 | 较弱,行为变化分散在参数中 |
| 适用场景 | 实验期、工具规则变化频繁 | 能力已经稳定,需要举一反三 |
| 失败回滚 | 简单,删除规则即可 | 需要保留旧权重或做版本控制 |
学习 CS329A 时,建议先实现外部知识库方式,跑通“反思 -> 写入规则 -> 再执行”的闭环,再考虑微调。否则很难判断能力提升来自数据质量还是模型参数变化。
4. 构建一个“最小自改进 Agent”的完整流程
4.1 任务定义和数据结构设计
为了演示,我们定义一个简单任务:根据用户描述,输出一个包含city和weather两个字段的 JSON。Agent 使用的工具是天气查询函数,评估器检查输出 JSON 是否合法且字段完整。
数据结构如下:
# schema.py from pydantic import BaseModel class TaskInput(BaseModel): user_input: str class TaskOutput(BaseModel): city: str weather: str这样做的好处是,评估器可以直接使用 pydantic 的字段定义。如果 Agent 返回的 JSON 缺少字段,pydantic 会抛出验证错误,评估器捕获后标记为失败。
4.2 实现 Agent 的执行、评估、反思、更新循环
下面给出一个完整的自改进闭环代码框架。为了清晰,模型调用部分保持抽象,实际项目中可以替换为任何大模型服务。
# self_improving_agent.py import json from typing import Callable, Dict, List, Optional from agent_base import MinimalAgent from evaluator import SimpleEvaluator from schema import TaskInput, TaskOutput class SelfImprovingAgent: def __init__( self, model_fn: Callable[[List[Dict[str, str]]], str], tools: Dict[str, Callable], evaluator: SimpleEvaluator, rules: Optional[List[str]] = None, ): self.model_fn = model_fn self.tools = tools self.evaluator = evaluator self.rules = rules or [] self.trajectories = [] def _build_messages(self, user_input: str) -> List[Dict[str, str]]: system_content = "你是一个天气查询助手。请严格输出 JSON 格式。" if self.rules: system_content += " 以下规则来自历史经验:" system_content += " ".join(self.rules) return [ {"role": "system", "content": system_content}, {"role": "user", "content": user_input}, ] def run(self, user_input: str) -> Dict: messages = self._build_messages(user_input) raw_output = self.model_fn(messages) try: output = json.loads(raw_output) except json.JSONDecodeError: output = {} eval_result = self.evaluator.evaluate(output) trajectory = { "input": user_input, "raw_output": raw_output, "parsed_output": output, "eval": eval_result, } self.trajectories.append(trajectory) return { "output": output, "evaluation": eval_result, } def improve(self): # 从失败轨迹中生成规则 failed_trajectories = [ t for t in self.trajectories if not t["eval"]["passed"] ] if not failed_trajectories: return examples = json.dumps(failed_trajectories[:3], ensure_ascii=False) rule_prompt = ( "分析以下 Agent 执行轨迹,找出失败原因," "并用一句话输出可执行的改进规则。\n轨迹:" + examples ) rule = self.model_fn( [ {"role": "user", "content": rule_prompt}, ] ).strip() self.rules.append(rule)这个类的工作流程是:运行任务 -> 记录轨迹 -> 评估失败 -> 从失败轨迹中生成新规则 -> 写回规则列表 -> 下次任务使用新规则。
4.3 运行和验证这个闭环
为了让示例可运行,我们写一个模拟的模型函数。它第一次返回缺少字段的 JSON,第二次在规则指导下返回正确结果。
# demo.py from self_improving_agent import SelfImprovingAgent from evaluator import SimpleEvaluator fake_messages = [] def fake_model(messages): global fake_messages fake_messages = messages if len(fake_messages[0]["content"]) > 50: # 模拟带规则时输出正确 JSON return '{"city": "北京", "weather": "晴"}' else: # 模拟初始输出缺失字段 return '{"city": "北京"}' evaluator = SimpleEvaluator(required_fields=["city", "weather"]) agent = SelfImprovingAgent(model_fn=fake_model, tools={}, evaluator=evaluator) result1 = agent.run("北京天气怎么样") print("第一次结果:", result1) agent.improve() print("生成的规则:", agent.rules[0]) result2 = agent.run("北京天气怎么样") print("第二次结果:", result2)这个例子非常简化,但展示了自改进循环的关键:失败 -> 反思 -> 规则注入 -> 行为变化。真实项目中,模型函数要换成真实 API 调用,反思 prompt 也要更精细。
4.4 从学习用例走向真实项目时要注意什么
上面示例只适合学习和理解概念。真实项目还需要补充以下内容:
- 评估器不能只检查字段存在,还要检查值是否合理。
- 规则库需要版本管理和冲突检测,不能无限制追加。
- 每次策略更新后需要跑回归测试,确认旧任务没有被破坏。
- 模型调用成本需要记录,自改进不能以无限增加调用次数为代价。
- 需要区分训练环境和生产环境,避免生产环境直接自动更新规则。
5. 学习与实战中的常见坑和排查路径
5.1 模型生成结果不稳定,导致评估结果忽高忽低
现象是同一个 Agent 使用同样输入,多次执行结果不同,今天成功明天失败。可能原因包括模型采样参数中的 temperature 设置过高、prompt 中规则互相冲突、工具返回结果存在随机性。
排查先看日志中记录的温度参数和 prompt 版本。然后固定一组测试用例,在相同参数下运行 10 次,统计成功率。如果成功率波动大,可以先把 temperature 调到 0 或一个较低值,排除随机性后再逐步调整。
5.2 自改进导致“越改越差”,出现能力退化
加入反思规则后,简单任务成功率提升,但复杂任务成功率下降。这通常是规则过拟合造成的,新的规则只适用于少数失败案例,却干扰了正常路径。
检查方式是把历史轨迹按难度分组,分别统计成功率。如果某一组显著下降,说明新规则可能过于激进。解决方法是给规则增加生效条件,例如“当输入包含城市名时才注入天气工具规则”,并建立规则回滚机制。CS329A 里也强调,自改进不等于盲目叠加规则,先验证后上线。
5.3 评估指标和真实目标不一致
一个常见误区是评估器只检查 JSON 是否合法,但用户真正的需求是“回答得足够详细”或“找到准确信息”。如果 Agent 学会了用合法但不相关的内容绕过评估器,自改进就会优化一个错误的指标。
排查时要把评估指标和真实业务目标写在一张表里逐项对照。例如:
| 评估指标 | 真实目标 | 是否一致 |
|---|---|---|
| 输出包含 city 和 weather 字段 | 用户能知道今天能不能出门 | 部分一致,缺少温度范围判断 |
| 回答包含“注意安全” | 风险提示 | 指标未覆盖真实风险场景 |
| 调用工具次数少于 3 次 | 降低延迟 | 不一定,可能减少必要探索 |
发现不一致后,要修改评估指标,而不是让 Agent 更努力地迎合旧指标。
5.4 成本膨胀,自改进调用链越来越长
自改进 Agent 每次任务都需要执行主模型、评估器、反思模型,可能导致成本成倍增长。排查方法是记录每次任务的 token 消耗和 API 调用次数。可以在代码中增加一个计数器,把日志输出到表格或时序数据库。
| 阶段 | 平均调用次数 | 平均输入 token | 平均输出 token |
|---|---|---|---|
| 主 Agent 执行 | 3 | 2000 | 500 |
| 评估器 | 1 | 800 | 200 |
| 反思生成 | 0.2 | 1000 | 300 |
如果反思模型调用成本过高,可以改为只在批量离线流程中触发反思,不在每次在线请求中触发。这是学习环境和生产环境的关键差异之一。
6. 从课程到项目:实践建议和扩展方向
6.1 先把基线跑通,再谈自改进
很多人学习自改进 Agent 时会犯同一个错误:直接设计复杂框架,结果第一版就出现大量 Bug。更好的做法是先实现一个没有自改进功能的普通 Agent,固定测试集,记录基线的成功率和平均成本。然后再逐步添加记忆、反思、规则注入。
每添加一个模块,都重新跑同一组测试。这样能清楚知道哪个改动真正带来提升。建议把测试集保存为 JSONL 文件,每个样例包含输入、标准输出、评估字段。
{"input": "北京天气怎么样", "expected": {"city": "北京", "weather": "晴"}} {"input": "上海天气怎么样", "expected": {"city": "上海", "weather": "多云"}}测试集规模不用大,但必须覆盖正常情况、边界情况和错误输入。
6.2 自改进 Agent 生产化的六个关键步骤
- 配置外置化:规则、prompt、模型版本不要写死在代码里,使用配置文件或配置中心管理。
- 日志结构化:每一条轨迹都保存为 JSON,方便后续离线分析。
- 评估自动化:把评估器接入 CI,每次规则变更后自动跑回归。
- 策略回滚:规则和模型权重都要有版本记录,发现问题可以迅速回退。
- 监控指标:至少监控任务成功率、平均成本、平均延迟、规则命中率。
- 安全护栏:对 Agent 的工具调用权限做最小化限制,防止错误动作影响外部系统。
6.3 可作为课程作业或项目扩展的方向
如果正在跟学 CS329A,可以从小任务开始扩展。推荐几个方向:
- 自动代码修复 Agent:根据编译器报错信息修改代码,并用单测通过率作为评估指标。
- 信息检索 Agent:根据用户问题选择搜索词,再根据文档相关性优化搜索策略。
- 表格问答 Agent:让 Agent 学会写 SQL 查询,并用查询结果正确率评估。
- 多工具协作 Agent:让 Agent 自行决定先调用哪个工具,通过任务成功率评估工具调度策略。
每个方向都可以套用“执行 -> 评估 -> 反思 -> 策略更新”的闭环框架。关键不是框架本身,而是你能否为任务定义出可靠的评估器和可执行的反思规则。
6.4 可复用的自改进 Agent 检查清单
发布自改进 Agent 前,对照以下清单逐项检查:
- 明确任务边界:Agent 可以做什么,不能做什么。
- 固定测试集:至少 30 个典型样例,包含成功和失败场景。
- 评估器可解释:每个评估分数都能对应到具体错误原因。
- 反思规则可执行:规则不是“下次要细心”,而是“当输入包含城市名时直接调用天气工具”。
- 有回归测试机制:策略更新后,旧任务成功率不下降。
- 记录轨迹和版本:每个输出都能追溯模型版本、规则版本、工具版本。
- 控制成本和延迟:有 token 用量和调用次数上限。
- 保留人工审核入口:关键任务或高风险任务不允许完全自动更新策略。
这份清单既适用于课程作业,也适用于生产项目。自改进 Agent 的价值不在于让模型“自己变聪明”,而在于让开发者有能力把每一次失败都转化为下一次执行的指导信号。
学习 CS329A 时,最重要的练习不是背诵概念,而是亲手把一个没有记忆的 Agent 改造成一个能感知失败、生成规则、并验证规则有效性的系统。跑通一次完整的自改进循环之后,你会对评估信号、轨迹质量和策略更新的关系有更实际的理解。这个理解可以直接迁移到 RAG 调优、自动化测试、数据分析助手和代码生成等多个场景中。