拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

从零构建AI工程:RAG、Agent与部署监控的完整实践路线

从零构建AI工程:RAG、Agent与部署监控的完整实践路线

1. 项目缘起:为什么我要从零开始折腾 AI 工程

我得先交代一下背景。做这个 "ai-engineering-from-scratch" 项目之前,我其实已经在传统后端领域写了七八年代码,Java、Go、Python 都摸过一轮。但真正让我下定决心从零系统梳理 AI 工程的,是一次让我非常尴尬的线上事故:我用 LangChain 搭了一个内部知识库问答机器人,结果上线第一天,它把一份过期的财务文档当成最新政策回答给了业务部门。当时我就意识到,光会调 API、会用几个框架的皮毛,根本算不上 AI 工程,顶多算是个"提示词搬运工"。

所以这个项目的核心目标很简单:**把 AI 工程从"能跑 demo"推进到"能扛生产"。**它不是什么新鲜理论,更不是某个框架的教程,而是一条自底向上的学习路线——从最基础的 Python 工程能力,到模型 API 的熟练运用,再到 RAG 检索增强、Agent 自主决策、最后落地的部署与可观测性。整条链路走下来,我踩过的坑、验证过的方案、否决掉的技术选型,都会在这篇文章里如实交代。

这篇文章适合谁?如果你是刚转 AI 方向的开发者,或者已经有了一些应用开发经验但总感觉在"调包侠"和"AI 工程师"之间隔着一层窗户纸,那我这条路线对你应该很有参考价值。我会尽量说人话,把每个方案背后的"为什么"讲清楚,而不是丢给你一堆名词。

老规矩,先列个全景路线图,后面每一部分我都会单独展开。

2. 学习路线的全景设计:别急着碰大模型

很多人一上来就学 LangChain,我觉得这是最大的误区。AI 工程这个领域,工具链更新速度快到让人窒息,你今天学的框架接口,下个月可能就 deprecated 了。所以这个项目的第一原则是:把根基打在一两年内不会变的东西上。

2.1 三层能力金字塔:基础、核心、应用

我在设计路线时,把 AI 工程能力拆成了三层,每一层都有明确的学习目标和验收标准:

第一层是工程基础层。包括 Python 的类型系统、异步编程、装饰器、设计模式,以及最基础的线性代数和概率论。这一层不过关,后面读源码、调 bug 会非常痛苦。我的验收标准是能不看文档写出一个带类型标注、异常处理、日志记录的多线程爬虫框架。

第二层是模型交互层。包括 OpenAI/Anthropic 等 API 的接入、Prompt 设计、函数调用(Function Calling)、结构化输出、流式传输处理。这一层解决的是"如何让模型稳定地输出你要的东西",是从"能调用"到"调得好"的分水岭。

第三层是工程化应用层。包括 RAG 检索增强、向量数据库、Agent 工作流、效果评估、部署监控。这一层才是真正意义上的"AI 工程"——因为它涉及的已经不只是模型本身,而是数据管道、系统架构、稳定性治理这些经典工程问题。

2.2 为什么我把 RAG 放在 Agent 前面

很多教程把 Agent 放在最高优先级,渲染得特别玄乎。但在这个项目里,我是先完整做了一个 RAG 系统,才去碰 Agent。原因很简单:Agent 的内部逻辑,本质上就是一个"检索-决策-执行"的循环,你不把 RAG 的问题摸透,直接上 Agent 只会被各种不可控行为折磨。

具体的路线顺序我是这么排的:

阶段核心内容验收指标
1Python 工程化能力+AI 基础能独立完成一个带测试的爬虫工具
2模型 API+Prompt+结构化输出能稳定输出 JSON 并对接业务系统
3RAG:切分、向量化、检索、重排问答准确率 85% 以上(自建评测集)
4Agent:规划、工具调用、记忆能完成多步骤任务(如自动化调研)
5部署、评估、监控、成本优化系统稳定运行 30 天无重大事故

