使用百炼大模型:LangGraph快速构建Agent工作流应用
1、工作流

2、完整执行流程示意
输入问题
↓
[planner] → 输出计划 → event 循环捕获 → 打印日志
↓
[agent] → 执行搜索 → event 循环捕获 → 打印日志
↓
[replan] → 判断是否完成 → event 循环捕获 → 打印日志
↓
[agent] → (如果需要继续)
↓
... 循环直到完成
↓
[__end__] → 跳过,退出循环
3、代码
from langchain_tavily import TavilySearch
from dotenv import load_dotenv
import os
from pydantic import BaseModel, Field
from typing import Annotated, List, Tuple, TypedDict, Optional
from typing import Literal
env_path = os.path.join(os.path.dirname(__file__), '..', '.env')
load_dotenv(env_path)
tools = [TavilySearch(max_results=1)]
from langchain_openai import ChatOpenAI
import asyncio
from langchain.agents import create_agent
llm = ChatOpenAI(
model=os.getenv("DASHSCOPE_MODEL", "qwen3.5-plus"),
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url=os.getenv("DASHSCOPE_BASE_URL"),
temperature=0.0
)
agent_executor = create_agent(llm, tools)
import operator
class PlanExecute(TypedDict):
input: str
plan: List[str]
past_steps: Annotated[List[Tuple], operator.add]
response: str
class Plan(BaseModel):
"""任务分解计划"""
steps: List[str] = Field(description="需要执行的步骤列表,按顺序排列")
class FinalAnswer(BaseModel):
"""最终答案"""
answer: str = Field(description="整合所有信息后的完整答案")
from langchain_core.prompts import ChatPromptTemplate
planner_prompt = ChatPromptTemplate.from_template(
"""请将以下问题分解为多个独立的搜索步骤。每个步骤应该获取一个具体信息,所有步骤完成后能得出完整答案。
问题:{input}
请只返回 JSON 格式:{{"steps": ["步骤 1", "步骤 2", ...]}}"""
)
planner = planner_prompt | llm.with_structured_output(Plan)
class ReplanAction(BaseModel):
"""重新规划动作"""
is_complete: bool = Field(description="是否已完成所有必要搜索,可以给出最终答案")
next_step: Optional[str] = Field(default=None, description="如果未完成,下一个要执行的步骤")
final_answer: Optional[str] = Field(default=None, description="如果已完成,最终答案是什么")
replanner_prompt = ChatPromptTemplate.from_template(
"""基于以下信息判断是否需要继续搜索:
原始问题:{input}
已完成步骤:{past_steps}
请评估:
1. 是否已有足够信息回答原始问题?
2. 如果需要更多信息,下一步应该搜索什么?
3. 如果信息已足够,请直接给出完整答案。
请只返回 JSON 格式:
- 如果未完成:{{"is_complete": false, "next_step": "下一步要搜索的内容"}}
- 如果已完成:{{"is_complete": true, "final_answer": "整合所有信息的完整答案"}}"""
)
replanner = replanner_prompt | llm.with_structured_output(ReplanAction)
async def main():
async def plan_step(state: PlanExecute):
try:
plan = await planner.ainvoke({"input": state["input"]})
print(f"\n=== 任务分解 ===\n共 {len(plan.steps)} 个步骤:")
for i, step in enumerate(plan.steps, 1):
print(f"{i}. {step}")
return {"plan": list(plan.steps)}
except Exception as e:
print(f"计划生成失败:{e}")
return {"plan": [f"搜索并回答:{state['input']}"]}
async def execute_step(state: PlanExecute):
if not state["plan"]:
return {"past_steps": state["past_steps"]}
plan = state["plan"]
plan_str = "\n".join(f"{i + 1}. {step}" for i, step in enumerate(plan))
task = plan[0]
task_formatted = f"""当前总任务:{state["input"]}
待执行计划:
{plan_str}
请执行第 1 步:{task}
提供准确、详细的信息。"""
print(f"\n=== 执行步骤 ===\n正在搜索:{task}")
agent_response = await agent_executor.ainvoke(
{"messages": [("user", task_formatted)]}
)
result = agent_response["messages"][-1].content
print(f"搜索结果:{result[:200]}...")
return {
"past_steps": state["past_steps"] + [(task, result)],
}
async def replan_step(state: PlanExecute):
try:
output = await replanner.ainvoke({
"input": state["input"],
"past_steps": state["past_steps"]
})
if output.is_complete and output.final_answer:
print(f"\n=== 整合答案 ===\n{output.final_answer}")
return {"response": output.final_answer}
elif not output.is_complete and output.next_step:
remaining_plan = state["plan"][1:] if len(state["plan"]) > 1 else [output.next_step]
print(f"\n=== 进度更新 ===\n下一步:{output.next_step}")
return {"plan": remaining_plan}
else:
if state.get("past_steps"):
last_result = state["past_steps"][-1][1]
return {"response": f"基于搜索结果:{last_result}"}
else:
return {"plan": [f"继续搜索:{state['input']}"]}
except Exception as e:
print(f"重新计划失败:{e}")
if state.get("past_steps"):
last_result = state["past_steps"][-1][1]
return {"response": f"基于已有信息:{last_result}"}
else:
return {"plan": [f"重新尝试:{state['input']}"]}
def should_end(state: PlanExecute) -> Literal["agent", "__end__"]:
if "response" in state and state["response"]:
return "__end__"
else:
return "agent"
from langgraph.graph import StateGraph, START, END
workflow = StateGraph(PlanExecute)
workflow.add_node("planner", plan_step)
workflow.add_node("agent", execute_step)
workflow.add_node("replan", replan_step)
workflow.add_edge(START, "planner")
workflow.add_edge("planner", "agent")
workflow.add_edge("agent", "replan")
workflow.add_conditional_edges(
"replan",
should_end,
{
"__end__": END,
"agent": "agent"
}
)
app = workflow.compile()
graph_png = app.get_graph().draw_mermaid_png()
with open("agent_workflow.png", "wb") as f:
f.write(graph_png)
config = {"recursion_limit": 50}
inputs = {"input": "2024 年巴黎奥运会男子 100 米自由泳决赛冠军的家乡是哪里?请用中文答复"}
print("=" * 60)
print(f"开始处理问题:{inputs['input']}")
print("=" * 60)
async for event in app.astream(inputs, config=config):
for k, v in event.items():
if k != "__end__":
print(f"\n[{k.upper()}] 输出:")
print(v)
print("\n" + "=" * 60)
print("处理完成!")
print("=" * 60)
if __name__ == "__main__":
asyncio.run(main())
4、结果展示
============================================================
开始处理问题:2024 年巴黎奥运会男子 100 米自由泳决赛冠军的家乡是哪里?请用中文答复
============================================================
=== 任务分解 ===
共 2 个步骤:
1. 搜索 2024 年巴黎奥运会男子 100 米自由泳决赛冠军的姓名
2. 搜索该冠军的家乡或出生地信息
[PLANNER] 输出:
{'plan': ['搜索 2024 年巴黎奥运会男子 100 米自由泳决赛冠军的姓名', '搜索该冠军的家乡或出生地信息']}
=== 执行步骤 ===
正在搜索:搜索 2024 年巴黎奥运会男子 100 米自由泳决赛冠军的姓名
搜索结果:2024 年巴黎奥运会男子 100 米自由泳决赛的冠军是**潘展乐**。
他的家乡是**中国浙江省温州市**(具体祖籍为温州市永嘉县桥下镇,出生于温州市区)。
在 2024 年巴黎奥运会上,潘展乐以 46 秒 40 的成绩打破世界纪录并夺得金牌,成为中国首位在该项目上获得奥运冠军的运动员。...
[AGENT] 输出:
{'past_steps': [('搜索 2024 年巴黎奥运会男子 100 米自由泳决赛冠军的姓名', '2024 年巴黎奥运会男子 100 米自由泳决赛的冠军是**潘展乐**。\n\n他的家乡是**中国浙江省温州市**(具体祖籍为温州市永嘉县桥下镇,出生于温州市区)。\n\n在 2024 年巴黎奥运会上,潘展乐以 46 秒 40 的成绩打破世界纪录并夺得金牌,成为中国首位在该项目上获得奥运冠军的运动员。')]}
=== 整合答案 ===
2024 年巴黎奥运会男子 100 米自由泳决赛冠军是潘展乐,他的家乡是中国浙江省温州市。
[REPLAN] 输出:
{'response': '2024 年巴黎奥运会男子 100 米自由泳决赛冠军是潘展乐,他的家乡是中国浙江省温州市。'}
============================================================
处理完成!
============================================================
Process finished with exit code 0