LangGraph:工具调用与条件边 - 附简单ReAct代码示例

工具定义

langgraph=1.1.10 为例,LangGraph可以接收裸函数或者带@tool修饰符的函数作为LLM可调用的工具。如下是让LLM做乘法的一个工具示例

python 复制代码
@tool
def multiply(a: float, b: float) -> float:  
    """Multiply a and b  
  
    Args:        
	    a (float): The first number        
	    b (float): The second number    
	"""    
	return a * b
	

此处 @tool可以没有。

LLM会通过描述和函数签名来感知其内容:

内容 感知结果
描述部分 multiply a and b
参数部分(Google Style) 参数及其对应结果
函数签名 函数名称本身,即multiply

可以得到类似如下的schema

json 复制代码
{
  "name": "multiply",
  "description": "Multiply a and b",
  "parameters": {
    "type": "object",
    "properties": {
      "a": {
        "type": "number",
        "description": "The first number"
      },
      "b": {
        "type": "number",
        "description": "The second number"
      }
    },
    "required": ["a", "b"]
  }
}

补充:可以用 help(func) 来观察 LLM 实际"看到"的工具描述,便于调试 docstring 的质量。

工具挂载

工具于LLM挂载

工具需要在两个位置挂载。

其一为LLM本身。该步骤目的为让LLM知道有哪些工具可以用。示例:

python 复制代码
llm = ChatDeepSeek(  
    model="deepseek-v4-pro",  
    extra_body={"thinking": {"type": "disabled"}}  
)  # 如果是ReAct Agent,禁用思考模式


llm_with_tools = llm.bind_tools([multiply])  # 列表内填函数即可

工具于条件边挂载

其二为图结构。在图结构中我们可以像一般的条件边一样手搓处理逻辑。Message相关类通常会含有如下两个参数:

  • content:会返回给用户的对话内容
  • tool_calls:专门用于调用工具的内容 一般情况下,对话时AIMessage对象会往content内填充内容,tool_calls为空;而当调用工具时则反之。我们可以利用这一点来手搓一个判定逻辑。如下是一个触发了工具调用的AIMessage
json 复制代码
AIMessage(
	content='', 
	additional_kwargs={
		'refusal': None, 
		'reasoning_content': 'The user wants me to multiply 123 by 11. I can use the multiply function for this.'}, 
		response_metadata={
			'token_usage': {
				'completion_tokens': 81, 
				'prompt_tokens': 302, 
				'total_tokens': 383, 
				'completion_tokens_details': {
					'accepted_prediction_tokens': None, 
					'audio_tokens': None, 
					'reasoning_tokens': 21, 
					'rejected_prediction_tokens': None
				}, 
				'prompt_tokens_details': {
					'audio_tokens': None, 
					'cached_tokens': 256}, 
					'prompt_cache_hit_tokens': 256, 
					'prompt_cache_miss_tokens': 46
				}, 
				'model_provider': 'deepseek', 
				'model_name': 'deepseek-v4-pro', 
				'system_fingerprint': 'fp_9954b31ca7_prod0820_fp8_kvcache_20260402', 
				'id': 'd11f4bf3-acbd-4f0a-a376-86cd144b6c29', 
				'finish_reason': 'tool_calls', 
				'logprobs': None
			}, 
			id='lc_run--019e1f82-fe07-7fe2-8361-a4dc2d81b326-0', 
			tool_calls=[{'name': 'multiply', 'args': {'a': 123, 'b': 11}, 'id': 'call_00_jBsUEskW1HEvJVMgSusb2317', 'type': 'tool_call'}], 
			invalid_tool_calls=[], 
			usage_metadata={
				'input_tokens': 302, 
				'output_tokens': 81, 
				'total_tokens': 383, 
				'input_token_details': {'cache_read': 256}, 
				'output_token_details': {'reasoning': 21}})

具体的轮子此处略,找到tool_calls并判定即可

除了人为手工编写,还可以使用LangGraph内置的ToolNodetools_condition来便捷处理这个过程。

其中:

  • ToolNode用于封装工具节点(可以内涵多个工具以形成组),流入该节点后自动执行相关函数并返回结果
  • tools_condition表明是否调用工具并决定终止条件。即:1) 若调用工具,默认牵引向命名为tools的节点;2)若无,则流向 END

