从零搭建AI工程:知识库问答系统的落地实践与避坑指南
1. 先把话说清楚AI工程到底是什么又难在哪市面上聊“AI工程”的文章不少但大多讲的是怎么调一个模型接口、怎么把提示词写得更漂亮。真正意义上的AI 工程ai-engineering是从零开始把一个 AI 想法变成稳定、可维护、能度量、能迭代的系统而不是一次性的 Demo。它横跨数据工程、模型服务、评测体系、可观测性、成本控制和持续交付是典型的工程师活儿。我刚接触这个领域时也以为难点在模型本身。玩了几个月之后才明白模型只占整个系统的三四成剩下六七成全是工程问题。比如数据对不对、评测怎么建、版本怎么管理、线上怎么监控、请求失败怎么兜底、怎么在预算范围内把效果调到可接受水平——这些才是 AI 工程真正吃人的地方。这篇文章我会以“从零起步”为主线把我搭建 AI 工程项目时沉淀的方法、选型逻辑、代码级别的实操过程和排查经验完整摊开。无论你是想入门 AI 工程的小白还是已经在做业务落地的工程师应该都能找到可以直接照搬的东西。2. 动手前的核心决策不要一上来就写代码很多新手拿到一个 AI 项目第一反应是找模型、配环境、写接口。我强烈建议你先冷静。在写第一行代码之前有几件事必须定下来它们直接决定你后面是顺风顺水还是反复返工。2.1 先定义“任务边界”模型是主角但问题是老板我见过太多失败的 AI 项目根因不是模型太弱而是想解决的任务太模糊。比如“做一个智能客服”这就是个模糊任务。它的真正子任务是意图识别、知识库问答、工单分类、情绪识别、话术生成——每个子任务的输入输出、评估方式、数据要求都完全不同。所以在项目启动时我会花至少一天时间做任务拆解并且把每个子任务写清楚三件事输入是什么形态文本、图片、混合长度限制语言输出需要什么结构自然语言、JSON、分类标签、向量可接受的失败是什么答非所问、超时、拒答、错误分类只有把这些写清楚后面选模型、设计 Prompt、建评测集才有依据。否则你永远在“感觉效果还行”和“好像哪里不对劲”之间摇摆。2.2 技术选型什么都能做的时代反而更要想清楚现在的模型生态已经足够丰富从闭源 API 到开源权重从小模型到大参数基本上“什么都能选”。但选择多不等于可以随便选。我建议用以下四条标准做取舍选型维度核心判断依据我的实践经验模型能力任务复杂度、领域术语密度、是否需要多模态能用小模型绝不上大模型节省成本也减少延迟部署成本GPU 资源、显存、QPS 预估、运维能力没有 GPU 资源时优先考虑 API有 GPU 也要算清楚利用率数据隐私数据是否能出域、是否需要私有化涉及敏感数据时开源模型 私有部署是唯一选择生态成熟度周边工具链、社区活跃度、推理框架支持选生态大的踩坑时有解决方案可查以我自己做过的知识库问答项目为例场景是内部文档问答数据量大但领域集中最终选了开源向量模型做召回 闭源 API 做生成。为什么这么混搭因为召回对领域语义理解要求高需要精细调优而生成环节用 API 能快速获得高质量表达且不会频繁变化。混搭当然有代价后面我详细说。2.3 评估先行没有衡量尺子优化就无从谈起这一点是全书最重要的方法论也是新手最容易忽略的。先建评测集再开始调系统。没有评测集你所有“看起来不错”的感觉都是幻觉。评测集不需要很大我通常从 100 ~ 200 条开始但必须覆盖典型正例正常输入、标准输出边界情况超长输入、空输入、特殊字符、多轮上下文已知难点模型容易犯错、业务最在意的场景对抗样本故意误导模型的输入每条样本要有预期答案并尽量写成可自动判定的形式。比如分类任务就直接比对标签抽取任务比对关键实体生成任务则要设计统一的评分标准。这一步定下来后面每做一次改动都能跑一遍评测集看分数是升是降而不是靠拍脑袋。3. 从零搭建一套可落地的 AI 工程基线定好方向和评估方式后就可以开始搭建系统了。我会以一个经典场景——企业内部知识库智能问答——作为贯穿案例因为它的技术栈覆盖了 AI 工程的主要环节数据解析、向量化、检索、生成、评估和部署。这也是一套可以复用到简历筛选、合同审查、舆情分析、代码检索等场景的通用基线架构。3.1 数据准备AI 工程最脏最累但最值钱的一环AI 工程里流传一句话垃圾进垃圾出。模型再好数据不行全部白搭。知识库问答的第一步就是把散落在 PDF、Word、Markdown、网页里的非结构化内容清洗成可供下游使用的文本块。我的建议是你先关注三个核心动作格式解析与内容抽取PDF 用 PyMuPDF 或 pdftotextWord 用 python-docx网页用 BeautifulSoup。解析之后先人工抽看 20 ~ 50 个样本确认文本没有乱码、表格没有错位、页眉页脚没有被错误混入正文。清洗与去重去掉无意义符号、重复段落、空行。经常被忽略的是“文档自带模板噪声”比如每个 PDF 顶部都带公司名和日期这些内容会导致检索时大量命中噪声片段。分块策略这是直接影响检索质量的关键。块太大会引入无关信息导致召回不精准块太小则语义不完整生成时缺乏上下文。我常用的经验参数是300 ~ 500 个中文字符带 50 字符的重叠。长文档优先按章节结构切分没有结构时再用滑动窗口。切分完了还需要给每个块写 metadata来源、章节、更新时间等。这一步非常重要它会在后续“带引用的回答”和“权限过滤”里帮大忙。简单说metadata 越完整后续检索和生成的可控性就越高。3.2 向量化与检索设计Embedding 选型 混合检索知识库问答的检索环节通常由 Embedding 模型将文本块变成向量再用向量相似度返回最相关的片段。选 Embedding 模型时我会先跑一个“小数据集评测”拿测试集的 query 去检索人工看 Top 5 返回结果是不是真的相关。在实际落地中纯向量检索往往不够需要做混合检索。原因是知识库里经常有精确匹配的场景比如工单编号“INV-2024-089”、某个版本号“v3.2.1”这种内容用向量检索效果其实不稳定不如直接用文本搜索。我的做法是“关键词稀疏检索 向量稠密检索”双路召回最后用 RRFReciprocal Rank Fusion做分数融合。具体参数关键词检索用 BM25这个算法在 Elasticsearch 里有成熟实现向量检索的相似度度量中文场景我用余弦相似度实测比内积稳定RRF 融合时 K 值取 60这个参数越大会让排序越平均举个例子用户问“采购流程中审批金额超过多少需要董事会决议”关键词检索可以精确命中“董事会决议”和“审批金额”向量检索则能理解“采购流程”和“决议流程”的语义关系两条路召回的结果取并集再融合就能兼顾精确和泛化。3.3 生成环节别急着堆 Prompt先设计好上下文检索拿到内容之后生成环节要把“检索片段 用户问题 系统指令”组装成请求发给大模型。这里有三个我踩过坑之后总结的关键点第一上下文拼接顺序有讲究。将检索片段按相关度倒序排列并在每个片段前标注来源编号。这样模型在生成长回答时更容易优先参考排在前面的高相关片段。实测下来顺序调换对回答质量的扰动比你想象的大得多。第二系统指令要“约束角色但不限制步法”。你告诉模型“你是企业内部知识库助手只能依据提供的文档内容回答如果文档中没有答案请明确回复不清楚”但不强制要求“你必须……你必须……”。给模型留出表达空间回答会更自然。同时要求在回答末尾标注引用的来源 ID这对提升可信度和后期排查非常有价值。第三输出结构化。如果下游系统需要继续处理结果建议要求模型输出 JSON 格式例如{ answer: 根据采购管理办法第四节金额超过 500 万元的采购订单需提交董事会决议……, confidence: high, references: [3, 7] }输出 JSON 后下游可以稳定解析字段而不是靠正则去匹配自然语言。这里我的一般做法是把 JSON 输出指令写在用户消息里比写在系统指令里效果更稳定因为系统指令容易被长上下文稀释。3.4 一个最小可运行的工程骨架示例这一小节的代码是一个极度简化的骨架但核心逻辑完整适合在你自己电脑上跑通后再扩展。它把“检索 生成”串起来也是理解 AI 工程全流程最快的路径。from openai import OpenAI client OpenAI(base_urlhttps://your-llm-endpoint, api_keyyour-key) def retrieve(query: str, top_k: int 5) - list[dict]: 简化示例假设已有向量库和BM25索引 实际项目中使用 向量检索 关键词检索 RRF 融合 bm25_hits bm25_search(query, top_ktop_k) vector_hits vector_search(query, top_ktop_k) return rrf_fusion(bm25_hits, vector_hits, k60) def build_prompt(query: str, candidates: list[dict]) - str: context \n\n.join( f[{i1}] 来源: {c[source]}\n{c[content]} for i, c in enumerate(candidates) ) return f请仅依据下列文档内容回答用户问题。 如果文档中没有答案请回复“根据现有文档无法回答该问题”。 回答后附上引用的来源编号。 文档内容 {context} 用户问题{query} 输出格式JSON包含 answer、references 两个字段。 def answer(query: str) - str: candidates retrieve(query) prompt build_prompt(query, candidates) resp client.chat.completions.create( modelyour-model-name, messages[{role: user, content: prompt}], temperature0.2, ) return resp.choices[0].message.content if __name__ __main__: print(answer(采购审批金额超过多少需要董事会决议))这段代码骨架虽然短但包含了 AI 工程的关键闭环检索、组装上下文、请求模型、返回结果。实际系统需要在此基础上加的东西后面的章节继续展开。4. 工程化落地的四条命脉骨架能跑通只是开始离“能上线”还有一大段路。我把它归纳为四条命脉可观测性、成本控制、评测机制、迭代方法。任何一个缺失系统都稳不了。4.1 可观测性没有日志和追踪AI 系统就是黑箱AI 应用最麻烦的一点是不确定性。同一个问题每次答的都不完全一样。线上出问题之后如果你没有完整日志根本没法定位是模型抽风、检索没召回、还是 Prompt 被截断。我的日志设计会至少记录以下内容请求的完整输入query、上下文片段、完整 prompt模型返回的原始输出检索阶段每一条候选的来源、得分、排名响应耗时、Token 消耗、总费用版本号代码版本 提示词版本按这个标准我用 JSON 行格式写日志每行一个请求记录后续用日志分析工具做聚合统计。或者更直观的做法是接入现有的 LLM Observability 工具在 Dashboard 上直接看每次请求的完整链路。没有可视化观测的 AI 项目我强烈建议不要上线。4.2 成本控制Token 是 AI 工程里的“商品粮”很多人做 Demo 时不在乎 Token 费用上线后被账单吓一跳。Token 消耗的大头通常在长上下文。每轮对话都把 30 个文档片段塞进去一个月下来费用非常可观。我的成本控制三板斧缓存把完全相同的 query 及其结果缓存起来KV Cache 命中率做到 30% ~ 50% 不难能省掉一大笔重复调用费用。上下文瘦身检索 Top K 不是越大越好。Top 3 到 Top 5 通常就能保证效果Top 10 除了费钱还会引入噪声。把 K 从 10 减到 5费用直接降一半。分级路由简单问题用便宜的小模型复杂问题才调更强的大模型。可以先让一个小模型做个分类判断问题属于哪个难度级别再路由到不同模型。这一层能省 30% 以上费用。4.3 评测体系从人工抽检到自动化回归评测体系是“迭代敢不敢”的心理保障。没有自动化评测你每次改 Prompt 都要花半天去人工看十几个例子效率太低而且容易被“感觉变好了”误导。我建议分三个层次搭评测体系层级方法频率用途L1 单元评测用固定评测集跑自动化断言比对分类标签、实体、JSON 格式每次改动后快速拦截明显回归L2 多维打分对生成式回答用 LLM-as-Judge大模型当裁判打分每日/每周评估回答质量、相关性、忠实度L3 人工抽检抽选线上真实日志做人工评估每周发现评测集覆盖不到的问题LLM-as-Judge 是现在比较通用的做法也就是用一个大模型给另一个模型的输出打分。比如让裁判模型按“回答是否忠实于文档内容”“是否完整解决用户问题”“是否包含幻觉”三个维度打分。不过裁判模型自身也需要防偏置我的经验是给裁判提供用户问题和参考答案并要求输出 JSON 格式的评分理由这样可以过滤掉一部分误判。4.4 迭代方法不是改模型而是建闭环AI 系统上线只是迭代的起点。我维护一套“发现 → 记录 → 修复 → 回归”的四步闭环发现从线上日志和用户反馈里挖出失败案例。记录把失败案例加入评测集写清楚预期输出。修复针对问题修改检索逻辑、Prompt 或后处理规则。回归完整跑一遍评测集确认修复没有弄坏其他功能。这个闭环看起来朴素但它才是 AI 工程质量提升的真正引擎。我见过太多团队每天都在调 Prompt但没有评测集等于蒙眼开车今天这里变好了明天那里变差了永远在打地鼠。5. 真实项目里的坑排查思路与速查表AI 工程的坑比传统软件工程多一个维度因为“没有崩溃”不代表“没有坏”。我把过去大半年里踩过的坑整理成一张速查表你在自己的项目里大概率会遇到其中几个。症状可能原因排查路径解决方案回答里出现幻觉来源上下文被截断模型没看到完整引用信息检查日志中实际送入的上下文长度和内容加大上下文窗口或优化分块策略切断引用来源检索总是返回无关内容数据清洗不彻底噪声块污染向量库抽查向量库里的段落内容看是否有模板头、乱码重新清洗数据过滤噪声块重跑向量化问题稍微换个说法就答不出来评测集覆盖不足只覆盖了字面匹配观察日志中失败 case 的检索排名扩充评测集增加同义改写样本调 BM25 参数系统越跑越慢向量库数据量增长没有做索引优化查每个请求的检索耗时分布换 HNSW 索引参数、缩小搜索范围、加缓存费用每个月都在涨请求量上升 上下文过长按 query 维度分析 Token 消耗 TOP 榜加缓存、降 Top K、模型分级路由多轮对话中模型“忘了”上文上下文管理没有裁剪历史看多轮请求的 log 里 messages 长度做历史摘要压缩或限制保留最近 N 轮完整消息这里我额外分享一条排查原则遇到任何诡异行为先看检索结果再看 Prompt 组装最后才怀疑模型。我统计过自己的项目问题分布大约 60% 的问题出在检索和数据25% 在 Prompt 设计真正模型本身能力不足导致的问题其实不到 15%。很多人一上来就怀疑模型不行换了一个又一个结果问题出在数据分块没做好白白烧了时间。具体排查时可以这么操作把一个线上失败的 query 复制出来单独跑一遍检索逻辑看看 Top 5 结果里有没有正确答案。没有就是检索或数据的问题有但模型回答错了才需要去看 Prompt 和生成配置。依此二分定位效率非常高。6. 再站起来看从“跑通”到“长期稳定”系统上线后还有一个常被忽视但极其重要的环节——持续运行保障。AI 服务不像传统 CRUD 接口那样稳定可预期它有隐含的漂移风险。6.1 数据漂移与提示词漂移上游文档更新了你向量库里的旧版本还在API 供应商把模型背后悄悄换了一个新版本同一 Prompt 的输出风格变了。这些都不是代码层面能察觉的变化。我的对策是设置“健康巡检”每天用一小撮固定样本跑一遍评测集把评分变化做成趋势曲线。一旦发现分数连续多日下降立刻通知相关人介入排查。没有这种巡检机制AI 服务质量是在“温水煮青蛙”式下滑的。6.2 Hot Path 与降级方案线上真实流量中总有超出预期的情况。QA 系统最怕的是检索服务挂了或者生成服务超时。我通常做三级降级设计请求进入优先完整检索 生成检索失败但生成可用时直接用用户问题请求模型不加知识库上下文并告知“未检索到资料以下回答仅供参考”生成不可用时返回最近一次缓存结果或引导用户走人工通道降级方案看似简单但没有它线上事故就是“系统全挂”有了它用户体验只是“答案质量变差”这是完全不同的两个等级。6.3 团队协作与代码管理AI 工程是跨学科协作的活算法工程师、后端工程师、业务方要紧密配合。我建议把 Prompt 当代码做版本管理把评测集当测试代码做版本管理每次改动走 MR 评测回归。团队里必须有一个明确的“评测集 Owner”负责维护评测集质量防止评测集被随便改得失去基准作用。我在实操中沉淀的几个额外技巧最后再分享几个很难在教科书里找到的小经验。Prompt 里引用编号要显式关联来源。某个回答让模型“结合文档 [1] 和 [3] 回答”比只给一堆文本让它自己发挥更稳定。模型在答案中引用编号时人类用户会天然增强信任感而且你可以通过检查编号是否真存在快速识别幻觉。温度参数未必越低越好。知识库问答我一般用 0.1 ~ 0.3但如果你做头脑风暴类辅助工具温度 0.7 反而更有用。统一用一个温度是常见的偷懒但也常常限死了一类任务的效果上限。对空结果要特判。检索不到任何相关内容时不要硬生生把空上下文发给模型那会让模型自由发挥编造答案。先让系统返回“该问题未在知识库中找到相关信息”并提示用户尝试换个说法。这一条直接维护了系统的可信底线。日志一定要包含 prompt 的完整快照。很多团队只记录最终答案事后想复盘却发现 prompt 已经改了好几版根本对不上。把 prompt 快照写进日志一眼就能看出当时的输出是根据什么上下文生成的。我自己从零开始做 AI 工程的体会是真正难的从来不是“调用模型”而是围绕模型搭出那套扎实的工程底座。底座稳不稳取决于数据好不好、评测全不全、观测透不透、迭代快不快。这些环节没有捷径但每一步做到了系统就会从“能跑”变成“可信赖”。希望这篇内容能帮你少走一些我走过的弯路也欢迎你按我上面的框架先拿一个小场景把这个闭环跑通再慢慢拓展到更复杂的业务里去。