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

Function Calling实战:让大模型调用外部工具

Function Calling:让 AI 学会用工具

LLM 再聪明也只是「脑子」,没有手和脚。Function Calling 就是给 AI 装上手脚——让它能查天气、发邮件、操作数据库。这是 Agent 开发最核心的一课。


一、LLM 的先天缺陷

LLM 训练数据有截止日期,它不知道今天几号、你的数据库里有什么、你的 API 能不能用。直接问它:

问:帮我查一下北京现在的天气 答:抱歉,我无法获取实时天气信息。建议您查看天气预报网站...

但如果你告诉它「有一个 get_weather 函数可以用」,它就能输出一个结构化的调用请求,由你的程序去真正执行。


二、Function Calling 的工作原理

┌──────────┐ ① 用户提问 + 工具描述 ┌──────────┐ │ │ ──────────────────────────→ │ │ │ 你的程序 │ │ LLM │ │ │ ←────────────────────────── │ │ └──────────┘ ② LLM 返回:应该调哪个工具 └──────────┘ │ 调什么参数 │ ③ 真的去执行 │ ▼ ┌──────────┐ ④ 执行结果 → LLM ┌──────────┐ │ 天气 API │ ──────────────────────────→ │ LLM │ │ 数据库 │ │ │ │ 文件系统 │ ←────────────────────────── │ │ └──────────┘ ⑤ LLM 用自然语言回复用户 └──────────┘

LLM 不执行工具,它只是说「应该调哪个工具、传什么参数」。真正执行的是你的代码。


三、完整实战代码

3.1 定义工具

importjsonimporthttpxfromopenaiimportAsyncOpenAI client=AsyncOpenAI(api_key="your-api-key",base_url="https://api.deepseek.com/v1")# ── 定义工具(用 JSON Schema 描述) ──tools=[{"type":"function","function":{"name":"get_weather","description":"获取指定城市的当前天气,包括温度、天气状况和湿度","parameters":{"type":"object","properties":{"city":{"type":"string","description":"城市名称,如 北京、上海"}},"required":["city"]}}},{"type":"function","function":{"name":"calculate","description":"执行数学计算,支持 +、-、*、/ 和括号","parameters":{"type":"object","properties":{"expression":{"type":"string","description":"数学表达式,如 '23 * 47 + 15 * 8'"}},"required":["expression"]}}}]

3.2 工具的实际实现

asyncdefget_weather(city:str)->str:"""模拟天气查询(生产环境替换为真实 API)"""# 实际项目用 httpx 调天气 API# response = await httpx.AsyncClient().get(f"https://api.weather.com?city={city}")weather_db={"北京":"晴,25°C,湿度 40%","上海":"多云,28°C,湿度 65%","深圳":"阵雨,30°C,湿度 80%",}returnweather_db.get(city,f"未找到{city}的天气数据")defcalculate(expression:str)->str:"""安全执行数学计算"""# ⚠️ 生产环境不要用 eval!这里仅作示例importastimportoperator allowed_ops={ast.Add:operator.add,ast.Sub:operator.sub,ast.Mult:operator.mul,ast.Div:operator.truediv,}try:result=eval(expression,{"__builtins__":{}},{})returnstr(result)exceptExceptionase:returnf"计算错误:{e}"

3.3 Agent 主循环

importasyncio# 工具名 → 实际函数available_functions={"get_weather":get_weather,"calculate":calculate,}SYSTEM_PROMPT="""你是一个智能助手,可以查询天气和执行计算。 - 用户问天气时,调用 get_weather 工具 - 用户问计算时,调用 calculate 工具 - 获取结果后,用友好的中文回复用户"""asyncdefagent_loop(user_input:str):messages=[{"role":"system","content":SYSTEM_PROMPT},{"role":"user","content":user_input}]# 第一步:询问 LLM 是否需要调工具response=awaitclient.chat.completions.create(model="deepseek-chat",messages=messages,tools=tools,tool_choice="auto"# 让 LLM 自己决定要不要用工具)msg=response.choices[0].message# 如果 LLM 决定调工具ifmsg.tool_calls:fortool_callinmsg.tool_calls:func_name=tool_call.function.name func_args=json.loads(tool_call.function.arguments)print(f"🔧 调用工具:{func_name}({func_args})")# 真正执行func=available_functions[func_name]ifasyncio.iscoroutinefunction(func):result=awaitfunc(**func_args)else:result=func(**func_args)# 把工具调用的结果追加到对话中messages.append(msg)# LLM 的工具调用消息messages.append({"role":"tool","tool_call_id":tool_call.id,"content":result})# 第二步:把结果交给 LLM,让它生成友好回复final_response=awaitclient.chat.completions.create(model="deepseek-chat",messages=messages)returnfinal_response.choices[0].message.content# 如果不需要工具,直接返回returnmsg.content# ── 测试 ──asyncdefmain():# 测试 1:天气查询print("="*50)print("用户:北京现在天气怎么样?")reply=awaitagent_loop("北京现在天气怎么样?")print(f"助手:{reply}")print()# 测试 2:计算print("="*50)print("用户:23 × 47 + 15 × 8 等于多少?")reply=awaitagent_loop("23 × 47 + 15 × 8 等于多少?")print(f"助手:{reply}")print()# 测试 3:不需要工具print("="*50)print("用户:你好,请介绍一下你自己")reply=awaitagent_loop("你好,请介绍一下你自己")print(f"助手:{reply}")asyncio.run(main())