如下是向图挂载工具节点的一个示例代码,该步骤向工作流实际提供了工具,并使用add_conditional_edges来自动处理是否调用工具的判断:

python 复制代码
builder = StateGraph(MessagesState)  
builder.add_conditional_edges(  
    "assistant",  
    tools_condition  
)

需要注意的是,模型是否具有Tool Use的SFT将影响图的.invoke操作,具体处理方案内置于各个langchain社区包中,如langchain-deepseeklangchain-openailangchain-anthropic等具有SFT的模型会直接调用对应供应商的接口来得到格式化输出

flowchart TD A[AIMessage] B{tools_condition} C[ToolNode] D["END(亦作 __end__)"] A --> B B --需要工具调用-->C B --无需工具调用-->D

补充:ToolNode 只负责执行工具并返回 ToolMessage,不会对结果做任何总结或处理。如需对工具结果做进一步总结,须在 ToolNode 之后再添加专门的节点,不应将总结逻辑混入工具节点。

Router:单次工具调用

Router 是最简单的工具调用图:LLM 决定是否调用工具,调用一次后即结束,不循环回 LLM。适用于"查一次就够"的场景。

flowchart TD A["START(__start__)"] B["tool_calling_llm"] C["tools(ToolNode)"] D["END(__end__)"] A --> B B -.需要工具调用.-> C C --> D B -.无需工具调用.-> D
python 复制代码
from langgraph.graph import StateGraph, START, END, MessagesState
from langgraph.prebuilt import ToolNode, tools_condition

def tool_calling_llm(state: MessagesState):
    return {"messages": llm_with_tools.invoke(state["messages"])}

builder = StateGraph(MessagesState)
builder.add_node("tool_calling_llm", tool_calling_llm)
builder.add_node("tools", ToolNode([multiply]))
builder.add_edge(START, "tool_calling_llm")
builder.add_conditional_edges(
    "tool_calling_llm",
    tools_condition
)
builder.add_edge("tools", END)  # 工具执行后直接结束,不回流

graph = builder.compile()

不触发工具时(如打招呼),LLM 直接走到 ENDToolNode 不会被执行。

Router 与 ReAct 的核心区别在于 tools 之后的边:Router 直接连向 END,ReAct 则回流到 assistant 形成循环。

完整ReAct示例

以如下的ReAct结构为例:

flowchart TD A["START(__start__)"] B["assistant"] C["tools(ToolNode)"] D["END(__end__)"] A --> B B -.需要工具调用 .-> C C --结果回传 -->B B -.无需工具调用 .-> D

其中ReAct的三个步骤为:

  • Reason: assistant节点,LLM直接根据提示词决定是否调用工具
  • Act: 循环调用工具
  • Observe: 工具结果重新进入messages,LLM可以读取

ReAct 的终止条件为 LLM 不再发出工具调用,本质上等同于用提示词控制 Agent 的行为。此外,也可以在 .invoke 时套上最大执行次数的限制作为兜底:

python 复制代码
react_graph.invoke(
    {"messages": messages},
    config={"recursion_limit": 25}  # 默认值为 25
)

定义如下的函数以供备用:

python 复制代码
from langchain_core.tools import tool
  
@tool  
def multiply(a: float, b: float) -> float:  
    """Multiply a and b  
  
    Args:        
		a: first number        
		b: second number  
   
    """    
    return a * b  

@tool
def add(a: float, b: float) -> float:  
    """Add a and b  
  
    Args:        
	    a: first nunmber        
	    b: second number  
    """    
    return a + b  

@tool
def divide(a: float, b: float) -> float:  
    """  
    Divide a and b    
    Args:        
	    a: first number        
	    b: second number  
    """    
    return a / b

定义LLM,并挂载工具

python 复制代码
from langchain_deepseek import ChatDeepSeek
tools = [add, multiply, divide]  
# 禁用思考模式,降低实现难度  
# DeepSeek的思考内容必须回传,需要手动处理相关逻辑,社区SDK存在问题
# 补充:截止转录至稀土掘金(2026.06.01),这个Bug仍然没有被修复
llm = ChatDeepSeek(  
    model="deepseek-v4-pro",  
    extra_body={"thinking": {"type": "disabled"}}  
)  
llm_with_tools = llm.bind_tools(tools)

