首页/新闻资讯/正文详情

从零手搓AI工程:告别调包,掌握核心链路与排查能力

发布时间:2026/11/3 13:07:50 来源:云帆数科 栏目:资讯中心
从零手搓AI工程:告别调包,掌握核心链路与排查能力
1. 从零搭建AI工程能力为什么“手搓”比调包更值得投入这两年AI应用层的工具链成熟得吓人LangChain、LlamaIndex、各种Agent框架几乎把“调用大模型”这件事压缩成了几行代码。但我观察到一个挺有意思的现象越是能快速跑通Demo的人越容易在真实项目里翻车。原因不复杂——框架帮你屏蔽了细节也顺便屏蔽了你对细节的理解。一旦线上出现幻觉失控、检索召回率暴跌、Token成本失控你连从哪一层开始排查都不知道。ai-engineering-from-scratch这个方向说白了就是反其道而行不急着上框架先把AI工程里那些绕不开的核心环节用最原始的方式亲手实现一遍。它解决的不是“怎么快速做个聊天机器人”而是“当聊天机器人不工作时你知不知道问题出在哪”。适合谁看适合已经会用API调模型、但总觉得自己在“黑盒上跳舞”的开发者也适合想从传统后端/算法岗转向AI工程岗、需要补全工程直觉的人。我自己走过这条路最开始也是pip install一把梭后来被一个检索精度问题折磨了整整两天才发现问题出在文本分块的边界处理上——而那个分块逻辑框架里是封装好的我从来没看过。从那以后我就养成了一个习惯任何核心链路先用最朴素的方式实现一版再决定要不要换框架。这篇文章就把这套“从零搭建”的思路和实操细节完整拆开讲。2. 整体设计思路把AI工程拆成可独立验证的积木2.1 为什么选择“从零实现”而不是“直接上框架”先说清楚一个前提从零实现不等于拒绝框架。它的真正价值在于建立分层认知。一个典型的AI应用从用户输入到最终输出中间至少经过这几层输入预处理、检索/工具调用、上下文组装、模型推理、输出后处理。框架把这几层揉成了一个chain你调用的时候是一整条出问题的时候也是一整条很难定位。从零实现的核心思路是每一层都做成可单独测试、可单独替换的模块。比如检索层你先用最朴素的余弦相似度实现一版跑通了、知道它的瓶颈在哪了再换成向量数据库。这样你对“为什么要用向量数据库”是有体感的而不是因为教程这么说。另一个考量是成本可控。框架往往默认帮你做很多事比如自动重试、自动截断、自动加系统提示词这些都会消耗Token。从零实现时你会清楚地知道每一次API调用花了多少钱哪些调用是必要的哪些是框架的“隐形开销”。我实测过一个场景同样的任务去掉框架的自动重试和冗余提示词后Token消耗降了将近四成。2.2 模块划分与依赖关系我习惯把整个系统拆成五个核心模块依赖关系是单向的从下往上基础层模型调用封装、Token计数、重试与超时控制数据层文档加载、文本分块、向量化、向量存储与检索编排层提示词模板、上下文组装、多轮对话状态管理应用层具体业务逻辑比如问答、摘要、分类观测层日志、指标、链路追踪这个划分的好处是每一层都可以独立测试。比如你想验证分块策略对检索效果的影响只需要替换数据层里的分块模块其他层完全不用动。这种“可插拔”的设计是从零实现最大的工程收益。提示不要一上来就追求模块划分完美。我最初把编排层和应用层混在一起后来发现改一个提示词要动业务代码非常痛苦。建议第一版就先按“数据进、模型出”的粗粒度分跑通后再细化。2.3 技术选型的取舍逻辑从零实现时选型的原则是“最小依赖”。能用标准库就不装第三方包能用一个包解决就不装两个。比如向量化早期我直接用numpy手写余弦相似度数据量小的时候完全够用而且调试起来一目了然。等到数据量上万条、检索延迟明显了再引入faiss或向量数据库。模型调用这块我建议先封装一个统一的LLMClient接口把不同厂商的API差异屏蔽掉。这样后面换模型时业务代码不用改。接口设计上至少包含三个方法complete单轮补全、chat多轮对话、embed向量化。参数上暴露temperature、max_tokens、top_p这几个最常用的就行别一上来就封装几十个参数用不到还增加理解成本。3. 核心细节解析那些框架不会告诉你的关键点3.1 文本分块检索质量的隐形杀手文本分块看起来简单实际上是最容易埋坑的地方。框架通常提供RecursiveCharacterTextSplitter这类工具默认按字符数切但默认参数往往不适合你的数据。我踩过的坑是按固定字符数切分结果把一句完整的话从中间切断检索出来的片段语义不完整模型拿到后答非所问。正确的做法是按语义边界切分。具体操作上我通常按这个优先级处理先按段落切段落太长再按句子切句子还太长才按字符切。句子切分可以用简单的标点规则中文按。英文按.!?注意处理省略号和引号的情况。分块大小也需要根据场景调。做问答检索时块大小建议在200到500字之间太小则上下文不足太大则噪声多、检索精度下降。还有一个技巧是重叠切分相邻块之间保留10%到20%的重叠内容避免关键信息刚好落在边界上被切断。我实测下来重叠比例设在15%左右召回率和精度比较平衡。注意分块前一定要做文本清洗。PDF提取的文本经常有多余换行、页眉页脚、乱码字符这些噪声会严重影响向量化质量。我一般会先做一轮正则清洗去掉连续空白、特殊符号再进入分块流程。3.2 向量化与相似度计算别迷信“越大越好”向量化的核心是选模型。常见的有两类一类是通用文本嵌入模型一类是专门针对检索优化的模型。选型时不要只看维度维度高不代表效果好。我试过用高维模型做短文本检索效果反而不如中等维度的检索专用模型原因是高维空间里短文本的区分度反而被稀释了。相似度计算上余弦相似度是最常用的但它有个前提向量要归一化。如果忘了归一化点积的结果会受向量模长影响导致长文本天然得分高。我建议在向量入库前统一做L2归一化这样后续用点积或余弦都等价计算还更快。还有一个容易被忽略的点是查询改写。用户的问题往往很短直接拿去检索效果一般。我通常会在检索前做一步查询扩展比如用模型把问题改写成几个相关表述分别检索后合并结果。这一步能明显提升召回率代价是多几次模型调用需要权衡。3.3 上下文组装Token预算的精细管理上下文组装是把检索结果、对话历史、系统提示词拼成一个完整提示词的过程。这里最大的约束是模型的上下文窗口。很多人直接把检索到的所有内容塞进去结果要么超限被截断要么Token成本爆炸。我的做法是先算预算再组装。假设模型窗口是8K Token系统提示词占500对话历史占1000输出预留1000那么留给检索内容的只有5500。然后按相关性排序从高到低填充填满为止。这样既保证不超限又保证最相关的内容一定在。对话历史的管理也有讲究。不是所有历史都需要保留我通常只保留最近几轮更早的做摘要压缩。摘要用模型生成控制在200字以内放在历史最前面。这样既保留了长期上下文又不会占用太多窗口。4. 实操过程从零搭建一个可运行的问答系统4.1 环境准备与基础封装先建一个干净的项目结构我习惯这样组织ai-from-scratch/ core/ llm_client.py token_counter.py data/ loader.py splitter.py embedder.py store.py pipeline/ retriever.py assembler.py qa.py config.pyllm_client.py里封装统一调用接口关键是加超时和重试。超时设30秒重试最多3次每次间隔指数退避。重试只针对网络错误和限流错误参数错误不重试否则会浪费调用次数。token_counter.py用tiktoken做精确计数不同模型对应不同的编码器这个要对应好。计数不准会导致预算算错要么超限要么浪费。4.2 数据加载与分块实现加载器要支持多种格式我至少实现了纯文本、Markdown、PDF三种。PDF用pypdf提取注意处理加密和扫描件的情况扫描件需要走OCR这个另说。分块器我写了一个SemanticSplitter核心逻辑是def split(text, max_size400, overlap60): paragraphs text.split(\n\n) chunks [] current for p in paragraphs: if len(current) len(p) max_size: current p \n\n else: if current: chunks.append(current.strip()) if len(p) max_size: sentences split_sentences(p) # 按句子累积超限则切分 ... else: current p \n\n if current: chunks.append(current.strip()) # 添加重叠 return add_overlap(chunks, overlap)这段代码的关键是先按段落、再按句子的降级策略以及最后的重叠处理。重叠不是简单复制而是取前一块的尾部若干字符拼到后一块开头保证语义连续。4.3 向量化与检索实现向量化模块先做归一化再存成numpy数组。检索时计算查询向量与所有文档向量的点积取Top-K。数据量小的时候这样完全够用我实测一万条以内单次检索在50毫秒左右。def retrieve(query_vec, doc_vecs, top_k5): scores doc_vecs query_vec # 已归一化点积即余弦 idx np.argsort(scores)[::-1][:top_k] return [(i, scores[i]) for i in idx]这里有个优化点如果文档量大可以先用faiss建索引把暴力检索换成近似检索。但近似检索有精度损失需要评估召回率是否可接受。我的经验是数据量超过五万条再考虑换否则暴力检索的精度优势更值得保留。4.4 提示词组装与模型调用组装器负责把检索结果、历史、系统提示拼成最终提示词。我用的模板大致是[系统] 你是一个基于给定资料回答问题的助手。只使用资料中的信息资料中没有的不要编造。 [历史摘要] {summary} [最近对话] {recent_turns} [参考资料] {context} [用户问题] {question}关键约束是“只使用资料中的信息”这一句能显著降低幻觉。但要注意如果检索没召回相关内容模型会直接说“资料中没有”这时候需要有个兜底策略比如提示用户换个问法或者触发联网搜索如果业务允许。模型调用时temperature设0.1到0.3之间问答场景不需要太高的创造性。max_tokens根据预期答案长度设一般500到1000够用。5. 常见问题与排查技巧实录5.1 检索召回不准的排查路径召回不准是最常见的问题排查要按顺序来。先看分块是否合理把检索到的块打印出来人工判断语义是否完整。如果块本身就不完整问题在分块层。再看向量化模型是否适合你的领域通用模型在专业领域可能表现差需要换领域微调过的模型。最后看查询本身用户问题太短或太模糊时加查询改写。我整理了一个速查表现象可能原因排查方法解决方向召回内容不相关分块过大含噪声打印块内容人工检查减小块大小增加重叠召回内容不完整分块切断语义检查块边界改按语义边界切分相关文档没召回向量模型不匹配换模型对比测试用领域适配的嵌入模型短查询效果差查询信息量不足对比改写前后加查询扩展5.2 Token超限与成本失控的处理Token超限通常是因为上下文组装没做预算控制。解决方法是先算可用预算再按相关性填充。成本失控则要检查是否有不必要的调用比如重复检索、冗余的模型调用。我建议加一个调用日志记录每次调用的输入输出Token数和费用定期review。提示模型调用失败重试时要确保不会重复计费。有些厂商的重试是重新计费的这个要在封装层做好幂等控制。5.3 模型输出不稳定的应对输出不稳定表现为同样的问题有时答得好有时答得差。原因通常是temperature太高或者检索结果波动。先把temperature降到0.1如果还不行检查检索是否稳定。检索不稳定往往是向量化或索引的问题比如归一化没做、索引没更新。还有一个隐蔽原因是提示词里的示例few-shot顺序影响输出。如果用了few-shot示例的顺序和数量都会影响结果建议固定下来不要随机打乱。6. 从零实现后的进阶方向把基础链路跑通后你会对每个环节的瓶颈有清晰认知这时候再引入框架或优化就有针对性了。比如检索慢你知道是向量计算的问题可以换faiss上下文组装复杂可以引入模板引擎多轮对话状态难管理可以上状态机。我个人的体会是从零实现最大的收获不是代码本身而是排查问题的能力。当系统出问题时你能沿着数据流一层层往下查而不是对着框架的报错发呆。这种能力在真实项目里比会调几个API值钱得多。后续可以扩展的方向包括加入缓存层减少重复计算、做A/B测试对比不同分块策略、引入评估指标量化检索和生成质量。每一步扩展都建立在你对基础链路的理解之上不会变成“为了用而用”。最后分享一个小技巧每次改动核心模块后跑一遍固定的测试集记录检索命中率和答案质量分。这样你能清楚知道每次改动是变好了还是变差了避免凭感觉优化。我维护了一个50条问题的测试集覆盖常见和边界场景每次改动后跑一遍几分钟就能得到反馈。这个习惯帮我避免了好几次“以为优化了实际退步”的情况。

