尧图精选

从零搭建AI工程能力:提示词管理与上下文控制实战

🕒 发布时间:2026/10/1 19:14:44 📁 来源:尧图网络
1. 从零搭建AI工程能力为什么我劝你别一上来就调包这两年AI应用开发的门槛肉眼可见地降低了随便拉个框架几行代码就能跑通一个对话机器人。但我带过的新人里十个有八个在遇到真实业务场景时直接卡死——模型输出不稳定、上下文一长就崩、部署上线后延迟高得离谱、成本失控到老板天天追着问。问题的根源不在于他们不会用工具而在于跳过了对AI工程底层链路的理解直接站在了抽象层的最顶端。“ai-engineering-from-scratch”这个方向说白了就是把AI应用从原型到生产的每一层都亲手摸一遍。它不是让你去从头训练一个大模型那既不现实也没必要而是让你理解一个AI系统到底由哪些环节组成数据怎么流转、提示词怎么组织、上下文怎么管理、推理怎么调度、输出怎么校验、效果怎么评估、成本怎么控制。这些环节里任何一个没吃透上线后都会变成半夜把你叫醒的告警。这篇文章适合三类人一是刚转行做AI应用开发、只会调API但不知道背后发生了什么的工程师二是有传统后端经验、想系统补齐AI工程链路的开发者三是技术负责人需要判断团队在AI项目上的能力短板到底在哪。我会按照一个真实项目的推进节奏从整体设计思路讲到核心模块的实现细节再把我自己踩过的坑和排查经验整理出来。你不需要有机器学习背景但需要有一定的编程基础至少写过完整的项目。2. 整体架构设计一个AI应用到底该拆成几层2.1 为什么不能把所有逻辑塞进一个函数我见过太多项目最开始就是一个main.py里面从读取用户输入到调用模型到返回结果全在一起。原型阶段没问题但一旦要加缓存、要换模型、要做A/B测试、要记录日志这个文件就会膨胀成上千行的怪物改一处崩三处。合理的做法是按职责分层每一层只关心自己的事。我在实际项目中通常拆成这么几层接入层负责请求的接收和鉴权编排层负责决定这次请求要走什么流程提示层负责构造发给模型的指令模型层负责实际的推理调用后处理层负责校验和格式化输出评估层负责记录效果指标。每一层之间通过明确的接口通信这样你换掉任何一个模型供应商或者调整提示词策略都不会影响到其他层。这个分层思路的核心逻辑是隔离变化。AI工程和传统后端最大的区别在于模型的行为是不确定的你今天调好的提示词明天模型版本一更新可能就失效了。如果提示词散落在代码各处你根本没法快速定位和替换。分层之后所有提示词集中在提示层模型调用参数集中在模型层出问题时排查范围立刻缩小。2.2 技术选型别被框架绑架现在AI开发框架层出不穷LangChain、LlamaIndex、Haystack各有各的拥趸。我的建议是先用最薄的原生SDK把链路跑通再根据实际痛点决定要不要引入框架。原因很简单框架帮你省掉的代码量往往会在你遇到边界情况时以调试成本的形式还回来。比如你想自定义一个重试逻辑框架的抽象层可能让你找不到在哪里插入你想看实际发给模型的原始请求长什么样框架可能把它包装了好几层。我自己的做法是第一版永远用官方SDK直接写把请求构造、发送、解析、重试全部显式写出来。等这套逻辑稳定了如果发现某些模式反复出现再考虑抽象成内部工具库而不是直接套一个通用框架。具体到技术栈Python生态最成熟FastAPI做接入层足够轻量Pydantic做数据校验和序列化非常顺手Redis做缓存和限流PostgreSQL存日志和评估数据。模型调用方面OpenAI的SDK设计得比较干净其他供应商的接口也大同小异封装一层统一的调用接口就行。这套组合的好处是每一块你都能完全掌控出了问题知道去哪里看。2.3 数据流设计请求进来之后发生了什么一个完整的AI请求链路我通常设计成这样的数据流用户请求先经过接入层做参数校验和身份识别然后进入编排层。编排层根据请求类型决定走哪条处理路径比如是简单的单轮问答还是需要检索增强的多步推理。确定路径后编排层从提示层获取对应的提示词模板把用户输入和上下文数据填充进去构造出完整的模型请求。模型层收到请求后负责处理重试、超时、降级这些可靠性问题把请求发给实际的模型服务。拿到响应后后处理层做格式校验、敏感内容过滤、结构化解析如果解析失败还要决定是重试还是返回兜底结果。最后评估层异步记录这次请求的耗时、token消耗、输出质量指标用于后续的监控和优化。这条链路里每个环节都有可以优化的空间但前提是你得先把它显式地画出来。我习惯在项目初期就用一个简单的文档把数据流写清楚标注每个环节的输入输出和异常处理策略后面写代码时直接对照着实现效率高很多。3. 核心模块实现提示词、上下文与输出控制3.1 提示词工程从硬编码到模板化管理提示词是AI应用里最容易被低估的部分。很多人觉得提示词就是写一段话让模型干活但实际上提示词是需要像代码一样被版本管理和测试的。我刚开始做的时候也是把提示词直接写在代码里后来发现几个问题一是改提示词要重新部署二是没法对比不同版本的效果三是同一个提示词在不同场景下需要微调但容易改乱。后来我改成用模板文件管理每个提示词是一个独立的文件用变量占位符标记需要动态填充的部分代码里只负责读取模板和填充变量。模板文件的结构我通常这样组织文件头部用注释写明这个提示词的用途、适用场景、已知限制和最后修改时间。正文部分把系统指令、上下文占位、用户输入占位、输出格式要求分块写清楚。比如一个客服场景的提示词模板系统指令部分会写明角色定位和回答风格上下文占位会插入历史对话和知识库检索结果输出格式要求会指定返回JSON结构。这样做的好处是提示词的修改不需要动代码测试的时候可以直接对比两个模板文件的输出差异回滚也只需要换回旧文件。我还会给每个模板文件配一个简单的测试用例文件里面放几条典型的输入和期望的输出特征每次修改提示词后跑一遍测试确保没有引入回归问题。3.2 上下文管理token预算怎么算怎么花上下文窗口是AI工程里最现实的约束之一。模型能处理的token数量有限而你的对话历史、检索结果、系统指令都要占额度。如果不做管理要么请求被截断导致信息丢失要么token消耗爆炸导致成本失控。我的做法是给每次请求做一个token预算分配。假设模型支持128K token的上下文我不会全部用满而是留出20%的余量给输出。剩下的额度里系统指令固定占一部分检索结果按相关性排序后动态分配对话历史按时间倒序保留最近的若干轮。具体分配比例取决于场景客服场景可能检索结果占大头创作场景可能历史对话更重要。计算token数量不需要特别精确用简单的字符数除以4来估算英文中文按字符数除以1.5来估算误差在可接受范围内。关键是在构造请求之前就做好预算检查如果发现超出限额按照优先级从低到高裁剪内容。我通常把裁剪逻辑写成独立的函数输入是各个上下文片段和它们的优先级输出是裁剪后的结果这样测试起来很方便。还有一个容易被忽略的点是上下文的顺序。模型对上下文开头和结尾的内容注意力更强中间部分容易被忽略。所以重要的指令和关键信息应该放在开头或结尾检索结果如果有多条最相关的放在最前面。这个细节在长上下文场景下影响很大我实测过同样的内容换个顺序输出质量能差出两成。3.3 输出控制让模型返回你能用的东西模型输出不可控是新手最头疼的问题。你让它返回JSON它可能给你返回一段带解释的JSON你让它只输出答案它可能给你加一句“根据我的理解”。解决这个问题不能靠运气得靠结构化的输出约束和严格的校验。首先在提示词里明确输出格式最好给出一个具体的示例。比如你要JSON就在提示词里写清楚字段名、字段类型和一个完整的示例值。其次利用模型供应商提供的结构化输出功能比如有些API支持指定response_format为JSON这样模型会在底层做约束比纯靠提示词可靠得多。但即使这样也不能假设输出一定合法。后处理层必须做校验解析失败时要有兜底策略。我的做法是第一次解析失败后把解析错误信息和原始输出一起发回给模型让它修正格式重新输出最多重试两次。如果还是失败就返回一个预定义的默认结构同时记录一条告警日志。这个重试逻辑看起来简单但能解决八成以上的格式问题。另外对于关键字段我会在提示词里要求模型输出置信度或者让模型标注哪些部分是推测的。这样在后处理时可以根据置信度决定是否直接采用还是需要人工复核。这个做法在信息抽取类任务里特别有用能显著降低错误信息直接流向用户的风险。4. 可靠性工程重试、降级与成本控制4.1 重试策略不是所有错误都值得重试模型调用失败的原因五花八门网络超时、速率限制、服务端临时故障、请求本身不合法。如果不加区分地重试不仅浪费时间和额度还可能让问题变得更糟。比如请求本身格式错误你重试一百次也不会成功。我的重试策略是这样的先对错误分类。网络超时和5xx服务端错误属于可重试的用指数退避策略第一次等1秒第二次等2秒第三次等4秒最多重试三次。速率限制错误需要看返回的Retry-After头按指示等待。4xx客户端错误一般不重试直接记录日志并返回错误。还有一种特殊情况是模型返回了内容但内容质量不达标这种属于业务层面的重试需要单独处理。指数退避的实现很简单但有个细节容易忽略加随机抖动。如果多个请求同时失败同时重试会在同一时刻再次冲击服务端形成惊群效应。在退避时间上加上一个随机的小偏移量比如正负20%就能有效分散重试请求。这个技巧在并发量大的时候特别重要我吃过亏之后每次都加上。4.2 降级方案模型挂了怎么办生产环境不能假设模型服务永远可用。降级方案要在设计阶段就考虑好而不是等出事再临时想。我通常准备三级降级第一级是切换到备用模型比如主用模型超时或报错时自动切到另一个供应商的模型虽然效果可能有差异但至少能用第二级是返回缓存结果对于重复性高的查询如果缓存里有近期的高质量回答直接返回缓存并标注第三级是兜底回复告诉用户当前服务繁忙请稍后重试同时把请求存入队列稍后处理。降级策略的触发条件要明确不能靠感觉。我一般设置几个硬指标连续失败次数超过阈值、平均延迟超过阈值、错误率超过阈值。这些指标用滑动窗口统计避免偶发波动触发降级。降级状态也要有恢复机制比如每隔一段时间发一个探测请求成功了就自动切回主路径。这里有个经验降级方案要定期演练。我见过太多项目降级代码写了但从来没跑过真出事的时候发现降级逻辑本身有bug。每隔几周手动触发一次降级走一遍完整流程确保关键时刻不掉链子。4.3 成本控制token就是钱AI应用的成本大头在token消耗上尤其是输入token。很多人只关注输出长度忽略了输入里塞了多少东西。一个典型的RAG应用每次请求可能塞进去几千token的检索结果如果检索质量不高这些token就是纯浪费。控制成本要从几个方面入手。首先是缓存对于相同或相似的请求直接返回缓存结果。缓存的key可以用请求的语义哈希比如把用户输入做归一化处理后计算哈希相似度高的请求命中同一个缓存。缓存的有效期根据场景定事实类查询可以长一些时效性强的短一些。其次是检索优化检索结果不是越多越好。我通常先检索一个较大的候选集然后用重排序模型或者简单的相关性打分筛出最相关的几条只把这几条塞进上下文。实测下来从检索20条精简到5条效果几乎不降但token消耗能减少六成以上。最后是输出长度控制在提示词里明确要求简洁回答避免模型长篇大论。对于结构化输出指定字段后模型不会额外发挥。我还会在评估层统计每次请求的token消耗按用户和场景维度做报表找出消耗异常的点针对性优化。5. 效果评估与持续迭代5.1 离线评估上线之前怎么知道行不行AI应用最麻烦的地方在于没有一套放之四海而皆准的评估指标。准确率、召回率这些传统指标在生成式任务里往往不适用因为同一个问题可能有多种正确的回答方式。我的做法是构建一个场景化的评估集里面包含典型的输入和期望的输出特征而不是精确的答案。评估集的构建从项目第一天就开始每次发现bad case就加进去。评估集里的每条数据包含输入、期望的输出特征比如必须包含某些信息、不能包含某些内容、格式必须符合某个结构、以及一个权重表示这条数据的重要程度。跑评估的时候用当前版本的提示词和模型配置批量处理评估集然后逐条检查输出是否满足期望特征最后算一个加权通过率。这个通过率不需要追求100%因为生成式任务本身就有不确定性。关键是看趋势每次修改提示词或调整参数后跑一遍评估通过率上升说明改对了下降就要回滚。我一般把评估脚本做成命令行工具一条命令跑完输出报告方便快速迭代。5.2 在线监控上线之后怎么发现问题离线评估通过不代表线上就没问题真实用户的输入分布和评估集往往有差异。线上监控要关注几个维度延迟P50和P99都要看P99高说明有长尾请求需要优化错误率按错误类型分类统计区分是模型问题还是系统问题token消耗按用户和场景统计发现异常消耗输出质量这个最难自动化我通常用抽样人工评估加用户反馈信号来近似。用户反馈信号包括用户是否重新提问了同一个问题说明上次没答好、用户是否点了不满意按钮、用户是否在对话中表达了负面情绪。这些信号不精确但能帮你发现明显的问题。我还会定期抽样一批线上请求人工标注质量和离线评估集做对比看看有没有分布偏移。监控数据要能下钻比如发现某个场景的错误率突然升高要能快速定位到是提示词问题、模型问题还是数据问题。这要求日志记录足够详细每次请求的完整上下文、模型参数、响应内容、耗时都要存下来。存储成本不低但排查问题时没有这些数据会非常被动。5.3 迭代节奏小步快跑还是憋大招AI应用的迭代节奏和传统软件不太一样。传统软件可以按版本发布AI应用更适合小步快跑因为模型行为的不确定性太高一次改太多东西出了问题根本不知道是哪个改动导致的。我的做法是每次只改一个变量要么改提示词要么换模型要么调参数不同时改多个。改完跑离线评估通过了再上小流量线上测试观察监控指标没有异常再全量。这个流程听起来慢但实际上因为每次改动小、验证快整体迭代速度反而比憋大招快。版本管理也很重要。提示词、模型配置、评估集都要有版本号每次上线记录清楚用了哪个版本。出问题时能快速回滚到上一个稳定版本。我见过没有版本管理的项目出事后连上次改了什么都不知道排查全靠猜那才是真的慢。6. 常见问题与排查技巧实录6.1 模型输出不稳定的排查思路模型输出时好时坏是最常见的问题。排查的时候先确认输入是否一致同样的输入如果输出差异很大说明模型本身有随机性可以通过设置temperature参数来降低随机性但注意temperature太低会导致输出过于死板一般设在0.1到0.3之间比较平衡。如果输入一致、参数也固定输出还是不稳定那就要检查上下文里有没有引入噪声。比如检索结果里混入了不相关的文档或者历史对话里有错误信息被模型当成了事实。这种情况下要优化检索质量或者在提示词里明确告诉模型忽略哪些内容。还有一种情况是模型版本更新导致的输出变化。供应商升级模型后行为可能改变这是最隐蔽的问题。我的做法是在模型调用层记录实际使用的模型版本号发现输出质量下降时先对比版本号有没有变化。如果有变化要么回滚到旧版本要么重新调优提示词适配新版本。6.2 延迟过高的优化路径延迟高的问题要分段排查。先看模型调用耗时这个通常占大头。如果模型调用本身就慢可以考虑换更快的模型或者用流式输出让用户先看到部分结果。流式输出对感知延迟的改善非常明显用户看到第一个字出来就不会觉得等太久。如果模型调用不慢那就要看前后处理的耗时。检索、重排序、格式校验这些环节如果实现得不够高效累积起来也很可观。我遇到过检索用了同步的HTTP调用改成异步批量请求后整体延迟降了一半。还有JSON解析用了低效的库换一个性能好的库也能省不少时间。缓存是降低延迟的利器。对于重复性高的请求缓存命中时延迟可以降到毫秒级。缓存的粒度要设计好太粗了命中率低太细了管理复杂。我一般按语义相似度做缓存相似请求返回缓存结果同时异步更新缓存内容。6.3 成本超支的紧急止血方案成本突然超支时先别急着改代码先看监控数据定位消耗来源。是某个用户疯狂调用还是某个场景的请求量突增还是单次请求的token消耗异常。定位到来源后再针对性处理。如果是单次请求token消耗异常检查上下文里是不是塞了太多东西。我遇到过检索模块的bug导致每次返回几百条结果全塞进上下文修复后成本直接降了八成。如果是请求量突增先确认是不是正常业务增长是的话考虑优化缓存命中率或者换更便宜的模型处理简单请求。紧急止血的手段包括临时降低上下文长度限制、启用更激进的缓存策略、对非核心场景切换到便宜模型、设置用户级别的速率限制。这些手段会影响体验所以要在止血后尽快找到根因做正式修复不能一直靠限流撑着。6.4 常见问题速查表问题现象可能原因排查方法解决方向输出格式不对提示词约束不够明确检查提示词是否有格式示例增加格式示例启用结构化输出输出内容重复temperature过低或提示词有循环诱导检查temperature参数和提示词适当提高temperature修改提示词延迟突然升高模型服务波动或上下文过长分段计时检查token数量切换模型裁剪上下文成本异常增长缓存失效或检索结果过多查看token消耗分布修复缓存优化检索数量回答质量下降模型版本更新或上下文噪声对比模型版本检查检索质量回滚版本或重新调优提示词请求频繁失败速率限制或网络问题查看错误码分布调整重试策略增加备用通道6.5 几个我踩过的坑第一个坑是过度依赖模型的自解释。早期我让模型在输出JSON的同时解释每个字段的含义结果模型经常在JSON外面加一堆说明文字解析起来非常麻烦。后来改成让模型只输出JSON解释信息放在单独的字段里解析成功率立刻上去了。第二个坑是忽略冷启动问题。缓存刚上线时是空的所有请求都穿透到模型延迟和成本反而比没缓存时更高。后来加了缓存预热机制项目启动时先跑一批常见请求把缓存填上平稳过渡。第三个坑是评估集过时。项目跑了一段时间后用户的实际提问方式和评估集里的差异越来越大评估通过率很高但线上效果不好。后来养成了习惯每月从线上抽样一批请求补充到评估集里保持评估集和真实分布的同步。第四个坑是重试逻辑没有幂等保证。有些操作比如写数据库重试时如果第一次其实成功了只是响应丢了第二次重试就会产生重复数据。后来所有涉及副作用的操作都加了幂等键重试前先检查是否已经执行过。7. 从能跑到好用工程化的最后一公里把AI应用做到能跑不难做到好用需要补很多工程细节。日志和可观测性是基础每次请求的完整链路都要能追踪出了问题能快速定位。我通常用请求ID串联所有环节的日志从接入层到模型调用到后处理一个ID查到底。配置管理也很关键。模型名称、超时时间、重试次数、缓存有效期这些参数不能硬编码要放在配置文件或配置中心里支持不重启修改。我见过因为改一个超时时间要重新部署整个服务的项目迭代效率极低。测试覆盖要包括单元测试和集成测试。单元测试覆盖各个模块的逻辑集成测试跑完整的请求链路。AI应用的测试难点在于输出不确定我的做法是测试断言不检查精确输出而是检查输出是否满足结构约束和关键特征。比如检查JSON能否解析、必填字段是否存在、数值是否在合理范围内。文档不是可有可无的。提示词的设计意图、评估集的构建标准、降级策略的触发条件这些都要写清楚。新人接手时能快速理解为什么这么设计而不是对着代码猜。我习惯在项目根目录放一个DESIGN.md把架构决策和关键取舍记录下来比代码注释更系统。最后说一个心态上的体会AI工程和传统软件工程最大的区别是接受不确定性。你没法保证模型每次输出都完美但可以通过工程手段把不确定性控制在可接受的范围内。重试、降级、校验、监控这些手段组合起来就能让一个本质上不确定的系统表现得足够可靠。这个思路转变过来之后很多问题就有了解决的方向。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →