斯坦福 CS329A 这学期的主题是 Self-Improving AI Agents,也就是自我改进的 AI 智能体。它不是一门教你“怎么调 Prompt、怎么接工具链”的应用课,而是把最底层的问题摆在桌面上:一个 Agent 在犯错之后,如何通过推理、搜索和强化学习,让自己下一次做得更好。
这门课最值得关注的地方,是它把三大块技术串成了同一条训练链路:推理(Reasoning)负责让模型“想得更深”,搜索(Search)和规划(Planning)负责在更大的解空间里找更优答案,强化学习(RL)负责把“答对/做对”变成可持续的优化信号。也就是说,你会在同一门课里看到 AlphaGo 式的搜索思想、ChatGPT 式的偏好对齐、以及 Kendrick 所谓“test-time compute”式的推理时扩展,如何被统一进一个 Agent 框架。
这篇文章会把 CS329A 的知识体系拆开看,并给出一条可以自己在本地机器上落地的学习路线。你不需要先上完课才能动手:从最基本的“多次采样 + 验证”开始,可以一路做到 DPO 微调和规则奖励强化学习的小闭环。读完你应该能回答这几个问题:Agent 的自我改进到底改的是什么、需要什么环境、先做哪个实验最划算、最容易在哪个环节翻车。
如果你已经在做 Agent 应用开发,但总觉得停留在“调用 API、拼接工具”的层面,这篇建议直接收藏。
1. 课程核心模块速览
先给一张课程层面的速览表,方便快速判断这门课是否需要投入精力。
| 维度 | 说明 |
|---|---|
| 课程定位 | 斯坦福大学开设的 AI 专题课程,聚焦自我改进 AI 智能体 |
| 核心技术线 | 推理(Reasoning)、搜索与规划(Search & Planning)、强化学习(RL) |
| 学习形式 | 公开讲义与视频、编程作业、项目实践,具体以课程官方发布为准 |
| 前置要求 | Python、PyTorch 基础,熟悉 Transformer 结构和基本微调流程 |
| 运行环境 | 有 NVIDIA GPU 最理想;没有本地 GPU 可用 Colab / Kaggle 等云环境替代 |
| 核心产出 | 理解 Agent “生成->评估->优化” 的完整链路,掌握可迁移的训练实验方法 |
再看一张概念速览表。这些概念会反复出现在课程材料和作业里,提前建立印象会让后续学习顺很多。
| 核心概念 | 一句话解释 | 在课程链路里的位置 |
|---|---|---|
| CoT(思维链) | 让模型在给出答案前先输出推理过程 | 推理模块的起点 |
| verifier / reward model | 给模型输出打分的模块,是搜索和 RL 的“裁判” | 贯穿推理、搜索、强化学习 |
| self-correction | 让模型检查自己的答案并修正 | 推理模块的关键能力 |
| best-of-n / beam search | 在生成阶段保留多个候选再筛选 | 搜索模块的代表方法 |
| MCTS(蒙特卡洛树搜索) | 通过模拟和回传选择最优动作路径 | 规划与决策模块 |
| DPO / GRPO | 不需要单独训练奖励模型的偏好优化方法 | 强化学习模块的轻量入口 |
| tool use / 环境反馈 | Agent 调用外部工具或执行代码获得真实反馈 | 应用与部署层 |
从课程结构看,这门课不是在讲单点技巧,而是反复围绕一个循环:模型先产生输出,再通过某种信号判断好坏,最后把这些判断转成下一次改进的依据。下面几个章节会按照这条主线展开。
2. 这门课适合谁,不适合谁
先说适合的人群。
第一类是正在做 Agent 应用开发的工程师。很多人已经能熟练使用 LangChain、AutoGPT 这类框架,但对模型为什么失败、如何把失败变成训练信号缺乏系统理解。CS329A 正好补上这一层:你会学到如何定义验证信号、如何构造偏好数据、如何用搜索和强化学习提升模型的稳定性。
第二类是想从 Prompting 转向模型训练的开发者。如果你已经会调用模型 API,但还没碰过微调、偏好优化和强化学习,这门课是一个完整的升级路径。它不会只让你“跑通一个 DPO 脚本”,而是让你理解 DPO 在整个自我改进闭环中的位置。
第三类是算法方向的学生和研究员。课程会涉及大量文献和实验设计,对梳理“推理增强”“测试时搜索”“偏好优化”这几条研究线非常有帮助。
不太适合的情况也很明确。如果你只是想快速上线一个 Agent 产品,这门课偏原理和实验,不是框架使用手册,短期 ROI 不高。如果你是零基础,连 Python 和机器学习基础都比较薄弱,建议先补完基础再回来。另外,如果完全没有 GPU 而且也不愿意用云端环境,动手环节会非常受限,光看讲义很难建立体感。
关于使用边界,有几件事必须提前说明。课程讲义、视频和作业材料版权归课程方所有,请通过官方渠道获取,不要二次分发。实验阶段如果用公开数据集或 API 生成合成数据,要检查数据授权和厂商使用政策。本地方案可以处理私有数据,但一旦把数据发给云端 API,就默认接受该平台的数据使用条款。最后,在认知边界上也要冷静:自我改进不是“模型自己突然变强”,而是“在明确反馈信号下不断优化”。如果信号设计有漏洞,模型会找到你没想到的捷径,也就是后面会说到的 reward hacking。
3. 环境准备与前置条件
3.1 前置知识清单
建议在上手前确认自己具备以下基础,不需要全部精通,但至少要熟悉:
- Python 工程能力:会写脚本、处理 JSON、做简单的多线程和异常处理。
- 机器学习基础:理解损失函数、梯度下降、过拟合这些概念。
- LLM 基础:了解 Transformer 结构、pre-training 和 fine-tuning 的区别,知道 RLHF 的大致流程。
- 数学基础:主要是概率论和线性代数,计算量不会很大,但概念要能看懂。
如果这些还不熟,先不要急着刷课程,把 Python 和 Prompt 基础打牢会更高效。
3.2 硬件与系统要求
从课程实验的常见需求看,推理实验用普通 NVIDIA GPU 就能起步。进入微调和强化学习阶段后,显存会更吃紧;更稳妥的起点是 8GB 以上显存,实际以你本机情况为准。没有本地 GPU 的话,Colab 的免费 T4、Kaggle 的 GPU 环境都是可行的替代方案。
操作系统方面,Windows 建议用 WSL2 跑 Linux 环境,macOS 可以跑推理和小规模训练,但真正做 RL 实验还是会受限于显卡。模型大小和显存的关系,建议遵循一个原则:消费级显卡优先从 7B 以下模型开始,用 LoRA / QLoRA 做微调实验。
3.3 软件环境搭建
下面给一套通用实验环境。这不是课程指定的环境,而是覆盖了推理、微调、RL 和 API 调用所需的最小工具链。
conda create -n cs329a python=3.10 -y conda activate cs329a pip install torch transformers datasets accelerate peft trl pip install vllm openai pandas装完以后,可以用下面两条命令确认 GPU 和磁盘状态。
nvidia-smi # 查看显卡型号和当前显存占用 df -h # 查看剩余磁盘空间,微调和模型下载都需要空间另外准备一个模型目录和一个日志目录,后续实验的数据、模型权重、日志分开存放,避免根目录堆成一团。
4. 把课程主线拆成四个可执行阶段
CS329A 的知识体系可以粗略拆成四个阶段。每个阶段都对应一个可以独立完成的实验,建议按顺序走。
4.1 阶段一:理解推理,先学会观察模型“在想什么”
自我改进的第一步,不是调参,而是观察模型在什么情况下会答错、为什么答错。思维链(CoT)是最直接的观察窗口:让模型先输出推理过程,再给出最终答案。一旦推理过程可读,你就能区分“算错了”和“推理路径本身就是错的”。
更进一步,self-consistency 的思路是同一道题多次采样,然后做多数投票;self-correction 则是在模型给出答案后,再让模型自己检查一遍。这些方法不需要训练新的模型,只靠采样和验证就能提升效果,是理解自我改进最直观的入口。
下面是一个通用示例脚本,目的是观察候选答案的多样性:
from transformers import pipeline # 通用示例:用一个小模型生成多个候选答案 gen = pipeline("text-generation", model="Qwen/Qwen2.5-7B-Instruct", device=0) def sample_with_cot(prompt: str, n: int = 4) -> list[str]: outputs = [] for _ in range(n): res = gen( prompt + "\n请先写出推理过程,再给出最终答案。", max_new_tokens=512 )[0]["generated_text"] outputs.append(res) return outputs prompt = "小明有12个苹果,吃掉3个后又买来5个,现在有几个?" candidates = sample_with_cot(prompt, n=4) for i, c in enumerate(candidates): print(f"候选{i + 1}:\n{c}\n---")这个实验的目的不是刷分,而是让你直观感受“采样温度”“候选数量”“提示词约束”对输出分布的影响。能把这一步做好,后面构造训练数据会顺手很多。
4.2 阶段二:搜索与规划,把采样本身变成算法
当“让模型生成一个答案”变成“让模型生成很多答案再选一个最好的”,搜索就进来了。
这一阶段需要掌握的几种方法,可以放在一起对比:
| 方法 | 核心思想 | 并行友好度 | 典型场景 |
|---|---|---|---|
| best-of-n | 生成 n 条候选,用验证器选最优 | 高 | 一次生成多条答案再打分 |
| beam search | 解码时逐步保留 top-k 路径 | 中 | 受限解码、翻译、结构化生成 |
| MCTS | 用模拟和回传选择最优动作路径 | 中 | 代码生成、游戏、规划类任务 |
| self-consistency | 多次采样 + 多数投票 | 高 | 数学推理、事实类问答 |
重点理解 best-of-n,因为它是最容易落地的搜索改进手段。它的核心逻辑是:不改变模型权重,只通过“多次采样 + 验证器筛选”就能显著提升正确率。这在课程里通常被当作一个基础实验,也是后面构造偏好数据集的原料。
这一阶段要注意成本和延迟。搜索空间变大,验证器每跑一次都是在花钱花时间。控制候选数量、限制采样长度、对验证结果做缓存,都是必要的工程手段。
4.3 阶段三:从数据到偏好优化,把“对错”变成训练信号
搜索能在推理阶段提升效果,但模型本身没有变强。要让模型后续直接生成更好的答案,需要把搜索得到的高质量结果变成训练数据。
这一步的完整路径是:收集轨迹数据 -> 筛选出高质量样本 -> 做 SFT(监督微调)让模型模仿好行为。在此基础上,再引入偏好优化。DPO 的核心是用偏好对(chosen / rejected)直接优化策略,不必单独训练奖励模型;GRPO 则在 RL 过程中用组内相对奖励来减少方差,是 DeepSeek-R1 这类公开模型采用的轻量化方案。DPO 和 GRPO 对实验者更友好,也是当前开源社区最常用的两个工具。
DPO 训练用 HuggingFace TRL 库可以写得很短:
from trl import DPOTrainer from transformers import AutoModelForCausalLM, AutoTokenizer model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct") tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B-Instruct") trainer = DPOTrainer( model=model, tokenizer=tokenizer, train_dataset=preference_dataset, # 需要自己构造 beta=0.1, max_length=1024, ) trainer.train()这里preference_dataset是关键,它需要由你自行构造,常见格式是prompt、chosen、rejected三个字段。整个流程中最花时间的往往不是训练本身,而是构造干净、可靠的偏好对。
4.4 阶段四:强化学习闭环,让 Agent 在交互中获得反馈并更新
最后一个阶段是把前面所有内容串成一个闭环:Agent 在环境中采取动作、获得奖励、更新策略、再行动,如此循环。
经典 RLHF 流程是“SFT -> 奖励模型 -> RL 策略优化”。奖励模型给任意输出打一个分数,策略模型在这个分数的引导下持续更新。但这个流程成本高、实现复杂。看现在业界的轻量实践,用规则奖励做强化学习是更现实的做法:比如数学题,答案和标准结果一致就给正奖励,格式不满足就给负奖励。DeepSeek-R1 公开披露的思路里,就是用格式奖励加答案正确性奖励来驱动推理能力的提升。
一个最小强化学习交互循环可以抽象成下面这样:
for step in range(num_steps): action = policy.generate(state) # 策略模型采样动作 reward = env.compute_reward(action) # 规则或模型打分 memory.append((state, action, reward)) policy.update(memory) # 用 DPO / GRPO 等方式更新策略这个阶段的重点不是写复杂的算法,而是理解三个组件的关系:策略模型负责生成、环境负责反馈、更新算法负责把反馈转成参数变化。任何一个环节有偏差,结果都会变得诡异,最典型的就是 reward hacking——模型找到一种“奖励很高但实际很蠢”的输出,直接把你辛苦设计的训练流程带偏。
5. 实战:搭一个最小的自我改进循环
理论讲完,下面给一个可以直接在本地跑起来的最小闭环。整个流程用“生成 -> 验证 -> 筛选 -> 微调 -> 迭代”五个步骤串联,先小规模跑通,再考虑放大。
5.1 实验设计
选一个带标准答案的任务,数学推理是最合适的,因为验证信号可以写成规则而不是再请一个模型打分。这里用 GSM8K 数据集的部分样本作为示例,目的是演示流程,你可以换成任何带标准答案的私有数据集。
具体步骤:
- 准备带标准答案的测试问题。
- 用基础模型对每个问题采样 8 个候选答案。
- 用规则验证候选答案是否正确。
- 构造偏好对:正确候选作为 chosen,错误候选作为 rejected。
- 用 DPO 微调原模型。
- 在固定评估集上重测效果。
- 把微调后的模型当成新的基础模型,继续迭代。
5.2 候选生成与验证
下面的代码用 vLLM 做批量生成,用规则抽取最终答案并对比标准答案:
import random from datasets import load_dataset from vllm import LLM, SamplingParams # 示例数据:GSM8K 前300条 dataset = load_dataset("openai/gsm8k", "main", split="train[:300]") model = LLM(model="Qwen/Qwen2.5-7B-Instruct", tensor_parallel_size=1) sampling = SamplingParams(temperature=0.8, max_tokens=1024) def get_answer(text: str) -> str: # 只取最后一行作为最终答案,具体抽法按任务调整 return text.strip().splitlines()[-1] def verify(pred: str, gold: str) -> bool: return pred.replace(",", "").strip() == gold.replace(",", "").strip() chosen, rejected, prompts = [], [], [] for item in dataset.select(range(64)): prompt = item["question"] outputs = model.generate([prompt] * 8, sampling) cands = [o.outputs[0].text for o in outputs] ok = [c for c in cands if verify(get_answer(c), item["answer"])] bad = [c for c in cands if not verify(get_answer(c), item["answer"])] if ok and bad: prompts.append(prompt) chosen.append(ok[0]) rejected.append(bad[0])这里有几个容易踩的坑。第一,GSM8K 的答案里有逗号,直接==对比会误判,所以先去掉逗号再比。第二,模型输出可能包含大量解释文字,抽取最终答案的策略要提前看几批结果确认稳定。第三,候选全对或者全错的样本要丢掉,无法构成偏好对。
5.3 构造偏好数据集并微调
把上一阶段得到的chosen和rejected组成 DPO 数据集:
from datasets import Dataset dpo_data = Dataset.from_dict({ "prompt": prompts, "chosen": chosen, "rejected": rejected, }) dpo_data.save_to_disk("./data/dpo_example")然后跑 DPO 训练。先用默认参数跑一轮预览,确认数据格式没问题,再正式调参。LoRA 可以有效降低显存占用,配置如下:
from peft import LoraConfig from trl import DPOTrainer from transformers import AutoModelForCausalLM, AutoTokenizer model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct") tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B-Instruct") lora_config = LoraConfig( r=16, lora_alpha=32, target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], lora_dropout=0.05, ) trainer = DPOTrainer( model=model, ref_model=None, tokenizer=tokenizer, train_dataset=dpo_data, args=training_args, peft_config=lora_config, beta=0.1, ) trainer.train() trainer.save_model("./models/dpo_experiment_v1")5.4 评估与迭代
微调完不要只看 loss,要回到固定的评估集上重新测。评估集不能和构造偏好对的数据重合,否则测出来的是记忆不是泛化。可以记录以下几个指标:
- 正确答案比例提升多少。
- 生成长度是否异常变短或变长。
- 是否出现模型通过“偷懒格式”获得奖励的情况,例如只输出答案不输出推理过程。
一轮实验跑完,把效果合格的模型作为下一轮的基础模型。这样就是一个完整的自我改进循环:搜索或采样生成数据 -> 验证 -> 偏好优化 -> 评估 -> 再采样。课程里讨论的“自我改进”,本质上就是这条链路的不断迭代。
这里有两点得说清楚。第一,用公开数据集做实验没问题,但如果是商业项目,数据授权是第一关。第二,实验中可能不止精度提升,也可以观察输出分布变化,比如模型是否更爱输出结构化推理。最终判断标准应该来自你的实际业务场景,而不是单一测试集。
6. 把自我改进循环工程化:推理引擎、接口与批量任务
课程实验可以只跑脚本,但真实项目最终要落到“服务化”和“批量任务”上。自我改进循环里有两个高频工程环节:批量采样候选、批量验证结果。
6.1 用 vLLM 启动本地推理服务
本地批量生成场景下,vLLM 是性价比较高的选择。支持 OpenAI 兼容接口,也支持连续批处理,可以对并发采样做更好的资源利用。
vllm serve Qwen/Qwen2.5-7B-Instruct --port 8000启动后可以用 OpenAI SDK 直接访问本地接口:
from openai import OpenAI client = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="EMPTY") resp = client.chat.completions.create( model="Qwen/Qwen2.5-7B-Instruct", messages=[{"role": "user", "content": "1+1=?"}], n=4, ) for choice in resp.choices: print(choice.message.content)接口能跑通后,就可以把验证、筛选、构造偏好数据这些逻辑接到这个接口上,形成稳定的数据生产管线。
6.2 批量任务队列与失败重试
批量采样的关键不是“循环里调接口”,而是“并发控制 + 失败重试 + 结果落盘”。一个稳定做法是把任务先写进队列,再并发消费,失败重试指数退避。
import time from concurrent.futures import ThreadPoolExecutor def run_inference(task: dict) -> dict: # 调用本地或远程接口,返回生成结果 response = client.chat.completions.create( model="Qwen/Qwen2.5-7B-Instruct", messages=[{"role": "user", "content": task["prompt"]}], n=4, ) return {"id": task["id"], "outputs": [c.message.content for c in response.choices]} def process_with_retry(task: dict, retries: int = 3) -> dict | None: for attempt in range(retries): try: return run_inference(task) except Exception as e: print(f"task {task['id']} failed: {e}, retry {attempt + 1}") time.sleep(2 ** attempt) return None with ThreadPoolExecutor(max_workers=8) as pool: results = list(pool.map(process_with_retry, task_list))工程上还有一些细节值得注意:每条结果要带 task id,方便失败后定位;输出结果要按批次落盘,避免中途断掉丢失全部进度;验证器在并发场景下要保证可重入、无状态。
7. 资源占用与性能观察
自我改进实验的资源消耗差异很大,推理阶段和训练阶段要分开看。
推理阶段,主要观察 GPU 显存、吞吐和延迟。用 vLLM 做推理时,max_model_len、并发数、采样温度都会影响资源占用。候选数量和 batch size 越大,显存压力越大,但吞吐不一定线性变化,需要实测才能找到当前硬件条件下收益最高的并发数。观察命令可以一直挂着:
watch -n 1 nvidia-smi训练阶段,普通消费级显卡建议从 7B 以下模型开始,配合 LoRA 或 QLoRA 控制显存。如果需要进一步压缩,可以打开 gradient checkpointing、降低 batch size、使用 4bit 量化加载基础模型。需要注意“能跑起来”和“稳定训练”是两回事,量化会带来一定的精度损失,需要实验验证。
降低资源占用的通用手段按优先级排:先用更小的模型验证流程,再逐步放大;训练阶段优先 LoRA,而不是全参数微调;推理阶段优先并发控制和缓存,而不是堆更多显卡。
8. 常见问题与排查方法
自我改进实验链路长,问题往往不在单点,而在多个环节的叠加。下面是按现象整理的排查表。
| 问题现象 | 可能原因 | 排查方式 | 解决思路 |
|---|---|---|---|
| GPU 显存不足 | 模型过大、batch 过大、并发过高 | 用 nvidia-smi 看实际占用 | 降低 batch、开梯度检查点、用 4bit 量化、换更小模型 |
| 微调后效果反而变差 | 偏好对噪声大、过拟合、评估集太小 | 对比训练前后固定评估集 | 清洗数据、加评估集、早停、降低学习率 |
| 候选答案全是错的 | 基础模型能力不足、提示词不够明确 | 人工抽样看输出 | 换更强模型、增加采样轮数、优化提示词 |
| API 调用频繁报错 | 触发限流、服务不稳定 | 查看状态码和错误日志 | 指数退避重试、控制并发、本地缓存 |
| 推理速度很慢 | 模型太大、并发排队、未用批量推理 | 看延迟和吞吐指标 | 上 vLLM、减小 max_model_len、限制并发 |
| 训练不收敛 | 学习率过高、偏好对冲突 | 观察 loss 和 reward 曲线 | 降低学习率、清洗偏好对、换 DPO 参数 beta |
| 出现 reward hacking | 奖励规则有漏洞 | 检查高奖励样本的内容 | 增加格式约束、使用更强验证器、人工复核 |
| 端口被占用 | 本地 vLLM 或其他服务占了 8000 | netstat / lsof 查端口 | 换端口启动 |
| 课程资料访问受限 | 网络或平台访问问题 | 确认官方发布渠道 | 以官方渠道为准,不要使用来路不明的二次分发 |
最容易翻车的三个点:偏好数据质量不过关导致 DPO 越训越差、评估集和训练数据重叠导致“假提升”、奖励规则有漏洞导致模型学会刷分。这三个问题都可以通过在流程里加入人工抽检和固定评估集来缓解。
9. 最佳实践与使用建议
把 CS329A 的路线落地到实际项目,有几点工程建议值得固化下来。
第一,先跑最小闭环再优化配方。第一次做自我改进实验,不要一上来就堆大模型、大数据。用 64 条样本、8 个候选、跑一轮 DPO,先把整条链路打通,再逐步扩大数据规模。
第二,始终保留一套固定评估集。评估集不参与训练,不参与偏好对构造。每次实验都跑同一套评估,才能对比不同配置的真实差异。
第三,模型、数据、日志分目录管理。建议按data/、models/、logs/、scripts/分目录存放,每个实验结果带上时间戳和配置说明。RL 实验的状态非常容易混乱,没有日志就无法复盘。
第四,批量任务要具备断点续跑能力。候选生成、验证、偏好构造都可能有偶发失败。任务落盘、带上 task id、失败重试,这些基础功夫比模型算法更能决定实验效率。
第五,数据与合规检查要前置。使用任何公开数据集、API 生成数据、他人版权内容做训练,都要先确认授权范围。涉及人脸、声音、私密信息的数据,不经过脱敏和授权确认不能进入实验流程。接口服务如果开放到局域网或公网,务必加访问控制,防止被滥用。
第六,对“自我改进”的边界保持清醒。强化学习能提升的能力上限,受限于验证信号的质量。如果验证器本身判不准,模型只会朝着错误方向越走越远。投入资源之前,先确认你的奖励信号可靠。
10. 总结与下一步
CS329A 最值得尝试的点,是它把推理、搜索、强化学习统一进了一个循环:生成 -> 评估 -> 优化。这和单纯刷榜或者套框架是完全不同的思路,学到的是可迁移的Agent训练方法论。
接触这门课,最先应该验证的是 4.1 节的候选多样性实验。它成本最低,也最直观:你会亲眼看到同一个模型、同一个问题,在不同采样条件下输出有多不稳定。理解了这一点,自然就会明白为什么需要搜索、需要训练信号。
最容易踩的坑是 reward hacking 和评估集污染,这两点几乎是所有自我改进实验的共性风险。如果刚开始跑 DPO 发现效果不升反降,先检查偏好对质量,再怀疑训练参数。
下一步的扩展方向可以分几步走:先把课程里的推理和搜索模块吃透,接着在数学或代码任务上复现一轮 DPO 实验,然后尝试 GRPO 或规则奖励做强化学习,最后把实验闭环接到真实业务环境里做灰度验证。如果对多智能体协作感兴趣,还可以在这个基础上继续关注“多个自改进 Agent 如何通过外部信号互相校准”的方向,但前提是先把单 Agent 的自改进链路走通。