相关推荐

用大模型检测钓鱼邮件的工程实战:结构化信号、确定性分层与置信度校准(2026)
用大模型检测钓鱼邮件的工程实战:结构化信号、确定性分层与置信度校准(2026)

用大模型检测钓鱼邮件的工程实战:结构化信号、确定性分层与置信度校准(2026)法律红线声明:本文全部检测代码在自有测试环境与公开数据集(PhishNChips)上运行,所涉钓鱼样本均为公开基准数据或虚构… · 2026/11/3 13:07:50

Windows下Docker部署Dify的502错误解决方案
Windows下Docker部署Dify的502错误解决方案

1. 问题现象与背景分析 最近在Windows系统上通过Docker部署Dify时,不少用户反馈在install界面卡住,浏览器开发者工具显示step请求返回502错误。这种情况通常发生在首次安装或升级过程中,表现为安装向导无法正常跳转到下一步,控制台… · 2026/11/3 13:07:50

解决Windows Docker部署Dify时502错误的完整指南
解决Windows Docker部署Dify时502错误的完整指南

1. 问题现象与初步排查 最近在Windows系统上部署Dify时遇到了一个典型问题:Docker容器启动后,在install界面卡住,前端页面发起step请求返回502错误。这个问题看似简单,但涉及Docker网络、后端服务健康检查、代理配置等多个环节。经… · 2026/11/3 13:07:50

