AI 应用架构设计

2024 年之后,"AI 能力" 已经从锦上添花变成 SaaS 产品的标配。但把 ChatGPT API 包一层皮并不是 AI 应用——真正的 AI 产品需要解决模型选型、上下文管理、流式交互、知识增强、工具调用、成本控制等一系列工程问题。本章从需求分析出发,梳理 AI 应用的技术选型和架构设计,为后续章节(Chat、RAG、Agent、商业化)打下地基。

1. AI 应用需求分析

1.1 AI 应用的三个层次

在 SaaS 产品中嵌入 AI 能力,通常分为三个递进的层次:

层次典型功能技术复杂度用户价值
L1 对话助手智能客服、内容生成、翻译润色提升效率
L2 知识增强基于企业文档问答、智能搜索、报告分析数据价值释放
L3 自主代理自动化工作流、多步推理、跨系统操作流程自动化

大多数团队应该从 L1 起步——一个稳定的对话界面 + 流式输出就足以验证 PMF(Product-Market Fit)。过早跳到 L3 会导致可靠性问题:Agent 的每一步推理都有失败概率,五步链式调用的整体成功率可能只有 60%。

1.2 需求拆解框架

在决定技术方案之前,先回答这五个核心问题:

问题一:用户是谁?

  • B2B SaaS:用户是企业员工,对准确性要求高,对响应速度容忍度高
  • B2C 产品:用户是普通消费者,对体验要求高,对幻觉(Hallucination)容忍度低
  • 开发者工具:用户是技术人员,需要精确的代码/API 输出

问题二:输入是什么?

  • 纯文本对话:最简单,直接调用 Chat Completion API
  • 文档/文件:需要解析(PDF、Word、Excel)+ 分块 + 向量化
  • 结构化数据:需要 Text-to-SQL 或数据查询管道
  • 多模态:图片理解、语音转文字,模型选型完全不同

问题三:输出是什么?

  • 自然语言回答:标准 Chat 场景
  • 结构化数据:JSON 输出,需要 Schema 约束(Structured Output)
  • 执行动作:调用 API、发送邮件、创建工单——这就是 Agent

问题四:上下文有多大?

  • 短上下文(< 4K tokens):单轮问答,简单场景
  • 中上下文(4K - 32K):多轮对话 + 少量文档
  • 长上下文(32K - 128K+):整本文档分析、长对话历史

上下文窗口直接决定模型选型和成本。128K 上下文的单次调用成本可能是 4K 的 30 倍。

问题五:对延迟的要求?

  • 实时交互(< 2s 首 token):需要流式输出 + 快速模型
  • 后台处理(分钟级):可以用更强但更慢的模型,批量处理
  • 混合模式:简单问题快速回答,复杂问题异步处理

1.3 产品形态矩阵

根据上述问题,常见的 AI 产品形态可以归纳为:

产品形态输入输出核心技术代表产品
AI 聊天文本文本流Chat Completion + StreamingChatGPT, Claude
文档问答文档 + 问题答案 + 引用RAG (检索增强生成)Notion AI, Glean
内容生成提示词长文本/结构化Prompt Engineering + TemplatesJasper, Copy.ai
数据分析数据表 + 自然语言图表/SQL/洞察Text-to-SQL + VisualizationJulius AI
AI Agent目标描述多步执行结果Tool Use + ReActDevin, Cursor
AI 工作流触发条件自动化操作Workflow + LLM NodeZapier AI, n8n

本系列后续章节将逐一实现前四种形态——Chat(第 76 章)、RAG(第 77 章)、Agent(第 78 章),以及商业化(第 79 章)。

2. AI 技术选型

2.1 模型提供商对比

选择 LLM 提供商是第一个决策点。以下是 2024-2025 年的主流选择:

提供商代表模型上下文窗口优势劣势
OpenAIGPT-4o, GPT-4o-mini128K生态最成熟,工具调用稳定价格较高,数据隐私顾虑
AnthropicClaude 3.5 Sonnet/Haiku200K超长上下文,指令遵循优秀工具调用略弱于 GPT-4
GoogleGemini 1.5 Pro/Flash1M超长上下文,多模态原生API 稳定性有波动
DeepSeekDeepSeek-V3, R164K性价比极高,推理能力强服务可用性、合规性
本地部署Llama 3, Qwen2.532K-128K数据完全私有,无 API 费用需要 GPU,运维成本高

选型建议:

  • 快速起步:OpenAI GPT-4o-mini 作为默认模型,成本低、速度快、足够大多数场景
  • 高质量需求:GPT-4o 或 Claude 3.5 Sonnet 作为高级模型,用于复杂推理
  • 成本敏感:DeepSeek-V3 性价比极高,中文场景效果好
  • 数据合规:本地部署 Llama 3 或 Qwen2.5,数据不出内网

2.2 AI SDK 选型

在 Next.js 生态中,Vercel AI SDK 是事实标准。它解决的不是"能不能调 API"的问题,而是"如何优雅地处理流式响应、工具调用、多模型切换"。

SDK特点推荐场景
Vercel AI SDK (ai)框架级集成、Provider 模式、流式原语✅ Next.js 项目首选
LangChain.js链式编排、丰富的 Loader/SplitterRAG 管道复杂时
LlamaIndex.TS专注 RAG,索引/检索抽象好重度 RAG 场景
直接调用 OpenAI SDK最大灵活性简单场景或学习用途

Vercel AI SDK 的核心优势在于 Provider 模式——同一套代码可以无缝切换 OpenAI、Anthropic、Google 等多个模型提供商:

// 通过不同 Provider 使用不同模型,接口完全一致
import { openai } from '@ai-sdk/openai'
import { anthropic } from '@ai-sdk/anthropic'
import { google } from '@ai-sdk/google'
 
// 三行代码,三个模型提供商
const gpt4o = openai('gpt-4o')
const claude = anthropic('claude-3-5-sonnet-20241022')
const gemini = google('gemini-1.5-pro')

这种设计的工程价值在于:当你发现某个模型在特定任务上表现更好(或更便宜),切换成本几乎为零。

2.3 模型路由策略

成熟的 AI 产品不会只用一个模型。根据任务类型动态选择模型,是控制成本和质量的关键策略:

// lib/ai/model-router.ts
import { openai } from '@ai-sdk/openai'
import { anthropic } from '@ai-sdk/anthropic'
 
type TaskType = 'chat' | 'summarize' | 'code' | 'analysis' | 'simple'
 
const MODEL_MAP: Record<TaskType, ReturnType<typeof openai>> = {
  simple:    openai('gpt-4o-mini'),          // 简单问答:最便宜
  chat:      openai('gpt-4o'),               // 通用对话:平衡
  summarize: anthropic('claude-3-5-haiku-20241022'), // 摘要:Haiku 又快又好
  code:      anthropic('claude-3-5-sonnet-20241022'),// 代码:Sonnet 最强
  analysis:  openai('gpt-4o'),               // 数据分析:GPT-4o 结构化输出稳定
}
 
export function getModel(task: TaskType) {
  return MODEL_MAP[task]
}

更进一步,可以基于用户套餐选择模型——免费用户用 mini 模型,付费用户用旗舰模型。这个策略在第 79 章商业化中详细展开。

2.4 向量数据库选型

RAG(检索增强生成)需要向量数据库来存储和检索文档嵌入。主流选择如下:

方案类型特点推荐场景
pgvectorPostgreSQL 扩展复用现有 PG,运维简单✅ 中小规模首选
Pinecone托管服务全托管,自动扩展不想运维向量库
Weaviate开源向量库混合搜索(向量 + 关键词)搜索质量要求高
Qdrant开源向量库Rust 写的,性能好大规模向量检索
Chroma嵌入式向量库最简单,适合原型本地开发/POC

推荐 pgvector——SaaS 产品已经有 PostgreSQL 了,加一个扩展就能做向量检索,不需要引入新的基础设施。百万级文档以下,pgvector 的性能完全够用。

2.5 Embedding 模型选型

Embedding 模型将文本转换为向量表示,是 RAG 检索质量的关键:

模型维度价格 (1M tokens)特点
text-embedding-3-small1536$0.02✅ 性价比最高
text-embedding-3-large3072$0.13更高精度
Cohere embed-v31024$0.10多语言优秀
BGE-M3 (本地)1024免费中文效果好,需自部署

对于大多数 SaaS 场景,OpenAI 的 text-embedding-3-small 足够好且最便宜——1M tokens 只需 $0.02,一本 300 页的书大约 500K tokens,成本不到 $0.01。

3. 架构设计

3.1 整体分层架构

一个完整的 AI SaaS 应用分为五层:

