
第2篇Vibe Coding时代LangGraph State 设计实战解决 Agent 多轮任务状态丢失和上下文混乱问题一、问题场景代码生成 Agent 第一轮正常第二轮就开始失忆在上一篇文章中我们已经搭建了一个最小可用的 LangChain LangGraph Coding Agent。但是当我把它改成多轮对话后很快遇到一个很真实的问题用户帮我生成一个 FastAPI 登录接口 AI请确认是否需要 JWT 用户需要 AI请问你想生成什么功能看到这个结果的时候我第一反应是模型是不是太笨后来排查发现不是模型的问题而是状态设计的问题。第一轮用户需求、模型追问、用户补充信息没有被正确沉淀到 State 里导致第二轮执行时Agent 只拿到了最新一句“需要”却丢掉了完整上下文。这类问题在 Vibe Coding Agent 中非常常见用户补充需求后Agent 忘记前文审查意见没有传回代码生成节点测试失败信息没有进入修复节点多轮对话只记得最后一句重试次数没有记录导致无限循环本文专门解决这个问题如何设计 LangGraph State让 AI Coding Agent 在多轮任务中不失忆、可恢复、可调试。二、真实问题为什么 Agent 状态比 Prompt 更重要很多人做 AI Agent 时第一反应是优化 Prompt。但是在复杂工作流里Prompt 只是能力的一部分。真正决定 Agent 稳定性的往往是 State。举一个例子。错误做法defcode_node(user_input:str):returnllm.invoke(f请根据需求写代码{user_input})这段代码只能处理单轮输入。如果用户后面补充登录接口要加 JWT模型并不知道之前要写的是 FastAPI 登录接口。正确做法是把完整上下文放入状态state{messages:[...],requirement:...,constraints:[...],review_result:...,test_result:...}节点从 State 读取信息再把新结果写回 State。这才是 Agent 工程化的基础。三、错误示例状态设计太随意很多 Demo 会这样写state{input:user_input,output:}短期没问题。但是你一旦加入这些功能需求分析代码生成单元测试代码审查工具调用人工确认多轮补充需求input/output这种设计就完全不够用了。最后你会开始临时加字段state[tmp]result state[last]review state[data]tool_result state[final]answer这类状态设计非常危险。因为你自己过两天都不知道tmp是哪个节点产生的last是上一次审查还是上一次代码data是工具结果还是模型结果final是最终答案还是最终代码Agent 状态不是垃圾桶不能什么都往里面塞。四、解决方案把 State 按业务阶段拆开一个 Coding Agent 的状态建议分成 7 类1. 对话类状态 2. 需求类状态 3. 计划类状态 4. 代码类状态 5. 审查类状态 6. 执行类状态 7. 控制类状态对应到代码里可以这样定义。创建state.pyfromtypingimportTypedDict,List,Dict,Any,OptionalclassMessageItem(TypedDict):role:strcontent:strclassRequirementInfo(TypedDict):raw:strnormalized:strconstraints:List[str]unclear_points:List[str]classCodeArtifact(TypedDict):filename:strcontent:strlanguage:strclassReviewInfo(TypedDict):status:strissues:List[str]suggestions:List[str]classCodingState(TypedDict):messages:List[MessageItem]requirement:RequirementInfo task_plan:List[str]code_artifacts:List[CodeArtifact]review:ReviewInfo test_result:strretry_count:intcurrent_step:strerrors:List[str]metadata:Dict[str,Any]这个版本比上一篇的 State 更复杂但更接近真实项目。五、字段解释为什么要这样拆1. messages保存对话上下文messages:List[MessageItem]用于保存用户与 Agent 的多轮交互。例如[{role:user,content:帮我写登录接口},{role:assistant,content:需要 JWT 吗},{role:user,content:需要}]注意不要只保存最后一句。多轮任务里最后一句往往没有完整含义。2. requirement保存结构化需求requirement:RequirementInfo包括raw原始需求 normalized整理后的需求 constraints约束条件 unclear_points不明确点这样做的好处是用户自然语言输入可以被整理成工程需求后续节点不用重复理解原始文本Prompt 输入更稳定方便做需求澄清3. code_artifacts保存多个文件真实代码生成不是只生成一段代码。通常会生成多个文件main.py schemas.py auth.py requirements.txt README.md所以不要只设计一个generated_code:str更推荐code_artifacts:List[CodeArtifact]每个文件一个对象后面写入磁盘、执行测试、代码审查都方便。4. review保存审查结构不要只保存一段自然语言review_result:str生产环境建议结构化{status:REJECTED,issues:[缺少密码哈希,异常处理不完整],suggestions:[使用 passlib,补充 401 返回]}这样条件边判断会稳定很多。六、初始化 State不要偷懒创建factory.pyfromstateimportCodingStatedefcreate_initial_state(user_input:str)-CodingState:return{messages:[{role:user,content:user_input,}],requirement:{raw:user_input,normalized:,constraints:[],unclear_points:[],},task_plan:[],code_artifacts:[],review:{status:PENDING,issues:[],suggestions:[],},test_result:,retry_count:0,current_step:init,errors:[],metadata:{trace_id:,user_id:,},}很多 Bug 就是因为状态字段没有初始化。错误写法state{messages:[]}后面访问state[review][status]直接报错。七、需求整理节点把自然语言变成工程需求创建chains.pyimportjsonfromlangchain_openaiimportChatOpenAIfromlangchain_core.promptsimportChatPromptTemplate llmChatOpenAI(modelgpt-4o-mini,temperature0.1)defnormalize_requirement(raw_requirement:str)-dict:promptChatPromptTemplate.from_messages([(system,你是一名资深需求分析工程师。请把用户自然语言需求整理成 JSON不要输出多余解释。),(user, 用户需求 {raw_requirement} 请输出 JSON {{ normalized: 整理后的工程需求, constraints: [约束1, 约束2], unclear_points: [不明确点1] }} )])chainprompt|llm responsechain.invoke({raw_requirement:raw_requirement})returnjson.loads(response.content)这里有一个注意点要求模型输出 JSON。如果让模型自由发挥后面解析会很难。八、LangGraph 节点写法创建graph.pyfromlanggraph.graphimportStateGraph,ENDfromstateimportCodingStatefromchainsimportnormalize_requirementdefrequirement_node(state:CodingState)-CodingState:state[current_step]requirementtry:resultnormalize_requirement(state[requirement][raw])state[requirement][normalized]result[normalized]state[requirement][constraints]result.get(constraints,[])state[requirement][unclear_points]result.get(unclear_points,[])exceptExceptionase:state[errors].append(f需求整理失败{str(e)})returnstatedefshould_clarify(state:CodingState)-str:unclear_pointsstate[requirement].get(unclear_points,[])iflen(unclear_points)0:returnclarifyreturncontinuedefclarify_node(state:CodingState)-CodingState:state[current_step]clarifypointsstate[requirement][unclear_points]question为了继续生成代码需要你补充以下信息\nforindex,pointinenumerate(points,start1):questionf{index}.{point}\nstate[messages].append({role:assistant,content:question,})returnstatedefplan_node(state:CodingState)-CodingState:state[current_step]planstate[task_plan][创建 FastAPI 应用入口,定义请求模型,实现登录接口,补充异常处理,提供运行方式,]returnstatedefbuild_graph():workflowStateGraph(CodingState)workflow.add_node(requirement,requirement_node)workflow.add_node(clarify,clarify_node)workflow.add_node(plan,plan_node)workflow.set_entry_point(requirement)workflow.add_conditional_edges(requirement,should_clarify,{clarify:clarify,continue:plan,})workflow.add_edge(clarify,END)workflow.add_edge(plan,END)returnworkflow.compile()九、运行测试创建app.pyfromfactoryimportcreate_initial_statefromgraphimportbuild_graphdefmain():appbuild_graph()statecreate_initial_state(帮我写一个登录接口)resultapp.invoke(state)print(当前步骤,result[current_step])print(整理后的需求,result[requirement][normalized])print(不明确点,result[requirement][unclear_points])print(消息列表,result[messages])if__name____main__:main()运行python app.py如果模型判断需求不明确会输出类似当前步骤clarify 不明确点[使用什么 Web 框架, 是否需要 JWT, 用户数据从哪里读取]如果需求清晰当前步骤plan 整理后的需求使用 FastAPI 实现登录接口支持 username/password 登录成功返回 token失败返回 401。十、踩坑记录State 里不要放不可序列化对象很多人会把数据库连接、文件句柄、模型对象放到 State 里。错误写法state[db]db_connection state[llm]llm state[file]open(test.txt)这会影响持久化恢复执行调试打印JSON 序列化分布式任务执行State 里建议只放可序列化数据str int float bool list dict None模型对象、数据库连接、客户端对象应该放在节点外部或依赖注入中。十一、踩坑记录不要在多个节点随意改同一个字段比如state[result]analyze_result state[result]code_result state[result]review_result这会导致你后面根本不知道result当前是什么。更推荐state[requirement][normalized]analyze_result state[code_artifacts]code_result state[review]review_result字段名要表达业务含义不要用data result tmp info output这些名字在 Agent 项目里很容易成为灾难。十二、适合收藏State 设计检查表1. 是否保存了完整对话 messages 2. 是否保存了原始需求 raw 3. 是否保存了整理后的结构化需求 4. 是否记录了任务计划 5. 是否支持多个代码文件 6. 是否记录了审查状态 7. 是否记录了测试结果 8. 是否有 retry_count 9. 是否有 current_step 10. 是否有 errors 11. State 中是否都是可序列化对象 12. 字段名是否能看出业务含义十三、避坑清单坑点表现解决方案只保存最后一句用户输入多轮对话失忆保存 messagesState 字段太少后续疯狂加临时字段按业务阶段设计使用 tmp/result/data后续无法维护使用语义化字段名State 放模型对象持久化失败只放可序列化数据审查结果是自然语言条件判断不稳定改成结构化 JSON未记录 current_step无法排查流程每个节点更新步骤未记录 errors异常丢失所有异常写入 errors十四、总结这篇文章重点解决了 Agent 状态设计问题。我的经验是Agent 的稳定性三分靠 Prompt七分靠 State 和流程。如果 State 设计得好后面加代码生成、测试执行、工具调用、人工确认都会比较顺。如果 State 一开始就乱后面越写越难维护。建议你从第一版就把 State 当成核心工程设计而不是临时变量容器。