从零搭建AI工程体系:模型接入、提示词管理与上下文处理实战
1. 从零搭建AI工程体系为什么我劝你别一上来就啃框架这两年AI应用开发的门槛肉眼可见地降低了随便拉个程序员过来告诉他“调一下API就能跑”半天时间就能给你整出一个能对话的Demo。但如果你真在团队里带过项目就会发现一个扎心的现实能跑通Demo的人一抓一大把能把AI能力稳稳当当塞进生产环境、扛住真实流量、控制住成本、还能持续迭代的人少得可怜。这个差距就是“会调接口”和“懂AI工程”之间的鸿沟。ai-engineering-from-scratch这个标题说的就是从零开始构建AI工程能力这件事。它不是教你某个框架的API怎么用也不是带你复现某篇论文而是把AI应用从想法到上线这条链路上那些真正会卡住你的环节一个一个拆开讲清楚。适合谁看如果你已经会用Python写点脚本调过几次大模型接口但一到要做成产品就心里没底那这篇内容就是写给你的。如果你是完全的新手也没关系我会尽量用生活化的类比把底层逻辑讲透让你知道每一步在干什么、为什么要这么干。我自己的经历比较典型最早做AI项目的时候满脑子都是“模型选哪个”“提示词怎么写”结果上线第一周就被现实教育了——接口超时没人管、Token消耗失控、用户输入稍微刁钻一点就胡言乱语、想换个模型发现代码耦合得根本动不了。后来踩的坑多了才慢慢意识到AI工程的核心根本不是模型本身而是围绕模型构建的那一整套工程体系。这套体系包括什么怎么从零搭起来每个环节有哪些坑这就是接下来要展开的内容。2. AI工程到底在工程什么核心思路与整体设计2.1 先搞清楚AI应用和传统应用的本质区别传统软件的逻辑是确定的输入A经过一堆if-else和数据库操作输出B。你写测试用例覆盖所有分支心里就有底。AI应用完全不是这个逻辑。你输入一段话模型输出什么取决于模型权重、提示词、上下文、温度参数、甚至当天的服务负载。它本质上是概率性的同样的输入两次输出可能不一样。这个差异带来一个根本性的转变传统工程追求的是“确定性”AI工程追求的是“可控的不确定性”。你没法保证模型每次都说对话但你可以通过一系列工程手段把“说错话”的概率压到可接受的范围并且在它说错话的时候能及时发现、快速兜底。这就是AI工程要解决的核心问题。我见过太多团队在这个认知上栽跟头。他们用传统软件测试的思路去测AI应用写一堆断言结果发现根本没法写——模型输出是自然语言你怎么断言正确的做法是建立评估体系用一批标准问题去跑看整体表现而不是纠结单次输出。这个思维转变是从零构建AI工程能力的第一道坎。2.2 从零搭建的四个核心模块基于我自己的实践一个完整的AI工程体系可以拆成四个模块它们的关系有点像开一家餐厅第一个模块是模型接入层相当于餐厅的厨房。你得决定用哪个模型、怎么调用、怎么管理多个模型。是只用一家供应商还是多家备份是直接调API还是自己部署开源模型这个决策直接影响后面的成本、延迟和可控性。第二个模块是提示词与上下文管理相当于菜谱。同样的食材菜谱不同出来的菜天差地别。提示词工程不是玄学它有一套可复用的方法论怎么设定角色、怎么给示例、怎么约束输出格式、怎么处理多轮对话的上下文。这部分做得好能让同一个模型的表现提升一大截。第三个模块是应用编排与业务逻辑相当于餐厅的服务流程。用户点单、后厨做菜、服务员上菜中间还有催单、退菜、加菜各种情况。AI应用也一样一次用户请求背后可能涉及多次模型调用、外部工具调用、数据库查询、结果后处理。怎么把这些串起来怎么处理中间某一步失败的情况这是编排层要解决的。第四个模块是监控、评估与迭代相当于餐厅的品控和客户反馈。你得知道今天出了多少菜、哪道菜被退得最多、客户满意度怎么样。AI应用需要监控延迟、Token消耗、错误率、输出质量还需要建立评估集每次改动后跑一遍确保没有退化。这四个模块缺一个都跑不远。很多人的问题在于只关注第一个模块觉得“模型选好了就万事大吉”结果后面三个模块全是窟窿上线就崩。2.3 为什么建议从“薄封装”开始而不是直接上重型框架现在市面上有很多AI应用开发框架功能很全什么都有。但我个人的建议是如果你是从零开始先别急着上框架。原因很简单框架帮你屏蔽了细节但也屏蔽了你对细节的理解。当你遇到框架解决不了的问题时你会发现自己完全不知道从哪下手。更好的路径是先用最薄的方式把链路跑通直接调模型API自己写提示词拼接自己处理上下文自己写重试逻辑。这个过程会让你真切地感受到每个环节在干什么、哪里容易出问题。等你把这条薄链路跑顺了再根据实际痛点去引入框架这时候你是有判断力的知道框架帮你解决了什么、代价是什么。我自己的做法是第一个版本永远是最土的一个Python文件几十行代码直接调接口打印结果。跑通之后再逐步把提示词管理、上下文管理、错误处理、日志监控这些拆出来。每拆一步都是因为实际遇到了问题而不是因为“架构应该这样”。这种演进式的做法比一上来就设计一个大而全的架构要靠谱得多。3. 核心细节拆解模型接入、提示词管理与上下文处理3.1 模型接入层别把鸡蛋放在一个篮子里模型接入看起来简单不就是调个API吗但真到生产环境要考虑的事情不少。首先是供应商选择。不同模型的能力、价格、延迟、稳定性都不一样。有的模型推理能力强但贵有的模型便宜但容易胡言乱语有的模型响应快但上下文窗口小。我的建议是至少接入两家供应商一家主力一家备份。主力模型处理大部分请求备份模型在主模型不可用或者成本超预算时顶上。这样做的代价是代码要抽象一层但收益是巨大的——你不会因为某家服务抖动就整个应用挂掉。其次是接口封装。不要直接在业务代码里调模型API一定要封装一层。封装层要做几件事统一不同供应商的接口格式、处理认证和密钥管理、实现重试和超时、记录调用日志。这层封装不需要很复杂但必须有。我见过太多项目模型调用散落在几十个文件里想换个模型得改半天这就是没有封装的后果。关于超时和重试这里有个经验值可以参考。模型调用的超时时间一般设置在30到60秒比较合理。太短了稍微复杂一点的请求就超时太长了用户等不起。重试策略建议用指数退避第一次等1秒第二次等2秒第三次等4秒最多重试2到3次。但要注意不是所有错误都值得重试。网络超时、服务限流可以重试但参数错误、认证失败重试多少次都没用反而浪费资源。还有一个容易被忽略的点是流式输出。如果你的应用是对话式的用户对延迟很敏感那一定要用流式输出。模型生成一个字就返回一个字用户看到的是文字在逐渐出现体感延迟会低很多。实现流式输出需要你的整个链路都支持流式从模型接口到后端到前端中间任何一环做了缓冲流式就白做了。3.2 提示词管理把提示词当代码来管提示词是AI应用里最容易被低估的部分。很多人觉得提示词就是写一段话随便改改就行。但实际上提示词是AI应用的核心资产之一它应该像代码一样被管理有版本、有测试、有评审。提示词的结构化是第一步。一个好的提示词通常包含几个部分角色设定、任务描述、约束条件、输出格式、示例。角色设定告诉模型“你是谁”任务描述告诉模型“要干什么”约束条件告诉模型“不能干什么”输出格式告诉模型“怎么输出”示例告诉模型“大概长这样”。这五个部分不一定要全有但结构清晰比一大段话效果好得多。我举个实际例子。假设你要做一个客服助手提示词可以这样组织角色你是一名专业的电商客服负责处理订单相关问题。 任务根据用户的问题给出准确、友好的回复。 约束 - 只回答订单相关的问题其他问题礼貌拒绝 - 不确定的信息不要编造引导用户联系人工客服 - 回复控制在100字以内 输出格式直接输出回复内容不要加任何前缀 示例 用户我的订单什么时候到 回复您好请提供订单号我帮您查询物流状态。这种结构化的提示词比“你是一个客服请回答用户问题”这种一句话提示词效果要好得多。因为模型能清楚地知道自己的边界在哪里。提示词的版本管理也很重要。每次修改提示词都应该记录改了什么、为什么改、改完之后评估指标有没有变化。我自己的做法是在代码仓库里建一个prompts目录每个提示词一个文件用Git管理。修改提示词走正常的代码评审流程改完之后跑一遍评估集确认没有退化再合并。这个流程看起来麻烦但能避免很多“改了一个词线上效果崩了”的事故。提示词的测试是另一个关键。你需要准备一批标准问题覆盖正常情况、边界情况、恶意输入每次改提示词后跑一遍看通过率。这个评估集不需要很大几十条就够但一定要有。没有评估集你改提示词就是盲改改好改坏全靠感觉。3.3 上下文管理多轮对话的记忆与遗忘多轮对话是AI应用的常见需求但上下文管理是个技术活。模型本身是无状态的它不记得上一轮说了什么。你要把历史对话拼接到当前请求里模型才能“记得”。但模型的上下文窗口是有限的你不能无限拼接否则要么超长报错要么成本爆炸。上下文窗口的计算需要心里有数。不同模型的窗口大小不一样有的支持几万Token有的支持几十万Token。Token是模型处理文本的基本单位中文大概一个字对应1到2个Token英文一个单词对应1到2个Token。你要根据窗口大小和每次对话的平均长度估算能保留多少轮历史。上下文裁剪策略有几种常见做法。最简单的是保留最近N轮超过就丢掉最早的。这种做法简单但粗暴可能丢掉重要信息。稍微好一点的是保留最近N轮加上第一轮因为第一轮往往包含关键设定。再复杂一点的是用模型自己来判断哪些历史重要、哪些可以丢但这会增加一次模型调用成本和延迟都上去了。我自己的经验是对于大多数客服、助手类应用保留最近5到10轮对话就够了。如果对话很长可以在中间做一次摘要把前面的对话压缩成一段话保留关键信息。摘要可以用模型来做也可以用规则来做看你的精度要求。还有一个细节是上下文的格式。历史对话怎么拼接到请求里也有讲究。常见的方式是用特定的标记区分用户和助手比如用户xxx和助手xxx。有些模型对格式敏感格式不对效果会差很多。建议参考模型官方文档推荐的格式不要自己发明。4. 实操过程从零搭建一个可用的AI应用骨架4.1 环境准备与项目结构假设我们要从零搭一个AI问答应用支持多轮对话、流式输出、多模型切换。先看项目结构ai-app/ ├── config/ │ └── settings.py # 配置管理 ├── core/ │ ├── model_client.py # 模型接入封装 │ ├── prompt_manager.py # 提示词管理 │ └── context_manager.py # 上下文管理 ├── services/ │ └── chat_service.py # 业务编排 ├── utils/ │ ├── logger.py # 日志 │ └── retry.py # 重试工具 ├── prompts/ │ └── chat_assistant.txt # 提示词文件 ├── tests/ │ └── eval_set.json # 评估集 └── main.py # 入口这个结构不复杂但把关注点分开了。配置、核心能力、业务逻辑、工具、提示词、测试各归各的后面扩展的时候不会乱。环境准备方面Python 3.10以上装几个基础库httpx用于发请求pydantic用于配置管理python-dotenv用于读环境变量。不需要一上来就装一堆框架先把最核心的跑通。4.2 模型接入层的实现要点模型接入层的核心是抽象。我们定义一个ModelClient基类不同供应商实现各自的子类。基类定义统一的方法chat()用于非流式调用chat_stream()用于流式调用。class ModelClient: def chat(self, messages, **kwargs): raise NotImplementedError def chat_stream(self, messages, **kwargs): raise NotImplementedError具体实现的时候有几个关键点。认证信息从环境变量读不要硬编码在代码里。超时设置要合理连接超时和读取超时分开设。重试逻辑封装在装饰器里业务代码不用关心。日志记录要记录请求的模型、Token数、耗时、是否成功这些数据后面监控要用。流式输出的实现稍微复杂一点。以HTTP流式为例你需要逐行读取响应解析出每个数据块提取其中的文本增量然后yield出去。中间要处理各种边界情况数据块不完整、JSON解析失败、流意外中断。这些都要有兜底逻辑不能让一个异常把整个流搞崩。4.3 提示词与上下文的组装流程一次完整的对话请求组装流程是这样的从prompt_manager加载系统提示词从context_manager获取历史对话把系统提示词、历史对话、当前用户输入按顺序拼接成消息列表检查总Token数如果超限就裁剪历史把消息列表传给model_client发起调用这个流程里Token计数是个关键环节。你需要在拼接完成后估算Token数如果超过模型窗口的80%就触发裁剪。估算Token可以用简单的规则中文按字数乘1.5英文按单词数乘1.3也可以用专门的Token计数库。精度要求不高的话简单规则就够用留20%的余量作为缓冲。裁剪策略我一般用“保留最近N轮系统提示词”的方式。系统提示词永远保留因为它定义了模型的行为边界。历史对话从最近往前保留直到Token数降到阈值以下。如果最近几轮特别长可能只保留一两轮就够了。4.4 业务编排与错误处理业务编排层是把各个模块串起来的地方。以聊天服务为例它的职责是接收用户输入调用上下文管理获取历史调用提示词管理获取系统提示组装请求调用模型处理响应更新上下文返回结果。错误处理是编排层的重点。模型调用可能失败失败的原因有很多种网络超时、服务限流、认证过期、参数错误、内容被拦截。不同的错误要有不同的处理策略。网络超时和限流可以重试认证过期要刷新凭证参数错误要记录日志并返回友好提示内容被拦截要告知用户换个说法。我一般会定义一个错误分类函数把各种异常映射到几个大类然后针对每个大类写处理逻辑。这样代码清晰也好维护。还有一个重要的是降级策略。当主模型不可用时自动切换到备份模型。切换的时候要注意不同模型的提示词格式可能不一样输出风格也可能不一样要做好适配。降级不是简单的换个接口调用而是要保证用户体验尽量平滑。5. 常见问题与排查技巧实录5.1 模型输出不稳定怎么办这是最常见的问题。同样的输入有时候回答得很好有时候答非所问。排查思路是这样的先看温度参数。温度越高输出越随机。如果你的应用需要稳定输出把温度调到0或者0.1。但要注意温度太低输出会变得很死板缺乏灵活性。一般对话场景用0.7左右需要精确输出的场景用0.1到0.3。再看提示词。提示词里如果有模糊的表述模型就会自由发挥。把要求写具体把边界划清楚。比如“回答要简洁”就不如“回答控制在50字以内”明确。然后看上下文。如果历史对话里有干扰信息模型可能会被带偏。检查一下上下文里有没有无关的内容该裁剪的裁剪掉。最后看模型本身。有些模型在某些任务上就是不稳定这是模型能力问题换模型或者加后处理。后处理可以用规则过滤也可以用另一个模型来审核输出。5.2 Token消耗失控怎么排查Token消耗突然暴涨一般有几个原因。上下文没有裁剪历史对话越积越多每次请求都带着全部历史Token数自然爆炸。提示词太长系统提示词写了几千字每次调用都带着积少成多。输出没有限制模型生成了超长回复输出Token也算钱。重试次数过多一次失败重试好几次每次都是全量请求。排查的时候先把每次请求的输入Token和输出Token都记录下来按时间维度看趋势。如果输入Token持续增长那就是上下文没裁剪。如果输出Token异常那就是没有设置最大输出长度。定位到原因后对应的解决方法是加裁剪逻辑、精简提示词、设置max_tokens参数、优化重试策略。5.3 流式输出中断怎么处理流式输出中断的原因很多网络抖动、服务端超时、客户端断开。处理方式取决于中断发生在哪个阶段。如果还没开始输出就中断了直接重试整个请求。如果输出到一半中断了情况就复杂了。你可以选择重试整个请求但用户会看到重复的内容。更好的做法是记录已经输出的内容重试时告诉模型“继续从某处往下写”。但这需要模型支持这种续写模式不是所有模型都行。我的做法是在流式输出的外层包一层记录已经发送给用户的内容。如果中断了先尝试重连重连失败就告诉用户“网络异常请重试”让用户主动触发。同时后台记录这次中断用于后续分析。5.4 常见问题速查表问题现象可能原因排查方向解决思路输出答非所问提示词模糊、温度过高检查提示词结构、温度参数结构化提示词、降低温度Token消耗暴涨上下文未裁剪、输出无限制统计输入输出Token趋势加裁剪逻辑、设max_tokens响应延迟高模型负载高、请求链路长分段计时、看各环节耗时换模型、优化链路、加缓存流式中断网络抖动、服务超时看中断时机和频率重试、续写、降级内容被拦截输入含敏感内容看拦截返回信息提示用户修改、加前置过滤多轮对话失忆上下文未正确拼接检查消息列表组装修复拼接逻辑、增加轮数5.5 几个我踩过的坑第一个坑是把API密钥写在了代码里。早期图方便直接写在代码里结果代码传到仓库密钥就泄露了。后来全部改成环境变量并且加了密钥轮换机制。这个教训很基础但真的很多人犯。第二个坑是没有做超时控制。有一次模型服务抖动请求一直不返回把整个服务的线程池占满了导致所有请求都卡住。后来加了超时和熔断单个请求最多等60秒连续失败就暂时切断对该模型的调用过一段时间再试探恢复。第三个坑是评估集太小。一开始只准备了十几条测试问题改提示词后跑一遍感觉没问题就上线了。结果线上遇到各种奇怪输入表现一塌糊涂。后来把评估集扩充到上百条覆盖各种边界情况改动的信心才足了一些。第四个坑是忽略了成本监控。有个月账单出来吓了一跳排查发现是某个功能的上下文没有裁剪每次请求都带着几千Token的历史。后来加了Token预算控制每个请求的Token消耗超过阈值就告警。6. 监控、评估与持续迭代的落地方法6.1 监控指标延迟、成本、质量一个都不能少AI应用的监控和传统应用不太一样。传统应用看QPS、错误率、响应时间就够了AI应用还要看Token消耗、输出质量、内容安全。延迟监控要分段看模型调用耗时、后处理耗时、总耗时。模型调用耗时又可以分为首Token时间和总生成时间。首Token时间影响用户体感总生成时间影响吞吐。这两个指标要分开监控优化方向也不一样。成本监控的核心是Token消耗。按天、按功能、按用户维度统计输入Token和输出Token设置预算告警。我一般会设两个阈值日消耗超过预算的80%发提醒超过100%发告警并触发限流。质量监控比较难做因为质量是主观的。但可以用一些代理指标用户点赞率、用户重新提问率、对话轮数、异常退出率。用户重新提问率高说明上一次回答没解决问题。对话轮数异常多说明模型一直在绕圈子。这些指标虽然不直接等于质量但能反映问题。6.2 评估集建设从几十条到几百条评估集是AI工程的基石。没有评估集你所有的优化都是盲目的。评估集的建设是个持续的过程不是一次性的。起步阶段准备几十条标准问题覆盖核心场景。每条问题标注期望的输出要点不需要逐字匹配只要关键信息对就行。跑评估的时候可以用模型来打分也可以用规则来检查关键信息是否出现。随着应用上线把线上遇到的bad case收集起来补充到评估集里。每个bad case都是一次学习机会修好之后加进评估集防止以后退化。这样评估集会越来越大覆盖越来越全。评估的频率建议每次提示词改动、模型切换、代码逻辑调整后都跑一遍。评估结果要记录形成趋势图。如果某次改动导致通过率下降超过5%就要回滚或者重新设计。6.3 迭代节奏小步快跑每次只改一个变量AI应用的迭代最忌讳一次改太多。你改了提示词又换了模型又调了参数结果效果变差了你根本不知道是哪个改动导致的。正确的做法是每次只改一个变量改完跑评估确认有效再改下一个。迭代的节奏建议按周来。每周定一个优化目标比如“把客服场景的准确率从80%提到85%”然后围绕这个目标做改动。改动、评估、上线、观察形成一个闭环。不要天天改天天改会导致线上不稳定也没法归因。还有一个经验是保留回滚能力。每次上线新版本都要能快速回滚到上一个版本。提示词、模型配置、代码逻辑都要有版本管理。出问题的时候第一时间回滚然后再慢慢排查。6.4 一个完整的迭代案例举个我实际做过的例子。有个问答功能用户反馈“回答太啰嗦”。我做了以下几步第一步确认问题。看监控数据平均输出长度确实偏长用户重新提问率也偏高。第二步定位原因。检查提示词发现只写了“回答要详细”没有长度约束。第三步设计改动。在提示词里加一条“回答控制在100字以内重点突出”。第四步跑评估。评估集里有一批问题改之前平均输出180字改之后平均输出95字关键信息覆盖率没有下降。第五步上线观察。上线一周后用户重新提问率下降了15%输出Token消耗下降了40%。第六步固化成果。把改动合并到主分支评估集里补充几条相关的测试问题。这个案例里关键点是每次只改一个变量提示词改动前后都有数据支撑上线后有观察期最后有固化。这套流程跑顺了迭代效率会高很多。7. 关于AI工程能力建设我的一些个人体会从零搭建AI工程体系这件事最难的不是技术而是思维方式的转变。你得接受“不确定性”是常态然后学会用工程手段去管理这种不确定性。这跟传统软件开发追求确定性的思路是相反的需要刻意练习。我自己的体会是前期慢就是快。不要急着上框架、堆功能先把最薄的链路跑通把每个环节都摸清楚。模型接入、提示词管理、上下文处理、错误处理、监控评估这五件事一件一件做扎实。每做一件你都对AI应用的理解深一层。等这五件事都跑顺了你会发现后面加功能、换模型、扩场景都变得很轻松因为底层是稳的。还有一个建议是多动手少空想。AI工程是个实践性极强的领域很多坑只有自己踩过才知道。看再多文章不如自己写一个最小可用的版本跑起来然后一点点加东西。遇到问题就查、就问、就试这个过程本身就是最好的学习。最后分享一个小技巧给自己建一个“踩坑笔记”每次遇到问题、解决问题后简单记几笔——问题是什么、原因是什么、怎么解决的。这个笔记积累起来就是你自己的AI工程经验库。下次再遇到类似问题翻一翻能省很多时间。而且写着写着你会发现很多问题其实是相通的解决了一个一类问题都有了思路。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →