从零开始AI工程:模型调用、提示词、RAG与Agent实战指南
这两年问我“从零开始搞AI工程到底怎么入手”的朋友越来越多有写后端的、有做产品的也有刚毕业的学生。大家一个很普遍的困惑是教程铺天盖地可真的动手要做个能用的东西反而不知道第一步该踩在哪。所谓“ai-engineering-from-scratch”说的就是这条路——不靠复制粘贴现成项目而是从底层逻辑开始把一个AI应用从想法变成能稳定运行、能评估、能交付的工程系统。这篇内容就是写给想认真走这条路的人。我会按自己实际折腾过的顺序从最基础的一次模型调用开始一路讲到提示词工程、RAG检索增强、Agent工具调用、评估监控和上线踩坑把每一步为什么这么做、参数怎么定、出问题怎么排查都讲清楚。你不需要有机器学习和深度学习的底子但要有写代码的经验最好用过一门主流语言跑过几个脚本这样读下来会顺很多。1. 拆解“AI工程”到底在做什么1.1 AI工程不等于调API也不等于训练模型很多人一听AI工程第一反应是“不就是调接口吗”或者反过来觉得“那得先学会训练大模型”。这两个理解都不太对。调接口只是整个链条里最不起眼的一环而训练模型从头到尾跑一遍预训练对绝大多数业务场景来说既不现实也没必要。真正的AI工程处在两者之间的位置利用现成的大模型能力通过工程手段把它变成稳定、可控、可评估的业务功能。我一般这样给刚入门的人解释传统软件开发像是请一支纪律严明的施工队你画好图纸他们按图施工误差很小。而大模型更像一个能力很强但偶尔不按套路出牌的临时工你交代任务他多数时候完成得不错但偶尔会自由发挥、答非所问。AI工程的核心就是围绕这个“不太听话但很能干”的临时工建立一整套管理机制把任务说清楚提示词工程、给他配工具和资料RAG与Agent、验收他的工作评估体系、盯着他别出乱子监控与防护。这种定位直接决定了你学什么、不学什么。你不需要把Transformer的每一个数学公式都推一遍但你必须理解token是怎么计算的上下文窗口是什么意思为什么同样的提示词换一个模型表现天差地别为什么输出会突然变成一堆乱七八糟的JSON。这些才是工程实践中真正卡脖子的地方。1.2 从零开始需要建立的知识地图我最开始自学的时候走了不少弯路今天回头看一条比较合理的路径大概是五个部分缺一不可。第一是模型调用基础包括API怎么鉴权、消息结构长什么样、关键参数如温度和max_tokens怎么控制输出行为这是最底层的地基。第二是提示词工程不是“会写指令”就行而是理解角色设定、任务拆解、示例引导、格式约束这些手段如何影响模型输出并且能对提示词做版本管理。第三是结构化输出与工具调用让模型按照你规定的JSON格式返回数据或者让它自主决定调用哪个外部函数这决定了一个AI功能能否嵌入到真实业务系统里。第四是知识接入也就是RAG和Agent相关工作解决“模型不知道你的私有数据”和“模型不会主动操作外部系统”这两个核心问题。第五是评估与上线工程包括测试集构建、质量指标、成本与延迟监控、失败回退逻辑。这五块不是简单的并列关系前面是后面的基础。我自己见过太多人一上来就想搞一个复杂的Agent应用结果连一次模型调用的超时重试都没做对最后系统跑起来各种莫名其妙的问题根本分不清是提示词的锅还是代码的锅。与其这样不如老老实实从一次调用开始。2. 从一个最小可用的模型调用开始2.1 环境与依赖准备无论你最终选哪家模型第一步都一样把Python环境和SDK装好把密钥配置干净。我个人只用过OpenAI兼容接口的SDK因为它事实上成了行业标准接口很多国产模型服务商也都提供兼容端点学会了这套切换到别家通常只改base_url和模型名就行成本很低。具体来说我建议用Python 3.10以上版本装好openai这个包。密钥千万别写死在代码里更别提交到Git仓库。我用的是环境变量加本地.env文件的方式.env不进版本控制代码里通过os.getenv(OPENAI_API_KEY)读取。这一步看起来基础但真的是很多人翻车的高发区。去年有个朋友把带密钥的代码推到公司内网仓库第二天就被扫描工具拉黑警告这种事一次都不要发生。配置好环境后无论如何先跑通一次最小的对话调用把整个链路验证一遍from openai import OpenAI client OpenAI() response client.chat.completions.create( modelgpt-4o-mini, messages[ {role: system, content: 你是一个简洁的助手。}, {role: user, content: 用一句话解释什么是RAG。} ], temperature0.3, max_tokens200 ) print(response.choices[0].message.content)第一次看到这段代码跑通的时候说明你的网络、密钥、SDK、模型权限全部正常。接下来所有复杂的东西都是在这个最小调用的基础上长出来的。2.2 消息结构与关键参数背后的门道很多初学者不理解messages为什么是一个数组而不是直接传一句字符串。这个数组是在模拟一个完整的对话上下文system消息负责定义模型的角色和全局行为user消息是你这次输入的内容assistant消息则是模型之前的回答。多轮对话的时候就是不断往这个数组里追加消息。理解这一点特别重要因为上下文管理的一切技巧本质上都是在操作这个数组。temperature这个参数新手最容易乱调。它控制的是输出随机性值越低模型越倾向于选择概率高的输出结果更稳定、更可预测值越高输出越发散、更具创造性。做工程应用默认我几乎都是用0.2到0.4之间的低温度尤其是涉及数据提取、分类、代码生成这类任务稳定性优先。只有做文案创意、头脑风暴这类场景才会上调到0.7以上。还有一个容易忽略的点max_tokens不只是控制输出长度它同时决定了你每次调用的成本上限不设置的话默认值可能比你想象的大得多一笔意外的高额账单往往就是这么来的。2.3 把一次调用封装成可复用的工程模块真正做工程的时候你不可能在业务代码里到处裸写chat.completions.create。我习惯在第一周内就把调用封装成一个统一入口后来的所有功能都通过这个入口走。封装的时候至少要处理三件事超时、重试、日志。import time import logging from openai import OpenAI logger logging.getLogger(__name__) client OpenAI() def call_model(messages, temperature0.3, max_tokens1000, max_retries3): for attempt in range(max_retries): try: response client.chat.completions.create( modelgpt-4o-mini, messagesmessages, temperaturetemperature, max_tokensmax_tokens, timeout30 ) return response.choices[0].message.content except Exception as e: logger.warning(第%s次调用失败: %s, attempt 1, e) if attempt max_retries - 1: time.sleep(2 ** attempt) raise RuntimeError(模型调用持续失败)这里的重试策略用的是指数退避第一次失败等2秒第二次等4秒给临时的网络抖动或服务过载留出恢复时间。日志必须记下来不然后线上出问题的时候你连请求有没有发出去都不知道。这套封装做完你才算是从“调通了Demo”迈进了“做工程”的门槛。3. 提示词工程从“会聊天”到“稳定可控”3.1 一个可用提示词的四个基本组成很多人觉得提示词就是“跟模型说清楚你想干嘛”这个理解太粗了。实际项目里一段能稳定产出预期结果的提示词至少包含四个部分角色设定、任务描述、约束条件、示例演示。角色设定不是花架子它建立了模型输出的基调和视角任务描述要具体到“做什么、输入什么、输出什么”约束条件要明确“不要做什么、格式要求、长度限制”示例演示是提升稳定性的关键尤其对格式敏感的任务给一个输入输出的样例模型照着样子来的准确率会高一大截。举个例子我之前做一个合同关键信息提取的功能最初的提示词就说“提取合同里的甲方、乙方、金额、有效期”。结果输出的格式五花八门有的用JSON有的用表格有的还带一段解释文字。后来我把提示词重写成了这样你是一个合同信息提取助手。从用户提供的合同文本中提取以下字段 party_a, party_b, amount, effective_date, expiry_date。 要求 1. 只输出JSON对象不要输出任何解释文字。 2. amount是数字不带货币符号。 3. 字段缺失时填null。 示例输入... 示例输出{party_a: ..., ...}效果立竿见影。不是模型变聪明了而是你把模糊的期望变成了明确的验收标准。3.2 输出可控性的几个实操技巧除了提示词本身让模型输出稳定可控还有一些配套手段。最常见的坑是“要求模型输出JSON但解析的时候经常失败”。模型毕竟是生成式模型返回的文本里可能夹着Markdown代码块标记或者JSON末尾多了个逗号。工程上最稳妥的办法是用接口自带的JSON模式或函数调用能力从机制上保证输出合法JSON而不是靠提示词“求”它。比如OpenAI的response_format{type: json_object}参数就是强制模型输出可解析JSON。这个开关打开之后解析失败的情况几乎绝迹。还有一个非常实用的技巧是把复杂任务拆成多步每一步单独调用一次模型。新手很喜欢让模型“一步到位”完成一个复杂需求比如“分析这段文字的情感然后提取实体最后生成摘要”。结果往往是每一步都做得不精。正确做法是拆成三个独立调用每个调用只做一件事中间的数据用代码传递。多一次调用多花一点钱和延迟但换来的是稳定性和可排查性这笔帐非常划算。思维链提示词也是个好东西。你可以在提示词里要求模型“先一步步思考再给出答案”对数学、逻辑推理、多条件判断类任务准确率通常会有可感知的提升。但要注意工程输出场景里思维链内容不该出现在给用户的最终结果里可以把推理过程和最终答案分开输出或者用结构化字段包裹。3.3 提示词也是代码必须做版本管理这是我最想强调的一点提示词不是随便改改就行的“配置”它的重要性一点不比代码低。我带过的小团队里最早是每个人在自己代码里改提示词改完就上线结果某次线上效果突然变差排查了两天最后发现是同事下班前微调了个提示词里的一个词。从那以后我们定了两条规矩所有提示词统一集中到一个专门的文件里做版本管理任何提示词变更必须过一遍评估集才能合入。这里说的评估集简单说就是一组“输入-期望输出”的测试样例。改提示词之前先在评估集上跑一遍旧版本再跑一遍新版本对比通过率用数据决定要不要上线。这个习惯一旦养成你的系统会越来越稳而不是像很多人那样每次改提示词都像是在赌运气。4. 构建有记忆与知识边界的应用4.1 上下文窗口是硬约束不是软建议模型不能记住你所有对话历史它只能看得到messages数组里当前塞进去的内容。每个模型都有一个上下文窗口上限比如128K token听起来挺大但换算成中文也就是几万字而且输入和输出共享这个额度。一旦超出要么报错要么被策略性地截断。这个限制带来的工程问题很实际多轮对话应用里历史消息不能无限累积。我做过一个客服问答机器人用户聊了二十几轮之后把整段历史都塞进去结果每次请求的输入token越来越大成本飙升不说最关键的是模型反而被大量无关历史信息干扰回答质量下降。后来我实现了一个简单的滑动窗口按token数估算超过4000个token就把最早的非关键消息丢掉只保留最近的若干轮。再加一个简单的策略如果历史里已经有“用户对某个问题已明确解决”的记录就提前结束这个话题不再把旧对话喂给模型。4.2 RAG的完整链路让模型用上你的私有知识模型训练时不可能看到你公司内部的文档、产品手册、售后记录。要让它回答这些私有知识范围内的问题有两个思路微调或者RAG。微调适合改变模型的行为风格、固化特定任务格式但本质上模型对知识的记忆能力有限新知识更新也麻烦。RAG则更灵活把知识文档切块向量化存入向量数据库用户提问时先检索出相关片段然后把片段连同问题一起交给模型回答。大部分业务场景RAG的效率、成本和可维护性都更好。RAG链路看起来简单实操中的坑全在细节里。文档切分就是个技术活切得太碎语义被切断检索出来不知所云切得太大片段里噪音多还容易超出模型的上下文预算。我常用的做法是按500到800字切一块相邻块之间留50字左右的重叠这样能尽量保住段落边界的语义连续性。切完之后用嵌入模型转成向量存进向量数据库。检索的时候取相关性最高的前6到8个块拼进提示词的特定位置。这里有个很多人忽略的点检索到的片段不能无脑全塞给模型要做个简单的过滤和排序。比如用户问的是“退货政策”你检索出来的片段里有几条相关度很高、但信息早过时了这时候需要有一个更详细的业务标签过滤或者至少把时间最新的排前面。另外检索结果必须带上来源标记让模型回答的时候能引用这样用户看到答案之后可以溯源查证出了问题也好定位。4.3 Agent与工具调用模型从“说”到“做”的跨越如果说RAG解决了“模型不知道”的问题那么Agent和工具调用解决的是“模型不会做”的问题。所谓工具调用本质上就是让模型在回复中输出一个结构化的“要调用哪个函数、参数是什么”的声明然后你的代码去执行这个函数再把执行结果返回给模型继续思考。我做的第一个Agent功能是一个“日程安排助手”。用户说“帮我查一下明天下午有没有空闲会议再给张伟发一封邀约邮件”。传统意图识别要做一堆分类和槽位提取而用工具调用就简单得多给模型暴露两个函数一个是query_calendar(date)一个是send_email(recipient, content)。模型自己决定先查日历再根据查到的结果调用发邮件函数参数也是它自己填的。整个过程省掉了大量规则代码。但Agent有一个天然风险模型会“自作主张”。它可能在你没明确授权的时候就调用了发送类、删除类这类高影响操作。工程上必须给工具加上严格的权限与确认机制默认情况下读操作可以直接执行写操作、外部副作用操作必须经过人工确认。我习惯把所有Agent行为轨迹完整落日志谁在什么时间调了哪个函数、参数是什么全部可回溯。不然Agent出了错你连怎么错的都查不到。5. 评估、监控与上线踩坑5.1 评估集怎么建不只是准备几条测试用例很多团队上AI功能的时候“测试”就是人工聊几句感觉回答得还行就上线了。这在小范围试用阶段勉强可以一旦功能面向真实用户问题立刻暴露不同提问风格下回答质量波动、回归出了问题没人发现、换了一个模型版本整体行为变化不可控。所以正式的评估集必须尽早建。评估集我一般分三类第一类是黄金样例每条都有人工标注的期望答案用于量化回答准确率第二类是输入多样性与压力样例覆盖各种口语表达、长文本、异常输入第三类是安全与拒绝样例确保模型在遇到不该回答的问题时正确拒绝。三个集合合起来规模不用太大起步阶段两三百条就足够抓住大部分方向性问题。每次改动提示词、策略或模型版本就在评估集上跑一遍全量回归。评分标准不一样有的任务可以程序化判断比如JSON格式通过率、字段提取准确率有的是开放文本需要人工打分或让一个更强的模型当裁判。不管用哪种方式关键是“能对比”旧版本得分vs新版本得分一眼就能看出这次变更是不是真的改好了。5.2 上线之后盯什么成本、延迟、失败率、质量AI应用上线后的监控和传统应用有一个很大的不同除了代码报错你还得盯着模型层面的一堆指标。我列一个自己的监控清单每一项都踩过坑。成本指标要看单次请求平均token数、单日总费用。延迟要找P50和P95有些模型在峰值时段会明显变慢如果你做的是抓对话响应速度的功能P95延迟超过阈值就得考虑加缓存或换更小的模型。失败率方面除了HTTP层面的报错更要注意“输出格式不合法”这类软失败——状态码是200但内容解析不了比显式报错更隐蔽。质量监控最朴素也最有效给用户加一个“回答是否有帮助”的反馈按钮把负面反馈对应的输入和模型输出定期捞出来人工复盘。这比任何复杂的自动化指标都更能帮你发现问题。成本失控是我见过最多的事故。有的人写了个循环脚本忘了设置调用上限一晚跑了几十万次调用有的人没给模型输出做长度限制回答又臭又长token消耗翻了好几倍。我的习惯是所有批量脚本默认限制调用次数和并发数所有线上请求在网关层做预算监控单日费用超过阈值自动告警甚至熔断降级。5.3 常见问题排查表最后整理一个我实际干活时反复用到的排查表每个问题都是真实发生过的现象可能原因排查与解决办法模型回答开始胡说八道内容与资料不符检索到的上下文相关性差或提示词里知识边界没讲清打断点看实际检索到哪些片段检查切分粒度和检索top数在提示词里强约束“只能依据提供的资料回答”JSON解析偶发失败模型输出带了Markdown代码块标记或JSON语法不合法优先启用接口的JSON模式捕获解析异常时做一次“提取大括号内容再解析”的兜底逻辑多轮对话越跑越慢、token消耗越来越高历史消息无限累积超出上下文预算实现滑动窗口截断历史在应用层定期总结并压缩历史对话线上某时段延迟飙升模型服务商高峰期过载或单次请求上下文过大监控区分“网络延迟”和“首token延迟”对长输入做预处理压缩考虑换低延迟模型同一个提示词换模型版本后效果变化明显不同模型对指令的理解和格式遵循能力不一致变更模型必须走评估集回归不能想当然觉得同系列升级版一定更好上游接口调用失败导致整个流程崩溃没有做重试和熔断降级在统一调用入口加指数退避重试加上降级为固定兜底回复的逻辑这些坑不是耸人听闻每一个都是我或身边朋友在真实项目里花过时间填平的。提前看到这张表你能省下至少一两周的调试时间。我个人最大的体会是从零开始做AI工程真正难的不是某一个单点技术而是把“模型的不确定性”这个变量纳入整个工程体系里。刚开始你可能会被各种花哨的Agent框架吸引但扎下心把一个最小调用调稳、把提示词版本管理起来、把评估集建好这些笨功夫才是整个系统长期可靠的真正底座。等你把这些基本功都走了一遍再回头看复杂的应用场景会发现一切都只是这些基础能力的组合。