第2303篇:AI中台架构设计——企业共享AI能力层的建设路径
第2303篇:AI中台架构设计——企业共享AI能力层的建设路径
适读人群:正在规划企业AI平台的架构师和技术负责人 | 阅读时长:约18分钟 | 核心价值:理解AI中台的核心设计原则,避免"重复造轮子"和"中台失败"两个极端
我见过两类企业在AI中台上翻车。
第一类:多个业务线各自搭了一套AI基础设施,互不共享,同样的向量数据库搭了三套,同样的提示词管理没有,成本翻几倍还没人知道用了多少钱。
第二类:热情高涨地搞了"AI中台",但中台团队花了大半年搭基础设施,业务线等不了,自己偷偷绕开中台直接接AI API,中台成了摆设。
怎么避免这两种极端?我认为核心在于:AI中台要从真实的业务需求出发,先解决高价值的共性问题,而不是追求大而全的平台。
AI中台的核心价值主张
中台存在的前提是:有多个团队都需要某些能力,而这些能力单独做成本高、效果差,共享则更经济有效。
AI中台的高价值共性能力:
1. 统一的模型接入和管理:多个AI提供商的对接、API Key管理、成本追踪、速率限制。如果每个业务线自己搞,重复劳动多,且没有全局视图。
2. 知识管理基础设施:向量化、存储、检索。这是大多数AI应用的基础,但搭建成本不低。共享一套,质量更高,成本更低。
3. 提示词管理和版本控制:企业级提示词需要版本管理、A/B测试、审批流程,这是通用能力。
4. 安全和合规能力:PII过滤、内容审核、数据脱敏,每个业务线单独实现既重复又不一致。
不适合做到中台的能力:
- 业务领域的提示词内容:写什么提示词是业务问题,不是技术问题,中台管好格式和版本,内容由业务线负责。
- 业务特定的AI工作流:合同审查工作流和采购工作流是完全不同的,不要试图用一个通用工作流引擎来统一。
- AI能力的评估标准:不同业务对"好答案"的定义不同,评估框架可以共用,但标准和测试集是业务线的事。
中台架构设计
中台SDK设计
业务线通过SDK接入中台,SDK封装复杂性,提供简单的业务接口:
// 中台提供的Java SDK
// 业务线只需要引入这个依赖,就能使用中台所有能力
public class AiPlatformClient {
private final String tenantId;
private final String apiKey;
private final String platformBaseUrl;
private final HttpClient httpClient;
// 工厂方法(从环境变量或配置中心读取配置)
public static AiPlatformClient create() {
return new AiPlatformClient(
System.getenv("AI_PLATFORM_TENANT_ID"),
System.getenv("AI_PLATFORM_API_KEY"),
System.getenv("AI_PLATFORM_URL")
);
}
/**
* 核心对话接口
*/
public ConversationClient conversation() {
return new ConversationClient(this);
}
/**
* 知识库接口
*/
public KnowledgeBaseClient knowledgeBase(String kbId) {
return new KnowledgeBaseClient(this, kbId);
}
/**
* 内容安全检查
*/
public ContentSafetyClient contentSafety() {
return new ContentSafetyClient(this);
}
}
// 对话客户端
public class ConversationClient {
private final AiPlatformClient platform;
/**
* 发起对话
*/
public ChatResponse chat(ChatRequest request) {
// 自动处理:认证、速率限制、重试、日志
return platform.post("/api/v1/chat", request, ChatResponse.class);
}
/**
* 流式对话
*/
public Flux<String> chatStream(ChatRequest request) {
return platform.stream("/api/v1/chat/stream", request);
}
/**
* 使用业务线注册的提示词模板
*/
public ChatResponse chatWithTemplate(
String templateKey,
Map<String, String> variables,
String userMessage) {
return chat(ChatRequest.builder()
.promptTemplateKey(templateKey)
.promptVariables(variables)
.userMessage(userMessage)
.build());
}
}
// 知识库客户端
public class KnowledgeBaseClient {
/**
* 文档上传和向量化(中台处理切片、向量化、存储)
*/
public DocumentUploadResult uploadDocument(
String fileName, InputStream content, DocumentMetadata metadata) {
// 中台自动处理:文档解析、切片、向量化、存储
return platform.upload("/api/v1/knowledge-base/" + kbId + "/documents",
fileName, content, metadata);
}
/**
* 语义检索
*/
public List<SearchResult> search(String query, int topK) {
return platform.post("/api/v1/knowledge-base/" + kbId + "/search",
Map.of("query", query, "topK", topK),
new TypeReference<List<SearchResult>>(){});
}
/**
* RAG问答(知识库检索 + AI回答,一步完成)
*/
public RagResponse ask(String question) {
return platform.post("/api/v1/knowledge-base/" + kbId + "/ask",
Map.of("question", question),
RagResponse.class);
}
}中台服务端的关键实现
// 中台核心服务:租户隔离的知识库管理
@Service
public class TenantKnowledgeBaseService {
private final VectorStoreManager vectorStoreManager;
private final DocumentProcessor documentProcessor;
/**
* 为租户创建独立的知识库
* 每个租户的数据在向量数据库中隔离(用collection name区分)
*/
public KnowledgeBase createKnowledgeBase(String tenantId, CreateKbRequest request) {
// 生成唯一的collection名称
String collectionName = generateCollectionName(tenantId, request.getKbId());
// 在向量数据库中创建collection
vectorStoreManager.createCollection(collectionName, VectorConfig.builder()
.dimension(1536) // text-embedding-3-small维度
.metric(DistanceMetric.COSINE)
.build());
KnowledgeBase kb = KnowledgeBase.builder()
.kbId(request.getKbId())
.tenantId(tenantId)
.collectionName(collectionName)
.embeddingModel(request.getEmbeddingModel())
.createdAt(Instant.now())
.build();
kbRepository.save(kb);
return kb;
}
/**
* 文档处理流水线(异步)
*/
@Async("documentProcessorExecutor")
public void processDocument(String tenantId, String kbId, DocumentUploadEvent event) {
try {
// 1. 文档解析(PDF/Word/HTML → 纯文本)
String text = documentParser.parse(event.getFileContent(), event.getFileType());
// 2. 智能分块(按段落/语义分块,而不是固定字符数)
List<DocumentChunk> chunks = documentChunker.chunk(text, ChunkConfig.builder()
.maxTokensPerChunk(512)
.overlapTokens(64)
.strategy(ChunkStrategy.SEMANTIC)
.build());
// 3. 向量化(批量处理,减少API调用次数)
List<float[]> vectors = embeddingService.embedBatch(
chunks.stream().map(DocumentChunk::getText).collect(Collectors.toList()),
getEmbeddingModel(tenantId, kbId)
);
// 4. 存储到向量数据库
KnowledgeBase kb = kbRepository.findByTenantAndKbId(tenantId, kbId);
vectorStoreManager.upsert(kb.getCollectionName(), buildPoints(chunks, vectors));
// 5. 更新文档状态
documentRepository.updateStatus(event.getDocumentId(), DocumentStatus.INDEXED);
log.info("文档处理完成: tenantId={}, kbId={}, docId={}, chunks={}",
tenantId, kbId, event.getDocumentId(), chunks.size());
} catch (Exception e) {
documentRepository.updateStatus(event.getDocumentId(), DocumentStatus.FAILED);
log.error("文档处理失败: {}", event.getDocumentId(), e);
}
}
}多租户的资源隔离和配额
@Service
public class TenantResourceManager {
private final TenantConfigRepository tenantConfigRepository;
private final RedisTemplate<String, Long> redis;
/**
* 检查并扣减租户配额
*/
public void checkAndDeductQuota(String tenantId, QuotaType type, long amount) {
TenantConfig config = tenantConfigRepository.findByTenantId(tenantId);
// 检查月度配额
String monthlyKey = "quota:" + tenantId + ":" + type + ":" + currentMonth();
Long used = redis.opsForValue().get(monthlyKey);
if (used == null) used = 0L;
long limit = getLimit(config, type);
if (used + amount > limit) {
throw new QuotaExceededException(
String.format("租户[%s]的[%s]配额已用尽: 已用=%d, 上限=%d",
tenantId, type, used, limit)
);
}
// 扣减配额(原子操作)
redis.opsForValue().increment(monthlyKey, amount);
redis.expireAt(monthlyKey, endOfMonth());
}
/**
* 获取租户资源使用报告
*/
public TenantUsageReport getUsageReport(String tenantId, YearMonth month) {
// 聚合各类资源使用情况
return TenantUsageReport.builder()
.tenantId(tenantId)
.month(month)
.tokenUsage(getUsage(tenantId, QuotaType.TOKENS, month))
.apiCallCount(getUsage(tenantId, QuotaType.API_CALLS, month))
.storageGb(getVectorStorageUsage(tenantId))
.costBreakdown(calculateCosts(tenantId, month))
.build();
}
}中台建设的节奏
AI中台不是一次性建好的,要有清晰的建设路径:
第一阶段(2-3个月):解决最痛的共性问题。先做模型接入层和成本监控——这是所有业务线都需要的,价值最直接。同时把AI网关搭起来,统一认证和日志。
第二阶段(3-4个月):建知识管理基础设施。文档处理、向量化、检索是搭RAG的基础,大多数业务线都需要,且自己搭质量参差不齐。
第三阶段(持续):提示词管理、评估框架、安全合规能力。这些是提升质量的重要工具,但要在业务线有了一定实践经验后再做,否则做出来是空架子。
中台最重要的KPI不是"有多少能力",而是"有多少业务线在真正使用"。如果中台建了很多功能,但业务线绕开不用,那中台就失败了。要定期和业务线对齐,他们最痛的点是什么,优先做那些事。
