
这两年“AI 工程”这四个字被反复提起但绝大多数讨论要么停留在“调 API 写提示词”的玩具层面要么一上来就是几十人团队的大厂基础设施。我见过太多人包括我自己早期在跑通一个 notebook 之后信心满满地准备上线结果被并发、成本、幻觉、评测这些问题锤得晕头转向。真正“从零开始”的 AI 工程不是把模型接进去就完事而是从需求拆解、技术选型、数据准备、检索增强、评测闭环到部署监控一套完整的方法论。这篇文章就围绕“ai-engineering-from-scratch”这条主线结合我落地过的一个企业知识库问答助手项目把每个阶段的核心决策、具体步骤和踩过的坑掰开揉碎讲一遍。适合那些已经能写出 Python、跑通过基础模型推理但还不清楚怎么把一个想法变成稳定可用 AI 产品的开发者。1. 项目整体设计与核心思路1.1 先想清楚你要做的到底是不是“AI工程”很多初学者把“AI 工程”等同于“训练模型”或者“炼丹”。但实际在工作中大部分场景不需要从零训练一个大模型成本不允许时间也不允许。真正的 AI 工程更像是一条“把模型能力包装成产品能力”的流水线。我个人对它的定义是在模型能力之上构建一套可靠、可控、可评估、可维护的系统性解决方案。举个例子。接到一个“做企业知识库问答”的需求新手第一反应可能是“用 ChatGPT 就行把问题丢给它”。但你真去问了会发现三个问题模型不知道你们公司的内部制度细则它可能一本正经地编造答案你不敢让它直接面对客户。这时候你要做的不是写提示词而是设计一个系统文档从哪里来、怎么切分、怎么检索、怎么让模型基于检索结果回答、怎么评测回答得好不好、上线以后怎么监控。所以我接到项目的第一件事永远是画能力图而不是选模型。把需求拆成“数据层、检索层、生成层、评测层、交互层”明确每一层要解决的业务问题。你只有把问题定义清楚了后面每一步选型才有据可依。1.2 从零开始的路径规划先搭骨架再填细节我的习惯是分五步推进整个项目这也是这篇文章的主线需求与边界梳理确定场景To B 还是 To C、用户群体、数据形态、可接受的失败成本。技术选型决定模型、框架、向量库、部署方式。核心链路搭建做 RAG检索增强生成的话就是数据索引 检索 生成。评测体系建立没有评测的 AI 项目就是在裸奔这是我最强调的一点。部署与迭代机制不是上线就结束而是要有日志、监控、回归测试的闭环。这条路走下来你会发现 AI 工程的核心难点根本不在“模型能力”而在“工程稳定性”和“质量可预期”。在后面的章节里我会把每一步的实操过程展开讲。2. 技术与工具选型解析2.1 模型选型不能只看榜单分数选模型这件事我踩过最大的坑就是“什么火用什么”。去年我做过一个项目当时某开源模型的榜单分数很高结果迁移到垂直领域之后输出格式不稳定到让人崩溃最后不得不紧急换回 API 方案。现在做 AI 工程模型通常就两条路调用成熟商业 API或本地部署开源模型。两者怎么选我给出一个从成本和控制力角度的判断框架维度商业 API本地部署开源模型起步成本低按量付费高需要 GPU 或较高内存数据隐私有风险数据出站可控性强数据内部流转技术能力要求较低高需要处理推理优化、并发等定制化受限可微调、可深度定制以我的知识库问答项目为例当时企业客户明确要求数据不能出域所以最后选了本地部署路线。模型主体用的开源对话模型大概 7B 到 14B 参数级别配合量化部署到内部服务器。如果你没有严格的隐私要求起步阶段直接用商业 API 是更务实的做法先把业务闭环验证了再说不要一上来就折腾显卡。2.2 框架与向量库别沉迷“全家桶”很多初学者喜欢把一个生态全家桶都学一遍什么 LangChain、LlamaIndex、向量数据库都铺一大堆。我的建议是按需引入能用标准库解决的绝不加框架。框架能帮你省事但也会在出问题时把错误变得非常隐蔽。具体到 RAG 链路上我对各环节的常用工具选择的经验如下数据切分直接用 Python 的文本处理 LangChain 的文本分割器只借用这个工具类不上整套链向量数据库根据部署规模常见选择包括开源的 Chroma适合原型、Elasticsearch 或 Milvus适合生产级多节点检索在线服务框架FastAPI 为主简单高效任务编排不强行用 LangChain Agent很多流程用 Python 原生函数 Type 枚举就能写清楚。你可能会问“不都用 LangChain 吗” 坦率说它抽象层级比较高出了问题排查难度大。我现在的做法是把 LangChain 拆开用只要它的切分器和一些工具类核心编排全部自己写。这样我清楚每一行代码在干什么出了问题我能快速定位。2.3 为什么强调“最小可行系统”先行“从零开始”最忌讳的是过度规划。你先别管什么高可用、多租户、分布式向量检索先花三天时间搭一个能跑通的最小系统从一份 PDF 里抽出文本切分灌进向量库检索以后把上下文拼给模型拿到回答。这一步的目的是把整个链路的“手感”建立起来。我的实际经历是第一天搭好最小系统第二天就发现了两个此前完全没意识到的业务问题一是业务方期望的是“直接给结论”而不是“给段落引用”二是数据的命名规范太乱导致检索时噪声非常大。如果你一上来就铺大架构这些东西全都会被淹没在系统复杂度里。最小可行系统是这个项目里我认为最值得坚持的思路。3. 核心链路搭建从零实现一个 RAG 问答系统3.1 数据准备与清洗检索效果的上限由这里决定很多文章会花大篇幅讲模型和向量库但实际项目中决定体验的往往是数据质量。我做知识库的过程中清洗和切分的时间占了整个项目周期的四成。第一步是文档解析与清洗。企业内部资料通常是 PDF、Word 或网页导出的 HTML。解析时有一堆坑PDF 里的表格会被拆乱扫描件需要 OCRWord 里的页眉页脚会混入正文。我踩过一次最惨的是把项目文档里的修订记录几百条历史变更都当成正文索引进去了导致检索“版本变更”相关问题的时候模型回答的永远是最旧的版本。所以清洗阶段至少要处理这些事去掉页眉、页脚、目录、修订记录识别表格并尽量转成 Markdown 表格格式大表格要单独拆块对扫描版 PDF 做 OCR并记录置信度太低的需要人工复核统一编码UTF-8处理特殊字符和全角半角混乱问题。清洗完以后我通常会写一段校验脚本统计文档数量、总字符数、清洗前后的内容 diff 片段至少要保证肉眼抽查几个段落没有脏文本。这一步偷懒后面全链路都会跟着遭殃因为检索和生成都在垃圾数据上做运算。第二步是切分策略。切分在 RAG 里是最容易被低估的环节。切得太短语义不完整切得太长检索噪声大而且超出模型上下文窗口后会被强行截断。我一般按照语义相关性优先于固定长度的原则来处理具体做法如下按 Markdown 标题层级、列表、段落等结构信息先把长文档粗切为“候选块”对每个候选块如果它的字符数超出预设上限比如 800 到 1000 字就进一步按句子边界、标点二次切分相邻小块之间加一段重叠区例如重叠 50 到 100 字防止关键内容被从中间切断。这样的切分相比“硬切 500 字”效果会好不少但同时我也承认这个阶段需要耐心调试。你可以把切分后的结果存成 JSON 预览人工抽查几个关键主题的块质量然后再调整参数。3.2 向量化与索引构建embedding 选型和批量入库数据清洗和切分完下一步是向量化。Embedding 选型的逻辑也是从零开始必须搞清楚的一环。常见的方案分两类商业 API 的 embedding和本地开源的 embedding 模型。从我的经验看如果数据隐私要求不敏感调用 OpenAI、Cohere 等成熟 API 的 embedding 服务通常会有更好的泛化效果和更低的集成成本。如果必须本地化可以考虑 BGE、M3E 这类开源中文 / 多语言模型用Sentence Transformers 框架加载即可。在做一个中文知识库项目时我对比过开源 embedding 和商业 API 的效果。当时我用了一个包含 200 条业务问题的评测集在 top-10 召回率上商业 API 大概比本地开源模型高出近 10 个百分点但在垂直领域术语上本地模型经过微调后也有可能反超。结论是起步先默认商业 API等业务稳定后再逐步优化或替换。向量化以后要解决的就是索引与存储。我在原型阶段用的是轻量级的 Chroma很简单。到了生产环境我更倾向用 Elasticsearch 的 dense_vector 功能因为团队对它的运维更熟而且可以同时做文本关键词检索和向量检索的混合召回。索引构建时要往每个文档 chunk 的元数据里写入来源路径、页码、业务类型、版本号。这样检索出结果以后你能顺着元数据追溯原始文件这是业务方最关心的“可解释性”问题。批量构建索引时我一般控制请求并发不超过 8 到 16 个线程避免写入端直接被打满同时定期做一次索引完整性校验统计 embedding 数量应该等于切块总数还要抽查几篇文档做向量相似度自查询确认不会查无结果。3.3 检索策略从“相似度”到“可用的上下文”检索是整个 RAG 里的核心操作也是最容易糊弄的部分。很多教程就写一行“similarity_search(query)”但真实场景远没那么简单。我的标准做法是走多路召回 重排的路线具体如下向量检索把用户 query 编码成向量用余弦相似度取 top-k我的常见取值为 20 到 50 条关键词检索可选基于 BM25 或者 Elasticsearch 的 match query 做一遍文本检索解决向量检索对精确专有名词不敏感的问题合并与去重把两路结果合并按文档来源和语义去重重排序Rerank用一个专门的 Rerank 模型或者 LLM 对合并后的 top 候选重排选最优的 3 到 5 条作为最终上下文。这个过程听起来复杂但实际落到代码上并不难。给一个极简示例用 Python 描述思路def hybrid_search(query, top_k20): query_vec embed(query) vector_hits vec_db.search(query_vec, ktop_k) keyword_hits es.search(indexknowledge, query{match: {content: query}}, sizetop_k) merged merge_and_deduplicate(vector_hits, keyword_hits, bycontent_hash) reranked rerank_model.rerank(query, merged)[:5] return reranked重排模型推荐使用专门优化的 rerank 服务或小型交叉编码器模型它会对每个“query-段落”对综合打分效果通常比单纯向量相似度好很多。有人可能觉得这一步多余但我实测下来加了重排之后最终答案的可信度提升非常明显尤其是在多个候选文档语义高度相似的时候。这里还要特别强调query 预处理用户的问题往往很口语化比如“咱们的年假到底怎么休”。直接拿去检索可能命中“休假管理制度”里的某个段落但不够精准。我一般会先做关键词提取或者让模型对用户语义进行修正比如补全缺失的主语、转成规范问句。这一步叫 query rewrite在链路里的收益很大比较推荐自己写一个规则优先、模型兜底的 relay 层。3.4 生成环节提示词结构、上下文组装与输出约束检索到的内容并不是直接一股脑塞给模型就完事。上下文组装的质量直接影响回答的准确度和格式稳定性。我设计的提示词结构通常包含四个模块角色与任务说明告诉模型“你是企业知识库助手必须基于提供的资料回答资料不足以答复时要明确说无法回答”检索到的资料片段用明确的引用标记比如[1],[2],[3]并附来源说明用户的原始问题输出要求如“控制在 200 字以内”“必须给出以下几点不要编造数据”“不要提及‘根据内部资料’之类的字样”。给一个结构化的 Prompt 示例你是一名企业知识库问答助手。请严格基于以下资料回答用户问题。若资料中没有相关信息直接说明“资料库中未找到相关信息”不要编造。 资料 [1] 来源员工手册_第5章.docx内容年假天数根据累计工作年限计算... [2] 来源休假管理制度_2024.pdf内容当年未休完的年假可顺延至次年3月底... 用户问题我们公司的年假到底怎么休 要求 1. 回答简洁重点突出 2. 引用资料片段时用 [1] 标注来源 3. 若信息不足明确说明不要猜测。组装好 Prompt 之后还有一个非常容易被忽略的点模型输出的结构化约束。如果业务方需要的是 JSON 格式输出别靠“请输出 JSON”这种提示词而是采用以下组合方案设置 response_format 或 function calling输出侧做 JSON 解析解析失败就重试一次再失败则抛出兜底回答。在工程上这叫“输出护栏”实际项目中非常实用。3.5 上下文窗口与 Token 预算的计算有了上面这些检索和组装逻辑还要考虑一个硬约束模型上下文窗口。你检索到的 top 5 个片段假设每个 800 字算上中文提示词可能已经超过 4000 字了。如果用 7B 模型比如上下文是 8K 甚至 4K再算上回答的空间很容易爆掉。我的经验是给每个环节设定预算环节预算 Token约说明系统提示词300 ~ 500固定角色和规则检索片段上下文1500 ~ 2500根据模型窗口调整用户问题100 ~ 200必要时预处理输出预留500 ~ 800保证回答不被中途截断在实现时要写一个动态截断逻辑优先保留与 query 相似度最高的片段如果总长度超预算从低优先级的片段开始丢弃。注意不是简单截断文本而是整套的上下文优雅降级。做好了这些你才能在模型有限的窗口内稳定输出。4. 评测体系建设没有度量就没有改进4.1 为什么 AI 工程必须要自建评测集有句话我在很多场合说过AI 项目最容易犯的错误就是用“感觉变好了”来替代“实际变好了”。模型换了、提示词改了、检索参数动了你怎么知道结果是变好还是变坏唯一的办法就是评测。有些人说“自动化评测不靠谱”其实关键在于你怎么定义靠谱。我的做法是建设一个三层评测体系单元指标评测针对检索层的召回率、命中率等客观指标端到端效果评测对完整问答结果的质量做打分线上反馈监控通过用户行为点赞、点踩、追问形成闭环。用在知识库项目里我一般会先整理出一套人工标注的评测集比如 200 条常见问题每条问题标注标准答案、期望引用的知识库文件、可接受/不可接受的边界。4.2 检索质量评测命中率、召回率与重排收益检索层的评测相对客观。我先定义几个指标Hit Rate命中率Top-K 结果里有多少比例包含标注中期望的正确信息MRR平均倒数排名正确结果出现的位置越靠前分越高公式是1/rankRecallK正确信息在 Top-K 中被召回的比例。我习惯用一段简单的脚本跑检索评测大致逻辑如下def evaluate_retrieval(test_queries): total_hit, total_mrr 0, 0 for q, expected_info in test_queries: results hybrid_search(q, top_k10) hit any(expected_info in r.content for r in results) rank next((i 1 for i, r in enumerate(results) if expected_info in r.content), None) total_hit int(hit) total_mrr 1 / rank if rank else 0 return total_hit / len(test_queries), total_mrr / len(test_queries)跑完以后把不同切分策略、top-k 取值、是否加 rerank 的分数放到一张对比表里。我当时记录的结果是加 rerank 后 Hit5 从 0.64 提升到 0.78MRR 提升幅度更大。这个数据直接说服了我把重排阶段固化到生产流程里。如果你现在还在“凭感觉”调参我强烈建议先补上这一步。4.3 生成质量评测人工打分与基于模型的自动评判生成侧的“答案好不好”很难用单一指标量化我的实践是采用人工抽取 自动评判结合的方式。人工标注时我给每条回答打三个维度相关度是否答非所问、忠实度是否基于给定资料有没有幻觉、完整性是否覆盖问题核心点。这个工作确实累但它是评测集的金标准。为了减轻人工压力可以用更强的模型做“AI 裁判”。把用户问题、检索到的资料、模型生成的答案、甚至还有人工作为参考的答案一起提交给裁判模型让它按 1 到 5 分打分。注意裁判模型本身可能带有偏好冗余所以我一般会让它同时输出评分理由后面再抽查评分理由来发现偏差。评测维度说明可用方法相关度回答与问题是否相关人工 / LLM 评判忠实度是否严格依据资料有无幻觉LLM 对比资料打分完整性是否覆盖各方面人工为主LLM 辅助格式达标率是否按要求的结构输出脚本正则或规则校验有了这套评测后面换模型、调整 Prompt 时就不再是“拍脑袋”而是看数字说话。没有评测的 AI 工程就像没有仪表盘的飞行你以为在飞其实只是在打转。5. 生产化部署与迭代闭环5.1 从 demo 到服务API 封装与推理加速当你把链路在 Jupyter 里跑通就该考虑把它变成真正的服务了。我的习惯是先把核心模块整理成几个独立 Python 文件再做 API 封装。FastAPI 是我最常用的框架干净、性能好、文档自动生成。服务层通常包含这几个接口/index接收文档并完成切分、向量化、入库通常有权限控制/search纯检索接口方便调试和评测/chat完整的问答接口内部走检索 生成/health健康检查。一个最小可用的 FastAPI 代码结构如下from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class ChatRequest(BaseModel): query: str user_id: str default app.post(/chat) def chat(req: ChatRequest): context retrieve_top_k(req.query) answer generate_answer(req.query, context) return {answer: answer, sources: [c.metadata for c in context]}生产环境里还有一个经常被忽略的点并发控制与推理批处理。本地部署的模型如果一次请求进来就占满显存多个用户同时访问就会排队甚至 OOM。我一般会在模型推理层加一个队列或使用支持 continuous batching 的推理框架比如 vLLM如果你用的是兼容 OpenAI API 的服务这样吞吐能提升不少。另外无状态化也很重要不要在进程里保存对话历史除非你是做多轮对话且有持久化设计。我见过太多服务因为全局变量里存了 A 用户的 session结果 B 用户拿到 A 的上下文这种怪问题。每轮对话如果需要历史应显式传入对话状态参数并在服务外部管理。5.2 监控、日志与安全护栏上线以后最怕的不是模型回答错而是你不知道它回答错了。所以日志和监控必须从第一天就设计好。我会在/chat接口里记录完整的结构化日志query、检索结果 ID、拼接后的 Prompt、模型输出、耗时、Token 消耗。别嫌日志量大这些数据是你后续优化和排查问题最重要的素材。配合一些第三方可观测平台或者 Elasticsearch 做可视化检索问题定位就快很多。监控指标至少包括请求量、平均延迟、P95 延迟Token 消耗总量与成本估算失败率超时、解析失败、模型报错用户反馈率点赞/点踩/“没有帮助”按钮。安全护栏方面主要管两件事注入防控和内容合规。不管哪类模型都必须假设用户输入可能刻意引导模型跳出设定。我的防线是对用户输入做敏感词和注入模式检测同时在 prompt 里加固“不要执行除知识问答外的任何指令”输出侧再对模型回答做一次敏感内容过滤防止出现不当内容。这块不能完全避免问题但至少可以把风险控制在早期。5.3 持续迭代把线上反馈变成下一轮优化数据上线不是终点而是另一个起点。我每个迭代周期大概是两周到一个月一次从线上日志里拉取答错的案例点踩、无来源引用、超时等归纳出错模式可能是检索漏召回、模型幻觉、数据源缺失更新评测集每轮加入新的难例在评测集上跑完整回归确认改动确实提升整体指标而不是修一个新问题引来三个新问题。这个“反馈到数据到回归”的闭环是我认为 AI 工程从零开始最重要的工程文化。如果没有这个闭环你永远在盲改。有它至少每一次修改都变得有迹可循。6. 常见问题与排查技巧实录6.1 检索不到相关内容先查数据再查算法这是一个出现率极高的问题。遇到过用户问“公司报销流程怎么走”结果检索出来的全是员工守则里的考勤内容。排查顺序建议如下确认数据源里确实有“报销流程”相关内容。这是最容易被忽略的很多项目号称“知识库”其实只有几十篇挂了名的文档。确认切分后的文本是否保留了关键信息。打开索引里的具体 chunk 文本人工看看“报销”这个关键词是否在查 embedding 模型对业务词汇的敏感度。可以试把一个标准问句的向量和文档片段向量的相似度打出来如果普遍偏低考虑换 embedding 模型或增加关键词召回通道检查是否中间有截断或编码乱码。中文文本转编码时出现的乱码会直接把语义完全打乱。我处理这类问题的技巧是先做一次“拆弹测试”拿问题中的核心名词直接去零向量库里做关键词匹配走 Elasticsearch 的 match 查询如果零向量库里搜不到数据库里的数据就有问题。用这个方法你能把问题边界快速锁到“数据层”还是“检索层”。6.2 模型胡说八道幻觉怎么压幻觉的根源通常是三类检索到的资料本身就不支持回答、Prompt 给了模型自由发挥的空间、模型能力上限不够。我的处理顺序如下先给 Prompt 加硬性约束明确“只能基于资料回答资料不足就直言不清楚”把温度调低通常 0.2 以下减少随机性在检索阶段提高 top-k 数量和 rerank 质量让正确答案更容易进入上下文如果模型仍然乱编就需要在输出侧加“引用来源校验”——要求模型在回答末尾列出引用的资料编号业务上再对这些编号做硬校验。这个方法虽然不能 100% 消灭幻觉但能把幻觉率压到可控范围。有一次用户问一个很偏门的历史政策问题资料库里并没有对应记录因为我们加了“没有信息就直说”的强烈约束和引用校验模型最终回答的是“资料库中未找到相关历史政策建议联系人事获取正式文件”这实际上是比较理想的兜底表现。6.3 回答速度慢定位瓶颈在哪知识库问答的端到端延迟常常在 3 到 5 秒。如果慢到 10 秒以上需要定位瓶颈。如果检索层耗时高检查向量库的索引参数减少扫描的候选数量或者给向量索引加 HNSW 参数调优如果生成层耗时高检查模型输入 Token 是否过大、输出是否太长、机器有没有被别人占显存如果是 API 方案检查是不是网络链路和排队等待必要时加本地缓存策略。我在生产环境用过的一个技巧是两级缓存完全相同的问题走短缓存比如 10 分钟相似的问题走语义缓存把 query 的 embedding 相似度超过阈值的直接复用之前答案。这对高频重复咨询场景比如企业 FAQ能省掉一大半的重复计算成本延迟从 4 秒降到 200 毫秒。6.4 成本失控Token 在不知不觉中烧钱成本问题是 AI 工程最现实的坑。排查方法很简单每次请求的 Prompt 里到底拼了多少冗余内容你是不是把 Top-10 个 1000 字的片段全塞给模型了是不是一个用户短时间内反复请求同一个问题我的做法是记录每个请求的输入与输出 Token 数按天汇总给每个用户或者每个功能模块设置配额在检索组装层严格控制上下文预算优先填高价值片段对重复问题启用缓存减少不必要的模型调用。曾经我们一个月 API 成本高达近两万后来靠两层缓存和上下文预算控制直接降了一半以上。成本不是上线以后才考虑的而是设计阶段就要写在预算表里。写在最后的实操心得这两年和 AI 工程打交道最大的感受是这个领域变化太快但工程方法论是相对稳定的。今天你可能用这个模型明天换那个框架但“定义问题、选型、最小系统、评测闭环、监控迭代”这条链路永远适用。我在实际项目里最深的体会是不要崇拜任何一家工具或模型。今天的神器三个月后就可能过时。真正属于你自己的是一套能快速评估、快速落地、快速修正的工作方法和几个可靠的评估数据集。另外如果你是刚入门不用一上来就追求什么高并发架构先把我前面讲的最小链路老老实实跑通再往里添砖加瓦。踩过的坑多了你自然会形成自己的判断。最后再分享一个小技巧多做一个“项目复盘文档”把每次改动的动机、假设、实验结论和最终效果都记下来。下次遇到相似问题你能少走很多弯路。AI 工程是一场长跑慢就是快稳就是快。希望这篇文章能帮你把“从零开始”这四个字变成脚下已经走过的路。