AI Agent 之工具使用:从函数定义到实际应用

前言
        在 AI Agent 的发展历程中,工具使用能力无疑是其从 "聊天机器人" 跃升为 "智能助手" 的关键一步。想象一下,如果一个 AI 只能依靠其训练数据中的知识进行回答,那么它不仅会受限于知识的时效性,还会在面对需要实时计算或外部信息的问题时束手无策。

        本文将深入探讨 AI Agent 工具使用的核心技术,从工具的定义方法到函数调用技术的实现,再到从零构建一个具备多种工具使用能力的 ReAct Agent。无论你是 AI 开发者还是技术爱好者,掌握这些知识都将帮助你构建更强大、更实用的 AI Agent 系统。

一、AI Agent 工具系统的核心原理
1.1 为什么 Agent 需要工具?
        人类解决问题的过程,本质上是不断使用工具的过程 —— 遇到数学问题时使用计算器,查询信息时使用搜索引擎,记录时间时查看钟表。同样,AI Agent 要具备强大的问题解决能力,也必须拥有使用工具的能力。

工具为 Agent 带来了三大核心优势:

  • 突破知识边界:获取训练数据之外的信息,尤其是实时动态信息
  • 增强计算能力:处理复杂的数学运算或逻辑推理
  • 扩展操作范围:与外部系统交互,执行实际操作

1.2 工具的本质:函数抽象与能力封装

        从技术角度看,Agent 使用的工具本质上是对特定能力的函数抽象。每一个工具都是一个函数,它接收特定的输入参数,执行特定的操作,并返回相应的结果。

一个设计良好的工具应该具备:

  • 单一职责:专注于解决某一类问题
  • 明确接口:清晰的输入参数和输出格式
  • 错误处理:对异常情况的妥善处理
  • 文档说明:让 Agent 理解其功能和使用场景

1.3 Agent 使用工具的完整流程
Agent 使用工具解决问题的完整流程可以概括为以下步骤:

问题分析:理解用户问题,判断是否需要使用工具
工具选择:根据问题类型选择合适的工具
参数确定:明确工具所需的输入参数
工具调用:按照规定格式调用工具
结果处理:解析工具返回的结果
回答生成:基于工具结果生成最终回答
        这个流程通常不是线性的,而是一个循环迭代的过程,Agent 可能需要多次调用不同的工具才能完成复杂任务。

二、为 Agent 定义工具:从函数到能力
2.1 工具设计的基本原则
为 Agent 设计工具时,需要遵循以下基本原则:

实用性:工具应解决实际问题,避免设计不必要的工具
简洁性:工具功能应单一明确,避免过于复杂的多功能工具
一致性:保持工具接口设计的一致性,便于 Agent 理解和使用
安全性:考虑潜在的安全风险,如参数验证、权限控制等
可扩展性:设计时考虑未来可能的功能扩展
2.2 基础工具的实现:搜索、计算器与时间查询
下面我们实现几个基础但常用的工具,这些工具将在后续的 Agent 中使用。

2.2.1 搜索引擎工具
搜索引擎是 Agent 获取外部信息的主要途径,尤其适用于获取实时信息、特定知识等。
 

import requests
 
class SearchTool:
    """搜索引擎工具,用于获取实时信息、特定数据等"""
    
    def __init__(self, api_key=None):
        self.api_key = api_key
        
    def run(self, query: str) -> str:
        """
        执行搜索查询
        
        参数:
            query: 搜索关键词或问题,字符串类型
            
        返回:
            搜索结果,字符串类型
        """
        # 实际应用中应替换为真实的搜索引擎API
        # 这里使用模拟数据进行演示
        if "天气" in query and ("上海" in query or "北京" in query):
            if "上海" in query:
                return "上海今天(2023年11月15日)天气:晴转多云,气温18-25℃,东北风2级。"
            elif "北京" in query:
                return "北京今天(2023年11月15日)天气:晴,气温10-18℃,西北风3级。"
        elif "时间" in query or "日期" in query:
            return "当前时间为2023年11月15日星期三,14:30。"
        else:
            return f"关于'{query}'的搜索结果:这是模拟的搜索结果,实际应用中会返回真实的搜索内容。"
    
    def get_description(self) -> dict:
        """获取工具描述,用于让Agent理解工具功能"""
        return {
            "name": "search",
            "description": "用于获取实时信息、时事新闻、天气情况、特定数据等无法凭记忆回答的问题",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "搜索的关键词或问题"
                    }
                },
                "required": ["query"]
            }
        }
