Aioga
AI资讯 / 技巧观点
返回 AI资讯

构建一个先进的代理式测试框架

Hacker News 热门(buzzing.cc 中文翻译)Aioga 编辑团队2026-08-05T23:51:32.802Z热度 25

文章介绍如何构建一个先进的代理式测试框架(Agentic Harness),用于系统化评估和测试AI智能体。框架设计聚焦于可重复、可扩展的测试流程,以验证智能体在复杂任务中的表...

技巧观点Hacker News 热门(buzzing.cc 中文翻译)

今日 AI 情报摘要

文章介绍如何构建一个先进的代理式测试框架(Agentic Harness),用于系统化评估和测试AI智能体。

框架设计聚焦于可重复、可扩展的测试流程,以验证智能体在复杂任务中的表现。 文中讨论了框架的核心架构、关键组件以及实际应用中的考量,为开发者提供了一套完整的测试解决方案参考。

中文正文 · AI 翻译

那个基础安全带循环是正确的,但太天真了。一个孤身驾驶坚固喷气机的飞行员或许能赢得空战,但没人会用这种方式进行空中战役。真实行动:https://amzn.to/4vqYj0o 新增任务规划员,负责决定起飞前执行哪些出击,中队独立执行独立出击,燃料预算和呼叫,迫使燃料耗尽前返回基地,飞行记录员让每个任务事后可重建,以及事后审查,决定任务是否成功。这些都不能取代试播集。他们将试点包裹在结构中,确保整个系统保持快速、安全、可调试和可衡量。

Claude Code、Devin、Cursor、Hermes 以及其他生产代理对基本循环做的事情完全相同。在这篇文章中,我们将将所有基本线束部件升级到那个生产形状,同时不隐藏任何机制背后。整个活动的核心问题很简单:

如何将一个单一的LLM调用变成一个可靠的系统,能够规划、行动、恢复并证明自己做了正确的事?

我们的答案是作曲。我们构建了小型、可测试的原语:类型工具、计划DAG:https://en.wikipedia.org/wiki/Directed_acyclic_graph、分层内存、验证层级、预算和追踪器,并用刻意设计的编排器将其连接起来。每个原元素的存在,是因为天真的智能体以特定且可预测的方式失败。LLM会发明无效的工具参数,所以我们用Pydantic添加类型工具:https://pydantic.dev/docs/validation/latest/get-started/ validation。所有程序都是顺序运行的,所以我们添加了依赖图和并行执行。上下文窗口会被垃圾填充,所以我们在有限的检索预算下添加了多层内存。坏输出会悄无声息地传播,所以我们会添加验证层级。一个提示试图完成所有事情,所以我们把它分成了规划者、工作者和批评者三个角色。成本飞涨,因此我们采用多维度预算并优雅地递减。

验证这种背带通常有效,包括评估套件、取样基准和专业工人池,将在后续文章中全面处理。

在整篇文章中,我们构建了一个城市比较代理:给定一个城市列表,它会生成一份报告,比较这些城市的人口、时区以及每个城市的简短叙述性总结。这个任务看起来几乎简单到令人发笑,但它是经过仔细挑选的。每个城市-属性查询都是独立的,这意味着一个三城市的请求自然会分解成九个可以同时运行的工具调用。另一方面,最终报告依赖于所有查询结果先完成,因此我们已经远远超出了一个简单步骤列表。我们可以通过程序检查,确保每个请求的城市确实出现在报告中,以验证结果。而且各工具的成本差异巨大:人口和时区查询是内存字典读取,而每个城市的摘要和最终聚合都调用了大语言模型,这给我们带来了现实的预算管理压力。

为了可重现性,查询工具从一个小型模拟字典 CITY_FACTS 中读取,因此该笔记本在没有网络访问的情况下也能完全复现。基于大语言模型的部分可以对真实的 Anthropic 模型或确定性模拟进行运行,这引出了第一个基本原语。

我们即将构建的每个组件最终都要调用大语言模型:计划器、摘要器、聚合器、评论者。如果这个调用被硬性绑定到一个 SDK,那么整个框架就无法测试,并且会被供应商锁定。

