
1. 为什么“能跑”的代码生成 Agent 远远不够代码生成这件事很多人第一反应是“让大模型写一段代码不就行了”。我一开始也这么想直到真正把它放进一个需要连续产出、需要保证正确性的工作流里才发现问题根本不在“能不能生成”而在“生成错了之后怎么办”。一个只会一次性输出的代码生成 Agent本质上就是个高级自动补全它没有回头路也没有自我怀疑的能力。而自我修正这个能力才是把玩具级 Demo 和真正能用的工程工具区分开的那条线。LangGraph 这个框架最吸引我的地方就是它把 Agent 的执行过程显式地建模成了一张状态图。节点是动作边是流转条件整个循环、分支、回退都能被画出来、被调试、被复现。这跟那种“把一堆工具塞给模型然后祈祷它别乱来”的做法完全是两个思路。用 LangGraph 做自我修正的代码生成 Agent核心逻辑其实很朴素生成代码 → 执行/检查 → 如果失败把错误信息喂回去 → 重新生成 → 再检查直到通过或者达到重试上限。听起来简单但真正落地的时候状态怎么设计、错误怎么传递、循环怎么终止、什么情况下该放弃每一步都有坑。这篇文章适合两类人看。一类是已经用过 LangChain、Dify、CrewAI 这类框架想进一步搞清楚“Agent 编排”到底该怎么做的开发者另一类是正在做代码生成相关项目被“生成结果不稳定”折磨过的工程师。我会把整个 Agent 的架构设计、状态定义、节点实现、循环控制、错误处理全部拆开讲代码可以直接抄参数选择的理由我也会说清楚。读完之后你应该能自己搭一个能自我纠错的代码生成 Agent而不是只会调 API 的那种。2. 整体架构设计与 LangGraph 选型思路2.1 为什么是 LangGraph 而不是普通 Chain普通 Chain 是线性的A 完了到 BB 完了到 C中间没有回头路。但自我修正天然就是一个带环的图生成节点可能回到自己检查节点可能把流程打回生成节点。用 Chain 硬写循环代码会变成一堆 while 嵌套状态管理全靠全局变量调试的时候根本不知道当前在哪一步。LangGraph 把这种循环显式地表达成图的边每个节点只负责自己的逻辑状态通过一个共享的 State 对象流转谁改了状态、改了什么一目了然。我实测下来LangGraph 相比自己手写状态机最大的优势是可观测性。你可以把整个图编译出来看到每个节点的输入输出出问题的时候能精确定位是哪个节点、哪次循环出的错。这在调试“为什么 Agent 改了三次还是错的”这种问题时价值巨大。2.2 自我修正循环的核心设计整个 Agent 的骨架我设计成四个核心节点形成一个闭环generate 节点根据任务描述和已有的错误反馈生成或修正代码execute 节点实际运行代码捕获输出和异常evaluate 节点判断执行结果是否满足要求决定是继续修正还是结束finalize 节点输出最终结果清理状态流转逻辑是这样的generate → execute → evaluateevaluate 如果判定通过就进 finalize 结束如果判定失败就带着错误信息回到 generate形成修正循环。这里有个关键设计决策错误信息不是简单丢弃而是结构化地存进 State包括错误类型、错误消息、出错的行号、上一次的代码版本。这样 generate 节点在修正时能看到完整的上下文而不是盲目重试。提示循环一定要有终止条件。我见过有人忘了设重试上限结果 Agent 在两个错误之间反复横跳烧了一堆 token 还没结果。重试上限建议设在 3 到 5 次之间超过就强制结束并返回当前最优结果。2.3 状态对象的设计原则State 是整个 Agent 的血液。我用的 State 结构大概长这样任务描述、当前代码、执行结果、错误列表、重试次数、历史版本。这里有个经验历史版本一定要存。因为有时候 Agent 改着改着反而改坏了保留历史能让你回退到之前较好的版本。另外错误列表用列表而不是单个字符串因为一次执行可能报多个错全部保留能让修正更精准。状态设计还有一个容易忽略的点区分“可修正错误”和“致命错误”。比如语法错误、逻辑错误是可修正的但如果是环境问题、依赖缺失那再怎么改代码也没用这时候应该直接终止而不是浪费重试次数。我在 State 里加了一个 error_type 字段来做这个区分。3. 核心节点实现与关键细节拆解3.1 generate 节点让模型带着“记忆”修正generate 节点是整个 Agent 的大脑。它的输入不只是原始任务还包括上一轮的错误信息和历史代码。这里有个关键技巧提示词的结构决定了修正的质量。我试过直接把错误信息拼在任务后面效果一般后来改成结构化的提示模板明确告诉模型“这是你上次生成的代码”“这是执行时报的错误”“请针对性地修正不要重写整个逻辑”修正成功率明显提升。提示词模板我大致这么组织先给任务描述再给当前代码然后给错误详情最后给修正要求。修正要求里我会强调“只改必要的部分”“保持原有正确的逻辑”“如果错误信息不明确先分析可能的原因”。这样能避免模型每次修正都大改一通把本来对的地方也改错了。代码实现上generate 节点就是一个函数接收 State调用模型返回更新后的 State。用 LangGraph 的话节点函数签名是固定的返回一个字典表示要更新的状态字段。这里要注意只更新变化的字段不要整个 State 覆盖否则容易丢数据。3.2 execute 节点安全地运行不可信的代码execute 节点是最危险的一环因为你要运行的是模型生成的、未经审查的代码。我踩过的坑早期直接在本地环境 exec 生成的代码结果有一次模型生成了删除文件的代码差点出事。后来我做了两层防护一是沙箱隔离用子进程加资源限制运行二是静态检查在运行前先扫描危险操作。具体做法是用 subprocess 起一个独立进程设置超时时间我一般设 10 秒限制内存和 CPU。代码通过临时文件传递执行结果通过标准输出和标准错误捕获。超时或者异常都算执行失败把错误信息结构化后存进 State。注意千万不要在主进程里直接 exec 或 eval 模型生成的代码。哪怕你觉得模型很可靠也要假设它随时可能生成危险代码。这是安全底线。执行结果我分成三类成功退出码 0 且有预期输出、运行错误抛异常或非零退出码、超时。不同类型的错误后续 evaluate 节点的处理策略不一样。3.3 evaluate 节点判断“改好了没有”evaluate 节点负责做决策这次执行结果算不算通过如果不通过是继续修正还是放弃这个节点的逻辑看似简单其实最容易出问题。我一开始用“退出码为 0 就算通过”结果发现很多逻辑错误代码能正常退出但结果是错的。后来改成多维度判断退出码、输出内容是否符合预期、是否有异常、是否超时。判断逻辑我写成规则加模型判断的混合模式。规则部分处理明确的成功/失败比如退出码非零直接判失败模型判断部分处理模糊情况比如输出内容对不对。这样既快又准。evaluate 节点还要负责更新重试次数如果超过上限直接标记为“放弃修正”流转到 finalize。这里有个细节evaluate 要能区分“这次比上次好”还是“这次比上次差”。如果连续两次修正都没进步说明方向错了应该提前终止而不是耗到上限。我加了一个简单的进步检测比较当前错误和上次错误如果错误类型和位置都一样说明没改对可以提前结束。3.4 循环控制与终止条件循环控制是自我修正 Agent 的命门。除了重试上限我还加了几个终止条件连续两次错误相同、修正后代码长度异常增长说明模型在乱改、执行时间超过总预算。这些条件组合起来能有效防止 Agent 陷入死循环。LangGraph 里用条件边来实现这个逻辑。evaluate 节点返回一个标记条件边根据这个标记决定走 finalize 还是回 generate。条件函数要写得清晰每个分支对应什么情况要注释明白不然过两周自己都看不懂。4. 完整实操流程与可复现方案4.1 环境准备与依赖安装先把环境搭起来。我用的 Python 3.10LangGraph 版本建议用较新的稳定版因为早期版本 API 变动比较大。核心依赖就几个langgraph、langchain、以及你要用的模型 SDK。安装命令很简单pip install langgraph langchain langchain-openai模型这块我用的是 OpenAI 兼容接口你也可以换成任何支持 function calling 的模型。这里提醒一句自我修正对模型的推理能力要求比较高太小的模型修正效果很差建议至少用中等规模以上的模型。4.2 定义 State 与图结构先定义 State。我用 TypedDict 来定义字段包括任务、代码、执行结果、错误列表、重试次数、历史。然后创建 StateGraph把四个节点加进去再定义边和条件边。from typing import TypedDict, List from langgraph.graph import StateGraph, END class CodeAgentState(TypedDict): task: str code: str exec_result: str errors: List[dict] retry_count: int history: List[str] status: str图结构就是 add_node 四个节点然后 add_edge 连 generate→execute→evaluateevaluate 用 add_conditional_edges 根据 status 决定去向。编译成 app 之后就能 invoke 了。4.3 generate 节点的完整实现generate 节点接收 State构造提示词调用模型解析出代码更新 State。提示词里我会把历史错误和当前代码都带上。解析模型输出时要注意模型有时候会在代码外面包 markdown 代码块要写个清理函数把python 和去掉。def generate_node(state: CodeAgentState): prompt build_prompt(state) response llm.invoke(prompt) code extract_code(response.content) return {code: code, retry_count: state[retry_count] 1}build_prompt 里我会根据 retry_count 判断是首次生成还是修正修正时把错误信息格式化进去。extract_code 用正则匹配代码块匹配不到就当纯文本处理。4.4 execute 节点的沙箱实现execute 节点把代码写到临时文件用 subprocess 运行捕获输出。超时用 timeout 参数控制。这里我把危险操作扫描也加进去用简单的关键词匹配拦截明显危险的调用。import subprocess, tempfile, os def execute_node(state: CodeAgentState): with tempfile.NamedTemporaryFile(modew, suffix.py, deleteFalse) as f: f.write(state[code]) path f.name try: result subprocess.run( [python, path], capture_outputTrue, textTrue, timeout10 ) output result.stdout result.stderr status success if result.returncode 0 else error except subprocess.TimeoutExpired: output 执行超时 status timeout finally: os.unlink(path) return {exec_result: output, status: status}4.5 evaluate 节点与条件边evaluate 节点根据 status 和输出内容判断是否通过。通过就设 status 为 done否则设 retry 或 give_up。条件边函数读 status 决定走向。def evaluate_node(state: CodeAgentState): if state[status] success and check_output(state[exec_result]): return {status: done} if state[retry_count] 5: return {status: give_up} return {status: retry} def route(state: CodeAgentState): if state[status] done: return finalize if state[status] give_up: return finalize return generate4.6 参数选择与调优记录重试上限我设的 5实测大部分任务 2 到 3 次就能修正成功5 次是兜底。超时设 10 秒因为代码生成任务一般不会跑太久超过基本是死循环。模型温度我设的 0.2修正任务需要稳定温度太高会乱改。这些参数不是拍脑袋定的是我跑了大概几十个测试任务后调出来的你可以根据自己的任务特点微调。5. 常见问题排查与避坑经验实录5.1 模型反复犯同一个错误怎么办这是最常见的问题。模型改了两三次错误还是同一个。原因通常是错误信息不够具体模型不知道到底哪里错了。解决办法是把错误信息拆得更细比如把 traceback 的最后一层单独提出来明确指出出错的行和变量。另外可以在提示词里加一句“请先分析错误原因再给出修正代码”强制模型先思考再动手。5.2 执行环境不一致导致的假失败有时候代码在模型“想象”的环境里能跑在你实际环境里跑不了比如缺依赖、版本不对。这种错误模型是修不了的因为它不知道你的环境。我的做法是在提示词里明确告诉模型可用的库和版本并且在 execute 前做依赖检查缺什么直接报给用户而不是让模型瞎改。5.3 修正后代码越改越乱模型有时候会把正确的逻辑也改掉。对策是在提示词里强调“最小改动原则”并且保留历史版本如果新版本比旧版本错误更多就回退。我在 evaluate 里加了版本对比逻辑新版本错误数超过旧版本就回退到旧版本。5.4 常见问题速查表问题现象可能原因解决思路反复同一错误错误信息不具体细化错误提取强制模型先分析假失败环境不一致提示词声明环境执行前检查依赖越改越乱改动范围过大强调最小改动保留历史版本死循环无终止条件设重试上限和进步检测执行卡死代码死循环设超时子进程隔离5.5 独家避坑技巧分享几个我踩坑总结的技巧。第一错误信息里不要带太多噪音traceback 很长但真正有用的就最后几行提取关键部分能显著提升修正效果。第二给模型一个“放弃”的选项有些任务模型确实做不了硬逼它改只会浪费时间允许它说“我无法修正”反而更高效。第三记录每次修正的 diff方便事后分析模型的行为模式我靠这个发现了模型在某些错误类型上特别容易犯同样的错针对性优化了提示词。6. 扩展方向与个人实践体会这套 Agent 跑通之后我做了几个扩展。一个是多文件代码生成把 State 里的 code 从字符串改成文件字典execute 时先写多个文件再运行。另一个是接入测试用例让 evaluate 节点跑单元测试而不是只看输出判断更准确。还有一个是并行生成多个候选让模型一次生成几个版本选最好的那个成功率能再提一截。我个人在实际操作中的体会是自我修正 Agent 的效果七分靠提示词设计三分靠框架。LangGraph 帮你把循环和状态管好了但真正决定修正质量的是你怎么把错误信息喂给模型、怎么引导它思考。我见过太多人把框架搭得很漂亮提示词却写得很随意结果 Agent 表现很差还怪框架不行。另外不要追求一次到位先让最简单的循环跑起来再逐步加错误分类、进步检测、版本回退这些优化边跑边调比一开始就设计一个完美架构要靠谱得多。最后再分享一个小技巧把每次 Agent 的完整执行轨迹存下来包括每轮的代码、错误、决策。跑一段时间后你会发现很多失败案例是有规律的针对这些规律优化提示词比盲目调参有效得多。这个习惯我坚持了几个月Agent 的成功率从最初的六成左右提到了九成以上。