ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI Agent 入门——用 Function Calling 让大模型真正干活

AI Agent 入门——用 Function Calling 让大模型真正干活 个人主页 for_ever_love__ 欢迎各位大佬莅临其他栏目: 我想学python了 其他栏目: iOS项目总结大全 其他栏目: iOS UI 文章目录AI Agent 入门——用 Function Calling 让大模型真正干活一、大模型最大的尴尬只会说不会做二、Function Calling 的完整链路三、实战一让模型查天气最小可用版本四、实战二多轮调用循环真正的 Agent 雏形五、五个必踩的坑六、三种方案的取舍七、小结AI Agent 入门——用 Function Calling 让大模型真正干活一、大模型最大的尴尬只会说不会做你问大模型今天北京天气怎么样它会很礼貌地告诉你抱歉我无法获取实时天气信息建议你查看天气预报网站。这句话翻译过来就是我只是个语言模型我没有手。大模型本质上是一个根据上文预测下一个字的概率机器。它擅长组织语言、推理逻辑但它有三个天然短板没有实时数据训练数据截止之后发生的事它一概不知不会算让它算123456 × 789012它会一本正经地给你一个错答案没有副作用它没法帮你发邮件、查数据库、下单、改文件而Function Calling函数调用就是给大模型装上手的机制模型不直接干活但它能判断该调哪个工具、传什么参数真正的活由你的代码去执行。一句话理解这个分工大模型负责想现在该查天气了参数是 city北京 你的代码负责做真的去调天气 API拿到 26℃ 多云 大模型再负责说把结果组织成人话回答用户二、Function Calling 的完整链路先把整个流程画清楚后面所有代码都是照着这张图写的┌──────────┐ ① 用户问题 工具清单 │ 用户提问 │ ─────────────────────────▶ ┌──────────┐ └──────────┘ │ 大模型 │ └────┬─────┘ ② 模型返回我要调用 xx 工具参数是 yy │ ▼ ┌────────────────────┐ │ 你的代码执行真实函数 │ │ get_weather(北京) │ └─────────┬──────────┘ │ ③ 执行结果 26℃ 多云 ▼ ┌──────────┐ ④ 模型把结果组织成自然语言 ┌──────────┐ │ 最终回答 │ ◀────────────────────────────── │ 大模型 │ └──────────┘ └──────────┘注意关键点整个过程要跑两轮模型调用第二轮是把工具结果喂回去让模型总结。很多初学者卡在这里——拿到工具结果就直接返回给用户了得到的是一堆原始 JSON。还有一点容易误解Function Calling 不是模型自己执行了代码。模型输出的只是一段结构化的调用意图比如{name:get_weather,arguments:{\city\: \北京\}}真正去向气象局发请求的是你的 Python 函数。模型从头到尾没碰过你的网络、你的数据库。这也是这套机制安全的原因——能力边界完全由你定义。三、实战一让模型查天气最小可用版本下面这段代码可以直接跑。我用 OpenAI 兼容的 SDKDeepSeek、通义、 moonshot 等国内服务基本都兼容这个接口改一下base_url和api_key就能用。importjsonimportosfromopenaiimportOpenAI# 1. 初始化客户端换成你自己的服务地址和密钥clientOpenAI(api_keyos.getenv(OPENAI_API_KEY),base_urlhttps://api.deepseek.com,# 示例DeepSeek 兼容 OpenAI 协议)# 2. 定义真实函数这部分是你的手模型永远碰不到它的内部实现defget_weather(city:str)-str:假装调用了天气 API真实项目里这里替换成 requests.get(...)fake_db{北京:{temp:26,weather:多云,wind:东南风 3 级},上海:{temp:31,weather:晴,wind:东风 2 级},广州:{temp:34,weather:雷阵雨,wind:南风 4 级},}datafake_db.get(city)ifnotdata:returnjson.dumps({error:f暂不支持查询{city}的天气},ensure_asciiFalse)returnjson.dumps({city:city,**data},ensure_asciiFalse)# 3. 用 JSON Schema 描述这个工具告诉模型你的手长什么样tools[{type:function,function:{name:get_weather,description:查询指定城市的实时天气包括温度、天气状况和风力,parameters:{type:object,properties:{city:{type:string,description:城市名称例如北京、上海、广州,}},required:[city],},},}]# 4. 第一轮把问题和工具清单一起丢给模型messages[{role:user,content:北京今天天气怎么样适合出门跑步吗}]responseclient.chat.completions.create(modeldeepseek-chat,messagesmessages,toolstools,tool_choiceauto,# auto 让模型自己决定要不要用工具)msgresponse.choices[0].messageprint(【模型第一轮输出】,msg.tool_calls)运行结果【模型第一轮输出】 [ ChatCompletionMessageToolCall( idcall_0_abc123, functionFunction(arguments{city: 北京}, nameget_weather), typefunction ) ]看到没模型没有直接回答而是说我要调get_weather参数是{city: 北京}。而且它聪明地只提取了北京这个词把适合跑步吗这种无关信息自动过滤掉了——这就是 schema 里description写得清楚带来的好处。接下来执行函数、把结果喂回去# 5. 执行真实函数ifmsg.tool_calls:# 把模型的调用意图加进对话历史messages.append(msg)fortool_callinmsg.tool_calls:fn_nametool_call.function.name fn_argsjson.loads(tool_call.function.arguments)# 函数名 → 真实函数的映射表available_fns{get_weather:get_weather}fnavailable_fns[fn_name]resultfn(**fn_args)print(f【执行结果】{fn_name}-{result})# 把执行结果按约定格式塞回对话messages.append({role:tool,tool_call_id:tool_call.id,content:result,})# 6. 第二轮让模型看着工具结果做总结finalclient.chat.completions.create(modeldeepseek-chat,messagesmessages,)print(\n【最终回答】,final.choices[0].message.content)最终输出【执行结果】get_weather - {city: 北京, temp: 26, weather: 多云, wind: 东南风 3 级} 【最终回答】北京今天 26℃多云东南风 3 级。这个温度很适合户外跑步 不过风力稍大建议选择公园等有遮挡的路线注意补水。到这里一个最小可用的 Function Calling 就跑通了。核心只有三步描述工具 → 执行调用 → 结果回填。四、实战二多轮调用循环真正的 Agent 雏形真实场景里用户的问题往往需要连查好几个工具或者查完天气发现不适合跑步再帮我查个室内健身房。这种链式需求就要把上面的逻辑包成一个while循环defrun_agent(user_query:str,max_rounds:int5):带工具调用循环的简易 Agentmessages[{role:user,content:user_query}]forround_iinrange(max_rounds):responseclient.chat.completions.create(modeldeepseek-chat,messagesmessages,toolstools,)msgresponse.choices[0].message# 模型没要求调工具 → 说明它认为信息够了直接输出答案ifnotmsg.tool_calls:returnmsg.content messages.append(msg)forcallinmsg.tool_calls:argsjson.loads(call.function.arguments)fnavailable_fns[call.function.name]try:resultfn(**args)exceptExceptionase:# 关键工具报错也要如实告诉模型让它自己想办法或如实交代resultjson.dumps({error:str(e)},ensure_asciiFalse)messages.append({role:tool,tool_call_id:call.id,content:result,})print(f--- 第{round_i1}轮调用完成 ---)return抱歉尝试了多次仍未能完成任务。# 测试需要连续调用多个工具print(run_agent(北京和上海今天哪儿更适合户外跑))这个max_rounds非常重要下面坑位会讲到原因。五、五个必踩的坑坑现象解决办法工具描述含糊模型乱调、漏调、参数乱填description写清什么时候该用、什么时候不该用参数幻觉传了 schema 里不存在的字段或 city 传成拼音参数加enum约束代码侧做校验兜底忘记回填 tool 消息第二轮报错tool_call_id 无对应结果每个 tool_call 必须有一条roletool的消息死循环模型反复调同一个工具、账单飞涨设max_rounds超次数强制退出并上报工具报错直接崩一次异常整个对话断开try/except 包住调用把错误信息作为结果返回给模型其中参数幻觉最常见。比如用户说帮我查下魔都天气模型很可能老老实实传{city: 魔都}而你的数据库里根本没有这个 key。正确做法是在代码侧兜一层CITY_ALIAS{魔都:上海,帝都:北京,羊城:广州,鹏城:深圳}defget_weather_safe(city:str)-str:# 别名归一化把模型的自由发挥拉回可控范围cityCITY_ALIAS.get(city,city)returnget_weather(city)六、三种方案的取舍方案优点缺点适合场景纯 Prompt 约束输出 JSON不用改 SDK 调用方式格式经常不合法要写一堆重试简单场景、临时验证Function Calling格式有保证、多工具稳定、生态成熟要多写一层工具描述和分发逻辑绝大多数生产场景直接用 LangChain 等框架封装好、起步快抽象层次高出问题不好排查快速原型、团队已有技术栈我的建议是先用原生 SDK 手写一遍搞懂那张流程图再决定要不要上框架。跳过这一步直接用框架出问题的时候你会完全不知道该看哪一层。七、小结Function Calling 的本质是模型输出调用意图代码负责真实执行安全边界由你掌控完整链路必须跑两轮模型调用第一轮决策工具第二轮总结结果少了第二轮就只能给用户看 JSONdescription的质量直接决定调用准确率——它是给模型看的说明书包一层while循环并设置max_rounds就得到了一个最简可用的 Agent参数幻觉、工具报错、死循环这三个坑全部在代码侧兜底不要指望模型自己变乖掌握 Function Calling 之后往上一步是让 Agent 具备规划和反思能力——那才是完整的 Agent 工程实践。一句话总结Prompt 决定了模型说什么Function Calling 决定了模型能做什么。
返回列表