第05章:工具与 MCP(Tools)

版本:LangChain v1.3.7 | 讲师:汤姆小白


1. Tools 概述

1.1 什么是工具

大模型只能生成文本,无法与外部世界交互。Tools(工具)为 LLM 装上"手脚",让它能调用 API、搜索互联网、查询数据库、执行代码、操作文件等。

用户提问 → LLM 思考 → 决定调用工具 → 执行工具 → 获取结果 → 继续推理

1.2 工具的构成要素

每个 Tool 包含以下要素:

要素 说明 示例
name 唯一标识名称 "web_search"
description 功能描述(LLM 据此判断何时调用) "搜索互联网获取最新信息"
args_schema 参数 JSON Schema(Pydantic 模型) {"query": "string"}
func 实际执行的函数 调用搜索 API
return_direct 是否直接返回结果给用户 False(让 Agent 继续)

Tool 的 name 和 description 至关重要——LLM 完全靠这两项判断何时调用哪个工具。


2. 自定义工具

2.1 @tool 装饰器(最简单)

from langchain_core.tools import tool

@tool
def add(a: int, b: int) -> int:
    """两个整数相加"""
    return a + b

print(add.name)         # add
print(add.description)  # 两个整数相加
print(add.args)         # {'a': {'type': 'integer'}, 'b': {'type': 'integer'}}

# 调用
result = add.invoke({"a": 10, "b": 20})  # 30

自定义名称和描述:

@tool("calculator", description="执行数学计算", return_direct=True)
def calculate(expression: str) -> float:
    """计算数学表达式"""
    return eval(expression)

使用 Pydantic 完善参数描述:

from pydantic import BaseModel, Field

class SearchInput(BaseModel):
    query: str = Field(description="搜索关键词")
    max_results: int = Field(default=5, description="最大结果数")

@tool(args_schema=SearchInput)
def web_search(query: str, max_results: int = 5) -> str:
    """搜索互联网获取信息"""
    # 实际调用搜索 API
    return f"关于 '{query}' 的搜索结果..."

2.2 StructuredTool.from_function(更灵活)

from langchain_core.tools import StructuredTool

def get_weather(city: str) -> str:
    """获取城市天气"""
    return f"{city}今天晴,28°C"

weather_tool = StructuredTool.from_function(
    func=get_weather,
    name="get_weather",
    description="查询指定城市的天气情况",
)

weather_tool.invoke({"city": "北京"})  # 北京今天晴,28°C

异步工具:

async def fetch_stock_price(symbol: str) -> str:
    """获取股票实时价格"""
    await asyncio.sleep(0.5)  # 模拟 API 调用
    return f"{symbol} 当前价格: ¥128.50"

stock_tool = StructuredTool.from_function(
    coroutine=fetch_stock_price,  # 用 coroutine 而不是 func
    name="stock_price",
    description="查询股票实时价格",
)

2.3 两种方式对比

特性 @tool 装饰器 StructuredTool.from_function
简洁度 ⭐⭐⭐⭐⭐ ⭐⭐⭐
灵活性 ⭐⭐⭐ ⭐⭐⭐⭐⭐
异步支持
适用场景 大部分自定义工具 需要精细控制的工具

3. 工具与模型绑定

3.1 bind_tools:将工具绑定到模型

from langchain.chat_models import init_chat_model
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage

@tool
def add(a: int, b: int) -> int:
    """两个整数相加"""
    return a + b

@tool
def multiply(a: int, b: int) -> int:
    """两个整数相乘"""
    return a * b

model = init_chat_model("openai:gpt-4o-mini")
model_with_tools = model.bind_tools([add, multiply])

# LLM 会判断需要调用哪个工具
response = model_with_tools.invoke([HumanMessage(content="10 加 20 等于多少?")])

# 查看工具调用
if response.tool_calls:
    for tc in response.tool_calls:
        print(f"调用工具: {tc['name']}")
        print(f"参数: {tc['args']}")
        # 手动执行工具
        if tc['name'] == 'add':
            result = add.invoke(tc['args'])
            print(f"结果: {result}")

3.2 工具调用执行循环

完整的工具调用流程:

from langchain_core.messages import HumanMessage, ToolMessage

# 1. 模型判断需要调用工具
response = model_with_tools.invoke([HumanMessage(content="25乘以4是多少?")])

# 2. 执行工具
tool_results = []
for tc in response.tool_calls:
    tool_name = tc['name']
    if tool_name == 'multiply':
        result = multiply.invoke(tc['args'])
    tool_results.append(ToolMessage(
        content=str(result),
        tool_call_id=tc['id']
    ))

# 3. 将工具结果返回给模型,获得最终回答
final_response = model_with_tools.invoke([
    HumanMessage(content="25乘以4是多少?"),
    response,          # AI 的工具调用消息
    *tool_results,     # 工具执行结果
])
print(final_response.content)  # 25乘以4等于100

