Demo 3天写完,上线花了3周——AI工程化的安全/成本/可观测性/部署,每条都是真实踩坑经验

56 阅读14分钟

AI 工程化实践与 PrismAI 全景

第一个 AI 应用 3 天写完——RAG 对话机器人,Demo 完美。准备上线那天,安全同事一句话把我问住了:"Prompt 注入防了吗?用户输入'忽略之前所有指令'怎么办?"然后是财务——"你一个复杂问题的 Token 消耗是简单问题的 10 倍,成本算过吗?"然后是运维——"LLM 调用链路上日志监控告警接了吗?"从 Demo 到上线,不是加几个配置——是工程化改造。又花了整整 3 周。

阅读约 22 分钟 | 系列第 9/14 篇(文本AI线收束篇)


⚠️ 时效性提示:本文基于 2026 年 7 月的技术状态撰写。大模型版本迭代迅速(通常 3-6 个月一次大版本更新),文中涉及的模型名称、API 端点及性能基准数据请以各厂商最新公告为准。建议重点关注文章中的架构原理与设计决策——这些内容具有更长的时效性。

一、内容安全纵深防御

AI 应用的内容安全不只是 Prompt Injection 防护,而是输入→模型→输出的三层纵深防御。

第一层:输入安全(用户给 LLM 的内容)

  • 敏感词过滤:本地部署敏感词库,用户输入命中则拦截(< 5ms 延迟)
  • 文件内容扫描:上传的 PDF/Word 先过文本审核再入知识库
  • 频率限制:单用户每分钟最多 N 次请求,防滥用

第二层:Prompt 约束(模型的行为边界)

  • 系统提示词中写入内容边界:"你只能回答考试相关内容,拒绝政治/违法/色情/暴力问题"
  • 注意:Prompt 约束定义的是正当行为底线,不是防注入的手段(注入会覆盖系统指令)

第三层:输出安全(LLM 给用户的内容)

  • 规则校验:正则 + 关键词黑名单检测输出
  • 可疑标记:触发规则 → 标记"仅个人可见" + 后台记录待审查
  • 人工审核:AI 自动生成的题库题目需管理员审核后方可进入公共题库

PrismAI 安全架构

用户输入 → 敏感词过滤 → 合规 → Agent 处理
                       → 不合规 → "输入包含不当内容,请修改后重试"

LLM 输出 → 规则校验 → 合规 → 返回用户
                    → 可疑 → 标记+仅个人可见+后台待审查

Prompt Injection 四道防线(金融系统特别重要):

  1. 输入校验:用户输入只作为"数据"处理,用参数化方式拼接,绝不让用户输入成为"指令"
  2. 角色隔离:System Prompt(不可变)和 User Message(标签化)物理隔离
  3. 输出过滤:AI 回复在返回前二次检查——正则匹配敏感词、长度异常检测
  4. 最小权限:AI 数据库连接用只读账号、代码沙箱 --network none --read-only

二、成本控制策略

PrismAI 采用 BYOK(用户自带 Key)模式,平台不承担模型成本。但用户体验的核心是"帮我省钱"。

成本控制层次

第一层:模型选型(最大省钱点)
  DeepSeek(¥1/百万 token) vs GPT-5(¥70/百万 token)= 70 倍差距
  日常 CRUD  DeepSeek,复杂推理  Claude

第二层:语义缓存
  高频问题相似度 > 0.95  直接返回缓存  不走 LLM

第三层:上下文控制
  对话超过 20 轮自动摘要压缩  减少每次请求的 token 

第四层:限制兜底
  ReAct 最多 10 步、追问最多 5 层、上下文最多 8000 Token
  防止 Agent 无限循环消耗 Token

Token 消耗的构成与计算

理解一次对话的费用,需要知道 Token 消耗的构成:

单次 API 调用的 Token 构成
═══════════════════════════

① System Prompt(每次请求固定携带)
   典型大小:500-2000 Token(工具越多越大)

② 对话历史(Message History)
   典型大小:第 1~200 Token,第 20 轮可能累积至 ~8000 Token

③ 当前用户消息(User Message)
   典型大小:20-500 Token

④ 工具调用往返(Tool Call Round)
   一次 Agent 任务通常有 2-6 轮工具调用

⑤ 模型最终回复(Completion)
   典型大小:200-2000 Token

费用计算公式

一次 Agent 任务的总费用 =

   输入部分:
   (System Prompt + 历史消息 + 用户消息 + 工具结果) × 输入单价
   + 输出部分:
   (各轮 tool_call + 最终回复) × 输出单价(通常为输入的 2-4 倍)

