摘要
把一个能调用模型的 Spring Boot 服务直接称为 AI 平台,通常只完成了平台能力的很小一部分。生产级平台需要同时管理模型、知识库、Agent、会话、工具、权限、成本和运行状态,并把这些能力稳定地提供给多个业务系统。
本文以 Spring Boot 和 Spring AI 为基础,设计一个企业内部 AI 应用平台,重点介绍:
- 平台与单个 AI 应用的边界;
- 模型目录、应用配置和运行时上下文;
- 会话、知识库、工具和 Agent 的统一接入;
- 多租户隔离、权限和审计;
- 限流、重试、降级、预算和可观测性;
- 如何把平台拆成可独立演进的模块;
- 从单体平台演进到多服务架构的条件。
一、背景与问题
1. 单个 AI 应用解决不了平台问题
前面的文章已经分别实现了模型接入、流式对话、Prompt、知识库、向量检索、Tool Calling、Agent 和调用治理。单独使用这些能力时,每个业务团队都可能复制一套配置:
订单助手
├─ 自己的模型 Key
├─ 自己的 Prompt
├─ 自己的知识库
└─ 自己的限流逻辑
客服助手
├─ 另一套模型 Key
├─ 另一套 Prompt
├─ 另一套知识库
└─ 另一套限流逻辑
复制能够加快第一个项目,但很快会出现:
- 模型升级需要修改多个服务;
- Key、预算和供应商策略无法统一;
- 相同知识库被重复向量化;
- 工具权限和审计口径不一致;
- 每个应用都有自己的会话表和消息格式;
- 故障、成本和质量无法横向比较。
平台的目标不是把所有业务逻辑集中到一个项目,而是把可复用的 AI 能力沉淀为统一运行时。
2. 平台需要管理的对象
| 对象 | 平台职责 | 业务应用职责 |
|---|---|---|
| 模型 | 供应商、版本、价格、限流、健康状态 | 选择适合任务的模型 |
| Prompt | 模板版本、变量和发布 | 定义业务提示词 |
| 知识库 | 文档、切片、向量和权限 | 提供业务资料 |
| 工具 | 注册、Schema、超时和审计 | 实现业务接口 |
| Agent | 工作流、记忆和工具集 | 定义任务目标 |
| 会话 | 消息、上下文和状态 | 定义业务会话语义 |
| 策略 | 权限、预算、内容检查 | 配置业务约束 |
二、核心概念
1. AI 应用平台
AI 应用平台是一组共享运行能力,向上提供应用、会话和任务接口,向下连接模型、向量库、工具和观测系统。
它至少包含四层:
接入层
REST、SSE、管理 API
应用运行层
应用、Agent、Prompt、会话、上下文
能力层
模型网关、知识库、工具注册、权限、预算
基础设施层
MySQL、Redis、向量库、对象存储、观测系统
2. 应用定义
平台中的“应用”不是一个 Spring Boot 进程,而是一份可版本化的运行配置:
application:
code: customer-support
model: qwen-plus
prompt: support-answer:v4
knowledgeBases:
- product-docs
- refund-policy
tools:
- query-order
- create-ticket
policies:
maxInputTokens: 12000
dailyBudget: 300
业务请求携带 applicationCode,平台根据发布版本组装模型、Prompt、知识和工具。
3. 运行时上下文
每次请求都生成不可变的运行时上下文:
tenantId + userId + applicationVersion
↓
RuntimeContext
├─ model policy
├─ prompt variables
├─ permitted tools
├─ knowledge scope
└─ traceId
后续检索、模型调用和工具执行都使用这份上下文,避免各模块重复解析权限。
三、工作原理
1. 请求流程
客户端
↓
API Gateway
↓
AiPlatformController
↓
ApplicationRuntimeService
├─ 加载应用版本
├─ 校验用户权限
├─ 检查预算和限流
├─ 组装 RuntimeContext
↓
ConversationOrchestrator
├─ 保存用户消息
├─ 检索知识库
├─ 调用模型或 Agent
├─ 执行已授权工具
└─ 保存回答和 Trace
平台负责编排,具体订单、工单和知识处理仍由领域服务完成。
2. 模块划分
在一个 Spring Boot 单体中先按模块隔离:
ai-platform
├─ platform-api
├─ platform-application
├─ platform-conversation
├─ platform-model
├─ platform-knowledge
├─ platform-tool
├─ platform-policy
└─ platform-observability
模块之间通过接口通信。platform-api 不能直接访问模型 SDK,模型供应商细节停留在 platform-model。
3. 配置发布
应用配置不能只存在于 application.yml。平台使用草稿、审核和发布三个状态:
DRAFT → REVIEW → PUBLISHED → ROLLED_BACK
已发布版本不可原地修改。Prompt、工具集或模型调整时创建新版本,便于比较效果和回滚。
4. 数据边界
| 数据 | 建议存储 | 原因 |
|---|---|---|
| 应用、版本、权限 | MySQL | 需要事务和审计 |
| 会话和消息 | MySQL | 需要分页、检索和归档 |
| 限流、缓存 | Redis | 需要高并发和过期控制 |
| 文档原文 | 对象存储 | 文件体积大 |
| 向量 | 向量库 | 支持向量检索 |
| Trace 和指标 | 观测系统 | 需要聚合和告警 |
四、实战示例
1. 平台接口
@RestController
@RequestMapping("/api/ai/applications")
public class AiApplicationController {
private final ApplicationRuntimeService runtimeService;
@PostMapping("/{code}/chat")
public ChatAcceptedResponse chat(@PathVariable String code,
@RequestBody ChatCommand command,
Authentication authentication) {
ChatRequest request = ChatRequest.builder()
.applicationCode(code)
.tenantId(command.tenantId())
.userId(authentication.getName())
.conversationId(command.conversationId())
.message(command.message())
.build();
return runtimeService.submit(request);
}
}
接口立即返回 traceId,长任务由异步执行器处理;短对话也可以在同一接口中选择同步或 SSE 响应。
2. 运行时组装
public RuntimeContext load(ChatRequest request) {
PublishedApplication app = applicationRepository
.findPublished(request.tenantId(), request.applicationCode())
.orElseThrow(() -> new ApplicationNotFoundException(request.applicationCode()));
policyService.checkAccess(request, app);
budgetService.reserve(request.tenantId(), app.dailyBudget());
rateLimiter.acquire(request.tenantId(), app.code());
return RuntimeContext.builder()
.traceId(tracer.nextId())
.tenantId(request.tenantId())
.userId(request.userId())
.applicationVersion(app.version())
.modelPolicy(modelCatalog.get(app.modelCode()))
.prompt(promptRepository.get(app.promptRef()))
.knowledgeScopes(app.knowledgeBases())
.tools(toolCatalog.findPermitted(request, app.tools()))
.build();
}
预算预留发生在模型调用之前。请求失败、取消或实际 Token 少于预估值时,再释放或修正预留额度。
3. 统一编排
public ChatResult orchestrate(RuntimeContext context, ChatRequest request) {
conversationStore.appendUserMessage(context, request.message());
List<KnowledgeChunk> chunks = knowledgeService.retrieve(
context.knowledgeScopes(), request.message(), 5);
ChatResponse response = modelGateway.chat(ModelCommand.builder()
.context(context)
.history(conversationStore.recent(context, 20))
.knowledge(chunks)
.tools(context.tools())
.build());
conversationStore.appendAssistantMessage(context, response);
traceStore.save(context.traceId(), response.usage(), response.toolCalls());
return ChatResult.from(response);
}
Agent 模式可以替换 modelGateway.chat(),但权限、预算和 Trace 仍由平台统一处理。
4. 模型目录
models:
- code: qwen-plus
provider: dashscope
upstream: qwen-plus
maxContext: 128000
inputPrice: 0.0008
outputPrice: 0.002
timeout: 30s
fallback: qwen-turbo
- code: local-coder
provider: openai-compatible
baseUrl: ${LOCAL_MODEL_BASE_URL}
upstream: qwen2.5-coder
maxContext: 32768
timeout: 60s
业务配置只引用 qwen-plus 或 local-coder。供应商地址、价格和 Fallback 变化时,不需要修改业务服务。
5. 平台最小表
create table ai_application_version (
id bigint primary key,
tenant_id varchar(64) not null,
app_code varchar(64) not null,
version int not null,
model_code varchar(64) not null,
prompt_ref varchar(128) not null,
config_json json not null,
status varchar(32) not null,
published_at datetime null,
unique key uk_app_version (tenant_id, app_code, version)
);
工具、知识库和策略可以先放入 config_json。等查询和权限需求稳定后,再拆成独立关联表。
五、常见问题与实践建议
1. 不要把业务系统整体搬进平台
平台保存 AI 应用配置和运行记录,不替代订单、CRM 或工单系统。工具调用应访问既有业务服务,而不是在平台内复制业务表。
2. 先统一模型入口
平台建设可以从一个模型网关开始,再逐步加入知识库、工具和 Agent。一开始就拆成十几个微服务,会让配置、事务和排障成本过高。
3. 发布版本必须可回滚
Prompt 或模型升级可能降低回答质量。每次发布保留旧版本,并允许按租户或流量比例灰度。
4. 预算要按租户和应用细分
只限制平台总预算无法阻止一个应用耗尽全部额度。至少记录租户、应用、模型、用户和 Trace 五个维度。
5. 平台自身也要有降级路径
模型供应商故障时,平台可以切换到备用模型;知识库故障时,可以退化为无检索对话并明确标记;工具故障时,应返回可理解的失败,而不是让模型编造执行结果。
六、进阶思考
1. 单体何时拆分
出现以下情况再拆分服务:
- 模型调用、文档处理和 Agent 执行的资源曲线明显不同;
- 多个团队需要独立发布;
- 知识库索引任务影响在线对话;
- 安全边界要求工具执行进入独立沙箱。
优先拆出文档索引、模型网关和 Agent 执行器,在线会话服务保持相对稳定。
2. 多租户隔离级别
| 级别 | 实现 | 适用情况 |
|---|---|---|
| 逻辑隔离 | tenant_id 条件 | 一般企业内部应用 |
| 索引隔离 | 每租户独立向量索引 | 数据敏感性较高 |
| 运行隔离 | 独立 Key、队列和配额 | 需要限制相互影响 |
| 部署隔离 | 独立服务或集群 | 高合规要求 |
隔离级别应写入应用策略,不能只依赖开发约定。
3. 质量评估进入发布流程
平台可以维护一组固定评测样本。Prompt、模型或检索参数发布前运行样本,比较准确率、拒答率、工具成功率和平均成本。未达到阈值的版本不能进入生产。
4. 平台管理面与数据面分离
管理面负责应用配置、模型目录和权限;数据面负责对话和任务执行。管理 API 使用更严格的身份认证,不与普通聊天接口共用同一暴露面。
结论
生产级 AI 应用平台的关键,是把模型、Prompt、知识库、工具、Agent、权限和成本组织成可版本化的运行时,而不是简单堆叠多个 AI 功能。Spring Boot 可以先承载这个单体平台,但模块边界、配置发布、租户隔离和可观测性要从第一阶段建立。
后续可以继续实现平台的数据安全与权限模型,并把模型目录接入本地推理服务,形成云端模型与私有模型统一调度的运行环境。