☰
AI工程从零搭建:从Demo到可上线大模型应用的完整方法论
2026/9/28 13:40:02 网站建设 项目流程

先说结论:ai-engineering-from-scratch这个主题,拆开看就一句话——真正把一个AI能力做成能上线、能维护、能持续迭代的产品,靠的不只是模型调参和写提示词,而是一整套工程体系。这篇文章我把从零搭建这套体系的过程、选型逻辑、踩过的坑和沉淀下来的方法一次性写清楚。


1. AI工程到底在解决什么问题

1.1 一个AI Demo到可用产品之间差了什么

很多团队第一次做AI应用时,都会经历一个典型路径:先用现成的大模型API调通了一个Demo,回答效果还不错,大家都很兴奋。但Demo和产品之间隔着一条很深的沟:瓶颈不在“模型行不行”,而在“怎么让模型稳定地运行在真实业务里”。

我举一个实际场景。你写了一个基于大模型的文档问答助手,在测试环境里用精心挑选的几份文档做验证,效果拉满。但上线后用户丢进来一堆格式五花八门的PDF和扫描件,问题从“帮我总结这段”变成“这个合同第几条说的是违约金”,模型开始答非所问、上下文混乱、漏看关键信息。这时你才发现,真正要解决的不是Prompt写得不够好,而是从文件解析、文本切片、向量化、检索召回到大模型调用的全链路质量问题。

这就是AI工程的核心:把模型能力嵌入业务流程后,用工程手段保证它在不确定性中尽量稳定。它不同于单纯的算法研发,也不同于传统后端开发,而是两者的交集,需要同时处理概率系统的不可控和分布式系统的复杂度。

1.2 AI工程的全链路关键环节

我习惯把一套完整的AI工程链路拆成六个环节,每个环节都有独立的难点:

  1. 模型接入层:选择API调用还是私有化部署,决定成本、延迟和可控性。
  2. 数据处理层:从原始文档到可被检索的知识库,涉及清洗、解析、切片、向量化。
  3. 检索增强层:从向量数据库召回相关内容,涉及召回精度、重排策略和上下文组装。
  4. 应用编排层:把检索结果、Prompt模板、工具调用组合成最终模型输入,涉及提示词管理和状态设计。
  5. 评估观测层:离线评估集和线上监控指标,回答好不好不能靠感觉。
  6. 部署迭代层:灰度发布、版本回滚、成本优化和持续收集反馈。

这六个环节任何一个拖后腿,整个系统的体验就会被拉低。国内很多团队前两个环节做得不够扎实,以为换个更强的模型就能解决,结果只是把问题转移到了别处。下面我按实际落地顺序逐层展开。


2. 从零搭建:基础设施与技术栈选型

2.1 模型接入:直接调API还是自己部署

模型接入是第一道选择题,也是贯穿始终的成本和性能决策。直接调云端API的优势是起步快、无需考虑GPU运维,劣势是数据出域风险和长线成本不可控;私有化部署则反过来,前期投入大但单次调用成本会随规模下降。

我的建议是按“业务敏感度和调用量”两条轴来判断:

  • 数据敏感度高、调用量大:优先私有化部署开源模型,比如在国产卡或消费级GPU上跑7B~14B量级的模型,配合量化技术把单卡推理做到可用。
  • 数据敏感度一般、需要最强推理能力:直接调商业API,把精力省下来做业务流程。
  • 中间状态:用API做前期验证,验证清楚业务Pattern后再评估是否迁移到私有化方案。

这里有一个容易忽略的点:模型接入不是写完代码就结束,要在一开始就设计好模型网关,统一管理不同提供方的API格式拆分、超时控制、重试策略和成本统计。我见过不止一个团队,上线三个月后想换模型供应商,结果发现所有调用代码散落在各个服务里,改起来伤筋动骨。

2.2 向量数据库、缓存和检索组件的选型逻辑

知识库场景里,向量数据库几乎是标配,但选型时要避免“性能参数崇拜”。不同向量数据库在千万级数据下的ANN检索性能差距确实存在,但对大多数中小业务来说,几十万到几百万条向量量级下,选型更重要的是:是否支持Metadata过滤、是否方便增量更新、运维成本多高、能否和现有存储统一。

我实际用过几种方案,做一个主观但真实的对比如下:

方案适合场景注意点
PostgreSQL + pgvector数据量不大、已有PG依赖事务一致性天然具备,但大规模向量检索需要调参
Milvus数据量大、高并发检索组件多,运维复杂度较高,适合有专门运维的团队
Qdrant中等规模、需要快速上手Rust编写,性能好,过滤功能灵活
Elasticsearch + 向量插件已有ES体系、需要全文检索和向量混合混检方便,但内存开销较大

缓存也要提前规划。同样的用户问题在短时间内重复查询是很常见的行为,尤其在企业内部工具中,几十个人对同一批制度文档反复提问。加一层语义缓存或者直接按问句哈希缓存,能把响应延迟从秒级压到毫秒级,同时大幅降低模型调用费用。

2.3 最小工程骨架代码参考

下面这个骨架不是完整生产实现,但覆盖了工程化接入需要的基本要素:超时、重试、缓存、简单的调用统计。我建议每个团队初期至少做到这个水平再往上加功能:

import hashlib import time from dataclasses import dataclass from typing import Optional import redis import requests @dataclass class LLMClient: api_base: str api_key: str model: str cache_ttl: int = 600 timeout: int = 30 max_retries: int = 3 def __post_init__(self): self.redis = redis.Redis.from_url("redis://localhost:6379/0") def _cache_key(self, prompt: str) -> str: return f"llm:{self.model}:{hashlib.md5(prompt.encode()).hexdigest()}" def _call_once(self, prompt: str) -> str: resp = requests.post( f"{self.api_base}/v1/chat/completions", headers={"Authorization": f"Bearer {self.api_key}"}, json={"model": self.model, "messages": [{"role": "user", "content": prompt}]}, timeout=self.timeout, ) resp.raise_for_status() return resp.json()["choices"][0]["message"]["content"] def chat(self, prompt: str) -> str: cache_key = self._cache_key(prompt) cached = self.redis.get(cache_key) if cached: return cached.decode() last_exc = None for attempt in range(self.max_retries): try: result = self._call_once(prompt) self.redis.set(cache_key, result, ex=self.cache_ttl) return result except Exception as exc: last_exc = exc time.sleep(2 ** attempt) raise last_exc

这段代码里有两个值得说的细节。第一,重试策略用指数退避而不是固定间隔,避免瞬时故障时所有请求同时打爆服务。第二,缓存必须区分场景,像“总结这份对话记录”这种个性化请求就不适合直接哈希缓存,应该按业务语义设计缓存键。


3. 数据工程与知识库:AI应用的地基

3.1 文档解析与清洗的隐藏工作量

数据上,最常见的认知偏差是“把数据喂进去就行”。实际情况是,企业内部的文档数据远比想象中脏:扫描版PDF没有文字层、有页眉页脚和页码干扰、表格被拆成错乱的文本、Word里嵌的图片里的信息全部丢失。做AI工程有一半时间在“洗数据”,这不是夸张。

一个实战经验:根据文档类型分成不同的预处理管线,不要试图用一套流程处理所有文件。

  • 文本型PDF:用PDF解析库提取文字,重点是版面还原。
  • 扫描件/图片型PDF:必须走OCR管线,中文老文档推荐偏专业的OCR引擎,通用大模型OCR虽然能力提升快,但批量处理时成本和延迟都比较高。
  • Word/Markdown:保留标题层级信息,这对后续切片有巨大价值。
  • Excel/CSV:每行转成自然语言描述比较麻烦,容易被低估,但表格问答场景下,结构化数据转文本的规则决定了检索质量。

清洗环节要时刻有“模型是纯文本消费者”的意识。模型不读二进制,不读PDF里的图表,只读你转换出来的纯文本。所以转换质量就是系统质量的上限。

3.2 文本分块策略:块大小、重叠与分隔符

分块是知识库检索中最容易被忽略却又影响最大的参数。切太大,召回结果可能涵盖太多无关内容,既稀释注意力又浪费Token;切太小,语义完整性被破坏,单块信息量不足以回答复杂问题。

我建议从下面这组初始参数开始调:

  • 块大小(chunk size):300~500个汉字左右,中英文混合时按Tokenizer计数更准确。
  • 重叠(overlap):块大小的10%~20%,用来缓解“好死不死切断关键句”的问题。
  • 优先按标题/段落分隔:Markdown标题、PDF章节、换行符都算语义边界,优先在这些位置切,而不是硬按字符数切。
  • 保留元数据:每块都带上文档ID、来源页码、章节路径,便于溯源和后续过滤。

分块为什么不能照抄最佳实践?因为知识库内容差异太大。给制度文档做知识库和给研发文档做知识库,理想的块大小可能差一倍。我是通过建立一个小型标注集,人工标记“哪些检索结果对回答是有用的”,然后用不同分块参数跑一遍看召回效果来确定参数的。这个工作听起来很笨,但比拍脑袋选参扎实得多。

3.3 Embedding模型与检索优化

Embedding模型的选择,影响的是“语义相关”的判定基准。中文场景下,通用模型能力已经很能打,但如果你的知识库有大量专业术语,建议在几个开源Embedding模型之间做一下对比测试,用你真实的数据算召回率,不要只看公开榜单。

检索优化方面,几个立竿见影的手段如下:

  1. Metadata过滤优先于向量检索:先在业务维度上缩小范围,比如“只查2024年合同”“只要产品手册”,再对小块做向量检索,比全局检索更准、也更快。
  2. Hybrid Search:把向量检索和关键词检索结合,解决纯向量方案对专有名词、英文缩写不敏感的问题。
  3. 召回后重排:先用低成本方式召回Top 20,再用重排模型或规则过滤到Top 5。这个环节对最终效果提升很明显。
  4. 上下文精炼:不要把整段原始文本都塞进Prompt,用大模型做一次“提炼关键信息”的中间步骤,能有效减少幻觉。

有一类错误是“为了RAG而RAG”——问题根本不需要检索外部知识,系统却非要从知识库里硬拉一堆段落出来,反而污染了模型输出。所以应用层还需要一个判断逻辑:什么情况下走检索、什么情况下直接生成。


4. 应用编排层的工程化细节

4.1 提示词管理不能只靠聊天记录

我见过太多团队的Prompt像“个人笔记”,散落在Jupyter Notebook、微信聊天记录和某次会议的白板里。一旦模型升级或换了供应商,同样的Prompt效果可能大幅波动,这时如果没有版本管理,排查问题会非常痛苦。

建议把Prompt当成代码来管理:

  • 每个模板有独立文件,内部变量用占位符表示,禁止拼接字符串写死在业务代码里。
  • 提交到Git仓库,每次调整都走Code Review。
  • 重要Prompt要有备注,记录当时调优的背景和预期效果。
  • 线上运行版本和测试版本分开,方便做A/B对比。

小技巧:Prompt模板的变更往往比代码变更更频繁,而且每次变更都直接影响回答质量,所以日志里一定要记录“这一次请求用的是哪个版本的Prompt”。

4.2 对话状态、缓存、并发与超时

AI应用一旦进入生产环境,工程化细节就开始占据主导地位。对话服务不仅要处理“单次问答”,还要管理多轮上下文,这里最容易出的问题是上下文无限膨胀——每轮都把历史消息全部塞给模型,很快超出上下文窗口。

我常用的方案是维护一个“可裁剪的滑动窗口”:

def build_messages(history, current_query, max_tokens=4000): messages = [{"role": "system", "content": SYSTEM_PROMPT}] budget = max_tokens for item in reversed(history): text = item["content"] tokens = estimate_tokens(text) if budget - tokens < 0: break messages.insert(1, item) budget -= tokens messages.append({"role": "user", "content": current_query}) return messages

从后往前累加,先保住离当前最近的信息,旧内容超预算就丢弃。这里有个更进阶的做法:对历史消息做摘要,而不是简单截断,比如每三轮对话生成一条浓缩摘要作为长期记忆。缺点是会引入额外模型调用,所以实现时要评估成本收益。

并发和超时也要同时考虑。大模型推理是慢操作,一个请求动辄几秒,如果服务是同步处理,几个用户就能拖垮线程池。生产上要引入异步队列,把请求和响应解耦,用户先拿到“任务已受理”,结果生成后通过轮询或回调获取。同时为不同类型的请求设置差异化超时,简单分类闲聊给短超时,复杂分析任务给长超时。

4.3 安全护栏与输入输出过滤

AI应用的安全不是“有防火墙就够”,而是要在应用层加两道护栏。输入侧,对用户提交的内容做限制,避免注入类提示词尝试改变系统设定;输出侧,拦截不合规内容,同时过滤模型可能输出的内部提示词或无关格式。

具体做法举例:

  • 输入长度限制,超出后前置截断并提示用户精简。
  • 关键词+分类模型双通道检测敏感内容。
  • 输出内容经过规则过滤后才返回前端。
  • 所有涉及个人信息的问题,默认走“脱敏后回答”。

我强调一下:护栏设计要早做。系统上线后再补拦截,往往只能挡部分链路,而且容易误伤正常业务。更推荐把护栏作为应用编排层的一个独立网关组件,所有模型请求和响应都统一经过它。


5. 评估体系与可观测性:别让回答质量靠“感觉”

5.1 离线评估集怎么设计与维护

很多团队对AI应用的评价停留在“看起来还可以”,但无法回答两个问题:这个版本比上个版本好多少?换模型后效果是提升还是下降?没有评估体系,所有优化都是盲人摸象。

我建议从第一天就建立一个小而有效的评估集,不必一上来就追求大规模。起步可以就准备四类样本:

  1. 标准问答:从真实业务中挑选50~100个高频问题,附带标准答案要点。
  2. 边界Case:检索不到相关内容、问题超出知识库范围、模糊提问等场景。
  3. 对抗样本:故意绕开规则、诱导模型输出不该有的内容的提问。
  4. 多轮对话:覆盖状态切换、指代消解和追问场景。

评估方式可以先用LLM-as-Judge自动化打分,再用人工抽检校准。这里提醒一句:让模型给自己打分天然有偏好,所以Judge提示词的设计要明确打分维度和参考标准,而且要记录评分一致性,太低时说明评估设计本身有问题。

5.2 线上监控指标:除了延迟,还要关心“无效回答率”

线上环境不能只监控CPU、内存和接口延迟。AI应用特有的指标包括:Token消耗、缓存命中率、检索召回数量、重排后采纳比例、空回答率、超时重试率、用户反馈点击率。

在这些指标里,我认为“无效回答率”比平均延迟更重要。一个回答如果用户根本不采纳,延迟再低也没意义。在实际业务里,可以通过前端加反馈按钮、会话中检测用户是否重复提问、回答后是否立即离开页面等方式来近似估算。

监控数据不仅用来发现故障,更是产品迭代的输入。我在团队里一直强调:模型升级、Prompt调整、检索参数变更都必须有对应的指标对比,哪怕简单到就是一张Excel记录,也比“凭感觉说变好了”有价值。

5.3 日志记录的正确姿势

日志是所有可观测性的基础。AI应用的日志和传统后端日志有几点不同:

  • 必须记录完整的Prompt和模型输出,否则排障时无从查起。
  • 必须携带版本标识,包括模型版本、Prompt模板版本、向量库版本。
  • 必须记录检索到的文档块ID,不然用户说“你答错了”,你都不知道它看了什么资料。
  • 日志要脱敏,用户上传的文档内容可能包含敏感信息。

我有一个印象很深的排障经历:用户反馈某类问题回答质量下降,查日志发现不是Prompt变了,而是上游数据同步任务某天开始把错误格式的文档写进了知识库,所有新增Block都是乱码。如果日志里没有检索来源ID,这个问题可能要好几天才能定位。


6. 部署上线与持续迭代

