尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
AI工程从零开始:从LLM原理到RAG实战的完整路线
2025年如果你打开招聘软件搜“AI工程师”会看到一个薪资很高、岗位很多但能力要求写得含糊不清的职位。AI工程ai-engineering到底是做什么的是熟练调大模型API的“接口工人”还是精通PyTorch的算法专家又或是能训练自己模型的炼丹师我带了几个从零转行的同事也翻过大量学习计划越来越强烈的感受是大多数人不是学不会而是被各种课程和路线图带偏了方向。今天这篇东西我想认真聊聊一条我认为最靠谱的“AI工程从零开始ai-engineering-from-scratch”路径。它不要求你数学博士毕业也不满足于把你训练成一个只会拼请求的API搬运工。它的目标是让你能把一个LLM应用从Demo一路推到真能上线的状态。不管你是后端、前端、测试还是算法出身只要会点编程愿意花几个月时间这条路基本都能走通。1. 从零开始学AI工程到底在学什么1.1 先画边界AI工程不是调API也不只是机器学习课AI工程这几年被包装得神神秘秘我接触下来觉得它其实是个交叉地带。往左看是传统软件工程往右看是机器学习底下垫着大模型推理和数据处理。真正的AI工程是把大模型技术模块化、产品化、生产化的一整套方法论。说得直白一点AI工程解决的是这样几类问题怎么把用户的自然语言问题变成高质量的大模型输入怎么在回答前塞进私有知识库里的相关资料怎么让模型稳定地调用外部工具去完成多步任务怎么把你调通的接口压到能扛住线上的流量以及怎么判断这套系统到底变好还是变坏。很多人以为学会LangChain就算入门AI工程学会调参数就算精通LLM应用这是天大的误解。LangChain只是个抽象层的工具箱它把一些常见套路封装成了链式调用但底层的数据流和状态管理仍然是你自己的工程问题。同样仅仅会用OpenAI的API发几条请求也不叫AI工程那叫“API消费者的基本功”。“from scratch”这个前缀之所以重要就是因为它强调了要回到本质去理解。你不一定要能从零手写Transformer但你得清楚token是怎么切出来的、上下文窗口意味着什么、为什么回答会瞎编、为什么某些prompt技巧有效而某些纯粹是玄学。这些底层认知决定了你遇到线上故障时是只能重启大法还是能一眼定位到问题源头。1.2 最常见的三种错误学习姿势我见过太多自学者的失败案例归纳下来就是三种固定的错误姿势。第一种是“一头扎进数学底洞”。买了一堆深度学习理论书籍从反向传播开始推导MNIST手写识别刷了三遍却始终不敢碰大模型API。这些人往往在数学里迷失了方向学了大半年偏导数最后连一个能跑的聊天机器人demo都没做过。做AI工程你的目标不是发表论文而是交付系统。Transformer里面的矩阵推导可以作为拓展阅读但绝对不该是入门第一站。第二种是“只会调接口不懂原理”。这种人熟练应用各种大模型API会用Prompt模板能接上LangChain但问一句“为什么temperature设为0.7比0.2更合适”就卡壳了。他们写的应用一旦遇到意外输入就崩因为完全依赖模型临场发挥没有结构化兜底。不懂原理调出来的参数换个模型、换个场景大概率要重新碰运气。第三种是“看教程很多做项目为零”。收藏了上百个仓库GitHub星标了一圈但自己一行端到端的业务代码都没写过。看别人做RAG觉得简单轮到自己处理脏PDF时就发现全是细节。学习AI工程的最短路径永远是带着一个真实项目往前走教程只是你的地图路还是得自己走。2. 核心知识体系拆解先把地基打结实2.1 模型原理入门token、上下文窗口、温度和注意力要做AI工程不啃论文也能懂的核心概念其实用生活类比就能讲清楚。tokenizer可以理解成“切菜工”它把一段文本切成模型能处理的最小单元。英文里一个token大概是一个单词的一部分中文里一个token大概是一个字到一个词。切法直接决定了你花多少钱、能传多少内容。很多人调接口时发现超长文本被截断就是因为没注意到token不是按“字符”算的是按“切出来的块”算的。一个中文字符在大多数模型里可能占0.6到1个token算上下文预算时千万别按字符数去估。上下文窗口就是“桌子大小”。模型一次能“看见”多少文本取决于它的context window。这就像一张桌子你摆得下多少资料模型就能参考多少资料。超出部分要么被丢弃要么需要你自己决定怎么取舍。做RAG系统时这个参数直接决定你能塞进去多少检索结果。temperature和top_p控制的是模型采样的“随机性”。想象一下猜谜游戏temperature高就像每轮都瞎猜新答案擅长发散temperature低就像把已有答案从头背一遍稳定但死板。写代码任务、结构化输出任务我基本都设成0到0.3头脑风暴、创意写作才敢把温度往高了调。注意不同模型的temperature标定口径有细微差异同一个数值在不同模型上表现可能差很多最好自己用小批量样本实测一下。注意力机制简单理解就是模型在读文章时动态给每个词分配“注意力权重”。哪些词对理解当前句话更重要就给更高的权重。Transformer革命性地取消了对文本顺序的强先验依赖直接让所有位置同时计算注意力这使得模型能在长文本里跨距离捕捉依赖关系。工程上理解到这个程度其实就够了剩下的可以交给框架。很多人会问搞清这些原理有什么用我举个亲身经历。之前排查一个线上问题发现某些用户问同样的问题同一个模型答案质量时好时坏。后来定位到是prompt里拼了太长的历史记录把模型真正的上下文窗口撑爆了早期对话被静默截断。如果你不懂上下文窗口的概念遇到这种问题只会一头雾水。2.2 应用层四件套Prompt设计、RAG、Agent与评估应用层四大件是目前AI工程最核心的工作内容。Prompt设计是入门第一步但也是最容易被神化的一步。结构化模板是有用的例如给模型固定System Role、明确输出格式、给一两个few-shot示例这能显著提高输出稳定性。思维链这种技巧也确实有效因为LLM在逐步思考时更容易保持逻辑一致性。但我要提醒的是Prompt是上限最低的优化手段。很多时候你花一周调出来的prompt不如直接换一个更强的模型或者引入检索来得效果显著。所以别陷入“prompt炼丹”的泥潭应该把它当成工程栈里的一个普通组件。RAG是检索增强生成也是现在落地最多的一类应用。它的核心思想很朴素模型不知道你私有的数据那我们就先把相关资料找出来塞进prompt里让它看着回答。这样做一是缓解幻觉二是让知识可以实时更新三是解决了私有数据怎么给模型用的问题。一个完整的RAG链路会涉及文档解析、切片、向量化、检索、重排、生成多个环节每个环节都有工程决策要做。我在后面的实操章节会详细展开。Agent是今年最热的方向之一。一个AI Agent本质上就是一个“计划加执行”循环模型先理解用户目标把它拆成子任务然后调用外部工具去执行拿到结果后不断调整计划直到完成。这里面最重要的是工具调用的可靠性以及多轮状态的管理。很多初级开发者喜欢堆一堆工具给Agent但根本没有定义清楚工具之间的依赖和异常处理结果Agent在错误路径上越走越远。工具越少越精Agent才越稳。评估是四大件里最容易被忽略却又最重要的一环。没有评估就无法量化你的Prompt修改是变好还是变坏RAG检索优化是否有效Agent流程调整是否更可靠。线上评估可以用人工抽检开发期最好的方式是把评测用例沉淀成数据集用LLM-as-judge或者传统指标来批量打分。到这里就理解了为什么很多AI工程师越往后越忙——他们在搭建自己的评估体系而不是在调东调西。2.3 部署与性能从notebook到生产环境的必经之路模型调得再漂亮部署不了等于零。AI工程的部署有两个层面。第一层是接口服务化。如果你用云端大模型API你要做的服务化很简单加个FastAPI或者Flask套壳转发就行。但要注意并发控制、超时处理、错误重试和限流逻辑这反而是最容易出问题的点。比如线上用户多了以后API偶尔返回超时你的客户端没有重试机制的话用户看到的就全是报错。另外还需要考虑成本控制同样的请求次数配合语义缓存能省下不少钱。第二层是开源模型的自部署。如果你选择用开源模型比如Qwen系列、Llama系列那就要考虑推理引擎和显存管理。推理引擎选型通常考虑vLLM它对高吞吐、长上下文的支持比较好还集成了PagedAttention等显存优化手段。本地部署还要考虑量化把模型从FP16压到INT4或者INT8显存占用能少一半以上推理速度往往还更快只是精度会略有下降。性能优化方向也很有讲究。流式输出streaming是交互体验的刚需能显著降低用户等待焦虑。批处理batching可以提升吞吐把多个请求拼成一批一起推理GPU利用率会高很多。KV Cache命中就是在缓存模型过过的历史token对多轮对话场景帮助很大很多引擎对这个量做了优化。3. 手把手实操从零搭一套RAG知识库问答系统3.1 项目定义与整体架构纯聊概念没有用我来带你把一套RAG知识库问答从零搭起来。这个项目是最典型、最落地、也最适合作为“from scratch”里程碑的练习。场景定义团队内部有一堆Word和PDF格式的文档产品经理和技术团队想做个内部问答助手员工自然语言提问系统到文档库里找答案并给出回答还要标注引用来源。整体架构分为两条链路。入库链路文档上传之后先做格式解析把文本切成适当大小的块然后逐个embedding成向量存入向量数据库同时保留原文块的索引信息。查询链路用户提问后先把问题embedding成向量去向量库做相似度检索同时用BM25做一遍关键词检索两份结果用RRF融合再到重排模型把最终最相关的几条挑出来拼进prompt模板送给大模型生成回答最后把引用来源一并返回前端。技术选型我建议按下面的思路定下来不需要一上来就上重型组件。组件推荐选项理由向量数据库起步用FAISS后续转pgvector或QdrantFAISS内存索引最简单适合学习和原型验证生产要持久化和多租户再迁移Embedding模型开源BGE-M3或bge-large-zh中文效果好、免费、可控且能离线推理LLM先接云端API做通闭环再换vLLM自部署开源模型先降低试错成本后面按预算和隐私要求切换检索策略向量检索 BM25 RRF融合单用向量会漏掉精确关键词匹配融合显著提升召回率重排用bge-reranker系列轻量模型二次筛选非常有效后端服务FastAPI异步支持好生态成熟和ML框架配合也顺选型逻辑多说一句。很多人一开始就想上重型分布式的向量数据库这其实是过度设计。你做原型验证时几千个文档、几万条chunkFAISS跑在内存里性能完全够用。等真正需要多个用户、并发写入、权限隔离的时候再迁移迁移成本可控因为你代码里抽象了检索接口。3.2 文档切分与向量化切分是整个RAG系统中最容易被低估的环节。切得太小语义不完整检索到的片段常常说不清前因后果切得太大噪声太多超过模型上下文预算而且向量表达会被稀释。我的默认起步值是chunk_size500个token左右overlap50个token。这个数值不是拍脑袋它大致对应两三个自然段落的长度既能保留局部语义又能通过overlap让段间承接不丢上下文。来看一个实际切分代码用LangChain的话很简单但我更建议先手写一个理解原理def split_text(text: str, chunk_size: int 500, overlap: int 50): # 先用简单标点把文本切成语义块 import re sentences re.split(r(?[。]), text) chunks, current [], [] current_len 0 for sent in sentences: sent sent.strip() if not sent: continue current.append(sent) current_len len(sent) if current_len chunk_size: chunk_text .join(current) # 计算overlap尾部 tail chunk_text[-overlap:] if overlap 0 else chunks.append(chunk_text) current [tail] if tail else [] current_len len(tail) if current: chunks.append(.join(current)) return chunks这段代码是个简化版本生产环境中你还会需要处理代码块、表格、列表等特殊结构。比如Markdown表格被切开之后检索到一半表格模型根本看不懂。经验是先用文档结构识别标题层级、表格区域做预分区再在区内切块不要让chunk跨过表格边界。向量化部分用sentence-transformers可以直接跑from sentence_transformers import SentenceTransformer model SentenceTransformer(BAAI/bge-m3) chunks split_text(raw_text) embeddings model.encode(chunks, normalize_embeddingsTrue) # 写入FAISS索引 import faiss import numpy as np dim embeddings.shape[1] index faiss.IndexFlatIP(dim) index.add(embeddings.astype(float32))选择BGE-M3主要是因为它在中文长文本上的表现稳定支持8192长度的输入而且多语言向量空间设计合理。注意我encode时开了normalize_embeddings这样用内积IndexFlatIP等价于余弦相似度检索结果更稳定。把这个步骤记下来这是很多教程不会提醒的细节。3.3 混合检索与重排为何能显著提升效果向量检索擅长“语义相似”匹配比如用户问“报销流程”文档里写“差旅费报销规范”语义上能被召回。但它有个短板如果query里的精确关键词比如某个系统编号、项目代号没有出现在文档里向量检索可能会把真正含这个编号的文档排在后面。BM25恰恰擅长关键词精确匹配。所以业界普遍做法是混合检索两路召回再融合。融合算法里最常用的是RRFReciprocal Rank Fusion。它的思想很直观不去比两路结果的绝对得分因为两路分数分布完全不同没法直接比较而是按排名折算出得分。排在第1位的给最大权重排名越靠后权重越小最终把两路的排名加权合并。def rrf_fusion(vector_ids, bm25_ids, k60): score {} for rank, doc_id in enumerate(vector_ids): score[doc_id] score.get(doc_id, 0) 1 / (k rank 1) for rank, doc_id in enumerate(bm25_ids): score[doc_id] score.get(doc_id, 0) 1 / (k rank 1) return sorted(score.keys(), keylambda x: score[x], reverseTrue)注意RRF里的k是平滑因子通常取60数值小会让排名靠前的文档获得更大优势。强烈建议不要改这个参数除非你有足够的实验数据支撑。实测下来用RRF融合后的Top5命中率比单用向量检索高20到30个百分点这个提升基本是白捡的。重排环节我们再上一个reranker。重排和向量检索思路不同它不是把问题和文档分别编码再算相似度而是把问题和候选文档拼在一起交给一个轻量级模型直接判定相关性。这个交叉编码方式精度高得多但速度慢所以只能对小批次比如Top50做精排。实践中我的管道是混合召回50条重排取前5条进prompt。实测中重排后首条答案正确率提升非常明显强烈建议把reranker加入RAG管道。3.4 生成环节与全链路串联检索得到候选上下文后生成环节不要简单粗暴拼接。首先要处理的是“上下文溢出”。把5条候选全塞进prompt可能超过模型窗口限制我的做法是先粗算token超过阈值就按rerank排名的权重截断保前几条。其次要写清楚prompt的系统指令明确要求模型只能基于提供的资料回答不知道说不知道。一个实际可用的prompt模板你是一名企业内部知识库问答助手。 请严格根据下面提供的资料回答问题。 如果资料中没有足够的信息请直接回复“未找到相关信息”不要尝试编造。 回答时请分点陈述关键依据并在末尾列出使用的资料编号格式为[1][2]。 资料 {context_with_sources} 问题 {question}标注引用来源是RAG工程里很重要的一环。我通常会给每一条chunk一个全局编号生成阶段让模型输出引用编号后端再根据编号去反查原文文件路径和页码。用户看到回答时能点击跳转到原始文档对应章节一下子就解决了“AI回答能不能信”的信任问题。最终把整条链路串起来核心流程大概是# 查询链路简化版 def answer_query(query: str): # 1. 混合召回 q_emb embed_model.encode(query, normalize_embeddingsTrue) vector_hits index.search(q_emb, 50) bm25_hits bm25.search(query, 50) fused rrf_fusion(vector_hits, bm25_hits) # 2. 重排精排 candidates [all_chunks[i] for i in fused[:50]] rerank_scores reranker.rank(query, candidates) top5 [candidates[i] for i, _ in rerank_scores[:5]] # 3. 拼装生成 context_with_sources format_context(top5) prompt prompt_template.format(contextcontext_with_sources, questionquery) answer llm_client.chat(prompt) return answer, top5_sources到这里一个最小可用的RAG问答系统就跑通了。我建议第一批数据不要用网上爬来的杂乱文章就用自己的工作文档几篇就好把全链路调通再慢慢扩到几百篇。先把一条路走通再追求规模化。4. 工程落地中的典型问题与避坑经验4.1 环境与依赖的坑版本地狱做AI工程环境问题可能是第一个劝退你的东西。PyTorch、CUDA、transformers、tokenizers这几个库版本相互依赖经常出现安装torch了但CUDA不可用、transformers版本太老不支持某模型架构、tokenizers和transformers不匹配导致乱码之类的问题。我的建议是第一所有实验环境用虚拟环境隔离不要图省事直接pip install到全局。第二锁定版本号不要用latest。你会发现上月还能跑的项目今天重新装就莫名其妙报错了九成原因是某个传递依赖偷偷升了级。第三有条件就上Docker把环境固化到镜像里这样任何机器上都能复现。特别是部署到服务器时镜像会把很多“在我电脑上是好的”问题直接消灭掉。另一个值得提的坑是GPU驱动和CUDA版本对不上。很多时候你明明有显卡torch.cuda.is_available()却返回False往往就是PyTorch版本编译时用的CUDA版本和驱动不匹配。解决办法是不要自己装CUDA开发套件直接用PyTorch官方提供的对应CUDA版本轮子例如torch的cu121/cu118等预编译包它自带运行库省去一大堆麻烦。4.2 数据与检索方向的坑RAG系统的效果上限很大程度取决于数据质量但数据质量偏又是新手最容易踩坑的地方。第一个坑是没清理就入库。我见过有人直接把扫描版PDF读出来的乱码文档丢进向量库检索出来一堆“口口口”无意义内容模型看着这些垃圾资料认真“答题”结果自然惨不忍睹。文档入库前一定要做清洗检测乱码、去除页眉页脚、把PDF转换后的文字先人工抽几页看看质量。宁可少入库也不要污染向量库。脏数据一旦进了索引检索端怎么优化都白搭。第二个坑是切分导致的结构破坏。代码块被拦腰切开表格被拆成上下两半Markdown的标题层级错乱这些都会让RAG召回的上下文不可读。我自己的做法是对代码类文档用代码结构感知切分器对表格整张表作为一个chunk哪怕大一点也不要切碎对带标题的文章在标题层级上做预分区。结构化文档其实比纯文本更适合rule-based处理值得多花时间。第三个坑是embedding模型的语言和领域不匹配。你用英文模型处理中文文档效果会明显下滑用通用模型处理专业领域术语多的文档召回也会失准。当前中文开源模型里BGE系列是一个稳妥的选择领域特殊的话可以先用通用模型跑一版基线再在领域数据上用对比学习微调。这个进阶操作没那么玄本质是让你embedding模型看懂你领域里的黑话。4.3 性能与稳定性方向的坑线上系统的要求与demo完全不同这里有三个高频故障。第一个是内存OOM。embedding模型加载到显存后占几个GBAPI服务里如果每一路请求都加载一次模型并发一高直接爆显存。正确做法是模型常驻内存做成单例请求只做推理不加载。向量索引也存在类似问题FAISS索引加载到内存后注意规模增长最好定期重建或者干脆切到支持磁盘映射的向量库。第二个是流式输出与普通接口的兼容问题。很多前端框架默认是普通JSON响应你改造为SSE流式后如果没同步更新前端解析逻辑页面就会白屏或者收到一堆格式错误。另一方面后端微服务之间如果用了普通HTTP流式输出也不太好透传需要网关支持SSE或者改用WebSocket。开发RAG服务时我习惯把“是否流式”做成一个开关参数给前端页面走流式给内部接口走非流式两边互不干扰。第三个是成本方面的坑。大模型API按token计费Prompt越长越贵。同一个问题被不同用户反复问每次都跑一遍完整RAG链路成本其实很高。语义缓存可以很有效地解决这个问题把问题和答案缓存起来新问题来了先算和已有问题的语义相似度超过阈值直接命中历史答案。实测在问答类场景里语义缓存能省30%到50%的token花费很值得加进生产链路。在做AI工程这条路上走了这么久我一直向身边新人强调一个顺序先用已有的大模型API把业务闭环跑通再慢慢替换成自部署模型先用最简单的向量检索把Demo搭起来再逐步加入混合检索和重排。不是所有项目一开始就需要最复杂的方案从简单出发才能让你快速发现问题、找到真正的瓶颈而不是被花里胡哨的架构拖住了脚步。这套RAG系统就是最好的例子——它没有用到任何高深的技术但每一步都是工程决策。以后你还会碰到的多模态、Agent、微调本质上都是在同样的工程思路上迭代和扩展能力。
RELATED