因此,在进行其他操作之前,我们定义了一个基类,对各种大语言模型调用 API 的细节提供抽象。

我们还实现了一个 MockProvider,用于测试和调试,其返回确定性的、角色感知的响应:在被要求制定计划时返回标准计划,在被要求总结时返回模板化的一行摘要,在被要求判断时返回基于规则的通过/失败判定。这使我们在开发过程中能够区分“我的编排错了吗?”与“模型计划得不好吗?”,这也是本文中每个实验能够在任何机器上复现的原因。

在基础框架中:https://data4sci.substack.com/p/building-a-basic-agentic-harness 我们手动验证了工具参数,这种方法迅速失效:每增加一个新工具就重复验证逻辑,LLM 从未看到正式的模式,只能猜测参数形状,产生的错误是模型无法自我纠正的临时字符串。

升级的方法是将每个工具的参数声明为 Pydantic 模型,并让一个定义驱动一切:

这种方法为我们提供了运行时验证、符合 Anthropic 和 OpenAI 工具使用 API 所期望形状的 JSON Schema、文档(每个 Field( …, description =… ) 成为计划器读取的目录的一部分)以及通过 cost_hint 进行成本核算的钩子。在执行前失败可以避免具有潜在副作用的昂贵工具调用。一个不好的计划应在验证层快速失败,而不是在数据库查询深处失败。这种方法类似于像 LangChain 工具、Anthropic 工具使用和 OpenAI 函数调用等成熟框架的做法。

我们的注册表包含四个工具,分为三个成本等级:get_population 和 get_timezone() 基本上是免费的字典查找(cost_hint =0.1),summarize_city() 每个城市调用一次 LLM(cost_hint =1.0),aggregate_report() 做一次产生最终 markdown 的大量 token 合成调用(cost_hint =2.0)。注意最后两个是内部调用 LLM 的工具。LLM 就像其他任何工具一样。工作器看到统一的工具接口,但有些工具是子提示的包装器,这意味着你可以独立于框架缓存、限速或替换内部模型。

基础框架每轮执行一个操作。当步骤严格顺序执行时这有效,但我们的任务有九个独立查找汇总成一个聚合:

构建一个先进的代理式测试框架

:https://substackcdn.com/image/fetch/$s_!bqrk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda72665-ee70-4a2e-9194-eed00992aaad_1057x778.png

while循环一次运行这些任务。一个有向无环图(DAG):https://en.wikipedia.org/wiki/Directed_acyclic_graph 明确表达了依赖关系,并允许执行器并发运行当前准备好的所有任务。所以我们不是一次向LLM请求一个操作,而是一次性向规划器请求整个图。LLM在执行任何操作之前声明结构。由于规划器本身是LLM,它也可能产生虚构结构:依赖不存在的节点ID,或者产生永远无法完成的循环依赖。因此,我们对计划进行的第一件事就是在尝试执行损坏的计划浪费令牌之前先验证它。

ready_nodes() 是调度器的核心:在任何时刻,它返回所有依赖都已满足的节点集合。对于我们的三城市目标,规划器输出十个节点:九个依赖列表为空的 fetch 节点,全部可以并行运行,还有一个依赖所有九个节点的 aggregate_report capstone 节点。

执行器是一个层次同步的 DAG 遍历器:计算 ready 集合,使用 asyncio.gather 并发启动每个就绪节点,标记每个节点为完成或失败,然后重复直到没有剩余节点或无法进一步前进。

两项小决策在这里起到最大作用。首先,asyncio.to_thread 在线程池中运行我们的同步工具函数,这意味着我们不必将工具重写为 async def,也不必将加载器与异步原生 SDK 耦合。其次,信号量限制并发,因为没有它,一个五十节点的计划会同时生成五十次 LLM 调用,并立刻触发速率限制或成本激增。这显然不是完整的动态调度器,没有工作窃取和优先队列。对于像我们这样的代理工作负载,每个节点都是持续数百毫秒到几秒的 API 调用,层次同步并行可以获得大部分收益。顺序执行时,实际耗时大约是 fetch 延迟的总和;并行执行时,实际耗时大约是最大延迟加上聚合步骤。

天真的代理会把所有内容都扔进提示中:完整的聊天记录、每个工具输出、每个先前的任务。这种做法失败的原因有两个——你会为永远不会使用的令牌付费,而且当无关文本稀释目标时,模型的性能会明显下降。生产环境中的代理则使用分层记忆,灵感部分来自认知科学。工作记忆是始终在上下文中的便笺:当前目标、计划摘要以及最近的几个结果。情节记忆存储过去任务的结果,当过去的任务与当前任务相似时会被调用。语义记忆保存背景事实,也通过相同方式调用,但不与任何特定运行绑定。

我们从不注入所有内容;我们会根据与当前目标的相似度提取 top-k 记忆,然后在严格的字符预算下组装上下文:

情节记忆优先于语义记忆,因为在类似任务上的过去错误通常比通用事实更具可操作性,并且当预算耗尽时,截断是明确的而非默默进行。上下文应主动组装,而不是被动累积。

对于相似度函数本身,存储支持两种后端。Jaccard 相似度:https://en.wikipedia.org/wiki/Jaccard_index 不产生成本,非常适合教学,但在处理同义改写时会失败:“法国著名地标”几乎没有与“巴黎以埃菲尔铁塔闻名”共享单词。通过 all-MiniLM-L6-v2:https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2 生成的 384 维向量的真实句子嵌入可以将同义改写映射到邻近向量。我们的 MemoryStore 首先尝试使用嵌入,如果模型不可用,则备用使用 Jaccard。量化此升级效果需要一个合适的基准测试,我们将在未来的文章中进行。

代理会生成流畅、自信但错误的输出。如果不进行验证,默默丢掉一个城市的报告会直接发送给用户,并且回归问题会一直被忽略,直到有人偶然查看输出为止。但并非所有检查的成本都相同,因此我们将它们排列成层级:确定性的结构检查基本免费,而用于主观质量的 LLM 判定则需要真实令牌。规则是始终先运行低成本层,只有通过此层的内容才会升级到下一层。

给它一个故意不完整的报告(比如只给巴黎,而实际上要求的是三个城市),它将在确定性层面失败,原因是“缺失城市:[‘东京’, ‘纽约’]”。在判断上没有消耗任何令牌,并且原因字符串足够可操作,一个重规划步骤(或人工)可以确切看到出了什么问题。这种两层门控是大多数生产评估管道背后的稳健模式:先进行低成本过滤,然后只对幸存者执行高成本评判。和层级结构一样重要的,是背后的关注点分离:Worker(执行者)生成,Critic(评审者)评价,因此生成器永远不会评自己的作业。

一个同时计划、执行、总结并自我批评的单一提示往往会使目标混淆(计划约束会影响写作风格),且无法隔离“规划部分”,这会使测试和替换更加复杂。

我们将工作拆分成狭窄的代理,每个代理都有简短的系统提示和单一的合同。Planner(规划者)接收目标和工具模式并返回DAG JSON,我们在运行前对其进行验证。Worker(执行者)接收DAG并简单执行。Critic(评审者)接收目标和完成的报告并返回判决。Planner的系统提示中直接嵌入了实时工具目录,因此它只能引用实际存在的工具:

在真正的模型支持下,规划器返回的计划在结构上有效但风格多样,并且在提示中锁定协调者以后需要找到的单个节点,比在后处理代码中完成要便宜得多。

构建一个先进的代理式测试框架

:https://substackcdn.com/image/fetch/$s_!5A9N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b4fb9de-22ea-419c-a112-1d77ffbb4611_1131x1711.png

这与生产系统如AutoGPT风格的规划器、SWE-agent执行者或LLM作为评判者的评估做法相似,同时仍保持精简到可一口气阅读的程度。而且因为所有三个角色都通过相同的LLMProvider.complete( …, role =… )接口,所以更换规划器模型或模拟评审者只需一行代码即可完成。

基本的 harness 只有一个 max_steps 计数器,这掩盖了真实的约束条件:你可能还有剩余步骤但没有剩余的 tokens,或者在 token 预算范围内但工具调用受到速率限制,或者网络挂起调用消耗了实际时间却完全不增加任何计数器。BudgetMulti 同时跟踪 tokens、工具调用、墙钟时间和预估费用,而运行会在任一维度耗尽时停止。它最有用的输出是一个标量:

Pressure 是所有维度中最大利用率。你会受到首先耗尽的资源限制,就像真实计费一样。这个数字驱动了优雅降级:低于 0.7 时,全流程运行,包括 LLM 评判;高于 0.9 时,调度程序会跳过昂贵的评论程序,仅回落到确定性检查;达到 1.0 时,运行会在部分结果下停止。生产环境的代理使用相同的信号来切换到更便宜的模型,减少检索深度,或向用户请求确认。

操作理智的另一半是认识到不是每个错误都值得同样处理。速率限制或超时是短暂的,可采用指数回退(带抖动,这样一组代理不会同步重试)并重试。验证错误是工具误用,我们将结构化错误反馈给 LLM,使其可以纠正自己的论证。未知实体是信息缺失,所以重试实际上有害,因为重试一个捏造的城市名字每次都会失败;正确做法是重新规划而不使用该信息。策略违规是致命的,我们应立即停止。一个小型 classify_error() 函数将错误字符串映射到这四类中,恢复策略依据类别而非盲目重试。

当一个代理失败时,我们需要一个仅追加的结构化事件日志,它可以回答事情发生的顺序、每个步骤花费的时间、哪个角色消耗了令牌,以及在事情出错之前预算压力是否在上升。我们的 Tracer 中的每个事件都捕捉了身份信息(步骤 ID 和一个将工作步骤链接回生成它们的计划的父 ID)、语义信息(角色和所采取的动作)、经济信息(延迟、令牌、成本以及写入时的预算压力快照),对于评论事件还包括判决。模式是平的且简单无趣:一列可以序列化为 JSON 的字典,你可以将它们写入文件、发送到 OpenTelemetry 或 LangSmith,或者直接用 matplotlib 绘制。你不需要专有格式就能获得真实的可观测性,只需要足够的模式。

按步骤延迟,并按角色着色,能立刻显示 summarize_city() 和 aggregate_report() 节点占据了大多数墙钟时间,而查找操作延迟平缓。LLM 调用才是消耗秒数的地方,因此并行运行它们非常重要。

构建一个先进的代理式测试框架

按角色的每步延迟

随着时间的推移,预算压力单调上升,在聚合器处急剧跳升,如果它在评论执行前超过 0.9 的降级阈值,追踪日志本身就能解释为什么 LLM 审判被跳过。

构建一个先进的代理式测试框架

:https://substackcdn.com/image/fetch/$s_!7_R8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff1bb4ea-8569-4e51-a81d-fc411115565f_3684x2484.png

最后,按角色分配的令牌显示计划或执行是否在消耗预算。在这项任务中,它严重倾向于工作者,因为其调用了许多 summarize。

构建一个先进的代理式测试框架

:https://substackcdn.com/image/fetch/$s_!5U5q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ef2844d-5bb2-4053-9dc7-9b1c2cbc2a6f_3684x2484.png 按角色的令牌使用情况。工作者在此任务中占主导地位。

有了这些原语之后,协调器(Orchestrator)几乎变得无聊。它从记忆中构建上下文,向规划器(Planner)请求一个有向无环图(DAG),将 DAG 交给工作器(Worker)并附上一个在每次工具调用时记录跟踪事件并计入预算的回调,检查失败情况,使用压力感知降级验证结果,将结果存储在情节记忆中以备将来运行,并返回一个 RunResult,捆绑报告、裁决、DAG、跟踪和预算。它唯一新增的行为是重新规划循环。如果执行失败且错误被分类为信息缺失,它会将失败上下文发送回规划器,而不是盲目重试,最多可配置为 max_replans。

该方法的核心可以用几行代码概括:

