由来

最开始是OpenAI提出的function calling,那个时候还没有agent的概念,就是在调用LLM的时候,告诉他有function XXX,在可以用到的时候返回function和参数调用。

后来有了agent的概念,就变成了tool calling,加上langchaintool的封装就越来越好用了。

流程

flowchart TD %%% 外层容器 - 白色背景 subgraph 工具调用流程图 [Tool Calling 流程图] style 工具调用流程图 fill:#ffffff,stroke:#000000,stroke-width:2px,padding:20px %%% 用户层 subgraph 用户层 A[用户发送请求] K[查看工具执行结果] Z[接收最终响应] end %%% 本地系统层 subgraph 本地系统 B[请求处理器] C[工具调用管理器] D[工具执行器] I[工具执行结果] E{工具调用完成?} TL[工具列表Tool List<br/>- 搜索工具<br/>- 计算器<br/>- 数据库查询<br/>- 文件操作<br/>- 其他自定义工具] end %%% 远程服务层 subgraph 远程服务 F[远程LLM服务] end %%% 流程连接 A --> B B -->|获取可用工具| TL B -->F F -->|分析请求| G{是否需要工具调用?} G -->|是| H[生成工具调用指令<br/>包含工具名称+参数] H --> C C -->|验证参数| D D -->|执行本地工具| I I --> E I --> K E -->|是| B F -->|整合结果| J[生成最终响应] G -->|否| J J --> Z end %%% 样式设置 style 本地系统 fill:#e6f7ff,stroke:#1890ff,stroke-width:2px style 远程服务 fill:#fff7e6,stroke:#fa8c16,stroke-width:2px style 用户层 fill:#f6ffed,stroke:#52c41a,stroke-width:2px style TL fill:#fff0f6,stroke:#eb2f96,stroke-width:2px

代码实现

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
基于LangChain的工具调用示例
功能:实现多轮工具调用循环,展示工具调用的完整流程
"""
import datetime
import json
from langchain_core.tools import tool
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_react_agent

# 第一步:定义工具类


@tool
def get_current_time() -> int:
    """获取当前的毫秒时间戳。

    Returns:
        int: 当前时间,毫秒时间戳
    """
    current_time = int(datetime.datetime.now().timestamp() * 1000)
    print(f"\n[工具调用] get_current_time() -> {current_time}")
    return current_time


@tool
def get_weather(input_str):
    """查询指定地点和时间的天气情况。

    Args:
        input_str: 输入参数,可以是字典或JSON字符串

    Returns:
        str: 天气信息
    """
    # 处理整个输入是JSON字符串的情况
    if isinstance(input_str, str):
        try:
            input_data = json.loads(input_str)
            if isinstance(input_data, dict):
                # 从解析后的字典中提取参数
                location = input_data.get("location")
                time = input_data.get("time")
            else:
                # 如果解析后不是字典,使用默认处理
                location = input_str
                time = int(datetime.datetime.now().timestamp() * 1000)
        except json.JSONDecodeError:
            # JSON解析失败,使用默认值
            location = input_str
            time = int(datetime.datetime.now().timestamp() * 1000)
    elif isinstance(input_str, dict):
        # 直接从字典中提取参数
        location = input_str.get("location")
        time = input_str.get("time")
    else:
        # 其他类型,使用默认值
        location = str(input_str)
        time = int(datetime.datetime.now().timestamp() * 1000)

    # 确保time是整数
    if isinstance(time, str):
        try:
            time = int(time)
        except ValueError:
            time = int(datetime.datetime.now().timestamp() * 1000)

    # 将毫秒时间戳转换为用户可读格式
    readable_time = datetime.datetime.fromtimestamp(time / 1000).strftime(
        "%Y-%m-%d %H:%M:%S"
    )
    # 模拟天气数据,返回mock结果
    weather_data = {
        "location": location,
        "time": readable_time,
        "temperature": "22°C",
        "condition": "晴",
        "humidity": "55%",
        "wind": "东北风 3级",
    }
    result = json.dumps(weather_data, ensure_ascii=False, indent=2)
    print(f"\n[工具调用] get_weather(location='{location}', time={time}) -> {result}")
    return result


# 第二步:创建LLM实例
# 配置硅基流动的OpenAI兼容API
llm = ChatOpenAI(
    model="",
    api_key="sk-rx",  # 请替换为你的硅基流动API密钥
    base_url="https://api.siliconflow.cn/v1",  # 硅基流动API地址
    temperature=0.0,
)

# 第三步:创建工具列表
tools = [get_current_time, get_weather]

# 第四步:创建提示模板 - REACT风格专用
from langchain_core.prompts import MessagesPlaceholder

prompt = ChatPromptTemplate.from_template(
    """Answer the following questions as best you can. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action, must be a JSON object with the correct parameter names
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

Question: {input}
Thought:{agent_scratchpad}"""
)

# 第五步:创建REACT风格的Agent
agent = create_react_agent(llm, tools, prompt)

# 第六步:创建Agent执行器
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# 第七步:测试工具调用
if __name__ == "__main__":
    # 测试场景:查询南京现在的天气
    response = agent_executor.invoke({"input": "现在南京的天气"})
    print(f"\n[最终回复] {response['output']}")

结果

> Entering new AgentExecutor chain...
我需要先获取当前时间,然后根据当前时间查询南京的天气情况。
Action: get_current_time
Action Input: {}
[工具调用] get_current_time() -> 1754103192901
1754103192901
现在我有了当前时间,可以使用这个时间来查询南京的天气情况。  
Action: get_weather  
Action Input: {"location": "南京", "time": 1754103192901}  
[工具调用] get_weather(location='南京', time=1754103192901) -> {
  "location": "南京",
  "time": "2025-08-02 10:53:12",
  "temperature": "22°C",
  "condition": "晴",
  "humidity": "55%",
  "wind": "东北风 3级"
}
{
  "location": "南京",
  "time": "2025-08-02 10:53:12",
  "temperature": "22°C",
  "condition": "晴",
  "humidity": "55%",
  "wind": "东北风 3级"
}
我获得了南京的天气信息,现在可以给出最终答案。  
Final Answer: 南京现在天气晴朗,温度为22°C,湿度55%,风向为东北风,风力3级。

> Finished chain.

[最终回复] 南京现在天气晴朗,温度为22°C,湿度55%,风向为东北风,风力3级。

最佳实践

  1. 编写清晰详细的功能名称,参数描述和说明

  2. 尽量少的参数,已知的就直接内置

  3. 提示词给出清晰且统一的schema,以防参数传递解析出错

  4. tool的数量不要太多,消耗token

Tool Calling的工程意义

维度

价值

解耦

模型只负责调用,逻辑完全独立

智能调度

多工具协同,自主判断,链式调用

易于扩展

新增tool注册即可

可观测性

能看到决策过程

安全性

参数异常handler

多轮调用

可以串联多个业务

工程化进阶方向

  1. 注册中心:统一管理tool,动态加载

  2. 参数校验:防止注入和错误传参

  3. 重试机制:调用失败或超时重试

  4. 缓存机制:减轻服务器压力

  5. 权限控制:不同角色有不同的调用权限

  6. 日志追踪:内部思维链过程记录

  7. 可视化面板:内部思维连可视化