真实案例(PrismAI 智能问答)

典型一次出题任务(3 轮工具调用):
  System Prompt:   1,500 Token
  对话历史:        3,000 Token10 轮历史)
  工具结果:        2,000 Token3RAG 检索返回)
  最终回复:          800 Token5 道题的 JSON)

  总输入:  6,500 Token × 1 元/百万   = 0.0065 元
  总输出:    800 Token × 2 元/百万   = 0.0016 元
  ─────────────────────────────────────────
  单次任务总费用(DeepSeek):约 0.008 元

  第 20 轮费用:约 0.013 元(比第 1 轮增加 63%,原因是历史累积)

成本优化三原则

  1. 压缩 System Prompt:工具描述精简,每次可省 500-1000 Token
  2. 管理对话历史:超过 20 轮自动摘要压缩
  3. 精简工具返回:RAG 检索返回 Top-5 而非 Top-20

三、AI 应用可观测性

AI 应用的可观测性和传统后端不同——不是 CPU/内存/QPS,而是 AI 特有的指标:

指标类别具体指标为什么重要
成本Token 消耗(输入/输出/总计),按用户+方向汇总用户自带的 Key,要知道钱花在哪了
延迟首 Token 延迟(用户感知最强)、工具调用耗时等 2 秒 vs 等 10 秒出第一个字的体验天差地别
质量检索命中率、平均相似度、AI 搜索触发频率命中率持续下降 = 知识库需要更新
可用性LLM API 错误率、Embedding 失败率、沙箱执行失败率降级策略触发频率过高 → 需要扩容或优化
用户反馈点赞率、点踩率、追问接受率点踩率突增 → prompt 或检索策略可能出问题了

PrismAI 的 Token 追踪设计:90 天明细保留(tb_token_usage_log),按日汇总归档(tb_token_daily_summary)。可以做趋势分析——"这周的 Token 消耗为什么比上周多了 30%?"


四、模型部署与量化

本地 vs 生产,技术栈完全不同

本地开发(Ollama):
  ollama pull deepseek-r1:7b  → 一行命令搞定
  自动下载 Q4_K_M 量化版本,CPU 友好
  适合:验证、测试、数据不出域的场景

生产部署(vLLM / TensorRT-LLM):
  vLLM:PagedAttention 显存优化 + Continuous Batching 吞吐提升 10-20 倍
  vllm serve Qwen/Qwen2.5-7B-Instruct → 一条命令启动 OpenAI 兼容 API
  适合:上线服务、高并发

量化——把模型从 CD 压成 MP3

  • FP16 → INT4,体积缩 4 倍,速度提 2-4 倍,效果几乎无损
  • 三种格式:GGUF(CPU 推理/Ollama)、GPTQ(GPU/需校准数据)、AWQ(GPU/精度优于 GPTQ)
  • Q4_K_M:4-bit,质量与速度的最佳平衡点(Ollama 默认)

五、微调入门

虽然大部分场景 RAG 就够了,但了解微调有助于理解模型能力边界。

三种方式,成本从高到低

方式成本原理适用
全量微调最高(7B 需 56GB+)更新全部参数大公司专属模型
LoRA中(7B 需 8GB)在旁路训练小参数包当前主流
QLoRA低(7B 需 4GB)先量化再 LoRA消费级显卡可跑

DPO(直接偏好优化)——当前开源模型的标准对齐方法:

  • 传统 RLHF 需要三步:训奖励模型→评分→PPO 强化学习(复杂+不稳定)
  • DPO 直接用好/坏回答对优化,数学上等价 RLHF 但简单得多

微调数据准备——最容易被忽视的环节:

  • 数据格式:{"instruction":"任务","input":"用户输入","output":"期望输出"}
  • LoRA 几百到几千条高质量样本就有效果
  • 数据质量 >> 数据数量:100 条精心标注 > 1000 条自动生成
  • 多样性:覆盖正常/边界/异常场景

选型判断:RAG 优先验证价值 → 确实需要固化输出风格/格式 → LoRA/QLoRA 微调。大部分场景 RAG + Prompt 工程已经够用。


六、实战全景:PrismAI 平台架构

本章将前面五章的知识点串联到一个完整的项目中。PrismAI 源码见 Gitee

项目状态声明:PrismAI 一期,采用渐进式开发策略。下表对照文章涉及的功能与代码仓库实际状态:

功能文章位置代码状态说明
多模型集成(DeepSeek/GLM/Qwen)+ BYOK§1-2✅ 已实现ChatClient 多模型路由
Prompt 模板化管理(DB 存储 + 变量占位符)§1✅ 已实现tb_prompt 表 + 运行时替换
JWT 认证 + AES-256 API Key 加密§1✅ 已实现每次加密随机 16 字节 IV
SSE 流式 + ThinkingEvent 可见§1,§3✅ 已实现ReActContext.thinkingCallback
ReAct Agent 循环 + Tool Calling§3✅ 已实现ReActLoop + 3 个工具
RAG 文档摄取管道(解析/切分/向量化)§2✅ 已实现DocumentParser + TextChunker + EmbeddingService
RAG 向量检索 + 混合检索§2🟡 二期V1 仅 SQL LIKE 关键词匹配,向量存了但未搜索
语义缓存(相似度 > 0.95 直接返回)§2❌ 二期架构已预留,代码未开始
代码执行沙箱(Docker 隔离)§1❌ 二期架构设计中,完全未开始
AI 搜索兜底(知识库不足时自动外搜)§2❌ 二期降级链中已定义,代码未实现
可观测性(Token 追踪/错误率/延迟)§3🟡 部分基础日志有,结构化指标采集未做

为什么不等到全部做完再发文章? 工程实践类文章的价值在于展示真实的开发过程——哪些决策是对的、哪些需要返工、一期为什么选择某些简化方案。一个"全部做完"的项目反而无法展示这些工程权衡。读者看的不只是代码,更是设计的推演过程。详见 PrismAI 需求规格说明书

6.1 项目架构全景

业务定位:AI 驱动的多方向考试/面试辅导平台。用户自带 API Key(DeepSeek/GLM/通义千问),选择备考方向(Java 求职/会计中级/架构师),平台通过 Agent + RAG 提供智能问答、模拟考试和简历评估。

技术栈

技术备注
后端框架Spring Boot 4.1.0 + Spring MVCJava 17
AI 框架Spring AI 2.0(ChatClient + ToolCallback)而非 LangChain4j
数据库MySQL 8.0(业务) + 内存 ConcurrentHashMap(向量)V1 无外部向量数据库
前端Vue 3 + TypeScript + Element Plus + Vite 6Composition API
安全JWT + AES-256-CBC(API Key 加密)每次加密随机 16 字节 IV
实时通信SSE(LLM 流式) + WebSocket(代码沙箱输出,二期)

Maven 模块结构

mindforge (parent POM)
├── mindforge-common/      共享 DTO/枚举/常量/异常
├── mindforge-repository/  JPA Entity + Repository(MySQL)
├── mindforge-security/    JWT + AES 加密 + Spring Security 配置
├── mindforge-rag/         Embedding 向量化 + 内存向量索引
├── mindforge-agent/       AgentTool 接口 + ReActLoop + 工具实现
├── mindforge-service/     业务逻辑编排(调用 Agent + RAG)
├── mindforge-web/         REST Controller + Spring Boot 入口
└── mindforge-frontend/    Vue 3 前端(Vite)

依赖方向:web → service → {repository, agent, security} → common

6.2 关键设计决策复盘

以下是 PrismAI 开发过程中的关键设计决策,体现了 AI 应用开发中的典型工程权衡:

决策 1:Spring AI 2.0 而非 LangChain4j

需求文档最初写的是 LangChain4j,实际选的是 Spring AI。原因:

  • Spring AI 2.0 的 ChatClient API 与 Spring Boot 生态深度集成
  • ToolCallback 机制比 LangChain4j 的 @Tool 注解更直接地映射 Agent 工具
  • 减少跨生态依赖降低维护成本

决策 2:内存向量索引而非 Milvus

V1 选择了最简单的方案——ConcurrentHashMap + 启动时全量加载。这是刻意的工程决策:

  • V1 阶段文档量少(每个备考方向几百条 chunk),暴力搜索 O(n) 完全够用
  • 避免引入外部依赖(Milvus 需要 Docker 部署,增加运维复杂度)
  • 代码中明确标注了升级条件(>5000 条或 P99 > 200ms)和升级路径(Lucene HNSW)

决策 3:平台统一 Embedding 而非用户自带

公共知识库需要跨用户检索,如果各用各的 Embedding 模型 → 向量在不同语义空间 → 检索完全不可用。解决:平台统一使用 BGE 中文模型。

决策 4:Agent 模块独立,不反向依赖 Service

mindforge-agent 模块不依赖 mindforge-service——通过 extraToolParams 从外部接收运行时参数,ChatModel 由调用方通过 LlmChatRouter 获取后传入。经典的依赖倒置。

决策 5:BYOK 模式的前提和代价

  • 前提:用户有自己的 API Key
  • 好处:平台零模型成本、用户可选择便宜的模型
  • 代价:Key 安全存储(AES-256-CBC 加密+UI 脱敏)、过期验证、多 Key 管理、Embedding 策略必须独立于用户 Key

6.3 代码核心链路走读

链路 1:智能问答完整流程

用户在前端输入问题 → Vue 发起 SSE 连接(fetch + ReadableStream)

Controller 层(ExamController / ChatController):
  → 验证 JWT Token → 获取 userId
  → 获取当前备考方向 directionId
  → 获取用户 API Key → AesEncryptionService.decrypt()
  → 构造 ReActContext → 调用 ReActLoop.execute()

Agent 层(ReActLoop):
  → buildInitialMessages(): System Prompt(含工具列表)+ 历史 + 用户问题
  → buildToolCallbacks(): 6 个 AgentTool → 6 个 ToolCallback
  → ChatClient.prompt().messages().tools().call().chatResponse()
  → Spring AI 自动处理 LLM 的工具调用决策(多轮)
  → LLM 返回最终答案

SSE 推送:
  → ThinkingEvent 通过 thinkingCallback → SseEmitter.send()
  → 前端展示 "正在检索知识库..." / "正在搜索外部资料..."
  → 最终答案逐 token 流式返回

链路 2:API Key 加密存储

// AesEncryptionService.java — AES-256-CBC,每次加密随机 16 字节 IV
public String encrypt(String plaintext) {
    byte[] iv = new byte[16];
    new SecureRandom().nextBytes(iv);         // 每次随机 IV
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv));
    byte[] ciphertext = cipher.doFinal(plaintext.getBytes(UTF_8));
    // 存储格式: Base64(IV + ciphertext)
    byte[] combined = new byte[16 + ciphertext.length];
    System.arraycopy(iv, 0, combined, 0, 16);
    System.arraycopy(ciphertext, 0, combined, 16, ciphertext.length);
    return Base64.getEncoder().encodeToString(combined);
}

6.4 从需求到代码:一个 AI 应用的完整工程链路

PrismAI 不只是"一个 CRUD 项目加了 LLM 调用"——它涉及了 AI 应用开发的完整工程链路:

需求分析(6 个功能模块 + 5 个非功能需求)
  → 数据库设计(16 张表 + 50 个索引 + ER 图)
  → 接口设计(74 个 REST API + 14 种 SSE 事件类型 + WebSocket)
  → 前端设计(Vue 3 + 状态机驱动的考试流程 + 5 个 Pinia Store)
  → 部署设计(Nginx + systemd + Docker 沙箱(二期)+ .env 密钥管理)
  → 编码实现(7 个 Maven 模块 + Spring AI + Vue 3 + Element Plus)
  → AI 特有的工程关注:Prompt 模板管理、语义缓存(二期)、降级策略、Token 追踪、内容安全

核心要点回顾

  1. 内容安全 = 三层纵深防御(输入过滤 + Prompt 约束 + 输出校验),金融系统需加四道 Prompt Injection 防线
  2. Token 成本可精确计算——一次 PrismAI 出题任务约 0.008 元,长对话的历史累积是最大的隐性成本
  3. AI 应用的可观测性 ≠ 传统监控——首 Token 延迟、检索命中率、用户点赞率才是核心指标
  4. Ollama 本地开发 → vLLM 生产部署——同一模型,不同运行时,差距可达 10-20 倍吞吐
  5. PrismAI 的 5 个设计决策(Spring AI/LangChain4j、内存/Milvus、统一/自带 Embedding、Agent 独立/耦合、BYOK)是最有价值的部分——展示的不仅是"知道什么",更是"怎么选择"

AI 工程化的四座大山——安全、成本、可观测性、部署——不是"上线前检查一遍"的一次性工作,而是持续迭代的系统能力。从 Demo 到生产,这四件事缺一不可。收藏这张工程化全景 Checklist,下次 AI 项目要上线时逐项对照——每一条都是真实踩出来的坑。

点赞+收藏,文本 AI 线到此收束。下一篇进入多模态线——AI 图片生成完全指南。 上一篇:《Agent的本质:ReAct循环不到50行+FC vs MCP》 | 下一篇:《AI图片生成完全指南:扩散模型+DiT架构+FLUX/SD3.5实战》 系列合集掘金AI合集 配套代码PrismAI