ADS Ptolemy协同仿真:EDGE功放ACPR/EVM/PAE评估与避坑
ADS Ptolemy协同仿真:EDGE功放ACPR/EVM/PAE评估与避坑

简介:一份面向射频与通信电路设计者的Keysight ADS应用笔记,聚焦功率放大器的ACPR、EVM与PAE仿真评估,适合需要验证调制信号频谱泄漏、调制精度和效率指标的中高级读者。压缩包仅含1个PDF,大小约1.8MB,内容以应用说明与… · 2026/11/3 13:41:34

机器人决策皮层如何做到可算账:Jev在边缘SoC上的工程实践
机器人决策皮层如何做到可算账:Jev在边缘SoC上的工程实践

机器人做决策这件事,很多人第一反应是"上大模型"或者"堆规则",但真正落到产线、落到移动底盘上,最难的其实不是"能不能决策",而是"这个决策能不能算清楚账"——每一步动作消耗多少算力、… · 2026/11/3 13:41:34

小米相机预设包导入全攻略:全机型通用教程与避坑指南
小米相机预设包导入全攻略:全机型通用教程与避坑指南

手机摄影圈里有个挺有意思的现象,很多人手里拿着硬件素质相当不错的设备,拍出来的照片却总感觉“差口气”,色彩平淡、层次感弱,发到社交平台上跟别人的作品一比,差距肉眼可见。这两年“徕卡一瞬”这类相机预设包在摄影… · 2026/11/3 13:41:34

生成式AI如何革新机器人设计流程
生成式AI如何革新机器人设计流程

1. 生成式AI与机器人设计的融合趋势 机器人设计领域正在经历一场由生成式AI驱动的革命。过去三年里,我们看到越来越多的研究团队将扩散模型、Transformer架构等生成式AI技术应用于机器人概念设计阶段。这种融合不仅改变了传统机器人开发的流程,更在创意激… · 2026/11/3 13:41:34

WorkBuddy AI工作台从入门到精通:Skill机制、规则配置与实战避坑指南
WorkBuddy AI工作台从入门到精通:Skill机制、规则配置与实战避坑指南

1. 为什么我要认真聊聊 WorkBuddy 这个 AI 工作台第一次接触 WorkBuddy 是在一个做企业数字化的朋友那里。他当时丢给我一句话:“你把它当成一个能自己动手干活的 AI 同事,而不是一个只会聊天的机器人。”这句话基本概括了 WorkBuddy 的定位——腾讯推出… · 2026/11/3 13:41:34

Vue集成pdf.js实现前端发票预览技术方案
Vue集成pdf.js实现前端发票预览技术方案

1. 项目概述:Vue集成pdf.js实现前端发票预览在B端系统开发中,发票预览是常见的业务需求场景。传统方案通常采用后端渲染图片或调用本地PDF阅读器的方式,但这些方案存在响应慢、兼容性差等问题。基于pdf.js的前端PDF渲染方案,能够实… · 2026/11/3 13:40:31

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码