一、背景与目标
大多数企业的核心业务系统基于 Dubbo 微服务架构构建,服务之间通过 Dubbo RPC 协议通信。而新一代 AI Agent 平台(如 DeerFlow)则以 HTTP API 对外暴露能力。
这就产生了一个架构鸿沟:
┌─────────────┐ Dubbo RPC ┌──────────────┐ HTTP ┌──────────────┐
│ 业务系统 A │ ────────────────▶ │ 业务系统 B │ │ AI Agent │
│ (OA 系统) │ │ (财务系统) │ │ (DeerFlow) │
└─────────────┘ └──────────────┘ └──────────────┘
↑ 传统 Dubbo 通信 ↑ 仅暴露 HTTP
⚠️ 这篇文章不是教你怎么接一个大模型 API 然后写死几个业务场景。 市面上大多数所谓的"AI 集成"就是在 Controller 里写 if-else 调一下 OpenAI,场景写死、能力固定、扩展困难。
我们做的是:底层 Harness 全栈能力支撑 + Skills 插件式架构 + Agent 自主规划执行。
- 不是简单 HTTP 透传,Identity Guardrail 权限控制、Sandbox 执行隔离、Skills 插件系统、MCP 协议,全部保留
- 不是写死场景,每个业务域一个 SKILL.md,Agent 自主读文档、自主规划、自主调接口
- 不是玩具,是企业级的安全可控
二、项目架构概览
我们构建了四个模块:
java-simple-dubbo-app/
├── dubbo-api/ # 公共 Dubbo 接口和 DTO
│ ├── FinanceService.java # 财务系统接口
│ └── QuickFlowService.java # AI Agent 接口
├── finance-system/ # 财务系统 — Dubbo Provider
│ └── FinanceServiceImpl.java # @DubboService 实现
├── oa-system/ # OA 系统 — Dubbo Consumer
│ ├── OaBizService.java # → Dubbo → Finance
│ ├── OaAiService.java # → Dubbo → QuickFlow Adapter
│ └── OaAiController.java # 含业务意图识别+数据拼接
└── quickflow-adapter/ # 协议转换层 — Dubbo Provider
└── QuickFlowServiceImpl.java # Dubbo → HTTP → DeerFlow
端口分配
| 服务 | HTTP 端口 | Dubbo 端口 |
|---|---|---|
| finance-system | 8082 | 20880 |
| oa-system | 8081 | — |
| quickflow-adapter | 8083 | 20881 |
| DeerFlow Gateway | 2026 (Nginx) | — |
三、三种集成模式
模式 A:Dubbo 直连(传统业务服务间通信)
OA 系统通过 @DubboReference(url = "dubbo://localhost:20880") 直连财务系统。
完整链路:
浏览器 → OA HTTP (8081)
→ @DubboReference → Finance Service (20880)
→ 查询报销单/校验预算
→ 返回 Mock 数据
// OA 侧 — Dubbo Consumer
@DubboReference(url = "dubbo://localhost:20880")
private FinanceService financeService;
public ReimburseResponse queryReimburse(String reimburseNo, String applicantId) {
return financeService.queryReimburse(new ReimburseRequest(reimburseNo, applicantId));
}
模式 B:Dubbo → HTTP 协议转换(AI Agent 集成)
这是最核心的模式。OA 系统只认 Dubbo 协议,DeerFlow 只暴露 HTTP API,中间需要一个协议转换层。
OA 系统 (Dubbo Consumer)
│ @DubboReference(url = "dubbo://localhost:20881")
▼
QuickFlow Adapter (Dubbo Provider + HTTP Client)
│ WebClient POST → /api/runs/wait
▼
DeerFlow Gateway (FastAPI, port 2026)
│ DeepSeek / OpenAI
▼
AI 回复
为什么需要这个适配器层?
- OA 系统是纯 Dubbo 体系,没有 HTTP Client 的依赖和管理
- 适配器可以集中管理 API Token、超时、重试等配置
- 适配器可以被多个业务系统复用(一篇适配,多处调用)
- 未来如果 DeerFlow 升级或更换,只改适配器即可
@DubboService
public class QuickFlowServiceImpl implements QuickFlowService {
@Value("${quickflow.base-url}")
private String baseUrl;
@Value("${quickflow.api-token}")
private String apiToken;
private WebClient webClient;
@Override
public ChatResponse chat(ChatRequest request) {
// 1. 构建 DeerFlow 请求体
ObjectNode body = mapper.createObjectNode();
body.putNull("assistant_id"); // 使用默认 Agent
// 2. 调用 DeerFlow HTTP API
String jsonResponse = webClient.post()
.uri("/api/runs/wait")
.bodyValue(body)
.retrieve()
.bodyToMono(String.class)
.block();
// 3. 解析返回的 messages,提取 AI 文本回复
return parseResponse(jsonResponse, conversationId);
}
}
模式 C:Intent Resolver(Java 层先查数据再喂给 AI)
如果老系统不暴露接口,或者你希望更可控,可以在 Java Controller 层做意图识别 + 数据预查:
@PostMapping("/chat")
public ResponseEntity<?> chat(@RequestBody Map<String, String> request) {
String message = request.get("message");
// 意图识别:如果包含报销单号
Pattern pattern = Pattern.compile("RE\\d{9}");
Matcher matcher = pattern.matcher(message);
if (matcher.find()) {
// 自己查数据
String reimburseNo = matcher.group();
ReimburseResponse data = financeService
.queryReimburse(new ReimburseRequest(reimburseNo, null));
// 把数据拼进去,再发给 AI
message = message + "\n\n[系统查询结果]\n" + JSON.toJSONString(data);
}
// 发给 AI
return oaAiService.chat(message, conversationId);
}
模式 C 的价值:数据不出内网、意图识别可控、AI 只做 NLG。
四、安全设计
4.1 API Token — 调用方身份认证
DeerFlow 通过 API Token 认证调用方。Token 可以关联到具体租户或用户:
Authorization: Bearer dft_xxx
设计要点:Token 绑定到具体权限范围;只暴露 AI Agent 调用能力;可单独吊销。
4.2 Org Key — 数据隔离与资源控制
┌──────────────────────────────────────────────────────┐
│ DeerFlow Gateway │
│ API Token → 身份认证 → 获取 Org/User Identity │
│ Identity → Identity Guardrail → 权限校验 │
│ ↓ │
│ skill:invoke → 可以调用 skills + 工具 │
│ thread:read → 可以读取当前线程 │
│ thread:write → 可以创建/修改线程 │
│ 无权限 → 返回 authz.tool.denied │
└──────────────────────────────────────────────────────────┘
DeerFlow 的权限模型基于 Identity Guardrail,每次工具调用前检查权限,不在白名单中的工具默认拒绝(default-deny)。
4.3 序列化安全
Dubbo 默认使用 Hessian2 序列化。在跨版本通信时需要配置序列化白名单:
dubbo:
security:
serialize:
allowlist: com.dubbo.demo, java.util, java.lang
- DTO 必须实现
Serializable+ 固定serialVersionUID dubbo-api用最低 JDK 版本编译,双方共用
4.4 服务隔离
| 安全维度 | 实现方式 |
|---|---|
| 网络隔离 | Adapter 作为唯一对外暴露节点,内网 Dubbo 服务不暴露公网 |
| 权限最小化 | Adapter 只暴露 AI 需要的接口,不暴露完整的 Dubbo 服务 |
| 调用审计 | 可在 Adapter 层记录所有 AI 发起的请求 |
| 限流控制 | 可在 Adapter 层配置 QPS 限制 |
五、前端集成
OA 系统在右下角加入浮动聊天机器人。AI 在对话过程中会根据 SKILL.md 自主决定调什么接口:
用户: "销售部申请15000预算够吗?"
→ AI 识别意图 → 读 SKILL.md
→ 发现可用 Adapter HTTP 接口
→ 主动调 GET /api/finance/budget/check?deptId=DEPT_SALES&amount=15000
→ 获取数据,回复"预算充足,剩余10万"
用户: "查一下报销单 RE202607001 的状态"
→ AI 识别出报销单号
→ 调 GET /api/finance/reimburse?reimburseNo=RE202607001
→ 回复"已审批,张三差旅费¥12,800.50"
用户: "帮我写一段出差申请"
→ AI 判断无需调接口 → 直接输出申请模板
一切由 AI 自主规划,不是预先写死的 if-else。
六、JDK 8 兼容性说明
分层策略:让 Adapter 升级,让业务系统保持不变
核心思路:Adapter 升级到 JDK 17+,业务系统保持 JDK 8。
┌──────────────────────────────┐
│ OA 系统 (JDK 8) │ ← 不改
│ Dubbo 2.7.x │
└──────────┬───────────────────┘
│ Dubbo RPC(跨版本兼容)
▼
┌──────────────────────────────┐
│ QuickFlow Adapter (JDK 21) │ ← 升级
│ Dubbo 3.x + Spring Boot 3.x │
└──────────────────────────────┘
Dubbo 2.7.x 和 3.x 使用相同的 dubbo:// 协议,底层 Hessian2 序列化兼容。
七、实际运行效果
# 启动三个服务
java -jar finance-system/target/finance-system-1.0.0.jar
java -jar quickflow-adapter/target/quickflow-adapter-1.0.0.jar
java -jar oa-system/target/oa-system-1.0.0.jar
# 验证链路
curl "http://localhost:8083/api/finance/reimburse?reimburseNo=RE202607001"
curl -X POST http://localhost:8081/api/oa/ai/chat \
-H "Content-Type: application/json" \
-d '{"message":"查一下 RE202607001","conversationId":"test"}'
八、集成模式对比与选型建议
| 模式 | 适用场景 | 老系统改动 | 数据流向 | AI 自主度 |
|---|---|---|---|---|
| A: Dubbo 直连 | 传统业务服务间通信 | ❌ 零改动 | 纯内网 | 无 AI 参与 |
| B: Protocol Adapter | Dubbo 系统调 HTTP AI | ❌ 零改动 | AI 主动调 | 高 |
| C: Intent Resolver | 业务可控优先 | ❌ 零改动 | 先查再送 | 中 |
九、Scale Up:从 Adapter 到 Sidecar 的架构演进
当前 Adapter 的三个瓶颈
中心化 Adapter 解决了从 0 到 1 的问题,但量上来后会面临:单点故障、接口膨胀、部署耦合。
Sidecar 模式:去中心化的答案
Sidecar 借用了 Istio/Envoy 的经典架构思路——把基础设施能力从中心抽出来,推到每个业务系统旁边。
改造前:
OA 服务器 Adapter 服务器(瓶颈!)
┌──────────┐ Dubbo ┌────────────┐ HTTP ┌──────────┐
│ OA │ ──────────▶ │ Adapter │ ────────▶ │ DeerFlow │
│ (8081) │ │ (8083) │ │ (2026) │
└──────────┘ 经过网络 └────────────┘ └──────────┘
改造后(Sidecar):
OA 服务器
┌─────────────────────────────────────┐
│ OA 进程 │
│ @DubboReference(url="dubbo:// │
│ localhost:20081") │ ← 本机 Sidecar
└────────────┬────────────────────────┘
│ Dubbo(本地回环,不走网络)
▼
┌─────────────────────────────────────┐
│ AI Sidecar │
│ - 本地缓存(Caffeine) │
│ - 熔断器(Resilience4j) │
│ - 无状态,随业务系统自动扩缩 │
│ ↓ HTTP │
│ DeerFlow │
└──────────────────────────────────────┘
Sidecar 对比中心化 Adapter
| 维度 | 中心化 Adapter | Sidecar |
|---|---|---|
| 部署位置 | 一台独立服务器,所有系统共用 | 跟业务系统同机,一一对应 |
| 实例数量 | 1 个(单点) | N 个(随业务系统数量) |
| 故障影响 | 挂了 = 所有系统失去 AI 能力 | 只影响挂了的那台机器 |
| 调用延迟 | 网络 RTT(0.5~2ms) | 本地回环(0.01~0.1ms) |
从 Adapter 到 Sidecar 的平滑迁移
第 1 步:Sidecar 跟 Adapter 共存,灰度切流
第 2 步:全部切到 Sidecar
第 3 步:Adapter 退役,控制面上线(控制面只做配置下发,不参与数据面)
整个过程 OA 系统代码一行不改——QuickFlowService 接口不变,只改 url。
十、代码结构
java-simple-dubbo-app/
├── dubbo-api/ # 公共接口(JDK 8 可编译)
├── finance-system/ # 财务系统 — 老系统模拟
├── quickflow-adapter/ # 协议转换层 — 唯一新服务
└── oa-system/ # OA 系统
十一、不止 Dubbo:Adapter 模式的普适规律
我们用 Dubbo 做了完整示例,但这个模式不限于 Dubbo。gRPC、SOAP/WebService、ESB 总线、甚至银行核心用的 IBM MQ 和 Tuxedo——思路完全一样,只是底层协议实现不同。
核心规律
不管底层是什么协议,Adapter 做的事始终只有三件:
老系统 → [协议 A] → Adapter → [HTTP JSON] → AI Agent
↓
1. 连上老系统(用老系统的协议)
2. 拿到数据(解析老系统的响应格式)
3. 转成 HTTP JSON(暴露给 AI)
唯一变化的是第 1 步和第 2 步。第 3 步永远是 HTTP JSON。
各协议对比
| 老系统协议 | 怎么连 | 解析复杂度 | 常见行业 |
|---|---|---|---|
| Dubbo | @DubboReference 直连 Provider | 低 | 互联网、企业应用 |
| gRPC | grpc-client-spring-boot-starter | 中 | 云原生微服务 |
| SOAP/WebService | WebServiceTemplate 或 CXF | 低 | 银行、金融、政府 |
| ESB 总线 | 接入 ESB 的对外接口 | 中 | 大型企业核心系统 |
| IBM MQ | JMS 客户端 | 中 | 银行核心交易 |
银行 SOAP + ESB 场景拆解
AI → Adapter(HTTP → SOAP 转换)→ ESB(不变)→ 核心系统
已经有 ESB 了为什么还要加 Adapter?因为 ESB 和 Adapter 定位完全不同:
| 维度 | ESB(已有) | Adapter(新增) |
|---|---|---|
| 服务对象 | 业务系统间通信(高 TPS) | AI 零星查询(几分钟一次) |
| 接口粒度 | 完整交易接口(几十个字段) | 精简查询(3-5 个参数) |
| 变更门槛 | 以月为单位 | 以天为单位 |
| 安全模型 | ESB 自有认证 | API Token + Org Key |
代码对比:Dubbo vs SOAP
// Dubbo Adapter
@DubboReference(url = "dubbo://localhost:20880")
private FinanceService financeService;
@GetMapping("/api/finance/reimburse")
public Map<String, Object> queryReimburse(@RequestParam String no) {
ReimburseResponse resp = financeService.queryReimburse(new ReimburseRequest(no));
return Map.of("code", 0, "data", resp);
}
// SOAP Adapter(同一套思路,换协议)
@Autowired
private WebServiceTemplate wsTemplate;
@GetMapping("/api/bank/account")
public Map<String, Object> queryBalance(@RequestParam String accountNo) {
QueryBalanceRequest req = new QueryBalanceRequest();
req.setAccountNo(accountNo);
QueryBalanceResponse resp = (QueryBalanceResponse)
wsTemplate.marshalSendAndReceive("http://esb:9080/QueryBalance", req);
return Map.of("code", 0, "data", Map.of("balance", resp.getBalance()));
}
两个 Adapter 的区别只在于:第 1 行注解方式不同、URL 格式不同。剩下 90% 的代码——Controller 结构、错误处理、返回格式——完全一样。
部署位置权衡
| 部署方式 | 适用协议 | 优缺点 |
|---|---|---|
| 同进程嵌入 | Dubbo、SOAP(客户端轻) | ✅ 延迟最低 ❌ 依赖 JDK 版本 |
| 边车 Sidecar | gRPC、Thrift | ✅ 协议与业务解耦 ❌ 多一跳 |
| 对接 ESB | SOAP、JMS | ✅ ESB 不改 ❌ 依赖 ESB 团队 |
| 独立网关 | 多种协议混合 | ✅ 统一入口 ❌ 架构复杂 |
不值得做 Adapter 的判断标准
如果 AI 一周都问不到一次这个系统的数据,就别接。
十二、总结
本文通过一个完整的 Java Dubbo 示例项目,展示了企业级微服务集成 AI Agent 的三种模式:
- Dubbo 直连 — 传统业务服务间通信
- Protocol Adapter — Dubbo 体系调用 HTTP 服务的标准模式
- Intent Resolver — 业务侧先查数据再喂给 AI
同时覆盖了企业级安全设计(API Token、序列化安全、QoS)、JDK 8 兼容方案、Sidecar 大规模演进路线,以及不同协议(gRPC、SOAP、ESB、MQ)的普适规律。
整套方案的核心理念是:对现有系统零侵入,通过边缘适配器逐步引入 AI 能力。
你们不需要知道 AI 的存在。不需要改任何代码。不需要升级 JDK。 你们的 Dubbo 服务正常跑就行。Adapter 会主动来连你们。
完整示例代码:java-simple-dubbo-app