2.2.2 计算器工具

计算器工具用于处理各种数学计算,避免 Agent 在复杂计算中出错。

class CalculatorTool:
    """计算器工具,用于执行数学计算"""
    
    def run(self, expression: str) -> str:
        """
        执行数学计算
        
        参数:
            expression: 数学表达式,字符串类型,支持加减乘除等基本运算
            
        返回:
            计算结果,字符串类型
        """
        try:
            # 仅允许基本的数学运算,确保安全性
            allowed_operators = {'+', '-', '*', '/', '(', ')', '.', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
            for char in expression:
                if char not in allowed_operators:
                    return f"计算错误:不支持的字符 '{char}'"
            
            # 使用eval执行计算,实际应用中可考虑更安全的计算库
            result = eval(expression)
            return f"计算结果:{result}"
        except ZeroDivisionError:
            return "计算错误:除数不能为零"
        except Exception as e:
            return f"计算错误:{str(e)}"
    
    def get_description(self) -> dict:
        """获取工具描述,用于让Agent理解工具功能"""
        return {
            "name": "calculator",
            "description": "用于执行数学计算,如加减乘除等",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "要计算的数学表达式,例如 '25 + 30' 或 '(18 - 10) * 2'"
                    }
                },
                "required": ["expression"]
            }
        }
2.2.3 时间查询工具

时间查询工具用于获取当前的日期和时间信息。

import datetime
 
class TimeTool:
    """时间工具,用于获取当前时间和日期"""
    
    def run(self, format: str = "%Y年%m月%d日 %A %H:%M:%S") -> str:
        """
        获取当前时间
        
        参数:
            format: 时间格式字符串,默认为"%Y年%m月%d日 %A %H:%M:%S"
            
        返回:
            当前时间,字符串类型
        """
        try:
            now = datetime.datetime.now()
            return now.strftime(format)
        except Exception as e:
            return f"时间查询错误:{str(e)}"
    
    def get_description(self) -> dict:
        """获取工具描述,用于让Agent理解工具功能"""
        return {
            "name": "get_current_time",
            "description": "用于获取当前的日期和时间信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "format": {
                        "type": "string",
                        "description": "时间格式字符串,例如 '%Y-%m-%d %H:%M:%S',默认使用'%Y年%m月%d日 %A %H:%M:%S'",
                        "default": "%Y年%m月%d日 %A %H:%M:%S"
                    }
                },
                "required": []
            }
        }
2.3 工具元数据:让 Agent 理解工具用途

        仅仅实现工具的功能还不够,我们还需要为每个工具提供详细的元数据(metadata),让 Agent 能够理解工具的用途、参数和使用场景。

工具元数据通常包括以下信息:

  • 名称(name):工具的唯一标识符
  • 描述(description):工具的功能和适用场景
  • 参数(parameters):工具所需的输入参数,包括参数名称、类型、描述和是否必需

      这种结构化的元数据描述,使得 Agent 能够根据问题需求,自主选择合适的工具,并正确构造参数。

三、函数调用(Function Calling)技术详解
3.1 什么是函数调用技术?
        函数调用(Function Calling)是 LLM 与外部工具交互的标准方式,它允许模型生成结构化的函数调用指令,然后由外部系统执行这些指令并返回结果。

简单来说,函数调用技术解决了两个核心问题:

如何让 LLM 以机器可理解的方式表达其要执行的操作
如何将工具执行的结果反馈给 LLM,形成完整的交互闭环
        随着 GPT-3.5/4、Claude 等大语言模型对函数调用的原生支持,这一技术已经成为连接 LLM 与外部世界的标准接口。

3.2 函数调用的格式规范
        为了让 Agent 和工具之间能够准确通信,我们需要定义清晰的函数调用格式。目前最常用的是 JSON 格式,配合特定的分隔符。

一个标准的函数调用格式通常如下:

<function_call>
{
  "name": "工具名称",
  "parameters": {
    "参数1": "值1",
    "参数2": "值2"
  }
}
</function_call>

