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

DeepSeek Function Calling 原理与天气查询实战

1. 为什么“查天气”是 Function Calling 的黄金入门题

很多人第一次听说 Function Calling,脑子里浮现的可能是“调用数据库”“执行支付”“生成PDF”这类听起来就“很重”的操作。但真正让我在凌晨三点拍着桌子喊出“原来如此”的,是第一次让模型成功返回“北京今天最高气温26℃,东南风3级”——就这短短一句话,背后串起了整个大模型与现实世界交互的逻辑闭环。

“查天气”之所以成为 Function Calling 的经典教学案例,根本原因在于它完美复刻了人类使用工具的最小认知单元:有明确输入(城市名)、有确定输出(结构化天气数据)、有稳定外部服务(气象API)、有可验证结果(你打开手机天气App就能立刻对答案)。它不像“写一封辞职信”那样纯文本生成,也不像“分析财报”那样依赖模型内部知识,而是强制模型走出“自说自话”的舒适区,学会“先想清楚要什么,再去找谁要,最后把结果组装好”。

我带过十几期LLM工程实践小班,发现一个惊人规律:凡是卡在 Function Calling 理解上的同学,90%不是败在代码上,而是卡在“为什么非得这么绕”。他们觉得:“模型自己不就知道天气吗?干嘛还要调API?”——这恰恰暴露了对大模型本质的误解。DeepSeek-R1 或 DeepSeek-VL 这类模型,它的知识截止于训练数据,它无法知道“今天上海是否下雨”,就像你无法靠背《新华字典》预测明天的股价。Function Calling 不是给模型“加功能”,而是给它配了一部能实时拨号的电话。模型负责判断“该打给谁”“问什么问题”,而真正的信息获取,交给专门的、可信赖的、实时更新的外部服务。

更关键的是,“查天气”这个任务天然具备三层验证能力:第一层是语法验证——模型生成的 function call JSON 是否符合你定义的 schema;第二层是逻辑验证——它调用的函数名、传入的参数是否合理(比如传了个“火星市”进去,你就知道它没理解上下文);第三层是结果验证——API返回的数据能否被模型准确提取并组织成自然语言回答。这三层验证,构成了一个极短的反馈回路,让你能在5分钟内完成“写定义→跑调用→看报错→改提示词→再试”的完整闭环。而那些一上来就搞“自动订机票+查酒店+生成行程单”的同学,往往卡在第三步整整两天,连错误日志都看不懂。

所以,别小看这个“查天气”。它不是玩具,而是你和大模型之间建立信任关系的第一张契约。当你亲眼看到模型主动放弃胡编乱造,转而老老实实调用你指定的函数、等待返回、再把结果嚼碎了喂给你时,你就真正跨过了那道门槛——从此,它不再是个会说话的鹦鹉,而是一个开始学会借力、懂得协作的智能体。

2. DeepSeek 原生 Function Calling 的底层机制拆解

很多教程直接甩出一段tools = [...]的 JSON 定义,然后告诉你“复制粘贴就能跑”,却从不解释:DeepSeek 是怎么“看懂”这段定义的?它凭什么相信你写的get_weather就是查天气,而不是查股票?这个过程远比“模型读JSON”要精巧得多。

DeepSeek 的 Function Calling 并非简单地做字符串匹配或正则提取。它的核心是一套双阶段语义理解引擎,我把它拆解为“意图识别层”和“参数绑定层”,二者缺一不可。

2.1 意图识别层:模型如何决定“该不该调用”以及“调用哪个”

当你把 tools 列表传给 DeepSeek API 时,模型首先做的,不是解析 JSON Schema,而是进行一次增强型指令微调推理(Enhanced Instruction Tuning Inference)。它会将你的用户提问、系统提示词(system prompt)、以及所有 tools 的 description 字段,全部拼接进一个超长上下文窗口,然后运行一次轻量级的“决策前向传播”。

举个真实例子。假设你定义了两个工具:

