49 Tool Calling 集成:Python 工具调用 Java API
Tool Calling 集成:Python 工具调用 Java API
Tool Calling 的关键不是让模型输出函数名,而是把模型提出的调用请求安全转换成受控 API 调用。
[TOC]
1. 先把 Agent 想清楚
很多人一听到 Agent,就会想到“让大模型自己决定下一步”。这个理解只对了一半。
在真实项目里,Agent 更像是一个被工程规则约束住的任务执行器:
模型负责理解自然语言、提出候选动作
代码负责权限校验、状态流转、工具执行
业务接口负责最终事实和写操作
日志审计负责事后追踪
模型生成工具调用参数只是候选结果。真正的鉴权、校验、执行和审计必须发生在应用和业务服务里。
所以这一阶段的博客不要写成“模型越来越聪明”。更准确的写法是:我们正在学习如何把模型的灵活性放进一个可控的工作流里。
2. 一个业务场景
LangGraph 用 Python 编排,订单和工单服务在 Java Spring Boot。Python Tool 负责调用 Java API,但 Java API 仍然要按当前用户鉴权。
这个场景里面最重要的不是模型能不能说出一段好听的话,而是:
它选了哪个工具
为什么能调用这个工具
参数从哪里来
权限在哪里校验
失败后状态怎么变
用户确认前会不会产生真实写入
把这些问题问清楚,Agent 项目才不会变成一个不可解释的聊天黑盒。
这一节要先说明工具调用解决什么。
判断顺序可以这样写:
1. 先判断哪些信息模型不能凭空回答,必须调用工具获取事实
2. 每个工具都要定义名称、用途、参数、返回结构和失败码
3. 参数不能直接相信模型,要做类型、权限、范围和必填校验
4. 工具结果要回写状态和日志,后续节点只能基于工具事实继续
5. 最后用错误参数、缺参数和无权限样例验证工具边界
它解决的是事实接入问题:模型负责提出工具调用意图,代码负责把调用变成安全、可验证的业务动作。
3. 核心概念
1. 工具 Schema
定义工具名、参数、类型、描述和限制。Schema 越清楚,模型越不容易误用。
这一点在项目里要落到具体规则:工具要清晰。
2. Java API
业务事实和写操作由 Java 服务负责,Python 不直接碰数据库。
这一点在项目里要落到具体规则:业务规则在服务端。
3. 结果解析
HTTP 200 不等于业务成功,要看业务 status 和 errorCode。
这一点在项目里要落到具体规则:响应要结构化。
4. 重试策略
只重试可重试错误,写操作必须先有幂等。
这一点在项目里要落到具体规则:重试不能乱来。
4. 整体流程
graph TD;
A["模型提出工具调用"] --> B["校验工具名和参数"];
B["校验工具名和参数"] --> C["Python 调 Java API"]; C["Python 调 Java API"] --> D["Java 鉴权和执行业务"];
D["Java 鉴权和执行业务"] --> E["返回结构化结果"];
E["返回结构化结果"] --> F["写回 State"]; F["写回 State"] --> G["继续路由"];
这张图里,模型只是其中一个节点。工具、状态、权限、确认和审计同样重要。
如果一个 Agent 图里只有“用户 -> 模型 -> 答案”,那它还只是 Chat,不是能进入业务系统的 Agent。
5. 落地步骤
1. 建立工具白名单
只允许注册过的工具被调用,未注册一律拒绝。
完成后应该能看到:工具注册表。
2. 双层校验
Python 校验参数格式,Java 校验身份、状态和业务规则。
完成后应该能看到:校验链路。
3. 统一客户端
集中处理超时、traceId、错误映射和脱敏。
完成后应该能看到:ToolClient。
4. 保护工具结果
工具返回内容也视为不可信,不允许其中指令覆盖系统规则。
完成后应该能看到:工具结果包装格式。
6. 状态、接口或工具字段
| 项目 | 说明 |
|---|---|
| toolName | 工具名称 |
| schema | 参数结构 |
| riskLevel | 只读或写操作 |
| timeoutMs | 超时时间 |
| retryPolicy | 重试策略 |
| audit | 是否审计 |
字段设计要尽量结构化。不要让下游通过一段自然语言去猜“成功了没有”“下一步要干什么”。
7. 示例
@tool
def query_order(order_id: str) -> dict:
"""查询当前登录用户自己的订单摘要。"""
response = api.get( f"/api/orders/{order_id}", timeout=3, headers={"X-Trace-ID": trace_id} ) return normalize_tool_result(response)
示例里的字段不一定照搬,但思想要保留:输入、状态、输出、错误和审计信息要分开。
8. 哪些事不能交给模型自由发挥
模型不能提供 userId 或 tenantId 来决定查询范围。这些身份信息应由服务端根据登录态注入。
这一节特别重要。Agent 项目最容易踩坑的地方,就是把“理解意图”和“执行业务动作”混成一件事。
模型可以建议动作,但最终执行必须经过代码和业务系统的规则。
9. 测试路径
建议至少准备下面几类测试:
正常路径:输入完整,工具正常返回
缺字段路径:订单号、联系方式、问题描述不完整
权限路径:查询别人的订单、访问无权知识库
失败路径:工具超时、模型输出非法、检索为空
恢复路径:等待确认时服务重启、重复确认、超时确认
Agent 的测试不能只看最终回答。你要检查状态、工具调用、审计日志和是否产生真实副作用。
10. 工程取舍
- 跨语言调用增加复杂度,但能复用 Java 业务系统。
- 工具越少越稳,工具越多越灵活。
- 写工具必须比读工具更严格。
取舍讲清楚以后,读者才能知道你为什么这样设计,而不是以为所有问题都要上 Agent。
11. 常见坑
- 工具描述模糊导致模型误用。
- 只在 Python 层鉴权。
- 对所有 POST 自动重试。
- 异常堆栈直接返回给模型和用户。
这些坑本质上都和一个问题有关:没有把模型输出当成“不可信的候选结果”。
12. 验收清单
- ☐ 工具白名单生效
- ☐ 参数经过 Schema 校验
- ☐ Java API 二次鉴权
- ☐ 写工具有幂等
- ☐ 工具调用有 traceId
如果这些检查项没过,先不要急着加更多工具。工具越多,边界越乱,排查越难。
13. 落地清单
- ☐ 定义 3 个工具 Schema
- ☐ 写统一 ToolResult 结构
- ☐ 设计工具超时策略
- ☐ 模拟一次 Java API 业务失败
练习时可以先用 Mock,不要一开始就连真实订单或工单系统。等状态、权限和幂等设计清楚后,再接真实接口。
14. 小结
Tool Calling 不是让模型获得系统权限,而是让模型在受控边界内请求工具。边界越清楚,Agent 越安全。
Agent 的价值来自灵活性,但安全来自边界。能把这两件事同时讲清楚,才算真正理解 Agent 工程化。