这种格式具有以下优势:

  • 结构化:机器可以轻松解析
  • 可读性:人类也能理解其含义
  • 灵活性:可以支持任意数量和类型的参数

  在实际应用中,我们需要在 Prompt 中明确告知 LLM 这种格式要求,使其能够生成符合规范的函数调用指令。

3.3 函数调用与 ReAct 范式的结合
函数调用技术与 ReAct 范式的结合,形成了强大的 Agent 工作流:

Thought:LLM 分析问题,决定需要调用的工具
Function Call:LLM 生成符合格式的函数调用指令
Tool Execution:Agent 执行函数调用,获取结果
Observation:将工具返回的结果反馈给 LLM
Repeat:重复上述过程,直到可以生成最终回答

   这种结合使得 Agent 既具备强大的推理能力,又能够利用外部工具扩展其能力边界,从而解决更复杂的问题。

四、从零构建带工具的 ReAct Agent
4.1 Agent 架构设计
我们设计的 ReAct Agent 将包含以下核心组件:

工具管理器(ToolManager):负责管理所有可用工具,提供工具调用接口
LLM 接口(LLMInterface):与大语言模型交互的模块
函数调用解析器(FunctionCallParser):解析 LLM 生成的函数调用指令
循环控制器(LoopController):管理整个 ReAct 循环的流程
提示构建器(PromptBuilder):构建用于 LLM 的提示信息
这些组件协同工作,使 Agent 能够完成从理解问题到使用工具,再到生成最终回答的完整流程。

4.2 核心组件实现
4.2.1 工具管理器
工具管理器负责注册、管理和调用各种工具:
 

from typing import Dict, List, Any, Callable
 
class ToolManager:
    def __init__(self):
        self.tools = {}  # 存储工具实例,key为工具名称
    
    def register_tool(self, tool: Any):
        """注册工具"""
        tool_desc = tool.get_description()
        tool_name = tool_desc["name"]
        self.tools[tool_name] = tool
        return self
    
    def get_tool_descriptions(self) -> List[Dict]:
        """获取所有工具的描述信息"""
        return [tool.get_description() for tool in self.tools.values()]
    
    def call_tool(self, tool_name: str, parameters: Dict[str, Any]) -> str:
        """调用指定工具"""
        if tool_name not in self.tools:
            return f"错误:未知的工具 '{tool_name}'"
        
        try:
            tool = self.tools[tool_name]
            # 调用工具的run方法,**parameters解包参数
            result = tool.run(** parameters)
            return str(result)
        except Exception as e:
            return f"工具调用错误:{str(e)}"
4.2.2 函数调用解析器

解析器负责从 LLM 的响应中提取函数调用信息:

import re
import json
from typing import Dict, Optional, Tuple
 
class FunctionCallParser:
    def __init__(self):
        # 用于匹配函数调用的正则表达式
        self.pattern = re.compile(r'<function_call>(.*?)</function_call>', re.DOTALL)
    
    def parse(self, response: str) -> Tuple[Optional[str], Optional[Dict]]:
        """
        解析LLM响应中的函数调用
        
        返回:
            (工具名称, 参数字典) 如果解析成功
            (None, None) 如果没有函数调用
        """
        match = self.pattern.search(response)
        if not match:
            return None, None
        
        try:
            function_call = json.loads(match.group(1).strip())
            return function_call.get("name"), function_call.get("parameters", {})
        except json.JSONDecodeError:
            return None, None
4.2.3 提示构建器

构建器负责生成用于 LLM 的提示信息:

class PromptBuilder:
    @staticmethod
    def build_prompt(question: str, tools: List[Dict], history: List[str]) -> str:
        """
        构建完整的提示信息
        
        参数:
            question: 用户问题
            tools: 工具描述列表
            history: 历史对话记录
        
        返回:
            完整的提示字符串
        """
        prompt = """你是一个可以使用工具解决问题的智能助手。你拥有调用工具的能力,并能根据工具的返回结果回答问题。
        
## 可用工具
你可以使用以下工具:
"""
        # 添加工具描述
        for tool in tools:
            prompt += f"- {tool['name']}: {tool['description']}\n"
            prompt += f"  参数: {json.dumps(tool['parameters'], ensure_ascii=False)}\n\n"
        
        # 添加使用规则
        prompt += """## 使用规则
1. 当你需要解决问题时,首先思考是否需要使用工具。
2. 如果需要使用工具,请按照以下格式生成函数调用:
   <function_call>
   {
     "name": "工具名称",
     "parameters": {
       "参数1": "值1",
       "参数2": "值2"
     }
   }
   </function_call>
3. 你会收到工具返回的结果,然后基于此进行新一轮思考。
4. 重复上述过程,直到你可以给出最终答案。
5. 最终答案请用自然语言清晰表述,不需要包含函数调用格式。
## 历史记录
"""
        # 添加历史记录
        if history:
            prompt += "\n".join(history) + "\n"
        else:
            prompt += "无\n"
        
        # 添加当前问题
        prompt += f"\n## 当前问题\n{question}\n\n请思考并给出你的回答(如需使用工具,请按照指定格式):"
        
        return prompt
4.3 完整的 ReAct Agent 实现

将上述组件整合,实现完整的 ReAct Agent:

import json
import openai
from typing import List, Dict, Optional
 
class ReActAgent:
    def __init__(self, llm_model: str = "gpt-3.5-turbo"):
        self.tool_manager = ToolManager()
        self.parser = FunctionCallParser()
        self.llm_model = llm_model
        self.max_iterations = 5  # 最大循环次数,防止无限循环
    
    def register_tool(self, tool: Any) -> "ReActAgent":
        """注册工具"""
        self.tool_manager.register_tool(tool)
        return self
    
    def _call_llm(self, prompt: str) -> str:
        """调用LLM获取响应"""
        try:
            response = openai.ChatCompletion.create(
                model=self.llm_model,
                messages=[{"role": "user", "content": prompt}]
            )
            return response.choices[0].message['content'].strip()
        except Exception as e:
            return f"LLM调用错误:{str(e)}"
    
    def run(self, question: str) -> str:
        """运行Agent处理问题"""
        history = []
        
        for _ in range(self.max_iterations):
            # 1. 构建提示
            tool_descriptions = self.tool_manager.get_tool_descriptions()
            prompt = PromptBuilder.build_prompt(question, tool_descriptions, history)
            
            # 2. 调用LLM
            llm_response = self._call_llm(prompt)
            
            # 3. 解析是否需要调用工具
            tool_name, parameters = self.parser.parse(llm_response)
            
            # 4. 如果不需要调用工具,直接返回结果
            if not tool_name:
                # 提取思考过程,添加到历史
                thought = llm_response.replace("<function_call>", "").replace("</function_call>", "").strip()
                history.append(f"思考:{thought}")
                return llm_response
            
            # 5. 记录思考过程和函数调用
            thought = llm_response.split("<function_call>")[0].strip()
            history.append(f"思考:{thought}")
            history.append(f"函数调用:<function_call>{json.dumps({'name': tool_name, 'parameters': parameters}, ensure_ascii=False)}</function_call>")
            
            # 6. 调用工具
            tool_result = self.tool_manager.call_tool(tool_name, parameters)
            history.append(f"工具返回:{tool_result}")
        
        # 如果达到最大循环次数仍未完成
        return f"抱歉,在{self.max_iterations}轮思考后仍无法完成回答。当前已获取的信息:\n" + "\n".join(history)
 
# 使用示例
if __name__ == "__main__":
    # 配置API密钥
    openai.api_key = "your_openai_api_key"
    
    # 创建Agent实例
    agent = ReActAgent()
    
    # 注册工具
    agent.register_tool(SearchTool())
    agent.register_tool(CalculatorTool())
    agent.register_tool(TimeTool())
    
    # 测试问题
    question = "上海今天的天气如何?它和北京的气温差多少?"
    answer = agent.run(question)
    print(answer)

4.4 工作流程解析
上述代码实现的 ReAct Agent 的完整工作流程如下:

初始化与工具注册:创建 Agent 实例并注册所需的工具(搜索、计算器、时间查询)。

接收问题:用户输入需要解决的问题,如 "上海今天的天气如何?它和北京的气温差多少?"。

构建提示:根据问题、可用工具和历史记录构建完整的提示信息。

LLM 推理:将提示发送给 LLM,获取推理结果。

函数调用解析:检查 LLM 的响应中是否包含函数调用指令。