运行结果

用户:北京现在天气怎么样? 🔧 调用工具:get_weather({'city': '北京'}) 助手:北京现在是晴天,温度 25°C,湿度 40%,很适合出门哦! 用户:23 × 47 + 15 × 8 等于多少? 🔧 调用工具:calculate({'expression': '23 * 47 + 15 * 8'}) 助手:23 × 47 + 15 × 8 = 1201 用户:你好,请介绍一下你自己 助手:你好!我是智能助手,可以帮你查天气、做计算...

四、Function Calling 的进阶技巧

4.1 并行调用

LLM 可以一次返回多个 tool_call:

# 用户问:"北京和上海今天天气怎么样?"# LLM 一次返回两个 tool_call:tool_calls=[{"name":"get_weather","arguments":{"city":"北京"}},{"name":"get_weather","arguments":{"city":"上海"}}]# 并行执行importasyncio tasks=[get_weather(c["arguments"]["city"])forcintool_calls]results=awaitasyncio.gather(*tasks)

4.2 工具描述就是「API 文档」

工具 JSON Schema 写得越详细,LLM 选工具越准:

# ❌ 太模糊{"name":"search","description":"搜索东西","parameters":{"q":{"type":"string"}}}# ✅ 精确{"name":"search_harmonyos_docs","description":"搜索华为 HarmonyOS 官方文档,返回相关的 API 说明和代码示例。关键词应使用中文技术术语。","parameters":{"query":{"type":"string","description":"中文搜索关键词,如 '状态管理'、'页面路由'"},"api_version":{"type":"string","enum":["API12","API11"],"default":"API12"}}}

4.3 错误处理:工具调用失败怎么办

try:result=func(**args)exceptExceptionase:result=f"工具调用失败:{e}。请告诉用户暂时无法完成此操作。"# LLM 拿到错误信息后会自动向用户解释

五、Function Calling vs MCP

这是面试高频题:

维度Function CallingMCP
是什么LLM 的推理能力通信协议
谁定义使用方(你的代码)工具提供方(MCP Server)
复用性每次要重新定义写一个 Server,到处用
关系LLM 的输出格式工具的标准接入方式

MCP 底层也会触发 Function Calling。MCP 解决的是「工具从哪来」,Function Calling 解决的是「LLM 怎么选工具」。


六、生产实战:这些坑你迟早会踩

6.1 工具调用失败后的重试策略

Agent 最怕的不是工具失败,是工具失败了 LLM 以为成功了。

# 三种重试策略asyncdefexecute_tool_with_retry(func_name:str,args:dict,max_retries:int=2):"""工具调用 + 智能重试"""last_error=Noneforattemptinrange(max_retries+1):try:result=awaitavailable_functions[func_name](**args)return{"success":True,"result":result}exceptTimeoutError:last_error=f"超时(第{attempt+1}次尝试)"awaitasyncio.sleep(2**attempt)# 指数退避exceptValueErrorase:# 参数错误 → 不用重试,让 LLM 修正参数return{"success":False,"error":f"参数错误:{e}","retry_with_fix":True}exceptExceptionase:last_error=str(e)return{"success":False,"error":last_error}

6.2 并行调用的陷阱

# ❌ 错误:串行调天气forcityin["北京","上海","深圳","广州"]:weather=awaitget_weather(city)# ✅ 正确:并行调天气results=awaitasyncio.gather(get_weather("北京"),get_weather("上海"),get_weather("深圳"),get_weather("广州"),return_exceptions=True# 一个失败不影响其他的)

生产环境经验:10 个城市并行查天气,串行要 2 秒(每个 200ms),并行只要 200ms。但要注意 API rate limit——并行太多可能被限流。

6.3 工具 Schema 写得不好 = LLM 选错工具

# ❌ 两个工具描述太像,LLM 不知道该用哪个{"name":"search_docs","description":"搜索文档"}{"name":"search_code","description":"搜索代码"}# ✅ 差异化描述{"name":"search_harmonyos_docs","description":"搜索华为官方 HarmonyOS 开发文档,返回 API 说明和代码示例。适用场景:用户问 API 用法、组件属性等。"}{"name":"search_csdn_articles","description":"搜索 CSDN 博客的技术文章,返回实战经验和踩坑记录。适用场景:用户问怎么做、有什么坑等。"}

