1. 从一次“模型答得挺好但啥也干不了”说起AI Tool 是什么简单说它是让大语言模型LLM从“只会聊天”变成“能真正干活”的那层桥。LLM 本身只能输出文本它没法自己查数据库、调接口、发邮件、读文件。你问它“帮我看看北京今天天气”它要么编一个要么说“我无法获取实时信息”。AI Tool 就是给模型配上一双手模型负责判断“该用哪个工具、传什么参数”你的程序负责真正执行再把结果喂回模型让它组织成人话回复。适合谁适合正在做 Java 或 Python 后端、想给自己的应用接上多模型能力、又不想为每家模型单独写一套调用逻辑的开发者。Function Call 是这套机制里最核心的动作。模型不会直接执行你的函数它只是输出一段结构化的 JSON告诉你“我要调用 get_weather参数是 city北京”。你的代码解析这段 JSON执行本地函数把返回值再发回去。整个链路走通一次你就理解了 Agent 的雏形。我试过在同一个项目里同时接 Java 和 Python 两套服务最烦的不是写工具函数而是每家模型的 Key、Base URL、模型名都不一样切来切去容易乱。这篇就按“原理 → 统一通道 → Java 落地 → Python 落地 → 排障”的顺序把 AI Tool 的 Function Call 链路完整跑一遍配置片段都能直接复制。2. TaoToken 统一 Key 通道多模型调用的前置准备在写 Tool 之前先把“模型从哪来”这件事解决掉。Function Call 对模型有要求不是所有模型都支持 tools 参数而且不同厂商的返回格式略有差异。如果你一会儿用这个模型、一会儿换那个代码里到处是 if-else维护成本很高。TaoToken 在这里的角色是一个统一的 API 通道。它对外暴露一套兼容 OpenAI 规范的接口你拿一个 Key就能在多个模型之间切换Base URL 和鉴权方式保持一致。对 Function Call 来说这意味着你的 tools 定义、消息结构、解析逻辑基本不用改只换 model 字段就行。官网入口在这里https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。API 地址是 https://taotoken.net/api 注意这个不带 UTM 参数配置里填这个。你需要准备三样东西我把它叫“三件套”配置项值说明Base URLhttps://taotoken.net/api所有请求的前缀API Key在控制台生成形如 sk-xxx别泄露Model ID例如 gpt-4o / qwen-plus / deepseek-chat按你账号可用模型填Key 的生成在控制台完成路径是 consolehttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。进去之后找 API Keys 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 新建一个复制出来存到环境变量里别硬编码进代码。注意Key 只显示一次丢了就重新生成。生产环境一定用环境变量或密钥管理服务不要提交到 Git。如果你只是想先验证模型通不通可以用模型对话页面直接试https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。但 Function Call 这种带 tools 的请求建议还是用代码跑页面不一定能完整展示 tool_calls 的返回结构。接入文档在这里遇到参数疑问可以对照https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。文档里对 OpenAI 兼容格式、请求头、错误码都有说明排障时会用到。为什么强调“统一通道”因为 Function Call 的调试很依赖日志。如果每个模型走不同的 SDK、不同的鉴权你排查“为什么这次没触发工具调用”时得先排除一堆环境差异。统一之后变量只剩 model 和 prompt问题定位快很多。3. 可复制配置Java 与 Python 的接入片段这一节给的是能直接粘贴的配置。Java 用 Spring AI 风格Python 用 openai SDK 原生写法两边都指向同一个 Base URL。3.1 Python 侧配置先装依赖pip install openai然后建一个config.py把三件套集中管理import os from openai import OpenAI client OpenAI( api_keyos.environ.get(TAOTOKEN_API_KEY, sk-你的Key), base_urlhttps://taotoken.net/api ) MODEL_ID gpt-4o # 可换成 qwen-plus / deepseek-chat 等如果你用settings.json或.env管理可以这样{ base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, model_id: gpt-4o, timeout: 30 }代码里读这个 JSON把api_key_env对应的环境变量取出来传给 client。这样切换模型只改model_id不用动业务代码。3.2 Java 侧配置Spring AI 的application.ymlspring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o temperature: 0.7对应的pom.xml依赖dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version1.0.0/version /dependency如果你用的是 Codex 那套auth.json风格有些 CLI 工具会读这个文件格式大致是{ base_url: https://taotoken.net/api, api_key: sk-你的Key, model: gpt-4o }注意auth.json里的 Key 同样是敏感信息文件权限设成仅本人可读别放进公开仓库。三件套在任何一种配置里都必须齐全Base URL 指向https://taotoken.net/apiKey 从环境变量或密钥文件读Model ID 明确写出来。缺一个请求就会 401 或者 model not found。4. 端到端验证一次 Function Call 请求跑通配置好了现在写一个最小可运行的例子验证“模型决定调用工具 → 本地执行 → 结果回传 → 模型总结”这条链路。4.1 Python 验证脚本import json from config import client, MODEL_ID # 1. 真实工具函数 def get_weather(city: str) - str: # 这里用假数据真实场景换成 API 调用 return json.dumps({city: city, temp: 25, condition: 晴}, ensure_asciiFalse) # 2. 工具描述OpenAI 格式 tools [ { type: function, function: { name: get_weather, description: 查询指定城市的实时天气。当用户问到天气、温度、是否下雨时调用。, parameters: { type: object, properties: { city: {type: string, description: 城市中文名例如北京、上海} }, required: [city] } } } ] available {get_weather: get_weather} def run(user_input: str): messages [{role: user, content: user_input}] while True: resp client.chat.completions.create( modelMODEL_ID, messagesmessages, toolstools, tool_choiceauto ) msg resp.choices[0].message messages.append(msg) if not msg.tool_calls: return msg.content for tc in msg.tool_calls: name tc.function.name args json.loads(tc.function.arguments) print(f[调用工具] {name} 参数{args}) result available[name](**args) messages.append({ role: tool, tool_call_id: tc.id, content: result }) print(run(北京今天天气怎么样))跑起来后终端应该先打印[调用工具] get_weather 参数{city: 北京}然后输出类似“北京今天晴气温 25°C”。如果只输出了自然语言、没有工具调用日志说明模型没触发 tools检查tool_choice和 description 是否够清楚。4.2 Java 验证片段Spring AI 用注解方式更省事import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Component; Component public class WeatherTools { Tool(description 查询指定城市的实时天气。当用户问到天气、温度时调用。) public String getWeather( ToolParam(description 城市中文名例如北京) String city) { return {\city\:\ city \,\temp\:25,\condition\:\晴\}; } }然后在 ChatClient 里注册Bean public ChatClient chatClient(ChatModel model, WeatherTools tools) { return ChatClient.builder(model) .defaultTools(tools) .build(); }调用chatClient.prompt(北京天气怎么样).call().content()Spring AI 会自动把Tool方法转成 JSON Schema处理 tool_calls 循环你拿到的就是最终自然语言。4.3 成功结果长什么样一次成功的 Function Call日志里应该能看到三段第一段是请求发出messages 里带 tools 定义第二段是模型返回finish_reason: tool_callstool_calls数组里有 name 和 arguments第三段是你回传role: tool后模型返回finish_reason: stopcontent 是总结性文字。如果finish_reason一直是stop且没有 tool_calls说明模型选择直接回答没走工具。这时候优先改 description把“什么时候该调用”写具体。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一节按真实遇到的报错来。每个都给出原因和改法。401 Unauthorized。最常见。原因通常是 Key 没读到、Key 写错、或者环境变量名对不上。先确认echo $TAOTOKEN_API_KEY有值再确认代码里读的就是这个变量名。如果用的是auth.json检查 JSON 格式有没有多逗号。还有一种情况Base URL 写成了带路径的https://taotoken.net/api/v1而 SDK 自己会拼/v1导致路径重复。统一用https://taotoken.net/api让 SDK 处理版本段。local proxy failed / connection refused。这个报错通常出现在你本机设置了网络代理但代理没启动或者端口不对。SDK 会读HTTP_PROXY/HTTPS_PROXY环境变量。如果你不需要代理把这两个变量清掉再跑。命令是unset HTTP_PROXY HTTPS_PROXYLinux/Mac或set HTTP_PROXYWindows。清掉后重试多数能通。reading choices 报错 / KeyError choices。这通常不是网络问题而是返回体结构和你预期不一致。比如模型返回了错误对象{error: {...}}你直接取resp[choices]就炸了。排查方法先把原始返回print(resp)出来看。常见触发原因是 model 名写错服务端返回 404 错误体。确认 Model ID 在账号可用列表里别照抄别人的模型名。OAuth 相关报错。有些 CLI 工具比如某些 coding agent默认走 OAuth 登录而不是 API Key。如果你在配置文件里填了 Key但工具仍然尝试 OAuth就会报 token 无效。这时候要找到工具的配置项显式指定用 API Key 模式并把 Base URL 指向https://taotoken.net/api。如果工具支持auth.json按第 3 节的格式写全三件套。工具没被调用。不是报错但很常见。检查三点tools 数组是否真的传进去了打印请求体确认description 是否写得太模糊tool_choice是不是被设成了none。另外部分模型对 tools 支持有限换一个明确支持 Function Call 的 Model ID 再试。参数解析失败。模型返回的 arguments 是字符串需要json.loads。如果模型输出了非法 JSON会抛异常。解决办法是在工具函数外层包 try-except把异常信息作为字符串返回给模型让它重新组织。这也是 Tool 编写规范里“异常不要抛返回给 LLM”的原因。排障时建议开 verbose 日志。Python 的 openai SDK 可以设client OpenAI(..., timeout30)并打印每次请求Spring AI 可以调日志级别到 DEBUG看它生成的 Schema 和 tool_calls 解析过程。6. 把链路固定下来从验证到长期使用跑通一次之后下一步是让它稳定。我的做法是把三件套写进配置中心Java 和 Python 两边读同一份来源避免“这边改了那边忘了”。模型切换只改 Model IDtools 定义和解析逻辑完全复用。如果你要长期做编码类或 Agent 类任务可以考虑用 Coding Plan 来管理调用额度入口在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。它适合那种需要反复调模型、跑多轮工具调用的场景比单次对话更划算。验证模型是否支持某个工具调用格式用模型对话页面最快https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。但记住页面展示和代码里的 tool_calls 结构可能不完全一样最终以代码日志为准。最后给一个实用技巧把每次 Function Call 的请求和响应落库字段包括 user_input、tool_name、arguments、result、final_answer。跑一周你就能看出哪些工具从没被调用description 有问题哪些参数经常填错schema 描述不清。这比拍脑袋优化有效得多。链路跑通只是开始让工具被正确调用才是长期要调的事。
阅读完成 · 觉得有帮助?