新闻详情

新闻详情

首页 / 资讯中心 / 详情

从零手写AI工程:RAG、Agent与上下文管理实战指南

发布时间:2026/9/29 19:26:04来源:尧图网络
从零手写AI工程:RAG、Agent与上下文管理实战指南
1. 项目拆解AI工程从零到一到底在学什么先聊点扎心的现在网上聊 AI 开发的帖子十个里有八个是“调 API 三分钟做个聊天机器人”。真到了生产环境需求一复杂数据一脏并发一上来那些 demo 全得推翻重写。我见过太多人 LangChain 用得飞起RAG 跑起来也能出结果但问一句“召回率怎么算”“chunk_size 为什么取 512”“上下文超长了你怎么办”立马卡壳。“ai-engineering-from-scratch”这个项目核心思想全在这几个字里不借助高级封装从零开始把 AI 应用的每一层亲手搭一遍。为什么值得这么干理由很简单。框架封装度高是把双刃剑。它能让你三天出 demo也能让你三年看不懂底层。一旦线上出问题黑盒的排查成本远超你当初省下的那点时间。写这个项目的目的就是把你从“调用者”掰回“构建者”。从头写一遍调用逻辑、检索逻辑、Agent 循环、评估链路你才知道每个组件到底在解决什么问题、性能瓶颈在哪、边界条件在哪。适合谁来参考呢三类人已经能用 LangChain/LlamaIndex 写 demo但想更深一层理解原理的开发者正准备做 AI 应用落地不想被框架绑架、想自己掌控全流程的工程师刚入门想知道“AI 工程”到底包含哪些环节、应该怎么系统学习的新手。这个仓库的理想学习路径不是按框架文档走而是按照 AI 应用的物理结构从薄到厚拆解从大模型调用 → 上下文管理 → 检索增强 → 工具调用 → 评估反馈 → 部署监控。每加一层你就更接近一个能扛住真实业务压力的系统。2. 为什么“从零手写”比“直接上框架”更值得我用个真实经历说明。之前做个内部知识库问答最早图省事直接用 LangChain 的RetrievalQA代码二十行跑起来确实能答。结果上线第一天就翻车用户问法稍有变化检索回来的段落全是噪声答非所问。我盯着 LangChain 的链式调用日志看了半天不知道它内部做了哪些改写、用了什么检索策略整个人懵住。后来沉下心把完整的 RAG 链路用原生代码重写了一遍才发现问题出在 query 改写默认做了一堆花活加上 top_k 取值过大把无关段落一并塞进了上下文。两行参数的事换成直接看底层代码立刻一目了然。“从零手写”最大的价值是让你拥有出问题时的定位能力。再换个类比。你用自动挡开车舒服是舒服但车半路抛锚你连引擎盖都不会开。从零手写就是逼你拆一遍发动机。虽然前期慢但后面你对每个零件的脾气都门儿清。从零手写还有几个实打实的优势灵活可控生产环境的需求千奇百怪框架预设的管线往往满足不了。自己搭想怎么改怎么改。便于排障每一层都是你自己写的日志、错误、性能瓶颈全在掌握之中。规避兼容性地狱LangChain 的版本更新有多频繁、Breaking Change 有多痛经历过的人都懂。自己写依赖少长期稳定。代码瘦身调用一个几十兆的框架只为用其中两个函数冗余度高。裸写往往几千行代码就覆盖了核心业务。当然我不是劝你完全抛开 LangChain。等你亲手写过一遍底层逻辑再回去用框架你会看得懂它的设计、知道怎么定制。那时候框架才真正为你所用而不是反过来。3. 核心环节拆解从模型到应用每一层都在解决什么问题3.1 模型层打好地基选对调用方式AI 工程最底层的二选一用托管 API还是自部署模型。托管 API 省心但会碰到三座大山成本不可控、数据出域、限流延迟。自部署模型用开源权重Qwen、Llama、DeepSeek 等前期折腾但长期成本低、可控性强。工程上的建议是接口统一、模型可替换。所有代码不要跟某一家厂商的 SDK 强绑定统一走 OpenAI 兼容协议。这样今天用 GPT-4o明天换成 Qwen-Max只改一个环境变量就行。这是我跟过的项目里最值得的一个决策——从没被单一厂商锁死过。自部署的话轻量场景用 Ollama 就够了一行命令拉起模型适合开发调试。生产环境追求吞吐推荐 vLLMPagedAttention 技术能把显存利用率拉高一大截QPS 轻松翻倍。跑过 7B 模型的人应该深有体会vLLM 与原生 transformers 部署的差距是肉眼可见的。3.2 上下文工程用一半费用做两倍效果上下文管理是 AI 应用被低估的重头戏。很多工程问题本质上就是上下文没管好。裁剪对话变长后不是无穷加 token而是把早期消息做摘要压缩。核心思路是“用更少的 token 保留关键信息”窗口能塞下更多有效内容。结构化注入不要把整本书塞进 prompt先用结构化提取把关键事实压缩成条目再喂给模型。动态检索不靠手工控制上下文靠向量检索动态取最相关的片段。这就是 RAG 的雏形。上下文管理的核心指标是“有效信息密度”。同样 8K 窗口A 方案只能容纳 20 轮对话B 方案能容纳 50 轮且关键信息不丢失B 的架构明显更健康。这行当里省钱和省事往往是一回事。3.3 RAG 层谁说加个向量库就叫 RAGRAG 看起来简单文档切割、向量化、检索、拼接。实际工程里每个环节都有讲究。切分文档是第一个暗坑。固定 500 字切一刀会把一段完整语义拦腰截断。我踩过之后改成了语义分段用段落 Markdown 标题、列表、空行做自然边界必要时结合滑动窗口重叠。分块质量直接决定检索质量这步偷懒后面全白搭。Embedding 模型选型也不能忽视。中文场景bge-m3系列效果稳且开源可商用text-embedding-3-large也很能打但按量付费、量大肉疼。实测下来换成中文优化过的 embedding 模型top-5 命中率能提升 15%-20%你检索效果差的锅模型至少背一半。检索策略同样不能无脑 top-k。固定取前 5 段必然带噪声。更好的做法是向量检索取前 20 → 重排模型如bge-reranker精排取前 3-4。步骤多了精度立竿见影。重排是 RAG 效果从“能用”到“好用”的分水岭。3.4 Agent 层最小可行循环Agent 核心不是概念是那个循环理解任务 → 选择合适的工具 → 调用并观察结果 → 决定下一步。工程实现上看清每个环节意图识别靠 prompt 明确列出可用工具和适用场景模型负责路由。工具调用原生 OpenAI Function Calling 或开源模型自带的工具调用能力把结构化参数传给函数。循环控制设最大迭代轮数一般 5-8 轮、条件终止、异常重试。不设上限Agent 会自己死循环烧光你的预算别问我是怎么知道的。记忆组织短期记忆存当前任务上下文长期记忆靠外部存储典型如向量库。自己从零实现 Agent 的价值在于你能精准感知每一步消耗的时间、token 和成功率而不是面对一个“智能体框架”干瞪眼。看日志从“天书”变成“正常人话”瞬间舒服。4. 从零实操一步步搭建你自己的 AI 应用这一节是硬货。我按“最小前向路径”走一遍完整体验 AI 应用的诞生过程。环境默认 Python 3.10。4.1 第一步搭一个最简 LLM 调用服务先不碰任何框架裸调模型 API。# requirements.txt openai1.30.0 python-dotenv fastapi uvicorn# app.py import os from openai import OpenAI from dotenv import load_dotenv load_dotenv() _client OpenAI( api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL) or https://api.openai.com/v1 ) def chat(messages, modelNone): model model or os.getenv(LLM_MODEL, gpt-4o-mini) response _client.chat.completions.create( modelmodel, messagesmessages, temperature0.3, streamTrue ) full_text for chunk in response: delta chunk.choices[0].delta.content if delta: full_text delta return full_text几个关键点备注一下temperature0.3日常问答降到 0.3 能显著减少幻觉和发散。要创意写作再上调。流式接口用户体验差异巨大。等 20 秒全量返回用户早跑了逐字输出5 秒内就有“在干活”的感觉。生产环境务必流式。统一 base_url换模型厂商零改动。我在.env里放OPENAI_BASE_URL切换供应商时只改配置。4.2 第二步手写一个 RAG 全链路这是整个项目的核心部分。完整实现下来的记忆点异常清晰比看十篇教程管用。# rag.py from openai import OpenAI import sqlite3 class VectorStore: 零依赖向量库把向量存 SQLite检索时全表扫 def __init__(self, db_path): self.conn sqlite3.connect(db_path) self.conn.execute(CREATE TABLE IF NOT EXISTS vectors (id TEXT PRIMARY KEY, embedding TEXT, content TEXT)) self.client OpenAI() def embed(self, text): resp self.client.embeddings.create( modeltext-embedding-3-small, inputtext ) return resp.data[0].embedding def add(self, doc_id, content): vec self.embed(content) self.conn.execute( INSERT INTO vectors VALUES (?, ?, ?), (doc_id, json.dumps(vec), content) ) self.conn.commit() def search(self, query, top_k5): q_vec self.embed(query) rows self.conn.execute(SELECT * FROM vectors).fetchall() scored [] for doc_id, emb_json, content in rows: emb json.loads(emb_json) # 余弦相似度 dot sum(a*b for a, b in zip(q_vec, emb)) norm_q sum(a*a for a in q_vec) ** 0.5 norm_e sum(b*b for b in emb) ** 0.5 score dot / (norm_q * norm_e 1e-9) scored.append((score, content)) scored.sort(reverseTrue, keylambda x: x[0]) return scored[:top_k]思路很直白全表扫描 余弦相似度。性能远不如专业向量库但作为教学训练逻辑一目了然便于理解核心机制。生产环境记得换 Milvus 这类专用引擎。实际文档切分我用的是递归字符切分器附上核心逻辑def chunk_text(text, chunk_size512, overlap80): 按分隔符优先策略切分保留上下文重叠 separators [\n\n, \n, 。, , , ] current for char in text: current char if len(current) chunk_size: # 找最近的边界符切开 for sep in separators: idx current.rfind(sep) if idx ! -1: yield current[:idxlen(sep)] current current[idxlen(sep):] break else: yield current current if current: yield current切分参数不是拍脑袋。512 是中等长度平衡点太长embedding 平均了语义导致检索不精准太短上下文碎片化丢失逻辑。80 的重叠让相邻片段保持线索。文本结构化越强的文档切分效果越依赖分隔符匹配这是我实测多轮之后的结论。最后“检索 生成”拼装代码如下def rag_query(question): chunks store.search(question, top_k5) context \n\n.join(chunk for _, chunk in chunks) messages [ {role: system, content: f基于以下资料回答问题。资料不充分时直接说不知道不要编造。\n\n资料\n{context}}, {role: user, content: question} ] return chat(messages)这套组合足以支撑起一个企业内部知识库问答应用。生产化还要加上文档更新时自动重算 embedding、切分策略按文档类型配置、检索结果缓存等。但骨架就是这样。4.3 第三步给 Agent 加上工具调用从零实现 Agent绕不开工具调用。我用最简单的天气查询示例展示设计模式import json TOOLS [ { type: function, function: { name: get_weather, description: 查询城市当前天气, parameters: { type: object, properties: { city: {type: string, description: 城市名} }, required: [city] } } } ] def get_weather(city): # 这里接真实天气 API return f{city}多云24℃东风3级 def agent_run(user_input, max_steps5): messages [{role: user, content: user_input}] for step in range(max_steps): resp client.chat.completions.create( modelgpt-4o-mini, messagesmessages, toolsTOOLS, tool_choiceauto ) msg resp.choices[0].message if not msg.tool_calls: return msg.content messages.append(msg) for tool_call in msg.tool_calls: args json.loads(tool_call.function.arguments) result globals()[tool_call.function.name](**args) messages.append({ role: tool, tool_call_id: tool_call.id, content: result }) return 达到最大步数已停止。这个模式就是所有 Agent 框架的底层骨架。max_steps5为了防止模型陷入死循环。我实测过不设上限的 Agent 经常在工具调用里转圈出不来token 烧得人心疼。这也是生产环境永不省略的保险丝。4.4 第四步评估和回归——调优的照妖镜没有评估体系的 AI 应用优化全凭运气。自己手写 RAG 时一定要把评估搭起来EVAL_SET [ {question: 公司的年假制度是怎样的, expected_keywords: [累计工作年限, 年假天数]}, {question: 报销流程需要几步, expected_keywords: [提单, 审批, 财务打款]}, # ... 大概50条以上覆盖各业务场景 ] def evaluate(): total, hit 0, 0 for item in EVAL_SET: answer rag_query(item[question]) total 1 if all(kw in answer for kw in item[expected_keywords]): hit 1 return hit / total这个简单精确的“关键词命中率”指标能当回归测试的守门员。每次改切分策略、换 embedding、调 top_k跑一遍看分数变化——涨了就保留跌了就打回。更精细一点可以用LLM-as-judgedef judge_answer(question, answer, reference): prompt f你是评估助手。判断以下回答是否准确。 问题{question} 参考答案{reference} 模型回答{answer} 输出完全正确 / 部分正确 / 错误 return chat([...]) # 返回上述三选一实践下来先埋头搭好了评估集再做优化效率是高一个层级的。没有度量一切调优都等于蒙眼开车。4.5 第五步部署上线与监控工程化最后一步是让应用真正 7x24 跑起来。上 FastAPI 封装from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class Query(BaseModel): question: str app.post(/query) def query_endpoint(q: Query): answer rag_query(q.question) return {answer: answer}服务化暴露 HTTP 接口前端/后端系统都能直接调用。别用 Jupyter Notebook 当生产服务掉线一次口碑崩一次。部署容器化 Dockerfile 要点FROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [uvicorn, main:app, --host, 0.0.0.0, --port, 8000]重头戏在监控。我强烈建议每个 AI 工程在日志之外加一层可观测性埋点至少包含Token 消耗和成本每轮对话花了多少钱按用户聚合统计响应延迟分段embedding 耗时、检索耗时、生成耗时分开记录定位瓶颈一目了然检索命中率用户点没点“有帮助”是最直接的反馈信号失败率4xx/5xx、超时、拒绝率。实测过 Langfuse埋点完成后 dashboard 能直接显示每次调用的完整轨迹花钱花在哪清清楚楚排查问题效率极高。5. 实战避坑我在从零构建中踩过的真实问题跟完上述步骤你大概率会遇到下面这些坎我提前把雷给你排了。5.1 上下文吞金兽prompt 越堆越多最典型的问题。对话超过 20 轮上下文里塞满了历史每次调用把老账都翻出来算钱响应也慢得像蜗牛。排查思路先打印每次 request 的 token 数把缺失的信息暴露出来。解决三板斧早期消息摘要化详情见 3.2窗口长度限制滑动截断对用户输入做长度预检超额先压缩再入模型。5.2 检索命中率虚低问题出在切分不是模型有段时间我把切分调成 256 字后检索全乱套。查了半天才发现固定长度切分把语义拆碎了。比如“报销流程”四个字段落在前一片详细步骤说明在后一片检索到的片段全是残肢。解决思路切分策略与文档结构强相关。Markdown 标题明确的先按标题切法律/说明书类句子自成段的按句子切。确定策略后在评估集上跑分对比别用感觉选方案。5.3 模型乱编答案幻觉压不下去幻觉的根源是模型在“知识空白区”做了补全。我的处理矩阵系统提示里写死“资料里没写就回答不知道禁止自行补充”temperature从 0.7 压到 0.2效果很明显要求输出引用来源答案自带可追溯性对关键数据问题走“检索-抽取-比对”逻辑让模型输出结论后必须附上原文片段。5.4 线上响应慢卡在生成环节AI 应用的延迟大头几乎都在生成阶段。RAG 链路里 embedding 加检索大概占 300ms模型生成 500 token 要 6-10 秒。优化手段流式输出先让用户动起来小问题用小模型路由比如简单问答走 4o-mini复杂逻辑走大模型对高频问题做 exact-match 缓存需要重逻辑的场景加前缀缓存。5.5 第三方 API 抖动重试策略直接决定体验线上跑了一段时间你就会发现再好的 API 也会偶尔抽风。工程上要实装指数退避重试1s、2s、4s最大三次、超时设置connect 10s / read 60s、失败降级策略主模型故障切备选模型这些就是应用稳定性的压舱石。6. 从零到产线下一个升级目标是哪三个方向“from-scratch”项目跑通之后整个体系的升级路线一般有三个优先方向按投入产出比排优先级第一优先级数据飞轮。把线上用户真实提问采集下来每周筛选 badcase补充进评估集。做评估不是为了这一版是为了下一版。跑得越久评估集越厚系统调优的目标越清晰。第二优先级多路召回融合。单路向量检索能力有上限试着叠加 BM25 关键词检索、SQL 结构化查询最后用重排模型融合。QA 准确率通常能再上 5-10 个百分点。第三优先级领域微调若确实需要。RAG 跑通后如果某些专业术语的生成风格始终不对考虑用 LoRA 做一次轻量微调。这步是加分项前两个方向没做扎实的情况下不建议碰。实操中的心得体会永远记住一句话先跑通再调优先动手再换框架。只要核心链路是你亲手搭的后续无论接 LangGraph、上 Agent 工作流、做多模态都不会慌。地基是你打的话盖楼心里就有底了。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

栏目写完了,访客为什么还是找不到入口 2026/9/29 22:41:14

栏目写完了,访客为什么还是找不到入口

运营把栏目树铺得很满:产品、方案、帮助、关于我们,每一项下面还有子页。自己进后台,搜索一下就能定位。把链接发给客户,对方却在首页转了两圈,问:你们文档入口在哪? 这不是「再加一个 Banner」…

阅读更多 →
STM32开发参考方案全攻略:从选型到调试实战 2026/9/29 22:41:08

STM32开发参考方案全攻略:从选型到调试实战

很多刚开始碰 STM32 的朋友,问的问题其实都差不多:手里有一块板子,想做个项目,但不知道怎么找参考方案;或者已经在开发了,遇到问题不知道上哪找靠谱的资料和平台。我自己这些年从标准外设库一路用到 HAL 库…

阅读更多 →
小店做AI获客?5步让客户主动搜到你 2026/9/29 22:41:08

小店做AI获客?5步让客户主动搜到你

小店做AI获客?5步让客户主动搜到你很多老板还没意识到,客户找服务的习惯已经变了——以前是翻平台一条条看,现在是直接问AI:"附近哪家修车靠谱?"AI推荐哪家,客户就去哪家。为什么现在是做AI获客的…

阅读更多 →
上海24小时自助健身房系统开发实战:从架构到部署全指南 2026/9/29 22:41:08

上海24小时自助健身房系统开发实战:从架构到部署全指南

上海24小时自助健身房系统开发实战:从架构到部署全指南 在健身行业数字化转型的浪潮中,上海等一线城市的24小时自助健身房模式逐渐成为主流。这类系统需要解决的核心问题包括:无人值守环境下的用户身份验证、设备控制、计费结算、远程监控以及…

阅读更多 →
windows搭建git服务器 2026/9/29 22:41:08

windows搭建git服务器

在 Windows 上自建 Git 服务器,最省心、最轻量的选择是 Gitea。它是一个用 Go 语言写的开源 Git 托管平台,界面和操作体验很像 GitHub,但只有一个可执行文件,对 Windows 环境非常友好 下面是在 Windows 上快速搭建 Gitea 的步骤&a…

阅读更多 →
作者有话说|AI编程入门:TaoToken统一Key接入Claude Code与Cursor的settings.json配置骨架 2026/9/29 22:41:07

作者有话说|AI编程入门:TaoToken统一Key接入Claude Code与Cursor的settings.json配置骨架

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

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