┌──────────────────────────────────────────────────┐
│                   客户端层                        │
│  Chat UI ─ Markdown 渲染 ─ 流式显示 ─ 文件上传    │
├──────────────────────────────────────────────────┤
│                   API 层                          │
│  Route Handlers ─ Server Actions ─ 鉴权 ─ 限流    │
├──────────────────────────────────────────────────┤
│                   AI 编排层                        │
│  Prompt 管理 ─ 模型路由 ─ 工具调用 ─ RAG 管道      │
├──────────────────────────────────────────────────┤
│                   数据层                          │
│  对话历史(PG) ─ 向量存储(pgvector) ─ 文件存储(S3)  │
├──────────────────────────────────────────────────┤
│                   基础设施层                       │
│  LLM API ─ Embedding API ─ 队列(Inngest) ─ 缓存   │
└──────────────────────────────────────────────────┘

每一层的职责边界清晰:

  • 客户端层:只负责显示和交互,不直接调用 LLM API
  • API 层:鉴权、限流、参数校验,是安全边界
  • AI 编排层:核心逻辑——Prompt 构建、模型选择、RAG 检索、工具调度
  • 数据层:持久化存储,对话历史、向量索引、上传文件
  • 基础设施层:外部依赖——LLM 提供商、异步队列、缓存

3.2 Next.js 路由结构

app/
├── (dashboard)/
│   ├── chat/
│   │   ├── page.tsx           ← 对话列表
│   │   └── [id]/page.tsx      ← 单个对话
│   ├── knowledge/
│   │   ├── page.tsx           ← 知识库列表
│   │   └── [id]/page.tsx      ← 知识库详情 + 文档管理
│   └── agents/
│       ├── page.tsx           ← Agent 列表
│       └── [id]/page.tsx      ← Agent 配置
├── api/
│   ├── chat/route.ts          ← 对话 API(流式)
│   ├── knowledge/
│   │   ├── upload/route.ts    ← 文档上传
│   │   └── query/route.ts     ← RAG 查询
│   └── agent/
│       └── run/route.ts       ← Agent 执行
lib/
├── ai/
│   ├── models.ts              ← 模型配置 + 路由
│   ├── prompts.ts             ← System Prompt 管理
│   ├── tools.ts               ← 工具定义
│   └── rag/
│       ├── embed.ts           ← 文档嵌入
│       ├── retrieve.ts        ← 向量检索
│       └── chunk.ts           ← 文档分块

3.3 数据模型设计

AI 功能需要的核心数据表:

// lib/db/schema.ts(AI 相关表)
 
// 对话
export const conversations = pgTable('conversations', {
  id: uuid('id').primaryKey().defaultRandom(),
  tenantId: uuid('tenant_id').references(() => tenants.id).notNull(),
  userId: uuid('user_id').references(() => users.id).notNull(),
  title: text('title'),                    // 自动生成的对话标题
  model: text('model').default('gpt-4o-mini'),
  systemPrompt: text('system_prompt'),
  metadata: jsonb('metadata'),             // 额外配置
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().notNull(),
})
 
// 消息
export const messages = pgTable('messages', {
  id: uuid('id').primaryKey().defaultRandom(),
  conversationId: uuid('conversation_id')
    .references(() => conversations.id, { onDelete: 'cascade' }).notNull(),
  role: text('role', { enum: ['user', 'assistant', 'system', 'tool'] }).notNull(),
  content: text('content').notNull(),
  toolCalls: jsonb('tool_calls'),          // 工具调用记录
  toolResults: jsonb('tool_results'),      // 工具返回结果
  tokenCount: integer('token_count'),      // 消耗的 token 数
  model: text('model'),                    // 实际使用的模型
  latencyMs: integer('latency_ms'),        // 响应时间
  createdAt: timestamp('created_at').defaultNow().notNull(),
})
 
// 知识库
export const knowledgeBases = pgTable('knowledge_bases', {
  id: uuid('id').primaryKey().defaultRandom(),
  tenantId: uuid('tenant_id').references(() => tenants.id).notNull(),
  name: text('name').notNull(),
  description: text('description'),
  embeddingModel: text('embedding_model').default('text-embedding-3-small'),
  chunkSize: integer('chunk_size').default(512),
  chunkOverlap: integer('chunk_overlap').default(50),
  documentCount: integer('document_count').default(0),
  createdAt: timestamp('created_at').defaultNow().notNull(),
})
 
