Skip to content
企业级 AI 平台设计:从统一模型接入到业务落地
1. 概述:企业级 AI 平台的定义与能力边界
当应用需要接入多个大语言模型时,直接在业务代码中调用各个模型提供商的 API 会带来一系列问题:接口协议不一致、密钥管理分散、无法统一计量与审计、模型升级困难。企业级 AI 平台是一种中间层基础设施,它屏蔽下游模型的差异,向上游应用提供统一的模型访问界面,同时承担流量治理、模型管理、观测与安全控制等职责。
与直接调用模型 API 相比,企业级 AI 平台的核心区别在于:它将“模型调用”从点对点集成变成平台化能力。应用不再关心模型部署在哪里、使用什么协议、如何计费,而是通过平台的统一接口完成请求。平台自身则负责模型路由、负载均衡、限流熔断、版本灰度、调用审计与成本统计。
一个企业级 AI 平台通常需要覆盖以下能力边界:
- 多模型统一接入:支持外部托管 API 与私有化部署的模型,提供统一的协议转换。
- 推理服务管理:对私有化模型进行资源调度、弹性伸缩与性能优化。
- 模型生命周期治理:包括版本管理、权重分配、灰度发布与安全合规。
- 知识接入:通过向量检索与 RAG 链路,将私有知识注入模型生成过程。
- 应用编排:管理 Prompt、工作流与 Agent 运行时,使模型能力与业务逻辑组合成可复用的应用。
- 评测与观测:提供可量化的应用评测体系和调用链观测能力。
需要注意的是,平台并不负责模型本身的训练与精细调优。模型算法和预训练细节属于平台的上游;平台关注的是部署、调用、治理与运营。其目标是将模型能力转化为业务可用的稳定服务。
2. 总体架构:分层设计与组件协作
2.1 四层模型与组件职责
一种常见的设计思路是将企业级 AI 平台在逻辑上划分为以下四层:
- 接入层(模型网关):向上游应用暴露统一 API,负责鉴权、限流、路由、协议转换与审计。
- 服务层(推理服务与模型运行时):管理私有化模型部署,提供高吞吐、低延迟的推理能力。
- 模型层(模型实例):包括外部托管模型、私有化模型以及知识库中的嵌入模型。
- 应用层(业务系统与编排层):基于平台 API 构建业务应用,包括 Prompt 管理、工作流和 Agent 运行时。
各层之间通过明确的接口协作。例如,应用层通过 HTTP 调用模型网关,模型网关根据策略将请求转发到服务层中的某个推理实例或直接转发到外部模型 API。服务层中的推理实例通过模型层提供的模型权重执行推理。
这种分层结构与传统的应用网关和微服务架构类似,但额外增加了模型特有的治理维度:模型版本、Token 计费、上下文窗口、向量检索等。
2.2 控制平面与数据平面
在实现上,可以借鉴服务网格中的控制平面与数据平面思想:
- 数据平面:承担实际的模型请求转发与推理过程。它包含模型网关的代理进程、推理服务实例、向量数据库连接等。数据平面强调低延迟、高吞吐和弹性。
- 控制平面:负责数据平面的配置下发和状态管理,包括路由规则、限流策略、模型版本、灰度比例、权限策略等。控制平面通常以配置服务和监控服务的形式存在。
例如,当需要调整某个应用的模型路由权重时,通过控制平面修改策略,数据平面中的网关自动加载新策略,不需要重启上层应用。这种分离使得平台的动态治理能力成为可能。
3. 多模型统一接入:模型网关设计
模型网关是企业级 AI 平台中的关键组件。它向上提供标准接口,向下接入多种模型服务。一个模型网关通常需要具备以下能力:
- 协议转换:将不同模型的自然差异(如请求格式、响应格式、错误格式)转换为统一格式。目前业界广泛采用 OpenAI 兼容协议作为统一标准,这样应用只需实现一种客户端即可。
- 统一鉴权:通过 API Key、虚拟密钥或企业身份系统控制模型访问权限,并支持按用户、应用、部门设置额度。
- 流控与治理:包括限流、熔断、重试与降级。当某个模型服务不可用或响应过慢时,网关可以自动切换到备用模型或返回降级响应。
- 路由与负载均衡:根据模型能力、成本、可用性等维度决定请求应当发往哪个模型服务。例如,简单需求可以路由到轻量模型,复杂推理则路由到高性能模型。
- 审计与计量:记录每一次调用的模型、Token 消耗、耗时、调用方等信息,为成本核算和合规审计提供数据。
LiteLLM 提供了现成的模型网关实现。它定位为一个 OpenAI 兼容的代理服务(OpenAI Proxy Server),通过统一接口支持接入 100 多个大语言模型服务,并支持虚拟密钥、预算、速率限制、负载均衡、路由、回退、流量镜像、日志告警等功能[1]。在网关之外,LiteLLM 还提供缓存、Guardrails、策略和自定义插件能力,可用于治理与可观测性[2]。
下面是一个使用 LiteLLM 代理的简单配置示例,展示如何将多个模型暴露为统一的 OpenAI 兼容接口:
yaml
model_list:
- model_name: gpt-3.5-turbo # 对外统一名称
litellm_params:
model: openai/gpt-3.5-turbo
api_key: ${OPENAI_API_KEY}
- model_name: gpt-3.5-turbo
litellm_params:
model: azure/chatgpt-v2
api_key: ${AZURE_API_KEY}
api_base: https://my-azure-endpoint.openai.azure.com/
- model_name: llama-3-8b
litellm_params:
model: vllm/llama-3-8b-instruct
api_base: http://my-vllm-server:8000配置中 model_name 是对外暴露的模型名称。同一个 model_name 可以配置多个后端,网关会自动进行负载均衡或按策略路由。上层应用无需感知底层模型的差异。
如果不使用现成网关,也可以基于 Node.js 自行实现一个简易代理。下面的 Express 中间件演示了如何对不同的模型服务进行统一转发,并为每个请求添加唯一 ID:
typescript
import express from 'express';
import axios from 'axios';
import crypto from 'node:crypto';
const app = express();
app.use(express.json());
app.post('/v1/chat/completions', async (req, res) => {
const requestId = crypto.randomUUID();
const modelName = req.body.model;
const target = routeToModel(modelName); // 根据路由策略决定目标
try {
const upstream = await axios.post(target.url, req.body, {
headers: { Authorization: `Bearer ${target.apiKey}` },
});
res.json({
request_id: requestId,
model: modelName,
...upstream.data,
});
} catch (err) {
res.status(err.response?.status ?? 502).json({
request_id: requestId,
error: err.message,
});
}
});
function routeToModel(model: string) {
// 根据模型名称返回目标地址
if (model === 'gpt-3.5-turbo') {
return { url: 'https://api.openai.com/v1/chat/completions', apiKey: process.env.OPENAI_API_KEY };
}
return { url: 'http://localhost:8000/v1/chat/completions', apiKey: 'internal' };
}
app.listen(3000);上面的示例定义了一个 /v1/chat/completions 路由。crypto.randomUUID() 为每个请求生成唯一标识;routeToModel() 函数根据请求体中的模型名称返回目标服务的 URL 和密钥。网关将请求转发到上游后,在响应中附加请求 ID 和模型名称,便于调用方追踪。若上游请求失败,则返回 502 状态码和包含错误信息的 JSON 结构。
这个示例省略了限流、重试、熔断等细节,但体现了模型网关的基本职责。实际实现时,需要将路由表、密钥管理、重试策略等通过配置中心动态下发,而不是像示例中硬编码在函数中。
在网关设计中,有几个关键点需要特别留意:
- 协议兼容:不要只兼容 Chat Completions 接口,还要考虑 Embedding、Function Calling 等接口的统一。
- 超时控制:不同模型响应速度差异很大,需要为每个上游设置合理的超时时间,避免应用长时间挂起。
- 重试幂等:重试时要避免重复扣费和重复写入;对于非幂等请求,可以携带
request_id让上游去重。 - 错误映射:不同提供商返回的错误结构千差万别,网关应将其转换为统一错误码,便于应用处理。
4. 模型推理服务与资源管理
私有化模型需要企业自行部署推理服务。推理服务的核心任务是将模型权重以 API 形式暴露,并在 GPU 上高效执行推断。与直接调用外部 API 相比,私有化部署的优势是数据不出域、可深度定制,劣势是需要自行管理和优化硬件资源。
推理服务选型时,通常会关注两个开源项目:vLLM 和 TensorRT-LLM。
vLLM 是一个快速且易用的 LLM 推理与服务库,源自 UC Berkeley。它引入了 PagedAttention 机制来高效管理 attention 的 key/value 缓存,并通过 continuous batching 提升吞吐量[5]。对于大多数场景,vLLM 的部署成本低、兼容性好,适合作为推理服务的入门选择。
TensorRT-LLM 是 NVIDIA 提供的 LLM 推理库,支持 Python API 定义模型,并提供 Python/C++ 运行时。它支持从单 GPU 到多节点的部署,具备多种并行策略(TP、PP、CP、MoE-EP 等)、量化、KV cache、LoRA、投机解码、prefix caching、CUDA graphs、chunked context 等能力[3]。TensorRT-LLM 通常能带来更极致的高性能,但配置和编译成本也更高。
推理服务的资源管理主要集中在 GPU 资源调度与弹性伸缩上。由于 GPU 成本高昂,需要尽量提高资源利用率。常见机制包括:
- 动态批处理(continuous batching):将多个请求合并到同一个 batch 中处理,提高 GPU 利用率。
- KV Cache 优化:通过 PagedAttention 等机制减少显存浪费,从而提高并发上限。
- 弹性伸缩:根据队列长度和 GPU 利用率,自动增加或减少推理实例。例如,当请求排队时间超过阈值时,调度器分配新的 GPU 节点;空闲时回收实例。
- 模型并行:对于超大规模模型,将模型分片到多张 GPU 上进行张量并行或流水线并行。
TensorRT-LLM 的节点元数据中会暴露 GPU SKU、数量、内存、CUDA 版本、模型架构类名、并行配置、量化算法、dtype、KV cache dtype 等信息[4]。这些信息可以被上层调度系统收集,用于实例调度和容量规划。
下面是一个使用 vLLM 启动一个 OpenAI 兼容服务的命令示例:
bash
vllm serve meta-llama/Llama-3-8B-Instruct \
--host 0.0.0.0 \
--port 8000 \
--tensor-parallel-size 2 \
--max-model-len 8192--host 0.0.0.0 表示监听所有网络接口,--port 8000 指定服务端口,--tensor-parallel-size 2 使用两张 GPU 进行张量并行,--max-model-len 8192 设置模型最大上下文长度。启动后,该服务暴露 OpenAI 兼容的 /v1/chat/completions 接口。业务层或模型网关可以像调用 OpenAI API 一样调用私有化模型。
当使用 Node.js 调用该服务时,可以复用官方的 openai 客户端,只需将 baseURL 指向私服地址:
typescript
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: 'internal-key',
baseURL: 'http://localhost:8000/v1',
});
const completion = await client.chat.completions.create({
model: 'meta-llama/Llama-3-8B-Instruct',
messages: [{ role: 'user', content: 'Hello' }],
});
console.log(completion.choices[0].message.content);这里的 apiKey 可以是任意非空字符串,因为 vLLM 服务默认不校验密钥,但客户端要求该字段存在。将 baseURL 指向 vLLM 服务的 /v1 路径后,即可沿用 OpenAI SDK 的类型定义和调用方式。
私有化推理服务的运维复杂度比调用外部 API 更高。过程中需要注意:
- 显存规划:需要根据模型大小、并发数和 KV Cache 设置预留足够的显存,避免 OOM。
- 冷启动时间:模型加载和预热需要时间,弹性伸缩时需要预留缓冲。
- 多模型共存:在同一 GPU 上同时部署多个小模型时,需要隔离资源并对 SLO 分别管理。
5. 模型治理:版本、权重与灰度发布
当平台接入多个模型后,模型治理成为关键问题。所谓治理,是指对模型的版本变化、流量分配、可追溯性进行管理,使上层应用不因模型升级而中断。
5.1 模型版本管理
每个模型实例应被视为一个独立版本。例如,某些模型供应商会在模型名后附加发布日期作为版本标识。平台需要记录版本信息,包括模型标识、供应商、部署地址、配置参数、发布时间等。模型网关在路由时,可以基于版本号精确地将请求转发到特定实例。
5.2 权重管理
对于同一个逻辑模型名,可以配置多个模型版本,并分配不同的流量权重。权重通常用于分阶段上线。例如,可以将大部分请求指向新版本,少量请求指向旧版本,并随着验证通过逐步提高新版本比例。
权重分配可以通过配置中心动态调整。LiteLLM 支持对同一 model_name 配置多个上游,并通过负载均衡和路由策略分配流量[1]。
5.3 灰度发布
灰度发布是一种风险控制手段。当新模型版本上线时,先让少量请求使用新版本,观察运行指标,再逐步扩大流量比例。灰度过程包括以下几个步骤:
- 将新版本注册到模型网关,但初始权重为 0。
- 通过测试请求验证新版本的功能与响应质量。
- 将流量权重逐步提高,从低比例逐步扩大到全量。
- 持续监测错误率、延迟、Token 消耗以及业务反馈。
- 如果指标异常,则立即将权重降为 0,回滚到旧版本。
在模型网关中,灰度通常以 A/B 测试或流量镜像的形式实现。LiteLLM 支持流量镜像,即把真实请求复制一份到新模型进行对比,同时不会将新模型的响应返回给用户[2]。这种方式可以用在灰度前期的质量评估中。
模型治理还需要与评测体系联动。灰度发布不能只看工程指标(延迟、错误率),还需要看模型输出的质量。因此,平台需要为每次灰度配置自动化评测,以判断新版本是否达到上线标准。
6. 知识接入:向量检索与 RAG 链路
大语言模型的知识来自训练数据,无法覆盖企业内部文档和实时数据。RAG(Retrieval-Augmented Generation,检索增强生成)通过将检索到的额外数据与用户查询一起作为上下文提供给模型,使生成结果更准确[6]。
RAG 的基本流程分为知识库预处理、查询检索和增强生成三个阶段[7]。首先,将企业文档切片并使用嵌入模型转换为向量,存入向量数据库;收到用户查询时,将查询同样转换为向量,在向量数据库中检索最相关的片段;最后,将检索到的片段与用户查询拼接到 Prompt 中,交给大模型生成回答。
下面是一个简化但可运行的 RAG 数据摄取示例。它使用 Node.js 将文档分块,并调用 OpenAI Embedding API 生成向量,然后存储到本地数组:
typescript
import fs from 'node:fs';
import OpenAI from 'openai';
const openai = new OpenAI();
function chunkText(text: string, size = 500): string[] {
const chunks: string[] = [];
for (let i = 0; i < text.length; i += size) {
chunks.push(text.slice(i, i + size));
}
return chunks;
}
async function embed(text: string) {
const res = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: text,
});
return res.data[0].embedding;
}
(async () => {
const doc = fs.readFileSync('manual.txt', 'utf-8');
const chunks = chunkText(doc);
const records = [];
for (const [index, chunk] of chunks.entries()) {
const vector = await embed(chunk);
records.push({ id: index, text: chunk, vector });
}
// 写入向量数据库(此处以 JSON 文件模拟)
fs.writeFileSync('vectors.json', JSON.stringify(records));
})();这段数据摄取脚本将 manual.txt 按 500 字符切块,依次调用 Embedding API 得到向量,最后将 { id, text, vector } 的记录写入本地 JSON 文件,模拟向量数据库的结果表。
检索阶段,计算用户查询向量与所有文档向量的余弦相似度,返回最接近的片段:
typescript
function cosineSimilarity(a: number[], b: number[]): number {
const dot = a.reduce((sum, x, i) => sum + x * b[i], 0);
const normA = Math.sqrt(a.reduce((s, x) => s + x * x, 0));
const normB = Math.sqrt(b.reduce((s, x) => s + x * x, 0));
return dot / (normA * normB);
}
async function search(query: string, topK = 3) {
const queryVector = await embed(query);
const records = JSON.parse(fs.readFileSync('vectors.json', 'utf-8'));
const scored = records
.map((r) => ({ ...r, score: cosineSimilarity(queryVector, r.vector) }))
.sort((a, b) => b.score - a.score);
return scored.slice(0, topK);
}cosineSimilarity 计算两个向量的余弦值,数值越接近 1 表示方向越一致。search 函数将查询向量的嵌入与所有记录逐一比较,按得分降序返回前 topK 条。
最后,将检索到的片段拼入 Prompt,请求模型生成回答:
typescript
async function ask(question: string) {
const results = await search(question);
const context = results.map((r) => r.text).join('\n\n');
const completion = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'system', content: '请仅根据提供的上下文回答用户问题。' },
{ role: 'user', content: `上下文:\n${context}\n\n问题:${question}` },
],
});
return completion.choices[0].message.content;
}ask 函数执行一次完整的 RAG 查询:先检索与问题最相关的文档片段,将其拼接为上下文,再交由模型生成回答,从而让模型利用外部知识生成结果。
RAG 系统的性能高度依赖向量数据库和检索策略。相关研究对 FAISS、Qdrant、Chroma 等向量数据库在 RAG 场景下的表现进行过系统比较,评估指标包括检索质量、延迟、资源占用等[8]。在实际选择时,应考虑是否支持过滤、混合检索、数据持久化、高可用等企业级需求。
在 RAG 设计中需注意:
- 分块策略:块过大导致上下文包含无用信息;块过小导致语义不完整。需要根据文档类型和模型上下文窗口调整。
- 相似度度量:余弦相似度是常见选择,但不同向量数据库对度量的实现有细微差别,需验证一致性。
- 元数据过滤:在检索时通过部门、权限、文档类型等元数据过滤,避免跨权限数据泄露。
- 检索结果排序:有时需要结合重排模型(reranker)对初步检索结果做二次排序,以提升相关性。
7. 应用编排:Prompt、工作流与 Agent 运行时
模型网关和推理服务解决了“如何调用模型”的问题,而应用编排解决“如何将模型调用组合成业务能力”的问题。典型手段包括 Prompt 管理、工作流编排和 Agent 运行时。
7.1 Prompt 管理
Prompt 是模型交互的核心。企业级平台需要将 Prompt 版本化,并针对不同场景进行统一管理。一个 Prompt 模板通常包含固定文本、变量插槽和模型参数:
typescript
const promptTemplate = `
你是智能客服。请根据以下产品信息回答客户的问题。
产品信息:
{productInfo}
客户问题:
{question}
回答要求:
- 简洁、专业
- 如果信息不足,请说明无法回答
`;平台管理 Prompt 时,可以从配置服务加载模板,避免在代码中硬编码:
typescript
function renderPrompt(template: string, variables: Record<string, string>) {
return template.replace(/\{(\w+)\}/g, (_, key) => variables[key] ?? '');
}renderPrompt 使用正则匹配模板中的 {变量名} 占位符,并从 variables 对象中取值替换。该函数是 Prompt 模板渲染的最小实现。
7.2 工作流编排
对于复杂的业务场景,可以通过工作流将多个模型调用和逻辑判断组织到一个有向无环图中。例如,一个客户支持工作流可能是:
- 对用户问题做意图分类。
- 如果问题属于“订单查询”,调用订单 API 获取信息。
- 将信息送给模型生成回答。
工作流编排器需要支持条件分支、循环、超时、重试和人工审批等节点。实际工程中,可以使用 Node.js 编写简单的状态机,或使用专门的编排引擎。
7.3 Agent 运行时
Agent 在大模型之上增加了“感知-决策-行动”的循环。它允许模型调用外部工具,根据工具返回结果调整后续动作。Agent 运行时的核心是工具调用(Function Calling / Tool Calling)的管理。
下面是一个 Node.js 示例,演示如何向模型声明一个查询天气的工具,并在模型请求调用时执行该工具:
typescript
import OpenAI from 'openai';
const openai = new OpenAI();
async function getWeather(city: string) {
return { city, temperature: 24, unit: 'C' };
}
async function run() {
const messages = [
{ role: 'user', content: '北京明天天气如何?' },
];
const tools = [
{
type: 'function',
function: {
name: 'get_weather',
description: '查询某个城市的当前天气',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: '城市名' },
},
required: ['city'],
},
},
},
];
const first = await openai.chat.completions.create({
model: 'gpt-4o',
messages,
tools,
});
const choice = first.choices[0];
if (choice.message.tool_calls) {
const toolCall = choice.message.tool_calls[0];
const args = JSON.parse(toolCall.function.arguments);
const result = await getWeather(args.city);
messages.push(choice.message);
messages.push({
role: 'tool',
tool_call_id: toolCall.id,
content: JSON.stringify(result),
});
}
const second = await openai.chat.completions.create({
model: 'gpt-4o',
messages,
});
console.log(second.choices[0].message.content);
}该示例展示了工具调用的完整循环:第一步将用户消息和工具定义发送给模型,模型返回一个 tool_calls 指令,其中包含函数名和参数;程序执行该函数后,将结果以 role: 'tool' 的消息追加到会话,并携带 tool_call_id 关联到原始调用;最后将包含了工具结果的完整消息列表再次发送给模型,由模型基于工具输出生成最终回答。
注意:此示例中的 choice.message.tool_calls 是模型协议中用于函数调用返回的字段。不同的 SDK 可能在类型定义上略有差异,实际项目中应以所用模型提供方的文档为准。
Agent 运行时还需要管理多轮对话中的上下文。每次工具调用都会产生新的消息,上下文长度会不断增长。因此需要设计上下文裁剪和摘要策略,例如保留最近 N 轮消息、将早期消息摘要化等。
8. AI 应用评测体系
模型输出的非确定性使得应用上线前必须经过系统性评测。评测体系的目的是回答两个问题:
- 这个模型/应用是否达到了预期质量?
- 当模型升级时,质量是否回退?
8.1 评测集
评测集由输入和期望输出组成。输入可以是问题、指令、对话历史;期望输出可以是标准答案、分类标签或评分规则。评测集需要覆盖典型业务场景和边界情况。对于企业内部场景,评测集通常来自真实用户请求的抽样,再经过人工标注。
8.2 评测指标
评测指标可以分为工程指标和质量指标。
- 工程指标:包括响应延迟、吞吐量、可用性、Token 消耗、成本。
- 质量指标:对于生成类任务,常用指标包括正确性、忠实性、相关性、连贯性和无害性。对于分类或抽取任务,可以使用准确率、召回率、F1 等传统指标。
许多质量指标无法自动计算,需要借助 LLM 作为评测助手。例如,给定问题和参考答案,让另一个模型为候选回答打分,或判断回答是否与检索上下文一致。这种方式存在一定偏差,但胜在可大规模执行。
8.3 自动化评测框架
评测需要自动化才能支撑持续迭代。平台通常会提供一个评测流水线,流程如下:
- 从评测集中选取一批用例。
- 调用被测应用,得到输出。
- 使用自动评估器(如 LLM-as-Judge、语义相似度、规则校验)计算指标。
- 汇总结果并输出报告。
下面是一个简单的自动化评测脚本示例,使用 Node.js 对一组问题执行测试并计算准确率:
typescript
import OpenAI from 'openai';
const openai = new OpenAI();
const evalSet = [
{ input: '北京有哪四个季节?', expected: ['春', '夏', '秋', '冬'] },
{ input: 'AI 的中文全称是什么?', expected: ['人工智能'] },
];
async function evaluate() {
let correct = 0;
let total = 0;
for (const item of evalSet) {
const completion = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: item.input }],
});
const answer = completion.choices[0].message.content ?? '';
total += 1;
const passed = item.expected.some((kw) => answer.includes(kw));
if (passed) correct += 1;
console.log(`Input: ${item.input}`);
console.log(`Answer: ${answer}`);
console.log(`Passed: ${passed}\n`);
}
console.log(`Accuracy: ${correct / total}`);
}
evaluate();该脚本遍历评测集中的问题,调用 gpt-4o 生成回答,并检查回答是否包含期望中的关键词。统计正确次数后输出整体准确率。这里的“关键词包含”判定逻辑只适用于简单任务;真实评测应根据任务类型设计更合适的判定逻辑,例如使用 LLM-as-Judge 打分或语义相似度计算。
评测体系的建设是一个持续过程。每次模型升级、Prompt 修改或 RAG 链路变化后,都应该重新运行评测集。平台应保存历史评测结果,用以对比版本变化。
9. 可观测性与运营
可观测性是平台持续稳定运行的保障。它覆盖三个维度:日志、指标和追踪。模型网关作为唯一入口,天然适合收集调用元数据。
9.1 日志
模型网关应记录每次调用的完整信息:请求 ID、调用方、模型名、输入输出摘要、Token 消耗、耗时、错误状态等。出于隐私考虑,完整的输入输出不应直接写入日志,可以记录哈希值或截断文本,原始数据单独加密存储。
9.2 指标
核心指标包括:
- 请求速率(QPS)
- 响应延迟分布(P50、P95、P99)
- 错误率
- Token 消耗速率
- 按模型的成本统计
- 限流触发次数
这些指标可以从网关中通过 Prometheus 格式暴露:
typescript
import express from 'express';
import client from 'prom-client';
const app = express();
const httpRequestDuration = new client.Histogram({
name: 'ai_gateway_request_duration_seconds',
help: 'Duration of model requests',
labelNames: ['model', 'status'],
});
const tokenCounter = new client.Counter({
name: 'ai_gateway_tokens_total',
help: 'Total tokens consumed',
labelNames: ['model', 'type'],
});
// 在中间件中计时并记录
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer();
res.on('finish', () => {
end({ model: req.body?.model ?? 'unknown', status: res.statusCode });
});
next();
});
app.get('/metrics', async (req, res) => {
res.set('Content-Type', client.register.contentType);
res.end(await client.register.metrics());
});这段代码注册了两个指标:ai_gateway_request_duration_seconds 是一个 Histogram,用于统计请求耗时的分布;ai_gateway_tokens_total 是一个 Counter,用于累计 Token 消耗量。/metrics 端点以 Prometheus 文本格式暴露所有已注册指标。
9.3 调用追踪
当请求经过应用、网关、推理服务、向量数据库等多个组件时,需要通过分布式追踪还原完整链路。可以在 HTTP 头中传递 trace_id,在各组件间透传。例如:
typescript
import { v4 as uuidv4 } from 'uuid';
app.use((req, res, next) => {
const traceId = req.headers['x-trace-id'] ?? uuidv4();
req.traceId = traceId;
res.setHeader('x-trace-id', traceId);
next();
});该中间件从请求头中读取 x-trace-id,如果不存在则生成一个新的 UUID,并将该 ID 写入响应头。后续组件通过读取请求头中的 x-trace-id 即可关联到同一个调用链。
9.4 反馈闭环
运营的最终目标是提升系统质量,因此需要为业务提供反馈渠道。例如,允许用户对模型回答进行点赞/点踩。反馈数据应回流到评测集或用于 Prompt 调优。将实际运行中的用户反馈与评测指标结合,可以形成持续改进的闭环。
10. 安全合规:权限、数据隔离与审计
企业级 AI 平台需要处理敏感数据和模型访问权限,安全设计必须贯穿各层。
10.1 统一身份与权限
所有模型访问必须经过身份认证。应用使用 API Key 或服务账号获取访问令牌,模型网关对令牌进行校验,并检查该调用方是否有权访问指定模型。LiteLLM 支持虚拟密钥,即为每个应用或用户生成独立的密钥,并绑定预算和速率限制[1]。这样可以避免共享密钥导致无法计量和追责。
10.2 数据隔离
对于多租户场景,不同业务部门的数据不能互相访问。隔离措施包括:
- 在模型请求中附带租户标识,网关据此进行权限判定。
- 在向量数据库中,通过元数据字段(如
tenant_id)过滤检索结果。 - 对敏感信息进行脱敏处理,例如在写入日志前移除身份证号、手机号等。
10.3 审计
审计日志与普通日志不同,需要满足合规和追溯要求。审计日志通常包含:操作者身份、操作时间、调用的模型、输入输出(或经授权的摘要)、数据访问范围、异常行为标记。审计日志不能随意修改,应保存到独立的存储系统中,并设置合理的保留周期。
11. 落地实施路径
11.1 分阶段实施
从零开始建设企业级 AI 平台,可以遵循以下阶段:
- 明确场景:选择一两个高价值、低风险的业务场景作为试点,例如内部知识问答、客服助手。避免一开始就接入多个复杂 Agent。
- 搭建最小网关:先用开源网关或自研代理,统一接入外部模型 API,让试点业务通过平台调用。
- 建立评测机制:为试点场景准备评测集。先人工评测,再逐步引入自动评测。
- 接入私有化模型:当有数据合规或成本控制要求时,再部署 vLLM 等推理服务。
- 完善治理与观测:补充版本管理、灰度发布、计量和审计能力。
- 横向扩展:将平台能力开放给更多业务部门,建立自助接入流程。
11.2 工程注意点
在实施过程中,以下问题需要特别关注:
- 模型选择应以场景需求为前提,而不是一味追求大参数模型。外部 API 与私有化部署的成本差异很大,需要根据调用量预估。
- Prompt 和模型版本会频繁变化,必须通过配置管理并做版本记录,避免“上周还能用,这周突然不行”的问题。
- RAG 链路中,文档更新频率影响检索质量。需要建立文档同步机制,让向量数据库反映最新内容。
- 模型输出的“幻觉”并不能完全避免。对于高风险决策场景,应添加人工审批环节,或限制系统对用户的承诺。
12. 模型协议与生态演进
模型生态正在快速标准化。OpenAI 兼容协议已经成为事实标准,vLLM、LiteLLM 等工具都提供 /v1 兼容接口。这意味着企业可以在不修改上层代码的情况下替换底层模型。
另一个值得关注的趋势是 MCP(Model Context Protocol)。MCP 提供一种标准化的方式,让模型访问外部数据源和工具。它类似于 HTTP 对 Web 的作用:如果每个工具都实现一套自己的接入协议,Agent 将难以维护。MCP 的出现使得工具接入和复用变得更规范。
对于平台架构师来说,技术选型时需要保留协议抽象层。即使某些模型尚未支持标准协议,也可以通过网关适配器将其转换为标准格式。这样,当模型生态继续演进时,平台的接入成本才能保持较低。
参考链接
- [1] https://docs.litellm.ai/docs/simple_proxy
- [3] https://github.com/NVIDIA/TensorRT-LLM
- [5] https://docs.vllm.ai/en/latest
- [6] https://www.mongodb.com/docs/vector-search/tutorials/rag
- [7] https://github.com/microsoft/generative-ai-for-beginners/blob/main/15-rag-and-vector-databases/README.md
- [8] https://ieeexplore.ieee.org/document/11206778
