ARTICLE DETAIL

资讯详情

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

从0到1构建Agent:14个设计决策与通用内核的复利实践

从0到1构建Agent:14个设计决策与通用内核的复利实践 做 Agent 这件事我前前后后折腾了差不多一年。从最开始觉得不就是把大模型套个循环调工具到后来被上下文爆炸、工具误调用、状态丢失、评测失真轮番教育中间推翻重来的架构少说有三四版。这篇不打算讲什么宏大叙事就把我踩过的坑、做过的 14 个设计决策以及最后沉淀出来的那个通用内核到底怎么产生复利一条条摊开讲清楚。如果你正准备从 0 搭一个 Agent或者手里的 Agent 已经能跑但总在真实场景里翻车这篇应该能帮你少走几个月弯路。先说结论性的判断Agent 的难点从来不在能不能调通模型而在于你怎么设计它的决策边界、状态流转和工具契约。模型能力是外部变量你控制不了但内核设计是你自己的事做对了后面每加一个能力都是复利做错了每加一个功能都是负债。下面我按为什么这么决策的逻辑展开而不是给你一份照抄的清单。1. 先想清楚 Agent 到底在解决什么别急着写循环1.1 从套壳循环到决策系统的认知转变我最早写的 Agent 特别朴素一个 while 循环把用户输入丢给模型模型返回工具调用就执行执行完把结果塞回上下文直到模型说我完成了。跑 demo 的时候感觉良好一旦任务超过五六步就开始出问题——要么无限循环要么提前收工要么把中间结果忘得一干二净。后来我才想明白这个循环本质上是个状态机而我只写了状态转移没定义状态本身。Agent 的核心不是调用模型而是维护一个关于任务进展的信念状态并基于这个状态决定下一步动作。这个认知转变直接影响了后面所有设计决策。举个具体例子。用户说帮我把这个季度的销售数据整理成报告。朴素循环会怎么做它可能先查数据库拿到一堆原始数据然后试图直接生成报告结果发现数据格式乱七八糟又回头清洗清洗完忘了原本要按什么维度汇总。而一个设计良好的 Agent 会先建立任务状态目标生成季度报告、已知数据源位置、待办取数、清洗、汇总、成文、约束季度范围、报告格式。每一步动作都在更新这个状态而不是把一切寄托在模型的上下文记忆里。1.2 什么任务适合 Agent什么任务纯属自找麻烦这是我踩过的第一个大坑不是所有任务都值得做成 Agent。我一开始雄心勃勃想把所有内部工具都 Agent 化结果发现很多任务用固定工作流workflow反而更稳、更便宜、更好调试。判断标准其实很简单我总结成一张表任务特征适合 Agent适合固定工作流步骤数量不确定动态变化固定可枚举分支逻辑依赖中间结果动态判断提前能画清楚工具选择需要模型自主决定每步用哪个工具是确定的容错要求可接受重试和探索要求一次成功成本敏感度相对不敏感高度敏感比如根据用户自然语言查询生成 SQL 并执行这种步骤固定理解意图→生成 SQL→校验→执行→格式化用工作流就够了硬套 Agent 只会增加不确定性和 token 消耗。而帮我调研这三个竞品并写一份对比分析这种中间要搜索、要判断信息是否充分、要决定是否深挖某个方向才是 Agent 的主场。提示如果你能用一张流程图把任务完整画出来且没有根据结果决定下一步的菱形分支那它大概率不需要 Agent。1.3 通用内核的雏形把变化的部分隔离出去想清楚上面两点后我开始意识到一个关键设计原则内核要稳定能力要插件化。这也就是后来通用内核 插件化思路的起点。内核负责什么负责 Agent 的生命体征——任务状态的维护、上下文的组装与裁剪、工具调用的调度与校验、循环的终止判断、错误的捕获与恢复。这些逻辑无论你做什么类型的 Agent 都不会变。插件负责什么负责具体能力——查数据库、调 API、读文件、发消息、画图。这些是随业务变化的应该像积木一样可插拔。这个隔离带来的好处是复利式的内核改一次所有 Agent 受益插件加一个所有 Agent 都能用。我后面做的四种复利本质上都是这个隔离原则的延伸。2. 上下文管理Agent 翻车的头号重灾区2.1 为什么把历史全塞进去必然失败我踩过最惨的一次坑是一个长任务跑到第 40 多步的时候突然开始胡言乱语。排查半天发现上下文已经堆到了十几万 token模型开始注意力涣散把早期的指令和最新的结果混在一起甚至开始编造不存在的工具调用。这不是模型不行是我设计不行。上下文不是越多越好而是越相关越好。人的工作记忆也有限你不会在做第 40 步的时候还逐字回忆第 1 步说的每句话你会记住结论和关键约束。Agent 也该如此。我后来采用的策略是分层上下文系统层角色定义、核心约束、可用工具清单。这部分永远保留但要求极度精简。任务层当前目标、已完成的关键结论、待办事项。这是动态维护的工作记忆。近期层最近 N 步的完整交互。保留细节但 N 要小。归档层更早的历史压缩成摘要或存到外部需要时再检索。2.2 上下文裁剪的三种实操手段具体怎么裁我试过三种手段各有适用场景。第一种是滑动窗口。最简单只保留最近 K 轮。缺点是会丢掉早期的重要约束。我一般只在任务短、约束少的场景用。第二种是摘要压缩。让模型把早期历史总结成一段话。这里有个坑摘要会丢信息而且摘要本身也要花 token。我的经验是摘要要结构化而不是叙述化——不要生成用户先让我做 A然后我做了 B这种流水账而要生成目标X已完成A、B关键约束Y待办C这种结构化状态。第三种是外部记忆 检索。把历史存到向量库或结构化存储里需要时按相关性检索回来。这是最强大的方案但也是最复杂的检索质量直接决定效果。我一般在中长任务里用这套。实际项目中我通常是组合使用近期用滑动窗口中期用结构化摘要远期用外部检索。2.3 一个反直觉的经验主动遗忘比主动记忆更重要大部分人做 Agent 记忆时第一反应是怎么记住更多。但我实测下来设计好什么时候忘比记住什么更关键。原因很简单无关信息是噪声会稀释模型的注意力。一个已经完成的子任务它的详细过程对后续步骤往往毫无价值留着只会干扰。我现在会在每个子任务完成后主动把它的详细过程压缩成一句结论把原始交互丢掉。这个主动遗忘的机制让我的 Agent 在长任务里的稳定性提升非常明显。具体做法是每完成一个里程碑触发一次状态整理把过程转成结论把结论并入任务层。3. 工具设计契约不清Agent 必乱3.1 工具描述写不好模型就会乱调我见过太多 Agent 项目工具函数写得挺漂亮但描述就一句话查询用户信息。结果模型要么不用要么乱用要么传错参数。工具描述本质上是给模型的 API 文档它得回答三个问题这个工具干什么、什么时候该用、参数怎么填。我现在的工具描述模板大致是这样{ name: query_order, description: 根据订单号查询订单详情。当用户询问某个具体订单的状态、金额、物流时使用。不要用于查询订单列表。, parameters: { order_id: { type: string, description: 订单号格式为 ORD 开头的 16 位字符串例如 ORD20240101123456 } } }注意几个细节描述里明确说了什么时候用和什么时候不用参数给了格式示例。这些看似啰嗦但能极大降低误调用率。3.2 工具粒度太粗和太细都是坑工具粒度是个反复权衡的问题。太粗比如一个处理订单工具包揽所有订单操作模型没法精细控制太细比如把查询订单拆成连接数据库执行 SQL解析结果三个工具模型要调三次才能完成一件事既慢又容易出错。我的经验法则是一个工具对应一个语义完整的动作。什么叫语义完整就是这件事在人看来是一个不可再分的业务操作。查询订单是完整的执行 SQL不是因为没人会关心你底层怎么查。还有一个技巧把高频组合动作封装成一个工具。比如查询订单并格式化如果总是成对出现就合成一个工具减少调用轮次。3.3 工具返回值的处理别把原始数据直接喂回去这是我早期的一个大坑。工具返回一大坨 JSON我原封不动塞回上下文结果上下文瞬间爆炸而且模型被无关字段干扰。正确做法是在工具层做一次结果整形只返回模型决策需要的信息把无关字段过滤掉把长文本截断或摘要把结构化数据转成模型友好的格式。比如查询订单返回 50 个字段但模型只需要订单状态、金额、预计送达时间那工具就只返回这三个。这个整形逻辑放在工具内部而不是让模型自己去筛。注意整形不是丢信息而是按当前任务需要保留信息。如果后续可能用到完整数据可以存到外部在返回值里给个引用 ID。3.4 工具调用的幂等与副作用隔离有副作用的工具发消息、下单、删数据必须特别小心。我踩过的坑是Agent 因为超时重试把同一条消息发了两遍。解决方案是给有副作用的工具加幂等键。每次调用带一个唯一 ID服务端根据 ID 去重。同时Agent 内核要区分只读工具和写入工具写入工具调用前要有更严格的确认机制甚至引入人工确认环节。4. 循环控制与终止判断什么时候该停4.1 无限循环的三种典型成因Agent 跑飞最常见的形式就是无限循环。我总结下来有三种成因第一种是目标未达成但模型以为达成了。比如模型说已完成但实际输出是空的或错的。这需要内核做结果校验而不是盲信模型的自述。第二种是工具反复失败但模型反复重试。比如 API 一直超时模型就一直重试同一个调用。这需要内核设置重试上限和失败升级机制。第三种是模型在两个动作间反复横跳。比如一会儿查 A 一会儿查 B来回切换。这通常是上下文里信息冲突导致的需要检测动作重复模式并强制打断。4.2 终止条件要分层设计我现在用的终止判断是分层的硬性上限最大步数、最大 token、最大耗时。这是兜底防止失控。目标达成判断由内核校验而不是模型自述。比如要求输出必须包含某些字段或者必须通过某个校验函数。无进展检测如果连续 N 步状态没有实质变化判定为卡住触发退出或求助。显式终止模型主动调用完成任务工具且通过校验。这四层里无进展检测是最容易被忽略但最有用的。很多循环不是真的无限而是原地打转加上这个检测能省下大量 token。4.3 失败不是终点而是决策点我早期把失败当成异常直接抛错退出。后来发现失败其实是 Agent 最有价值的决策点。工具失败了Agent 应该能判断是重试、换工具、降级处理还是上报求助这个判断能力是 Agent 和固定脚本的本质区别。我在内核里加了一个失败处理策略层根据失败类型超时、参数错、权限错、结果空给出不同的默认动作同时允许模型覆盖。比如参数错就重新生成参数权限错就直接上报超时就重试一次再上报。5. 状态与记忆Agent 的人格连续性5.1 无状态 Agent 为什么走不远我做过一个完全无状态的 Agent每次调用都是独立的不保留任何历史。短任务没问题一旦任务需要跨多轮、跨会话就彻底歇菜——用户昨天说的偏好今天完全不记得。Agent 的连续性来自状态。这个状态至少包括任务状态当前在做什么、用户状态偏好、历史、环境状态外部系统的当前情况。5.2 状态存哪里内存、文件还是数据库这是个工程选择我三种都用过内存最快但进程重启就没了。适合单次会话内的临时状态。文件简单可持久化适合单机、低频场景。我用 JSON 存任务状态读写都方便。数据库适合多用户、高并发、需要查询的场景。但引入的复杂度也最高。我的建议是从文件开始需要时再升级。很多项目一上来就上数据库结果大部分复杂度都花在了状态同步上得不偿失。5.3 记忆的写入时机比读取策略更关键大部分人优化记忆时盯着怎么检索得更准但我发现写入时机才是决定记忆质量的关键。如果每步都写记忆会写进去大量噪声如果只在最后写会丢掉中间的重要发现。我的做法是在里程碑处写入完成一个子任务、得到一个关键结论、用户给出一个重要偏好时才触发记忆写入。这样写进去的都是精华。6. 插件化架构通用内核的复利来源6.1 内核与插件的边界怎么划这是整个架构设计的核心问题。我的划分原则是内核管怎么想插件管做什么。内核负责循环调度、上下文管理、状态维护、工具注册与调用、错误处理、终止判断。这些是元能力与具体业务无关。插件负责具体工具的实现、特定领域的提示词、领域相关的校验逻辑。这些是业务能力随场景变化。边界清晰的好处是我可以拿同一个内核去跑客服 Agent、数据分析 Agent、代码助手 Agent只需要换插件。6.2 插件接口设计稳定优先插件接口一旦定下来就要尽量稳定因为所有插件都依赖它。我踩过的坑是接口改来改去导致每个插件都要跟着改维护成本爆炸。我现在用的插件接口大致包含name唯一标识、description给模型看的描述、parameters参数 schema、execute执行逻辑、side_effect是否有副作用、timeout超时设置。这套接口跑了很久没大改因为它在抽象层级上足够高。6.3 插件的注册与发现别硬编码早期我把插件硬编码在一个列表里加一个插件就要改内核代码。后来改成自动发现扫描指定目录加载符合接口的插件。这样加插件就是加文件内核完全不用动。这个改动看似小但它让加能力的成本从改内核 测试 发布降到了加一个文件。这就是复利的开始。7. 评测没有评测的 Agent 优化都是玄学7.1 为什么 Agent 评测比模型评测难模型评测有标准答案对错分明。Agent 评测难在任务是开放的路径是多样的结果好坏往往需要综合判断。我早期优化 Agent 全靠感觉改完跑几个 case 觉得好像好点了其实根本没有量化依据。后来痛定思痛搭了一套评测集才发现很多优化其实是负优化。7.2 评测集怎么构建从真实失败案例来我的评测集不是凭空造的而是从真实失败案例里攒的。每次线上或测试中发现一个失败 case就把它整理成一条评测样本标注期望行为和判定标准。这样攒下来的评测集特别接地气因为它覆盖的都是真实会翻车的场景。我现在有几百条这样的样本每次改内核都跑一遍回归问题一目了然。7.3 评测指标别只看成功率只看成功率会漏掉很多问题。我现在的指标包括指标含义为什么重要任务成功率最终是否达成目标基础指标平均步数完成任务用了多少步反映效率token 消耗总消耗反映成本工具误调用率调用了不该调的工具反映契约质量循环率出现原地打转的比例反映终止判断人工干预率需要人介入的比例反映自主性这几个指标一起看才能全面判断 Agent 的健康度。比如成功率没变但步数涨了说明效率退化了。8. 四种复利通用内核到底带来了什么8.1 能力复利加一个插件所有 Agent 受益这是最直接的复利。因为内核统一、插件标准化我写一个发邮件插件客服 Agent、运营 Agent、个人助理 Agent 全都能用。写一次处处受益。我现在的插件库已经有几十个覆盖了常见的查询、通知、文件、数据处理能力。新做一个 Agent很多时候只需要组合已有插件几乎不用写新代码。8.2 优化复利内核改一次全局提升上下文裁剪策略优化了所有 Agent 的稳定性一起提升。终止判断改进了所有 Agent 的循环率一起下降。错误处理增强了所有 Agent 的健壮性一起变好。这种改一处、惠全局的效应是通用内核最大的价值。如果每个 Agent 都是独立实现同样的优化要做 N 遍。8.3 评测复利一套评测集复用所有场景评测集和评测框架也是复用的。内核级的评测比如循环控制、上下文管理对所有 Agent 都适用。领域级的评测可以按插件组织用到哪个插件就跑哪部分。这让持续优化变得可行。没有评测复利每次优化都是盲人摸象有了它优化变成了有反馈的迭代。8.4 认知复利踩过的坑变成团队资产最后一种复利最隐性但最重要踩过的坑变成了可复用的认知。因为内核是统一的一个 Agent 踩的坑修在内核里其他 Agent 就不会再踩。比如工具返回值要整形这个教训我修在内核的工具调用层之后所有 Agent 都自动受益。这种认知的沉淀和复用是团队效率的长期来源。9. 那些让我印象最深的坑9.1 坑一以为模型能记住一切早期我特别信任模型的上下文记忆觉得只要塞进去它就能记住。结果长任务里模型频繁失忆把早期约束忘得一干二净。教训是永远不要假设模型会记住要用显式的状态管理来兜底。9.2 坑二工具描述写得像给同事看的我一开始写工具描述默认看的人懂业务写得特别简略。结果模型完全不懂什么时候该用。教训是工具描述是写给一个聪明但完全不了解你业务的新人看的要假设对方零背景。9.3 坑三没有评测就优化这个坑我踩得最久。凭感觉优化了大半年搭了评测集才发现很多改动是负优化。教训是先建评测再谈优化。没有度量优化就是赌博。9.4 坑四把 Agent 当万能药我曾经想把所有任务都 Agent 化结果很多简单任务被搞得又慢又不稳。教训是Agent 是重武器用在对的地方。能用工作流解决的别上 Agent。9.5 坑五忽略成本早期我只看效果不看成本一个任务跑几十步、烧几万 token 也不心疼。上线后账单教我做人。教训是成本和效果要一起优化很多场景下够用就好比极致效果更划算。10. 给正在从 0 做 Agent 的你如果你现在正准备动手我给几条最实在的建议。第一先想清楚任务适不适合 Agent。别一上来就写循环先画流程图看看有没有动态分支。没有的话工作流更香。第二内核和插件尽早分离。哪怕一开始只有一个 Agent也把通用逻辑抽出来。这个前期投入会在你加第二个、第三个 Agent 时成倍回报。第三上下文管理要当成一等公民。别等上下文爆炸了才想起来优化一开始就设计好分层和裁剪策略。第四工具描述当文档写。多花十分钟写清楚什么时候用、什么时候不用、参数什么格式能省下后面无数次的调试。第五尽早建评测集。哪怕只有几十条也比没有强。从真实失败案例攒越攒越有价值。第六接受失败是常态。Agent 不可能 100% 成功关键是设计好失败后的降级和求助路径而不是追求完美。最后分享一个我自己的体会做 Agent 最忌讳的是想一步做一步。它是个系统工程上下文、工具、状态、循环、评测环环相扣任何一环设计不好都会拖垮整体。花时间把内核设计对后面就是复利急着堆功能后面就是还债。我踩过的这些坑希望你能绕过去。
返回列表