当前位置: 首页 > news >正文

AI Agent 工具调用系统设计:让大模型掌控世界

AI Agent 工具调用系统设计:让大模型掌控世界

前言

工具调用(Tool Use / Function Calling)是 AI Agent 实现复杂任务的关键能力。通过工具调用,大模型可以与外部世界交互,执行计算、查询数据库、调用 API,真正变成一个能够"行动"的智能体。

我之前设计的代码审查 Agent 就有完善的工具调用能力,可以搜索文档、读写文件、执行代码。工具调用系统设计得好不好,直接决定了 Agent 的能力边界。今天分享一些我在实践中总结的设计模式和经验。

工具调用的基本原理

什么是 Function Calling

Function Calling 是让大模型生成结构化调用指令的能力:

# OpenAI API 的 Function Calling 示例 response = client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": "北京今天天气怎么样?"}], tools=[ { "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的天气信息", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "温度单位" } }, "required": ["city"] } } } ] ) # 模型可能返回: # { # "tool_calls": [{ # "id": "call_xxx", # "function": { # "name": "get_weather", # "arguments": '{"city": "北京", "unit": "celsius"}' # } # }] # }

工具调用的工作流程

用户输入 → LLM 判断是否需要工具 → 是 → 解析工具和参数 → 执行工具 → 返回结果 → LLM 生成最终回答 ↓ 否 直接生成回答

工具描述设计

Schema 设计原则

工具的 JSON Schema 描述直接决定模型能否正确理解和使用工具:

# 好的工具描述示例 good_tool_schema = { "name": "search_documents", "description": "在企业知识库中搜索相关文档。适用于查找技术文档、API说明、最佳实践等。", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "搜索查询语句,建议使用完整的问句或关键词组合。例如:'如何使用 REST API' 或 'REST API 使用方法'" }, "top_k": { "type": "integer", "description": "返回的最多文档数量,默认 5", "default": 5 } }, "required": ["query"] } } # 不好的工具描述示例 bad_tool_schema = { "name": "search", "description": "搜索", "parameters": { "type": "object", "properties": { "q": {"type": "string"} }, "required": ["q"] } }

工具分组策略

当工具数量很多时,应该进行分组:

TOOL_GROUPS = { "information": { "description": "信息查询类工具", "tools": ["search_documents", "get_weather", "get_time"] }, "code": { "description": "代码处理类工具", "tools": ["execute_code", "read_file", "write_file"] }, "communication": { "description": "通信类工具", "tools": ["send_email", "send_message"] } } def get_tools_for_scenario(scenario: str) -> List[dict]: """根据场景选择合适的工具组""" if scenario == "technical_support": return [get_tool_schema("information"), get_tool_schema("code")] elif scenario == "business": return [get_tool_schema("information"), get_tool_schema("communication")] else: return get_all_tools()

工具执行框架

基础执行器

from dataclasses import dataclass from typing import Dict, List, Callable, Any import json @dataclass class ToolCall: """工具调用请求""" id: str name: str arguments: dict @dataclass class ToolResult: """工具执行结果""" call_id: str success: bool result: Any error: str = None class ToolExecutor: """工具执行器""" def __init__(self): self.tools: Dict[str, Callable] = {} self.schemas: Dict[str, dict] = {} def register(self, name: str, schema: dict, func: Callable): """注册工具""" self.tools[name] = func self.schemas[name] = schema def execute(self, call: ToolCall) -> ToolResult: """执行工具调用""" if call.name not in self.tools: return ToolResult( call_id=call.id, success=False, result=None, error=f"Unknown tool: {call.name}" ) try: func = self.tools[call.name] result = func(**call.arguments) return ToolResult( call_id=call.id, success=True, result=result ) except Exception as e: return ToolResult( call_id=call.id, success=False, result=None, error=str(e) ) def execute_batch(self, calls: List[ToolCall]) -> List[ToolResult]: """批量执行工具调用""" return [self.execute(call) for call in calls]

实际工具实现

# 注册实际工具 executor = ToolExecutor() # 搜索引擎 def search_web(query: str, num_results: int = 5) -> dict: """搜索网页""" # 实际实现调用搜索引擎 API results = google_search(query, num_results) return { "query": query, "results": [ { "title": r.title, "snippet": r.snippet, "url": r.url } for r in results ] } executor.register( "search_web", { "name": "search_web", "description": "搜索网页获取最新信息。适用于查询实时新闻、未知问题等。", "parameters": { "type": "object", "properties": { "query": {"type": "string", "description": "搜索查询"}, "num_results": { "type": "integer", "description": "返回结果数量", "default": 5 } }, "required": ["query"] } }, search_web ) # 计算器 def calculator(expression: str) -> dict: """安全计算数学表达式""" # 只允许基本运算 allowed_chars = set("0123456789+-*/.() ") if not all(c in allowed_chars for c in expression): return {"error": "不允许的字符"} try: result = eval(expression) return {"expression": expression, "result": result} except Exception as e: return {"error": str(e)} executor.register( "calculator", { "name": "calculator", "description": "计算数学表达式的值。只支持基本运算符:加(+)、减(-)、乘(*)、除(/)。", "parameters": { "type": "object", "properties": { "expression": { "type": "string", "description": "数学表达式,例如:2+3*4 或 (10+5)/3" } }, "required": ["expression"] } }, calculator )

