资讯动态

从自动化到能动性:AI Agent系统中“agency”比“agents”更关键

发布时间:2026/10/26 11:49:41 来源:尧图企业网站定制
最近整理代码仓库的时候反复看到一个叫“agency-agents”的目录名。刚开始没太在意直到有次跟某位做Agent框架的老朋友聊起多智能体系统设计才意识到这个命名其实点破了一个很关键的问题市面上的Agent项目大多在强调“agents”这个复数形态但决定系统上限的往往是“agency”——也就是每个代理的自主行动能力。今天这篇文章我想从项目标题的本意出发结合我实际搭建和维护这类系统的经验把代理设计、编排、落地中真正重要的东西拆开讲透。如果你正准备入手AI Agent开发或者已经在做但经常遇到“代理跑飞”“循环卡死”“多代理结果丢失”这类问题这篇文章应该能帮你省下不少弯路。1. 项目核心思路为什么“agency”比“agents”更值得关注1.1 从自动化到能动性的认知转变我们通常聊到的Agent绝大多数还停留在“自动化”的层面给定输入走固定流程调用固定工具输出结果。这种模式本质上是把原来写在代码里的业务逻辑换成了一个LLM来驱动流程的可变性和适应性提升了但系统本身并不具备“判断接下来该干什么”的能力。我理解“agency”这个词在Agent系统里的含义是代理在不确定环境中根据目标自主做出决策的能力。它意味着代理不仅会执行还会选择执行什么、何时执行、如何纠偏。举个生活化的类比电梯是自动化系统按了楼层就运行路线完全可预期但电梯操作员拥有的就是agency他会根据进出的人流判断该停哪层、该等多久、要不要提前关门。这两个层次之间隔着一种“主动推理”的能力。押注“agency-agents”这个方向的团队本质上是在做一个判断未来的Agent系统不只是执行单元的组合而是一个由多名具备能动性的成员组成的协作网络。每个成员都有目标、有工具、有决策逻辑并在必要时把问题拆解、分发、汇总。这个理念对系统设计的要求完全不同——你需要考虑的不再是“调用链”而是“决策循环”。1.2 项目要解决的核心痛点我在实际使用和调研项目时发现单纯把所有参数和工具塞给一个LLM让它在一个长上下文里硬推理会出现几个高频问题首先代理会在复杂任务里“迷失”。任务一多上下文一长模型就容易遗忘最初的用户目标开始自己编造子目标越跑越偏。其次代理会陷入无效循环。一次工具调用失败后代理不去分析失败原因反而换个参数反复重试直到步数耗尽。再次多代理协作时信息传递经常出现断层。子代理返回的结果被父代理漏掉或父代理只记住了摘要却丢了细节导致最终答案不完整甚至错误。agency-agents这类项目的思路就是尝试解决这些根本性问题。它不追求把每个环节做得更花哨而是通过合理的代理自主性边界、清晰的状态传递机制以及严密的执行循环控制让系统在复杂任务面前保持着稳定性和可控性。这非常接近我自己的工程观先求稳再求能。2. 核心架构设计与决策拆解2.1 代理执行循环的设计感知-规划-行动-反思一个具备现实可用性的代理在内部跑的往往不是一个简单的“用户提问-模型回答”过程而是一个循环。这个循环通常包含四个阶段感知Perceive、规划Plan、行动Act、反思Reflect。感知环节负责收集当前任务相关的所有信息包括用户的原始意图、历史对话、工具返回结果和系统状态。规划环节由LLM根据感知到的信息生成下一步动作计划。这里不同的项目会选择不同粒度的规划方式有的偏向“一次生成整个任务计划”有的则偏向“每次只决定下一步动作”。行动环节根据规划执行具体操作常见的操作是调用工具或向其他代理发送消息。反思环节在执行完行动后触发LLM对本次结果做一次自检判断是否已达成目标是否需要调整计划。这个循环的最大价值是让代理的行为具备了闭环调整的能力。没有反思环节代理规划错了就一路错下去没有感知环节代理依赖的上下文信息就会缺失。2.2 工具注册与调用接口的取舍实现一个Agent系统最先遇到的工程问题就是“如何让模型调用工具”。业界常见的做法是给模型提供一份工具清单每个工具对应一个JSON Schema描述模型根据输入决定是否调用某个工具以及传什么参数。设计这个接口时有几个细节值得反复打磨工具描述需要足够精确。模型是靠描述来理解工具用途的写得模糊就会导致选错工具。比如一个函数叫search_files描述写成“搜索文件”就太笼统了至少要明确是“按文件名关键字在项目内递归搜索返回文件路径列表”。参数校验必须前置。模型生成的参数并不是百分百合法。工具在执行前先跑一遍Pydantic校验不合法的输入直接返回结构化错误让代理自己去纠正要比在工具内部抛异常更可控。工具调用需要有超时和幂等设计。长耗时工具要设置超时上限重试型工具比如网络请求类要考虑幂等性防止重复调用产生副作用。2.3 主从代理协作的消息传递模式单代理能解决的问题域有限一旦任务复杂度超过单代理的处理范围就需要采用多代理协作。多代理协作里最常见也最稳的是主从模式一个主代理负责拆解任务、分发给子代理再收集各子代理结果。这个模式里最关键的是消息传递协议。我在实践中踩过最痛的坑是父代理把任务发给子代理之后子代理返回了详细的长文本结果父代理因为上下文窗口限制只保留了一个摘要丢弃了原始内容。看似省了Token但后续总结阶段需要细节时信息已经丢失了。更稳的作法是子代理执行完成后父代理接到的不仅是摘要还有指向原始结果的关键引用比如结构化文件路径或数据索引。当父代理需要细节时可以按引用去取回原文。这种“引用不搬移”的传递方式在信息完整性和token消耗之间找到了平衡。3. 实操从零实现一个“agency-agents”风格的最小系统3.1 环境准备与工程基础在动手前先明确一下技术栈选型。我用的是Python 3.11版本依赖管理走PoetryLLM调用部分采用一个统一的接口封装层这样无论底层模型怎么换上层逻辑不用改。封装层我选用的是OpenAI兼容的客户端协议因为目前主流的模型服务商都支持这个协议切换成本最低。依赖库里需要关注的有用于参数校验的Pydantic、用于结构化输出的JsonSchema工具链、以及一个支持并发调用的HTTP客户端。异步并发在真实项目中几乎是刚需因为代理之间往往是并行执行子任务的。# pyproject.toml 关键依赖 typing_extensions 4.8 pydantic 2.5 httpx 0.25 dotenv 1.03.2 核心类设计Agent、Tool、Memory一个最小可运行的Agent系统我通常会把它拆成三个核心类Agent代理主体、Tool工具抽象、Memory记忆容器。Agent类是核心它拥有名称、角色描述、可用工具列表、记忆存储和一次可执行的最大步数。它的主要职责是跑完整个感知-规划-行动-反思的循环。class Agent: def __init__(self, name, role, tools, memory, max_steps10): self.name name self.role role self.tools {tool.name: tool for tool in tools} self.memory memory self.max_steps max_steps async def run(self, task): self.memory.add(user, task) for step in range(self.max_steps): # 感知 context self.memory.get_recent() # 规划行动 action await self._decide_next_action(context) if action[type] finish: return action[output] # 执行 result await self._execute_action(action) # 反思 self.memory.add(assistant, f执行结果: {result})Tool类是代理与外部世界交互的桥梁。在最小系统里一个工具就是一个函数和处理它元数据的容器。元数据里最重要的是声明输入输出的样子这决定了大模型是否能正确理解并调用它。class Tool: def __init__(self, name, description, input_schema, handler): self.name name self.description description self.input_schema input_schema self.handler handler async def execute(self, **kwargs): # 前置校验 validated validate_input(kwargs, self.input_schema) if not validated.ok: return {error: validated.errors} # 正式执行 try: return {result: await self.handler(**validated.data)} except Exception as e: return {error: str(e)}Memory类的设计要兼顾容量和引用。我用的方案是双缓冲一块是结构化历史记录保留最近N轮对话摘要另一块是完整过程记录存放到一个按会话ID组织的存储可以是本地磁盘或向量库。需要细节时再按ID拉取。3.3 执行循环中的关键控制逻辑执行循环里最容易被忽视但又最要命的是循环检测和早停机制。没有这个机制代理会在某个错误结果上反复打转消耗大量token和时间。我的实现里加入了两个控制项重复动作检测和反思偏差阈值。重复动作检测是记录每一步动作的工具名参数哈希如果同一动作出现多次且中间穿插的动作没有改变上下文直接判定为循环触发重规划或终止。反思偏差阈值则是在反思阶段让模型判断“当前结果与目标的偏差方向”如果连续两次反思都是“接近目标但需要重试”但实际结果指标并没有变化也主动终止。# 循环检测示例简化 action_history [] def is_repeating(action): signature f{action[tool]}:{hash_params(action[args])} recent action_history[-3:] same_actions recent.count(signature) return same_actions 2这个看似粗糙的检测逻辑实际运行中拦截了大量的无效循环比单纯依赖大模型“认识错误”可靠得多。3.4 一个完整可运行的最小示例为了让你快速理解整体流程我给你写一个真实的简化场景一个资料调研代理用户要求它整理某技术主题的近期关键进展。这个代理配备了三个工具search_web(keywords)模拟搜索接口返回按时间倒序的标题列表fetch_page(url)模拟抓取指定页面正文返回摘要save_note(note)把内容保存到本地笔记文件主流程代理先搜索关键词然后根据搜索结果的标题判断哪几条值得抓取详情抓取后提取关键信息最后整理成一份结构化报告。from dotenv import load_dotenv import asyncio, json load_dotenv() # 工具定义 async def search_web(keywords): # 这里实际会调用搜索API的具体实现逻辑 return json.dumps([ {title: 标题A, url: https://example.com/a, date: 2025-02-01}, {title: 标题B, url: https://example.com/b, date: 2025-01-20}, ]) # 初始化代理 research_agent Agent( nameresearcher, role你是一名IT技术调研助理负责收集和总结指定主题的最新进展。, tools[ Tool(namesearch_web, description按关键词搜索网页返回标题列表, input_schema{type:object,properties:{keywords:{type:string}}}, handlersearch_web), # ... 其他工具 ], memoryMemory() ) # 启动 report asyncio.run(research_agent.run(帮我调研AI智能体可观测性工具的最新发展)) print(report)跑这样一个最小系统你很快就能体会到“agentic”和“脚本”的本质区别脚本的每一步都是写死的而代理会根据搜索结果的实际内容自主决定接下来抓取哪一页、保留哪些信息、忽略哪些噪音。4. 常见故障与排查实录这一节是纯实战内容。我把自己和团队成员在开发和试运行中反复踩过的坑梳理了一遍挑了几个出现频率最高、影响最大的问题附上排查方法和解决方案。4.1 代理陷入重复循环现象代理反复调用同一个工具、传递几乎相同的参数有时换一个相近参数又重试直到步数耗尽。排查思路第一步查看执行日志里的工具调用序列确认循环的周期长度和触发条件。第二步检查循环动作前后的memory变化如果memory里没有增加新的关键信息说明模型没有从结果里获得有效反馈。第三步检查工具返回的错误信息是不是过于笼统比如只返回“查询失败”而没有具体原因模型无法据此做出调整。解决方案我试过两种。一种是在系统prompt里强调“当某个行动没有产生新信息时应当停止并报告障碍”这个对提高模型识别力有帮助但不够彻底。另一种是工程化兜底也就是前面说的重复动作检测一旦判定循环强制触发一次“反思-重规划”动作让模型写一段“为什么我在原地打转”和“下一步怎么调整”。两种结合后循环问题基本控制在极低概率。4.2 工具返回了错误但代理没有感知现象工具内部出异常代理却返回了一个看起来正常、但实际内容空洞的结果。有的模型在拿到空结果时会“补全”一个虚构的答案这最坑人。排查思路查看工具返回体的结构。如果工具返回是一个字符串异常信息和正常结果混在一起模型就很容易混淆。另一个高发场景是工具返回了极大的JSON但模型在总结时把关键字段丢了。解决方案所有工具的返回都强制使用统一的封装结构区分status字段success/error、data字段和error_message字段。代理在复盘阶段只关注data字段遇到error字段则单独处理。同时在prompt里明确要求当最近一次动作返回error时必须解释错误原因并提出改进方案不得直接生成最终答案。我曾经见过一个极端案例网络工具因为超时返回了一个空列表代理把空列表解读成“没有相关内容”然后编造了一段看起来很有条理但实际上完全虚构的资料。统一返回结构之后这类问题几乎绝迹了。4.3 编排上下文爆掉或者信息衰减现象任务执行到一半上下文窗口被塞满系统强制截断或者截断后代理忘记早期的关键要求回答越来越偏。排查思路检查每次动作产生的token消耗。搜索、抓取页面这类工具返回的原始内容非常大如果全部塞进memory里很快就能把一个128K的上下文塞满。解决方案插入一个压缩节点。当memory容量达到阈值时触发一次摘要压缩将早期对话总结成一段带有指向原文引用的摘要保留必要细节的同时释放空间。这里的“指向原文引用”就是前面提到的引用传递方式——摘要记录里保留类似“原始结果位于run_123/file_007.md”这样的指引后续需要细节时可以按需加载而不是把全文留在上下文中。这一步优化后我的代理系统从只能稳定处理10-15步的小任务提升到可以稳定处理60-80步的中型调研任务。4.4 多代理协作时结果丢失或错乱现象主代理派发多个子代理并行执行任务返回结果时要么某个子代理的结果迟迟等不到要么子代理A的结果最终被记在子代理B的名下最后汇总答案张冠李戴。排查思路查看消息队列的时序和每个子代理的完成回调。大多数情况下问题出在父代理等待结果的方式——如果父代理使用一个简单的事件循环去收集结果而没有做结果归属校验很容易出现错乱。解决方案为每个子任务分配一个唯一任务ID子代理完成时直接回传任务ID和结果封装体。父代理只在任务ID匹配的情况下登记完成状态并等待全部任务完成后再进入汇总阶段。同时给每个子代理设置独立超时超过预期时间未返回的就标记为失败由父代理决定是补发还是豁免。这个机制看起来朴素但在生产环境里非常可靠。我见过有些开源项目为了省事省略了这个ID绑定最终在真实流量下暴露了严重的并发错乱。5. 上线后的经验与调优建议5.21 Token成本怎么控制Agent系统跑起来token消耗大得让人肉疼。我在一个中等规模的任务里做过统计一次完整的任务30步左右大约消耗掉的输入token是输出token的好几倍大头几乎全在把历史记录和回溯信息反复塞给模型。控制成本有以下手段一是记忆窗口限制模型每次决策只拿最近若干轮的关键上下文而不是全部历史二是工具结果截断抓取的网页正文最前面做显著摘要模型需要完整内容时再单独获取三是模型分级全局规划用高能力模型局部工具调用和文本整理可以用低能力模型成本能大幅下降性能损失却不大。还有一个小技巧是延迟工具调用。有的代理系统习惯在每步都调用一次工具确认状态但绝大多数任务的中间状态不需要实时感知。我后来把“主动检查状态”这类工具的调用改为“按需触发”即在反思阶段发现信息不足时才调用节省了接近一半的工具调用量。5.2 代理可观察性的设计实践调试一个多步代理最难的不是看代码逻辑而是观察模型内部在做什么、为什么这么决策。没有可观察性的代理系统基本没法维护。我给每个代理运行的每个步骤都加了事件日志内容包括当前步骤号、感知到的上下文片段摘要、模型规划出的动作类型和参数、执行结果状态、反思结论文本。日志统一写入一个本地跟踪文件格式尽量用结构化JSON方便后续分析。调试时最常用的操作是“重放”。把日志里记录的每一步感知上下文回放给模型就能复现它在拿到某条信息时的所有决策过程。这个能力在排查“代理为什么会判断错误方向”时尤其有效。5.3 从单代理到多代理的演进路线不少项目一开始是单代理架构随着任务复杂才逐步演进到多代理。这个演进过程要非常克制不要一开始就设计几十个代理否则系统复杂度和维护成本会迅速失控。我的建议是从“主从分工”这种最简单模式开始一个主代理负责理解需求下面一两个专业子代理负责具体环节。确认这种模式跑通后再根据真实任务流量里的瓶颈决定是否增加新的子代理类型或引入并行机制。每一步演进都保留回退点确保新结构出现问题时可以快速恢复到上一版本。我在多个项目里坚持这条路线结论是最好的多代理架构不是“设计”出来的而是“生长”出来的。先让单一代理产生价值再根据瓶颈一点点扩展系统整体会稳定得多。最后再分享一个经验又回到“agency-agents”这个标题。这段时间反复琢磨下来我愈发觉得项目起名的用意很深。构建Agent系统时把精力过多放在“如何加入更多agents”往往是误区真正值得投入的是打磨每个代理的agency——它的决策能力、纠偏能力和边界内自持能力。一个拥有高 agency 的单代理远比十个只会机械执行的低 agency 代理更能够在真实任务里创造价值。如果你也在做类似的项目我的建议是先花两周时间把执行循环、工具返回结构、循环检测这三个基础点打磨扎实不要急着追新框架。基础打的足够牢后续无论代理数量怎么变系统都能稳稳地跑住。这是我的切身体会。

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

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

免费获取报价 →
↑