资讯动态

AI Agent七要素与七个决策点:从零搭建最小Agent的工程实践指南

发布时间:2026/10/27 11:45:13 来源:尧图企业网站定制
1. 为什么“七要素”和“七个决策点”是理解 Agent 的最短路径很多人第一次接触 AI Agent脑子里冒出来的第一个画面是“会自己干活的聊天机器人”。这个理解不算错但太浅了。真正动手搭过 Agent 的人都知道一个能跑起来的 Agent 和一段普通的 LLM 调用之间差的不是一点半点——差的是整套工程结构。你让模型回答一个问题那是问答你让模型自己决定要不要调工具、调哪个工具、调完之后结果对不对、不对要不要重试这才叫 Agent。我见过太多团队在这一步栽跟头Demo 阶段用几十行代码拼出一个“能查天气、能算数”的玩具一旦要接入真实业务、要处理多轮任务、要保证稳定性整个系统就开始崩。问题不在于模型不够强而在于他们脑子里没有一张清晰的结构图。七要素解决的是“一个 Agent 由哪些部件构成”七个决策点解决的是“这些部件在运行时到底在做什么判断”。前者是静态解剖后者是动态流程。把这两张图吃透你再看 LangChain、Dify、CrewAI 这些框架就不会被它们的 API 绕晕——因为你知道它们本质上都在实现同一套东西。这篇文章面向的是已经写过 LLM 调用、想往 Agent 方向深入的人也适合正在做 Agent 选型和架构设计的技术负责人。我会把七要素逐个拆开讲清楚它为什么存在、缺了会怎样再把七个决策点按运行时顺序串起来配上参数计算、代码骨架和踩坑经验。读完你应该能自己从零搭一个不依赖重框架的最小 Agent并且知道每一步为什么这么设计。先给一个总览后面逐层展开。七要素是模型Model、指令Instruction、工具Tools、记忆Memory、编排Orchestration、状态State、护栏Guardrails。七个决策点是是否触发 Agent、选哪个模型、要不要调工具、调哪个工具、参数怎么填、结果是否可信、下一步走向哪里。这两组概念不是并列的而是互相咬合的——每一个决策点背后都依赖某几个要素的支撑。提示不要一上来就纠结框架选型。框架是七要素的封装你连要素都没搞明白选哪个框架都是碰运气。2. 七要素拆解每个部件缺了会出什么事2.1 模型与指令Agent 的“大脑”和“岗位说明书”模型是 Agent 的推理核心这一点没有争议。但很多人忽略的是Agent 场景对模型的要求和普通对话场景完全不同。普通对话看重的是语言流畅度和知识广度Agent 场景看重的是三件事指令遵循能力、结构化输出能力、多步推理的稳定性。一个在聊天里表现惊艳的模型放到 Agent 里可能因为不肯老老实实输出 JSON 而让整个流程瘫痪。我实测下来的经验是选模型时优先看它在 function calling 或 tool use 上的原生支持程度。原生支持工具调用的模型输出的参数结构更规范解析失败率能低一个数量级。如果模型不支持原生工具调用你就得靠 prompt 硬约束它输出特定格式然后自己写解析器——这条路能走但容错成本高很多后面第七节会详细讲怎么兜底。指令Instruction常被当成一句 system prompt 随便写写这是大坑。指令本质上是这个 Agent 的“岗位说明书”它要回答你是谁、你的目标是什么、你能用哪些工具、你不能做什么、遇到不确定时怎么办。我见过一个客服 Agent 因为指令里没写“无法确认订单号时不要编造”结果它凭空捏造了一个订单状态直接造成客诉。指令里必须显式列出禁止行为因为模型在压力下比如用户反复追问会倾向于“给个答案”而不是“承认不知道”。指令的写法有个实用技巧把工具清单和调用规则写进指令而不是只丢给工具定义。原因是模型在生成时指令的权重通常高于工具描述。你可以在指令里写“当用户询问天气时必须先调用 get_weather 工具禁止凭记忆回答”这样能显著降低它跳过工具直接瞎答的概率。2.2 工具与记忆Agent 的“手脚”和“笔记本”工具Tools是 Agent 与外部世界交互的唯一通道。没有工具Agent 就只是个会说话的模型有了工具它才能查数据库、发请求、写文件、调 API。工具的定义质量直接决定 Agent 的能力上限。一个工具定义包含三部分名称、描述、参数 schema。名称要语义清晰描述要说明“什么时候用”而不只是“这是什么”参数 schema 要严格。这里有个反直觉的点工具不是越多越好。当工具数量超过十几个模型的选择准确率会明显下降因为它要在太多选项里做判断。我的做法是按业务域分组每组不超过 8 个工具需要跨组时用编排层做路由。另外工具描述里一定要写清楚边界比如“本工具只能查询最近 30 天的数据”否则模型会在超范围时硬调然后拿到空结果还继续编。记忆Memory分短期和长期。短期记忆就是当前对话的上下文长期记忆是跨会话持久化的信息。很多新手把两者混为一谈结果要么上下文爆炸要么该记住的用户偏好每次都忘。短期记忆的管理核心是裁剪策略什么时候截断、截断时保留哪些、怎么压缩。长期记忆的核心是写入和检索什么信息值得存、存成什么结构、下次怎么捞出来。我踩过的一个坑是长期记忆无脑全存结果检索时噪声太大捞出来的十条里八条没用。后来改成只存“用户明确表达的偏好”和“任务关键结论”检索命中率立刻上来了。记忆不是越多越好是越准越好。2.3 编排、状态与护栏Agent 的“调度中心”“账本”和“刹车”编排Orchestration决定 Agent 的执行流程是单轮直出还是多步循环是线性执行还是带分支和并行。最简单的编排就是一个 while 循环——模型输出、判断是否要调工具、调完把结果塞回上下文、再让模型输出直到它给出最终答案或达到步数上限。复杂一点的编排会引入规划节点、反思节点、多 Agent 协作。状态State是 Agent 运行时的“账本”记录当前进行到哪一步、已经调过哪些工具、拿到了什么中间结果、还剩多少预算。状态管理做得好Agent 就能断点续跑、能审计、能回放做得差就会出现重复调用、上下文错乱、无法定位问题。我强烈建议把状态显式建模成一个结构体而不是散落在各个变量里。这样出问题时你能一眼看出是哪一步的状态不对。护栏Guardrails是最容易被忽略、但出事时最要命的一环。它包含输入过滤、输出校验、工具调用权限、预算限制、敏感操作二次确认等。护栏的本质是假设模型会犯错然后在关键节点上拦住它。比如工具调用前校验参数范围工具返回后校验结果格式最终输出前校验是否包含禁止内容。没有护栏的 Agent 上线等于开车不装刹车。要素核心作用缺失后的典型症状模型推理与生成无法理解任务、输出格式混乱指令定义角色与边界行为漂移、越权操作、编造答案工具连接外部世界只能空谈、无法执行实际操作记忆保持上下文与个性化多轮失忆、重复询问、体验割裂编排控制执行流程流程死板、无法处理多步任务状态记录运行时信息无法续跑、难以排查、重复执行护栏约束与保护越界调用、预算失控、输出风险内容3. 七个决策点Agent 运行时到底在判断什么3.1 触发决策与模型选择不是所有请求都该进 Agent第一个决策点是是否触发 Agent。这听起来像废话但实际系统里非常关键。Agent 调用是有成本的——token 成本、延迟成本、还有出错风险。如果用户只是说了句“你好”你没必要启动一个带工具调用的完整 Agent 流程。常见的做法是在 Agent 前面加一个轻量分类器或规则层判断这个请求是简单问答、闲聊还是需要多步执行的复杂任务。我做过一个项目前期没做这层过滤所有请求都走 Agent结果简单问题的平均响应时间从 800ms 涨到 4 秒成本翻了三倍。后来加了一个基于关键词和意图分类的前置判断把大约 60% 的请求挡在 Agent 之外直接走普通对话整体指标立刻好转。这个决策点的价值就在于此把 Agent 用在真正需要它的地方。第二个决策点是选哪个模型。一个成熟的 Agent 系统往往不是单模型而是模型组合。简单判断用小模型复杂推理用大模型工具参数生成用对结构化输出友好的模型。这个决策可以静态配置按任务类型路由也可以动态判断先让小模型试置信度低再升级。动态升级的策略要小心因为它会带来延迟抖动用户体验上要能接受才行。3.2 工具调用三连问要不要调、调哪个、参数怎么填第三个决策点是要不要调工具。模型需要判断这个问题我能不能凭已有知识回答还是必须借助外部信息。判断错误的两种表现是该调不调凭记忆瞎答可能过时或错误和不该调乱调浪费资源还可能引入噪声。降低误判的关键在指令里写清楚工具的适用场景以及“不确定时优先调用工具”的默认策略。第四个决策点是调哪个工具。当工具数量多时这是最容易出错的一步。模型要在所有工具里选出最匹配的那个。提升准确率的实用手段有三个一是工具描述写清楚使用场景和反例二是给工具分组先选组再选工具三是把常用工具排在前面利用位置偏好。我实测过光是优化工具描述这一项选择准确率就能提升 20% 以上。第五个决策点是参数怎么填。这是最琐碎也最容易翻车的一步。模型要根据用户输入和上下文把参数填成工具要求的格式。常见问题包括类型不对该填数字填了字符串、必填项缺失、枚举值超出范围、日期格式不匹配。护栏要在这里介入做参数校验不合法就返回错误让模型重填而不是硬着头皮调用。# 工具参数校验的简化骨架 def validate_tool_call(tool_schema, params): errors [] for field in tool_schema[required]: if field not in params: errors.append(f缺少必填参数: {field}) for key, value in params.items(): expected tool_schema[properties].get(key, {}).get(type) if expected integer and not isinstance(value, int): errors.append(f参数 {key} 应为整数) if expected string and not isinstance(value, str): errors.append(f参数 {key} 应为字符串) return errors3.3 结果可信度与下一步走向Agent 的自我校验和循环控制第六个决策点是结果是否可信。工具返回了结果但这个结果能不能直接用可能是空结果、可能是报错、可能是格式不对、可能是明显异常的值。Agent 需要判断这个结果是否满足任务需求不满足的话是重试、换工具还是向用户澄清。这一步做不好Agent 就会拿着错误结果继续往下走最后输出一个看似合理实则错误的答案。我的经验是给工具返回加一层“结果摘要”处理把原始返回压缩成模型容易判断的形式同时在指令里明确“如果工具返回为空或报错不要编造应告知用户或尝试其他方式”。这一步能挡掉大量幻觉。第七个决策点是下一步走向哪里。这是循环机制的核心继续调工具、生成最终答案、还是终止并报错。判断依据包括任务是否完成、步数是否超限、预算是否耗尽、是否陷入循环。循环检测特别重要我见过 Agent 因为工具一直返回相同结果而无限重试最后烧掉大量 token。解决办法是记录最近几次的工具调用签名如果重复出现就强制跳出。决策点判断依据出错后果主要防护手段是否触发请求复杂度、意图资源浪费或能力不足前置分类器选哪个模型任务难度、成本预算质量或成本失衡静态路由动态升级要不要调工具知识时效性、确定性幻觉或资源浪费指令约束默认策略调哪个工具工具描述匹配度调错工具、任务失败描述优化分组参数怎么填schema 约束调用报错参数校验护栏结果是否可信返回内容质量错误传播结果校验摘要下一步走向任务状态、预算死循环或提前终止循环检测步数上限4. 从零搭一个最小 Agent不依赖重框架的实操路径4.1 环境准备与核心循环骨架理解了七要素和七个决策点接下来动手搭一个最小可用 Agent。我建议第一版不要用 LangChain 这类重框架因为框架会帮你隐藏太多细节你反而学不到东西。用原生 API 加一个 while 循环把七个决策点显式写出来跑通之后再考虑要不要上框架。环境上你只需要一个能调用的模型 API 和一个 HTTP 客户端。核心循环的逻辑是把系统指令、对话历史、工具定义一起发给模型模型返回后检查它是否要求调用工具如果要求执行工具并把结果追加到历史如果不要求输出最终答案并结束。这个循环里七个决策点分别落在不同位置——触发判断在循环外模型选择在请求构造时工具相关决策在解析模型输出时结果校验在工具执行后走向判断在循环末尾。def run_agent(user_input, max_steps8): messages [{role: system, content: SYSTEM_PROMPT}] messages.append({role: user, content: user_input}) state {step: 0, tool_calls: [], done: False} while state[step] max_steps and not state[done]: response call_model(messages, toolsTOOL_SCHEMAS) if response.has_tool_call: call response.tool_call errors validate_tool_call(get_schema(call.name), call.args) if errors: messages.append({role: tool, content: f参数错误: {errors}}) state[step] 1 continue result execute_tool(call.name, call.args) state[tool_calls].append(call.name) messages.append({role: tool, content: summarize(result)}) else: state[done] True return response.content state[step] 1 return 任务未在限定步数内完成这段骨架虽然简单但已经把七要素里的模型、指令、工具、状态、编排、护栏都体现出来了。记忆在这里是 messages 列表护栏是参数校验和步数上限。你可以先跑通它再逐个增强。4.2 工具定义与参数校验的落地细节工具定义是这一步的重头戏。我建议用 JSON Schema 描述参数因为主流模型对它的理解最到位。每个工具的描述里除了功能说明还要写清楚“什么时候不该用”。比如一个查询订单的工具描述里要写“仅用于查询已支付订单未支付订单请用另一个工具”。这种负向说明能显著减少误调。参数校验要分两层第一层是结构校验检查必填项、类型、枚举范围第二层是业务校验检查参数在业务上是否合理比如日期不能是未来、金额不能为负。结构校验可以自动生成业务校验需要手写。两层都过了才执行工具任何一层不过都把错误信息返回给模型让它重填。这里要注意错误信息要写得让模型能理解并修正而不是抛一个技术堆栈。注意工具执行一定要设超时。我遇到过外部 API 卡住导致整个 Agent 挂起的情况加了超时和重试之后才稳定。超时后返回的错误信息也要友好让模型知道是超时而不是参数错误。4.3 记忆裁剪与上下文预算控制上下文窗口是有限资源Agent 跑多步之后 messages 会越来越长必须裁剪。裁剪策略我常用的是“保留系统指令 最近 N 轮完整对话 更早对话的摘要”。摘要可以用模型生成也可以规则提取关键信息。关键是别把工具调用的中间结果全留着那些往往很长且用完就没用了可以只保留摘要。预算控制要算清楚每步请求的 token 数 系统指令 历史 工具定义 当前输入。工具定义如果很多本身就很占 token。我的做法是动态加载工具定义只把当前任务可能用到的工具放进请求而不是全量塞进去。这样既省 token又能提升工具选择准确率一举两得。5. 那些文档不会告诉你的踩坑实录5.1 工具描述写得太“技术”模型反而不会用我早期写工具描述习惯写成“输入参数为 user_id返回用户信息”这种技术文档风格。结果模型经常在该调用的时候不调用。后来改成“当用户询问账户相关信息如余额、等级、注册时间时使用本工具参数 user_id 可从对话中获取”调用率立刻上来了。工具描述是写给模型看的不是写给同事看的要用自然语言说明使用场景而不是罗列技术细节。还有一个细节工具名称不要用缩写。qry_usr_bal这种名字模型很难理解query_user_balance就好很多。名称本身就是语义信号别浪费它。5.2 循环失控Agent 反复调同一个工具的排查过程有一次上线后监控报警某个会话的 token 消耗异常高。我拉日志一看Agent 在反复调用同一个查询工具每次参数都一样结果也一样但它就是不停。排查下来发现两个问题一是工具返回空结果时指令里没写清楚该怎么办模型就默认重试二是没有循环检测系统不知道自己在做重复动作。修复方案分三步第一在指令里明确“如果工具返回空结果最多重试一次仍为空则告知用户”第二在状态里记录最近三次工具调用签名重复就强制终止第三给单次会话设 token 预算上限超了就中断并提示。这三招下去循环失控再没出现过。这个坑的教训是永远假设模型会陷入循环提前设好刹车。5.3 参数幻觉模型编造了一个不存在的枚举值另一个印象深刻的坑是参数幻觉。一个工具的状态参数只接受pending、paid、cancelled三个值模型却填了个processing。它不是在乱填而是根据语义“合理推测”了一个值。问题在于工具端没做枚举校验直接拿这个值去查数据库返回空结果模型又基于空结果编了个答案给用户。修复很直接在参数校验层加枚举检查不合法就返回“状态参数只能是 pending/paid/cancelled 之一”让模型重填。同时把枚举值写进工具描述里。这件事让我意识到护栏不是可选项是必选项。模型的“合理推测”在开放场景是优点在需要精确匹配的场景就是灾难。5.4 多轮对话里记忆污染导致的答非所问还有个隐蔽的坑是记忆污染。用户先问了一个订单Agent 查了订单 A接着用户问“那它的物流呢”Agent 却去查了订单 B 的物流。原因是历史里订单 A 和订单 B 的信息混在一起模型没分清“它”指代谁。解决办法是在记忆里给关键实体打标签比如把订单号显式记录在状态里并在指令里要求“指代不明时先向用户确认”。另外工具返回结果里要带上实体标识方便模型对齐。这些坑的共同点是它们都不会在 Demo 阶段暴露只有真实流量、多轮交互、边界输入才会触发。所以我的建议是Agent 上线前一定要做压力测试和对抗测试专门喂一些模糊、矛盾、超范围的输入看它怎么反应。6. 框架选型LangChain、Dify、CrewAI 到底该怎么选6.1 先想清楚你要的是“库”还是“平台”框架选型的第一步不是比较功能而是想清楚你的使用形态。LangChain 更像一个库给你积木让你自己拼灵活但学习曲线陡Dify 更像一个平台可视化编排上手快但定制受限CrewAI 专注多 Agent 协作适合角色分工明确的场景。选错了形态后面怎么用都别扭。我的判断标准是如果你需要深度定制执行流程、要嵌入现有系统、团队有工程能力选 LangChain 这类库如果你想快速验证、业务人员也能参与编排、不需要太底层的控制选 Dify 这类平台如果你的任务天然可以拆成多个角色协作比如一个写一个审可以看 CrewAI。没有最好的框架只有最匹配的形态。6.2 用七要素当尺子去量每个框架不管你选哪个都可以用七要素去评估它。模型支持哪些、指令怎么管理、工具怎么注册、记忆怎么持久化、编排支持哪些模式、状态是否可观测、护栏有没有内置。拿这把尺子一量每个框架的强弱项就清楚了。比如有的框架工具生态丰富但状态管理弱有的编排灵活但护栏要自己加。框架形态编排能力状态可观测护栏内置适合场景LangChain库强可自定义需自行实现少需自建深度定制、嵌入现有系统Dify平台中可视化较好部分内置快速验证、业务参与CrewAI库多 Agent 协作中等少角色分工明确的任务6.3 我的实际组合轻框架 自建核心循环折腾一圈之后我现在的默认方案是核心循环自己写只借用框架里的工具定义规范和部分工具实现。这样既保留了控制力又不用重复造轮子。对于复杂编排我会引入轻量的状态机库而不是全套 Agent 框架。这个组合的好处是出问题时我能精确知道每一行在干什么而不是在框架的黑盒里猜。如果你团队刚开始做 Agent我的建议是先用原生 API 跑通最小循环把七要素和七个决策点都亲手实现一遍再决定要不要上框架。这个学习成本是值得的因为它让你在后续选型和排错时有了判断力。7. 让 Agent 稳定跑在生产环境的几条硬规矩7.1 可观测性没有日志的 Agent 等于裸奔生产环境的 Agent 必须可观测。每一步的输入、输出、工具调用、参数、结果、耗时、token 消耗都要记下来。我习惯给每个会话一个 trace_id所有日志按 trace 串起来出问题能完整回放。没有这层你连“它为什么这么答”都说不清更别提优化了。日志之外还要有指标监控平均步数、工具调用成功率、参数校验失败率、循环终止率、单会话 token 消耗分布。这些指标能帮你在用户投诉之前发现问题。比如参数校验失败率突然上升往往意味着模型更新了或者工具描述改了需要及时排查。7.2 预算与降级给 Agent 装上“油表”和“备胎”预算控制是生产环境的生命线。每个会话、每个用户、每天都要有 token 和调用次数上限。超限时的降级策略也要提前设计是直接返回“当前繁忙请稍后再试”还是降级到无工具的普通对话还是转人工。我一般用分级降级轻度超限降级到小模型重度超限直接终止并提示。降级还要考虑工具不可用的情况。外部 API 挂了Agent 不能跟着挂。我的做法是给每个工具配一个降级返回比如查询失败时返回“暂时无法获取请稍后重试”让模型基于这个信息给用户一个体面的回复而不是抛异常。7.3 安全护栏输入、工具、输出三道闸安全护栏要覆盖三个位置。输入侧过滤明显恶意的请求比如试图让 Agent 执行危险操作的指令工具侧做权限校验敏感工具如写操作、支付要二次确认或人工审批输出侧校验是否包含不该出现的内容。这三道闸缺一不可。工具侧的权限尤其重要。读操作和写操作要分开写操作要有更严格的校验和确认。我见过 Agent 因为指令理解偏差把“查询订单”理解成“修改订单”的案例。护栏在这里的作用就是即使模型判断错了系统也不会真的执行危险操作。注意护栏的校验逻辑要独立于模型不能靠模型自己判断“这个操作安不安全”。模型是被约束的对象不是约束的执行者。8. 我对 Agent 工程实现的一点个人体会折腾 Agent 这段时间最大的体会是Agent 的难点从来不在模型而在工程。模型能力每年都在涨但工程上的坑——循环控制、状态管理、参数校验、预算控制、可观测性——这些不会因为模型变强就自动消失。反而模型越强能做的事越多工程上的约束就越重要。七要素和七个决策点这套框架我用了很久最大的价值是它让我在设计和排错时有了统一的语言。团队讨论时说“第三个决策点出问题了”比说“它好像调错工具了”精确得多。如果你也在做 Agent建议把这套框架内化成自己的思维习惯比记住任何框架的 API 都管用。最后分享一个小技巧每次 Agent 出问题别急着改 prompt先去看状态和日志定位是哪个决策点错了。大部分问题不在模型而在你给它的结构。结构对了模型自然就稳了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