ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从零开始AI工程化:从API调用到Agent落地的完整路径

从零开始AI工程化:从API调用到Agent落地的完整路径 1. 从零开始之前先想清楚from scratch到底意味着什么大概是从Build a Large Language Model from Scratch这本书火起来之后大家都开始聊from scratch。但说实话很多入行的人把from scratch理解歪了。你问一个想做AI工程的人打算怎么开始十个里有八个说我想从头训练一个大模型。这个冲动我完全理解毕竟从零手写一个支持前向传播和反向传播的Transformer确实是深入理解模型底层的绝佳路径。但从工程落地的角度看从零实现训练算法几乎是最不划算的起步方式。原因很简单AI工程的核心产出是能够稳定交付价值的AI系统不是我亲手实现了一遍attention。已经有一个无比成熟的开源生态PyTorch、HuggingFace、vLLM如果不加利用你就是在重复造轮子。而且真正的难点从来不在模型怎么实现而在于拿到一个基础模型之后怎么围绕它设计数据流、评测机制、护栏策略、迭代流程让它在一个真实业务场景里从能用变成好用。这中间的跨度比从零写一个attention大得多。所以我理解的ai-engineering-from-scratch应该是另一条路径从零开始系统掌握AI工程化的完整能力栈。具体来说你要走过这几个阶段基础能力层能熟练调用模型APIOpenAI、Anthropic、开源模型的本地部署理解token、上下文窗口、温度参数、结构化输出这些基本概念。工程封装层能把一次API调用变成一个工程化的模块包含超时重试、错误分类、日志追踪、成本控制。系统设计层能设计多轮对话、上下文管理、检索增强、工具调用这类复杂交互系统。评估迭代层能建立回归评测集用自动化手段衡量每次prompt改动或模型升级是变好了还是变差了。Agent架构层能驾驭模型的自主规划—调用工具—循环执行能力并且有兜底护栏。这篇文章就是沿着这条路径展开的。没有夸张的项目没有高大上的架构图只有我踩过的坑、验证过的做法和还在用的套路。适合刚入门想做AI应用开发的工程师也适合后端转AI方向的人参考。2. 第一步实测从一次API调用到一个能上线的AI服务很多人以为AI开发最难的是prompt设计其实不是。最难的是让你的AI服务比一段能跑的demo多走一步成为生产环境里稳定运行的程序。我见过太多人写出来的代码是这样的调用一下模型接口把结果打印出来成功了收工。这种代码离工程化还差着十万八千里。2.1 选好模型但别把选型当成信仰模型选型的核心逻辑就是ROI。先想清楚你的业务场景对能力的诉求是什么再决定是用闭源API还是开源模型是用最大号的模型还是中等规模的模型。我个人的原则是这样的场景类型推荐方案理由追求效果上限有预算闭源最强API如GPT-4级别省去部署运维成本效果公认数据敏感、必须内网开源模型本地部署如Qwen系列、LLaMA系列数据不出域可深度定制高并发、成本敏感情中小规模开源模型 精调单次推理成本低吞吐量高快速验证产品概念直接用API 简单prompt先跑通再优化迭代速度快不要一上来就追求最强模型。一个很痛的教训是业务早期最合理的策略是用最笨的方法把流程跑通也就是先用API 最简单的prompt做出一条完整的数据链路加上日志、评测、版本管理然后再逐步优化。很多团队把精力耗在选哪个模型上结果产品形态还没验证就烧掉了大量预算。2.2 不要直接裸调API要包一层防空层我第一次做AI服务的时候就吃过亏直接调用模型API用户输入什么就原样拼到prompt里结果一个用户在文本里塞了一大段注入指令模型直接回了数据库里的敏感配置。从那以后我所有AI服务里必配输入校验和角色隔离。工程化调用模型的标准动作至少包含这几层请求层设置合理的超时时间、最大重试次数并对错误码做分类处理限流、服务不可用、上下文超长、内容过滤。数据层对输入做长度校验、脱敏处理、敏感词过滤必要时加一层指令注入检测。输出层验证返回格式是否符合预期用JSON Schema强制结构化解析失败走降级分支。观测层记录请求耗时、token消耗、模型返回结果摘要、失败原因方便事后复盘。代码层面看起来大概是这样的from openai import OpenAI import time, json, logging client OpenAI() SYSTEM_PROMPT 你是一个只能使用JSON格式输出的客服助手。 USER_TEMPLATE 用户的问题是{query} def safe_call_llm(query: str, max_retries3): payload {query: query[:500]} # 长度截断防止超长输入 for attempt in range(max_retries): try: resp client.chat.completions.create( modelgpt-4o-mini, messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: USER_TEMPLATE.format(**payload)} ], temperature0.2, response_format{type: json_object} ) result json.loads(resp.choices[0].message.content) return result # 注意这里返回的是dict不是字符串 except Exception as e: logging.warning(f第{attempt1}次调用失败: {e}) time.sleep(2 ** attempt) return {error: model call failed, fallback: 系统繁忙请稍后再试}这里有几个细节值得展开说。超时和重试模型接口偶尔会抖超时直接报错是常态。重试策略用指数退避2秒、4秒、8秒别上来就无脑重试那是给服务商制造压力。而且要注意有些错误不能重试比如你的prompt触发了内容审核重试一万次结果也一样。JSON结构化输出如果你希望模型返回的是结构化数据而不是一段散文一定要用response_format或者其他等效机制。然后做一次json.loads验证。凡是返回结果要落到下游逻辑里的都必须经过这层解析校验否则模型一本正经地输出了一句话你的程序里json.loads直接抛异常整个服务就崩了。降级方案哪怕重试三次都失败也必须有兜底策略。要么返回预置话术要么走规则引擎总之不能让用户看到一堆报错异常。生产环境里优雅降级永远比功能强大但强依赖模型更可靠。2.3 温度参数不是玄学很多人不知道temperature到底在控制什么。简单说它控制的是模型在生成时对候选token概率分布的平滑程度。温度越高选择低概率token的可能性越大输出越发散温度越低越倾向于选择高概率token输出越稳定和保守。我的习惯是事实抽取、分类、结构化输出这类任务temperature直接拉到0到0.1文案生成、头脑风暴这类需要创造力的任务设到0.7到0.9。很多新手从头到尾用默认值通常是1.0结果在结构化任务上偶尔出现莫名其妙的输出然后开始怀疑prompt写得不好其实改一下温度就好了。这一层做完你已经有能力把一个模型调用变成一个服务模块。但这一步只是热身——接下来才是拉开差距的地方。3. 从功能到系统上下文、记忆与评测三个绕不开的坎当你开始构建一个真正的AI应用——比如一个能连续对话的智能客服、一个能根据文档写周报的助手——你很快会发现用户需求不是调用一次模型而是一整个系统。这个系统里有三个绕不开的核心问题上下文怎么管状态怎么存改了prompt你怎么知道是变好了还是变坏了。3.1 上下文管理窗口有限但对话无限大模型的上下文窗口再大也是有限的而且输入越多、推理越慢、成本越高。一个核心矛盾是用户希望AI记住所有对话内容但模型的上下文窗口装不下无限历史。我的做法是三级上下文策略第一级滑动窗口只保留最近N轮对话通常15到20轮更早的内容会丢失。适合简单闲聊类场景。第二级摘要压缩当对话超过一定轮数用一个专门的prompt把历史对话压成一段摘要连同最近几轮消息一起发给模型。摘要里保留关键事实用户偏好、已确认的信息、待办事项丢弃寒暄和过程性内容。我在项目里实测摘要压缩至少能延长十轮以上的有效记忆。第三级外部记忆关键的事实性信息用户身份、订单状态、工单编号一旦出现就通过程序抽取出来写入KV存储或向量数据库。对话时先查外部记忆把相关结果拼进prompt。这个方案最稳因为不依赖模型记信息。一个标准的消息构造逻辑大概是这样的def build_messages(session_id: str, new_user_msg: str): recent get_recent_messages(session_id, limit20) summary get_session_summary(session_id) # 从缓存取历史摘要 facts query_user_facts(session_id) # 从数据库取关键事实 messages [] if summary: messages.append({role: system, content: f对话历史摘要{summary}}) if facts: messages.append({role: system, content: f用户已知信息{facts}}) messages.extend(recent) messages.append({role: user, content: new_user_msg}) return messages这套结构在工程上是非常经典的整形prompt系统提示词外部记忆摘要近期对话当前问题是让AI系统稳定工作的重要手段也是很多开源框架所谓memory management背后的真正原理。3.2 状态管理一场对话背后其实是一张状态表很多从AI项目入门的人有一个致命误区以为AI应用是无状态的。实际上越复杂的AI系统状态管理越重要。举个具体的例子。我做过一个自动写周报的助手用户会在对话中逐步提供信息我周一修复了登录模块的bug周二开了三场会周三和设计团队对完了新版首页的方案。一次对话里用户会持续补充碎片信息。如果我把每句话都单独丢给模型模型根本不可能在最终生成周报时把所有信息都组织好。正确的做法是在程序侧维护一个周报信息状态表每次模型从用户发言中抽取字段日期、事件类型、描述结构化地写入状态表最后生成周报时再把状态表整体丢给模型。更通用的说法是AI应用应该把对话当成前端交互手段把信息抽取和维护当成后端状态管理把模型生成当成最后的呈现步骤。如果你做任何非闲聊类的AI产品我强烈建议你画一张状态流转图把所有可能的数据字段列出来然后设计好什么时候抽取什么时候写入什么时候需要和用户确认再做功能开发。这套思路在Agent类应用里同样关键Agent每次执行工具调用后环境状态都会变化你必须在代码里主动追踪当前做到哪一步了目标是否完成而不是把一切都交给模型去想。模型会被带偏代码不会。3.3 评测集没有评测的prompt调试都是玄学这是我最想强调的一点。AI工程和传统软件工程最大的不同在于AI系统的输出是不确定的。同一个prompt、同样的输入模型可能给出不同的结果。这就导致一个问题你改了一版prompt肉眼看起来似乎效果更好了但你无法保证它在100条真实样本上都更好也无法保证明天换一个模型版本之后依然更好。解决这个问题的唯一可靠途径就是建立评测集。我们从第一天就应该为每一个AI功能建立一个回归评测集包含业务典型输入从真实日志里抽样20到50条用户输入。边界情况超长文本、空输入、带恶意指令的输入、多义性表述。金标准答案每一条输入人工标注期望的输出可以是模型输出模板、关键信息点列表、可接受的答案范围。判定方式可以分几档精确匹配适合分类任务、关键词命中适合抽取任务、LLM-as-judge让一个更强的模型当裁判评估两版输出哪个更好、人工抽检。有了评测集你的prompt优化就从一个手感活变成了一个可迭代、可回归的工程流程# 一个极简的评测脚本思路 1. 加载测试用例集包含输入期望结果 2. 用新prompt逐条执行模型调用 3. 对比每条输出与期望结果计算通过率 4. 和上一版prompt的通过率对比 5. 通过率提升才合入新版本我第一次给团队搭评测流程时的核心指标就一个核心任务通过率。prompt改版合入的标准是通过率不下降哪怕只是1%的提升也要有数据支撑。有了这套东西改prompt不再心惊胆战也敢尝试更激进的结构调整了。这个阶段走完你就已经超越了会调API的开发者开始迈进AI系统设计的门槛。接下来要碰的东西更刺激也更容易翻车——Agent。4. 从系统到Agent工程复杂度突然跳变的临界点如果说前面的东西都有成熟范式可以参考那么Agent就是那个范式还没定下来但你必须开始实践的东西。我的体会是Agent给AI工程带来的不是新API而是一种新的复杂度形态——你要从一个请求-响应范式切换到一个目标驱动的多步执行范式。4.1 Agent到底在解决什么问题传统AI应用的路径是用户输入 - 程序决定调什么模型 - 模型返回 - 程序决定下一步。流程由程序硬编码。Agent的路径是用户给一个目标 - 模型自己规划步骤 - 模型调用可用工具 - 观察工具结果 - 再规划下一步 - 直到任务完成。流程的一部分决策权交给了模型。为什么这个方向值得投入因为现实任务往往没法一次完成用户说帮我调研一下最近AI Agent框架的优缺点并整理成表格你得先搜索资料然后阅读多个网页对比信息最后生成表格。每一步的结果都是下一步的输入而中间需要走哪几步并不总是固定。Agent的意义在于让模型能够在过程中持续决策而不是靠人肉把所有分支都写在代码里。4.2 Agent工程化最核心的三块拼图我在实际项目里做Agent也踩过不少坑之后总结出工程化落地的三块核心拼图。第一块工具定义。模型需要通过工具来和外界的函数、API交互。每个工具的本质是一个函数 描述 参数Schema。函数是真实要执行的逻辑描述是给模型看的这个工具什么时候该用参数Schema是模型生成参数时的约束。tools [ { type: function, function: { name: search_news, description: 搜索指定主题的最新新闻。当用户需要查询实时信息或最近发生的事情时使用。, parameters: { type: object, properties: { query: {type: string, description: 搜索关键词}, limit: {type: integer, description: 返回条数默认5} }, required: [query] } } } ]工具定义的行业秘密是描述写得好不好直接影响模型调用工具的准确率。很多人以为参数Schema是最重要的其实模型大部分时候靠description来决定何时调用、传什么参数。描述要写清楚什么时候用什么时候别用边界在哪里。举个例子一个天气查询工具描述里应该写明仅支持查询国内城市否则模型就会拿一个英文城市名去调你的接口然后返回空结果。第二块循环控制。模型从调用工具到拿到结果再到再调用下一个工具这个过程工程上叫Agent Loop。我踩过最大的坑是在循环内部没有设置终止条件和最大步数限制一个Agent在某个失败分支里无限重试把token消耗到爆。我的Agent循环控制逻辑一定是这样的for step in range(MAX_STEPS): # 必须设上限 response client.chat.completions.create( modelLLM_MODEL, messagesmessages, toolstools, tool_choiceauto ) msg response.choices[0].message if not msg.tool_calls: break # 没有工具调用了任务结束 for call in msg.tool_calls: result execute_tool(call.function.name, json.loads(call.function.arguments)) messages.append({ role: tool, tool_call_id: call.id, content: json.dumps(result, ensure_asciiFalse) }) messages.append(msg) # 保留模型的中间决策几个容易踩的细节tool_call_id必须和模型返回的id一一对应不能乱传工具返回结果要做序列化和截断太长的工具输出会把上下文撑爆每一次循环都要把模型的中间决策消息msg本身加回消息列表这代表模型已经决定这么做了否则模型会丢失前面的决策脉络。第三块状态与护栏。Agent自由度越高出事的概率越大。所以生产环境的Agent必须有一个行为边界的概念。我在系统中至少要加这些护栏工具调用白名单机制模型只能调用系统预设的工具不能凭空发明。所有工具的参数取值也要在Schema层做约束。敏感操作二次确认涉及发邮件、下单、删除数据这类不可逆操作Agent必须先返回一个预备执行请求由程序转成需要用户确认的交互用户确认后才真正执行。这一步是Agent工程和普通函数调用的最大区别。轨迹审计Agent的每一步思考过程、工具调用、返回结果都要落日志。不是为了调试方便而是为了出问题时可以追溯到底是谁做了这个决定。这是合规审计的基本要求也有助于不断分析模型在哪一步容易犯错。4.3 别一上来就追全自主很多团队做Agent时盯住的目标是全自主——给模型一个目标它能自己搞定所有事。我的看法是生产环境不要追求这个。半自主 关键节点人工确认才是落地最稳的形态。一开始就追求全自主Agent会在各种意想不到的低级节点出错比如搜索到一个错误页面还一本正经地总结出结论比如把工具返回的JSON字段名看错。有一个框架非常实用叫Plan-Execute-Refine Loop规划-执行-优化循环。每一次Agent执行完一步都要回头检查一下这一步的结果和预期一致吗是否需要调整下一步计划这个检查点让Agent在长链条任务中不容易跑偏Plan规划用户给目标后模型先拆解出步骤计划。Execute执行按计划一步步调用工具。Refine优化每执行完关键一步把当前进度和原计划对比必要时重新规划。这个循环看起来简单实际上对稳定性的提升非常大。我做过的几个Agent项目里加上这个显式的Refine步骤之后任务成功率提升了20%以上。原因在于模型在长链条里很容易忘记最开始的目标Refine环节等于逼着模型每走一步都重新对齐一次目标。到这里AI应用已经从功能变成了系统再从系统变成了Agent。但还有一个横切所有阶段的东西我放在最后讲因为它最容易被人忽略一旦出问题也最致命——测试、部署和持续运营。5. 工程化落地测试、部署、成本与Prompt版本管理AI工程和传统后端工程在生产环境的差异一句话就能概括传统后端是代码写得对不对的确定性系统AI系统是概率模型加不加限制的非确定性系统。所以它的测试、部署与运营必然有一套专属打法。5.1 AI系统怎么测试从单测到集成到回归传统单测在AI工程里依然有用但测试对象变了。你没法断言模型输出必须等于xxx但你可以断言确定性代码逻辑工具调用参数是否正确解析、上下文截断长度是否符合预期、降级分支是否触发。这些用普通单测覆盖。模型输出的Schema合规性输出能不能成功json.loads必填字段是否齐全枚举值是否在允许范围内这些用结构化校验函数覆盖。业务效果指标用评测集跑回归用通过率这个指标衡量prompt和模型版本的效果变化。端到端集成测试模拟用户完整走一遍流程比如连续输入三段碎片信息最后检查生成的周报是否覆盖了三个关键事件。这类测试在发布前必须跑因为模型调用的组合场景往往是单测覆盖不到的。5.2 Prompt也是代码必须版本化管理很多团队把prompt放在代码里硬编码改一次功能就改一次代码、发一次版效率极低。我后来把prompt全部抽出来做成配置文件YAML/JSON每个prompt都有独立版本号。代码引用时通过版本号加载。配置管理的好处是prompt的改动可以独立于代码发版甚至可以让非技术人员参与prompt调优而不用碰代码仓。# prompts/weekly_report.yaml version: v3.2 model: gpt-4o-mini temperature: 0.3 system_prompt: | 你是一名周报撰写助手。根据用户提供的碎片信息生成结构化周报。 要求 1. 按日期归档 2. 区分“已完成”“进行中”“待协调”三类 3. 输出格式为JSON few_shot_examples: - input: 周一修了登录bug output: {date: 周一, type: 已完成, summary: 修复登录模块bug}版本化带来的直接好处是你可以做A/B实验给一部分用户用v3.1另一部分用v3.2用数据说话哪个版本更好而不是靠感觉。prompt就是代码改prompt也要走评审和回归测试这是成熟AI团队的基本底线。5.3 成本与延迟两个最容易失控的隐性指标AI项目的成本不是按服务器算的是按token算的。单看一次调用不贵但体量大起来之后每百万token的价格差异就会在月度账单上体现得非常明显。我有三个控制成本的有效手段上下文瘦身能用摘要压缩的就别把原文全塞进去求够用不求全量。一次长对话动辄几十万token的输入成本大部分浪费在重复的历史记录上。小模型先行绝大多数业务场景的80%请求可以用小模型如gpt-4o-mini完成只有复杂推理才路由到大模型。我给系统设计了一个路由层输入短、意图明确的任务走小模型长文本推理、复杂规划等任务走大模型。实测成本能省30%到50%。缓存高频请求对于相同或相似的输入比如常见FAQ、固定的系统指令可以用前缀缓存或语义缓存机制避免重复调用模型。同样的问题模型没必要每次都重新生成一遍回答。延迟就更直接了。用户能接受的AI响应时间一般在3秒以内超过5秒流失率就会明显上升。控制延迟的核心手段就几招小模型、限制输出长度max_tokens、减少多轮链式调用能一轮做完不要设计成三轮、对长文本做流式输出让用户先看到内容在动感知延迟会大幅降低。5.4 可观测性从这功能怎么不工作了到我知道它为什么这样AI服务的排查难度比传统接口高一个数量级。传统接口报错有堆栈AI服务报错往往是返回了一个和预期不符合的答案但没有异常、没有告警没人注意到。所以可观测性必须提前设计。我每次搭建AI服务都会在日志里记录这些字段输入原文与最终发给模型的完整Prompt模型输出的完整内容token消耗量与耗时模型名与prompt版本号是否走了降级分支用户最终反馈点赞/点踩有了这些你才能在用户投诉AI回答错了的时候快速定位是prompt问题、模型问题还是上下文遗漏问题。特别是用户反馈这一条很多团队不做但这是最真实的评测信号。每次用户对AI的回答点没用都是一条野生标注数据积累下来可以持续反哺评测集和prompt优化。关于部署本身AI服务本质上还是一个HTTP服务容器化、健康检查、滚动更新这些常规手段照用即可但有一个细节要注意模型API的服务商故障会直接传导到你的服务所以鲁棒性的设计必须前置。你的服务必须能处理模型供应商不可用的场景降级到缓存回答、规则引擎回答甚至人工接管而不是跟着一起挂掉。6. 写在最后AI工程真正值钱的部分不是模型本身走到这里这条路就完整了。回看整条路径你会发现一个有意思的事实真正让AI应用在业务里发挥价值的不是某个大模型的强大能力而是你围绕模型建立的这一整套工程系统——数据流、上下文策略、状态管理、评测机制、Agent护栏、prompt版本管理、成本控制、可观测性。这些东西没有一个存在于模型的权重里它们全是你一行一行代码写出来的。它们是模型的外部大脑让模型的概率输出变得可控、可测、可迭代、可依赖。说一个我最近的切身感受现在做大模型应用最难的部分早就不是模型的想象力了而是系统的约束力。你给模型一个广泛的权限它就能一路跑偏你给它一套设计精良的工具和边界它就能稳定地完成任务。这就像带一个能力很强但性格跳脱的新人光是能力强没用得靠流程、规范和反馈让他把能力用在刀刃上。如果你现在正准备开始你的第一个AI项目别急着训练模型也别急着上Agent。先把一个最简单的功能按照工程标准做完包一层调用层、做好日志、建一个评测集、设好版本号。然后在此基础上每次只加一个复杂度——今天加记忆明天加工具调用后天加Agent循环。每加一层复杂度都回到评测集上验证一次。按照这个节奏半年后你会发现from scratch不是指从零写代码而是指你从零建立了一整套理解和驾驭AI系统的方法论。那才是这个领域里真正能带走的资产。
返回列表