// 文档
export const documents = pgTable('documents', {
  id: uuid('id').primaryKey().defaultRandom(),
  knowledgeBaseId: uuid('knowledge_base_id')
    .references(() => knowledgeBases.id, { onDelete: 'cascade' }).notNull(),
  name: text('name').notNull(),
  fileKey: text('file_key'),               // S3 存储路径
  fileSize: integer('file_size'),
  mimeType: text('mime_type'),
  status: text('status', { enum: ['pending', 'processing', 'ready', 'failed'] })
    .default('pending').notNull(),
  chunkCount: integer('chunk_count').default(0),
  errorMessage: text('error_message'),
  createdAt: timestamp('created_at').defaultNow().notNull(),
})
 
// 文档块 + 向量(需要 pgvector 扩展)
export const documentChunks = pgTable('document_chunks', {
  id: uuid('id').primaryKey().defaultRandom(),
  documentId: uuid('document_id')
    .references(() => documents.id, { onDelete: 'cascade' }).notNull(),
  content: text('content').notNull(),
  embedding: vector('embedding', { dimensions: 1536 }),  // pgvector
  metadata: jsonb('metadata'),             // 页码、章节等
  chunkIndex: integer('chunk_index').notNull(),
})
 
// Token 用量记录
export const aiUsage = pgTable('ai_usage', {
  id: uuid('id').primaryKey().defaultRandom(),
  tenantId: uuid('tenant_id').references(() => tenants.id).notNull(),
  userId: uuid('user_id').references(() => users.id).notNull(),
  model: text('model').notNull(),
  inputTokens: integer('input_tokens').notNull(),
  outputTokens: integer('output_tokens').notNull(),
  cost: numeric('cost', { precision: 10, scale: 6 }),    // 美元
  operationType: text('operation_type'),   // 'chat' | 'embedding' | 'tool'
  createdAt: timestamp('created_at').defaultNow().notNull(),
})

几个关键的设计决策说明:

  • messages 表不嵌套在 conversations 的 JSONB 中:消息可能非常多,独立成表才能高效查询、分页、统计
  • documentChunks 使用 pgvector 的 vector 类型:PostgreSQL 原生支持向量相似度搜索
  • aiUsage 独立成表:用量统计是计费的基础,需要精确记录每一次调用
  • 所有表都带 tenantId:多租户隔离,和前几章的 SaaS 架构一致

3.4 关键索引

-- 向量搜索索引(IVFFlat,适合百万级数据)
CREATE INDEX ON document_chunks
  USING ivfflat (embedding vector_cosine_ops)
  WITH (lists = 100);
 
-- 对话查询:按租户 + 用户 + 时间
CREATE INDEX idx_conversations_tenant_user
  ON conversations (tenant_id, user_id, created_at DESC);
 
-- 消息查询:按对话 + 时间
CREATE INDEX idx_messages_conversation
  ON messages (conversation_id, created_at ASC);
 
-- 用量统计:按租户 + 月份
CREATE INDEX idx_ai_usage_tenant_month
  ON ai_usage (tenant_id, created_at);

3.5 请求生命周期

以一次 AI 对话请求为例,完整的生命周期如下:

用户发送消息
    │
    ▼
① Middleware:鉴权 + Rate Limit
    │
    ▼
② API Route:参数校验 + 获取 tenantId
    │
    ▼
③ 配额检查:当月 token 余量是否足够
    │
    ▼
④ 上下文构建:System Prompt + 历史消息 + RAG 检索结果(可选)
    │
    ▼
⑤ 模型调用:streamText() 流式输出
    │
    ▼
⑥ 流式返回:逐 token 推送到客户端
    │
    ▼
⑦ 完成回调:保存消息 + 记录 token 用量
    │
    ▼
⑧ 异步任务:生成对话标题(首次对话)

其中,步骤 ④ 到 ⑦ 是核心链路,必须确保低延迟。步骤 ⑧ 是异步的,不阻塞用户。

4. 与 SaaS 基座融合

4.1 复用已有基础设施

前面 14 章搭建的 SaaS 基座,在 AI 功能中可以直接复用:

SaaS 能力AI 场景中的用途
认证系统AI API 鉴权,确保只有登录用户才能调用
多租户对话/知识库按租户隔离
RBAC 权限控制谁可以管理知识库、谁只能对话
订阅计费按套餐分配 AI 额度(token 配额)
审计日志记录 AI 操作(谁在什么时候问了什么)
文件存储知识库文档上传复用 S3 存储
Feature Flags灰度发布 AI 新功能
后台管理监控 AI 用量、成本、异常