Agent 工具调用循环

完整的工具调用循环

class ToolUsingAgent: """支持工具调用的 Agent""" def __init__(self, llm, executor: ToolExecutor, max_iterations: int = 10): self.llm = llm self.executor = executor self.max_iterations = max_iterations def run(self, user_input: str) -> str: """运行 Agent 处理用户输入""" messages = [ {"role": "system", "content": self._get_system_prompt()}, {"role": "user", "content": user_input} ] for iteration in range(self.max_iterations): # 1. 获取 LLM 响应 response = self.llm.chat(messages, tools=self.executor.get_all_schemas()) # 2. 检查是否有工具调用 if not response.tool_calls: # 没有工具调用,直接返回 return response.content # 3. 执行工具调用 tool_results = [] for call in response.tool_calls: tool_call = ToolCall( id=call.id, name=call.function.name, arguments=json.loads(call.function.arguments) ) result = self.executor.execute(tool_call) tool_results.append(result) # 4. 将结果添加到消息 messages.append(response.to_message()) for result in tool_results: messages.append({ "role": "tool", "tool_call_id": result.call_id, "content": json.dumps(result.result) if result.success else result.error }) return "达到最大迭代次数" def _get_system_prompt(self) -> str: return """你是一个智能助手,可以通过调用工具来完成任务。 Available tools: """ + self.executor.get_tools_description()

结果验证与重试

class VerifiedToolExecutor(ToolExecutor): """带验证的工具执行器""" def execute_with_verification( self, call: ToolCall, expected_format: dict = None ) -> ToolResult: """执行并验证结果""" result = self.execute(call) if not result.success: return result if expected_format: # 验证结果格式 is_valid, error = self._verify_format(result.result, expected_format) if not is_valid: return ToolResult( call_id=call.id, success=False, result=None, error=f"Result format error: {error}" ) return result def _verify_format(self, result: Any, expected: dict) -> tuple: """验证结果格式""" if expected.get("type") == "array": if not isinstance(result, list): return False, "Expected array" elif expected.get("type") == "object": if not isinstance(result, dict): return False, "Expected object" for key in expected.get("required", []): if key not in result: return False, f"Missing required field: {key}" return True, None

工具调用的安全考虑

输入验证

class SecureToolExecutor(ToolExecutor): """安全的工具执行器""" def execute(self, call: ToolCall) -> ToolResult: # 1. 参数验证 schema = self.schemas.get(call.name) if not schema: return ToolResult(call.id, False, None, "Unknown tool") # 检查必需参数 required = schema.get("parameters", {}).get("required", []) for param in required: if param not in call.arguments: return ToolResult( call.id, False, None, f"Missing required parameter: {param}" ) # 2. 值域检查 properties = schema.get("parameters", {}).get("properties", {}) for param, spec in properties.items(): if param in call.arguments: value = call.arguments[param] if not self._validate_param_value(value, spec): return ToolResult( call.id, False, None, f"Invalid value for parameter {param}" ) # 3. 敏感操作检查 if self._is_sensitive_operation(call): # 记录审计日志 self._audit_log(call) return super().execute(call) def _validate_param_value(self, value, spec) -> bool: """验证参数值""" param_type = spec.get("type") if param_type == "string": if "maxLength" in spec and len(value) > spec["maxLength"]: return False if "enum" in spec and value not in spec["enum"]: return False elif param_type == "integer": if not isinstance(value, int): return False if "minimum" in spec and value < spec["minimum"]: return False if "maximum" in spec and value > spec["maximum"]: return False return True def _is_sensitive_operation(self, call: ToolCall) -> bool: """检查是否为敏感操作""" sensitive_tools = {"delete_file", "send_email", "execute_sql"} return call.name in sensitive_tools

异步工具调用

import asyncio from typing import List class AsyncToolExecutor: """异步工具执行器""" def __init__(self): self.tools: Dict[str, Callable] = {} def register(self, name: str, func: Callable): self.tools[name] = func async def execute_async(self, call: ToolCall) -> ToolResult: """异步执行单个工具""" if call.name not in self.tools: return ToolResult(call.id, False, None, "Unknown tool") try: func = self.tools[call.name] # 如果是异步函数 if asyncio.iscoroutinefunction(func): result = await func(**call.arguments) else: # 在线程池中运行同步函数 loop = asyncio.get_event_loop() result = await loop.run_in_executor(None, lambda: func(**call.arguments)) return ToolResult(call.id, True, result) except Exception as e: return ToolResult(call.id, False, None, str(e)) async def execute_batch_async(self, calls: List[ToolCall]) -> List[ToolResult]: """批量异步执行""" tasks = [self.execute_async(call) for call in calls] return await asyncio.gather(*tasks)

