ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从零搭建AI工程:从模型接入到Agent编排的完整实践指南

2026/10/3 23:55:06 拓冰建站 浏览量
从零搭建AI工程:从模型接入到Agent编排的完整实践指南 1. 项目概述当你说“从零开始做AI工程”的时候到底在说什么“ai-engineering-from-scratch”这个标题我第一眼看到的时候其实挺感慨的。市面上讲“从零开始学AI”的文章多到泛滥但绝大多数要么是教你怎么装个库跑个demo要么是直接甩给你一套调API的封装教程。真正能把“工程”两个字落到实处的少之又少。我个人的理解是这个项目标题背后藏着一个很明确的诉求不满足于当一个“调包侠”而是想从底层逻辑出发把AI应用从设计、开发、测试到上线迭代这条完整的链路自己捋一遍。它面向的是一群想真正理解AI系统如何运转的人——可能是刚入行的算法工程师可能是想往AI方向转的后端开发者也可能是已经在用LangChain或各类Agent框架做业务应用、但总觉得底层逻辑不够清晰的技术负责人。这篇文章我不会跟你扯什么复杂的数学推导也不会把某个开源库的源码逐行贴出来做书虫式讲解。我会以一个真实做过几个AI项目的从业者视角把这个标题背后涉及的工程链路、设计决策、踩坑实录和排查思路完整地讲一遍。整个项目围绕一条主线展开如何不依赖现成的“全家桶”式AI框架而是像搭积木一样从环境准备、模型接入、工具设计、Agent编排、评测体系到部署监控亲手把一套完整的AI应用系统搭建出来。从零开始不等于重复造轮子更不等于非要从线性代数开始补数学。我见过太多人把“from scratch”理解成“什么都自己写”结果死在了半路。真正的含义应该是理解每一层组件存在的意义知道系统里每个环节为什么这样设计遇到问题的时候能够定位到具体层次去修复。这就是AI工程和AI脚本之间最大的分水岭。2. 全链路架构拆解一套AI系统到底有哪些“层”2.1 基础设施层先想清楚你的算力和模型跑在哪里动手之前第一件事其实是决定模型和算力策略。这不是选个贵的就行的简单事它直接决定你后续所有架构设计。我当时做第一个项目的时候贪图方便直接全部调线上API结果到了评测环节发现数据要反复跑、token开销像流水一样淌一天烧掉几百块那是常有的事。后来学乖了先明确哪些场景必须上最强模型哪些场景用小模型或者缓存策略就能兜住。基础设施层主要包含三个决策点计算资源你用本地GPU、云GPU实例还是直接调用托管API这三种方式的成本结构完全不同而且会反向影响你的架构设计。比如本地GPU适合批量推理和模型微调但可用性低不能扛线上流量云实例弹性好但单价不菲API最省心但受限于网络和单次上下文的长度。模型接入层无论用OpenAI兼容接口、开源模型本地部署还是国产模型平台都需要一个统一抽象层。我习惯把所有模型调用封装成一个LLMClient接口上层业务完全不管底层是GPT、Claude、Qwen还是本地部署的Llama。这一步看起来多余但当你想换模型、做A/B测试、或者在多路模型之间做路由分发的时候它省下的时间能以十倍计算。配置与环境隔离API密钥、模型名称、温度参数、最大token数这些全部放进环境变量或配置文件严禁硬编码到代码里。我见过不止一个团队把密钥提交到Git仓库的事故那才是最致命的“from scratch”翻车现场。# 一个典型的模型接入抽象层用Python实现大概长这样 from abc import ABC, abstractmethod import os import httpx class BaseLLMClient(ABC): abstractmethod def chat(self, messages, temperature0.7, max_tokens1024): 输入消息列表返回模型回复字符串 ... class OpenAICompatClient(BaseLLMClient): 兼容OpenAI接口格式的客户端适配绝大多数在线模型 def __init__(self, model_name, base_urlNone): self.model model_name self.api_key os.getenv(LLM_API_KEY) self.base_url base_url or os.getenv(LLM_BASE_URL, https://api.openai.com/v1) self.client httpx.Client(headers{ Authorization: fBearer {self.api_key} }) def chat(self, messages, temperature0.7, max_tokens1024): resp self.client.post(f{self.base_url}/chat/completions, json{ model: self.model, messages: messages, temperature: temperature, max_tokens: max_tokens, }) resp.raise_for_status() return resp.json()[choices][0][message][content]注意上面的代码只是一个骨架示例真实生产环境还需要加超时重试、异常分类处理、token用量统计和流式输出支持。千万别把这个直接拷到生产用但理解这个抽象层的思路是后面所有工程化的前提。2.2 数据与上下文工程层模型不是数据库别什么都往里塞这是我认为整个项目中含金量最高的认知。很多第一次上手做AI工程的人把模型当成一个超大号的数据库什么问题都往上下文里一丢指望它自动输出正确答案。短期demo可以长期工程一定是灾难。上下文工程要解决的核心问题是用户的请求进来后系统如何高效地组织、检索、裁剪信息让模型在有限的上下文窗口内获得最相关的信息。这不是简单的“把文档塞进Prompt”而是需要一套完整的数据管线。我个人在项目里把这一层拆成了四个子模块知识库接入文档解析、清洗、切分。切分策略直接决定检索质量我踩过最大的坑是机械地按固定字符数切分结果把完整的业务逻辑拆得七零八落检索出来的片段语义不连贯。后来改成“按标题层级切分 语义相似度合并”效果立刻不一样了。向量化与检索Embedding模型选型、向量库的选择、检索策略。这里面有太多细节比如是单向量检索还是多路召回要不要做重排rerank每种方案在不同场景下的准确率差异很大。记忆管理多轮对话的记忆不是简单的消息堆叠而是要把历史会话中的关键信息抽取出来、结构化缓存。我自己常用的是“摘要 原始消息”双轨制短期的原始消息保留最近几轮再叠加一个不断更新的长短期摘要既保证上下文不膨胀又能留住跨会话的关键信息。上下文裁剪策略当检索结果太多或者对话历史太长时要做动态裁剪和排序。这里需要关注的不仅是token数量还有信息在上下文中的位置——模型对中间部分的注意力往往弱于首尾所以关键信息要尽量放在Prompt的开头或结尾附近。2.3 Agent编排与执行层从单轮问答到多步任务如果你只是想做一个“在线聊天机器人”那前两层就已经够了。但既然“AI工程”要解决的是真实业务问题那一定会碰到多步骤、需要调用外部工具、并根据中间结果动态调整路径的任务。这就是Agent要干的事。Agent编排层是项目里最复杂也是最有意思的部分。从结构上看它至少包含任务规划器Planner把用户的目标拆解成一个可执行的步骤列表。工具注册中心Tool Registry维护系统能调用的所有外部能力清单比如搜索引擎、数据库查询、代码执行器、HTTP API等。执行与反馈循环Execution Loop调用模型 → 解析决策 → 执行工具 → 观察结果 → 再调用模型的这一整圈循环。这个“循环”恰恰是当前AI Agent领域最热门也是最核心的概念。有些工程实践里把它叫 harness engineering有些叫 loop engineering本质都在讲一件事不要让大模型一步登天直接输出答案而是让它在一个受控的循环中不断思考、行动、观察逐步逼近正确结果。我之前在一个项目里手写过一个小型Agent循环核心逻辑大概像下面这样def run_agent(task, max_steps10): messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: task} ] for step in range(max_steps): response llm.chat(messages, temperature0.3) decision parse_decision(response) # 解析当前结果判断是结束还是需要调工具 if decision[type] final_answer: return decision[content] # 执行工具调用把结果追加回消息上下文 tool_result execute_tool(decision[tool_name], decision[arguments]) messages.append({role: assistant, content: response}) messages.append({role: tool, content: tool_result}) raise TimeoutError(Agent loop exceeded max steps)这个循环看起来简单但里面有三个设计要点一是如何解析模型的输出。是让模型输出严格的JSON、用函数调用的原生格式还是通过在文本中标记特殊符号来切分我经验是最稳妥的方案是优先选用模型自带的function calling能力因为这是模型在训练阶段就对齐过的格式解析失败率最低。如果用的是本地开源模型不具备这个能力那就退而求其次用约束式解码或者先让模型输出一小段思考过程再结构化输出。二是循环什么时候停。必须有最大步数限制和最小置信度阈值否则遇到一个模棱两可的工具调用结果Agent就会陷入“反复重试→结果还不一样的”死循环。这个问题在真实业务中非常常见我至少见过三四个团队在这个问题上栽跟头。三是状态跟踪与回溯。当Agent已经执行了三步发现路径不对需不需要回退很多简单实现的做法是只往下走不回退这在复杂业务场景中会累积错误。更健壮的设计是引入一个显式的“状态栈”每执行一步就把中间状态压栈一旦发现异常就可以逐层回溯重选路径。2.4 评测与质量保障层没有度量就没有工程这是“从零开始做AI工程”里最容易忽略但最关键的环节。传统软件开发有单元测试、回归测试、覆盖率这些手段到了AI时代很多人却回到了“跑一下看看效果好不好”的原始状态。这在工程上是完全不可接受的。我自己的经验是AI系统的评测要分三个梯度第一梯度确定性断言。比如工具调用格式是否正确、某些硬性关键字是否出现、返回的JSON是否能解析。这些可以用传统测试框架直接做属于不需要模型参与的规则评测。第二梯度传统指标。比如分类任务的准确率、召回率、F1检索任务的命中率、MRR。前提是你得准备一个有标准答案的测试集这就要回到数据标注的功夫上。第三梯度模型辅助评测。也就是用LLM-as-a-judge的方式拿一个强模型当裁判对生成内容做多维度的打分——比如相关性、忠实度、有害性等。这个方案灵活度高但有个隐患裁判模型本身也有偏好偏差。同一份回答用一个裁判模型换一个提示词打分可能差出10分。实操心得我在项目里落地评测体系时是分三步走的。第一步先搭一个离线评测集把过去半年业务中用户反馈好和反馈差的对话各抽几百条做成自带标签的测试夹具第二步把评测脚本接进CI/CD流水线每次模型或提示词变更都必须跑完全量测试集才能合并第三步才是在线监控对生产流量做抽样子采样配合人工标注修正评估模型的漂移。这套体系搭起来可能要花一周左右的时间但后面省下的返工时间绝对远超这个投入。3. 实操路线从0.5到1.0的五个递进阶段3.1 阶段零先跑通“最小可行闭环”很多人一上来就想直接写Agent框架这是最大的误区。我的建议是先把最基础的链路跑通用户输入 → 调用一次模型 → 展示响应。这个环节看似简单但有两个容易被忽略的点。第一个是模型参数对交互体验的影响。温度设太高用户觉得回答飘设太低创造性任务又不够灵活。最大token数设少了长文本生成直接被截断用户看到的是一句半截话。这些参数必须在一开始就建立配置化的管理方式而不是每改一次参数就动一次代码。第二个是错误处理。模型API不是每次都能成功的超时、限流、上下文超长、内容被内容审核拦截这些异常场景都必须有对应的用户友好的兜底逻辑。我在第一个版本里没考虑这些结果上线当天就因为高频请求触发了限流每个用户都看到一大串英文报错。这种事故本来完全可以避免。3.2 阶段一给系统接入“外部工具”最小闭环跑通后下一步就是让模型具备调用工具的能力。我强烈建议从搜索工具做起因为它能立刻让你的系统从“一个空有知识的聊天机器人”进化成“一个能获取实时信息的助手”而且搜索工具的结果好坏比较容易量化评估。工具接入的核心是函数描述的设计。每个工具至少要写清楚函数名称建议动词开头的snake_case、参数列表每个参数的名称、类型、是否必填、含义描述、返回值结构。这些描述会被编码进模型调用的上下文中描述写得越清晰精准模型做出正确工具调用的概率就越高。我当时做过一个实验把搜索工具的参数描述从一句“query: string”改成“query: 一个简洁的搜索关键词通常是疑问句中去除停用词后的核心实体”模型调用工具的成功率直接提升了十几个百分点。这个细节很值得跟大家分享函数描述本身就是一种廉价而高效的Prompt优化。3.3 阶段二加上记忆与上下文管理工具调用的下一步是让Agent在多次交互中“记住”之前的对话内容。这一步的工程复杂度是跳跃性上升的。我在项目里亲手实现了一套轻量级记忆模块核心设计如下短期记忆保留最近N轮N一般取5-8的原始消息用于保证对话的连贯性。长期记忆每一轮对话结束时异步调用模型把本轮对话压缩成结构化摘要存入本地向量库或JSON文件。记忆检索当新对话进来时先从长期记忆中检索与当前问题相关的旧摘要注入上下文。这里有一个很关键的参数平衡问题短期记忆的N轮数和长期记忆的摘要长度直接决定单次请求的token消耗和响应延迟。N太大则上下文爆掉N太小则对话连贯性受损。我是用线上日志统计用户的真实平均对话轮数来确定N的而不是拍脑袋定的。3.4 阶段三引入评测与回归机制当Agent能力扩充到一定程度就进入了最难受的阶段——改了A场景的表现B场景却退化了。我自己的项目里经常出现的情况是为了提升代码生成的准确率改了一下工具调用的逻辑结果语义问答的准确性掉了5个百分点。没有评测体系的话这种回归问题会累积到你完全不敢动代码的程度。我的落地方式是搭了一套“场景回归集”把过去业务中积累的所有用户场景分类每个场景手工准备20-30个测试用例并为每个用例标注明确的通过标准。然后再做“跨场景同测”脚本每次改动后自动跑一遍全部用例输出一张“场景×用例的通过率矩阵”。任何一次修改都必须保证所有场景的通过率不下滑才能进入下一步。这套东西前期准备烦琐但它几乎是最快能让你从“随缘开发”切换到“工程化迭代”的杠杆。3.5 阶段四多Agent协作与复杂任务编排单Agent的系统能力上限通常卡在上下文长度和模型单次推理的深度上。要处理更复杂的任务就得做多Agent协作。这一步项目本质上是把一套系统拆分成多个有明确分工的“角色”再由一个编排层统一调度。多Agent架构没有银弹我用过且验证可行的有三种模式主管-下属模式一个主管Agent负责任务拆解和结果验收多个下属Agent各自负责一个子任务。适合子任务之间耦合度低、可以并行的场景。流水线模式每个Agent是一个独立阶段前一个Agent的输出是后一个Agent的输入。适合任务链条清晰、每阶段目标明确的场景。辩论与投票模式多个不同角色Agent针对同一问题独立给出分析和建议再由一个裁判Agent或规则函数选择最优答案。适合高风险决策场景。多Agent协作最大的坑在于状态同步与通信开销。我做过一个项目里同时启用三个Agent每个Agent都要读共享上下文结果token消耗直接翻了四倍响应时间从2秒飙到了15秒。后来加了共享内存模块和按需检索把“全量共享上下文”改成“按需拉取上下文切片”才把性能拉回可用的范围。4. 工具选型解析框架用哪家还是自己写4.1 主流框架的定位与局限现在市面上的AI框架大致可以分为两类通用编排框架和特定用途脚手架。通用编排框架以LangChain、LlamaIndex为代表特点是组件丰富、抽象层次高、上手快。特定用途脚手架则更像是给某个具体业务场景量身定做的模板比如一些AutoGen、CrewAI这类主打多Agent协作的工具。我个人的使用感受是框架在项目初期能帮你节省大量时间但到了后期往往成为约束。尤其是当你要做细粒度的控制——比如自定义工具调用失败的恢复策略、定制上下文裁剪的逻辑、精细调整个别Agent的Prompt——框架的封装就会成为一层“厚厚的墙”你要么花大量精力去读懂它的源码和扩展机制要么就得绕开它自己另起炉灶。这里我给一个务实的建议如果你对Agent的底层运行机制还不完全清楚先用成熟框架把业务跑通把注意力放在业务逻辑和评测上等业务稳定了再选择性地把框架中与你业务耦合最深的那个环节替换成自研实现。不要一开始就为了“技术洁癖”而自己从零写调度器那样性价比实在太低了。4.2 自研小框架的取舍标准什么情况下值得自研我的判断标准是三条现有框架无法满足你对性能的极致要求比如单轮响应延迟必须低于某阈值而框架的延迟开销占了不可忽略的比例你需要对执行过程做非常细致的日志埋点和控制——比如把每一步工具调用的输入输出全部记录下来用于评测或审计而框架的原始日志不能满足你需要在多个Agent之间做自定义的复杂协同逻辑——比如条件分支跳转、任务优先级抢占、多Agent结果合并策略等。我后来给自己的项目写了一个极简的Agent核心也不复杂就三个类ToolRegistry工具注册、ContextManager上下文管理、AgentLoop循环调度。加起来不到三百行代码。但它让我获得的最大收益是任何一步出问题我都能快速定位到是哪一层逻辑的锅——这在调框架的黑盒时几乎不可能做到。4.3 模型选型不要只看排行榜模型选型是整个项目中性价比最高也最容易踩坑的决策点。很多人习惯用一两个公开榜单的指标来选模型但这在真实业务中往往不太实用。榜单指标和你业务场景上的实际表现之间的相关性常常低得惊人。我的做法是搭建一个业务专属的模型评测基线准备几十条与你业务高度相关的测试问题然后让候选模型各跑一遍按你定义的通过标准打分。一次评测可能花几个小时但比盲目相信榜单要靠谱得多。具体到选型的策略上我分三个档次主力模型承担最复杂的推理和生成任务选最强的模型但只在必要的时候启用。经济模型处理简单任务比如意图识别、实体抽取、格式转换。这类任务用最强模型纯属浪费经济模型至少能节省一半以上的token开支。本地模型处理隐私敏感数据或者作为离线批量处理的兜底。本地模型的质量目前跟在线模型有差距但进步很快值得持续跟踪。5. 常见问题与排查技巧实录5.1 模型输出不可控对话变成了“脱缰的野马”这是所有做AI工程的人都会碰到的问题。表现为模型偶尔不按预设格式输出、突然在回答里“扮演”其他角色、或者对同一个问题给出自相矛盾的答案。排查思路分两步首先检查系统提示词是否足够明确。很多人在System Prompt里只写了“你是助手”四个字然后就指望模型自己理解一切。这是不现实的。系统的约束需要是显式的、可校验的。我见过一个写得比较极端的System Prompt把输出格式、禁止行为、兜底回复示例、评分标准全部写进去了然后模型的失稳概率明显下降。其次要检查温度参数和采样参数。温度过高是脱轨的第一号元凶。有些模型支持top_p、frequency_penalty等参数它们对输出的可控性影响也很大。调试的时候建议先调低温度再看是否需要改提示词。不要一上来就大改提示词那样你根本分不清问题出在哪个环节。5.2 Agent工具调用失败循环卡住或者反复重试工具调用环节最常见的故障是模型生成了一个格式不正确的工具调用指令或者调用的参数与实际工具不匹配。我的经验是把工具调用失败的处理逻辑设计成分层恢复机制第一层语法修复。如果模型输出的JSON格式不完整尝试用正则或系统自身容错算法补全。第二层参数映射。模型给出的参数名或枚举值与工具定义不完全一致时做一个语义模糊匹配的转换。第三层错误反馈。把工具返回的完整错误信息送回模型让模型基于错误信息自己调整调用策略。第三层往往是最有效的因为大模型对“错误信息”非常敏感能给到高质量的纠错反馈。但要注意必须把最大重试次数限制在2-3次否则会让响应时间变得不可接受。5.3 Token耗尽与上下文膨胀上下文膨胀几乎是所有AI应用规模化的必经之痛。现象很直接系统运行着运行着变慢了成本越来越高模型回答开始丢失早期对话中的关键信息。排查时先用日志把每一轮的token消耗打出来观察是哪些环节在吃token。大部分情况下你会发现罪魁祸首是“历史消息重复发送”“检索结果冗余”“工具返回结果没压缩”这三件事。我的对策是历史消息分层短期记忆保留原文长期记忆压缩成摘要每层设置上限。检索结果压缩检索回来的文档先抽取与问题最相关的段落再做冗余去除和重排而不是把整篇文档塞进上下文。工具输出瘦身工具调用结果如果是长文本先做截断或摘要再放回上下文。这一步能极大延长可用的对话轮数。5.4 评测集维护标注过时与过拟合评测体系搭起来之后第二阶段最容易出现的问题是评测集与线上真实分布脱节。今天收集的测试用例三个月后业务侧重点变了测试集已经没有代表性了。这时就需要建立评测集的定期更新机制。我的做法是每两周做一次“线上用例抽样in”的流程从生产日志中随机抽一批新对话人工标注后加入评测集同时移除一部分已经失去代表性的旧用例。这是一个运营性质的工作没有自动化捷径但它是保证评测体系长期有效的必要成本。另外要警惕“过拟合测评集”的问题。当一个系统的Prompt或Agent逻辑反复根据评测集调优到一定程度后评测集上的分数会虚高线上效果却可能原地踏步甚至下降。缓解手段是维护两个互相隔离的测试子集——一个叫“开发集”用于迭代调优一个叫“留出集”只在发布前跑一遍。如果开发集分数一直在涨而留出集分数停滞那就说明开始过拟合了该停下来审查方向了。5.5 响应延迟多Agent系统的性能瓶颈多Agent协同的延迟问题是把“demo能跑”变成“产品能用”的最大障碍。我观察到延迟主要消耗在三个地方模型推理时间是天然的硬成本多个Agent串行执行时时间叠加还有多次模型调用之间的等待和调度开销。解决思路从两个方向入手。一是并行化把可以并行的子任务从串行改为并发执行用Python的asyncio或者多线程来管理。这一步往往能直接砍掉一半以上的响应时间。另一种是减少模型调用次数能一次推理完成的就不要拆成多次能用规则判断的就不要调用模型。模型不是万能的系统中尽可能把“确定性逻辑”用传统代码实现让模型只负责真正需要语义理解的部分这个原则能显著降低延迟和成本。6. 从“能跑”到“好用”我自己的一点点体会项目做到最后我最大的体感是AI工程和传统软件工程在思维方式上有一个根本的差异——传统开发追求的是确定性而AI工程必须拥抱概率性。你写的每一段代码都默认模型可能会有意外输出你设计的每一个流程都要考虑模型调用失败时的替代路径。这种思维模式的转变比学会某个具体框架难得多也重要得多。如果你正在做类似从零开始搭建AI系统的项目我的最后一条建议是不要追求一步到位先做一个小而完整的东西把链路跑通再逐步填充能力。跟你分享一个我最早做过的蠢事——那时候我花了两周时间设计一个“完美的”多Agent架构画了一堆架构图结果真正的代码一行都没写后来发现那个架构根本没法落地推倒重来。一切架构设计都应该基于对真实运行数据的理解而不是凭空想象。这个项目后续还有很多扩展空间比如把在线评测结果反馈为模型微调的训练数据、把单一Agent扩展到可插拔的工具生态、加入记忆压缩的自动策略优化等。但核心的工程底座——那些评测、循环、上下文管理、可观测性的设计——一旦打好后续无论往上叠什么能力都是顺水推舟的事。希望这篇文章里写下的这些实操经验能帮你少走几步我已经踩过的弯路。