6.1 从单体应用到可灰度发布

AI应用上线初期可以用单体架构,但不必为了“架构先进”过早拆分微服务。我更推荐的路径是:先让服务整体跑起来,再按“变更频率”来决定拆分边界。

在实际生产中,知识库更新频率通常远高于模型调用服务,如果两者耦合在一个服务里,每次向量库更新都要重新发版。把数据处理和在线推理拆成两个独立服务,是性价比最高的第一步。在线推理服务内部,则可以按“调用链版本化”的思路来做灰度:让一部分流量走新Prompt或新模型,另一部分还是旧逻辑,对比指标后全量切换。

6.2 Token成本优化的几个实用手段

大模型应用的成本大头就是Token费用,这块不能只在月底看账单才心疼。事中控制才是关键:

  • 缓存策略分级:高频重复问题用语义缓存,对应的高频Token直接省掉。
  • 模型分级调度:简单问题走小模型,复杂推理才走大模型。我见过不少团队不管什么问题都统一调最强的模型,成本高效果也没更好。
  • 输出Token限制:把max_tokens按场景限死,有些场景其实100字就能回答完整,却默认生成500字。
  • 知识库回答优先引用原文:让模型在检索到的内容里提炼,而不是自由发挥,既减少幻觉又能控制输出长度。

给一个可落地的预算思路:把月Token预算拆到日均,再估算每个请求的平均Token开销,反推出“每天能支撑多少次问答”,这个数字要同步给业务方,避免上线后超预算措手不及。

6.3 持续迭代闭环:反馈、分析、调整三步走

上线只是开始。我把迭代节奏总结成“一个闭环、每周一轮”:

  1. 收集:从日志和用户反馈里提取“回答不佳”的案例。
  2. 分析:定位是检索没召回、Prompt指令不清晰还是模型本身能力不足。
  3. 调整:针对原因做定向修改,记录改动内容并触发评估集回归。

有个重要经验是:不要每件事都归因到模型。排查顺序应该是“数据有没有问题 → 检索有没有召回 → Prompt是否给了正确的任务定义 → 最后才怀疑模型能力”。现实中大部分Case都出在前两层,很多人却直接跳到换模型。


7. 踩坑实录与实战心得

7.1 最容易被低估的三个问题

第一个是数据更新的一致性问题。知识库里旧版本数据和新版本数据混在一起,用户提问时召回结果不稳定,今天答A明天答B。工程上要引入数据版本和生效时间,确保同一次会话内上下文一致。

第二个是长文档处理。50页以上的文档直接切片后,每块信息密度极低,检索效果显著变差。我后来改成分层检索:先做全文摘要和信息索引,再做二次定位阅读,效果改善非常明显。

第三个是“测试集与线上分布不一致”。团队精心构造的测试集永远比真实用户提问“收敛”得多,于是离线评估高分,线上体验翻车。解决方法是需要持续把线上真实低分Case补充进评估集,保持评估集的动态更新。

7.2 我沉淀下来的几条工程守则

做了几个AI项目之后,我总结出几条很朴素的守则,分享给你作为参考:

  • 先跑通最小闭环,再增加复杂度。第一个版本不追求完美,但指标采集必须到位。
  • 所有变更都可追溯。Prompt、模型、数据、参数,每个环节都要有版本意识。
  • 能离线解决的问题,不要推到线上。数据清洗、分块策略、评估评分,都是离线完成的,反复在线上试错成本很高。
  • 把模型当组件,而不是当银弹。模型的角色是“给定输入生成合理输出”,业务逻辑仍然要靠代码来约束和校验。

7.3 最后再分享一个小技巧

我现在做AI工程有个习惯:每次调优只改一个变量。

比如这周只调分块大小、下周只换Embedding模型,其他全部保持不变,然后看评估集和监控指标的变化。同时修改多个变量会让结果无法归因,最后连怎么优化的都不知道。保持单变量实验,记录每个实验的结论,积累三个月,你会拥有一套“属于自己业务场景的最佳实践文档”,这比任何公开的通用教程都有价值。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询