工具调用优化

并行 vs 串行

class SmartToolExecutor(ToolExecutor): """智能工具执行器""" def execute_with_plan(self, calls: List[ToolCall]) -> List[ToolResult]: """分析依赖关系并优化执行顺序""" # 1. 构建依赖图 dependencies = self._build_dependency_graph(calls) # 2. 找出可以并行的调用 independent_calls = [ call for call in calls if not dependencies.get(call.id) ] # 3. 并行执行独立的调用 parallel_results = self.execute_batch(independent_calls) # 4. 串行执行有依赖的调用 remaining_calls = [c for c in calls if c.id in dependencies] serial_results = [] for call in remaining_calls: deps = dependencies[call.id] # 等待依赖完成 for dep_id in deps: self._wait_for_result(dep_id) result = self.execute(call) serial_results.append(result) return parallel_results + serial_results

总结

工具调用是 AI Agent 实现复杂任务的核心能力。设计一个好的工具调用系统需要考虑:

  1. 清晰的工具描述:让模型准确理解工具用途和参数
  2. 健壮的执行框架:错误处理、验证、重试机制
  3. 安全保障:输入验证、敏感操作审计
  4. 性能优化:并行执行、依赖分析

希望这些经验对大家有帮助。

http://www.jsqmd.com/news/864299/

相关文章:

  • ST STM32F407VET6代理商
  • 碧蓝航线全皮肤解锁终极指南:Perseus补丁5分钟快速上手教程
  • 2023B卷,响应报文时间
  • 终极Mac抢票解决方案:12306ForMac让你的购票体验飞起来
  • MetaFormer架构深度解读:为什么说PoolFormer的成功,揭示了Transformer家族的本质?
  • 东南大学论文模板:告别格式烦恼,专注学术创新的8倍效率解决方案
  • MYIR-ZYNQ7000系列-zturn教程(16):对axi_lite IP核进行仿真以及axi总线的初步讲解
  • AI专著撰写新利器!一键生成20万字专著,高效又便捷的写作体验!
  • DazToBlender插件终极指南:如何实现Daz Studio到Blender的无缝资产迁移
  • GitHub中文插件:3分钟让GitHub界面全面中文化,提升中文开发者效率的终极方案
  • 预训练模型技术演进史:从Word2Vec到多模态大模型
  • 蔚蓝档案主题鼠标指针:5分钟快速安装指南
  • 2026溧阳市本地人必选的瓷砖空鼓专业维修公司TOP5推荐!卫生间空鼓翘边,厨房空鼓翘边,客厅空鼓翘边,全天响应,免费上门,5月专业瓷砖空鼓修复公司持证上岗师傅排名最新深度调研方案) - 一休修缮
  • 基于AI流动性监测模型的黄金波动分析:油价跳水与美元回落下的黄金震荡企稳机制解析
  • 如何在浏览器中免费制作专业电子书:EPubBuilder完整指南
  • 如何利用 AI Agent 优化日常办公自动化流程?
  • 2026跑遍武汉:哪家店回收名表最爽快?检测流程和压价幅度全对比 - 李宏哲1
  • 3步解锁百度网盘全速下载:baidu-wangpan-parse技术解析与应用实践
  • 2026临安市本地人必选的瓷砖空鼓专业维修公司TOP5推荐!卫生间空鼓翘边,厨房空鼓翘边,客厅空鼓翘边,全天响应,免费上门,5月专业瓷砖空鼓修复公司持证上岗师傅排名最新深度调研方案) - 一休修缮
  • 3分钟搞定Android开发环境:Windows平台ADB驱动终极安装指南
  • G-Helper终极指南:3步释放华硕笔记本完整性能的轻量控制革命
  • Windows任务栏透明美化神器:5分钟掌握TranslucentTB完整使用指南
  • 仅限云南开发者获取:ElevenLabs方言微调私有API密钥申请通道(含已通过审核的12家本地企业白名单参考)
  • iOS与Android市场份额变动背后的多维动因与未来趋势
  • 别再乱用set_clock_group了!搞懂异步时钟、逻辑/物理独立时钟的实战区别与避坑指南
  • DroidCam OBS Plugin终极指南:将手机秒变专业摄像头
  • 大润发购物卡回收:几分钟就能完成的便捷变现方式 - 团团收购物卡回收
  • 2026林芝市本地人必选的瓷砖空鼓专业维修公司TOP5推荐!卫生间空鼓翘边,厨房空鼓翘边,客厅空鼓翘边,全天响应,免费上门,5月专业瓷砖空鼓修复公司持证上岗师傅排名最新深度调研方案) - 一休修缮
  • 突发环境事件怎么模拟?用Python+GIS实现高斯烟团模型(附完整代码)
  • IDEA配置Tomcat热部署翻车实录:war exploded模式启动失败?看这篇就够了