从零上手AI工程:RAG文档问答系统的完整实践路径
我最早看到 “ai-engineering-from-scratch” 这个标题时第一反应是又一个让人收藏后吃灰的教程清单。但真把这条线走下来才发现它更像一张地图标记了从“会跑别人代码”到“能自己做 AI 系统”之间的所有岔路口。想认真聊聊这条路的走法以及路上那些常规文档不会告诉你的事。先给这篇内容定个位。ai-engineering 不是算法科研不是写几篇 AI 应用文章而是把模型变成稳定业务的系统工程。你如果正卡在“会调 API、能跑通示例但真要自己做一个项目就无从下手”或者刚接触大模型想系统建立工程思维这篇能给你一条顺势可落地的路径。我的经验是以个人开发者身份从零搭过完整 RAG检索增强生成问答系统、做过大模型调用服务也踩过部署、评估、数据清洗的各种坑。这里不讨论论文推导只讲怎么把“感觉学会了”变成“真的跑起来”。1. 项目整体拆解ai-engineering 到底在学什么1.1 先破一个认知误区AI 工程不等于训练模型很多人一听到从零开始学 AI就认为要把 PyTorch 源码、反向传播推导、Transformer 架构手写一遍。这个想法在科研向、模型研发向的路线里没错但 ai-engineering 的目标不同它关注的是如何把现成模型通过数据工程、评测体系、服务化架构组合成一个能稳定使用的产品。我见过不少人死磕数学和原理结果卡了一个月还没碰过代码。而真正做 AI 工程的人第一周就把文本切块、向量检索、调用大模型接口全链路跑通了。不是说原理不重要而是“为什么这么设计”可以靠长期补课解决“系统能不能跑”才是工程的第一决策点。合理的姿势是先有系统全景再有局部精通。ai-engineering 的核心能力结构大概有三块。第一是模型应用与编排也就是会选模型、会写 prompt、懂怎么把模型接进业务流第二是数据工程包括文档清洗、知识切块、评测集构建第三是工程化交付包括 API 服务、错误处理、日志监控、性能评估。这三块合在一起恰好是一个 AI 项目从想法到部署的全过程。1.2 为什么从“做项目”切入效果最好纯学理论和纯追热点是两种极端前者容易丧失反馈后者容易被社交网络上的 Demo 带偏。我的建议是选定一个高频真实场景比如“团队知识库问答”“公众号文章总结”“简历分类助手”从项目倒推你缺什么然后逐个补齐。这种“以终为始”的学习方式能给每项知识点分配一个具体的落点难度感知会清晰很多进度也不会那么模糊。有一点我特别想提醒别贪多。很多新手今天看 Agent 火了去啃复杂多智能体框架明天看模型微调火了就去准备训练语料结果精力全花在切换赛道上了。从零开始的稳妥方式是挑一个 2 到 3 周能做完的中小型项目把它的链路走到黑。链路走通后你获得的是一套可复用的方法论而不是碎片化的工具函数。2. 技能地图与阶段规划从零基础到跑通一个 AI 系统2.1 拆成四个阶段每一步都能看到产出我给自己定的路线可以压缩成四个阶段每阶段都有明确产出用来确认自己是真会了不是看过就算学会。阶段一基础工程底子。要求会用 Python 处理数据会写函数和类会用虚拟环境管理依赖遇到报错有基本的排查思路。不用深入设计模式只要代码结构清晰、能调试能改就行。这段时间控制在两周内超出说明学习方法有问题该多动手写而不是多看教程。阶段二自然语言处理的常用工具使用。学正则、分词、文本向量化掌握至少一种向量数据库或检索库比如 Chroma、FAISS。能用 bge 系列或 OpenAI embedding 接口把文本变成向量并且会做简单的相似度检索。产出物是一个本地文档问答的原型把几篇 Markdown 文件切块后存进向量库输入问题并返回最相关的几个段落。阶段三大模型接入与提示词工程。学会至少一种大模型 API 或本地模型加载方式如 llama.cpp、Ollama。能把前面检索到的文本块组装进 prompt让模型基于上下文生成答案。同时需要系统理解 temperature、max_tokens、top_p 参数的意义和试调方向以及流式输出在交互体验上的作用。产出物是你自己的第二个版本问答系统此时答案有引用来源也能区分“我找到相关内容”和“我不能确定”。阶段四评估与优化。构建一个 20 到 50 条问题的评测集脚本化评估系统给出答案的质量包括忠实度、相关性和覆盖度。针对不理想结果做可干预的修改比如调整检索策略、切块大小、prompt 模板并量化前后指标变化。产出物是一份带指标对比的优化报告。这一步做完基本就脱离“照别人代码跑”的阶段了。2.2 工程视角下需要刻意练习的三件事常规教程最容易忽略的其实是这三件事读文档能力、抽象能力和排查能力。读文档不是一目十行而是能准确找到接口的输入输出和异常情形抽象能力表现在设计一个模块时知道哪些逻辑应该封装、哪些参数应该暴露排查能力就是遇到 Bug 时能根据日志和现象快速定位是数据、模型还是代码问题。这三个能力没有专门课程只能靠做项目时带着意识去练我通常用“写最小可复现脚本”来加速这个过程。比如跑向量检索结果不对不要直接在长项目里 debug而是抽出一个 20 行的脚本固定模型、固定数据单独看输出。很快就能确定是不是向量维度不对、查询文本没清洗或是 top_k 设得太小。2.3 学哪些可以往后放“从零”不是“速成数学博士”很多人一上来就陷进线性代数、概率论、Transformer 源码实际上这些对于初期做 AI 应用并不紧迫。你可以往后放的包括自注意力机制的完整推导、反向传播手工实现、模型分布式训练、K8s 部署、领域自适应微调。明确这些边界不是为了降低标准而是让你把有限的意志力投在当下最需要的环节。等到项目做深了、问题逼到墙角时再补原理也不迟那时理解成本反而更低。3. 工具选型与环境搭建最大程度减少“零基础”的摩擦力3.1 基础环境配置能少折腾就少折腾我用的是 Python 3.10 配合 conda 或 venv 管理独立环境。新手最容易翻车的就是全局环境装了一堆不同版本的包互相冲突为每个项目单独建环境能省掉大量重装系统的痛苦。建议你直接用 miniconda这比完整版 anaconda 更轻。建环境命令很简单一条 conda create -n ai-engineering python3.10 就能解决。之后安装依赖时先查兼容性不要一股脑把最新版本全装上。Jupyter Notebook 适合做数据探索和效果验证日常开发还是用一个带插件的编辑器我自己习惯用 VS Code。需要提醒的是国内下载工具包如果遇到超时或连接失败多试几次或者换更近的镜像源通常能解决。不要相信任何所谓“稳定加速方案”的神秘操作老老实实使用官方或镜像通道排查起来也更容易。3.2 核心库路线缺什么装什么别记“全家桶”很多教程一上来让你装 langchain、llama-index、chromadb、sentence-transformers 一大串这不合理。依赖越多变量越多新手根本分不清报错来自哪里。我建议最小化起步先只装 sentence-transformers 生成 embedding用 numpy 做相似度计算大模型调用走官方 OpenAI SDK 或 Ollama 的 HTTP 接口。链路跑通后再按需引入 langchain 或 llamaindex 这类编排框架。这样万一系统不工作你知道问题出在自己写的逻辑里而不是框架的魔法里。用向量库也有讲究。数据量在几千条内Chroma 和 FAISS 都足够好用。Chroma 自带持久化尤其适合做原型FAISS 更侧重检索性能适合做更底层的定制。我自己的选择是在 Demo 阶段用 Chroma免费、纯本地、API 简单等进入生产化再迁移到更正式的服务或云向量库。3.3 模型选择本地和在线 API 怎么取舍这是个绕不开的话题。大模型选择上追求效果和省事可以走商用 API花小钱换时间在意数据隐私和长期成本就上本地模型。本地模型我首推 Ollama 作为运行入口它对硬件要求相对友好一条指令就能下载运行 llama3.1、qwen2.5 这样的开源模型。嵌入模型我拿 bge-m3 当默认选项中文效果好、最长支持到 8192 token、体积也不算疯狂。如果你的任务是英文为主那也可以换 E5 或者 OpenAI embedding各有取舍没有绝对最优。硬件方面要说实话本地跑 7B 到 14B 的模型至少要有 16G 内存或 6G 以上显存量化版本可以再降需求。没有 GPU 也别慌CPU 推理很慢但能跑通小模型先打通流程再考虑性能优化。我曾经在 8G 内存的笔记本上跑过 qwen2.5:3b虽然每个回答要等十几秒但作为流程验证完全够用。4. 核心实操从零搭建一个文档问答系统RAG 全流程4.1 项目场景与整体链路设计实操部分我做的是团队内部文档问答机器人。场景很简单团队有几十篇操作手册和会议纪要散落在各个 Markdown 文件里新人入职要花大量时间翻文档才能找到答案。我的目标是把这批资料变成问答接口员工提问“我们上线流程第一步要做什么”系统能给出有依据的答案并附上原文链接。我把整个链路拆成六个环节数据处理、文本切块、向量化、建立索引、检索并组装、生成与引用。这一步一定要在动代码前画个流程草图哪怕只是写在自己笔记里。不画图直接开干八成会在跑到一半时发现数据格式没统一或者检索结果质量差得没法看回头再改结构成本翻倍。4.2 数据清洗和切块向量检索的成败起点很多人以为 RAG 的关键在于选模型和调 prompt实际上一半以上的答案质量问题出在数据处理环节。第一步先做清洗去掉乱码、空行、多余重复内容把 Word 或 PDF 转出来的垃圾字符清干净。我踩过最大的坑是 PDF 转文本后出现大量换行和乱码直接进向量库导致检索结果完全不可用。针对同一目录下格式不一致的文件我用一个统一的 extractor 函数先全部转成纯文本或 Markdown再进入后续流程。第二步是关键中的关键切块策略。切块太大会让 embedding 表达的话题维度变模糊导致检索结果不精准切块太小又会让单个块缺少足够上下文模型生成时没有完整背景。我对比过几种方案后给中文文档的默认参数是 chunk_size512、overlap64。说明一下为什么这样选512 约等于 500 个中文字符大致能容纳一个完整操作流程的描述overlap 保留重叠区可以让跨块边界的话题不丢失。英文文档我会倾向于用 800 到 1000 token因为英文词的平均信息密度略低于中文。与其迷信神奇参数不如建好评测集用数据告诉你哪个切法更适合你的语料。4.3 向量化与检索用可复现的脚本调通再封装向量化阶段的模板代码如下核心是把每一段文本转成 embedding 后存入 Chromafrom sentence_transformers import SentenceTransformer import chromadb model SentenceTransformer(BAAI/bge-m3) client chromadb.PersistentClient(path./docs_db) collection client.get_or_create_collection(team_docs) # 假设 chunks 已经是经过清洗和切块的字符串列表 embeddings model.encode(chunks, normalize_embeddingsTrue) for i, (chunk, emb) in enumerate(zip(chunks, embeddings)): collection.add( ids[fchunk_{i}], documents[chunk], embeddings[emb.tolist()], metadatas[{source: source_files[i % len(source_files)]}] )这里的 normalize_embeddingsTrue 是常见的细节坑。对余弦相似度检索来说归一化之后向量的点积就等于余弦相似度计算效率更高如果省略这个参数很多向量库默认用内积或欧式距离结果排序会有差异。编码后用 collection.query(query_texts[question], n_results5) 就能拿到最相关的片段。检索引擎选完你还要意识到一个问题单纯向量检索在短查询和长文档之间容易失配。用户提问通常是一句话而相关文档块可能是几百字这两者的语义距离并不总是最近。我的改进是加一层“查询改写”让大模型先基于原始问题生成几个更详细的检索 query再一起去向量库查。这策略叫 multi-query retrieval虽然多花一次模型调用但召回质量的提升非常明显。4.4 生成与引用让回答有依据而不是炼丹在拿到 top_k 相关文本块后组装 prompt 是下一个决定性动作。我的做法是把检索结果按固定格式拼接并明确提示模型“只基于文档内容回答如果文档中没有相关信息直接说无法确定”然后附上原文内容。例如请基于下面提供的文档片段回答用户问题。如果片段中没有足够信息请回答“根据现有文档无法确定”。 每个答案结尾需要列出引用文档编号。 文档片段 [1] 文档上线流程.md 本次版本上线的第一步是创建发布分支随后进行代码冻结... [2] 文档回滚指南.md 当发布完成后出现严重缺陷需要立即执行回滚操作... 用户问题我们上线流程的第一步是什么这里有一个被严重低估的细节要求模型在回答中引用来源编号。这么做有三个好处一是用户可以验证答案不至于模型一本正经地瞎说二是强迫模型回到给定片段里找证据三是便于你后续在页面里把“引用来源”当作功能亮点展示。从实际操作效果看加了引用约束之后回答的幻觉率明显下降人也更愿意相信系统。为了让结果易于使用我在返回答案的同时连同命中的文档块原始来源路径和相似度分值一起返回。这样既支持用户跳转原文也方便你在调试时判断“检索阶段选错了资料”还是“生成阶段理解错了”。4.5 效果评估不量化就没法优化项目做到这儿很多人会进入一种“感觉差不多了”的状态但我建议你压制住这种冲动直接用评测集跑一轮量化打分。我用 30 道从真实提问中整理出来的问题作为测试集每道题配上标准答案要点和参考文档。评估指标上我用了 RAGAS 里的三个核心指标忠实度answer faithfulness、答案相关性answer relevancy和上下文相关性context precision。打分的脚本逻辑很简单调用大模型当裁判对比系统回答、标准回答和检索上下文按 1 到 5 分输出。第一次跑出来的结果通常会让你清醒。常见的情况是上下文相关性不错但忠实度偏低说明模型生成时并没有严格受限于检索内容可能是在“自由发挥”。针对这个现象我要么强化 prompt 里的限制要么调低 temperature比如从 0.7 降到 0.2。另一类情况是答案相关性低但这不一定是生成的问题而是检索没召回到正确答案此时要优化的是检索链路而不是 prompt。这个切片分析思维特别重要一定要会分清问题出在哪一级。5. 踩坑实录与排查思路RAG 系统常见的五个翻车点5.1 向量库里搜不到“正确答案”先确认召回而不是怀疑模型表现用户问题明明在文档里有明确说明系统却返回了不相关内容。排查时我一般先做两个快速测试直接用精确关键词去库里搜原文片段确定文本确实被成功写入然后把用户问题换成与文档相同风格或更完整的表述看检索排序是否正确。如果换表述后能召回说明原问题写得太口语化解决方式是加查询改写如果精确搜也搜不到检查的是清洗和切块阶段是否把文字弄丢了常见原因包括 PDF 转文本乱码、特殊符号被过滤、切块重叠区覆盖不够。5.2 丢中文乱码和编码中文项目绕不开编码问题。处理外部文档时我遇到过明明是“标题”却变成“棰樺”这样的乱码还有编码声明不一致导致批量处理中断。解决方法是统一全链路 UTF-8 编码读取文件时显式指定 encodingutf-8同时处理 BOM 头。恶意文件造成的乱码不在讨论范围但自己项目里所有环节都统一编码能消除百分之九十的怪问题。5.3 大模型响应慢或超时本地模型体验上遇到最多的是响应速度慢到用户失去耐心。优化手段按性价比排序第一用流式输出让用户看到逐字生成而不是干等第二换更小更快的量化模型比如 7B 换 3B第三做查询缓存相同或相似的问题直接返回历史答案。商用 API 则要注意调用并发和上下文长度精简文档块内容把没必要塞进 prompt 的冗余片段裁掉这样响应时延和 token 成本都会显著下降。5.4 模型一本正经地“编答案”这是 RAG 最让人头痛的问题。单纯的 prompt 约束不能根治幻觉我最终采用的组合拳是约束生成范围加引用来源对高置信场景用更低 temperature加上一个“拒绝策略”——当检索到的文档片段平均相似度低于阈值比如 0.35系统直接回复“没有找到足够相关资料”而不是硬着头皮硬答。这样也许会让系统看起来“笨”但比提供可信的错误答案要好得多。5.5 文档更新后系统没及时同步当知识库文件被增删改你需要同步维护向量库索引。我最初的版本是每次启动时全量重建数据少还好后来文档超过几百份时效率太低。后来改成按文档修改时间和内容 hash 判断变更只对变化部分做增量更新。这里有个容易忽略的点删除文档时必须同步删除对应向量记录否则旧数据会一直参与检索导致用户总拿到过期信息。6. 向生产环境延伸我还会怎么继续往前走做完文档问答后我试着把这套经验迁移到更多场景里比如把多份表格和报表接入数据分析问答把会议纪要变成待办自动提取。迁移过程告诉我一个很重要的道理ai-engineering 的新鲜感不在模型多聪明而在你是否有能力快速构建评测集并验证方案。换一个领域你就要重新清洗数据、重新设计切块策略、重新定评估指标。这一整套流程越熟练你对 AI 工程的掌控感越强。继续扩展的方向我觉得有三个值得投入一是 Agent 工作流编排即让模型有步骤地调用工具、拆分任务这里出了很多花哨框架但核心还是“状态管理”和“容错设计”二是自动数据合成和模型微调当你需要模型持续适配某类领域时微调是比堆 prompt 更稳定的方案三是可观测性和成本治理AI 应用上线后不是结束而是开始请求延迟、token 花费、错误追踪、用户反馈回流机制都会决定这个系统能活多久。最后分享一段个人体会。我学 ai-engineering 的过程中最大的瓶颈其实不是知识本身而是“急于求成”。总想一步到位把 Agent、微调、部署全学会结果每个都只停留在表面。后来我强制自己把完整流程走完至少三遍每次换不同数据、不同场景很多原来不太理解的细节在重复中自然变得清晰。如果你也正从零开始记住一件事先走通一个最小闭环再谈扩大边界。闭环给你的正反馈比任何课程都更能把你留在编程桌前。