【LLM LangChain】 模型绑定工具+调用工具(手动调用/LangGraph/AgentExecutor)+相关注意事项
模型绑定工具
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool# 定义一个工具
@tool
def add_numbers(input: str) -> str:"""Add two numbers together. 输入格式: 'a,b'"""a, b = map(int, input.split(","))return str(a + b)# 初始化模型
model = ChatOpenAI(model= "qwen-plus",api_key= "sk-0cff369a69e1474d9a308f19370373f1",base_url="https://dashscope.aliyuncs.com/compatible-mode/v1") # ChatOpenAI(model="gpt-4o", temperature=0)# 给模型绑定工具
model_with_tools = model.bind_tools([add_numbers])# 传入对话消息
result = model_with_tools.invoke([{"role": "user", "content": "帮我计算 123 + 456"}
])print(result)
输出
content='' additional_kwargs={'tool_calls': [{'id': 'call_407dc2ba39074f34a319c2', 'function': {'arguments': '{"input": "123,456"}', 'name': 'add_numbers'}, 'type': 'function', 'index': 0}], 'refusal': None} response_metadata={'token_usage': {'completion_tokens': 25, 'prompt_tokens': 162, 'total_tokens': 187, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-24d6004f-53c9-4362-ade0-1ec3d09f1f5b', 'service_tier': None, 'finish_reason': 'tool_calls', 'logprobs': None} id='run--12c2fe5b-0b2c-4f05-9102-c20b73c93737-0' tool_calls=[{'name': 'add_numbers', 'args': {'input': '123,456'}, 'id': 'call_407dc2ba39074f34a319c2', 'type': 'tool_call'}] usage_metadata={'input_tokens': 162, 'output_tokens': 25, 'total_tokens': 187, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}
关键字段说明
-
content=''
- 空字符串,因为这次回答不是自然语言,而是“我要调用工具”。
-
additional_kwargs.tool_calls
-
模型生成的工具调用请求。
-
这里包含:
{"id": "call_407dc2ba39074f34a319c2","function": {"arguments": "{\"input\": \"123,456\"}","name": "add_numbers"},"type": "function","index": 0 }
name
: 工具名称 →"add_numbers"
arguments
: 工具的参数 →{"input": "123,456"}
- 这就是模型想让你执行的函数调用。
-
-
response_metadata.finish_reason='tool_calls'
- 表示这次生成 不是回答,而是“请求调用工具”。
-
tool_calls
-
更直观地列出了工具调用:
[{'name': 'add_numbers', 'args': {'input': '123,456'}, ...}]
-
-
usage_metadata
/response_metadata.token_usage
- 记录了这次请求的 token 用量(输入 162,输出 25)。
工具执行
- 拿到了 工具调用请求 (
tool_calls
),接下来就有几种执行方式。
- 手动执行:最灵活,适合简单场景或需要自己管理工具调用逻辑。
- LangGraph + ToolNode:推荐 👍,适合生产级、多工具、复杂工作流。
- AgentExecutor:入门快,写起来简洁,但定制性差。
方法 1:手动执行工具
- 自己解析
tool_calls
,调用对应函数,再把结果交回模型。
from langchain_core.messages import AIMessage, ToolMessage# 1. 模型返回的工具调用请求
ai_message = result # 就是你拿到的对象
tool_call = ai_message.tool_calls[0]
tool_name = tool_call["name"]
tool_args = tool_call["args"]# 2. 执行工具
tool_result = add_numbers.invoke(tool_args) # -> "579"# 3. 把工具的结果传回模型
final_result = model_with_tools.invoke([ai_message,ToolMessage(content=tool_result, tool_call_id=tool_call["id"])
])print(final_result.content) # 实际输出:最终答案: 123 加 456 等于 579。
方法 2:用 LangGraph 的 ToolNode
- LangGraph 专门为这种场景做了封装,你只要把工具交给
ToolNode
,它会自动检测tool_calls
→ 执行 → 把结果再反馈给模型。
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import ToolNode# 定义工具节点
tools = [add_numbers]
tool_node = ToolNode(tools)# 定义模型节点
def call_model(state: MessagesState):response = model_with_tools.invoke(state["messages"])return {"messages": [response]} # 必须返回 dict# 定义工作流
workflow = StateGraph(MessagesState)
workflow.add_node("agent", call_model)
workflow.add_node("tools", tool_node)
workflow.add_edge(START, "agent")# 条件判断:模型是否要调用工具
def should_continue(state: MessagesState):last_msg = state["messages"][-1]return "tools" if getattr(last_msg, "tool_calls", None) else ENDworkflow.add_conditional_edges("agent", should_continue, ["tools", END])
workflow.add_edge("tools", "agent")graph = workflow.compile()# 执行
result = graph.invoke({"messages": [("user", "帮我计算 123+456")]})
print("最终答案:", result["messages"][-1].content)
# 最终答案: 123 + 456 = 579
- 旧版本 LangGraph Python里使用MessagesAnnotation:
from langgraph.prebuilt import ToolNode
from langgraph.graph import StateGraph, MessagesAnnotation, START, END# 1. 定义工具节点
tools = [add_numbers]
tool_node = ToolNode(tools)# 2. 定义模型节点
def call_model(state: MessagesAnnotation.State):return {"messages": [model_with_tools.invoke(state["messages"])]}# 3. 定义图
workflow = StateGraph(MessagesAnnotation)
workflow.add_node("agent", call_model)
workflow.add_node("tools", tool_node)# 4. 逻辑:模型调用工具 → 工具执行 → 回到模型
workflow.add_edge(START, "agent")
workflow.add_conditional_edges("agent",lambda state: "tools" if state["messages"][-1].tool_calls else END,["tools", END],
)
workflow.add_edge("tools", "agent")graph = workflow.compile()# 5. 执行
result = graph.invoke({"messages": [("user", "帮我计算 123+456")]})
print(result["messages"][-1].content)
方法 3:用 AgentExecutor
- LangChain 有
AgentExecutor
,可以自动处理工具调用,尤其是想要 “让模型自由选择工具” 时。开箱即用,逻辑封装好,适合快速跑 demo,但灵活度不如 LangGraph。 - 支持多个工具也更简单:
tools = [add_numbers, subtract_numbers];agent = create_openai_functions_agent(llm, tools)
- agent 会打印执行轨迹(调用了哪个工具、返回了什么),用于调试。
from langchain.agents import initialize_agent, AgentTypeagent = initialize_agent(tools=[add_numbers],llm=model,agent_type=AgentType.OPENAI_FUNCTIONS
)result = agent.invoke({"input": "帮我计算 123+456"})
print(result["output"])
注意事项
如果没有tool_calls
- AgentExecutor:直接输出 Final Answer。
- LangGraph:你要在条件分支里处理这种情况。
- 手动执行工具:需要自己判断
tool_calls
是否为空。
调用报错
-
正确的情况
-
错误的情况:
-
工具参数定义是
input: str
,模型就可能输出"123,456"
、'123,456'
、甚至{"input": "'123,456'"}
。Agent 里最常见,因为它全自动调用工具,最容易出脏数据。 -
ValueError: invalid literal for int() with base 10: "'123"
,模型返回的参数带了引号,input.split(“,”) 得到的是 [“'123”, “456’”],转 int报错:
-
修复方法
方法 1:在工具里清理参数
@tool
def add_numbers(input: str) -> str:"""Add two numbers together. 输入格式: 'a,b'"""# 去掉可能的引号和空格cleaned = input.strip().replace("'", "").replace('"', "")a, b = map(int, cleaned.split(","))return str(a + b)
方法 2:直接用 结构化参数
from langchain_core.tools import StructuredTool
from pydantic import BaseModelclass AddInput(BaseModel):a: intb: intdef add_numbers(a: int, b: int) -> str:"""Add two numbers together"""return str(a + b)add_tool = StructuredTool.from_function(func=add_numbers,args_schema=AddInput,name="add_numbers",description="Add two numbers together"
)
预定义工具
- 自带工具:https://python.langchain.com/docs/integrations/tools/
- https://js.langchain.ac.cn/docs/integrations/tools/duckduckgo_search/
- https://python.langchain.com/docs/integrations/tools/tavily_search/