工具调用:如果有函数调用,执行相应的工具并获取结果。

循环迭代:将思考过程、函数调用和工具结果记录到历史中,重复步骤 3-6。

生成回答:当 LLM 认为已获取足够信息时,生成最终回答并返回。

以天气查询问题为例,Agent 的工作流程会是:

  • 第一次调用:决定需要搜索上海天气 → 调用搜索工具
  • 第二次调用:决定需要搜索北京天气 → 调用搜索工具
  • 第三次调用:决定需要计算温差 → 调用计算器工具
  • 第四次调用:根据所有信息生成最终回答

五、工具系统的扩展与最佳实践
5.1 设计更复杂的工具
随着需求的增长,我们可能需要设计更复杂的工具。以下是一些扩展方向:

多参数工具:支持更复杂的输入,如 "预订机票" 工具需要出发地、目的地、日期等参数
认证工具:需要身份验证的工具,如邮件发送、云服务调用等
异步工具:处理耗时操作的工具,如数据导出、视频处理等
复合工具:由多个基础工具组合而成的高级工具
设计复杂工具时,应特别注意参数验证和错误处理,确保工具的可靠性和安全性。

5.2 工具调用的错误处理
在实际应用中,工具调用可能会出现各种错误,我们需要设计完善的错误处理机制:

参数错误:当参数缺失或格式不正确时,应返回清晰的错误信息,指导 Agent 正确调用
执行错误:工具执行过程中出现的错误,应记录详细日志并返回友好提示
超时错误:处理耗时操作时设置合理的超时时间,并提供重试机制
结果解析错误:当工具返回的结果格式不符合预期时,应进行适当的转换或提示
        良好的错误处理不仅能提高 Agent 的鲁棒性,还能帮助 Agent 从错误中学习,改进后续的工具调用策略。

5.3 工具使用的最佳实践
工具粒度设计:工具应保持适当的粒度,既不过于简单(增加调用次数),也不过于复杂(降低复用性)

渐进式工具暴露:不要一次性向 Agent 暴露过多工具,而是根据任务需求逐步添加,提高 Agent 的学习效率

工具版本管理:为工具设计版本机制,当工具功能变更时,能够平滑过渡

使用日志与分析:记录工具的使用情况,分析哪些工具最常用、哪些调用容易出错,为工具优化提供依据

安全性考虑:对工具进行权限控制,特别是涉及用户隐私或系统操作的工具,需添加必要的安全检查

六、总结与展望
6.1 本文要点总结
本文深入探讨了 AI Agent 工具使用的核心技术,主要内容包括:

工具是 Agent 能力的重要扩展,能够突破知识边界、增强计算能力并扩展操作范围

为 Agent 定义工具需要同时实现工具功能和提供详细的元数据描述

函数调用技术是 Agent 使用工具的标准方式,通过结构化的格式实现 LLM 与工具的交互

我们从零构建了一个具备搜索、计算和时间查询能力的 ReAct Agent,展示了工具使用的完整流程

工具系统的扩展需要考虑错误处理、安全性和最佳实践等因素

6.2 未来发展趋势
随着 AI 技术的不断发展,Agent 工具系统将呈现以下发展趋势:

工具生态化:形成丰富的工具市场,开发者可以为不同领域的 Agent 提供专业工具

自适应工具选择:Agent 能够根据任务特点和工具性能,自主选择最优工具组合

工具学习能力:Agent 能够通过实践学习如何更高效地使用工具,甚至发现新的工具使用方式

多模态工具:支持文本、图像、音频等多种模态的工具,扩展 Agent 的感知和表达能力

安全可信工具:具备可验证性和安全性保证的工具,确保 Agent 的操作可追溯、可控制

结语
        工具使用能力是 AI Agent 走向实用化的关键一步。通过本文介绍的方法,你可以为 Agent 添加各种工具,使其能够解决更复杂、更贴近实际需求的问题。

        从简单的计算器到复杂的 API 调用,从单一工具到工具生态,AI Agent 的工具使用能力将不断发展,为我们带来更智能、更便捷的服务。作为开发者,掌握 Agent 工具系统的设计与实现,将使你在 AI 应用开发中占据先机。

AI大模型学习福利

作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获取

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。


因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

四、AI大模型商业化落地方案

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值