这个顺序的核心理念是"每个阶段都解决前一个阶段留下的痛点"。比如你不做完 RAG,就不会真正理解为什么上下文一长,模型就开始胡编乱造;不做完 Agent,也不会明白为什么工具调用失败率这么高,需要设计重试机制和人工确认。

3. 筑基:工程能力和 AI 基础到底要学到什么程度

现在聊第一层。很多 AI 初学者犯的毛病是数学和代码基础还没扎实,就急着跑模型。我见过太多人连 Python 的async都没搞明白,就开始复制 LangChain 代码,报错了只能 Google——这种学习方式效率极低。

3.1 编程语言:Python 之外的加分项

Python 是 AI 工程的主语言,这个没什么可争论的。但我想额外强调一下类型提示和异步编程的重要性。在写 AI 应用的时候,类型提示能让你在写 Prompt 和解析模型输出时少犯很多低级错误。我实际项目里,pydantic几乎成了标配——不光用来校验输入,更多是用BaseModel定义模型输出结构,然后让大模型按这个 schema 输出 JSON。这一招在工程化落地上极其关键,后面会展开讲。

异步编程同样重要。调用大模型 API 是典型的 I/O 密集型操作,一次请求可能要等 2-8 秒。如果你同步调用,10 个并发就要等 80 秒;但如果用asyncio配合httpx.AsyncClient,同样 10 个请求,总耗时还是 8 秒左右。在我做内部知识库工具时,这个优化直接把接口的吞吐量提升了 5 倍。

实操建议:就算你不用 FastAPI,也建议了解它的依赖注入机制和 Pydantic 集成方式。FastAPI 的生态本身就是 AI 应用后端的最佳实践范本。

3.2 数学与机器学习基础:不啃课本,按需取用

大学课本里的线性代数,学完你大概率就忘了。我更推荐按需学习,遇到什么补什么:

  • 向量与高维空间:在学 RAG 的时候必然会遇到。你只需要理解"一段文本被 Embedding 成几百维的向量,语义相似的文本在空间中距离更近"就够用。
  • 概率基础:理解困惑度(Perplexity)、温度参数(Temperature)的影响就够。
  • 评估指标:精确率、召回率、F1、RAG 的忠实度、答案相关性,这些是评估系统效果时必须用的。

我的经验是,别在理论上追求深入,优先建立直觉。比如温度参数,你只要记住"越低越确定,越高越发散",然后亲手跑几组实验,远比你背十遍公式有用。

3.3 核心工程工具:Git、Docker、CI 的三板斧

AI 工程的"从零开始",不能只是模型相关的从零。Git 分支管理、Docker 镜像构建、CI 自动化测试,这些基础工程能力在 AI 项目落地时一个都躲不掉。我在项目里把这三样也纳入了第一阶段的学习清单:

  • Git:要求掌握常规的工作流,每天提交,每次提交对应一个可运行状态。
  • Docker:要求能把 Python 应用打包成镜像,并且能写 Dockerfile 做依赖缓存,这一步对后期部署太关键了。
  • CI:要求用 GitHub Actions 跑单元测试和 lint,哪怕只有一个测试文件,也要把流程跑通。

这三样当时看起来平平无奇,但后面我拆 RAG 服务的依赖、迁移数据库、切换向量库版本的时候,多亏了 Docker 隔离环境,才没有把宿主机弄成一团浆糊。

4. 模型应用:从第一个 API 调用到稳定的结构化输出

前面基础打完了,就到了有趣的阶段。这部分的核心不是"怎么调 API",因为每个大模型厂商的 SDK 用法都差不多。真正的核心是:如何让模型的输出,变成你的工程系统里一个可靠的组件。

4.1 接入模型 API:别局限在单一厂商

