去年接了一个项目,客户想给内部客服系统加一套AI问答能力。我当时的第一个念头是租一台带A100的服务器,从零训练一个大模型——顺着这个思路找了一圈算力资源和训练方案,冷静下来后重新算了笔账,才真正意识到“AI engineering from scratch”这句话的重量。从零开始做AI工程,和从零开始训练一个模型,是两条完全不同的路,而大多数刚入门的人,包括当时的我,都容易把这两件事混在一起。
这篇文章想写的是我围绕“从零开始搭建AI工程能力”这一整套实践下来的完整思路,覆盖从模型接入、提示词设计、Agent开发,到多AI协作、测试评估和落地上线的完整链路。适合那些有传统软件开发经验、想切入AI应用层,但又不想(也暂时不必)陷入模型预训练深坑的开发者、产品经理或者独立创业者。文章里所有代码和配置我都跑过,属于可以直接拿去用的级别。
1. 先分清“训练模型”和“搭建AI系统”
1.1 为什么“从零开始”最容易理解错
“from scratch”这个词天然带有一种诱惑力,让人以为必须从底层把模型、数据集、训练流程全部造一遍才算真正的AI工程。我最初也是这么想的,直到认真评估了成本:一份像样的预训练语料清洗流程少说两三个月,单次训练烧掉的算力费用够一支小团队发一年工资,而且训练出来的模型能力大概率追不上已经开源的主流底座。更关键的是,就算把模型训练出来了,还要面对部署、推理优化、测评对齐等一系列问题,那条路不适合绝大多数应用团队。
我后来的理解是:AI工程的核心任务不是“造模型”,而是“用模型”。从零开始做AI工程,指的是从零开始构建一套围绕大模型能力的应用系统——你不需要从第一行注意力机制代码写起,但你需要从第一条API调用、第一个提示词模板、第一个Agent循环写起。这套系统的复杂度,恰恰不亚于训练一个模型,只是复杂的方向不一样。
1.2 一张认知地图:AI工程到底分几层
我给团队画过一张分层图,后来成了内部所有新人的入门材料,这里分享出来:
- 模型能力层:这是底座,包括基座模型的选型、上下文窗口长度、推理能力、多模态能力。这一层的决策决定了上层能做什么。
- 基础设施层:模型API接入、私有化部署、向量数据库、缓存、模型路由策略。这一层解决的是“模型服务怎么稳定可靠地暴露给业务层”。
- 应用编排层:提示词模板、Agent工具调用、工作流编排、记忆管理、多Agent协作。这一层是AI工程的真正主战场,也是大部分从业者最有价值感的领域。
- 产品闭环层:评估集构建、自动化测试、日志追踪、成本监控、灰度发布、反馈回收。没有这一层,AI应用永远只能停留在Demo阶段。
绝大多数人一上来就盯着第一层和第四层之间的巨大落差,要么想直接跳到最底层搞训练,要么只停留在最顶层写几个Prompt发朋友圈。真正成熟的AI工程团队,精力分配大概是我上面这张图倒过来的:产品闭环层消耗30%的时间,应用编排层消耗40%,基础设施层20%,模型能力层只需要10%。
1.3 学术路线和工程路线的分岔口
网上关于《Build a Large Language Model from Scratch》这本书的讨论热度一直很高,书确实写得好,把从数据集构造、tokenizer、注意力机制、预训练到微调的完整过程讲得很透。但它更适合走算法路线的人——研究模型内部机制,或者准备进入大模型训练相关岗位的人。如果你的目标是三个月内上线一个AI客服、一周内给业务做一个文档问答机器人,那么照这本书的路径走,一步都落不了地。
我认识一个做电商SaaS的朋友,花了两个月啃预训练相关的资料,最后做出来的东西还停留在本地跑通了一个小模型的玩具级推理。后来换了个思路,直接用开源底座加RAG,一周就交付了第一版可用功能,第二周客户反馈就回来了。这就是工程路线和学术路线的真实差距:前者在快速试探真实需求的形状,后者在试图一次性理解全部原理。两者没有高下之分,但你要先想明白自己站在哪条路上。
2. 模型接入的三种路径与选型逻辑
2.1 托管API:最快到达业务层的路径
托管API是最适合作为AI工程起点的接入方式。不需要关心显存、推理部署、动态batch,只需要一个Key,写几行代码就能把大模型能力接进自己的系统。业内主流的托管API我基本都测过,包括OpenAI的GPT系列、Anthropic的Claude系列,国内的话DeepSeek、通义千问、智谱GLM、Kimi也都有稳定的对外服务。从工程实践的角度,多接几家、做一层适配封装,而不是吊死在一家上,是更稳妥的做法。
一个最小可用的调用封装长这样:
import requests import json API_KEY = "你的key" BASE_URL = "https://api.deepseek.com/v1" def chat_with_model(prompt: str, system: str = "你是一个严谨、可靠的助手", temperature: float = 0.7) -> str: payload = { "model": "deepseek-chat", "messages": [ {"role": "system", "content": system}, {"role": "user", "content": prompt} ], "temperature": temperature } resp = requests.post( f"{BASE_URL}/chat/completions", headers={"Authorization": f"Bearer {API_KEY}"}, json=payload, timeout=30 ) resp.raise_for_status() return resp.json()["choices"][0]["message"]["content"]这段代码看起来简单,但有几个工程点值得注意。第一个是timeout必须设置,LLM的响应时间波动比普通API大得多,不设超时会被慢响应拖死整个调用链。第二个是system prompt不要写在代码里写死,要放到配置中心或者数据库里,这样调整系统人设时不需要重新发布代码。第三个是断点重试机制,模型服务偶尔会返回5xx错误,配一个指数退避的重试逻辑几乎是必须的。
2.2 私有化部署:什么时候才值得自己扛
开始做AI工程的头两个月,我始终绕不开一个问题:要不要上私有化部署?后来总结出一个判断标准:如果数据敏感度要求极高、请求量足够大(大到API调用成本已经明显高于自部署的硬件摊销),或者业务对网络路径有硬性的隔离要求,才值得考虑私有化。如果只是“感觉私有部署更安全”或者“大家都在搞”,那就先别碰。
以当前生态里比较成熟的本地推理工具Ollama为例,部署一个开源模型其实非常轻量:
ollama pull qwen2.5:14b ollama run qwen2.5:14b从拉取到跑通,也就几分钟。但真正的成本在之后:你需要关注显存占用、并发吞吐、量化精度损失、上下文长度限制。我实测过7B级别模型在纯CPU环境下的推理速度,慢到几乎无法支撑真实业务交互,所以私有化部署基本要和GPU绑定,这也意味着成本曲线在一个比较高的起点上。工程上还有一种折中方案:日常高频、简单的任务走托管API,敏感数据相关的任务走私有化部署,用一套路由层做分发。
2.3 模型路由:别把所有鸡蛋放一个篮子
真正做了一段时间AI工程之后,我发现模型选型不是一次性决策,而是持续演进的动态过程。不同模型的擅长领域、价格、响应速度差异很大,用同一个模型处理所有类型请求,成本上不划算,效果上也不是最优。比较常见的实践是搭一个轻量路由层,请求过来时先做规则判断:短文本分类任务走便宜的小模型,复杂推理和长文档任务走更强的旗舰模型,需要极低延迟的走专有模型版本。
路由判断不一定需要AI参与,大部分场景用传统规则就够了。比如请求里检测到“总结这份合同”“对比这两个方案”这类强推理意图时,直接路由到强推理模型;简单问答、关键词抽取这类任务,路由到轻量模型。这套逻辑的实现成本不高,但省下的token费用非常可观——我们线上一个业务跑下来,通过路由策略成本下降了差不多四成,响应时间的中位数也快了近一倍。模型能力不是越强越好,适合任务复杂度、成本预算和延迟要求的组合,才是好的选型。
3. 提示词工程:AI应用的第一层地基
3.1 为什么提示词值得按工程标准来做
很多人觉得提示词就是“跟AI说几句人话”,这种想法在个人娱乐场景下没问题,一旦进入生产环境就会崩。原因是业务提示词的生命周期远比想象中长——从第一个版本上线,到后续的优化迭代,中间可能要经历数不清的调整。如果提示词只是随手写在一段代码里,没有结构、没有版本、没有测试用例,那么每一次调整都是在赌运气。
我把提示词工程理解成“面向LLM需求描述的结构化方法”。它的产出物不是一句话,而是一份可以被版本管理、被测试、被复用的模板资产。生产环境的提示词应该有明确的层级、稳定的变量接口、可观测的输出格式。这是我做了几个项目之后才悟出来的,早期我也是一股脑把需求全部塞进一个系统提示词里,结果上线后模型经常出现“答非所问”或者“突发遗忘约束”的情况,排查起来极其痛苦。
3.2 结构化提示词的R-I-C-E框架
我自己整理了一套提示词模板方法,内部叫R-I-C-E框架,四个字母分别对应角色(Role)、意图(Intent)、约束(Constraints)、示例(Examples)。角色定义模型以什么身份回答问题,意图说明用户来做什么、模型要产出什么,约束圈定回答的边界与格式,示例则给模型提供具体的输出风格参考。这个框架不是什么学术成果,纯粹是从工程实践中揉出来的。
一个真实在用的模板长这样:
# 角色 你是一名资深的软件架构师,擅长系统设计评审。 # 意图 用户会给出一个系统设计方案,你需要从可扩展性、可维护性、安全性三个维度进行评审,并给出结构化的改进建议。 # 约束 1. 每个维度至少指出2个问题,最多5个问题。 2. 改进建议必须具体可执行,不能只给原则,比如要写明“在XX模块引入缓存,key设计建议为XX”。 3. 若用户提供的信息不足以支撑评审,明确列出缺失信息清单,不进行猜测。 4. 输出格式:使用Markdown,按“优点”、“问题”、“改进建议”三部分组织,总字数控制在800字以内。 # 示例 用户输入:一个基于微服务的电商中台设计…… 模型输出:……这个模板里,约束是灵魂。给模型划清楚边界,比在对话中反复纠正要有用得多。此外,示例的位置也很关键——放在约束之后,模型在理解规则后看到了正面的输出示范,生成质量的稳定性会明显提高。
3.3 温度与采样参数:一句参数改变整个性格
提示词文本之外,采样参数是经常被忽略的第二层控制。temperature控制随机性,值越低输出越保守、可复现性越强,值越高越有创造性但也越容易跑偏。top_p控制候选词累积概率范围,和temperature有相似的调节作用,但机制不同,实际使用中通常是固定一个、微调另一个。
不同任务对参数的偏好差异很大,我整理了一个参考表:
| 任务类型 | temperature | top_p | 说明 |
|---|---|---|---|
| 代码生成 | 0.0 - 0.2 | 0.1 - 0.5 | 代码要精确,宁可保守也不可乱造 |
| 实体抽取/分类 | 0.0 - 0.3 | 0.3 - 0.6 | 格式和内容都要严格可控 |
| 文案创作 | 0.7 - 1.0 | 0.8 - 1.0 | 需要一定的发散空间 |
| 技术问答 | 0.3 - 0.5 | 0.5 - 0.8 | 既要准确,又保留一点灵活性 |
| 头脑风暴 | 1.0 - 1.3 | 0.9 - 1.0 | 鼓励多样性 |
参数调整的原则是:先定任务要求的确定性程度,再选参数区间。如果发现输出结果总是“语义正确但格式不稳”,优先检查的是temperature是不是设太高了,而不是继续堆提示词。
3.4 few-shot策略:示例的数量和质量比想象中敏感
Early on我踩过一个很典型的坑:为了提升模型表现,我疯狂堆示例,一次给了模型二十多个历史good case,结果输出质量反而下降。后来研究采样原理才意识到,few-shot示例不是越多越好,它会挤占有限的上下文空间,并且示例噪声还可能把模型带偏。实践中效果最好的是给3到5个高质量示例,并且保证示例之间的差异性覆盖各种典型输入形态,而不是把相似的case重复贴一堆。
选示例的时候要有意识地做困难样本挑选。全是简单case会让模型过度拟合到“常规表现”,遇到稍微复杂一点的输入就崩。我在做AI客服辅助工具时,会在示例里专门放几个“用户描述含糊、包含错别字、信息不完整”的case,让模型学会在面对不完美输入时如何先澄清再回答。这种对困难示例的刻意设计,是提示词工程从入门走向及格的关键分水岭。
4. Agent开发的入场券:工具调用与任务循环
4.1 Agent的本质是“让模型拥有操作能力”
做完几个纯问答类项目之后,我明显感受到单轮对话的瓶颈:模型只会生产文本,但真实业务需要它“去做事”——查数据库、调用接口、写文件、发通知。如果把模型限制在“只能说话”的框框里,再好的提示词也救不了场景覆盖度。所以Agent开发成了我接下来重点突破的方向。
Agent和一次性Prompt调用的本质区别在于循环:模型不是给出一个答案就结束,而是进入“规划 → 调用工具 → 观察结果 → 再次规划”的循环,直到完成任务。你可以把Agent理解成一个配备了工具箱的实习生:他只擅长思考,每一步“动手”都要通过工具完成,而你要负责给他设计好工具箱的使用说明书(工具schema)和做事节奏(循环逻辑)。
4.2 工具调用的Schema设计:让模型“学会”调用接口
大模型本身不具备调用函数的能力,但它可以被“引导着”输出一段结构化的调用指令。主流模型厂商都支持function calling,本质是开发者声明一批工具的函数签名,模型根据用户请求决定要不要调用哪个工具、参数怎么填,然后返回一个结构化结果,由你的代码真正去执行。实测下来,工具schema写得清不清楚,直接决定了Agent的执行成功率。
设计工具schema有几点经验。函数描述要写得像给人类同事的交接说明一样具体,不要写“获取用户信息”这种模糊描述,要写“根据用户ID查询用户在系统中的注册信息、会员等级和最近订单时间,用于客服判断用户身份”;参数说明要讲清边界,比如字符串类型的日期参数要标注格式是YYYY-MM-DD;枚举值要全部列出来,否则模型经常自己发明数据。一个经过打磨的工具声明长这样:
[ { "name": "get_user_profile", "description": "根据用户ID查询用户注册信息、会员等级和最近订单时间,用于客服身份确认与权益判断", "parameters": { "type": "object", "properties": { "user_id": { "type": "string", "description": "用户唯一标识,格式为纯数字字符串" } }, "required": ["user_id"] } } ]4.3 一个最小Agent循环的骨架实现
理解了工具调用之后,写一个可用的Agent其实并不神秘。核心就是一个while循环,在循环里做四件事:把最新的工具返回结果拼进消息历史,请求模型判断下一步动作,解析模型输出(可能是最终答案,也可能是工具调用指令),执行工具并把结果追加到消息上下文。下面是简化版的骨架代码:
import json from typing import Callable, Dict TOOL_REGISTRY: Dict[str, Callable] = { "get_user_profile": get_user_profile, # 真实业务函数 "query_order_status": query_order_status, } def run_agent(user_message: str, system_prompt: str, max_steps: int = 5) -> str: messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_message}, ] for _ in range(max_steps): resp = call_model_with_tools(messages=messages, tools=TOOL_SCHEMAS) msg = resp["message"] # 情况一:模型给出的是最终回答 if not msg.get("tool_calls"): return msg["content"] # 情况二:模型要求调用工具 messages.append({**msg, "content": None}) for call in msg["tool_calls"]: func = TOOL_REGISTRY[call["function"]["name"]] args = json.loads(call["function"]["arguments"]) result = func(**args) messages.append({ "role": "tool", "tool_call_id": call["id"], "content": json.dumps(result, ensure_ascii=False) }) return "已到达最大步骤数,任务未完成"这段代码跑通了一个核心闭环,但距离生产还有一个关键问题:max_steps循环里如果模型反复调用同一个工具、拿同一个结果,就形成了死循环。我一般在每次循环里加一个简单的状态检查,记录最近三步的调用结果,如果模型重复请求相同参数的工具调用,直接打断并引导它基于已有信息给出结论。
4.4 记忆管理:Agent的上下文不能无限膨胀
Agent循环次数一多,消息列表会越来越长,上下文窗口很快被占满,而且越靠后的信息对模型的注意力影响越大,早期的关键信息容易被稀释。记忆管理成了Agent工程质量的关键点。我的实践分三层:对话滑动窗口,只保留最近N轮对话防止基础膨胀;工作摘要记忆,每一轮工具调用结束后让一个快速模型生成本轮摘要,把摘要追加到上下文中;长期知识检索,把关键的业务实体信息定期写入向量库,在需要时通过相似度检索重新召回。
三层配合使用,既能控制token消耗,又能保证Agent在复杂任务中不会“失忆”。这里有一个比较容易忽略的细节:摘要模型和主Agent模型的上下文不能混为一谈,摘要如果写得过于精简,丢失的细节反而会误导Agent。我踩过这个坑之后,把摘要的要求改成了“保留所有数字、名称、状态和结论”,信息丢失率明显降低。
5. 多Agent协作:把单点能力串成生产线
5.1 单Agent的极限与多Agent的动机
Agent单点能力再强,也逃不过两个问题:一是单个Agent的提示词上下文会被越来越多的职责稀释,让它既要会理解客户情绪又要能查数据库还要能写回复,每一项都做不精;二是很难用一套参数和模型适配所有子任务的最优需求。这时候多Agent协作的价值就出来了——一个系统里不同的Agent各司其职,分别拥有独立的提示词、独立的模型选型、独立的上下文窗口,它们之间通过消息传递完成工作交接。
我用一个内容生产流水线举例。传统做法是写一个超级Agent,输入“帮我出一篇关于智能家居的评测文章”,它从头写到尾,中间几乎没有过程控制和质检。多Agent的做法是拆成五个角色:策划Agent负责分析受众和选题角度,产出文章大纲;资料Agent按照大纲检索素材,整理成要点清单;写作Agent根据大纲和素材完成初稿;评审Agent扮演刁钻读者提出修改意见;编辑Agent根据意见做最后修订和格式优化。
5.2 消息协议:Agent之间怎么说话
多Agent协作的工程重点不在每个Agent本身的实现,而在于它们之间的消息传递格式。实际项目中我倾向于定义统一的中间产物协议,用JSON格式在各个Agent之间流转,而不是直接传递自然语言。比如策划Agent的输出是这样一份结构化数据:
{ "title": "2025年智能家居选购指南", "target_audience": "对新装修家庭有智能家居配置需求、预算约2-5万的用户", "core_angles": ["性价比组合", "生态兼容性", "安装易用性"], "outline": [ {"section": "引言", "key_points": ["为什么现在适合入手", "文章将解决什么问题"]}, {"section": "预算分配", "key_points": ["不同价位段的组合方案", "推荐优先级"]} ] }下游的写作Agent不再需要自己琢磨选题角度和文章结构,它只需要接收这份JSON,然后专注于“把它变成一篇好文”。每一层Agent的输入输出边界清晰之后,整个系统就可以单独替换任意一环——换一个更强的写作模型,不动其他环节;给策划Agent加一个热点检测工具,也不影响下游。
5.3 工作流引擎的选型:从Dify到LangGraph
多Agent协作逻辑复杂起来之后,自己手写编排代码开始变得吃力,分支、重试、并发的逻辑散落在各处,维护成本快速上升。这时候值得引入工作流引擎。国内社区里比较成熟的可视化方案有Dify和Coze,国外常用的有LangGraph和Temporal。我的选型经验是:业务团队想快速看到效果、且非技术人员也需要参与配置的,优先看Dify,图形化编排对团队门槛低,插件生态也够用;技术团队主导、需要深度定制状态机和精确控制循环逻辑的,选LangGraph这类代码优先的框架。
不管用哪个引擎,核心编排思想是一致的:Agent之间的调用关系被抽象为节点和边,节点是具体的Agent或工具动作,边是流转条件。串行适合有严格先后依赖的工序,并行适合互不依赖的独立子任务,条件分支则根据前一步结果决定下一步走向。我在流水线里把资料检索和用户画像分析做成了并行节点,两个Agent同时跑,总耗时直接砍半。这类优化听起来不大,但在生产环境里就是实打实的体验提升。
5.4 编排中容易踩的稳定性坑
多Agent协作系统上线后,我遇到的最典型问题是“错误传导”。上游Agent偶尔的一次信息偏差,经过多轮传递之后会被下游Agent当作事实基础,最终输出完全走样。后来我在每个Agent的输入提示词里都加了一行强约束:若上游提供的信息中存在明显缺失或矛盾,禁止自行脑补,必须在输出中显式标注“信息存疑”。同时,在Agent间的消息协议里增加了一层“来源标注”,每个字段都记录是哪个上游Agent在哪个步骤生成的,出了问题可以精确回查。
另一个问题是token成本呈指数级增长。一次完整的多Agent协作,总token消耗可能是单Agent调用的五到十倍。应对手段有三板斧:尽量复用中间结果做缓存,相同输入的大纲生成不重复调用;并行节点合并小模型任务;对低风险的中间环节使用更便宜的轻量模型。成本控制不是事后核算,而是要在架构设计阶段就纳入约束条件。
6. 测试、评估与上线:AI工程闭环的最后一公里
6.1 没有测试集,就没有优化依据
业务系统上线前要写单元测试、集成测试,但AI应用经常被当成“没法测试”的特例。这是我早期最大的盲区。AI的输出具有概率性,传统断言方式确实不适用,但不代表不能测试。正确的做法是建评估集(Eval Set):收集一批代表性用户输入,为每条输入标注理想输出标准或关键要素,然后让系统在同样的输入上跑一遍,用规则或模型来判断输出质量。
评估集不需要一开始就做到面面俱到,我的建议是二十个golden case起步,覆盖三种类型:核心业务场景的成功路径、容易被绕过的边界与负面case、需要模型明确拒绝的违规输入。随着线上真实反馈积累,评估集逐步扩充,每次改动提示词或更换模型之后,都拿评估集整体回归一遍。就这样把“感觉变好了”变成“指标变好了”,AI应用的迭代才进入正循环。
6.2 自动化评估的落地方案
自动化评估的做法分层递进。第一层是规则断言,检查输出是否包含必要字段、长度是否合规、格式是否为合法JSON,这类检查最简单也最可靠。第二层是关键词与语义标注,判断回答是否覆盖了评估标注里的核心要点。第三层是用一个独立的“评审模型”对生成结果打分,输出结构化评分和理由。不同层级的成本和准确度差异明显,理想状态是一层一层叠加,而不是一上来就用评审模型代替全部检查。
一个简易的评估脚本骨架如下:
def evaluate_response(input_text: str, response: str, required_points: list) -> dict: results = {} # 第一层:格式与结构检查 results["valid_json"] = is_valid_json(response) results["length_ok"] = 100 <= len(response) <= 2000 # 第二层:关键要素命中 missing = [p for p in required_points if p not in response] results["coverage"] = (len(required_points) - len(missing)) / len(required_points) # 第三层:评审模型打分 judge_prompt = f"...需要补充评审模型的完整指令..." judge_score = call_judge_model(judge_prompt) results["judge_score"] = judge_score return results第三层的评审模型指令是这套方案能否成立的关键,写法和前文的提示词工程方法论一致:要让评审模型明确“按什么标准打分”“多少分算合格”“什么情况必须一票否决”。我给评审模型设置了一票否决项——比如回答中包含编造的数据、答非所问、或者直接暴露系统提示词内容,不管其他指标多好,这条case直接判失败。
6.3 可观测性建设:给AI系统装上仪表盘
AI应用上线后,黑盒运行是最大的隐患。提示词模板被命中多少次、某类请求的模型响应平均耗时、工具调用失败率、单次会话平均token消耗、用户对回答的点击反馈——这些数据必须全量采集。我的实践是在所有模型调用入口统一封装一个日志模块,每次调用记录模型名称、输入摘要、输出摘要、耗时、token数、成本估算、关联的业务ID,统一写入日志管道,再对接到可视化看板。
这套观测体系的直接价值是:某个模型供应商的服务变慢时,能从日志中立刻定位是哪类请求受影响,并且快速切换路由;某个提示词模板改完之后回答质量下滑,可以通过评分数据直接对比版本差异。没有观测数据,优化全靠玄学,有了观测数据,每个决策都有据可循。
6.4 灰度发布与人工复核兜底
AI系统的上线要慎之又慎,直接全量切换是危险操作。我的标准流程是先在内部小范围试用,再开放给5%的真实用户流量,观察评价数据和用户反馈,确认稳定后再逐步放大到30%、70%、100%灰度发布是AI工程里性价比最高的风险管理手段。每一步放量之间至少要间隔一个完整业务周期,确保能看到不同时段、不同用户群体对系统的真实使用情况。
灰度期间的人工复核兜底同样不能省。对于客服辅助、内容生成这类涉及业务合规的场景,在系统输出之后、正式发送给用户之前,保留一个人工确认步骤是完全值得的。机器先生成,人只负责审核和放行,带来的成本增加有限,但避免了大量潜在的业务风险。等到系统评分持续稳定在一个较高标准后,再逐步降低人工抽检比例。
做了一段时间之后回头总结,我对“AI engineering from scratch”最大的感受是:真正的难点不在某个单点技术有多深,而在于把这套链路中的每一个环节都当成工程问题来对待——选型要有依据,提示词要能维护,Agent要可控,评估要可量化,上线要有灰度。这和你做任何一款成熟软件产品的逻辑是一样的,只不过调度对象从一个确定性的代码库,变成了一个概率性的模型。
最后分享一个小技巧:起步阶段别一上来就铺多Agent、上工作流引擎、搞一套大而全的平台。先用手写代码把一个最小业务闭环跑通,哪怕是最傻的单次Prompt调用都行,然后再逐步加入工具调用、评估集、观测日志、协作编排。每一步都基于真实暴露的问题去演进,而不是基于想象去设计。这套从零开始的路子,至少帮我避开了无数轮无效的过度设计。