路由器
上一步我们手写了工具调用节点,langgraph在条件边内提供了toos_condition用来判断是否调用工具和ToolNode类用来搭建工具调用节点
新建根据工具结果输出节点和工具节点,上一步直接打印了工具输出结果,应当将工具结果给模型生成答案,因此需要将工具输出的结果再传回。
python
builder = StateGraph(MessagesState)
builder.add_node("llm_with_tools", llm_with_tools) # 添加节点
builder.add_node("tools", ToolNode([multiply_values])) # 定义工具节点
builder.add_node("answer_multiply", answer_multiply_node)
builder.add_edge(START, "llm_with_tools") # 从START到llm_with_tools的边
builder.add_conditional_edges("llm_with_tools",
# 如果助手的最新消息(结果)是工具调用 -> tools_condition 路由到 tools
# 如果助手的最新消息(结果)不是工具调用 -> tools_condition 路由到 END
tools_condition) # 根据LLM输出决定路由
builder.add_edge("tools", "llm_with_tools")
graph = builder.compile() # 编译图
tools_condition是官方提供函数,实现了ReAct风格的标准条件逻辑,若模型未调用工具将转到END节点。
在add_conditional_edges中,我们还可以自己编写判断函数,返回下一个节点名称,以下是一个示例:
python
def condition_edge(state: TestCaseState):
"""
条件边(节点4):逻辑判断,控制边的走向
判断逻辑:
- 如果通过 or 评审次数>3,则执行格式化节点
- 否则返回节点1(重新编写)
"""
print("\n=== 条件边判断 ===")
is_approved = state.get("is_approved", False)
review_count = state.get("review_count", 0)
print(f"是否通过评审:{is_approved}")
print(f"评审次数:{review_count}")
if is_approved or review_count > 3:
print("决策:进入格式化节点")
return "format_test_case_node"
else:
print("决策:返回节点1(重新编写测试用例)")
return "write_test_case_node"
不需要显式添加到 END 的边
ReAct架构
ReAct架构是一种融合推理 (Reasoning)与行动(Acting)的通用智能体框架,通过"思考→行动→观察"的循环机制,使大语言模型能够动态调用工具、获取外部信息并迭代优化决策,从而实现对复杂现实任务的自主解决。
下面进行一个小案例,搭建一个互联网搜索工具,爬取指定内容并智能生成报告。
首先,先创建互联网搜索工具,这里用到了阿里云的联网搜索api
调用联网搜索API-智能开放搜索 OpenSearch-阿里云
之前的工具定义中,我们使用了@tool注解,现在我们尝试一个进阶写法,通过继承BaseModel来实现复杂工具的定义:
python
import aiohttp
from pydantic.fields import Field
from langchain_core.tools import BaseTool
from pydantic import create_model
from config import ALIYUN_OPENSEARCH_KEY, ALIYUN_OPENSEARCH_URL
def get_title_and_link(result: list) -> list:
"""
获取标题和链接
"""
data = []
for i in result:
data.append({i['title']: i['link']})
return data
class SearchTool(BaseTool):
# 工具名称(在 LangChain 中显示)
name: str = "互联网搜索工具"
# 工具描述(告诉 Agent 这个工具能做什么)
description: str = "进行互联网查询"
# 是否直接返回结果(False 表示结果会经过后续处理或整合)
return_direct: bool = False
def __init__(self):
super().__init__()
# 动态创建一个 Pydantic 模型,用于校验和描述工具的输入参数
self.args_schema = create_model(
"SearchArgs", # 模型名称
query=(str, Field(..., description="需要进行查询的信息")),
num=(int, Field(..., description="返回结果的数量,可根据需求修改,默认为5,最大支持20")),
no_content=(bool, Field(..., description="是否仅返回标题和链接。True 表示不返回网页内容,False 则返回摘要内容"))
)
async def _arun(self, query: str, num: int = 5, no_content: bool = False) -> str:
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {ALIYUN_OPENSEARCH_KEY}"
}
data = {
"query": query,
"query_rewrite": False,
"top_k": num,
"content_type": "snippet"
}
async with aiohttp.ClientSession() as session:
async with session.post(ALIYUN_OPENSEARCH_URL, headers=headers, json=data) as response:
if response.status != 200:
error_text = await response.text()
return "搜索工具调用出错,错误信息:" + error_text[:500]
result = await response.json()
if no_content:
result = get_title_and_link(result["result"]["search_result"])
else:
result = result["result"]["search_result"]
return str(result)
def _run(self, query: str, num: int = 5, no_content: bool = False) -> str:
import asyncio
return asyncio.run(self._arun(query, num))
if __name__ == "__main__":
tool = SearchTool()
print(tool.invoke({"query": "中国最新科技成果", "num": 5, "no_content": False}))
然后再构建图:
python
from langchain_core.messages import HumanMessage
from langgraph.graph import StateGraph, START, END, MessagesState
from langgraph.prebuilt import ToolNode, tools_condition
from src.agent.llm import model
from src.agent.tools.get_time import get_time
from src.agent.tools.online_search import SearchTool
from src.agent.utils.show_graph import show_graph
search_tool = SearchTool()
# 绑定工具到 LLM
llm_tools = model.bind_tools([get_time, search_tool])
# 定义节点
def llm_with_tools(state: MessagesState):
return {"messages": [llm_tools.invoke(state["messages"])]}
# 构建图
builder = StateGraph(MessagesState)
# 实例化 ToolNode 并传入工具列表
tool_node = ToolNode(tools=[get_time, search_tool])
builder.add_node("llm_with_tools", llm_with_tools)
builder.add_node("tools", tool_node) # 传入实例
builder.add_edge(START, "llm_with_tools")
builder.add_conditional_edges("llm_with_tools", tools_condition)
builder.add_edge("tools", "llm_with_tools")
graph = builder.compile()
# 可视化图
show_graph(graph)
messages = []
while True:
user_input = input("请输入:")
messages.append(HumanMessage(content=user_input))
result = graph.invoke(MessagesState(messages=messages))
for m in result["messages"]:
m.pretty_print()
以上就实现了一个简易的智能体,所有的消息都在messages列表,那么我们之前使用create_agent来创建了一个智能体:
python
from langchain.agents import create_agent
agent = create_agent(
model=basic_model, # 默认模型
tools=tools
)
这个案例实际上就是它的简化版本,也就是说你已经基本理解了create_agent的工作原理,langgraph封装了强大的 create_agent,可以帮助我们快速构建智能体,搭建工作流,因此以后我们将聚焦于create_agent的学习。