Agent工具调用:让AI拥有超能力

🛠️ Function Calling | 工具注册 + 智能选择 + 结果整合 | LangChain Tools实战 | 完整项目代码


📖 为什么需要工具调用?

LLM的局限性

复制代码
# 纯LLM - 只能基于训练数据回答
llm = ChatOpenAI(model="gpt-4")

response = llm.invoke("今天北京的天气怎么样?")
# 输出: "抱歉,我无法获取实时天气信息" ❌

response = llm.invoke("计算 12345 * 67890")
# 输出: 可能计算错误 ❌

response = llm.invoke("查询张三的数据库记录")
# 输出: "我无法访问你的数据库" ❌

问题:

  • ❌ 无法获取实时信息
  • ❌ 数学计算可能出错
  • ❌ 无法访问外部系统
  • ❌ 无法执行实际操作

工具调用的优势

复制代码
# Agent + 工具 - 可以调用外部能力
agent = create_agent(llm, tools=[
    weather_tool,      # 天气查询
    calculator_tool,   # 精确计算
    database_tool      # 数据库访问
])

response = agent.run("今天北京的天气怎么样?")
# 自动调用weather_tool → "北京今天晴,25°C" ✅

response = agent.run("计算 12345 * 67890")
# 自动调用calculator_tool → "838102050" ✅

response = agent.run("查询张三的信息")
# 自动调用database_tool → "张三,30岁,工程师" ✅

优势:

  • ✅ 获取实时数据
  • ✅ 精确计算
  • ✅ 访问外部系统
  • ✅ 执行实际操作

🏗️ 工具调用架构

核心流程

复制代码
用户输入
   │
   ▼
┌──────────────┐
│  LLM分析     │ ← 理解用户需求,判断是否需要工具
└──────┬───────┘
       │
       ├─ 需要工具 ──→ ┌──────────────┐
       │               │ 工具选择     │ ← 从可用工具中选择
       │               └──────┬───────┘
       │                      │
       │                      ▼
       │               ┌──────────────┐
       │               │ 工具执行     │ ← 调用工具函数
       │               └──────┬───────┘
       │                      │
       │                      ▼
       │               ┌──────────────┐
       │               │ 结果整合     │ ← LLM整合工具结果
       │               └──────┬───────┘
       │                      │
       └─ 不需要工具 ────────┐│
                             ▼▼
                      ┌──────────────┐
                      │  最终回复     │
                      └──────────────┘

工具调用模式

模式 说明 适用场景
单次调用 调用一个工具后直接回复 简单查询
多次调用 连续调用多个工具 复杂任务
并行调用 同时调用多个独立工具 提高效率
条件调用 根据前一个结果决定下一步 决策流程

🛠️ 核心技术实现

方案1:LangChain Tools基础用法

安装依赖
复制代码
pip install langchain langchain-openai
1. 自定义工具
复制代码
from langchain.tools import tool


@tool
def calculate(expression: str) -> str:
    """计算数学表达式
    
    Args:
        expression: 数学表达式,如 "2 + 2" 或 "12345 * 67890"
        
    Returns:
        计算结果
    """
    try:
        # 安全计算(只允许基本运算)
        result = eval(expression, {"__builtins__": {}}, {})
        return f"计算结果: {result}"
    except Exception as e:
        return f"计算错误: {str(e)}"


@tool
def get_weather(city: str) -> str:
    """查询城市天气
    
    Args:
        city: 城市名称,如 "北京" 或 "上海"
        
    Returns:
        天气信息
    """
    # 模拟天气API
    weather_data = {
        "北京": "晴,25°C,空气质量优",
        "上海": "多云,28°C,湿度65%",
        "广州": "小雨,30°C,湿度80%"
    }
    
    return weather_data.get(city, f"未找到{city}的天气信息")


@tool
def search_database(query: str) -> str:
    """搜索数据库记录
    
    Args:
        query: 搜索关键词
        
    Returns:
        搜索结果
    """
    # 模拟数据库
    database = {
        "张三": {"age": 30, "occupation": "工程师", "city": "北京"},
        "李四": {"age": 25, "occupation": "设计师", "city": "上海"},
        "王五": {"age": 35, "occupation": "产品经理", "city": "广州"}
    }
    
    if query in database:
        info = database[query]
        return f"{query}的信息:年龄{info['age']}岁,职业{info['occupation']},所在城市{info['city']}"
    else:
        return f"未找到'{query}'的记录"


# 创建工具列表
tools = [calculate, get_weather, search_database]
2. 创建Agent
复制代码
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate

# 创建LLM
llm = ChatOpenAI(model="gpt-4", temperature=0)

# 创建提示词模板
prompt = ChatPromptTemplate.from_messages([
    ("system", """你是一个有用的助手,可以使用各种工具来帮助用户。
    
    可用的工具:
    - calculate: 计算数学表达式
    - get_weather: 查询城市天气
    - search_database: 搜索数据库记录
    
    当用户的问题需要这些信息时,请使用相应的工具。"""),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}")
])

# 创建Agent
agent = create_tool_calling_agent(llm, tools, prompt)

# 创建执行器
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,  # 显示详细过程
    handle_parsing_errors=True
)

# 使用Agent
result = agent_executor.invoke({
    "input": "今天北京的天气怎么样?"
})
print(result["output"])
# 输出: 北京今天晴,25°C,空气质量优

result = agent_executor.invoke({
    "input": "计算 12345 乘以 67890"
})
print(result["output"])
# 输出: 计算结果: 838102050

result = agent_executor.invoke({
    "input": "帮我查一下张三的信息"
})
print(result["output"])
# 输出: 张三的信息:年龄30岁,职业工程师,所在城市北京

方案2:内置工具集

常用工具
复制代码
from langchain_community.tools import (
    DuckDuckGoSearchRun,      # 网络搜索
    WikipediaQueryRun,        # 维基百科
    ArxivQueryRun,            # 学术论文
    ShellTool,                # 命令行执行
    PythonREPLTool,           # Python代码执行
)

# 网络搜索工具
search_tool = DuckDuckGoSearchRun()
result = search_tool.run("Python最新版本")
print(result)

# 维基百科工具
wiki_tool = WikipediaQueryRun()
result = wiki_tool.run("人工智能")
print(result)

# Python执行工具
python_tool = PythonREPLTool()
result = python_tool.run("print(2 + 2)")
print(result)  # 输出: 4
组合使用
复制代码
# 创建强大的Agent
tools = [
    DuckDuckGoSearchRun(),
    WikipediaQueryRun(),
    PythonREPLTool(),
    calculate,
    get_weather
]

agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# 复杂查询
result = agent_executor.invoke({
    "input": "搜索Python的最新版本,然后计算版本号中的数字之和"
})

方案3:自定义工具类

高级工具实现
复制代码
from langchain.tools import BaseTool
from typing import Type
from pydantic import BaseModel, Field


class WeatherInput(BaseModel):
    """天气查询输入"""
    city: str = Field(description="城市名称,如'北京'、'上海'")
    date: str = Field(description="日期,格式YYYY-MM-DD,默认为今天", default="today")


class WeatherTool(BaseTool):
    """天气查询工具"""
    
    name: str = "get_weather"
    description: str = "查询指定城市的天气信息"
    args_schema: Type[BaseModel] = WeatherInput
    
    def _run(self, city: str, date: str = "today") -> str:
        """同步执行"""
        # 这里可以调用真实的天气API
        weather_data = {
            "北京": {"temp": 25, "condition": "晴", "humidity": 40},
            "上海": {"temp": 28, "condition": "多云", "humidity": 65},
            "广州": {"temp": 30, "condition": "小雨", "humidity": 80}
        }
        
        if city in weather_data:
            data = weather_data[city]
            return f"{city}{date}天气:{data['condition']},温度{data['temp']}°C,湿度{data['humidity']}%"
        else:
            return f"未找到{city}的天气信息"
    
    async def _arun(self, city: str, date: str = "today") -> str:
        """异步执行"""
        return self._run(city, date)


class DatabaseTool(BaseTool):
    """数据库查询工具"""
    
    name: str = "query_database"
    description: str = "查询数据库中的用户信息"
    
    def _run(self, user_name: str) -> str:
        """查询用户信息"""
        # 模拟数据库连接
        database = {
            "张三": {"age": 30, "occupation": "工程师", "salary": 20000},
            "李四": {"age": 25, "occupation": "设计师", "salary": 15000},
        }
        
        if user_name in database:
            info = database[user_name]
            return f"用户{user_name}:年龄{info['age']},职业{info['occupation']},薪资{info['salary']}元"
        else:
            return f"未找到用户'{user_name}'"
    
    async def _arun(self, user_name: str) -> str:
        return self._run(user_name)