模拟规划器(mock planner)总是将顶点节点命名为 aggregate,而协调器的早期版本只是通过该 id 查找它。切换到真实模型后,这个假设悄然失效。规划器经常镜像工具名称 aggregate_report 或发明自己的 id,生成一个结构上有效但不是模拟训练时我们预期的计划。解决方法有两个方面:DAG 现在通过工具而非 id 解析顶点节点 dag.aggregate_node() 返回唯一的 aggregate_report 节点,(并拒绝包含多个节点的计划),规划器提示也增加了我们之前看到的明确顶点指令。永远不要完全相信 LLM,包括节点名称。

在基础框架(Basic Harness:https://data4sci.substack.com/p/building-a-basic-agentic-harness)与这个高级框架之间,我们几乎涵盖了构建一个成功自定义框架所需的所有主要概念和思想。我们从前一篇文章中的约 35 行循环出发,并叠加了七个生产级原语:根据一种模式提供验证和 LLM 内省的类型化工具(typed tools)、无需重写任何工具即可实现并行的 DAG 执行器、在严格预算下组装相关上下文的分层记忆、只在通过廉价检查的输出上花费 token 的验证层次结构、可以独立测试和替换的狭窄规划器/工作器/批评者角色、多维预算可以优雅降级而非崩溃,以及将运行转化为可比较实验而非轶事的跟踪器。

这些组件彼此不依赖,但可以组合在一起。向注册表添加一个工具,规划器会自动看到它的模式。收紧 verify_report() 后,每一次未来的运行都必须达到新的标准。更换整个 LLM 后端,测试平台会在不改变的情况下重新运行。可组合性是概念验证与可扩展测试平台的区别所在,也是编排器保持轻量的原因。

有几个注意事项:这里的内存是在进程内,而生产系统会将嵌入持久化到 Chroma、Weaviate 或 pgvector;工具输出被信任为指令,而生产系统必须将其作为数据进行沙箱处理以防提示注入;不可逆操作应需要人工批准;我们的令牌计算是根据字符数估算的,而实际系统从 SDK 读取使用元数据。每一项都是进一步的一层可组合性——这正是重点所在。

还有一个刻意省略的内容。一次成功的演示证明测试平台可以工作;本文没有证明它在大多数情况下确实可行。这是评估测试平台的工作,也是我们将在未来的后续文章中展开的内容。

情报判断

Aioga 编辑摘要

Aioga 编辑摘要:文章介绍如何构建一个先进的代理式测试框架(Agentic Harness),用于系统化评估和测试AI智能体。 Aioga 将其归入「技巧观点」方向,重点关注它对真实使用和行业竞争的影响。

背景分析

背景分析:实践类内容的价值在于是否能被复现、是否有明确边界,以及它能否转化为稳定的开发或工作流方法。

Aioga 观点

Aioga 判断:这条动态更适合作为行业观察信号,当前信息足以建立线索,但不足以推导长期结论。

影响与后续

影响分析:对相关团队而言,短期应先核对来源、可用范围和实际成本,再判断是否值得接入或跟进。 后续观察:继续观察示例是否可复现、工具版本变化、社区反馈和实际成本。

来源与版权说明

本页正文由公开来源页面提取并按原有信息整理,同时保留来源、发布时间和原文入口。版权归原作者及来源网站所有,请通过原文链接核验和阅读来源版本。

抓取通道: 摘要聚合 · 原始域名: data4sci.com

来源: Hacker News 热门(buzzing.cc 中文翻译)

原文链接: 打开原始来源

Aioga 归档: 查看情报页

Content record: source-page · Updated: 2026-08-05T23:51:32.802Z

API 中转站
API RELAY · DEVELOPER INFRASTRUCTURE

API 中转站

统一接入主流 AI 模型 API,为开发、测试与生产环境提供稳定调用入口。

立即访问 api.w173.com
AIOGA SHARE POSTER

分享这篇 AI 情报

文章介绍如何构建一个先进的代理式测试框架(Agentic Harness),用于系统化评估和测试AI智能体。框架设计聚焦于可重复、可扩展的测试流程,以验证智能体在复杂任务中的表...

Hacker News 热门(buzzing.cc 中文翻译)2026-08-05T23:51:32.802Z
扫码打开文章详情扫码直达文章详情

Aioga 自动聚合全球 AI 动态,并保留来源信息用于核验与引用。