我的建议是,第一阶段的实验统一用 OpenAI 兼容接口的格式,它实际上是行业事实标准。不管你后面用哪个模型,走 API 网关还是本地部署,协议基本都是 OpenAI 风格。这样做的好处是抽象了一层,迁移成本低。

一个完整的接入要考虑三件事:

  1. 超时与重试:模型服务是典型的慢接口,网络抖动、服务端过载都很常见。必须设置合理的超时时间(我一般用 30 秒),并实现指数退避重试。
  2. 流式与缓冲:流式输出能提升用户体验,但工程上要处理半截 JSON 的问题。我的做法是流式阶段只做展示层,数据入库时仍然用一次完整的非流式请求,避免解析残缺内容。
  3. 成本控制:Prompt 里塞的内容越多,Token 消耗越大。我做过最长一次事故是忘了清理对话历史,把几万字的上下文都发给了模型,一次请求花了上百块人民币。

注意:实际项目中,我强烈建议加一层模型网关,统一管理 API Key、限流、日志和预算。就算开始只是简单封装一个函数,也比到处裸调 SDK 强得多。

4.2 Prompt 工程:稳定性的第一道关卡

Prompt 工程是个被过度玄学化的话题。我的实践结论是:它本质上是接口契约设计,不是文学创作。你写 Prompt 就像设计函数签名一样,输入是什么、输出是什么、边界条件是什么,都得清清楚楚。

我常用的 Prompt 骨架分四段:

角色:你是一个严谨的金融数据分析助手 任务:根据提供的财报数据,回答用户关于营收增长的问题 约束:只使用以下数据来源,不要猜测;如果信息不足,直接回答"资料不足" 输出格式:JSON 对象,包含 answer(字符串)、confidence(0到1的数字)、sources(字符串数组)

这套模板帮我解决了很多问题。特别是"输出格式"这一节,我后来用 JSON Schema 或 Pydantic 模型定义好了之后,直接转成字符串塞进 Prompt,模型的输出就再也不会出现乱七八糟的格式错误。

4.3 函数调用:让模型触达系统能力的钥匙

当你的应用不只是聊天,而要真正执行业务动作(比如查数据库、发工单、调用第三方 API)的时候,就得用函数调用。

函数调用的本质是:你给模型提供几个函数的 JSON 描述,模型根据对话内容决定要不要调用某个函数、参数是什么。你可以用开源的 Function Calling 框架,也完全可以手写一个小调度器:

  • 准备函数列表(名称、描述、参数 schema)
  • 带上对话记录请求模型
  • 如果返回结果里有tool_calls,就执行对应的函数
  • 把执行结果作为新的消息发回给模型,让它生成最终的回复

我在早期实现的时候踩过一个很隐蔽的坑:函数描述写得含糊,模型就会乱传参。比如"获取用户信息"这个描述,模型可能会传一个不存在的用户 ID。后来我把描述写成"根据用户提供的姓名和工号精确查询,工号不存在时返回 None",参数校验的问题一下少了很多。

5. RAG 实战:让模型的回答长在你的数据上

如果你只记住这篇文章的一个部分,那就记这个部分。RAG(检索增强生成)是当前 AI 工程落地避不开的基础设施。它解决的问题非常朴素:大模型再强,也不知道你公司内部的数据。RAG 的答案,不是靠模型猜的,而是真的"查到"的。

5.1 文档切分:最容易被忽略的精度瓶颈

在 RAG 链路里,很多人把精力都花在调 Embedding 模型上,结果检索效果还是差。最后发现,问题出在文本切分上。

切分粒度太粗,一段文本里混杂了好几个主题,向量化之后语义被稀释,检索精度自然上不去。切分粒度太细,句子的上下文被切断了,"iPhone 15"和"上一代产品对比"这种跨句信息就丢失了。

我实测下来的一个可靠 baseline 是:用 400-600 个字符为一个 chunk,chunk 间重叠 50 个字符,优先按段落切、按句子兜底。这个参数看似随意,但它是检索精度和上下文完整性之间的一个平衡点。

切分完了不要急着灌库。先看几段切分结果,问自己几个问题:

  • 每个 chunk 是否聚焦一个主题?
  • 有没有关键信息被拆到两个 chunk 里?
  • 表格、代码块这类特殊格式有没有被破坏?

5.2 向量化与召回策略:向量检索+关键词兜底

向量检索是 RAG 的主干,负责找语义相近的内容;但纯向量方案遇到专有名词、代码符号、缩写的时候常常翻车(比如 "CQRS" 这种词,语义相近不容易靠向量体现)。我的做法是"混合检索":

  • 向量检索:用开源的text-embedding-3-large或者开源的 bge-m3,召回 top 20;
  • 关键词检索:用 BM25 做文本匹配,召回 top 10;
  • 合并两边结果,去重后再用基于交叉编码器(Cross Encoder)的重排序模型,把最终 top 5 作为上下文送给模型。

这个方案比单纯向量检索效果强了不止一个档次。代价只是多维护一个倒排索引,以及重排阶段多花几百毫秒。

5.3 上下文构造:把系统的可用上下文控制住

RAG 到了最后一步,是决定最终答案质量的关键一环。它也是你在工程上最容易失控的地方:上下文越长,回答越偏离,token 越昂贵,推理越慢。

上下文构造要分优先级:

  1. 始终只发送检索到的相关段落,而不是整份文档;
  2. 把与问题最相关的段落放在开头和结尾,因为模型对这两部分的注意力往往更强;
  3. 为每个段落标注来源,强制模型在回答里引用编号,这样能显著减少幻觉。

这里必须提一个我反复犯的错误:为了"不遗漏信息",我把 top 20 个 chunk 全部塞进上下文。结果模型反而被不相关内容干扰,答案质量直线下降。后来我只保留 top 5,准确率反而提升了。

实操心得:RAG 的调优,本质上是一场"召回-干扰-成本"三者间的平衡测试。你最好自己造一个 50 条的评测集(包含分支问答、跨文档问答、陷阱问答三个类别),每次改完参数,跑一遍评测集,用可量化的指标判断方案能不能上生产。

6. Agent 开发:从"问答收发"到"自主执行"

做完 RAG,你就理解了如何让模型"读资料"。下一步是让模型"动手干活"——这就是 Agent。Agent 不是一个神秘的自动编程机器人,它是一套决策循环,由模型负责"想下一步做什么",由工程代码负责"做这个动作"。

6.1 最小可用的 Agent:ReAct 模式

我在项目里实现的最简 Agent,核心逻辑其实只有四步(也就是 ReAct 模式):

  1. 思考(Thought):根据用户请求,推理出下一步需要什么信息。
  2. 行动(Action):选择一个工具(比如搜索资料库、调用接口)。
  3. 观察(Observation):拿到工具执行结果。
  4. 重复(Repeat):把结果加入上下文,继续下一次思考,直到推理出最终答案。

这是一段非常容易实现的伪代码流程:循环调用模型,解析出"Action"和"Action Input",执行对应工具函数,把结果追加进消息列表,继续下一次请求。看起来很简单,但循环上限必须设置(我通常限制在 8 次以内),否则模型会在某些任务上陷入死循环,疯狂调用工具不给出结论。

6.2 工具设计:描述质量决定成败

Agent 能调用什么样的工具,取决于你能给模型提供什么样的函数描述。工具描述和 Prompt 一样,本质是"模型视角的接口文档"。写工具 Description 的时候,多用"在什么场景下用这个工具、参数含义是什么、什么情况下返回空值"这样的描述。

举个例子,一个"查询菜价"的工具,差的描述是"获取价格"(太含糊,模型不知道什么时候该用);好的描述是"当用户询问某商品的市场价格时,用该工具按日期和品类查询,返回元组 (最低价, 最高价)"。

另外,Agent 的工具执行结果一定要做错误信息的结构化返回。如果工具抛异常,不要直接把堆栈丢给模型,那样它只会胡说八道。我的做法是返回一个标准的错误对象:{"status": "error", "message": "品类不存在,可用品类为: 蔬菜、水果"}。

6.3 记忆与多轮对话:短期、长期、业务态

Agent 系统的状态管理,比一般后端系统复杂得多。至少要考虑三层记忆:

  • 短期记忆:最近 2-3 轮对话的原始内容,用于保持连续话题。
  • 长期记忆:用户画像、偏好、历史结论,通常存 Redis 或向量库,需要时才检索取回。
  • 业务状态:当前任务执行到哪一步,哪些动作已完成,哪些待确认,必须放在可持久化的存储里(比如数据库中的任务状态表),防止进程重启后任务丢失。

我最开始做 Agent 的时候,总想把所有上下文都塞进模型请求里,结果 token 消耗直线上升,而且模型经常被一堆陈旧信息干扰。后来改成"按需检索记忆 + 短期窗口"的方案,成本和稳定性都好了很多。

7. 工程化落地:评估、部署、监控三板斧

如果说前面几章讲的是"怎么把功能做出来",那这一章就是"怎么把它安稳地跑起来"。很多人止步于 demo 和原型,就是因为在工程化这一步被劝退了。其实 AI 工程的工程化,比传统后端更像"持续调优的长期服务",你不可能一上来就指望它很准确。

7.1 评估体系:没有评测集,等于在盲改系统

RAG 也好,Agent 也好,改一次 Prompt、调一次参数,你都不知道到底是变好了还是变坏了,那还怎么迭代?所以我在项目里做了一个简单但能跑的评测集:

  • 50 条问答对,覆盖 5 大类业务问题;
  • 每条问题标注了标准答案和来源文档 id;
  • 用三类指标做自动评估:
    • 忠实度:回答中的事实是否都有检索到的参考内容支撑;
    • 答案相关性:是否准确回应了用户意图;
    • 上下文相关性:检索回来的内容与问题的相关比例。

每跑一轮改动,就把指标打一张报表。这个流程能帮你把"感觉好像变好了"的玄学,变成清晰的数据。

至于怎么自动评估"忠实度",我现在的做法是用一个大模型当裁判:把问题和模型回答,以及参考片段一起喂给评估模型,让它按 1-5 分打分并给出理由。再用 BLEURT 或 RAGAS 做交叉验证。有条件的可以人工抽检 10% 的样本,校准调用裁判模型的 prompt。

7.2 部署:从 Flask 到异步网关的演进

我把 RAG 服务的部署演进分了三步:

第一步是单体 Flask 应用,适合原型验证,但并发能力很差,因为 Flask 的同步模型扛不住模型 API 的长耗时请求。

第二步是 FastAPI 异步接口 + Gunicorn 多进程部署。把模型调用改成异步后,单机并发量上涨明显,500 QPS 以下其实不够稳定,但中小型内部工具够用了。

第三步是补上模型网关层,统一管控限流与缓存。命中缓存的重复问题(比如"报销流程是什么"),响应时间从 3 秒降到 100 毫秒以内,成本也直接砍半。

一个容易被忽视的部署细节:如果模型服务部署在外网,必须留意连接的 keep-alive 和连接池复用。否则每次请求都要重新握手,耗时轻松翻倍。用httpx时,建议显式配置limits=httpx.Limits(max_keepalive_connections=20)这类参数。

7.3 可观测性:日志、追踪、成本三位一体

传统后端看日志、看监控;AI 应用除了这些,还必须能追踪"模型是怎么一步步得出结论的"。所以我实现了三层可观测:

  1. 请求日志:记录每次对话的完整 Prompt、输出、耗时、token 用量。这是排查"为什么它这么回答"的第一手材料。
  2. 链路追踪:为 RAG 场景记录"检索了哪些 chunk、重排后选择了哪些、最终拼接的上下文长度";为 Agent 场景记录"哪一步调了什么工具、输入输出是什么"。
  3. 成本报表:按用户、按部门、按接口维度聚合的 token 消耗。AI 应用如果不盯成本,月底账单一定让你怀疑人生。

这是我踩过的最惨教训之一:我们早期没有日志,Agent 在线上乱调用了一次付费接口,直到月末出账才发现,一个月烧掉了够买一辆小汽车的费用。后来做了一套预算预警,单日消耗超过阈值就自动熔断,才把成本控制住。

8. 常见问题与排查实录:那些能让你少掉头发的坑

这一部分完全来自实操,我挑了三个出现频率最高、也是最难发现原因的问题来写。

8.1 模型"失忆":多轮对话越绕越偏

现象:用户在对话里问了很多轮,模型开始答非所问,甚至把更早的话题当成当前的问题。

排查过程:先看请求日志,发现我们确实把完整的历史消息都传给了模型。但问题是场景太长,已经有几十轮对话,早期的信息与当前问题的关联被稀释了。而且每次检索摘要的过程,本身会进一步引入噪声。

解决方案:给多轮对话加一个"滑动窗口 + 摘要"策略——最近的 6 轮完整保留,更早的对话由模型总结成摘要,作为系统消息放在最前面。同时,每次问答完成后,把"最终答案"单独存档为记忆点,下一轮优先参考记忆点而不是原始历史。

8.2 检索返回空:为什么明明该有的资料却搜不到

现象:某个问题的答案明明在知识库里,但 RAG 系统回复"资料不足"。

排查过程:一步步拆链路。先用同一个问题去查向量库和 BM25,发现向量检索确实召回了相关 chunk,但在重排阶段被排到很后面,最终被 top 5 截断了。原来是这段文档的描述和用户问法用了完全不同的表达(用户问"产品质保多久",文档写的是"自签收之日起一年内提供免费维修服务"),向量相似度够,但交叉编码器的排序偏好把更短的匹配排到了前面。

解决方案:重排之后,额外加一个"关键短语匹配"规则,对包含"质保、保修、售后、免费、期限"这类强信号短语的 chunk 做加权。同时把每个 query 扩展出同义词变体(比如"质保"->"保修")后再去检索。

8.3 Agent 死循环:模型一直在调用同一个失败工具

现象:Agent 在一个任务上不断重复调用某个工具,每次都报错,但模型不吸取教训,继续用同样的参数重试。

排查过程:观察 Agent 循环日志后,发现工具方的错误信息里有"参数格式错误 xxx",但模型每次构造的参数还是错的。原因出在工具描述里给出了一个误导性的示例,模型一直在照抄示例里的格式。

解决方案:修改工具描述,把正确示例和错误示例都写清楚,明示"不要使用旧版参数格式,新版参数格式如下"。另外,在工程层面加了一个重试熔断:同一个工具连续失败 3 次,就从 Agent 的可用工具列表里暂时移除,强制模型换思路。

9. 关于这个项目,我最后想分享的几点体会

再次强调,这套学习路线不是标准答案,更不是唯一路径。它只是我自己在 "ai-engineering-from-scratch" 这个项目里走通的方案,经过多次推翻重来后沉淀下来的经验。

如果你真的打算从零开始,我给的建议是:不要一上来就追求生产级完美,先做通一个最小的端到端场景。哪怕只是一个"读 PDF 回答问题"的小工具,也会逼你把 API 接入、切分、向量化、检索、上下文拼接、模型调用、结果输出全部走一遍。这条路走通了,后面再谈优化和工程化才有意义。

我自己的下一步计划,是把这套体系扩展成一套轻量级的 AI 工程训练营,让更多人可以通过实际项目而不是单纯看文档,建立起对 AI 工程的整体直觉。这个领域变化很快,但工程方法的内核——数据质量、系统设计、稳定可观测,永远会留在那里。

返回列表