这正是"SaaS 基座 + AI 能力"架构的优势——AI 不是一个独立系统,而是在已有基础设施上叠加的能力层。

4.2 权限模型扩展

在已有的 RBAC 基础上,新增 AI 相关权限:

权限说明owneradminmemberviewer
ai:chat使用 AI 对话
ai:knowledge:read查看知识库
ai:knowledge:write管理知识库(上传/删除文档)
ai:agent:use使用 Agent
ai:agent:manage创建/编辑 Agent
ai:usage:view查看用量统计

4.3 套餐与 AI 额度绑定

套餐月 Token 额度可用模型知识库数文档数/知识库
Free50K tokensgpt-4o-mini110
Pro1M tokensgpt-4o-mini, gpt-4o5100
Business10M tokens全部模型201,000
Enterprise自定义全部 + 私有部署无限无限

这种阶梯式的限制既能保证免费用户体验到 AI 能力(促进转化),又能控制成本(50K tokens 大约 $0.01,几乎可以忽略)。

4.4 配置管理

将 AI 相关配置集中管理,便于运维调整:

// lib/ai/config.ts
export const AI_CONFIG = {
  // 模型配置
  defaultModel: 'gpt-4o-mini',
  premiumModel: 'gpt-4o',
  embeddingModel: 'text-embedding-3-small',
 
  // RAG 配置
  chunkSize: 512,          // 分块大小(tokens)
  chunkOverlap: 50,        // 分块重叠
  retrieveTopK: 5,         // 检索返回数量
  similarityThreshold: 0.7, // 相似度阈值
 
  // 限制
  maxMessageLength: 10000, // 单条消息最大字符数
  maxHistoryMessages: 50,  // 上下文最多保留消息数
  maxToolCalls: 10,        // 单次对话最多工具调用次数
 
  // 成本(美元/1K tokens)
  costs: {
    'gpt-4o-mini': { input: 0.00015, output: 0.0006 },
    'gpt-4o':      { input: 0.0025,  output: 0.01 },
    'claude-3-5-sonnet': { input: 0.003, output: 0.015 },
    'text-embedding-3-small': { input: 0.00002, output: 0 },
  } as Record<string, { input: number; output: number }>,
}

4.5 安全考量

AI 功能引入了一些独特的安全风险:

风险说明缓解措施
Prompt 注入用户通过精心构造的输入覆盖 System PromptSystem Prompt 放在最前面 + 输入过滤
数据泄露模型可能泄露训练数据或其他租户的上下文严格租户隔离,不跨租户共享上下文
有害内容模型生成违规内容OpenAI Moderation API 过滤
成本攻击恶意用户大量调用消耗 tokenRate Limit + 配额限制
PII 泄露用户上传包含敏感信息的文档提示用户脱敏 + 可选 PII 检测
// lib/ai/safety.ts
import OpenAI from 'openai'
 
const openai = new OpenAI()
 
export async function moderateContent(text: string): Promise<{
  flagged: boolean
  categories: string[]
}> {
  const result = await openai.moderations.create({ input: text })
  const output = result.results[0]
 
  return {
    flagged: output.flagged,
    categories: Object.entries(output.categories)
      .filter(([, flagged]) => flagged)
      .map(([category]) => category),
  }
}

本章小结

  • 需求分析:AI 应用分三个层次(对话 → 知识增强 → Agent),从 L1 起步验证 PMF
  • 五个核心问题:用户是谁、输入是什么、输出是什么、上下文多大、延迟要求
  • 模型选型:GPT-4o-mini 做默认,GPT-4o / Claude Sonnet 做高质量,模型路由按任务动态选择
  • SDK 选型:Vercel AI SDK 的 Provider 模式统一多模型接口,切换成本为零
  • 向量数据库:pgvector 复用 PostgreSQL,百万级以下足够
  • 五层架构:客户端 → API → AI 编排 → 数据 → 基础设施,职责边界清晰
  • 数据模型:conversations / messages / knowledgeBases / documents / documentChunks / aiUsage 六张核心表
  • SaaS 融合:复用认证、多租户、RBAC、计费、审计、存储、Feature Flags
  • 套餐绑定:Free 50K tokens / Pro 1M / Business 10M,阶梯式 AI 额度
  • 安全:Prompt 注入防护 + 租户隔离 + 内容审核 + 速率限制 + 成本控制