从零手搓AI工程:上下文管理、重试降级与缓存设计实战
1. 从零搭建AI工程能力为什么“手搓”比调包更值得投入这两年AI应用层的工具链成熟得吓人一个下午就能用现成框架拼出一个能跑通的demo。但如果你真在团队里带过人、接过需求就会发现一个尴尬的现实能调通API的人一抓一大把能把一个AI功能稳定扛住线上流量、控制住成本、还能持续迭代的人少得可怜。ai-engineering-from-scratch这个标题戳中的正是这个断层——它讲的不是“怎么用某个库”而是从零开始把AI工程这条链路自己走一遍把每个环节的“为什么”搞清楚。我自己是从传统后端转过来的早期也走过弯路一开始觉得模型调用嘛不就是发个请求解析个返回。直到线上出现超时雪崩、上下文被截断、成本一个月翻三倍才意识到AI工程和普通业务开发在工程约束上完全是两码事。这个项目适合三类人一是想从“会调API”进阶到“能扛系统”的开发者二是需要给团队搭建AI能力底座的技术负责人三是对底层原理好奇、不想永远当黑盒使用者的学习者。它解决的核心问题就一个——把AI应用从“能跑”变成“能扛、能控、能迭代”。下面我会按我实际落地时的思路把整条链路拆开讲整体架构怎么设计、核心环节怎么实现、参数怎么算、坑怎么避。所有内容都是基于常见工程实践补全的你照着抄作业基本能复现一套属于自己的最小可用系统。2. 整体架构设计与技术选型思路2.1 为什么先定边界再选工具从零做AI工程最容易犯的错是一上来就纠结用哪个框架。我的经验是先把边界画清楚这个系统要处理的是在线实时请求还是离线批量任务输入是短文本还是长文档对延迟的容忍度是几百毫秒还是几秒这几个问题决定了后面所有选型。举个具体的例子。如果是在线对话场景延迟敏感那模型推理就必须考虑流式输出和首token时间如果是离线文档处理那吞吐量和成本才是第一优先级批处理、并发控制、失败重试的设计完全不同。我见过有人拿离线那套同步阻塞的写法直接搬到在线服务上结果QPS一上来整个服务卡死。所以第一步不是写代码是画一张数据流图标清楚每个环节的输入输出、延迟预算、失败后的行为。提示延迟预算要提前分配。比如端到端要求2秒那模型推理最多给1.2秒检索给0.3秒前后处理各留0.25秒。不提前分后面优化时你根本不知道瓶颈在哪。2.2 分层架构把易变的和稳定的隔开我采用的是一种朴素但极其有效的分层接入层、编排层、能力层、基础设施层。接入层负责协议转换、鉴权、限流编排层负责流程控制、上下文管理、重试降级能力层是模型调用、检索、工具执行这些具体能力基础设施层是缓存、日志、监控、配置。这么分的好处是模型供应商换了、检索方案换了只动能力层编排逻辑不用重写。我早期把所有逻辑揉在一个函数里后来想换个模型做A/B测试改得痛不欲生。分层之后能力层做成统一接口换实现就是换个类的事。层级职责易变程度典型技术点接入层协议、鉴权、限流低网关、令牌桶编排层流程、上下文、降级中状态机、重试策略能力层模型、检索、工具高适配器模式、接口抽象基础设施缓存、日志、监控低连接池、埋点2.3 从零实现的价值到底在哪有人会问这些框架不都帮你做了吗我的回答是框架帮你做了但出问题时你得能定位。我遇到过缓存穿透导致模型被疯狂调用、遇到过重试策略写错导致请求翻倍、遇到过上下文拼接把token撑爆。这些问题的根因都在框架的“黑盒”里你不自己实现一遍排查时就是盲人摸象。从零实现还有一个隐性收益你会对成本有肌肉记忆。自己算过token、自己统计过调用量做容量规划时心里才有数。我现在的习惯是任何AI功能上线前先手算一遍最坏情况下的单次成本再乘以预估峰值QPS得出一个上限这个数字直接决定我要不要加缓存、要不要做降级。3. 核心环节拆解与实操要点3.1 上下文管理AI工程最容易翻车的地方上下文管理听起来简单做起来全是细节。核心要解决三个问题放什么进去、放多少、怎么放。放什么进去取决于任务。对话场景要保留历史轮次但要剔除无关的寒暄文档问答要放检索到的片段但要按相关性排序。我的做法是维护一个“上下文预算”比如模型窗口是8K token我预留2K给输出剩下6K给输入其中系统提示占500历史对话占2000检索片段占3500。每个部分都有独立的裁剪策略。放多少就是token计算。不同模型的tokenizer不一样中文和英文的切分差异很大。我实测下来中文大致是1个汉字对应1到1.5个token英文是1个单词对应1.3个token左右。但这个只能估算精确计算必须用对应模型的tokenizer。我一般会在编排层做一个token计数中间件每次拼接完上下文先算一遍超了就按优先级裁剪。怎么放涉及顺序和格式。经验是最重要的信息放开头和结尾中间容易被模型忽略。系统指令放最前面当前问题放最后面检索片段按相关性从高到低排列。格式上用清晰的分隔符比如用三个短横线隔开不同片段模型对结构化输入的遵循度明显更高。def build_context(system_prompt, history, retrieved_docs, current_query, max_tokens): # 优先级系统提示 当前问题 检索片段 历史 budget max_tokens parts [] sys_tokens count_tokens(system_prompt) parts.append(system_prompt) budget - sys_tokens query_tokens count_tokens(current_query) budget - query_tokens # 检索片段按相关性排序后填充 for doc in sorted(retrieved_docs, keylambda d: d.score, reverseTrue): doc_tokens count_tokens(doc.text) if doc_tokens budget: parts.append(doc.text) budget - doc_tokens else: break # 剩余预算给历史从最近往远填 for turn in reversed(history): turn_tokens count_tokens(turn) if turn_tokens budget: parts.insert(1, turn) budget - turn_tokens else: break parts.append(current_query) return \n---\n.join(parts)注意裁剪历史时一定要从最旧的开始丢但系统提示和当前问题永远不能丢。我见过有人把当前问题裁掉了模型答非所问排查半天才发现是裁剪逻辑写反了。3.2 模型调用层重试、超时、降级一个都不能少模型调用是外部依赖外部依赖就会失败。失败分几种网络超时、限流、服务端错误、返回内容异常。每种的处理策略不一样。超时要设两层连接超时和读取超时。连接超时短一点比如2秒读取超时长一点因为模型生成需要时间流式的话可以设30秒。我早期只设了一个总超时结果连接阶段卡住把整个请求拖死。重试要区分错误类型。网络抖动、限流这种可以重试但要注意指数退避不然重试风暴会把下游打垮。我用的策略是第一次失败等1秒第二次等2秒第三次等4秒最多重试3次。服务端5xx错误可以重试4xx错误重试没意义直接降级。降级方案要提前设计。模型不可用时是返回缓存结果、返回兜底话术、还是走规则引擎我的做法是分级降级一级降级走缓存二级降级走轻量模型三级降级返回预设话术。每级降级都要有监控告警不然降级了没人知道问题被掩盖。import time import random def call_model_with_retry(payload, max_retries3): for attempt in range(max_retries): try: resp model_client.call(payload, timeout(2, 30)) if resp.status_code 200: return resp elif 500 resp.status_code 600: raise ServerError(resp.status_code) else: # 4xx 不重试直接抛 raise ClientError(resp.status_code) except (Timeout, ServerError) as e: if attempt max_retries - 1: return fallback(payload) # 指数退避 抖动 wait (2 ** attempt) random.uniform(0, 0.5) time.sleep(wait) return fallback(payload)3.3 缓存设计省下的都是真金白银AI调用贵缓存是性价比最高的优化。但缓存什么、怎么失效有讲究。缓存粒度上我分三层完全相同的请求直接返回缓存语义相近的请求走向量缓存相似度超过阈值就复用检索结果单独缓存因为检索比模型调用便宜但比内存贵。失效策略上对话类场景缓存命中率低因为上下文一直在变这时候缓存意义不大。但问答类、知识库类场景问题重复率高缓存能省一大半成本。我实测过一个客服问答场景加了语义缓存后模型调用量降了六成。向量缓存的阈值要调。设太高命中率低设太低答非所问。我的经验是从0.95开始往下试观察命中率和用户反馈找到一个平衡点。一般0.92到0.95之间比较稳。提示缓存key的生成要包含模型版本和关键参数。我踩过坑换了模型但缓存没失效返回的还是旧模型的结果排查了很久。4. 完整实操流程与关键参数计算4.1 环境准备与依赖管理从零搭建环境要干净。我用的是Python依赖管理用虚拟环境加锁定文件。核心依赖就几个HTTP客户端、tokenizer、向量库客户端、监控SDK。不要一上来装一堆框架先把最小依赖跑通。python -m venv venv source venv/bin/activate pip install httpx tiktoken numpy pip freeze requirements.txt版本锁定很重要。我遇到过tokenizer库升级后切分规则变了导致token计数全错上下文频繁超限。锁定版本升级前先在测试环境验证。4.2 参数计算并发数、超时、重试的量化并发数不是拍脑袋定的。假设模型服务单实例QPS是10你有3个实例那理论并发是30。但你要留余量实际按70%算也就是21。再考虑你的服务本身能扛多少并发取两者最小值。超时计算P99延迟是2秒那读取超时设3秒比较合理留50%余量。连接超时按网络RTT的3倍算一般2秒够用。重试次数假设单次失败率1%重试3次后失败率降到百万分之一。但重试会增加下游压力所以重试次数和退避时间要一起算。我的公式是总重试时间 退避基数 × (2^重试次数 - 1)这个时间不能超过上游的总超时预算。参数计算依据推荐值连接超时网络RTT×32秒读取超时P99延迟×1.53秒最大重试失败率与压力平衡3次退避基数下游恢复时间1秒并发数min(下游容量×0.7, 自身容量)动态调整4.3 监控埋点没有度量就没有优化监控要埋四个维度延迟、错误率、token消耗、缓存命中率。延迟分P50、P95、P99错误率分类型token消耗分输入输出缓存命中率分完全匹配和语义匹配。我用的埋点方式是在编排层统一拦截每个环节进出都打点。这样不管能力层怎么换监控数据是连续的。埋点数据要能下钻比如发现P99延迟高能快速定位是模型慢还是检索慢。import time class MetricsMiddleware: def __init__(self, next_handler): self.next next_handler def handle(self, request): start time.time() try: result self.next.handle(request) self.record(success, time.time() - start, result.tokens) return result except Exception as e: self.record(error, time.time() - start, 0, type(e).__name__) raise4.4 上线前的压测与容量规划上线前必须压测。压测不是简单打满QPS要模拟真实流量分布长短请求混合、缓存命中与未命中混合、正常与异常混合。我一般用阶梯加压从10%峰值开始每5分钟加10%观察各项指标。容量规划的核心是找到拐点。当QPS增加但吞吐不再增加、延迟开始飙升时就是拐点。实际容量取拐点的70%。这个数字要写进容量文档扩容时按这个基准算。注意压测环境要和线上环境尽量一致尤其是网络延迟和下游服务的限流策略。我见过压测时好好的上线就崩因为压测环境没开限流。5. 常见问题与排查技巧实录5.1 超时与雪崩的排查思路超时雪崩的典型表现是一开始只是少量超时然后重试导致请求翻倍下游压力更大超时更多形成正反馈。排查时先看重试次数和退避策略再看下游容量。我的处理步骤第一步临时关闭重试切断正反馈第二步看下游监控确认是下游问题还是自身问题第三步如果是下游问题降级如果是自身问题限流。恢复后要复盘重试策略是不是太激进超时设置是不是太短。5.2 上下文超限的定位方法上下文超限报错通常很模糊只说超了不说哪超了。我的做法是在拼接前打印各部分token数超限时直接看日志。常见原因历史没裁剪、检索片段太多、系统提示太长。排查顺序先看系统提示是不是写了一大堆没用的再看检索片段是不是top_k设太大了最后看历史是不是没设上限。我一般把top_k设在3到5历史轮次设在5到10系统提示控制在500token以内。5.3 成本异常的快速定位成本突然涨先看调用量再看单次token。调用量涨可能是缓存失效、重试过多、被刷单次token涨可能是上下文变长、输出变长。我遇到过一次成本翻倍排查发现是缓存key里包含了时间戳导致缓存永远不命中。还有一次是重试策略写错失败后无限重试。所以成本监控要细到每个环节不能只看总数。问题现象可能原因排查动作解决方向延迟飙升下游慢/重试风暴看重试次数、下游监控降级、限流、调退避成本翻倍缓存失效/重试过多看命中率、调用量修缓存key、调重试答非所问上下文裁剪错/检索差看拼接日志、检索相关性调裁剪优先级、调top_k频繁超限历史未裁剪/top_k过大看各部分token数设上限、减top_k5.4 独家避坑经验第一个坑不要用同步阻塞的方式调模型。我早期用requests同步调QPS一上来线程池就满了。后来换成异步吞吐量翻了好几倍。第二个坑日志不要打全量上下文。上下文里有用户数据打全量既占空间又有隐私风险。我一般只打token数和hash值需要排查时再按请求ID捞。第三个坑降级方案要定期演练。我见过降级代码写了但从来没跑过真到降级时发现报错等于没降。现在我每个月手动触发一次降级确保链路是通的。第四个坑模型版本要固定。供应商悄悄升级模型是常事行为可能变化。我在请求里显式指定版本号升级前先做回归测试。6. 从能跑到能扛我的落地体会这套东西我从零搭过两遍第一遍踩坑无数第二遍顺畅很多。最大的体会是AI工程的难点不在AI在工程。模型能力是供应商给的但稳定性、成本、可观测性是你自己的。把上下文管理、重试降级、缓存、监控这几块做扎实系统就能扛住大部分场景。还有一个心得是不要追求一步到位。我第一版就上了全套监控和降级结果复杂度太高自己都维护不动。后来改成先跑通主链路再逐步加缓存、加重试、加监控每加一块都验证一块反而更稳。从零开始的价值就是你知道每一块砖是怎么砌上去的哪块松了你一眼就能看出来。

相关新闻

DeepSeek+AI大模型财务智能化落地实战:OCR、LSTM与规则引擎参数配置

DeepSeek+AI大模型财务智能化落地实战:OCR、LSTM与规则引擎参数配置

简介:这份PPT方案面向企业财务负责人、数字化转型团队及AI应用规划者,系统阐述如何借助DeepSeek与AI大模型推动财务管理智能化升级。内容围绕自动化财务处理、智能预算与成本控制、现金流预测与风控体系、数据驱动决策支持、税务合规与审计升级五大模块展…

2026/10/1 17:13:47 阅读更多 →
软考存储管理核心考点:分页分段、地址转换与页面置换算法全解析

软考存储管理核心考点:分页分段、地址转换与页面置换算法全解析

软考软件设计师的存储管理这块,说难不难,说简单真不简单。上午选择题基本是年年必考,分页、分段、段页式、虚拟存储、页面置换算法这几个词绑定出现,很多考生在地址转换计算上卡壳,在置换算法模拟题上丢分,…

2026/10/1 18:05:45 阅读更多 →
Model-Optimizer 实战:量化、剪枝与图优化加速模型推理

Model-Optimizer 实战:量化、剪枝与图优化加速模型推理

1. 从"模型能跑"到"模型跑得省":Model-Optimizer 到底在解决什么做模型部署的人迟早会撞上一堵墙:训练时一切正常,指标也漂亮,可一旦要上线,推理延迟、显存占用、单位算力成本这三座大山就压过来了…

2026/10/1 18:05:53 阅读更多 →

最新新闻

ESP32-S3 Mini vs C3 Mini怎么选?PSRAM和USB差异是关键

ESP32-S3 Mini vs C3 Mini怎么选?PSRAM和USB差异是关键

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

2026/10/2 17:43:51 阅读更多 →
8 kHz电机控制频率的物理约束与实时系统设计

8 kHz电机控制频率的物理约束与实时系统设计

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

2026/10/2 17:43:51 阅读更多 →
Oracle医疗系统实战:PL/SQL驱动的业务型数据库设计

Oracle医疗系统实战:PL/SQL驱动的业务型数据库设计

简介:本资源是一套面向高校数据库课程学习者的Oracle数据库课程设计实战项目,聚焦医院信息系统建模与开发,适用于Java与数据库交叉学习的初学者及课程设计、毕设选题阶段的学生。项目完整呈现从ER建模、SQL脚本建库到Java应用层连接操作的全流…

2026/10/2 17:43:51 阅读更多 →
OpenClaw源码解析1-加载入口:从entry.ts看CLI启动链路与TaoToken接入点

OpenClaw源码解析1-加载入口:从entry.ts看CLI启动链路与TaoToken接入点

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

2026/10/2 17:43:50 阅读更多 →
储能参与现货电能量-调频市场的双层决策:从论文到可跑代码的落地路径

储能参与现货电能量-调频市场的双层决策:从论文到可跑代码的落地路径

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

2026/10/2 17:43:50 阅读更多 →
【码动四季·秋】从 commit 到发版全自动:Conventional Commits + semantic-release 发布流水线实战

【码动四季·秋】从 commit 到发版全自动:Conventional Commits + semantic-release 发布流水线实战

本文为 AtomGit 码动四季开源同行征稿活动参与文章 开源仓库的文件都就位之后,我回头看了一眼 git 历史,发现一个尴尬的事实:仓库里的"版本"只有两个——“刚开始"和"现在”。中间 1287 次提交,没有版本号&am…

2026/10/2 17:42:50 阅读更多 →

日新闻

从零搭建AI工程化:模型之外的完整闭环

从零搭建AI工程化:模型之外的完整闭环

先搞清楚一件事:从零开始做 AI 工程化,难的从来不是调模型、写提示词,而是把一套原型 Demo 变成长得像是“正经系统”的东西。你手里可能已经有了能跑通的代码,也可能刚读完一些概念,但真到了要把它变成可维护、可观测…

2026/10/2 0:00:20 阅读更多 →
大模型训练显存估计与混合精度训练实战指南

大模型训练显存估计与混合精度训练实战指南

1. 大模型训练显存估计与混合精度训练详解显存不够用,几乎是每个做大模型训练的人都会撞上的第一堵墙。你可能也经历过:模型代码写完了,数据管道跑通了,满心欢喜地按下训练启动脚本,结果几秒钟后终端弹出一行红字——C…

2026/10/2 0:00:20 阅读更多 →
小样本学习数据集选型指南:27个真正可用的高质量数据集

小样本学习数据集选型指南:27个真正可用的高质量数据集

1. 小样本学习的“弹药库”:为什么你总在找数据集,却总找不到真正能用的? 小样本、数据集——这两个词最近半年在我处理的200多个AI项目咨询里,出现频率排进前三。不是模型调不好,不是代码写不对,而是卡在…

2026/10/2 0:00:20 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 19:40:48 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/1 19:41:40 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/10/1 20:05:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/2 5:26:06 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/2 6:09:11 阅读更多 →