Files

4.4 KiB

id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week
id title type domain stack tags level prerequisites est_hours related week
backend.java-spring.scenario.call-java-service Python Agent 调用公司已有的 Java 业务系统:读接口、封装工具、错误映射和联调 scenario backend
java-spring
function-calling
fastapi
系统对接
工具封装
错误码映射
OpenAPI
联调
幂等
intermediate
backend.java-spring.awareness
ai.function-calling.tool-loop
2
backend.java-spring.interview
ai.function-calling.scenario.safe-business-tools
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 串起两边日志。

关键代码

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}       # 给模型的是结构化结果,不是堆栈

工具调用安全的完整讨论见安全的业务工具。

取舍

选择 优点 缺点
Python 工具层直接调用 Java 接口(本方案) 不改动 Java 服务主体 需要维护错误映射和模型定义
Java 团队提供专门给 Agent 的聚合接口 语义更清晰 依赖对方排期
用 Spring AI 在 Java 侧实现 Agent 技术栈统一 团队 AI 生态以 Python 为主

易错点

  • Agent 使用高权限服务账号,绕过了用户级权限。
  • 对非幂等接口自动重试。
  • 把 Java 的异常堆栈直接交给模型,既泄露内部信息又让模型困惑。
  • 金额字段在 JSON 中是字符串还是数字没有约定,精度丢失。

面试怎么讲

“我不是 Java 开发,但能读懂 Spring Boot 项目:从 Controller 和 DTO 读出真实的接口契约。对接时我在 Python 侧封装工具,鉴权透传用户身份;把 Java 的错误码分成可重试、告知用户、转人工三类,给模型结构化结果;和 Java 团队约定退款接口支持幂等键。联调前先写契约样例,用 request id 串联日志。”