十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

自我改进AI Agent实战:从概念到可运行代码闭环

自我改进AI Agent实战:从概念到可运行代码闭环 做 Agent 应用的同学应该都有过这种经历提示词精心写了好几版业务场景稍微一变效果立刻下降每次调试错误信息都要把报错反复粘贴到聊天窗口人工总结规律再手动更新提示词。整个过程既慢又非常依赖个人经验。更让人头疼的是这种“调一次跑一次”的方式并没有沉淀下来换一个任务又得从头再来。最近我在整理 Stanford CS329A 相关的学习资料时把 “Self-Improving AI Agents” 这条线单独抽了出来结合课程公开内容和自己跑过的一些小实验整理成了一份可以照着学的合集笔记。本文会从概念讲起再到核心机制拆解最后给出一个能运行的自我改进 Agent 示例。无论你是刚接触大模型应用开发还是已经在做 AI Agent 的工程落地这篇文章都能帮你理清“自我改进”到底是怎么发生的以及如何在自己的项目里复现这套闭环。1. 为什么要研究 Self-Improving AI Agents1.1 从“大模型应用”到“智能体应用”先理清一个基础问题普通的大模型应用和 AI Agent 有什么区别在普通的大模型应用中用户输入一段文本模型输出一段文本交互往往是一次性的。比如翻译、摘要、问答本质上都是“输入—输出”模式。即使你在系统提示词里写了大量规则模型也只是在做单轮生成。但 AI Agent智能体不一样。它需要面对一个目标自己拆解步骤调用外部工具观察执行结果再决定下一步动作。举例来说如果让一个 Agent 完成“自动修复代码中的 bug”这个任务它不能只生成一段修复建议而是要真正运行代码、读取报错信息、修改代码、再次运行直到测试通过。从这个角度看Agent 更像是一个“会行动的机器人”而不是一个“会聊天的模型”。而自我改进Self-Improving正是这类系统最核心的能力之一它不满足于“执行一次”而是希望从每一次执行中总结经验让下一次执行变得更好。1.2 固定提示词模式的局限很多团队做大模型应用时默认做法是“把提示词写到极致”。系统提示词里堆满规则、示例、少数样本希望模型能稳定输出。这种做法在简单场景下没问题但一旦任务变复杂就会暴露出三个问题。第一个问题是提示词的“上限”。模型上下文窗口虽然是有限的但即便你堆了很多例子模型也很难覆盖所有边界情况。任务越复杂规则之间越容易冲突提示词越长模型反而越容易忽略关键信息。第二个问题是人工维护成本高。每遇到一种新的错误类型就要手动把错误信息复制出来分析原因再补充到提示词里。这个过程是线性的业务增长越快人工负担越重。第三个问题是缺乏反馈闭环。固定提示词的系统不会因为一次执行失败而自动调整策略。同样的错误可能在真实场景中反复出现系统本身没有任何记忆。Self-Improving AI Agents 要解决的正是这几个问题让系统在执行中收集反馈把反馈转化为经验再把经验用于后续决策。1.3 不同类型的 Agent 评估方式在学习自我改进之前还需要理解一个前提自我改进必须有“信号”。没有信号系统就不知道自己做得好还是不好自然无法改进。常见的评估信号有三类。第一类是硬性信号比如测试用例是否通过、代码能否编译、接口是否返回 200、SQL 是否能成功执行。这类信号客观、准确适合作为自动改进的奖励。第二类是软性信号比如用户是否点赞、回答是否相关、任务耗时是否减少、模型输出是否符合某种格式。这类信号往往带有噪声但采集成本低可以辅助判断。第三类是业务指标比如转化率、留存率、任务完成率。这类信号离模型较远但最能反映真实价值。不过业务指标波动大直接用于 Prompt 优化容易出现滞后和过拟合问题。在设计自我改进 Agent 时第一步不是写代码而是明确“我拿什么作为改进信号”。信号选得好改进就是正向循环信号选不好改进就会变成盲目试错。2. 学习环境准备与工具链2.1 基础运行环境本文后续的示例代码是基于 Python 编写的所以先准备 Python 环境。建议使用 Python 3.10 及以上版本Python 3.11、3.12 都没有问题。你可以使用下面命令确认当前 Python 版本python --version如果你同时安装了多个 Python 版本建议先为当前项目创建虚拟环境避免依赖冲突。创建虚拟环境的命令如下python -m venv .venv创建完成后激活虚拟环境。在 Windows 下使用.venv\Scripts\activate在 macOS 或 Linux 下使用source .venv/bin/activate激活后终端提示符前面会出现(.venv)说明已经进入虚拟环境。2.2 依赖库版本说明本文示例需要用到两个核心依赖OpenAI Python SDK 和 Pydantic。前者用于调用大模型服务后者用于数据结构校验。你可以先创建一个requirements.txt文件内容如下openai1.0.0 pydantic2.0.0然后执行安装命令pip install -r requirements.txt需要说明的是具体版本号要根据你的项目实际情况调整。本文示例使用的是 OpenAI 兼容接口也就是只要模型服务商提供 OpenAI 格式的接口就可以复用同一套代码。如果你使用的是本地部署模型也可以通过 vLLM、Ollama、LM Studio 等工具暴露兼容接口然后把base_url指向本地地址。如果使用云服务商的模型请务必获得合法授权并遵守服务商的使用条款。2.3 示例项目结构为了让代码清晰可维护我把示例项目拆成几个文件self-improving-agent/ ├── agent_core.py # 模型调用封装 ├── code_runner.py # 执行被测代码 ├── memory.py # 经验库读写 ├── main.py # 主循环 ├── requirements.txt # 依赖清单 └── experience.json # 运行后生成的经验文件项目结构不复杂但每个文件职责明确。后面编写代码时我会逐一解释每个文件的作用。3. Self-Improving Agent 的核心机制拆解3.1 记忆模块经验从哪里来自我改进的第一个关键是记忆。Agent 需要把“发生过的事情”保存下来尤其是失败案例和修复经验。记忆可以分成三层。第一层是会话记忆保存当前任务上下文比如用户目标、历史消息、工具调用记录。这一层通常是模型上下文的一部分缺点是上下文窗口有限不能无限增长。第二层是长期记忆保存跨会话的经验。这一层可以是一个 JSON 文件、SQLite 数据库也可以是向量数据库。每次任务结束后Agent 会把有价值的结论写进去下次再遇到类似任务时就可以从长期记忆中检索相关内容。第三层是参数记忆也就是通过微调把经验写进模型权重。这一层成本最高通常只有在积累了大量高质量数据后才会考虑。对于大多数项目先做好前两层记忆就足够了。一个容易犯的错误是把所有历史记录都塞进记忆库结果经验越来越多真正有用的信息反而被淹没。更合理的做法是设置记忆上限只保留最近、最有价值的经验。3.2 评估与反馈信号没有信号就无法改进自我改进的第二个关键是评估。系统每执行完一轮动作都要拿到一个可量化的反馈才能决定下一步是继续尝试、换一种策略还是直接停止。在设计评估信号时有三个原则值得记住。第一信号要尽量客观。测试用例通过比模型自我感觉良好更可靠接口返回 200 比文字描述“成功了”更可信。客观信号可以自动化采集不需要人工介入。第二信号要及时。如果评估延迟太长Agent 就无法在有限轮次内快速调整。比如代码执行报错是一个即时信号而用户一周后才给出评价后者就不适合作为单轮改进的反馈。第三信号要能指导下一步动作。仅仅告诉模型“你错了”是没用的最好还能提供错误详情。比如AssertionError: expected 15 but got 10就比“测试失败”四个字有用得多。3.3 反思机制让模型学会总结错误“反思”是自我改进 Agent 中比较有辨识度的一环。简单来说反思就是让模型根据执行结果总结失败原因并提出下一步的修改方向。反思和普通重试的区别在于普通重试只是把同样的任务再发给模型期望模型碰巧生成一个正确答案反思则是先让模型分析错误再把分析结果作为新的上下文引导模型生成更合理的修复方案。下面给出一个简化版的反思提示词模板你可以根据自己的场景扩展REFLECTION_PROMPT 你是一个严谨的代码修复助手。 下面有一段代码以及代码运行后的结果。 请先分析失败原因再输出修复后的完整 Python 代码。 要求 1. 只输出代码不要输出多余解释。 2. 分析过程要简短但必须指出关键错误点。 3. 修复后的代码要能通过测试。 代码 {code} 运行结果 stdout: {stdout} stderr: {stderr} 这个提示词把“错误信息”和“修复要求”放在了一起。模型在生成代码前会先读一遍错误信息相当于把反思过程作为生成条件的一部分。实际项目中你可以把反思和分析拆成两步但核心思路是一样的把失败反馈变成可消费的上下文。3.4 更新策略改进什么、什么时候改自我改进的“改进”不是空泛的而是有具体作用对象。通常有四种更新目标。第一种是更新 Prompt。根据失败经验在系统提示词里补充一条规则比如“遇到 UnicodeDecodeError 时优先尝试 utf-8 编码”。这种更新成本低、见效快是大多数项目的第一步。第二种是更新记忆库。把出错场景、错误信息、修复结果保存到经验库中后续任务开始时先加载经验让模型参考历史做法。第三种是更新工具和代码。如果发现问题出在工具调用流程上比如某个工具参数传错、某个依赖版本不匹配就需要直接修改工具逻辑。这种更新比改 Prompt 更彻底但需要额外保障措施。第四种是更新模型本身也就是微调。微调能把经验固化到模型参数中效果最稳定但数据准备、训练、评估、上线的成本都不低适合长期投入的成熟项目。在实际工程中建议按照“先 Prompt、再记忆、再工具、最后微调”的顺序推进。每一步都要有对应的评估数据确认改进有效后再合并上线。4. 完整实战一个能自我修复代码的 Agent4.1 需求与设计下面我们来实现一个最小但完整的自我改进 Agent。这个 Agent 的任务是给定一个带有 bug 的 Python 函数自动运行测试读取报错信息反思问题生成修复代码然后再次运行测试直到通过。这里用到的“自我改进”主要体现在两个方面。第一Agent 每轮都会根据错误信息生成新的修复代码而不是盲目重试。第二每轮失败后错误信息和当前代码会被写入经验库后续任务可以复用这些历史经验。整体流程如下加载历史经验。构造初始代码和测试代码。执行代码运行测试。如果测试通过输出结果并结束。如果测试失败把错误信息发送给模型模型输出修复代码。将失败记录写入经验库。回到第 3 步直到达到最大轮次。4.2 模型调用封装首先编写agent_core.py用于封装大模型调用。这里使用 OpenAI 兼容接口方便替换成其他模型服务。# 文件路径agent_core.py from openai import OpenAI class LLMClient: 大模型调用封装。 这里使用 OpenAI 兼容接口方便对接不同模型服务。 使用时请根据实际情况配置 base_url 与 api_key。 def __init__( self, model: str gpt-4o-mini, base_url: str None, api_key: str None, ): if not api_key or api_key your-api-key: raise ValueError(请配置合法的 API Key) self.model model self.client OpenAI( base_urlbase_url, api_keyapi_key, ) def chat(self, messages: list, temperature: float 0.3, max_tokens: int 1024) - str: resp self.client.chat.completions.create( modelself.model, messagesmessages, temperaturetemperature, max_tokensmax_tokens, ) return resp.choices[0].message.content.strip()这段代码的逻辑比较简单但有两点需要说明。一是api_key不能为空否则直接报错避免等到真正请求时才暴露问题。二是temperature默认设为 0.3这是为了在修复代码时尽量稳定不鼓励模型过度发散。如果你希望模型更有探索性可以适当调高但通常不建议超过 0.7。4.3 执行代码并收集错误第二步编写code_runner.py负责在子进程中执行被测代码并捕获标准输出和标准错误。# 文件路径code_runner.py import subprocess import sys def run_code_with_test(code: str, test_code: str) - dict: 执行一段 Python 代码并返回运行结果。 参数 code: 被测函数代码片段。 test_code: 包含断言或打印的测试片段。 返回 包含 returncode、stdout、stderr 的字典。 full_code code \n\n test_code try: result subprocess.run( [sys.executable, -c, full_code], capture_outputTrue, textTrue, timeout10, ) return { returncode: result.returncode, stdout: result.stdout.strip(), stderr: result.stderr.strip(), } except subprocess.TimeoutExpired: return { returncode: -1, stdout: , stderr: 运行超时, }这里使用subprocess.run而不是在当前进程里直接执行原因有三个。第一被测代码可能包含语法错误直接exec会让主进程崩溃。第二被测代码可能进入死循环子进程可以通过 timeout 参数限制运行时间。第三子进程隔离了输出流方便我们收集 stdout 和 stderr。4.4 经验库读写第三步编写memory.py实现一个最简单的 JSON 经验库。# 文件路径memory.py import json from pathlib import Path EXPERIENCE_FILE Path(experience.json) def load_experience() - list: 加载历史经验列表。 if EXPERIENCE_FILE.exists(): return json.loads(EXPERIENCE_FILE.read_text(encodingutf-8)) return [] def save_experience(experiences: list): 保存经验列表到文件。 EXPERIENCE_FILE.write_text( json.dumps(experiences, ensure_asciiFalse, indent2), encodingutf-8, ) def append_experience(record: dict): 追加一条经验记录并限制最大数量。 experiences load_experience() experiences.append(record) # 只保留最近的 20 条避免经验库无限膨胀 save_experience(experiences[-20:])为什么限制为 20 条因为在没有向量检索的情况下经验库越大加载到提示词里的内容越多反而可能干扰模型判断。对于示例项目保留最近 20 条足够展示效果。真实项目建议使用向量数据库并按任务类型做索引。4.5 主循环反思、修复、重试最后编写main.py把前面的模块串起来。# 文件路径main.py import json from agent_core import LLMClient from code_runner import run_code_with_test from memory import append_experience, load_experience INITIAL_CODE def run(n): total 0 for i in range(n): total i return total TEST_CODE if __name__ __main__: assert run(5) 15 print(ok) SYSTEM_PROMPT 你是一个代码修复助手。用户会给你一段代码和运行结果。 你需要根据错误信息分析问题并输出修复后的完整 Python 代码。 只输出代码不要输出多余解释。 def clean_code(raw: str) - str: 清洗模型输出去掉 markdown 代码块标记。 if python in raw: return raw.split(python)[1].split()[0].strip() if in raw: return raw.split()[1].split()[0].strip() return raw.strip() def main(): # 1. 初始化模型客户端 client LLMClient(base_urlhttps://your-model-service.example.com, api_keyyour-api-key) # 2. 构造消息加载历史经验作为参考 messages [ {role: system, content: SYSTEM_PROMPT}, ] experience load_experience() if experience: messages.append({ role: system, content: 以下是历史修复经验可参考\n json.dumps(experience, ensure_asciiFalse), }) code INITIAL_CODE max_rounds 5 # 3. 开始自我改进循环 for round_idx in range(1, max_rounds 1): print(f Round {round_idx} ) result run_code_with_test(code, TEST_CODE) print(stdout:, result[stdout]) print(stderr:, result[stderr]) # 测试通过输出当前代码 if result[returncode] 0 and result[stderr] : print(测试通过当前代码) print(code) return # 测试失败把错误信息发送给模型 messages.append({ role: user, content: ( f代码\n{code}\n f标准输出{result[stdout]}\n f错误输出{result[stderr]} ), }) raw_answer client.chat(messages) new_code clean_code(raw_answer) messages.append({role: assistant, content: new_code}) # 记录经验 append_experience({ round: round_idx, error: result[stderr], code: new_code, }) code new_code print(达到最大轮次仍未通过测试。) print(最后一次代码) print(code) if __name__ __main__: main()这段代码有几个细节值得注意。第一messages列表一直在增长每一轮的错误信息和修复代码都作为历史上下文发送给模型。这样模型能看到自己的思考过程属于一种简单的“反思”。但如果轮次较多要小心上下文长度超限后面需要通过摘要或裁剪来控制。第二clean_code函数用来清洗模型输出。因为很多模型在输出代码时习惯加 markdown 代码块标记如果不清理直接执行会包含 python 这样的非法语法。第三每次失败都会调用append_experience把错误和修复代码写入经验库。这样即使进程结束经验也不会丢失下次运行时会作为参考上下文重新加载。4.6 运行与预期结果运行前你需要把main.py中的base_url和api_key换成自己可用的模型服务信息。然后执行python main.py预期的输出大致如下 Round 1 stdout: stderr: Traceback (most recent call last): File string, line 4, in module AssertionError Round 2 stdout: ok stderr: 测试通过当前代码 def run(n): return sum(range(n 1))第一轮执行时初始代码计算的是 01234 10与测试期望 15 不符所以抛出AssertionError。模型看到错误后会分析问题并输出修复代码。第二轮执行修复后的代码测试通过流程结束。这里要特别说明一下模型输出的“修复代码”不一定和示例完全一样。比如它可能改成return n * (n - 1) // 2 * ???这种错误公式也可能改成return sum(range(n 1))。只要测试能够通过具体实现并不重要。这正是 Agent 自我改进的特点通过外部信号驱动不限制内部实现方式。5. Agent 自我改进中的常见问题与排查思路5.1 常见问题汇总在实际运行这种自我改进 Agent 时会遇到不少问题。下面先把常见现象、原因和解决思路整理成一个表格方便快速查阅。问题现象常见原因解决思路多轮修复后测试仍不通过模型在同一个错误上反复打转增加更明确的反思提示或换更强模型上下文越来越长请求超时messages 列表无限增长压缩历史消息只保留最近几轮摘要模型输出包含 markdown 标记模型默认偏好输出代码块增加 clean_code 清洗逻辑经验库写入越来越慢每次都全量读写 JSON 文件改用 SQLite 或按日期分片存储Agent 调用工具执行了危险操作没有配置权限边界增加工具白名单和人工审批环节修复 A 问题导致 B 问题回归测试覆盖不足建立回归测试集每次修改后全量跑一遍5.2 循环不收敛最常见的现象是Agent 修了一个错误又引入一个新错误来回几次后测试仍然失败。这种情况往往是因为模型只看到当前错误信息没有理解整体代码结构。排查思路是增加一轮“反思摘要”。比如让模型在修复前先输出“失败原因分析”再根据分析输出代码。这样可以避免模型在没有深入理解的情况下直接改代码。另一个办法是给模型提供更多上下文比如完整的测试用例、被调用函数的签名甚至项目目录结构。如果仍然不收敛就要考虑是不是任务本身超出模型能力或者评估信号不够明确。比如测试用例本身就是错误的Agent 怎么修都过不了。5.3 上下文长度超限Agent 每轮都会把代码、错误信息、模型回复追加到 messages 中轮次一多上下文很容易超过模型限制。解决方案有三种。第一种是只保留最近两轮的错误信息和代码较早的内容不再发送。第二种是把历史信息压缩成摘要让模型回顾“之前做过了什么”。第三种是把关键信息写入经验库后续任务直接参考经验库而不是把所有原始调试过程都带上。5.4 成本快速上涨自我改进 Agent 每多尝试一轮就会多一次模型调用成本成倍增加。有些任务模型可能尝试十几次仍然失败成本完全失控。为了避免这种情况一定要设置最大轮次并且最好增加“成本预算”和“终止条件”。例如当连续两轮生成的代码完全相同且测试结果相同说明模型已经陷入重复应该提前终止而不是继续消耗 token。另一个控制成本的方法是使用小模型做反思和分类只在关键步骤使用大模型。比如先用小模型判断错误类型再决定是否调用大模型修复。6. 工程化落地从 Demo 到生产的建议6.1 建立可观测性Demo 阶段可以直接打印 stdout 和 stderr但生产环境必须有完整的日志和追踪体系。每次轮次开始、模型输入、模型输出、执行结果、经验写入都应该记录在案最好关联一个 request_id 或 trace_id。这样做的价值是当线上 Agent 出现异常时你可以快速定位是哪一轮决策出了问题。如果没有日志自我改进过程就会变成一个不可解释的黑盒出了问题也无法复盘。6.2 数据安全与最小权限如果一个 Agent 能自我改进这意味着它可能自动修改代码、写数据库、调用外部 API。在 Demo 里这没什么问题但在生产环境必须遵守最小权限原则。具体来说可以做到以下几点给 Agent 的工具设置白名单而不是开放所有工具。涉及写操作、删除操作时先进入人工审批流程。数据库操作在测试环境验证通过后才允许执行生产变更。所有修改都保留审计日志支持回滚。自我改进的前提是安全可控。没有安全边界的自动化往往会带来更大的事故。6.3 回归测试与环境隔离“修复一个 bug 引入另一个 bug”是所有自动修改系统面临的通病。为了降低风险应该为 Agent 建立一个回归测试集。回归测试不需要很复杂只需要覆盖最近几次失败案例即可。每次 Agent 生成新代码后不仅运行当前测试用例还要运行历史回归用例。只有全部通过才认为这次改进是有效的。此外建议在独立的沙箱环境中运行 Agent避免它直接操作生产环境。沙箱环境可以模拟真实的依赖和数据结构但不会造成不可逆影响。6.4 如何评估“自我改进”本身很多团队在接入 Agent 后只看任务完成率有没有提升。但任务完成率受模型版本、提示词、数据分布等多种因素影响并不完全代表改进有效。更好的做法是建立一个对比实验设定一个固定任务集记录不同版本 Agent 的通过率、平均轮次、平均耗时、平均成本。通过对比这些指标才能客观判断自我改进是否真的带来了收益。在实际项目中建议把评估任务集固化下来并长期维护。这是 Agent 工程化中最重要但最容易忽略的一环。7. 总结与后续学习路线到此我们已经完成了一个从概念到代码的 Self-Improving AI Agent 最小闭环。你可以从这份笔记中带走的几个关键点包括自我改进必须依赖明确的评估信号反思机制的核心是把失败信息转化为可消费的上下文记忆库能让经验在任务之间流动没有安全边界的自动修改在真实环境中是极度危险的。如果你想继续深入研究建议从三个方向扩展。第一个方向是读一些公开的 Agent 设计论文比如 Reflexion、Self-Refine、ReAct 等题目这些学术概念在课程和社区中被广泛讨论能帮助你建立更系统的认知框架。第二个方向是把这个示例代码扩展成多工具版本比如接入代码仓库、执行 shell 命令、读取日志文件让 Agent 真正处理复杂任务。第三个方向是建立自己的评估集把业务中最典型的 50 个失败场景记录下来用它们来衡量每一次改进的效果。如果你准备在自己的项目中落地自我改进 Agent我建议优先关注三点先定义可靠的评估信号再保证最小安全权限最后才考虑复杂的记忆和反思架构。先把闭环跑通再逐步增加能力。希望这份 Stanford CS329A 相关的合集学习笔记对你有帮助也欢迎你在实操后回来分享你的排错经验。
返回列表