3.3 控制工具调用行为

# 强制调用任意工具
model_with_forced_tool = model.bind_tools(
    [add, multiply],
    tool_choice="any",    # 必须调用某个工具
)

# 强制调用指定工具
model_with_specific = model.bind_tools(
    [add, multiply],
    tool_choice="add",    # 必须调用 add
)

# 禁用并行调用(某些模型支持)
model_serial = model.bind_tools(
    [add, multiply],
    parallel_tool_calls=False,
)

4. MCP 协议

4.1 什么是 MCP

MCP(Model Context Protocol) 是一种开放的标准化协议,让 AI 应用能够通过统一接口连接各种外部工具和数据源。类似 USB 协议统一了外部设备连接——MCP 统一了工具/数据源的接入方式。

LangChain → MCP Server → 外部服务(数据库/API/文件系统/...)

4.2 MCP 的优势

传统方式 MCP 方式
每个工具自己写胶水代码 统一协议,即插即用
各服务接口不一致 标准化接口
难以复用和共享 社区共享 MCP Server
绑死特定框架 跨框架通用(Claude/Cursor/Continue…)

4.3 使用 MCP 工具

from langchain_mcp_adapters.client import MultiServerMCPClient

# 连接 MCP Server
client = MultiServerMCPClient({
    "weather": {
        "command": "python",
        "args": ["-m", "mcp_server_weather"],
    },
    "database": {
        "command": "python",
        "args": ["-m", "mcp_server_sqlite", "--db-path", "data.db"],
    },
})

# 获取工具列表
tools = client.get_tools()
for t in tools:
    print(f"{t.name}: {t.description}")

# 绑定到模型
model = init_chat_model("openai:gpt-4o-mini")
model_with_mcp = model.bind_tools(tools)

# Agent 会自动调用 MCP 工具

MCP Server 可以用 Python、Node.js 等任何语言编写,也可以安装社区已有的各类 MCP Server(文件系统、数据库、搜索引擎等)。


5. 内置工具

LangChain 提供了丰富的内置工具。

5.1 Tavily 搜索引擎

pip install tavily-python
from langchain_community.tools.tavily_search import TavilySearchResults

search = TavilySearchResults(max_results=3, api_key="your_key")
results = search.invoke("2026年AI最新进展")
for r in results:
    print(r["content"])

5.2 Wikipedia 查询

pip install wikipedia
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper

wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
result = wikipedia.invoke("量子计算")
print(result)

5.3 Python REPL(代码执行)

from langchain_experimental.tools import PythonREPLTool

repl = PythonREPLTool()
result = repl.invoke("print(sum([i**2 for i in range(10)]))")
# 输出: 285

5.4 文件操作工具

from langchain_community.tools.file_management import (
    ReadFileTool, WriteFileTool, ListDirectoryTool, MoveFileTool, CopyFileTool
)

reader = ReadFileTool()
content = reader.invoke({"file_path": "./data.txt"})

writer = WriteFileTool()
writer.invoke({"file_path": "./output.txt", "text": "Hello LangChain"})

6. 工具集 Toolkits

Toolkit 是一组紧密相关的工具集合,开箱即用。

6.1 SQL 数据库工具集

pip install pymysql
from langchain_community.agent_toolkits import SQLDatabaseToolkit
from langchain_community.utilities import SQLDatabase

db = SQLDatabase.from_uri("mysql+pymysql://user:pass@localhost/db")
toolkit = SQLDatabaseToolkit(db=db, llm=model)

tools = toolkit.get_tools()
for t in tools:
    print(t.name)
# sql_db_query, sql_db_schema, sql_db_list_tables, sql_db_query_checker

6.2 JSON 工具集

from langchain_community.tools.json.tool import JsonSpec
from langchain_community.agent_toolkits import JsonToolkit

json_spec = JsonSpec(dict_={"name": "产品", "price": 99})
toolkit = JsonToolkit(spec=json_spec)

7. 工具最佳实践

  1. 描述要精准:工具描述直接决定 LLM 能否正确调用。写明"什么时候用"“输入什么”“输出什么”
  2. 参数用 Pydanticargs_schema 让 LLM 精确理解参数格式和含义
  3. invoke 代替 run:v1 统一用 invoke() 方法
  4. 工具要原子化:每个工具只做一件事,复杂流程用多个工具组合
  5. 异常处理:工具内部做好错误捕获,返回有意义的错误信息而非 traceback
  6. MCP 优先:系统级操作(数据库、文件系统、API)优先考虑用 MCP 协议接入

本章小结

概念 说明
@tool 最简单的自定义工具方式
StructuredTool 灵活的工具构造器
bind_tools() 将工具绑定到模型
tool_choice 控制工具调用的强制行为
MCP 标准化工具协议,即插即用
Toolkit 一组相关工具的集合
Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