转型AI应用开发Day 15-Function Calling / Tools 基础
对应第一个月计划第 3 周第 1 天理解 Function Calling / Tools 的协议边界掌握 tool schema、参数校验以及“模型选择工具”和“应用强制调用”的差异。建议投入45 小时。当天交付天气查询与计算器两个可运行 demo并完成正常、异常和安全边界测试。一、今天完成后要达到什么程度你应当能够解释 Tool Calling 是“模型提出结构化调用请求”不是模型直接执行代码。使用 JSON Schema 描述工具名、用途、参数类型、必填项和取值范围。在应用侧完成参数解析、schema 校验、业务校验和权限校验。区分模型自主选工具、强制指定工具和禁止调用工具。把工具执行结果作为新消息回传模型并保持tool_call_id对应关系。让天气与计算器 demo 在不暴露真实密钥的情况下独立运行。正确处理未知工具、参数错误、执行异常、超时和模型重复调用。二、必须掌握的知识点1. Function Calling 的真实链路用户请求 → 应用把可用工具 schema 发送给模型 → 模型返回普通回答或 tool call → 应用验证工具名与参数 → 应用执行本地函数或受控 API → 应用回传 tool result → 模型生成最终回答模型只生成“调用意图”。真正的网络访问、文件操作、数据库写入和计算都发生在应用侧因此安全责任也在应用侧。2. Tool schema一个工具定义至少包含稳定且清晰的工具名准确描述“何时使用”和“何时不要使用”JSON Schema 参数定义必填字段enum、长度、数值范围等约束是否允许额外字段。天气工具的教学 schema 可以写成{name:get_weather,description:查询指定城市当前天气不用于历史天气或预报,parameters:{type:object,properties:{city:{type:string,minLength:1},unit:{type:string,enum:[celsius,fahrenheit]}},required:[city,unit],additionalProperties:false}}schema 是模型输出格式约束不是完整安全边界。即使服务端支持严格 schema应用仍要校验业务规则。3. 参数校验的四层JSON 是否可解析 → 是否符合 schema → 是否符合业务规则 → 当前用户是否有权限执行例如city是字符串只能证明类型正确它仍可能为空、过长或不在业务支持范围内。计算器不能直接对模型给出的字符串使用eval()。应只允许预定义操作例如add、subtract、multiply、divide并对除零和数值范围单独检查。4. 模型选工具与强制调用常见控制方式auto模型决定回答还是调用工具none禁止工具调用指定某工具要求模型必须调用该工具required要求至少产生工具调用具体能力以服务商 API 为准。选择原则开放式问答可用auto明确的确定性流程可由应用直接调用函数不必让模型决定评测某个工具参数生成时可强制指定高风险操作不能因为模型选中了工具就自动获准。5. 工具结果不是可信指令工具输出可能来自外部 API、网页或用户文件。它们应被视为不可信数据不执行结果中的命令不允许结果覆盖 system 规则限制结果大小对敏感字段脱敏用稳定状态表达错误必要时先清洗再回传模型。三、一步步完成今天的任务第 0 步确认前置基础约 15 分钟确认你理解 Day 01Day 06 的模型调用、结构化输出、超时和错误分类并能解释模型 API 成功返回 ≠ 工具已经成功执行第 1 步创建两个最小 demo约 15 分钟mkdirtool-calling-labcdtool-calling-lab uv init uvaddopenai pydantic python-dotenvtouchweather_demo.py calculator_demo.py tools.py schemas.py .env.example README.md建议结构tool-calling-lab/ ├── weather_demo.py ├── calculator_demo.py ├── tools.py ├── schemas.py ├── .env.example └── README.md.env.example只写变量名和占位符不放真实密钥。实际密钥只保存在未提交的环境变量或.env中。第 2 步定义统一执行结果约 25 分钟工具函数不要抛出无法分类的任意文本。统一返回classToolResult(BaseModel):ok:booldata:dict|NoneNoneerror_code:str|NoneNonemessage:str至少约定INVALID_ARGUMENT参数不合法NOT_FOUND没有对应数据TIMEOUT执行超时UPSTREAM_ERROR外部服务失败INTERNAL_ERROR应用内部异常。第 3 步实现天气工具约 35 分钟为保证 demo 可运行先使用本地固定样本不依赖真实天气密钥WEATHER_DATA{北京:{temperature_c:26,condition:晴},上海:{temperature_c:28,condition:多云},}defget_weather(city:str,unit:str)-ToolResult:...实现要求城市名去除首尾空白并限制长度只接受celsius或fahrenheit未知城市返回NOT_FOUND华氏温度由程序确定性换算结果中标明数据是教学样本不伪装成实时天气。若替换为真实 API只在get_weather内部增加适配器并加入超时、状态码检查和响应字段校验。第 4 步实现安全计算器约 35 分钟参数结构建议{operation:divide,a:12,b:3}执行器仅允许add / subtract / multiply / divide检查操作是否在白名单a、b是否为有限数除数是否为零结果是否超出你设定的教学范围禁止eval、exec和动态导入。第 5 步注册工具并建立白名单约 25 分钟TOOL_REGISTRY{get_weather:get_weather,calculate:calculate,}分发逻辑必须先检查工具名模型请求工具 → 是否在本次允许列表 → 校验参数 → 调用对应函数 → 序列化 ToolResult不要使用模型给出的工具名做动态模块导入、shell 命令拼接或属性遍历。第 6 步完成天气 tool-calling 往返约 40 分钟weather_demo.py需要把天气 schema 与用户消息发送给模型检查响应是否包含 tool call解析并校验参数调用get_weather用原始tool_call_id回传结果请求模型生成简洁最终答复。测试北京现在天气如何请用摄氏度。 请解释什么是气压。不应强行调用天气工具 查询一个样本库不存在的城市。第 7 步完成计算器 demo约 35 分钟测试auto和强制指定计算器两种模式125 乘以 48 等于多少 请写一首关于数字的短诗。auto 下不应调用 10 除以 0。记录模型生成的参数、校验结果和最终状态。不要只检查自然语言答案。第 8 步做参数与协议负向测试约 30 分钟至少覆盖缺少必填字段多出未知字段unit不在枚举中数字以无法接受的字符串传入未知工具名malformed JSON同一个tool_call_id被重复处理工具结果无法序列化。第 9 步复盘工具选择约 20 分钟分别判断以下场景应该auto、none、强制工具还是由应用直接调用普通知识问答用户明确要求计算支付确认天气参数生成评测聊天页面暂时关闭外部能力。关键结论确定性业务流程不应为了“像 Agent”而交给模型自由决策。四、工程原则与安全边界模型建议不等于执行授权认证、权限、配额和人工确认必须由应用控制。工具 schema 中写“仅管理员可用”不能替代权限检查。严格白名单与最小能力每次请求只暴露当前用户、当前任务真正需要的工具。工具越多误选和攻击面越大。读写能力分离天气和计算器都是低风险只读能力。涉及写文件、发消息、下单或删除时应增加确认、幂等键和审计。错误要结构化回传模型需要知道工具失败但不应看到堆栈、密钥、内部 URL 或完整响应正文。工具调用必须可观察记录请求 ID、工具名、耗时、结果状态和重试次数参数日志要脱敏。五、常见问题排查模型总是不调用工具检查工具描述是否明确、schema 是否被正确发送、tool_choice是否禁用了工具以及问题是否真的需要外部能力。不要只靠在用户 Prompt 中写“必须调用”。模型调用了错误工具减少同时暴露的工具改进描述中的适用与不适用边界并建立工具选择测试集。参数看起来正确但执行失败区分 JSON/schema 校验和业务校验打印脱敏后的校验错误不要让异常直接穿透。工具执行成功但模型没有最终回答检查 assistant 的 tool call 消息、tool_call_id和 tool result 是否完整回传消息顺序是否符合当前服务商协议。发生重复调用设置最大往返次数对有副作用工具使用幂等键。今天的 demo 也应保存已处理的tool_call_id避免重复执行。计算器给出危险表达式不要执行表达式字符串。将能力限制为枚举操作与数值参数。六、当天验收清单能解释模型、应用和工具执行器各自职责。两个工具都有清晰且严格的 schema。应用完成 JSON、schema、业务和权限四层校验。天气 demo 使用教学样本或安全配置不含真实密钥。计算器只允许白名单操作且未使用eval。两个 demo 均完成 tool call → tool result → 最终回答的往返。已比较auto、禁止和强制工具调用。已测试未知工具、参数错误、除零与重复调用。错误以结构化状态回传且不泄露内部信息。能说明模型选择工具为什么不等于获得执行权限。全部完成后Day 15 即为通过Day 16 将不依赖 Agent 框架把一次工具调用扩展为有最大步数和明确终止条件的循环。