在一次企业级 AI 应用架构升级中,我们面临一个典型挑战:随着 RAG、Agent、MCP 等能力逐步接入,原有单体式服务在任务调度、模型路由、状态管理等方面暴露出职责模糊、链路耦合、故障扩散等问题。本文基于一次真实架构重构,详解如何通过分层设计明确模块边界、降低系统熵增,并给出可落地的工程实现方案。
背景与现象
我们的 AI 应用最初以“问答 + 知识库检索”为核心,采用单一服务处理用户请求:接收输入 -> 检索向量库 -> 调用大模型生成 -> 返回结果。随着业务扩展,逐步引入 Agent 编排、MCP 工具调用、定时巡检、多模型路由等能力,原有架构开始出现以下现象:
- 任务调度逻辑与业务逻辑强耦合,新增任务类型需修改核心流程;
- 模型路由策略分散在多个服务中,无法统一监控与降级;
- Agent 执行链路过长,任一环节失败导致整条链路不可观测;
- 后台管理功能(如知识库上传、模型配置)与在线服务共用资源,影响稳定性。
这些问题并非孤立存在,而是系统缺乏清晰分层导致的连锁反应。
问题拆解
我们将问题归纳为三类核心矛盾:
- 职责边界模糊:调度、路由、执行、监控等功能混杂在同一服务中,导致变更影响面不可控。
- 链路可观测性缺失:长链路任务缺乏统一追踪机制,故障定位依赖日志拼接。
- 资源隔离不足:后台管理操作与在线服务共享线程池、数据库连接等资源,易引发级联故障。
进一步分析发现,根本原因在于架构未遵循“高内聚、低耦合”原则,且缺乏对“稳定性治理”的前置设计。
核心原因
1. 缺乏分层抽象
原有系统将“做什么”(业务逻辑)与“怎么做”(调度、路由、执行)混为一谈。例如,Agent 编排逻辑直接嵌入 HTTP 控制器,导致无法独立测试或复用。
2. 链路状态管理缺失
长链路任务(如多步 Agent 执行)依赖本地变量或临时缓存维护状态,一旦进程重启或异常退出,状态丢失且无法恢复。
3. 监控指标分散
各模块使用不同埋点方式,缺乏统一指标定义(如“任务成功率”“模型调用延迟”),导致无法构建端到端可观测性。
4. 资源竞争未隔离
后台任务(如知识库重建索引)与在线请求共用线程池,高峰时段引发线程饥饿,影响核心链路响应。
实现方案
我们采用“四层三总线”架构重构系统,明确各模块职责与交互边界:
架构分层
| 层级 | 职责 | 典型模块 | |------|------|----------| |接入层| 请求路由、鉴权、限流 | API Gateway、Session Manager | |调度层| 任务分发、状态机管理 | Task Scheduler、State Machine Engine | |执行层| 模型调用、工具执行、RAG 检索 | Model Router、Agent Executor、RAG Pipeline | |治理层| 监控、告警、配置管理 | Observability Bus、Config Center、Audit Log |
关键设计决策
1. 调度层独立化
将任务调度从业务逻辑中剥离,设计统一任务抽象:
public interface Task { String getType(); Map<String, Object> getPayload(); TaskContext getContext(); }调度器仅负责“何时执行何种任务”,不关心具体业务逻辑。通过状态机引擎维护任务生命周期(Pending -> Running -> Success/Failed),支持重试、超时、依赖检查等策略。
2. 模型路由集中治理
在调度层与执行层之间引入Model Router模块,统一处理模型选择逻辑:
- 基于请求特征(如复杂度、成本敏感度)动态路由;
- 支持会话粘性(Session Sticky)避免频繁切换模型;
- 内置降级策略(如主模型超时自动切备用模型)。
路由决策通过治理层下发的配置动态调整,避免硬编码。
3. RAG 与 Agent 解耦
RAG 模块仅负责“检索-重排-上下文构建”,不参与生成逻辑;Agent 模块专注“任务分解-工具调用-结果聚合”。两者通过标准化上下文对象交互:
{ "query": "用户问题", "context": ["检索片段1", "检索片段2"], "tools": ["tool_a", "tool_b"], "history": [] }4. 治理层统一可观测性
构建Observability Bus,统一收集四类数据:
- Metrics:任务成功率、模型延迟、队列积压;
- Logs:结构化日志,关联 TraceID;
- Traces:全链路追踪,支持跨服务跳转;
- Events:关键状态变更(如模型切换、任务失败)。
通过治理层提供统一 Dashboard,支持按链路、模型、用户维度下钻分析。
模块交互流程
以“用户发起 Agent 任务”为例:
- 接入层校验权限,生成 TraceID;
- 调度层创建任务,写入状态机;
- 执行层获取任务,调用 Model Router 选择模型;
- RAG 模块检索知识库,构建上下文;
- Agent 模块分解任务,调用 MCP 工具;
- 治理层记录全链路 Metrics 与 Events;
- 调度层更新任务状态,通知接入层返回结果。
风险与边界
1. 调度层单点风险
调度器作为核心枢纽,需部署多实例 + 分布式锁(如 Redisson)保障高可用。任务状态持久化至数据库,避免内存丢失。
2. 模型路由抖动
动态路由可能因配置更新引发短暂抖动。解决方案:
- 路由策略变更后延迟生效(如 30s 灰度);
- 保留上一版本策略作为兜底;
- 监控路由切换频率,超阈值自动告警。
3. 长链路超时
Agent 任务可能因工具调用延迟而超时。设计边界:
- 单步工具调用超时 ≤ 5s;
- 整条链路超时 ≤ 60s;
- 超时后自动保存中间状态,支持手动重试。
4. 资源隔离不足
后台任务(如知识库重建)需独立资源池:
- 专用线程池(如
ScheduledExecutorService); - 独立数据库连接池;
- 限制最大并发数,避免影响在线服务。
总结
本次架构重构的核心收益在于:
- 职责清晰:四层分工明确,变更影响范围可控;
- 链路可观测:全链路追踪 + 统一指标,故障定位效率提升 70%;
- 稳定性增强:资源隔离 + 降级策略,核心链路 SLA 达 99.95%;
- 扩展性提升:新增能力(如 MCP 工具)仅需实现标准接口,无需修改主干逻辑。
AI 系统架构设计不能仅关注“功能实现”,更需前置考虑“如何稳定运行”。通过分层抽象、模块解耦、统一治理,才能支撑复杂 AI 应用的长期演进。
技术补丁包
任务状态机设计原理:基于状态模式实现任务生命周期管理,支持重试、超时、依赖检查。 设计动机:避免长链路任务因异常中断导致状态丢失。 边界条件:状态变更需原子化,避免并发冲突。 落地建议:使用数据库事务 + 乐观锁保障一致性,关键状态变更记录审计日志。
模型路由会话粘性原理:在同一会话中固定使用同一模型,避免频繁切换引发性能抖动。 设计动机:提升用户体验稳定性,降低模型调用开销。 边界条件:会话过期时间需合理设置(建议 30min),避免资源占用过长。 落地建议:基于 Redis 存储会话-模型映射,设置 TTL 自动清理。
Observability Bus 数据聚合原理:通过统一 SDK 收集 Metrics、Logs、Traces、Events,写入中心化存储(如 Prometheus + Loki + Tempo)。 设计动机:打破数据孤岛,实现端到端可观测性。 边界条件:避免高频埋点导致性能损耗,采样率需动态调整。 落地建议:关键路径全量采集,非关键路径按 10% 采样,通过治理层配置动态生效。
后台任务资源隔离原理:为后台任务分配独立线程池、数据库连接池、消息队列。 设计动机:防止后台操作挤占在线服务资源。 边界条件:资源配额需根据业务峰值动态调整,避免过度预留。 落地建议:使用 Spring 的
@Async配合自定义ThreadPoolTaskExecutor,配置最大并发数与队列容量。Agent 链路超时兜底原理:为每一步工具调用设置独立超时,整条链路设置全局超时。 设计动机:避免因单个工具故障导致整条链路阻塞。 边界条件:超时时间需根据工具类型差异化配置(如 HTTP 工具 ≤ 3s,本地脚本 ≤ 10s)。 落地建议:使用 Resilience4j 的
TimeLimiter实现分层超时控制,超时后自动保存上下文供人工介入。