经验法则:如果你在描述里加一个「不适用场景」,LLM 选错的概率降 30%。

6.4 流式输出 + 工具调用

Agent 对话中用户期待实时反馈,但工具调用是阻塞的:

asyncdefagent_loop_streaming(user_input:str):"""流式 Agent,工具调用时发送状态更新"""yield{"type":"status","text":"正在分析你的问题..."}# ... LLM 决定调用工具 ...yield{"type":"status","text":f"正在调用{tool_name}..."}result=awaitexecute_tool(tool_name,tool_args)yield{"type":"tool_result","text":f"{tool_name}返回:{result[:100]}..."}# ... LLM 生成最终回答 ...asyncforchunkinllm.chat_stream(messages):yield{"type":"chunk","text":chunk}

用户盯着空白界面等 3 秒会觉得卡。每步发一个状态更新,体验完全不同。


六、总结

  1. Function Calling 是 Agent 的核心——没有它,LLM 只是个聊天机器人
  2. LLM 不执行——它只说用什么工具,真正执行的是你的代码
  3. 工具描述决定准确率——JSON Schema 写得好,LLM 才能选对工具
  4. 并行调用——多个独立工具可以同时执行,提升性能
  5. Function Calling 是 MCP 的底层机制——理解它才能理解 Agent 生态

下一篇:《RAG:给 LLM 装上知识库》——向量数据库、Embedding、Chunking,从原理到完整可运行的 RAG 系统。

系列文章:00-总纲 → ①-LLM 原理 → ②-Prompt 工程 → ③-Function Calling → ④-RAG → ⑤-Agent 模式 → ⑥-LangGraph → ⑦-MCP → ⑧-Multi-Agent

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

相关文章:

  • 嵌入式开发实战:从防御性编程到安全启动,构建高可靠系统的核心方法论
  • 2026电动空压机租赁技术指南:空压机销售、静音发电机出租、发电机保养、发电机组回收、发电机维修、发电机销售、工地发电机组租赁选择指南 - 优质品牌商家
  • 给Arduino和STM32玩家的TSL1401CL线性CCD对比测评:时序、精度与易用性谁更强?
  • 2025届必备的降重复率助手推荐榜单
  • 基于Adafruit Trinket的敲击测速节拍器DIY:嵌入式开发实战
  • Elasticsearch:混合搜索新范式 - 零样本排序融合实战 (RRF)
  • 从递归到滚动数组:爬楼梯问题的四种解法演进与实战剖析
  • 基于CircuitPython与NeoPixel的智能婴儿床挂饰:蓝牙控制与声光互动实践
  • 2025届最火的十大AI写作平台横评
  • 基于Arduino Yun与eTape传感器的智能液位监测系统构建指南
  • 工单数据分层序列化:全量保留+高效处理方案
  • 从电源拓扑到代码:STM32F103移相全桥DCDC数字控制入门实践(附完整工程)
  • 安全数组类模板
  • NotebookLM引用格式生成突然失准?紧急预警:2024年Q2模型微调导致DOI解析兼容性降级(含临时修复Patch)
  • vue基于springboot框架的校园生活智慧服务平台
  • Spring Boot条件装配原理
  • 毕业写作提质利器盘点:9 大 AI 论文创作工具实测,okbiye 稳居实用首选
  • FPGA驱动RGB屏幕时序详解:从VGA原理到480x272分辨率实战调试记录
  • 基于RP2040与CircuitPython打造可编程USB媒体旋钮:从硬件组装到代码自定义
  • TPS61088RHLR升压芯片:从数据手册到实战PCB设计的完整指南
  • Figma中文界面插件:设计师告别英文困扰的终极解决方案
  • Multi-Agent系统生产环境架构设计:可扩展性、高可用与弹性伸缩完整方案
  • 深度强化学习在无人机控制中的挑战与优化策略
  • 项目管理工具在2026年迎来哪些关键变革?
  • 2026Q2全自动啤酒机厂家名录:四川啤酒机设备/四川精酿啤酒供应链/四川精酿啤酒厂家/成都啤酒机供货商/成都精酿啤酒供应链/选择指南 - 优质品牌商家
  • 树莓派/BeagleBone连接TMP006红外测温传感器Python实战指南
  • 静态站点生成器打造个人导航页:配置驱动与自动化部署实践
  • SMARC模块化电脑标准:嵌入式系统设计、选型与集成实战指南
  • 告别硬件SPI!用Arduino模拟SPI搞定LD3320语音识别的完整指南
  • 2026实验室可燃气体报警器检定装置标杆名录:小型可燃气体报警器检定装置/工业用可燃气体报警器检定装置/工业用配气仪/选择指南 - 优质品牌商家