相关推荐

模型下载后跑不起来?用OpenVINO理清本地推理流水线

模型下载后跑不起来?用OpenVINO理清本地推理流水线

上个月帮朋友调一个从网上下载的目标检测模型,模型文件明明就在硬盘里,Netron里也能看到结构图,图像也读进来了,但一跑就报错,一会儿Requested shape不匹配,一会儿推理结果全是错误框。这种问题在本地推理里…

📅 2026/11/3 5:55:51
OpenVINO本地推理流水线踩坑指南:从模型下载到推理成功

OpenVINO本地推理流水线踩坑指南:从模型下载到推理成功

模型下载完,双击打开想跑个推理,结果报错刷了一屏。这个场景我见过太多次,甚至我自己刚接触OpenVINO的时候也栽过跟头。看网上的教程都写得轻描淡写,什么“下载模型→load→跑起来”,真到自己上手,一个模型…

📅 2026/11/3 5:55:51
嵌入式面试核心:任务状态机与信号量语义深度解析

嵌入式面试核心:任务状态机与信号量语义深度解析

简介:本资源是一份面向嵌入式开发工程师、应届求职者及高校相关专业学生的面试笔试备考资料,聚焦嵌入式系统核心原理与实战考点,助力高效突破技术面试关卡。内容覆盖嵌入式系统三要素、软件体系结构(驱动层/OS层/中间件/应用层&am…

📅 2026/11/3 5:55:51
MORE NEWS

更多资讯

📰

【数据仓库-9】hadoop生态圈与数据仓库

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

📰

【部署】Deploying Trino on linux

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

📰

【trino权威指南】使用trino详解:trino client安装、查询sql、DBeaver连接trino、java通过JDBC连接trino

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

📰

【工具】hadoop国内下载镜像

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

📰

027-时间同步服务(chronydNTP)

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

📰

基于STM32与HX711的智能体重秤设计:从原理图到代码的完整实战

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

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