ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从零搭建AI工程体系:避开调包陷阱的完整实践指南

从零搭建AI工程体系:避开调包陷阱的完整实践指南 1. 从零搭建AI工程体系为什么我劝你别一上来就调包这两年“AI工程”这个词被说得太多了多到有点变味。打开任何一个技术社区满屏都是“三行代码调用大模型”“十分钟搭建RAG”“零基础微调自己的模型”。我不否认这些工具确实把门槛拉低了但如果你真打算把AI能力落到一个能扛住真实流量的业务里光会调API是远远不够的。ai-engineering-from-scratch这个标题之所以值得单独拿出来聊是因为它戳中了一个被大多数人跳过的环节——从底层把AI工程这条链路自己走一遍。我自己带过几个从零起步的AI项目也见过太多团队在“能跑demo”和“能上生产”之间反复摔跤。摔跤的原因往往不是模型不够强而是工程链路里那些不起眼的地方出了问题数据管道断了、推理服务的内存泄漏了、向量检索的召回率莫名其妙掉了、评测集和线上分布对不上了。这些问题调包调不出来只有你亲手从零搭过一遍才知道每个环节的边界在哪里。这篇内容适合三类人看。第一类是有一定编程基础、想系统理解AI工程全貌的开发者你可能已经会写Python但对“一个AI系统到底由哪些部分组成”还没有完整的认知。第二类是正在做AI项目、但总觉得哪里不对劲的工程师你手里的东西能跑但你不确定它是不是“对”的。第三类是对AI感兴趣、想找一个靠谱路径入门的技术爱好者你不想只停留在调包层面想真正搞明白背后的机制。我会按照一条完整的工程链路来讲从环境与依赖管理开始到数据处理管道、模型推理服务、向量检索与RAG、评测体系、可观测性最后聊部署和迭代。每一块我都会说清楚“为什么这么设计”“关键参数怎么定”“我踩过哪些坑”。这不是一篇教程更像是一个做过几轮的人坐下来跟你复盘。2. 整体架构设计先想清楚边界再动手写代码2.1 从零搭建的核心思路与分层原则很多人一上来就开始写推理代码结果写到一半发现数据格式不对、依赖版本冲突、评测没法做只能推倒重来。我的建议是动手之前先把整个系统分成几层每层只负责一件事层与层之间通过明确的接口通信。这样做的好处是任何一层出问题你可以单独替换或调试而不会牵一发动全身。我通常会把一个AI工程系统分成五层。最底层是基础设施层包括运行环境、依赖管理、硬件资源。往上是数据层负责数据的采集、清洗、切分、存储。再往上是模型层包括推理服务、模型版本管理、提示词管理。然后是应用层也就是RAG、Agent、工作流这些面向业务逻辑的部分。最上面是观测与评测层负责监控、日志、评测、反馈闭环。这个分层不是教科书上的标准答案而是我在实际项目里摸索出来的。它的核心原则是每一层只依赖它下面那一层不允许跨层调用。比如应用层不能直接去读原始数据文件必须通过数据层提供的接口。这样做看起来麻烦但当你的系统从demo长成一个有几十个模块的项目时你会发现这种约束救了你无数次。提示分层不是为了好看是为了让每一层的变更不会波及全局。你在数据层换一个清洗逻辑不应该影响模型层的推理代码。2.2 技术选型背后的取舍逻辑选型这件事我的原则是“用最无聊的技术”。什么意思就是优先选那些社区成熟、文档齐全、你团队里有人用过的方案而不是最新最酷的。AI工程这个领域变化太快了如果你在基础设施上也追新你会发现自己一半的时间在解决工具本身的bug而不是业务问题。具体来说运行环境我推荐用容器化方案把依赖锁死在一个镜像里。Python的依赖管理是个老大难pip、conda、poetry各有各的坑。我的做法是用pyproject.toml声明依赖用锁文件固定版本然后在容器里安装。这样本地开发和线上部署的环境是一致的不会出现“我本地能跑”的经典问题。推理框架的选择要看你的场景。如果你只是调用外部API那没什么好选的做好超时和重试就行。如果你要自己部署模型vLLM和TGI是目前比较主流的选择前者吞吐量高后者对HuggingFace生态支持好。如果你要做本地轻量推理llama.cpp系列是不错的选择。这里没有银弹关键看你的延迟要求、并发量和硬件条件。向量数据库这块我踩过的坑最多。早期我用过FAISS它轻量、快但不支持持久化和分布式适合原型阶段。后来换到Milvus和Qdrant前者功能全但运维重后者轻量且API友好。我的建议是如果你的数据量在百万级以下Qdrant或pgvector就够了别一上来就上重型方案。层级推荐方案适用场景主要坑点环境管理容器 锁文件所有场景镜像体积膨胀推理服务vLLM / TGI自部署模型显存管理复杂向量检索Qdrant / pgvector百万级以下索引参数调优评测框架自建 开源工具所有场景评测集污染2.3 目录结构与工程规范从零搭建的另一个好处是你可以从一开始就把工程规范定好。我见过太多项目代码写到后面目录乱成一锅粥新人接手要花一周才能找到入口。我的做法是在项目根目录下按层划分文件夹每个文件夹里再按功能细分。一个典型的目录结构大概是这样infra/放容器配置和部署脚本data/放数据处理管道models/放推理服务和模型管理app/放业务逻辑eval/放评测代码observability/放监控和日志配置。每个文件夹里都有一个README.md说明这一层的职责和接口。命名规范也很重要。我坚持用动词名词的方式命名函数比如load_documents、clean_text、build_index而不是documents、text这种含糊的名字。变量名要能看出它的类型和用途比如raw_docs、chunked_docs、embedded_vectors。这些细节看起来琐碎但当你的代码超过几千行它们决定了你还能不能读懂自己三个月前写的东西。3. 数据管道AI工程里最脏最累但最重要的部分3.1 数据采集与清洗的实操要点数据是AI系统的地基但大多数人把80%的精力花在模型上只给数据留了20%。这是个巨大的误区。我的经验是一个AI系统的效果上限在数据阶段就基本决定了。模型再强喂进去的是垃圾出来的也是垃圾。数据采集的第一步是明确数据源。你的数据可能来自文档、网页、数据库、API、日志等等。每种来源的处理方式不同。文档类数据要处理格式转换PDF、Word、Markdown各有各的解析库。网页数据要处理HTML清洗和正文提取。数据库数据要处理字段映射和去重。清洗环节是最考验耐心的。我通常会把清洗分成几个步骤去重、去噪、格式化、分块。去重不只是完全相同的文本还包括近似重复的内容这时候要用到MinHash或SimHash这类算法。去噪要处理HTML标签、特殊字符、乱码、页眉页脚。格式化要统一编码、统一换行符、统一标点。分块要根据语义边界切分而不是简单地按字数切。注意分块策略直接影响后续的检索效果。按固定字数切分会切断语义导致检索到的片段不完整。我推荐按段落或标题层级切分再根据模型上下文长度做二次合并。3.2 分块策略与元数据设计分块这件事看起来简单实际上有很多讲究。块太大检索时噪声多模型处理慢。块太小语义不完整检索到的信息碎片化。我的经验值是对于中文文本每块控制在300到500字之间比较合适英文可以到500到800词。但这只是个起点具体要看你的文档类型和查询模式。更重要的是元数据设计。每个块除了文本内容还应该带上来源、标题、层级、时间、作者等信息。这些元数据在检索时可以用于过滤和排序。比如用户问的是某个产品的最新政策你就可以用时间元数据过滤掉旧版本。用户问的是某个章节的内容你就可以用层级元数据缩小检索范围。元数据的设计要遵循“够用就好”的原则。我见过有人给每个块加了二十几个字段结果维护成本极高实际用到的没几个。我的建议是先确定你的检索场景需要哪些过滤条件再倒推需要哪些元数据。通常来源、标题、时间、类型这四个字段能覆盖大部分需求。3.3 数据版本管理与可复现性数据版本管理是很多人忽略的一环。你的模型效果变差了你怎么知道是模型的问题还是数据的问题如果没有数据版本管理你根本无从查起。我的做法是每次数据管道跑完都给输出打一个版本号记录输入数据的哈希、处理参数的配置、输出数据的统计信息。这个版本号要贯穿整个系统。模型训练时记录用了哪个数据版本评测时记录用了哪个评测集版本线上推理时记录用了哪个索引版本。这样当问题出现时你可以沿着版本号一路回溯快速定位是哪一环变了。可复现性还要求你的数据管道是确定性的。同样的输入跑两次应该得到同样的输出。这意味着你要固定随机种子、固定排序规则、固定分块边界。任何引入随机性的地方都要显式地控制住。这一点在调试阶段尤其重要否则你连问题都复现不了。4. 模型推理服务从能跑到能扛的关键细节4.1 推理服务的封装与接口设计把模型跑起来不难难的是让它稳定地跑、高效地跑、可维护地跑。我见过太多项目推理代码和业务代码混在一起改一个提示词要重新部署整个服务。正确的做法是把推理服务封装成一个独立的模块对外暴露清晰的接口。接口设计上我推荐用HTTP或gRPC。HTTP简单通用适合大多数场景。gRPC性能好适合高并发低延迟的场景。接口的输入输出要用结构化的格式比如JSON并且要有明确的字段定义和校验规则。不要用裸字符串传参那样后期维护是灾难。推理服务内部要做好几件事请求队列管理、批处理、超时控制、错误重试、限流。请求队列管理是为了应对突发流量批处理是为了提高GPU利用率超时控制是为了防止慢请求拖垮整个服务错误重试是为了应对偶发的网络或硬件问题限流是为了保护后端不被压垮。这些机制缺一不可。4.2 批处理与显存管理的参数计算批处理是提升推理吞吐量的关键手段但批大小不是越大越好。批太大会导致显存溢出批太小又浪费算力。怎么定这个值我的方法是先估算单个请求的显存占用再根据可用显存反推最大批大小然后留20%的余量。单个请求的显存占用主要包括模型权重、KV Cache和中间激活值。模型权重是固定的比如一个7B参数的模型用FP16存储大概占14GB。KV Cache跟序列长度和批大小成正比公式大概是2 * 层数 * 头数 * 头维度 * 序列长度 * 批大小 * 精度字节数。中间激活值跟模型结构有关通常占比较小。举个例子假设你的GPU有40GB显存模型权重占14GB系统预留2GB剩下24GB给KV Cache和激活值。如果序列长度是2048每请求的KV Cache大概是1GB那理论上最多能批24个请求。但实际中要考虑碎片和波动我一般会设成16左右然后根据监控数据动态调整。提示批大小不是固定值要根据实际负载动态调整。我通常会在服务里加一个自适应逻辑根据队列长度和显存使用率自动扩缩批大小。4.3 提示词管理与版本控制提示词是AI工程里最容易被忽视的“代码”。很多人把提示词硬编码在业务逻辑里改一次要发一次版。正确的做法是把提示词当成配置来管理独立存储、独立版本、独立测试。我的做法是每个提示词模板都有一个唯一ID和版本号存储在配置文件或数据库里。业务代码通过ID引用提示词而不是直接写字符串。这样改提示词不需要改代码只需要更新配置。同时每次改提示词都要记录变更原因和测试结果方便回溯。提示词的测试也很重要。我会为每个提示词准备一组测试用例覆盖正常情况、边界情况和异常情况。每次修改提示词都要跑一遍测试确保没有回归。这个习惯帮我避免了很多“改了一个词效果全崩”的事故。5. 向量检索与RAG召回质量决定一切5.1 向量化模型的选择与评估RAG系统的效果七成看召回三成看生成。而召回的质量又很大程度上取决于向量化模型。选向量化模型不能只看榜单要看你的数据分布和查询模式。通用榜单上的第一名在你的垂直领域可能表现平平。我的做法是先准备一批真实的查询和对应的正确文档然后拿几个候选模型分别跑一遍看召回率、MRR、NDCG这些指标。不要只看一个指标要综合看。召回率高但排序差的模型用户体验也不好。另外要注意模型的维度维度越高存储和计算成本越大但效果不一定线性提升。中文场景下我试过不少模型有些在通用语料上表现好但在专业术语上就拉胯。这时候可以考虑用领域数据做微调或者用混合检索的方式把关键词检索和向量检索结合起来。混合检索往往比单一向量检索更稳尤其是在专有名词和数字查询上。5.2 索引构建与检索参数调优索引构建不是把向量塞进去就完事了。不同的索引类型有不同的适用场景。扁平索引精度最高但速度慢适合小数据集。IVF索引速度快但精度有损适合大数据集。HNSW索引在速度和精度之间平衡得比较好是我最常用的。调优的关键参数有几个nlist控制聚类数量nprobe控制搜索的聚类数ef_search控制HNSW的搜索范围。这些参数直接影响召回率和延迟。我的调优方法是先固定其他参数单独调一个画出召回率-延迟曲线找到拐点。通常nprobe设在nlist的10%到20%之间比较合适。还有一个容易被忽视的点是重排序。向量检索出来的Top-K结果可以用一个交叉编码器做重排序把最相关的排到前面。这一步能显著提升最终效果代价是增加一点延迟。我的经验是如果延迟预算允许重排序几乎总是值得做的。5.3 RAG流程的组装与上下文管理RAG的流程看起来简单检索、拼接、生成。但每一步都有细节。检索阶段要决定检索多少个文档太多会超出上下文长度太少可能漏掉关键信息。我的做法是检索一个较大的候选集比如20个然后重排序取前5个。拼接阶段要注意上下文的组织方式。不要把检索到的文档随便堆在一起要按相关性排序并且加上来源标注。这样模型在生成时能更好地利用信息也方便用户溯源。如果文档之间有冲突要在提示词里说明如何处理冲突。上下文管理还要考虑长度限制。不同模型的上下文窗口不同你要根据模型的能力来裁剪。我的做法是先算好系统提示词、用户查询、检索文档各占多少token然后动态调整检索文档的数量。如果超了就按相关性从低到高丢弃。6. 评测体系没有评测就没有迭代6.1 评测集构建与标注规范评测是AI工程里最容易被跳过的一环也是最不该跳过的一环。没有评测你根本不知道你的改动是变好了还是变差了。我见过太多团队凭感觉调参结果越调越差。评测集要覆盖真实场景的分布。不要只用简单问题要包含难例、边界例、对抗例。标注规范要明确什么算正确、什么算部分正确、什么算错误都要有清晰的定义。标注人员要经过培训保证一致性。我通常会做双人标注然后计算一致性指标低于阈值就重新标注。评测集的规模不用很大几百条高质量的就够用了。关键是质量不是数量。我见过有人搞了几万条评测数据结果一半是重复的一半是标注错误的这种评测集还不如没有。6.2 自动化评测与人工评测的结合自动化评测适合快速迭代人工评测适合深度分析。我的做法是日常迭代用自动化评测每周做一次人工抽检。自动化评测可以用规则匹配、模型打分、指标计算等方式。人工评测则关注那些自动化覆盖不到的地方比如语气、逻辑、安全性。自动化评测的一个坑是评测集污染。如果你用同一个评测集反复调参你的模型会过拟合到这个评测集上。解决办法是准备一个“开发集”用于日常迭代一个“测试集”只在关键节点用。测试集不能频繁看看了就失去意义了。人工评测的另一个价值是发现新问题。自动化评测只能测你想到的东西人工评测能发现你没想到的。我每次人工评测都会记录新发现的问题类型然后补充到自动化评测里。这样评测体系会越来越完善。6.3 线上反馈闭环的搭建线上反馈是最真实的评测数据。用户的点击、停留、追问、点赞、举报都是信号。要把这些信号收集起来定期分析反哺到评测集和模型迭代里。我的做法是在应用层埋点记录每次交互的输入、输出、用户行为。然后定期抽样人工判断质量把有问题的样本加入评测集。同时对于用户明确反馈不好的case要单独分析原因是检索问题、生成问题还是提示词问题。反馈闭环的关键是快。从用户反馈到问题定位到修复上线周期越短越好。我通常会把反馈处理流程自动化用户反馈后自动分类、自动通知、自动创建任务。这样能把响应时间从几天缩短到几小时。7. 可观测性与部署让系统在真实环境里活下去7.1 日志、指标与追踪的三位一体系统上线后你需要知道它在干什么。日志记录事件指标记录趋势追踪记录链路。三者缺一不可。日志要结构化用JSON格式包含时间戳、级别、模块、请求ID、关键参数。不要用print要用日志库。日志级别要合理DEBUG用于开发INFO用于正常事件WARN用于异常但可恢复ERROR用于需要关注的错误。指标要覆盖几个维度延迟、吞吐、错误率、资源使用率。延迟要看P50、P95、P99不要只看平均值。吞吐要看QPS和并发数。错误率要分类是超时、是模型错误还是业务错误。资源使用率要看CPU、内存、GPU、显存。追踪要能串起一次请求的完整链路。从用户请求进来到检索、到推理、到返回每一步的耗时和状态都要记录。这样当延迟变高时你能快速定位是哪一环慢了。7.2 灰度发布与回滚机制AI系统的更新不能一把梭。模型换了、提示词改了、索引重建了都可能引入回归。灰度发布是控制风险的关键手段。我的做法是新版本先放1%的流量观察指标。如果指标正常逐步扩大到5%、10%、50%、100%。每一步都要有明确的观察期和回滚条件。回滚条件要提前定好比如错误率超过阈值、延迟超过阈值、人工抽检发现严重问题。回滚机制要能快速执行。我通常会把模型、提示词、索引都做成可切换的配置回滚只需要改配置不需要重新部署。这样能把回滚时间从几十分钟缩短到几秒钟。7.3 成本控制与资源优化AI系统的成本主要来自推理和存储。推理成本跟模型大小、请求量、批处理效率有关。存储成本跟向量维度、数据量、索引类型有关。控制成本的手段有几个。第一是模型分级简单请求用小模型复杂请求用大模型。第二是缓存相同或相似的请求直接返回缓存结果。第三是批处理把多个请求合并处理。第四是量化用INT8或INT4降低显存占用和计算量。我的经验是缓存能省掉30%到50%的推理成本尤其是对于重复查询多的场景。批处理能提升2到5倍的吞吐。量化能降低一半的显存但可能损失一点效果要评估后再用。8. 常见问题与排查技巧实录8.1 检索效果差的排查路径检索效果差是最常见的问题。排查时我会按这个顺序走先看查询本身有没有问题再看向量化模型适不适合再看索引参数有没有调好最后看分块策略合不合理。查询问题包括拼写错误、语义模糊、超出知识范围。向量化模型问题包括维度不匹配、领域不适应、版本不一致。索引参数问题包括nprobe太小、ef_search太小、索引没重建。分块问题包括块太大、块太小、边界切错。我遇到过一个典型案例用户问“最新的退款政策是什么”检索总是返回旧政策。排查后发现是元数据里没有时间字段检索时无法按时间过滤。加上时间元数据后问题解决。这个案例说明很多检索问题不是模型问题是数据问题。8.2 推理延迟高的优化手段推理延迟高先定位是哪一段慢。是网络传输慢、是排队慢、是推理慢还是后处理慢。定位方法就是看追踪数据每一段的耗时都记录下来。如果是排队慢说明并发太高要加机器或限流。如果是推理慢要看是模型太大还是批处理没做好。如果是后处理慢要看是不是重排序或格式化耗时太多。优化手段包括模型量化、批处理、缓存、异步处理、预计算。我通常会先上缓存和批处理这两个见效最快。如果还不够再考虑量化和模型替换。8.3 效果不稳定的归因方法效果不稳定时好时坏是最难排查的问题。可能的原因有数据分布变化、模型版本不一致、提示词被改动、缓存污染、并发竞争。我的排查方法是先固定所有变量只改一个看效果变化。如果固定不了就加详细的日志记录每次请求的完整上下文。然后对比好case和坏case的差异找到共同点。我遇到过一次效果波动最后发现是缓存key设计有问题不同用户的请求命中了同一个缓存。修复key的设计后问题解决。这个教训是缓存key要包含所有影响输出的变量不能偷懒。问题类型典型表现排查方向解决手段检索差召回不相关查询、模型、索引、分块换模型、调参数、改分块延迟高响应慢网络、排队、推理、后处理缓存、批处理、量化效果波动时好时坏数据、版本、提示词、缓存固定变量、加日志、对比分析成本高账单超预算模型、请求量、存储分级、缓存、量化8.4 我踩过的几个典型坑第一个坑是依赖版本冲突。早期我没用容器本地装了一堆包结果线上部署时各种版本不兼容。后来全部容器化问题解决。教训是环境一致性要从第一天就抓。第二个坑是评测集泄露。我用同一个评测集调了两周参数效果看起来很好上线后一塌糊涂。后来才知道模型过拟合到评测集了。教训是评测集要分开发集和测试集测试集不能频繁看。第三个坑是提示词硬编码。提示词写在代码里改一次要发一次版效率极低。后来把提示词抽出来做成配置改提示词不用发版。教训是提示词是配置不是代码。第四个坑是没有监控。系统上线后没有监控出了问题只能等用户反馈。后来加了日志、指标、追踪问题能在几分钟内发现。教训是可观测性不是可选项是必选项。9. 迭代与扩展从能用到好用的路系统上线只是开始不是结束。真正的挑战在于持续迭代。迭代的方向来自三个方面用户反馈、评测结果、线上监控。用户反馈告诉你哪里不好用评测结果告诉你哪里不够准线上监控告诉你哪里不够稳。迭代的节奏要控制好。太慢问题积累太快风险失控。我的做法是小改动随时发大改动走灰度。每次改动都要有明确的预期和验证方法。改完要看数据不要凭感觉。扩展的方向有几个。一是支持更多数据源二是支持更多模型三是支持更复杂的查询四是支持多模态。每扩展一个方向都要重新评估架构是否撑得住。我见过太多系统一开始设计得太窄扩展时只能推倒重来。最后分享一个小技巧定期做“故障演练”。故意关掉一个服务、故意注入延迟、故意让模型返回错误看系统能不能扛住。这种演练能暴露很多平时发现不了的问题。我每次演练都能找到几个隐患修完之后系统稳定性明显提升。这个内容后续还可以这样扩展把评测体系做成自动化的CI流程每次提交代码自动跑评测把提示词管理做成可视化的平台让非技术人员也能调把可观测性做成统一的dashboard一屏看全所有指标。这些都是我下一步打算做的事等做完再跟大家复盘。
返回列表