模型绑定工具

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 END

workflow.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, AgentType

agent = 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 BaseModel

class AddInput(BaseModel):
    a: int
    b: int

def 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"
)

预定义工具

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