纲要
- 项目背景与目标
- 系统架构与流程
- 多智能体协作模式
- Mermaid 流程图
- 核心实现
- 参考文档加载
- State 设计
- 节点实现
generate:生成代码解决方案code_check:导入检查与模拟执行reflect:反思与错误分析
- 条件边与重试控制
- 完整可运行代码
- 项目结构
- 依赖安装
- 主程序
code_assistant.py
- 运行测试与效果
- API 速览
- Demo 示例
- 参考文档
- 总结
- 润色纠正的前后对比
项目背景
在软件开发过程中,工程师经常需要查阅框架文档以编写符合规范的代码。借助 LangGraph,可以构建一个多智能体代码助手:该助手首先参考官方文档,生成包含必要导入语句和完整代码块的解决方案,随后自动检查代码的可执行性。若执行出错,则触发反思机制并重试,直至生成可运行的代码或达到最大尝试次数。
此助手本身是 LLM 应用开发的典型场景——将文档检索、代码生成、静态检查与错误修复组合成一个闭环工作流。
系统架构
工作流由三个主要节点构成,并通过条件边形成闭环:
generate:根据用户问题和参考文档,生成结构化的代码解决方案(包含描述、导入列表、代码块)。code_check:提取生成的代码,尝试动态执行导入语句和模拟运行,若出错则标记错误信息。reflect:对失败案例进行反思,分析错误原因,并将反思结果反馈给generate节点。
核心实现
参考文档加载
为使助手生成符合 LangChain LCEL 规范的代码,需从官方文档中提取相关知识。此处使用 WebBaseLoader 加载网页内容,并借助 BeautifulSoup 过滤出纯净文本。
注意:WebBaseLoader 在较新版本的 langchain-community 中可能已被弃用,推荐使用 AsyncHtmlLoader 或 BeautifulSoupTransformer 作为替代方案。若使用 WebBaseLoader,需确保 beautifulsoup4 和 lxml 已正确安装。
State 设计
工作流需要记录生成结果、错误信息及重试次数,State 定义如下:
from typing import TypedDict, Optional, List
from langchain_core.messages import BaseMessage
class CodeState(TypedDict):
messages: List[BaseMessage]
generation: Optional[dict]
error: str # "no" 表示无错误,"yes" 表示有错误
retry_count: int
max_retries: int
节点实现
生成节点
调用 LLM 生成结构化输出,包含 prefix(方案描述)、imports(导入语句列表)和 code(完整代码块)。当检测到上一轮存在错误时,自动将反思信息附加到提示中。
def generate_node(state: CodeState) -> dict:
print(">>> 生成代码方案...")
question = state["messages"][-1].content if state["messages"] else "如何创建LCEL链?"
reflection = ""
if state.get("error") == "yes":
# 从消息历史中提取最近的错误信息
for msg in reversed(state["messages"]):
if "执行错误" in msg.content or "反思" in msg.content:
reflection = msg.content
break
result = generate_chain.invoke({
"question": question,
"document": doc_text,
"format_instructions": parser.get_format_instructions(),
"reflection": reflection
})
new_generation = {
"prefix": result.prefix,
"imports": result.imports,
"code": result.code
}
print("生成的方案描述:", result.prefix)
return {
"generation": new_generation,
"retry_count": state.get("retry_count", 0) + 1,
"messages": state["messages"] + [AIMessage(content=result.prefix)]
}
代码检查节点
提取 generation 中的 imports 和 code 字段,动态执行导入语句并尝试模拟运行。捕获所有异常并记录到 error 字段。
def code_check_node(state: CodeState) -> dict:
print(">>> 检查代码...")
gen = state.get("generation")
if not gen:
return {
"error": "yes",
"messages": state["messages"] + [AIMessage(content="无代码可检查")]
}
code = gen["code"]
imports = gen.get("imports", "")
try:
# 逐条执行导入语句
for imp in imports.replace(";", "\n").split("\n"):
imp = imp.strip()
if imp and (imp.startswith("import") or imp.startswith("from")):
exec(imp, {})
# 编译代码以检查语法
compile(code, "<ast>", "exec")
# 模拟执行
local_env = {}
exec(code, {}, local_env)
print("代码检查通过")
return {"error": "no"}
except Exception as e:
err_msg = f"{e}\n{traceback.format_exc()}"
print(f"代码检查失败:{err_msg}")
return {
"error": "yes",
"messages": state["messages"] + [AIMessage(content=f"执行错误:{err_msg}")]
}
反思节点
使用 LLM 分析代码与错误信息,生成改进建议,供下一轮生成参考。
def reflect_node(state: CodeState) -> dict:
print(">>> 反思错误...")
gen = state.get("generation")
code = gen["code"] if gen else ""
# 提取最近一条错误信息
last_error = "未知错误"
for msg in reversed(state["messages"]):
if "执行错误" in msg.content:
last_error = msg.content
break
reflection = reflect_chain.invoke({"code": code, "error": last_error})
print("反思建议:", reflection.content)
return {
"messages": state["messages"] + [AIMessage(content=f"反思:{reflection.content}")]
}
条件边与重试控制
根据 error 字段与 retry_count 决定下一步路由。若无错误或已达最大重试次数则终止,否则进入 reflect 节点。
def should_continue(state: CodeState) -> str:
if state.get("error") == "no" or state.get("retry_count", 0) >= state.get("max_retries", 3):
return "end"
return "reflect"
图构建
builder = StateGraph(CodeState)
builder.add_node("generate", generate_node)
builder.add_node("check", code_check_node)
builder.add_node("reflect", reflect_node)
builder.set_entry_point("generate")
builder.add_edge("generate", "check")
builder.add_conditional_edges("check", should_continue, {
"reflect": "reflect",
"end": END
})
builder.add_edge("reflect", "generate")
memory = MemorySaver()
app = builder.compile(checkpointer=memory)
完整可运行代码
项目结构
code_assistant_demo/
├── code_assistant.py
├── .env
└── requirements.txt
依赖安装
pip install langgraph langchain-openai langchain-community python-dotenv beautifulsoup4 lxml
环境配置 .env
OPENAI_API_KEY=sk-xxxx
LANGCHAIN_TRACING_V2=false
主程序 code_assistant.py
import os
import traceback
from typing import TypedDict, Optional, List
from dotenv import load_dotenv
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
from langchain_community.document_loaders import WebBaseLoader
from langchain_core.messages import HumanMessage, AIMessage, BaseMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
load_dotenv()
# ----- 1. 加载参考文档 -----
url = "https://python.langchain.com/docs/how_to/lcel_cheatsheet/"
loader = WebBaseLoader(url)
docs = loader.load()
doc_text = "\n".join([d.page_content for d in docs])[:5000]
# ----- 2. 定义结构化输出模型 -----
class CodeSolution(BaseModel):
prefix: str = Field(description="解决方案的文字描述")
imports: str = Field(description="需要的导入语句,多条用分号隔开或分行")
code: str = Field(description="完整的可执行代码块")
parser = PydanticOutputParser(pydantic_object=CodeSolution)
# ----- 3. 定义State -----
class CodeState(TypedDict):
messages: List[BaseMessage]
generation: Optional[dict]
error: str
retry_count: int
max_retries: int
# ----- 4. 创建LLM和生成链 -----
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
generate_prompt = ChatPromptTemplate.from_messages([
("system", """你是一位精通LangChain的编程助手。根据提供的文档内容回答用户问题。
确保生成的代码完整可执行,包含必要的导入语句。
{format_instructions}
文档内容:
{document}
反思信息(如有):{reflection}"""),
("human", "{question}")
])
generate_chain = generate_prompt | llm | parser
reflect_prompt = ChatPromptTemplate.from_messages([
("system", "你是一位代码审查专家。分析以下代码和错误,给出简短的改进建议(中文)。"),
("human", "代码:{code}\n错误:{error}\n建议:")
])
reflect_chain = reflect_prompt | llm
# ----- 5. 节点函数 -----
def generate_node(state: CodeState) -> dict:
print(">>> 生成代码方案...")
question = state["messages"][-1].content if state["messages"] else "如何创建LCEL链?"
reflection = ""
if state.get("error") == "yes":
for msg in reversed(state["messages"]):
if "执行错误" in msg.content or "反思" in msg.content:
reflection = msg.content
break
result = generate_chain.invoke({
"question": question,
"document": doc_text,
"format_instructions": parser.get_format_instructions(),
"reflection": reflection
})
new_generation = {
"prefix": result.prefix,
"imports": result.imports,
"code": result.code
}
print("生成的方案描述:", result.prefix)
return {
"generation": new_generation,
"retry_count": state.get("retry_count", 0) + 1,
"messages": state["messages"] + [AIMessage(content=result.prefix)]
}
def code_check_node(state: CodeState) -> dict:
print(">>> 检查代码...")
gen = state.get("generation")
if not gen:
return {
"error": "yes",
"messages": state["messages"] + [AIMessage(content="无代码可检查")]
}
code = gen["code"]
imports = gen.get("imports", "")
try:
for imp in imports.replace(";", "\n").split("\n"):
imp = imp.strip()
if imp and (imp.startswith("import") or imp.startswith("from")):
exec(imp, {})
compile(code, "<ast>", "exec")
local_env = {}
exec(code, {}, local_env)
print("代码检查通过")
return {"error": "no"}
except Exception as e:
err_msg = f"{e}\n{traceback.format_exc()}"
print(f"代码检查失败:{err_msg}")
return {
"error": "yes",
"messages": state["messages"] + [AIMessage(content=f"执行错误:{err_msg}")]
}
def reflect_node(state: CodeState) -> dict:
print(">>> 反思错误...")
gen = state.get("generation")
code = gen["code"] if gen else ""
last_error = "未知错误"
for msg in reversed(state["messages"]):
if "执行错误" in msg.content:
last_error = msg.content
break
reflection = reflect_chain.invoke({"code": code, "error": last_error})
print("反思建议:", reflection.content)
return {
"messages": state["messages"] + [AIMessage(content=f"反思:{reflection.content}")]
}
def should_continue(state: CodeState) -> str:
if state.get("error") == "no" or state.get("retry_count", 0) >= state.get("max_retries", 3):
return "end"
return "reflect"
# ----- 6. 构建图 -----
builder = StateGraph(CodeState)
builder.add_node("generate", generate_node)
builder.add_node("check", code_check_node)
builder.add_node("reflect", reflect_node)
builder.set_entry_point("generate")
builder.add_edge("generate", "check")
builder.add_conditional_edges("check", should_continue, {
"reflect": "reflect",
"end": END
})
builder.add_edge("reflect", "generate")
memory = MemorySaver()
app = builder.compile(checkpointer=memory)
# ----- 7. 运行测试 -----
if __name__ == "__main__":
question = "如何在LCEL中并行执行两条链?"
config = {"configurable": {"thread_id": "code-assistant-1"}}
initial_state: CodeState = {
"messages": [HumanMessage(content=question)],
"generation": None,
"error": "no",
"retry_count": 0,
"max_retries": 3
}
print("===== 开始工作 =====")
result = app.invoke(initial_state, config)
print("\n===== 最终解决方案 =====")
final_gen = result.get("generation")
if final_gen:
print("描述:", final_gen["prefix"])
print("导入:", final_gen["imports"])
print("代码:\n", final_gen["code"])
else:
print("未能生成有效方案,对话历史:")
for msg in result["messages"]:
print(msg.content)
运行说明
- 在
.env文件中填写有效的OPENAI_API_KEY。 - 执行
pip install -r requirements.txt安装所有依赖。 - 运行
python code_assistant.py,观察控制台输出。 - 程序将加载 LangChain 官方 LCEL 速查表作为参考文档,随后根据问题“如何在 LCEL 中并行执行两条链?”生成代码,经过检查与反思重试后,输出最终可用的代码片段。
API 速览
| API / 方法 | 所属库 | 说明 |
|---|---|---|
StateGraph | langgraph.graph | 构建状态机工作流的核心类,支持节点与边的定义。 |
MemorySaver | langgraph.checkpoint.memory | 提供内存级检查点存储,用于状态持久化与恢复。 |
PydanticOutputParser | langchain_core.output_parsers | 将 LLM 输出解析为 Pydantic 模型实例。 |
WebBaseLoader | langchain_community.document_loaders | 从网页 URL 加载文档内容,需配合 beautifulsoup4 使用。 |
ChatPromptTemplate | langchain_core.prompts | 构建多轮对话提示模板,支持系统消息与人类消息。 |
Demo 示例
本博客提供的 code_assistant.py 即为完整可运行的 Demo。它演示了以下核心机制:
- 文档加载:使用
WebBaseLoader从指定 URL 获取 LangChain 官方 LCEL 文档。 - 结构化生成:通过
PydanticOutputParser确保 LLM 输出符合预定义的CodeSolution模型。 - 代码检查与模拟执行:动态执行导入语句和代码块,捕获异常并反馈。
- 反思重试闭环:利用
StateGraph的条件边实现自动重试逻辑,最多重试 3 次。 - 状态管理:通过
MemorySaver实现工作流状态的持久化,支持中断恢复。
参考文档
官方文档
参考链接
总结
本文完整介绍了基于 LangGraph 构建代码分析与生成助手的全过程,涵盖了从参考文档加载、结构化输出生成、动态代码检查到反思重试的完整闭环。
核心技术栈包括 LangGraph、LangChain、Pydantic 以及 BeautifulSoup。通过此架构,开发者可轻松将“生成-验证-修正”模式迁移到任意编程语言或框架的文档场景中,构建具备自我修正能力的智能编程助手。
416



被折叠的 条评论
为什么被折叠?



