ARTICLE DETAIL

资讯详情

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

AI工程从零到生产:RAG与Agent实战最短路径

AI工程从零到生产:RAG与Agent实战最短路径 “你这项目其实不难但你是不是还没跑通一个闭环”这是上周一个做技术的朋友看完我 RAG 项目之后说的第一句话。当时我有点不服气但他问得没错。市面上关于 ai-engineering from-scratch 的教程很多可大多数都卡在两个极端要么上来就甩一堆数学符号要么就是“调一个 API 就完事”的 demo。真正从零开始动手的人最需要的是知道先做什么、后做什么、每一步为什么要这么做。我花了一年时间从连 embedding 是什么都不知道到做出稳定回答内部文档问题的应用再到接上 Agent 工具链和完整评测体系。这篇内容就是我验证过的最短路径不绕弯子把每一个坑和对应的解决办法都摊开讲。适合没有 ML 背景但会写代码的工程师也适合已经会调 API 但总觉得系统不稳定的朋友。1. 先诊断你要做的到底是“AI 工程”里的哪一块很多人学 Deep Learning 学得头昏脑涨原因不是不努力而是没搞明白自己根本不需要那部分知识。在做任何学习计划之前先把自己想要的结果归类能帮你省下大量时间。AI 工程这个盖子下面大致有三条完全不同的路。1.1 三条路线的目标、成本与受众完全不同这里我先说结论大部分从零开始的人应该走路线 A而不是冲进路线 C。路线目标产物核心知识首个可见成果适合谁A. AI 应用开发RAG 问答、Agent 工作流、API 产品API 调用、提示词、检索系统、后端工程2-4 周做通一个 RAG大部分想入行的工程师和产品经理B. 模型工程私有化模型、推理优化、微调PyTorch 基础、量化、推理框架、训练/lora1-3 个月跑通一次微调有数据处理或 ML 基础的人C. 算法研究新模型、新机制深度学习理论、扎实数学功底半年到一年目标进研究机构或实验室的人我遇到过太多想做“AI 产品”的人第一件事却是去啃论文这是最典型的路线错配。反过来如果你目标是做模型优化天天调 API 写提示词也不行。路线本身没有高低只有是否匹配你的目标。1.2 从零开始的技能基线会这些就够起步了路线 A 的真正门槛不比你之前做过的任何一个 Web 项目高。你只需要会读 Python 代码会写简单的函数和类熟悉命令行操作和 JSON 数据结构知道怎么调用 HTTP APIrequests 都可以会查文档、会搜索引擎、会调试仅此而已。线性代数、反向传播、Transformer 架构这些起步阶段完全用不上。我见过一个前端同事几乎不懂 Python用了两周就把一个文档问答应用跑起来了。他的核心技能是“会调试”这比数学能力重要得多。我的观点很直接AI 工程师首先是工程师其次才是 AI。很多人被“AI”两个字吓住了反而忽略了自己已有的工程能力。这种能力迁移才是 from-scratch 最常用的起跑器。1.3 我验证过的 90 天起步顺序如果你认同上面的诊断接下来可以直接照抄这个节奏第 1 周把本地环境搭好用最简代码调用一次大模型 API把输入输出跑通第 2-4 周不碰任何复杂框架纯手写一个 RAG 管线解决一个具体业务问题第 5-8 周在 RAG 基础上扩展 Agent 工具调用让系统能完成“检索动作”第 9-12 周补上评测集、日志监控、成本控制让项目可以长期维护这个顺序的核心逻辑是每个阶段都在为下一阶段提供可复用的骨架而且每个阶段都有一个可量化的里程碑。没有里程碑的阶段很容易变成无底洞。2. 搭环境的时候真正让你慢下来的不是代码第一条建议很简单别用系统自带的 Python。系统 Python 往往版本老旧还和系统其他软件共用环境。我曾经在 macOS 上直接用系统 python装一个科学计算库的时候把系统关键路径搅得一塌糊涂重装系统才算完。2.1 先用 pyenv 把 Python 版本锁死我认为 Python 版本选择本身比文本编辑器、IDE 之争重要得多。具体操作是装 pyenv然后固定项目要用的版本pyenv install 3.11.x pyenv local 3.11.x不要追求最新版本。3.13 虽然新但很多 AI 库的二进制轮子还没跟上遇到莫名其妙的编译错误很浪费时间3.11 和 3.12 是当前生态验证最充分的版本。版本统一的好处是你踩过的坑别人大概率也踩过搜解决方案最容易命中。2.2 用 uv 而不是 pip 或 conda包管理这一层我用 uv 之后几乎没再碰过 pip 和 poetry。uv 用 Rust 写的安装依赖的速度比传统方案快一个量级而且生成的锁文件非常干净遇到版本冲突时提示也清晰。uv init ai-engineering-from-scratch uv add openai chromadb sentence-transformers另外我强烈建议初期不要装 langchain 这类重型框架。不是说它不好而是它把太多细节藏起来了。从零开始的最大优势就是能看清每一步的内部逻辑。等你自己手写过一遍 RAG 和 Agent再回来用框架感受会完全不同。2.3 本地模型和 API开发期怎么选这里我给一个我踩过很多坑后确定的策略开发调试用本地模型效果验证用 API。选型优点缺点适用阶段本地模型Ollama免费、隐私、可离线、不限请求量效果弱于顶级 API需要显卡配置开发调试、跑通链路、单元测试模型 API效果直接、省事、无需维护硬件按 token 收费、依赖网络最终产品、关键效果验证本地模型推荐 Ollama两条命令就能跑起来ollama pull qwen2.5:7b ollama pull deepseek-r1:7b ollama run qwen2.5:7b我实际的体验是本地 7B 模型在多数简单问答上已经能干活但一旦涉及复杂推理和多步工具调用明显不如 API 大模型所以两者是配合关系不是替代关系。2.4 一个够用且不臃肿的目录骨架项目结构不用复杂但必须从一开始就分清楚职责。我目前最常用的是这个骨架aideep/ ├── app/ # API 服务和入口 ├── data/ # 原始文档和切分结果 ├── src/ │ ├── loader.py # 加载不同格式的文件 │ ├── chunker.py # 文本切分逻辑 │ ├── embedder.py # 向量化封装 │ ├── retriever.py # 检索逻辑 │ └── generator.py # 生成逻辑 ├── tests/ ├── eval/ # 评估集和打分脚本 └── pyproject.toml不要一开始就搞微服务、消息队列、K8s。AI 工程和传统后端最大的不同是核心链路本身还包含不确定性你需要先让链路稳定再谈架构。过早引入基础设施只会让你分不清问题出在代码逻辑还是出在编排层。3. 第一版 RAG把“检索生成”完整做通我默认你已经有具体的业务场景比如给团队做一个内部产品文档问答。没有具体场景的 RAG 学习项目很容易迷失在参数调整里。为什么不建议做“通用问答”因为切分策略、检索参数全都依赖资料类型。你问的是操作手册还是论文库处理方式完全不同。3.1 切分是 RAG 效果的第一道坎这一步看着不起眼实际决定检索质量的上限。我最早期版本直接按固定 512 个字符硬切结果一个二级标题被劈成两半检索出来的内容前言不搭后语。后来改成按 Markdown 标题结构切分效果立刻上来。def split_by_headings(markdown_text, max_chars800, overlap120): sections [] current for line in markdown_text.splitlines(): if line.startswith(#) and current: sections.append(current) current line \n else: current line \n if len(current) max_chars: sections.append(current) current current[-overlap:] if current: sections.append(current) return sections这段代码不完美但它表达了核心思想优先按文档结构切分长段落再按长度二次切且保留重叠区域。至于参数我给一组我实测的参考值资料类型chunk_size 建议overlap 建议短说明文档400-600 字符10%长教程/章节600-1000 字符15-20%表格/代码为主300-400 字符10%提示切分时不要把 Markdown 的代码块拦腰截断否则检索到的那段内容在生成阶段会有很差的阅读体验。可以在切分前先识别代码块边界把它们保护起来。3.2 向量化选一个稳的中文 embedding中文场景下我推荐 BGE 系列bge-m3 在同尺寸模型里表现很稳既能做稠密检索也能做稀疏检索。你也可以直接用模型 API 提供的 embedding 接口省一点维护成本。通过 sentence-transformers 加载本地模型的用法from sentence_transformers import SentenceTransformer model SentenceTransformer(BAAI/bge-m3) chunk_vectors model.encode(chunks, normalize_embeddingsTrue)嵌入模型选好后尽量不要频繁更换因为每次更换都要重新向量化整个知识库。这也是为什么建议先认真选型、再动手索引。3.3 向量数据库起步用 Chroma 足够很多人在向量库选型上纠结太久实际上数据量不超过几十万条、并发不高的时候Chroma 完全够用。它部署在应用进程里API 简单对从零开始非常友好。import chromadb client chromadb.PersistentClient(path./data/chroma) collection client.get_or_create_collection(docs) collection.add(ids[str(i) for i in range(len(chunk_texts))], documentschunk_texts, embeddingschunk_vectors) results collection.query(query_embeddings[query_vec], n_results5)等数据量真的涨上去了再迁移到 Qdrant 或 Milvus 不迟。迁移的成本比你想象的低核心 API 概念是相通的。3.4 检索环节top-k 和 rerank 的正确用法默认 top_k 不要太大4-8 是比较稳的区间。我试过 10 以上效果反而变差因为引入了太多不相关内容冲淡了生成阶段的注意力。如果直接命中的结果不够准加一个重排序环节效果提升非常明显。做法很简单先用轻量向量检索召回 20-30 个候选再用 rerank 模型精排取前 3-5 个。推荐用 BGE-reranker它只负责打分排序不需要向量化。注意rerank 的代价是延迟增加几十到几百毫秒。内部知识库问答这种场景完全值得但如果你的场景对延迟极敏感就要权衡收益率。3.5 生成环节把上下文拼成一段可回答的提示词prompt 模板我固定了以下结构你是内部文档问答助手。只能根据下面的参考资料回答回答时在句末用 [来源编号] 标注引用。 如果资料中没有答案直接说“资料中没有相关信息”不要编造。 参考资料 [1] ... [2] ... 问题...temperature 设置成 0.2-0.3。为什么回答稳定性优先。做知识问答不是创作文案你不需要模型发挥天马行空需要的是每一句都有据可查所以让输出的确定性尽量高。3.6 第一版跑通后立刻做三件事这一步很多人会跳过但它是后续优化的地基随机抽 10-20 个真实问题把检索结果打印出来人工看把采样结果记录在 eval/ 目录里形成第一版 baseline在调用链路上加上最基础的日志问题和答案原文我见过最快翻车的方式就是跑通 demo 后立刻去调 prompt却没有记录前后对照。没有 baseline你根本分不清改进是真的还是自我感觉良好。4. 从 RAG 到 Agent工具、循环与状态管理RAG 的需求是“基于资料回答问题”但用户经常要的不只是回答。比如“这个季度三个项目的预算总和还剩多少”RAG 可以检索到“项目 A 剩余 4 万项目 B 剩余 2 万项目 C 剩余 6 万”但“求和”这一步必须交给工具。这就是 Agent 的价值把大模型从“只会回答”变成“能调工具、能计算、能执行动作”。4.1 最小 Agent 循环四个要素缺一不可不要被 Agent 这个词吓住。核心循环就四步模型根据用户问题决定是否调用工具、调用哪个工具解析模型的调用请求拿到工具名和参数执行工具得到结果把结果作为新消息回填给模型模型判断是继续调用还是最终回答一个能跑通的最小循环代码大概是这样def run_agent(question, tools, max_steps5): messages [{role: user, content: question}] for step in range(max_steps): resp llm.chat(messages, toolstools) messages.append(resp) if resp.get(tool_calls): for call in resp[tool_calls]: result execute_tool(call) messages.append({ role: tool, tool_call_id: call[id], content: json.dumps(result, ensure_asciiFalse) }) else: return resp[content] return reach max steps这个循环虽然简单但已经是很多成熟 Agent 框架的原型。你完全可以在没有框架的情况下先跑通它再考虑引入抽象层。4.2 工具注册与描述工程模型远比你想象的“傻”Agent 能否选对工具极大程度取决于工具描述写得好不好。我吃过一个亏工具描述写得太简单模型在多个相似工具面前犹豫不决反复追问用户“您是想查天气还是想查降雨量”。问题不在模型在描述。差的描述“获取天气”。好的描述“根据城市名获取未来 3 天天气。入参city_name中文城市名例如‘北京’返回 JSON 包含每日最高/最低温度和天气现象用于出行建议、穿衣推荐。”差别在哪好的描述写清了入参格式、返回结构和使用场景。模型没有常识你要手把手告诉它这个工具是干什么的、怎么传参、拿到结果后能做什么。写工具描述有个技巧把它当成“写给新同事看的接口文档”而不是“写给自己看的注释”。4.3 JSON 解析与防跑飞模型偶尔会输出不合法的 JSON这种情况在本地小模型上更常见。我的处理策略是不要直接 json.loads先截取第一个{到最后一个}之间的片段再解析解析失败就让模型重试一次。另外max_steps 必须有。没有上限的 Agent 循环早晚会陷入“模型问自己-自己回答”的死循环既浪费 token 又让用户干等。生产环境我通常把上限设为 3-5 步超过就直接返回当前信息和“暂时无法完成该操作”的兜底提示。4.4 多轮状态管理把 Agent 的 messages 列表作为 session 状态持久化这样多轮对话里模型能记住上下文。但 messages 不能无限增长超出上下文窗口就会报错或丢信息。我的经验是最近两轮完整消息保留更早的历史用一句话总结概览再塞回上下文中。这个策略在成本和效果之间比较均衡。5. 评测和监控不做你永远不知道改得好不好这是我吃过最大的亏。有一阵子我改了 prompt觉得回答质量“好像变好了”就把新版本推到线上。结果一周后用户反馈说答案质量下降我拿不出任何数据反驳只能灰溜溜回滚。根源就是没有评估集。从那以后所有改动进线上之前必须过一遍评估。5.1 没有评估集就是盲调评估集不需要一开始就很大但必须是真实的问题。你可以在三个渠道收集真实用户对话记录最有价值内部文档中重点内容转写成的提问业务同事常问的问题我的建议是 30-50 条起步覆盖不同难度。不要追求一开始就做 500 条初期 50 条足够让你在“大版本方向”上做判断。标注也不要求写标准答案只要标注回答中必须覆盖的关键信息点就行打分时重点看有没有覆盖到。5.2 离线指标检索和生成必须分开看很多团队只盯着最终答案看这样的坏处是生成模型很强的时候检索错了它也能强行圆回来但细节处全是错的。所以我把指标拆成检索侧和生成侧。环节指标判断标准检索命中率正确的资料是否出现在前 k 个结果里检索recallk关键词点是否被召回检索MRR正确结果排得有多靠前生成忠实度回答是否基于资料而非凭空编造生成相关性是否直接正面回答用户问题生成完整性答案是否覆盖了所有关键信息点不一定要算得特别严谨。手动抽 20 条逐条给一个 0-5 分计算平均分也比“我感觉变好了”靠谱一万倍。5.3 用 LLM 当裁判的三个原则我自己跑过几轮 LLM-as-judge也踩过一些坑总结出三条原则第一打分模型不要和被评估模型同源。比如你的主模型用的是 Qwen打分就尽量别用同一个否则会有一致性的偏好在里面。第二一次只评估一个维度。你让模型同时打“忠实度、相关性、完整性”结果它往往只关注最明显的那一个维度另外两个随机给分。分开跑效果会稳定很多。第三自动打分之外一定要有人工抽检。评估 prompt 可以参考请对回答质量打分0-5 分只评估忠实度 即回答是否严格基于参考资料而非编造内容。 参考资料{...} 回答{...} 只输出 JSON{score: 0, reason: ...}5.4 线上监控最简但必须有的摸具日志至少要记录这几个字段用户问题、检索到的资料 ID、最终答案、模型延迟、用了多少 token。这几个字段能让你在用户说“回答很怪”时几分钟内定位是检索问题还是生成问题还是输入上下文被截断导致丢信息。另外建议每天看一眼“平均首 token 延迟”和一个“每请求 token 数”。前者影响体验后者直接决定成本曲线。用户对 AI 系统的容忍度远低于对普通网页的容忍度3 秒没反应就会认为“AI 挂了”。6. 走向生产成本、延迟、幻觉三个绕不开的话题当你的 RAG/Agent 不再是玩具之后来自生产环境的压力会接踵而至。我挑出三个最现实的话题每个都是我用钱和头发换来的经验。6.1 token 经济账先学会估算每一次请求很多人对 token 成本没有概念。我来算一笔典型账假设一次问答中检索资料占 800 字、历史对话占 1500 字、提示词占 200 字输出答案约 500 字。中文场景下一个汉字大约对应 1-2 个 token所以一次请求总消耗大约在 3000-6000 token 之间。如果每天有 1000 个用户请求日消耗就在 300 万-600 万 token 之间。不同模型 API 的单价差异很大但换算之后的结论是一致的上下文无节制往上堆的时代已经结束每一轮对话都要思考一个问题——“这段历史信息真的需要保留吗”6.2 上下文裁剪的三种手段裁剪不是简单粗暴地截断推荐按优先级组合使用滑动窗口只保留最近两轮完整消息最老的消息直接丢弃历史摘要把更早的对话压缩成一句概括既保住了“用户大概聊了什么”又能控制长度关键信息抽取对用户画像类信息比如用户之前提到的偏好单独存到结构化字段里而不是留在对话历史中随窗口滑掉我只用第一个手段的时候用户连续聊了十轮之后系统已经忘了第一轮提到的关键信息。加上历史摘要之后这个问题基本解决成本也只增加了一小部分。6.3 幻觉与引用约束把问题暴露出来而不是假完美幻觉无法彻底根除但可以工程化约束。最简单有效的一招就是prompt 强制要求回答者只能使用给定资料且必须带引用编号。这样即使模型某次编造了一个不存在的“资料”用户也能看到引用的编号是伪造的这件事就变得可验证了。在高风险场景财务、医疗、合同不要只靠提示词再加上结构化输出规则校验。比如让模型先输出 JSON 格式的回答再写一层规则检查凡是涉及数字的语句必须与检索到的原文逐字对照。这个规则写起来不复杂但它能把错误扼杀在出口。6.4 流式输出、语义缓存与可替换性流式输出是延迟优化的第一选择。把“等待完整答案”变成“边生成边看”用户感知上的首字延迟会从 1-3 秒降到 300ms 左右体验提升非常明显。语义缓存适合重复度高的场景。用户问“你们的退款政策是什么”每天被问上百遍没必要每次都调模型。把问题和答案向量化存储新问题先算相似度命中就直接返回缓存答案。注意相似度阈值要保守低于 0.92 就别用了宁可不命中也不给错答案。最后一件必须现在做的事把模型调用封装成一层独立接口。不要在你的业务代码里到处直接调用某个专属 API。理由很简单大模型市场半年一小变一年一大变你要能随时切换到效果更好、成本更低的新模型。切换之后重跑一遍评估集用数据说话而不是靠某天刷到热搜就急着换。把模型调用封成 interface 并不难难的是持续用评估集验证替换后的效果。我每季度会跑一轮“模型对比评测”拿同一批 50 条评估集分别用不同模型跑得到一个对照表。这个表就是模型选型决策的依据比任何技术博客的推荐都可靠。写到这里我想把最真切的一点体会放在最后从零开始做 AI 工程最大的敌人不是复杂算法而是迟迟做不出一个能跑的闭环时那种反复怀疑自己的感觉。我第一版 RAG 花了整整一周跑通验证集的那个晚上比后来研究任何论文都踏实。别怕代码简单也别迷信架构先进。先把最小闭环稳稳跑起来再把评测、监控、成本控制一层层加进去。最后分享一个坚持到现在的习惯把每次改动的评估分数和踩坑记录写在项目根目录的 CHANGELOG.md 里格式随意哪怕一句话都行。三个月后回看你会看到一条非常明显的能力提升曲线——那才是从零开始这件事最大的回报。
返回列表