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

第3讲:工具调用(Function Calling)——Agent 的“手”

在第一讲里,我们让 Agent 学会了“动嘴”(思考与对话),第二讲让它学会了“发声”(API 调用与流式输出)。但这还不够,真正的 Agent 必须能“动手”干活。

这一讲,我们将解锁大模型最强大的原生能力之一:Function Calling(工具调用)。这是大模型从“聊天机器人”进化为“智能体”的分水岭。


一、为什么需要 Function Calling?

如果没有 Function Calling,大模型就像一个被关在玻璃罩里的超级大脑:

  • 它能告诉你“北京现在天气是 25 度”,因为它背过书。

  • 但它无法直接去查实时的天气 API,也无法直接操作你的电脑、数据库或文件系统。

Function Calling 就是打破玻璃罩的那只手。它允许大模型在对话过程中,识别出用户的需求,并主动返回一个“调用指令”,告诉程序:“我要用某个工具,参数是 XXX”。


二、Function Calling 的核心原理

整个过程像一个“三幕剧”:

  1. 第一幕(声明):你告诉大模型:“我这里有几个工具,名字叫get_weathercalculator,分别用来查天气和算数学题。”

  2. 第二幕(决策):用户问:“今天北京热吗?顺便帮我算下 125 * 47”。大模型分析后,决定调用get_weathercalculator

  3. 第三幕(执行):大模型返回结构化的 JSON 数据(而非自然语言)。你的代码拿到这个 JSON,去真正执行对应的 Python 函数,然后把结果再喂给大模型,大模型最后组织成一句人话回答用户。


三、实战:打造一个带工具的 Agent

我们将创建一个具备“天气查询”和“计算器”能力的 Agent。

1. 定义工具集(Tool Schema)

首先,我们需要按照 OpenAI 兼容的格式,定义我们的工具:

# 工具定义列表 tools = [ { "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的当前天气温度(摄氏度)。", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称,如北京、上海"} }, "required": ["city"] } } }, { "type": "function", "function": { "name": "calculator", "description": "执行数学计算,支持加减乘除。", "parameters": { "type": "object", "properties": { "expression": {"type": "string", "description": "数学表达式,如 '125 * 47'"} }, "required": ["expression"] } } } ] # 本地对应的 Python 函数实现 def get_weather(city: str): # 模拟天气数据 API mock_data = {"北京": 25, "上海": 28, "广州": 32} temp = mock_data.get(city, 20) return f"{city}当前温度是 {temp}°C" def calculator(expression: str): # 安全计算:只允许数字和基础运算符 try: allowed_chars = set("0123456789+-*/.() ") if not all(c in allowed_chars for c in expression): return "表达式包含非法字符" result = eval(expression) # 注意:生产环境慎用 eval,这里仅为演示 return str(result) except: return "计算失败"
2. 构建 Agent 循环逻辑
import json import requests API_KEY = "你的API_KEY" BASE_URL = "https://api.deepseek.com/v1/chat/completions" def call_llm_with_tools(messages, tools): headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } payload = { "model": "deepseek-chat", "messages": messages, "tools": tools, "tool_choice": "auto", # 让模型自动决定是否调用工具 "temperature": 0.0 } response = requests.post(BASE_URL, headers=headers, json=payload) response.raise_for_status() return response.json() def agent_run_with_tools(user_input): messages = [ {"role": "system", "content": "你是一个有帮助的助手,可以调用工具获取天气或进行计算。"}, {"role": "user", "content": user_input} ] # 第一步:模型决策是否调用工具 response_data = call_llm_with_tools(messages, tools) message = response_data["choices"][0]["message"] # 检查是否有工具调用 if "tool_calls" in message and message["tool_calls"]: print("[模型决策]: 需要调用工具...") tool_call = message["tool_calls"][0] func_name = tool_call["function"]["name"] args = json.loads(tool_call["function"]["arguments"]) print(f"[调用工具]: {func_name}({args})") # 执行本地函数 if func_name == "get_weather": tool_result = get_weather(**args) elif func_name == "calculator": tool_result = calculator(**args) else: tool_result = "工具不存在" print(f"[工具返回]: {tool_result}") # 将工具结果返回给模型,让模型总结回答 messages.append(message) # 添加模型的调用请求 messages.append({ "role": "tool", "tool_call_id": tool_call["id"], "name": func_name, "content": tool_result }) # 第二步:模型根据工具结果生成最终回答 final_response = call_llm_with_tools(messages, tools) # 注意:第二次调用不需要传 tools,或者传空,视模型而定,这里为了简单省略 final_content = final_response["choices"][0]["message"]["content"] return final_content else: # 如果没有调用工具,直接返回内容 return message["content"] # 运行测试 if __name__ == "__main__": user_input = "今天北京热吗?顺便帮我算下 125 乘以 47 是多少?" output = agent_run_with_tools(user_input) print(f"\n[最终输出]: {output}")
3. 运行效果预览
[模型决策]: 需要调用工具... [调用工具]: get_weather({'city': '北京'}) [工具返回]: 北京当前温度是 25°C [调用工具]: calculator({'expression': '125 * 47'}) [工具返回]: 5875 [最终输出]: 北京当前温度是 25°C,不算太热。125 乘以 47 的结果是 5875。

四、避坑指南(排雷专区)

  1. 模型瞎编函数名(Hallucination)

    • 症状:模型返回的tool_calls里的name并不是你定义的get_weather,而是weather_api_v2

    • 解法:在system prompt中严格限定可用工具列表,并且开启strict模式(如果支持)。同时,代码中要有兜底判断:如果func_name不在我们的字典里,就返回错误,不要继续执行。

  2. 参数格式不对(JSON 解析失败)

    • 症状json.loads(tool_call["function"]["arguments"])报错。

    • 解法:大模型的参数生成有时不规范。务必在parameters定义中写清楚typerequired。如果解析失败,可以尝试让模型重新生成,或者使用正则提取。

  3. 无限递归调用

    • 症状:Agent 陷入死循环,一直调用同一个工具。

    • 解法:在代码中加入max_iter(最大迭代次数)限制,比如最多允许调用 3 次工具,超过就强制结束并提示用户。


五、课后作业

  1. 扩展工具集:增加一个名为search_web的工具(模拟返回一段文本),让 Agent 能回答“今天是几号?”这种需要实时知识的提问。

  2. 人工确认机制:修改代码,如果调用的是“删除文件”这类危险工具,Agent 不直接执行,而是输出:“即将执行删除操作,请确认[Y/N]”,只有在用户输入 Y 时才真正执行。

  3. 观察日志:在代码中打印出每一次messages的完整内容,观察大模型是如何通过role: tool的消息把控制权交还给模型的。


六、总结

这一讲我们掌握了:

  • Function Calling 的运作机制:声明 -> 决策 -> 执行 -> 反馈。

  • 如何让大模型调用本地 Python 函数:通过 JSON Schema 定义接口。

  • 构建了一个具备实际功能的 Agent:能查天气、能计算。

  • 学会了处理工具调用中的常见异常

至此,你的 Agent 已经拥有了“手脚”,不再是只会动嘴的聊天机器人。下一讲,我们将讲解ReAct 模式(思考 + 行动),让 Agent 学会像人类一样,在复杂任务中进行多步骤的推理和规划。


🧰 开发之余,处理 Base64、JWT 解析、JSON 格式化、Crontab 计算、PDF 合并压缩这些碎片需求,我常用一个纯前端本地工具箱:zz365.top(子页 PDF 大师:PDF 大师 - zz365工具箱)。所有计算在浏览器完成,文件不上传服务器,关页即清。免费、无登录、无广告,适合开发者当常驻标签页。

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

相关文章:

  • 2026年清镇个体户营业执照办理常见问题与服务关注点 - 兔兔不是荼荼
  • BOWELL战略升级打造工业AI时代的物理世界编译基础设施
  • AI赋能企业增长培训哪家强?2026年08月按课程落地成果选5家机构
  • 5分钟学会AI视频抠像:MatAnyone让普通人也能制作专业级视频
  • 3分钟解锁音乐自由:NCM解密工具完全指南
  • Mac的顶级屏幕+Popvee Player的强大功能,纪录片爱好者有福了
  • 2026不打滑菜刀实用品牌盘点:正规合规品牌筛选标准,选购避坑FAQ及场景适配详解 - 产业观察报
  • 终极指南:CheatEngine-DMA插件完整配置与ZDMA设备支持
  • 嵌入式开发八大通信协议详解:从UART到CAN的实战指南
  • 提升 iOS 开发效率的必备工具清单,IDE和调试测试可以用哪些软件
  • 抖音评论采集终极教程:3步批量提取评论数据,无需编程经验
  • 2026年盐城收银系统服务商深度盘点:数字化转型的“风向标”,微城市网络科技领跑全城 - 微城市网络
  • 非金属材料特性与应用全解析
  • ThreadLocal介绍
  • Firefox Reality 虚拟现实浏览器深度解析与配置指南
  • CTF隐写术实战:从爱因斯坦题目解析多层文件隐藏技巧
  • VMware Unlocker 4.2.8:在普通PC上免费运行macOS虚拟机的完整指南
  • Dubbo框架常见报错排查与优化指南
  • PHP幂等性实现方案与最佳实践
  • 高压断路器试验是什么?有哪些测试项目? - HVHIPOT
  • 三步找回QQ空间全部历史说说的完整技术指南
  • CentOS 7下Oracle 11g静默安装全攻略:从环境配置到安全加固
  • 2026年沈阳合规代账挑选攻略:鲜克财税服务梳理与行业避坑指南 - 比奇堡111
  • Wand-Enhancer:开源WeMod增强工具完全指南
  • 文物复原软件精度测试:三维扫描与数字建模关键技术
  • Lenovo Legion Toolkit终极指南:5个技巧释放拯救者笔记本全部潜能
  • 健身场馆智慧化系统开发哪家靠谱?器械蓝牙对接方案
  • 【信息科学与工程学】【数据中心】计算机科学与自动化——第三百零五篇 数据中心 Scale-Up、Scale-Out、Scale-Across101 芯片接入数据中心125
  • Hive 4.2.0部署指南:从环境准备到生产调优
  • 2026免费Excel转PDF保姆级教程:多Sheet全攻略,本地安全不泄露 - 时时资讯