{ "name": "get_weather", "description": "获取指定城市的当前天气状况,包括温度、湿度、风速和天气现象", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,如北京、上海" } } } }, { "name": "get_stock_price", "description": "获取指定股票代码的最新收盘价和涨跌幅", "parameters": { "type": "object", "properties": { "symbol": { "type": "string", "description": "股票代码,如 AAPL, 600519.SH" } } } }

当用户问:“北京今天热不热?”
模型在意图识别层会计算:

  • get_weather.description中的“城市”“天气状况”“温度”与“北京”“今天”“热”高度语义匹配,得分 0.92;
  • get_stock_price.description中的“股票代码”“收盘价”与问题零相关,得分 0.03。

于是它果断选择get_weather,并压制掉所有其他工具的调用概率。这个过程不是硬编码规则,而是模型在千万级工具调用对(question + tool_name)上微调出来的语义对齐能力。这也是为什么 DeepSeek-V4-Pro 对中文工具描述的理解远超早期开源模型——它的训练数据里,塞进了大量中文 API 文档、钉钉/飞书机器人配置说明、甚至淘宝开放平台的接口手册。

提示:如果你发现模型总调错函数,第一反应不应该是改代码,而是重写description。把“查询用户订单状态”改成“根据用户手机号,从订单中心数据库查询其最近3笔订单的物流状态和预计送达时间”,模型识别准确率能提升40%。描述越具体、越贴近真实业务场景,模型的意图识别就越稳。

2.2 参数绑定层:模型如何从一句话里精准抠出“北京”这两个字

意图定了,接下来是更难的活:从“北京今天热不热?”里,精准提取出{"city": "北京"}。这一步叫槽位填充(Slot Filling),是 NLU(自然语言理解)领域的经典难题。

DeepSeek 的处理方式非常务实:它不追求100%泛化,而是采用强约束 Schema 引导式抽取。模型会把parameters中每个字段的typedescription当作“填空提示”,强行将用户输入映射到这些预设字段上。

继续上面的例子:

  • city字段的typestringdescription是“城市名称,如北京、上海”;
  • 模型会扫描输入句,找到所有符合“地名实体”的片段(NER识别),再结合description中的示例“北京、上海”,确认“北京”就是目标值;
  • 如果用户问:“我住在上海浦东,那边现在啥天气?”,模型会忽略“浦东”(因为description明确说“城市名称”,而浦东是区),只提取“上海”。

这个机制带来一个关键实操结论:parameters的设计,本质上是在教模型“你要关注什么”。你写"city": {"type": "string"},模型就只找字符串;你写"date": {"type": "string", "description": "日期,格式为YYYY-MM-DD,例如2024-05-20"},模型就会尝试把“明天”“后天”“下周三”全部转换成标准格式。它不是在猜,而是在按你画的格子填。

我在线上调试时发现一个高频坑:有人把citytype设为integer,结果模型死活抽不出“北京”。不是模型坏了,是你给它发了错误指令——它收到的信号是:“请从这句话里找一个数字”,于是它开始数“北”字几笔画……这种低级错误,90%源于没吃透参数绑定层的运作逻辑。

3. 从零手写一个可落地的天气查询工具链

光讲原理不够,我们来写一个真正能跑通、能 debug、能上线的最小可行工具链。这里不碰 LangChain、LlamaIndex 这些封装层,全部用原生requestsjson实现,确保你每行代码都看得懂、改得了。

3.1 第一步:选一个真正免费、免密、能扛压的气象API

别一上来就冲高德、和风,它们要么要企业资质,要么QPS极低。我实测下来,最稳的是Open-Meteo(https://api.open-meteo.com)。它完全开源、无需注册、支持全球城市、返回纯 JSON、且有官方中文文档。关键是,它用经纬度查天气,而我们正好可以用geocoding服务把城市名转成经纬度——这就构成一个完美的、无商业风险的双跳工具链。

我们定义两个工具:

  • geocode_city: 将城市中文名转为经纬度(调用 Open-Meteo 的地理编码API)
  • get_weather_by_coords: 根据经纬度获取天气(调用 Open-Meteo 的天气预报API)

下面是完整的tools定义(注意:这是给 DeepSeek API 用的,不是 Python 函数):

[ { "type": "function", "function": { "name": "geocode_city", "description": "将城市中文名称转换为对应的经纬度坐标,用于后续天气查询。仅支持中国境内城市。", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市全称,例如:北京市、广州市、成都市" } }, "required": ["city"] } } }, { "type": "function", "function": { "name": "get_weather_by_coords", "description": "根据经纬度坐标获取当前天气数据,包括温度、天气现象、风速和湿度。", "parameters": { "type": "object", "properties": { "latitude": { "type": "number", "description": "纬度,范围 -90 到 90,例如 39.9042" }, "longitude": { "type": "number", "description": "经度,范围 -180 到 180,例如 116.4074" } }, "required": ["latitude", "longitude"] } } } ]

注意:type: "function"是 DeepSeek API 的固定写法,不是 JSON Schema 的type。很多新手在这里栽跟头,把type写成"object"就直接 400 报错。

3.2 第二步:实现 Python 端的工具执行器(Executor)

这个模块才是真正干活的,它接收模型生成的function_call,执行 HTTP 请求,并把结果喂回去。关键点在于:必须做异常兜底,且返回格式必须严格对齐模型预期。

import requests import json from typing import Dict, Any, Optional def execute_tool(tool_name: str, tool_input: Dict[str, Any]) -> Dict[str, Any]: """ 执行指定工具函数,返回标准化结果 """ try: if tool_name == "geocode_city": city = tool_input.get("city") if not city: return {"error": "缺少城市参数 'city'"} # 调用 Open-Meteo 地理编码API geocode_url = f"https://geocoding-api.open-meteo.com/v1/search?name={city}&count=1&language=zh&format=json" response = requests.get(geocode_url, timeout=10) response.raise_for_status() data = response.json() if not data.get("results"): return {"error": f"未找到城市 '{city}' 的地理信息"} result = data["results"][0] return { "latitude": result["latitude"], "longitude": result["longitude"], "timezone": result["timezone"], "country_code": result["country_code"] } elif tool_name == "get_weather_by_coords": lat = tool_input.get("latitude") lon = tool_input.get("longitude") if lat is None or lon is None: return {"error": "缺少经纬度参数 'latitude' 或 'longitude'"} # 调用 Open-Meteo 天气API(当前天气) weather_url = f"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}&current=temperature_2m,weather_code,wind_speed_10m,relative_humidity_2m&timezone=auto&forecast_days=1" response = requests.get(weather_url, timeout=10) response.raise_for_status() data = response.json() current = data.get("current", {}) return { "temperature": current.get("temperature_2m"), "weather_code": current.get("weather_code"), "wind_speed": current.get("wind_speed_10m"), "humidity": current.get("relative_humidity_2m"), "weather_description": _weather_code_to_text(current.get("weather_code", 0)) } else: return {"error": f"未知工具名: {tool_name}"} except requests.exceptions.Timeout: return {"error": "请求超时,请稍后重试"} except requests.exceptions.ConnectionError: return {"error": "网络连接失败,请检查网络"} except Exception as e: return {"error": f"执行异常: {str(e)}"} def _weather_code_to_text(code: int) -> str: """将 Open-Meteo 的 weather_code 转为中文描述""" mapping = { 0: "晴天", 1: "晴间多云", 2: "局部多云", 3: "多云", 45: "雾", 48: "冻雾", 51: "毛毛雨", 53: "中等毛毛雨", 55: "浓密毛毛雨", 56: "冻毛毛雨", 57: "浓密冻毛毛雨", 61: "小雨", 63: "中雨", 65: "大雨", 66: "冻雨", 67: "浓密冻雨", 71: "小雪", 73: "中雪", 75: "大雪", 77: "雪粒", 80: "小雨", 81: "中雨", 82: "大雨", 85: "小雪", 86: "大雪", 95: "雷暴", 96: "雷暴伴小雨", 99: "雷暴伴大雨" } return mapping.get(code, "未知天气")

这个execute_tool函数有三个设计哲学:

  1. 强类型校验:每个分支开头就检查必填参数,避免下游 API 报 400;
  2. 错误友好返回:所有异常都捕获并转为{"error": "xxx"},模型能读懂,前端也能直接展示;
  3. 语义增强_weather_code_to_text把冰冷的数字码转成“晴天”“雷暴”这种人话,省去模型二次翻译的负担。

3.3 第三步:构建完整的调用循环(Orchestration Loop)

这才是 Function Calling 的灵魂——模型不是调一次就完事,而是可能连续调用多个工具,形成一个“思考-行动-观察-再思考”的链条。我们必须写一个 while 循环,手动管理这个状态流。

import os from openai import OpenAI # 初始化 DeepSeek 客户端(假设你已配置好 API KEY) client = OpenAI( api_key=os.getenv("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com/v1" ) def run_conversation(user_query: str) -> str: """ 执行一次完整的 Function Calling 对话 """ # Step 1: 初始化消息历史 messages = [ {"role": "system", "content": "你是一个专业的天气助手,能准确查询并解释天气信息。请用简洁、友好的中文回答用户问题。"}, {"role": "user", "content": user_query} ] # Step 2: 定义可用工具(即前面定义的 tools 列表) tools = [ ... ] # 此处填入 3.1 节的 tools JSON # Step 3: 主循环:最多尝试 5 轮(防死循环) for step in range(5): try: # 向 DeepSeek 发起请求,要求它决定是否调用工具 response = client.chat.completions.create( model="deepseek-v4-pro", # 注意:必须用支持 Function Calling 的模型 messages=messages, tools=tools, tool_choice="auto", # 让模型自主决定 temperature=0.3, # 降低随机性,提高确定性 ) # 获取模型返回的内容 response_message = response.choices[0].message tool_calls = response_message.tool_calls # Case A: 模型决定不调用工具,直接回答 if not tool_calls: return response_message.content # Case B: 模型要求调用一个或多个工具 messages.append(response_message) # 把模型的“调用指令”加进历史 # 执行所有待调用的工具 for tool_call in tool_calls: function_name = tool_call.function.name function_args = json.loads(tool_call.function.arguments) # 执行工具 tool_result = execute_tool(function_name, function_args) # 将工具执行结果作为新消息加入历史(role=tool) messages.append({ "role": "tool", "content": json.dumps(tool_result, ensure_ascii=False), "tool_call_id": tool_call.id }) except Exception as e: return f"对话执行出错: {str(e)}" return "对话超时,未能完成查询。请稍后重试。" # 测试 if __name__ == "__main__": result = run_conversation("上海今天会下雨吗?") print(result)

这个循环的关键细节:

  • tool_choice="auto"是精髓:你不是命令模型“必须调用”,而是给它选择权。模型会在“直接回答”和“调用工具”之间做最优决策;
  • messages.append(...)的顺序不能错:必须先把模型的tool_calls加进去,再把tool的返回结果加进去,否则模型会丢失上下文;
  • role="tool"是 DeepSeek 的硬性约定,写成"assistant""function"都会报错;
  • tool_call_id必须严格对应,这是模型用来关联“哪条指令对应哪次结果”的唯一凭证。

我第一次跑通时,卡在tool_call_id不匹配上整整3小时。DeepSeek 的错误提示是Invalid tool_call_id,但文档里根本没写这个 ID 从哪来——它就藏在tool_call.id里。这种坑,只有亲手撸一遍才记得住。

4. 深度排错:95% 的 Function Calling 失败都发生在这五个环节

Function Calling 不是“写完就跑”,而是一个精密的齿轮组。任何一个齿崩了,整个链条就卡死。我在生产环境维护过17个基于 DeepSeek 的 Agent 服务,总结出最常出问题的五个环节,附上真实日志和修复方案。

4.1 环节一:模型返回了tool_calls,但arguments是空 JSON{}

现象
模型返回:

{ "tool_calls": [{ "id": "call_abc123", "function": {"name": "get_weather_by_coords", "arguments": "{}"}, "type": "function" }] }

你的execute_tool一执行就报KeyError: 'latitude'

根因
这是 DeepSeek 的“保守策略”。当模型对参数提取极度不确定时,它宁可返回空对象,也不愿瞎猜。常见于:

  • 用户提问模糊:“那边天气怎么样?”(“那边”指代不明);
  • parameters描述太笼统,没给模型足够线索;
  • 模型置信度低于阈值(DeepSeek 内部有个隐式 confidence cutoff)。

修复方案
execute_tool前加一层“参数补全”逻辑:

def safe_parse_arguments(tool_call) -> dict: try: args = json.loads(tool_call.function.arguments) except json.JSONDecodeError: # 如果解析失败,返回空dict,但记录日志 print(f"[WARN] Invalid JSON arguments: {tool_call.function.arguments}") args = {} # 强制补全缺失的必填字段(按 tools 定义中的 required) required_fields = get_required_fields(tool_call.function.name) # 你需要自己实现这个函数 for field in required_fields: if field not in args: args[field] = None # 或设为默认值 return args

更重要的是,在 system prompt 里加一句硬约束:

“如果用户问题中缺少必要参数(如城市名、日期),请先向用户提问澄清,不要调用任何工具。”

这句提示能直接把此类失败率从 35% 降到 5% 以下。

4.2 环节二:工具执行成功,但模型在第二轮直接“失忆”

现象
第一轮:模型调用geocode_city("北京")→ 返回{"latitude": 39.9, "longitude": 116.4}
第二轮:模型本该调用get_weather_by_coords(39.9, 116.4),但它却再次调用geocode_city("北京"),陷入死循环。

根因
消息历史(messages)拼错了。你很可能漏掉了tool角色的消息,或者tool_call_id没对上。DeepSeek 的上下文窗口里,tool消息是模型理解“上次调用结果”的唯一依据。没有它,模型就真以为自己还没查过。

排查清单

  • messages列表中,tool消息的tool_call_id是否严格等于上一轮tool_call.id
  • tool消息的role是否为"tool"(不是"assistant")?
  • tool消息的content是否为json.dumps(...)的字符串(不是 dict)?
  • tool消息是否放在了response_message之后、下一次create()之前?

我用一个表格对比了正确与错误的消息序列:

步骤正确写法错误写法后果
第一轮后追加messages.append(response_message)
messages.append({"role":"tool", "content":"{...}", "tool_call_id":"call_1"})
只加了response_message,漏了tool消息模型看不到结果,无限重试
tool消息内容"content": "{\"latitude\":39.9}"(字符串)"content": {"latitude":39.9}(dict)API 400 报错
tool_call_id"tool_call_id": "call_1"(完全一致)"tool_call_id": "call_1 "(末尾空格)ID 不匹配,模型忽略该消息

4.3 环节三:get_weather_by_coords返回了{"error": "请求超时"},但模型却当成正常数据继续编造

现象
工具返回{"error": "请求超时"},模型却视而不见,直接生成:“根据查询,北京今天的天气是……”,仿佛那个 error 字段不存在。

根因
模型的训练数据里,error字段出现频率极低。它更习惯处理“成功返回”的 JSON。当遇到{"error": "xxx"}时,它的默认行为是忽略error键,继续解析其他字段(如果有的话)。

终极解决方案
永远不要在tool消息的content里返回{"error": "xxx"}。改为返回一个结构完全一致的成功响应,但用特殊字段标记失败

# 错误做法(模型会忽略) return {"error": "网络超时"} # 正确做法(模型能识别并处理) return { "temperature": None, "weather_code": -1, "wind_speed": None, "humidity": None, "weather_description": "服务暂时不可用,请稍后重试", "status": "failed", # 新增状态字段,模型能识别 "retry_suggestion": "建议1分钟后重试" }

然后在 system prompt 里加一句:

“如果收到的工具返回中status字段为failed,请直接将weather_description的内容原样告诉用户,不要自行解释或猜测。”

这样,模型就从“瞎猜”变成了“照念”,成功率100%。

4.4 环节四:本地测试一切正常,但部署到服务器后400 Bad Request

现象
本地 Python 3.9 + requests 2.31 跑得好好的,一上 Ubuntu 22.04 的服务器,DeepSeek API 直接返回400,提示The request body is invalid

根因
服务器上requests版本太老(比如 2.25),它发送的Content-Type默认是application/x-www-form-urlencoded,而 DeepSeek API 严格要求application/json。老版本 requests 在某些环境下不会自动设置这个 header。

修复方案
显式指定 headers:

response = client.chat.completions.create( model="deepseek-v4-pro", messages=messages, tools=tools, tool_choice="auto", # 强制设置 headers(虽然 OpenAI SDK 通常会自动设,但保险起见) extra_headers={"Content-Type": "application/json"} )

更彻底的方案:升级服务器上的requests

pip install --upgrade requests

这个坑我踩过两次,一次在阿里云 ECS,一次在客户内网 Kubernetes,都是因为运维镜像用了三年前的 base image。

4.5 环节五:模型调用geocode_city("火星"),工具返回了{"latitude": -12.5, "longitude": 133.2},模型居然信了

现象
用户问“火星今天天气”,模型真去调用地理编码,工具返回了一个坐标(Open-Meteo 真的会给火星返回假坐标!),模型接着调用天气 API,最后回答:“火星表面温度约 -60℃,多云……”

根因
工具层没有做业务校验。geocode_city应该拒绝一切非地球城市,但你没写校验逻辑。

防御式编程方案
geocode_city执行器里,加一层“地球坐标守门员”:

def execute_tool(...): if tool_name == "geocode_city": # ... 前面的地理编码逻辑 ... if not data.get("results"): return {"error": f"未找到城市 '{city}' 的地理信息"} result = data["results"][0] # 【新增】地球坐标强校验 if result.get("country_code") not in ["CN", "US", "JP", "KR", "DE", "FR", "GB"]: # 或者更狠:检查经纬度是否在地球范围内 if not (-90 <= result["latitude"] <= 90 and -180 <= result["longitude"] <= 180): return {"error": f"城市 '{city}' 的地理信息异常,疑似非地球坐标,请检查输入"} return { ... }

同时,在 system prompt 里埋一颗“怀疑种子”:

“你是一个严谨的天气助手。如果工具返回的坐标明显不合理(如纬度超出-90~90范围),请立即停止后续调用,并告知用户‘无法确认该地点的有效性’。”

Function Calling 的健壮性,80% 来自工具层的防御,20% 来自模型层的提示词约束。两者必须双管齐下。

5. 进阶实战:从“查天气”到“真·智能体”的三步跃迁

“查天气”只是起点。当你把这套机制跑熟了,就可以开始构建真正解决业务问题的智能体。我以一个真实的电商客服 Agent 为例,展示如何把“查天气”的经验平滑迁移到复杂场景。

5.1 第一步:把单工具链升级为多工具协同流水线

“查天气”是 A→B 的线性流程。但真实业务是网状的。比如客服场景:

  • 用户问:“我昨天下的单还没发货,能查下吗?”
  • 模型需要:① 先调get_user_by_phone查用户ID;② 再调get_order_by_user_id查订单;③ 最后调get_logistics_by_order_id查物流。

这不再是while循环能搞定的,你需要一个工具依赖图(Tool Dependency Graph)。我用一个极简的字典来管理:

TOOL_DEPENDENCIES = { "get_order_by_user_id": ["get_user_by_phone"], # 调用此工具前,必须已获得 user_id "get_logistics_by_order_id": ["get_order_by_user_id"] # 调用此工具前,必须已获得 order_id }

然后改造run_conversation循环,让它能自动识别依赖、排队执行。核心逻辑是:每次只提交“所有前置依赖都已满足”的工具调用。这比硬写if-elif-else清晰十倍,也更容易扩展。

5.2 第二步:引入状态记忆,让 Agent 记住“上下文”

“查天气”是无状态的。但客服必须记住:“刚才用户说他叫张三,手机号138****1234”。DeepSeek 本身不维护状态,你需要自己加一层session_state

class WeatherAgentSession: def __init__(self): self.state = { "user_phone": None, "last_city": None, "preferred_unit": "celsius" # 用户偏好摄氏度 } def update_state(self, key: str, value: Any): self.state[key] = value def get_state(self, key: str, default=None): return self.state.get(key, default)

然后在每次run_conversation前,把session_state注入 system prompt:

“当前用户偏好摄氏度,上次查询的城市是北京。请基于此上下文提供服务。”

这个session_state就是你 Agent 的“短期记忆”。它不存数据库,只在单次对话生命周期内有效,轻量又可靠。

5.3 第三步:用“成本感知”驱动工具调用决策

线上环境最怕什么?不是功能不行,而是调用成本失控。一次get_weather调用 0.001 元,但一次get_user_by_phone可能要查三次数据库,成本 0.05 元。你得让模型“精打细算”。

DeepSeek 的response.usage会返回prompt_tokenscompletion_tokens,但不包含工具调用成本。所以,你必须自己建一个COST_TABLE

COST_TABLE = { "geocode_city": 0.0005, # 每次调用成本(元) "get_weather_by_coords": 0.0008, "get_user_by_phone": 0.02, "get_order_by_user_id": 0.03 } def calculate_total_cost(tool_calls: List) -> float: return sum(COST_TABLE.get(tc.function.name, 0) for tc in tool_calls)

然后在循环里加一个熔断器:

if calculate_total_cost(tool_calls) > 0.1: # 单次对话成本上限 0.1 元 messages.append({ "role": "assistant", "content": "本次查询涉及较多步骤,为保障服务质量,我将分步为您处理。请先告诉我您的手机号,以便快速定位订单。" }) break

这就是“成本感知型 Agent”的雏形。它不再盲目执行,而是像一个精明的项目经理,在预算、时效、准确性之间做动态权衡。

我最后分享一个真实体会:Function Calling 的价值,从来不在“让模型能调 API”,而在于它迫使你把业务逻辑显式化、模块化、可验证化。当你为“查天气”写下geocode_cityget_weather_by_coords这两个函数时,你其实已经完成了对“天气查询”这个业务域的第一次抽象。这种抽象能力,才是大模型时代工程师最核心的护城河。

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

相关文章:

  • (2026最新)嘉兴防水补漏正规公司甄选推荐:漏水检测维修-暗管漏水精准定位检测漏水点-卫生间/厨房/屋顶/阳台/渗漏水维修-本地人必选的正规测漏公司 - 即刻修防水
  • PUBG-Logitech图像识别压枪:从零到精通的终极指南
  • ComfyUI-KJNodes终极模型优化指南:快速提升AI图像生成性能的完整方案
  • 英雄联盟玩家必备:3分钟掌握League Akari高效游戏工具
  • 2026年当前浙江高复学校选择指南:聚焦东阳高复中心的办学优势 - 品牌鉴赏官2026
  • Spring AI 2.0 + LangGraph4j 构建生产级AI搜索MultiAgent
  • 高性能MCU实战指南:从ARM Cortex-M7内核到外设的深度优化与避坑
  • SteamShutdown终极指南:告别熬夜等待,智能自动关机解决方案
  • (2026最新)哈尔滨防水补漏正规公司甄选推荐:漏水检测维修-暗管漏水精准定位检测漏水点-卫生间/厨房/屋顶/阳台/渗漏水维修-本地人必选的正规测漏公司 - 即刻修防水
  • AT32UC3L-EK评估板USB接口硬件解析与软件配置实战
  • 2026年当下江阳区窗台石工厂怎么联系?这份专业指南为您解答 - 品牌鉴赏官2026
  • 如何用TV Bro智能电视浏览器彻底改变你的大屏上网体验:终极指南
  • 如何用Video2X将低清视频无损放大到4K:免费AI视频增强完整指南
  • 人脸识别系统-OpenCV+Python
  • 膜结构汽车棚厂家哪个技术先进?
  • RK3588J+YOLOv8边缘部署实战:从ONNX转换到工业级推理优化
  • Claude Code:面向工业级代码深度理解的AI分析引擎
  • 创意解锁:用ABCJS在浏览器中谱写音乐新篇章
  • Codex订阅套餐怎么评估?额度、并发、重置周期和实际成本计算
  • 智能学习系统架构设计革命:从自动化工具到教育技术范式的演进
  • 第4节:我应该选择哪种Kafka?
  • 2026年临沂短视频哪家更有保障:最新权威排名与专业指南。
  • 找非标零件加工厂合作要经历哪些环节?
  • OpenCore Legacy Patcher技术深度解析:老旧Mac硬件兼容性创新解决方案
  • 深度解析Arduino-ESP32对ESP32-C2芯片的技术支持现状与架构演进
  • Chat2DB开源版与Pro版技术选型深度解析:架构评估与实施路径规划
  • Agent Loop本质:四步状态驱动的可执行决策流水线
  • OrigamiSimulator:5分钟掌握实时折纸物理模拟的GPU加速工具
  • Ext2Read:Windows系统无缝访问Linux分区的终极解决方案
  • 2026年,这家好用的peek模具制造企业究竟有何独特魅力?