# 使用自定义工具
tools = [WeatherTool(), DatabaseTool()]

方案4:工具路由与优化

智能工具选择
复制代码
from typing import List, Dict


class ToolRouter:
    """工具路由器 - 智能选择最合适的工具"""
    
    def __init__(self, tools: List[BaseTool]):
        self.tools = tools
        self.tool_index = {tool.name: tool for tool in tools}
        
        # 工具关键词索引(用于快速匹配)
        self.keyword_index = {}
        for tool in tools:
            keywords = self._extract_keywords(tool.description)
            for keyword in keywords:
                if keyword not in self.keyword_index:
                    self.keyword_index[keyword] = []
                self.keyword_index[keyword].append(tool.name)
    
    def select_tools(self, query: str, top_k: int = 3) -> List[str]:
        """根据查询选择相关工具
        
        Args:
            query: 用户查询
            top_k: 返回工具数量
            
        Returns:
            工具名称列表
        """
        # 提取查询关键词
        query_keywords = self._extract_keywords(query)
        
        # 计算工具相关性得分
        tool_scores = {}
        for keyword in query_keywords:
            if keyword in self.keyword_index:
                for tool_name in self.keyword_index[keyword]:
                    tool_scores[tool_name] = tool_scores.get(tool_name, 0) + 1
        
        # 按得分排序
        sorted_tools = sorted(tool_scores.items(), key=lambda x: x[1], reverse=True)
        
        return [tool_name for tool_name, score in sorted_tools[:top_k]]
    
    def _extract_keywords(self, text: str) -> List[str]:
        """提取关键词(简化版)"""
        # 实际应用中可以使用NLP技术
        keywords = ["天气", "计算", "搜索", "查询", "数据库", "用户"]
        return [kw for kw in keywords if kw in text]


# 使用示例
router = ToolRouter([WeatherTool(), DatabaseTool(), calculate])
relevant_tools = router.select_tools("北京今天的天气怎么样?")
print(relevant_tools)  # 输出: ['get_weather']
工具缓存
复制代码
from functools import lru_cache
import time


class CachedTool(BaseTool):
    """带缓存的工具"""
    
    def __init__(self, base_tool: BaseTool, cache_ttl: int = 3600):
        self.base_tool = base_tool
        self.cache_ttl = cache_ttl
        self.cache = {}
        self.cache_timestamps = {}
    
    def _run(self, *args, **kwargs) -> str:
        """执行工具(带缓存)"""
        # 生成缓存键
        cache_key = str((args, kwargs))
        
        # 检查缓存
        if cache_key in self.cache:
            timestamp = self.cache_timestamps[cache_key]
            if time.time() - timestamp < self.cache_ttl:
                print(f"✅ 使用缓存结果")
                return self.cache[cache_key]
        
        # 执行工具
        result = self.base_tool._run(*args, **kwargs)
        
        # 更新缓存
        self.cache[cache_key] = result
        self.cache_timestamps[cache_key] = time.time()
        
        return result

🎯 实战案例:智能研究助手

系统架构

复制代码
用户提问
   │
   ▼
┌──────────────┐
│ 意图识别     │ ← 判断需要什么类型的信息
└──────┬───────┘
       │
       ├─ 事实查询 ──→ 搜索引擎
       ├─ 学术问题 ──→ 论文数据库
       ├─ 计算问题 ──→ 计算器
       ├─ 代码问题 ──→ Python执行器
       └─ 其他 ─────→ LLM直接回答
              │
              ▼
       ┌──────────────┐
       │ 结果整合     │ ← 综合多个来源
       └──────┬───────┘
              │
              ▼
         最终答案

完整实现

复制代码
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain_community.tools import DuckDuckGoSearchRun
from langchain.tools import tool


# ==================== 定义工具 ====================

@tool
def calculate_expression(expression: str) -> str:
    """计算数学表达式"""
    try:
        result = eval(expression, {"__builtins__": {}}, {})
        return str(result)
    except:
        return "计算错误"


@tool
def search_web(query: str) -> str:
    """搜索网络信息"""
    search = DuckDuckGoSearchRun()
    return search.run(query)