定义assistant节点

python 复制代码
from langgraph.graph import MessagesState  
from langchain_core.messages import HumanMessage, SystemMessage  
  
system_message = SystemMessage(  
    content="You are a helpful assistant tasked with performing arithmetic on a set of inputs"  
)  
def assistant(state: MessagesState):  
    return {"messages": [llm_with_tools.invoke([system_message] + state["messages"])]}

开始构建图结构

python 复制代码
from langgraph.graph import START, StateGraph  
from langgraph.prebuilt import (  
    tools_condition, ToolNode  
)

builder = StateGraph(MessagesState)  
  
builder.add_node("assistant", assistant)  
builder.add_node("tools", ToolNode(tools))  
builder.add_edge(START, "assistant")  
builder.add_conditional_edges(  
    "assistant",  
    tools_condition  
)  
builder.add_edge("tools", "assistant")  
  
react_graph = builder.compile()

此时Agent可用,此处通过初始图状态的形式传入指令开始任务:

python 复制代码
messages = [  
    HumanMessage(content="Add 3 and 24. Multiply the output by 6. Divide the output by 3")  
]  
messages = react_graph.invoke({"messages": messages})

for m in messages["messages"]:  
    m.pretty_print()

如下是代码的一次运行结果:

text 复制代码
================================ Human Message =================================

Add 3 and 24. Multiply the output by 6. Divide the output by 3
================================== Ai Message ==================================

I'll solve this step by step.

**Step 1: Add 3 and 24**
Tool Calls:
  add (call_00_Gya1KfjUxr6oUIBcvOfB3366)
 Call ID: call_00_Gya1KfjUxr6oUIBcvOfB3366
  Args:
    a: 3
    b: 24
================================= Tool Message =================================
Name: add

27.0
================================== Ai Message ==================================

**Step 2: Multiply the output (27) by 6**
Tool Calls:
  multiply (call_00_0fGnLRmOSOJeoNYRWSHQ4452)
 Call ID: call_00_0fGnLRmOSOJeoNYRWSHQ4452
  Args:
    a: 27
    b: 6
================================= Tool Message =================================
Name: multiply

162.0
================================== Ai Message ==================================

**Step 3: Divide the output (162) by 3**
Tool Calls:
  divide (call_00_7Jj0pz0tSkqQKGtfi4591545)
 Call ID: call_00_7Jj0pz0tSkqQKGtfi4591545
  Args:
    a: 162
    b: 3
================================= Tool Message =================================
Name: divide

54.0
================================== Ai Message ==================================

Here's the summary:

- 3 + 24 = **27**
- 27 × 6 = **162**
- 162 ÷ 3 = **54**

The final result is **54**.
相关推荐
canyu1 小时前
从零设计一个自适应挖需的 AI 提示词系统:多轮对话 + 动态维度
agent
基因改造者1 小时前
多Agent交互设计
agent
前端再部署1 小时前
Nuxt3 AI Agent 控制台实战 17:排查香港服务器访问火山方舟北京模型超时问题
agent·全栈
格桑阿sir1 小时前
14-大模型智能体开发工程师:ReAct推理-行动框架
ai·大模型·llm·agent·react·智能体·推理模型
Artech2 小时前
[MAF的Agent管道详解-07]利用AIAgent中间件构建Agent管道
ai·agent·maf·agent管道
羑悻的小杀马特2 小时前
从 Claude Code 到 QClaw:AgentSkills 规范的跨生态实践与工程取舍!
人工智能·自动化·agent·skills·openclaw·qclaw
呆呆敲代码的小Y3 小时前
【最新Codex教程】 | 安装、入门和快速使用,适合新手
人工智能·gpt·ai·llm·openai·agent·codex
HIT_Weston12 小时前
99、【Agent】【OpenCode】task 工具提示词(Slash command)(一)
人工智能·agent·opencode
louisliao_198114 小时前
Hermes Agent:工具与技能的加载、执行与规模化策略
agent