phase 5 part 4: java-spring awareness, k8s basics with validated manifests, run_examples supports single-file Java
This commit is contained in:
1 parent
075872e31e
commit
f695bbd7cc
18 files changed
+1192
-23
No files matched your search
@@ -0,0 +1,82 @@
|
||||
---
|
||||
id: backend.java-spring.scenario.call-java-service
|
||||
title: Python Agent 调用公司已有的 Java 业务系统:读接口、封装工具、错误映射和联调
|
||||
type: scenario
|
||||
domain: backend
|
||||
stack: [java-spring, function-calling, fastapi]
|
||||
tags: [系统对接, 工具封装, 错误码映射, OpenAPI, 联调, 幂等]
|
||||
level: intermediate
|
||||
prerequisites: [backend.java-spring.awareness, ai.function-calling.tool-loop]
|
||||
est_hours: 2
|
||||
related: [backend.java-spring.interview, ai.function-calling.scenario.safe-business-tools]
|
||||
week: [14]
|
||||
---
|
||||
|
||||
# Python Agent 调用公司已有的 Java 业务系统:读接口、封装工具、错误映射和联调
|
||||
|
||||
## 业务背景与问题
|
||||
|
||||
公司的订单、售后系统是多年的 Spring Boot 服务。新做的 AI 客服 Agent(Python)需要“查订单、查物流、发起退款申请”。问题:
|
||||
|
||||
1. 接口文档过期,字段含义要读代码才知道;
|
||||
2. Java 服务的错误码体系复杂,Agent 不知道哪些错误可以重试、哪些要告诉用户;
|
||||
3. 退款接口不是幂等的,Agent 重试导致重复申请;
|
||||
4. 联调时双方互相指责。
|
||||
|
||||
## 约束
|
||||
|
||||
- 不能大改 Java 服务,只能小范围加接口或参数。
|
||||
- Agent 不能拥有超过用户本人的权限。
|
||||
|
||||
## 方案设计
|
||||
|
||||
1. **读接口**:从 Controller 和 DTO 读出真实的字段、校验规则、错误码;如果服务集成了 OpenAPI(springdoc 一类工具),导出规范生成 Python 客户端;否则自己写 Pydantic 模型。
|
||||
2. **封装工具**:每个业务能力一个工具,入参严格校验;工具层负责鉴权透传(用户令牌或服务账号 + 用户 id)、超时、重试(只对查询类和幂等接口)。
|
||||
3. **错误映射**:把 Java 错误码分为三类——可重试(超时、限流)、需要告诉用户(订单不存在、超过退款期限)、需要转人工(系统异常);返回给模型的是结构化的可读信息,而不是堆栈。
|
||||
4. **幂等**:和 Java 团队约定退款接口增加 `Idempotency-Key` 请求头(服务端用唯一约束实现),Agent 在一次会话中对同一操作使用同一个 key。
|
||||
5. **联调**:先写契约测试(请求 / 响应样例),双方各自对着样例开发;用 request id 串起两边日志。
|
||||
|
||||
## 关键代码
|
||||
|
||||
```python
|
||||
class JavaErrorKind(str, Enum):
|
||||
RETRYABLE = "retryable"
|
||||
USER_FACING = "user_facing"
|
||||
HANDOFF = "handoff"
|
||||
|
||||
ERROR_MAP = {
|
||||
"ORDER_NOT_FOUND": (JavaErrorKind.USER_FACING, "没有找到这个订单,请核对订单号"),
|
||||
"REFUND_EXPIRED": (JavaErrorKind.USER_FACING, "已超过可退款期限"),
|
||||
"RATE_LIMITED": (JavaErrorKind.RETRYABLE, "系统繁忙"),
|
||||
}
|
||||
|
||||
def call_refund(client: httpx.Client, user: User, order_id: str, reason: str, idem_key: str) -> dict:
|
||||
r = client.post("/api/refunds", json={"orderId": order_id, "reason": reason},
|
||||
headers={"Authorization": f"Bearer {user.token}", "Idempotency-Key": idem_key}, timeout=5)
|
||||
if r.is_success:
|
||||
return {"ok": True, "refund_id": r.json()["data"]["refundId"]}
|
||||
code = r.json().get("code", "UNKNOWN")
|
||||
kind, msg = ERROR_MAP.get(code, (JavaErrorKind.HANDOFF, "系统异常,已为你转人工"))
|
||||
return {"ok": False, "kind": kind, "message": msg} # 给模型的是结构化结果,不是堆栈
|
||||
```
|
||||
|
||||
工具调用安全的完整讨论见[安全的业务工具](../../../ai/function-calling/scenarios/safe-business-tools.md)。
|
||||
|
||||
## 取舍
|
||||
|
||||
| 选择 | 优点 | 缺点 |
|
||||
| --- | --- | --- |
|
||||
| **Python 工具层直接调用 Java 接口(本方案)** | 不改动 Java 服务主体 | 需要维护错误映射和模型定义 |
|
||||
| Java 团队提供专门给 Agent 的聚合接口 | 语义更清晰 | 依赖对方排期 |
|
||||
| 用 Spring AI 在 Java 侧实现 Agent | 技术栈统一 | 团队 AI 生态以 Python 为主 |
|
||||
|
||||
## 易错点
|
||||
|
||||
- Agent 使用高权限服务账号,绕过了用户级权限。
|
||||
- 对非幂等接口自动重试。
|
||||
- 把 Java 的异常堆栈直接交给模型,既泄露内部信息又让模型困惑。
|
||||
- 金额字段在 JSON 中是字符串还是数字没有约定,精度丢失。
|
||||
|
||||
## 面试怎么讲
|
||||
|
||||
> “我不是 Java 开发,但能读懂 Spring Boot 项目:从 Controller 和 DTO 读出真实的接口契约。对接时我在 Python 侧封装工具,鉴权透传用户身份;把 Java 的错误码分成可重试、告知用户、转人工三类,给模型结构化结果;和 Java 团队约定退款接口支持幂等键。联调前先写契约样例,用 request id 串联日志。”
|
||||
Reference in new issue
Block a user