@tool
def summarize_text(text: str, max_length: int = 200) -> str:
    """总结文本内容"""
    # 简单总结(实际应用可以用LLM)
    words = text.split()
    if len(words) > max_length:
        return " ".join(words[:max_length]) + "..."
    return text


# ==================== 创建Agent ====================

def create_research_assistant():
    """创建智能研究助手"""
    
    llm = ChatOpenAI(model="gpt-4", temperature=0)
    
    tools = [
        calculate_expression,
        search_web,
        summarize_text
    ]
    
    prompt = ChatPromptTemplate.from_messages([
        ("system", """你是一个智能研究助手,可以帮助用户:
        
        1. 搜索网络信息(使用search_web)
        2. 进行数学计算(使用calculate_expression)
        3. 总结长文本(使用summarize_text)
        
        根据用户的需求选择合适的工具。如果一个问题需要多个步骤,
        可以依次调用多个工具。
        
        回答要准确、简洁、有用。"""),
        ("human", "{input}"),
        ("placeholder", "{agent_scratchpad}")
    ])
    
    agent = create_tool_calling_agent(llm, tools, prompt)
    
    agent_executor = AgentExecutor(
        agent=agent,
        tools=tools,
        verbose=True,
        max_iterations=5,  # 最大工具调用次数
        handle_parsing_errors=True
    )
    
    return agent_executor


# ==================== 使用示例 ====================

def example_research_assistant():
    """研究助手使用示例"""
    
    print("="*60)
    print("智能研究助手演示")
    print("="*60)
    
    assistant = create_research_assistant()
    
    # 测试用例
    test_cases = [
        "计算 2的10次方是多少?",
        "搜索Python的最新版本信息",
        "帮我总结一下:人工智能是计算机科学的一个分支,它企图了解智能的实质,并生产出一种新的能以人类智能相似的方式做出反应的智能机器。人工智能的研究包括机器人、语言识别、图像识别、自然语言处理和专家系统等。"
    ]
    
    for i, query in enumerate(test_cases, 1):
        print(f"\n[问题 {i}]")
        print(f"👤 用户: {query}")
        print("-" * 60)
        
        result = assistant.invoke({"input": query})
        print(f"🤖 AI: {result['output']}")
        print("="*60)


if __name__ == "__main__":
    example_research_assistant()

📊 性能优化技巧

1. 并行工具调用

复制代码
import asyncio


async def parallel_tool_calls(agent_executor, queries: List[str]):
    """并行执行多个查询"""
    
    tasks = [
        agent_executor.ainvoke({"input": query})
        for query in queries
    ]
    
    results = await asyncio.gather(*tasks)
    return results


# 使用
queries = [
    "北京的天气",
    "上海的天气",
    "广州的天气"
]
results = asyncio.run(parallel_tool_calls(agent_executor, queries))

2. 工具预筛选

复制代码
def prefilter_tools(query: str, all_tools: List[BaseTool]) -> List[BaseTool]:
    """预筛选相关工具,减少LLM负担"""
    
    # 基于关键词快速过滤
    relevant_keywords = {
        "天气": ["get_weather"],
        "计算": ["calculate"],
        "搜索": ["search_web"],
        "代码": ["python_repl"]
    }
    
    selected_tools = set()
    for keyword, tool_names in relevant_keywords.items():
        if keyword in query:
            selected_tools.update(tool_names)
    
    # 返回匹配的工具
    return [t for t in all_tools if t.name in selected_tools]

3. 结果缓存

复制代码
class ToolCache:
    """工具结果缓存"""
    
    def __init__(self, max_size: int = 1000):
        self.cache = {}
        self.max_size = max_size
    
    def get(self, tool_name: str, args: tuple) -> Optional[str]:
        """获取缓存"""
        key = (tool_name, args)
        return self.cache.get(key)
    
    def set(self, tool_name: str, args: tuple, result: str):
        """设置缓存"""
        key = (tool_name, args)
        
        # LRU策略
        if len(self.cache) >= self.max_size:
            oldest_key = next(iter(self.cache))
            del self.cache[oldest_key]
        
        self.cache[key] = result

💡 最佳实践总结

1. 工具设计原则

原则 说明 示例
单一职责 每个工具只做一件事 天气工具只查天气
清晰描述 description要详细说明用途 "查询城市天气,返回温度和状况"
类型安全 使用Pydantic定义输入 args_schema = WeatherInput
错误处理 优雅处理异常情况 返回友好错误信息
幂等性 相同输入产生相同输出 避免副作用

2. 工具选择策略

复制代码
# 好的工具描述
@tool
def get_stock_price(stock_symbol: str) -> str:
    """获取股票实时价格。
    
    参数:
        stock_symbol: 股票代码,如'AAPL'、'GOOGL'
    
    返回:
        股票的当前价格和涨跌幅
    """
    pass

# 不好的工具描述
@tool
def stock(x):
    """股票"""
    pass

3. 调试技巧

复制代码
# 启用verbose查看详细过程
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True  # 显示每一步
)

# 查看中间步骤
result = agent_executor.invoke({"input": query})
print(result["intermediate_steps"])  # 工具调用历史

🔍 常见问题解答

Q1: 如何防止工具被滥用?

A:

复制代码
# 1. 限制调用次数
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    max_iterations=3  # 最多调用3次
)

# 2. 权限控制
def safe_execute(tool, args):
    if not check_permission(tool.name):
        raise PermissionError("无权使用该工具")
    return tool.run(args)

# 3. 输入验证
def validate_input(tool_name, args):
    if tool_name == "shell":
        dangerous_commands = ["rm", "sudo", "dd"]
        if any(cmd in args for cmd in dangerous_commands):
            raise ValueError("禁止执行危险命令")

Q2: 如何处理工具调用失败?

A:

复制代码
# 1. 重试机制
for attempt in range(3):
    try:
        result = tool.run(args)
        break
    except Exception as e:
        if attempt == 2:
            result = f"工具调用失败: {str(e)}"

# 2. 降级策略
try:
    result = premium_api.query(args)
except:
    result = basic_api.query(args)  # 降级到基础API

# 3. 错误传递给LLM
agent_executor = AgentExecutor(
    handle_parsing_errors=True  # 自动处理解析错误
)

Q3: 如何评估工具调用的效果?

A:

复制代码
# 关键指标
metrics = {
    "tool_selection_accuracy": 0.92,  # 工具选择准确率
    "success_rate": 0.95,              # 调用成功率
    "avg_response_time": 2.5,          # 平均响应时间(秒)
    "cost_per_query": 0.02             # 每次查询成本(美元)
}

# A/B测试
variant_a = AgentExecutor(tools=basic_tools)
variant_b = AgentExecutor(tools=enhanced_tools)

compare_performance(variant_a, variant_b)

🔗 相关资源


📝 总结

Agent工具调用是让AI拥有"超能力"的关键技术,通过:

工具注册 - 定义清晰的工具接口

智能选择 - LLM自动选择合适工具

结果整合 - 将工具结果融入对话

错误处理 - 优雅的异常处理机制

掌握了工具调用技术,你就能打造出真正实用的AI Agent,让它能够:

  • 获取实时信息
  • 执行复杂计算
  • 访问外部系统
  • 完成实际任务

下一步: 动手实践,从简单的计算器工具开始,逐步构建功能丰富的Agent系统。


专栏: AI Agent实战专栏
日期: 2026年5月10日
系列: AI Agent高级进阶系列第3篇

相关推荐
小豆包的小朋友02171 小时前
【无标题】
人工智能
IT_陈寒2 小时前
React性能优化踩的坑,这个错你可能也会犯
前端·人工智能·后端
zhangxingchao2 小时前
AI应用开发三:RAG技术与应用
前端·人工智能·后端
OpenBayes贝式计算2 小时前
教程上新丨支持 600+ 语言,小米开源 OmniVoice:仅需 3-10 秒参考音频实现语音克隆
人工智能
辰同学ovo2 小时前
用 Chrome DevTools MCP 给 AI 写的页面做“质检“
前端·人工智能·chrome devtools
果汁华3 小时前
Agent 与 Skill 的使用边界
人工智能
天上路人3 小时前
采用AI 神经网络降噪技术降噪模组A-59F如何区分“人声”与“环境噪声”?
人工智能·语音识别
啵啵肠3 小时前
给 AI Agent 一把求职 CLI:推荐一个面向 BOSS 直聘工作流的开源项目 boss-agent-cli
人工智能·github
小新同学^O^3 小时前
简单学习 --> 模型微调
开发语言·人工智能·python·模型微淘