一般

高效商业智能体剖析指南

10
分类技术博客
作者Anthropic
来源跳转
发表时间

内容

在过去一年里,我们与商业领域的各类团队合作,包括零售商、交易平台、旅游、娱乐和电信提供商,使用 Claude 构建商业智能体。

这些代理已经投入生产,企业客户在使用它们时看到了更大的购物车和更高效的卖家运营。它们也共享一种简单的架构:在一个带有一组技能、工具和强大 eval 套件的 agent loop 中运行的 Claude。

这篇文章面向正在构建这类代理(或其他面向消费者的代理)的工程师和工程负责人。第一部分介绍架构,这部分只需决定一次。第二部分介绍延迟和成本。第三部分介绍生产环境:记忆、安全、评测,以及如何在组织内扩展这项工作。

01 架构

一个标准 agent loop 中的单一模型,配合用于长尾场景的 skills,以及调用你已在运行的系统的 tools。这部分只需决定一次。

什么是商业智能体?

我们将商业智能体定义为一种简化在线目录中买卖流程的代理。

有些代理面向消费者:它们进行搜索、比较、替代,并组装订单。可能是零售购物车、旅行行程、手机套餐变更,或为演出保留座位。有些代理面向业务:它们回答销售相关问题、执行促销和营销活动,并管理库存和定价。

上面的两个面板运行的是同一个代理,使用相同的工具和提示词;不同的只是 harness。总耗时大致相同,但用户看到结果的时间差别很大。

核心架构是在一个标准 agent loop中的模型:围绕目标进行推理,探索上下文,通过工具采取行动,通过 skills 学习流程,提出澄清性问题,并观察结果,直到目标达成。

前面没有一个意图路由器来分流对话,后面也没有一组领域专用代理。

工程上下文

Skills,而不是 subagents

商业智能体 需要覆盖多个类别和意图下的广泛能力,这很容易让人想为每个领域都建立一个 subagent。

但在实践中,这并不是最优解,因为一次 commerce 对话本质上是跨多个意图和轮次的紧密耦合会话,而且需要大量共享上下文。

在 subagent 架构中,orchestrator 持有购物车或已暂存的变更、用户偏好以及对话历史。

每次交接给 subagent 都是一次有状态损失的操作,这通常会影响 subagent 响应的质量,进而影响整体响应。除此之外,每次交接还可能消耗数倍 token,并增加数秒延迟。

这些领域也很少能清晰分开。一个退货流程可能需要订单历史、当前购物车和产品目录,这意味着按领域划分 subagent 的方案要么在各处重复这些访问,要么在任务中途进行交接。

随着模型越来越聪明,它们也能处理更长上下文、更多 skills 和更多 tools,因此如今放置规则背后的限制会随着每一代模型而放松。

相较之下,agent skills 能在不付出交接代价的情况下,提供类似的按领域模块化与上下文控制,因为 skill 指令会加载到已经持有完整历史记录的主代理中。

在我们对多个企业部署的对比中,带 skills 的单一代理在质量上始终优于“一个提示词包打天下”的设计和 subagent 设计,而且通常每个任务的成本和延迟也更低。

subagent 真正适合出现的场景,是 orchestrator 可以把它当作一个 tool,用于狭窄或自包含的任务,而且该任务会受益于自己独立的上下文窗口。

一个常见的生产示例是深度研究 subagent:它搜索和阅读文档、编写并运行代码、遍历数据模型,也会走进死胡同。所有工作都发生在一个或多个 subagent 内部,最后只把一个紧凑答案返回给 orchestrator。

另一个例外是那些本来就已经有专门构建的 agent 的领域。如果你的药房或金融服务体验运行的是一个拥有自己合规边界的专用代理,正确做法可能是交接,即让那个代理接管任务,并通过它自己的 loop 直接与用户协作,直到任务完成。

区别在于对话的所有权。交接会让领域代理成为用户的对话对象,而委派则保留 orchestrator,让领域代理在单轮内进出,且每次交换都会让效果变差。

System prompt 还是 skill:按频率决定

决定一组指令应放入 system prompt 还是 skill 的主要因素,是代理需要它的频率。加载一个 skill 会消耗一个模型轮次,所以代理在大多数轮次都需要的内容,通常应放进 system prompt。

不过,这也取决于你的流量分布,以及 eval 显示出的代理行为。一个好的起点是:凡是与你至少三分之一流量相关的内容,无论是发布前预期的还是生产中观察到的,都放进 system prompt,其余则放进 skills。

如果某个 skill 可以由你已经拥有的信号推断出来,比如用户来自哪个页面,我们建议在第一次模型调用前由 harness 注入它,跳过额外一轮去加载该 skill。

关键指令,例如安全与法律规则、品牌约束,以及过敏史这类重要用户信息,始终放进 system prompt。

对于 商业智能体 来说,这意味着产品搜索应该放在 prompt 中,因为几乎每个会话都会涉及它,而 skills 负责覆盖长尾功能。

在我们的参考实现中,购物代理的 prompt 包含 grounding、购物车和结账语义,以及展示规则;其余部分由以下 skills 覆盖:search-discovery、purchase-research、planning-goals、customer-care 和 memory-personalization。

商家代理也按同样方式拆分,skills 分别是 performance-insights、catalog-listings、inventory-operations、pricing-promotions 和 marketing-campaigns,每个对应一个运营领域。

在 prompt 中 Shopping agent grounding、购物车和结账语义、展示规则,以及产品搜索。

Shopping skills 长尾场景 search-discovery · purchase-research · planning-goals · customer-care · memory-personalization

Merchant skills 每个运营领域一个 performance-insights · catalog-listings · inventory-operations · pricing-promotions · marketing-campaigns

工程化代理工具

我们关于为代理编写高效工具的文章已经概述了工具设计的一般原则。在 commerce 场景里,最重要的有两点:

在你的核心系统和逻辑之上构建代理工具。

一家 commerce 公司本来就已经有搜索与排序、购物车、偏好和画像存储、库存系统、促销和活动引擎、销售分析等系统;这些系统都编码了经过多年打磨的逻辑,承载着模型永远不会自行获得的信号。

代理的 tools 应该调用这些系统,而不是重新实现它们;工具边界就是这些系统逻辑结束、模型判断开始的地方。

例如,当代理调用 search_products 时,结果应当已经完成排序;代理的职责是决定哪些结果最能服务用户目标、展示多少,以及如何呈现。

工具结果就是上下文。

只返回模型推理所需的字段,删掉其余内容。每一行搜索结果里都带图片 URL,通常就是最常见的冗余来源。

需要时,在工具内部重塑原始响应,包括在数据里看不出来时补上下一步。

这在错误场景中尤其重要,因为模型更受指令而不是错误码的帮助。例如,别只返回一个通用的 403,而是添加一条错误指令:“查询库存时请包含 product ID。”

UI 组件就是工具

大多数 商业智能体 的响应不是散文,而是 UI 组件,比如产品轮播、行程表、座位图或图表。这意味着代理需要输出 schema,而不是纯文本。

一些团队会先通过提示模型输出自定义标签,再在客户端解析。这种方法在界面不断扩展时会失效,因为:

  • 模型对你的标记语言的训练程度不如它对 tool calls 的训练程度高,所以一旦加入嵌套组件,可靠性就会下降。仅靠提示并不能保证数据格式正确。
  • 标签定义存在于 system prompt 中,所以每新增一个组件都会膨胀上下文,每次修改还可能在 prompt 其他部分引入回归。
  • 过去的对话会以只有你的解析器能读的格式保存,因此加载历史记录时,要么在客户端解析原始消息,要么保留第二份对模型 API 并不原生的格式副本。

经实践验证的模式,是把每个 UI 组件都做成一个 tool。模型以带类型参数的方式调用 present_productspresent_itinerarypresent_plan_comparison;你的服务器对调用进行校验和增强并发出事件;客户端再进行渲染。

因为这些组件本身就是 tool call,它们已经以原生格式存在于 messages 数组中,所以在重新加载旧对话时不需要再次解析。下面以及参考仓库中都展示了一个 presentation-tool 合约示例。

上面的两个面板运行的是同一个代理,使用相同的工具和提示词;不同的只是 harness。总耗时大致相同,但用户看到结果的时间差别很大。

权衡在于流式粒度。工具调用的每个顶层参数都要先在服务器端缓冲以便校验,因此即使开启 streaming,presentation tool 的子组件也会分步骤到达。这会影响感知延迟。

如果想要 token 级流式输出,可以在工具定义中把 eager_input_streaming: 设为 true,这样就会跳过缓冲,也就失去了服务器端的 schema 保证。

在我们的 eval 中,Claude Sonnet 级别及以上模型的 schema 违规非常少,但对于少数漏网情况,仍应在调用外层加上重试。

Presentation tools 还会给代理留下“屏幕上显示了什么”的记录。当客户说“第一个酒店”或“左边往下数第三个”,布局信息就已经在 messages 数组里,位于最近一次 presentation 调用的参数中。

要让这一点生效,参数必须反映渲染后的布局,因此应当按 UI 的结构来组织,比如按顺序排列的行和轮播,而不是让客户端再去重排的扁平列表。

02 让它更快且更经济

从端到端和感知两个方面同时攻击延迟,并让缓存承担成本。实现这一点不应消耗智能。

在 commerce 场景中,延迟很重要,而面向消费者的界面最不容忍延迟。不过,在 agentic 界面上,我们一贯看到真正推动留存、参与度和购物车规模等指标的,是结果质量。

答案是否相关、任务是否真正完成,对这些指标的重要性通常高于微小的延迟改进。

因此要从两个方面攻击延迟。通过良好的工程实践压缩端到端延迟,同时降低感知延迟,因为用户看着代理工作时,这段时间本身就像“正在前进”。

每个用户都有一个延迟预算,下面这些技巧能让代理留在预算内,而且不需要消耗额外智能。

最小化任务完成延迟

任务完成延迟是各个模型轮次中“到最后一个 token 的时间”加上工具处理时间的总和。这给了你三个优化杠杆:更少的轮次、更快的工具和更快的 token。这些杠杆有时会相互竞争,因此真正要最小化的是它们的总和,而不是任何一个单项。

更少的轮次 预先加载可能相关的上下文,提高模型智能,并让模型并行调用彼此独立的工具。

更快的工具 优化工具自身的后端,并在参数一完成就尽早派发工具调用。

更快的 token 通过遍历 eval 套件来选择模型及其配置。

更少的轮次

查询复杂度会增加轮次,而且通常不受你控制。模型智能和相关上下文能帮助代理用更少轮次完成任务。我们在这方面的一些关键经验包括:

  • 预先加载可能相关的上下文。 如果用户从产品页打开助手,或者商家从活动仪表板打开它,就把该页面的数据放进会话上下文里。对话很可能就是围绕它展开,从上下文直接回答不需要额外轮次。
  • 提高模型智能。 更聪明的模型可以减少完成任务所需的总轮次,因为代理能更高效地规划并发出工具调用。这通常比它们更慢的 token 速度更重要。如果你的查询较复杂,或者生产环境显示每个任务超过大约五轮,那么更快的模型往往才是更聪明的那个。具体哪个更合适取决于你的流量,所以要按下文“选择模型”所述进行 sweep。
  • 让模型并行调用独立工具。 commerce 场景常常需要很多并行操作:搜索多个产品、查询大量政策文档,或者从多个销售数据源抓取记录。并行 tool use 可以避免多个独立查询额外消耗轮次。应当提示模型在一次轮次中调用多个工具,并把结果作为一个工具结果数组放在同一条用户消息里返回(见并行 tool use 文档)。

更快的工具

  • 优化工具自身的后端。 有时一个工具确实会向外分发,比如商家代理的“获取今日概览”查询,会通过三个彼此独立的调用读取销售、库存和活动状态。但我们更常见到的是,工具边界成了拼接缺失后端逻辑的地方:一个可用性检查先调用目录拿 SKU,再按门店调用库存服务,再调用履约服务拿截止时间,然后在工具自己的代码里应用替代规则和自提资格,最后才作答。这样一来,这个工具塞满了领域知识,随着规则变化越来越难保持正确,而且承载了本应位于上游系统中的逻辑。当你发现自己在工具里写这些逻辑时,修复方式是做一个后端端点来直接回答这个问题,然后用代理工具去调用它。
  • 尽早派发工具。 工具参数会像其他 token 一样从模型中流式输出,因此 harness 可以在每个工具的参数完成后立即执行该调用,并在模型仍在流式输出其他并行工具或内容块时处理它。我们看到这能把数秒级空档缩短到几百毫秒,且 Claude Agent SDK 默认就这么做。为了获得最大的延迟收益,你应该提示模型先输出最慢的调用。

上面的两个面板运行的是同一个代理,使用相同的工具和提示词;不同的只是 harness。总耗时大致相同,但用户看到结果的时间差别很大。

感知延迟

感知延迟指的是用户感觉屏幕开始有反应之前的时间。它在面向消费者的场景中特别关键,因为任何交易摩擦都会影响结账率和收入。有两种技术可以在不改动模型的前提下缩短它:

  • 在组件形成时就流式输出。 一个渲染后的 commerce 响应通常有 500 到 700 个输出 token,如果不流式输出,就相当于五秒或更久的转圈等待。把 presentation tool 的每个参数在流式输出时发送给客户端,并渐进式渲染页面。
  • 展示正在做什么。 当代理在收集上下文时,用通俗语言为每一步渲染一条简短进度文案(例如“正在找靠近水边的酒店”)。你可以直接用工具已有参数生成它(比如产品搜索的 query),也可以额外增加一个 user_facing_message 参数工具,让模型写出这行文案。

上面的两个面板运行的是同一个代理,使用相同的工具和提示词;不同的只是 harness。总耗时大致相同,但用户看到结果的时间差别很大。

Prompt 缓存

Prompt 缓存是你最大的降本候选项,而且 commerce 流量非常适合它。缓存输入 token 的读取成本只有新 token 的十分之一;虽然缓存写入大约要付出 1.25 倍的溢价,但一个缓存前缀在第二次使用时就能回本。在面向消费者、流量较大的应用中,你有独特机会利用默认的 5 分钟缓存过期时间,达到非常高的缓存命中率。

我们见过的最佳 commerce 部署,缓存命中率都能达到 90% 到 99%,而且从一开始就应该按这个范围来设计。我们的经验表明,在约 10 万 token 的规模下,缓存 token 的读取速度也大约快 1.5 到 2 倍,而且随着 token 数量增多,扩展关系相对线性。

缓存是基于前缀的。一次请求会一直从缓存中读取,直到与之前某次请求不同的第一个字节为止,所以真正重要的不只是上下文里有什么,还有它的顺序。可以把一次请求看作三个部分,按变化频率排序:

  • Global:大部分 system prompt 和工具定义,在每个会话中都相同。这是最热的缓存,到了规模化阶段很可能不会过期。保持它在不同轮次和会话中逐字节一致,并在其末尾放一个 cache breakpoint。
  • Session:每个用户的上下文和对话历史,会话之间不同,但在单个会话中保持稳定。这一段放在 global 之后。
  • Volatile:会话内会变化的内容,比如当前时间或当前页面。把它放在请求的最末尾,要么作为最新用户轮次中的一个标记块,要么在支持会话中系统消息的模型上,作为追加到 messages 数组中的 system-role 消息。我们最常见到的错误,是把时间戳或当前页面放在 system prompt 的顶部,这会悄无声息地让每次请求的缓存失效。

这里有两个实现细节要记住。第一,skills 应当以工具结果的形式加载,而不是追加到 system prompt 中。这样 skill 正文会落在对话前缀里,并与前缀一起被缓存。

第二,每一轮都要把 breakpoint 向前推进:一次请求允许的 breakpoint 数量有限,所以要把最新的那个移动到每个用户轮次的末尾。这样每一轮都能从缓存中读取累积历史,包括像搜索响应这样较长的工具结果。

选择模型及其配置

模型大小和 effort 设置本质上是同一个权衡:智能与延迟和成本之间的取舍。你应该通过测量来同时选择它们:

  1. 先定指标和底线。 选定你的业务所依赖的质量指标(任务完成率、答案相关性、事实准确性)、你不能低于的 eval 分数,以及 p50 和 p99 的延迟与成本预算。
  2. 做 sweep。 在你会考虑的每一个模型和 effort level 上运行整个 eval 套件。我们建议商家代理从 Opus 开始,因为它们的任务分析密集;消费者代理则从 Sonnet 开始,因为延迟更重要。如果你有生产流量,就按真实查询组合来加权结果。然后让数据决定。某些情况下,Opus 5 在推动购物车任务上的提升足以抵消与 Sonnet 的成本差异;某些情况下则不然。
  3. 仔细解读结果。 有两件事经常让团队意外。第一,prompt 是针对某个模型调过的,所以用一个 prompt 跑 sweep 时,可能会低估那些不是为它写的其他模型。较小的模型通常需要当前模型会自行推断的指令,而较大的模型会严格执行小模型原本忽略的指令。对每个候选模型的失败案例做几轮迭代,是在淘汰它们之前非常划算的一步。第二,更智能的配置有时会在延迟上取胜(最常见于 p90 和 p99),即使 token 更慢,因为它能更好地规划工具调用,并且在最复杂的请求上所需轮次更少。

衡量时要按“完成任务的成本”而不是“每次模型调用的成本”来算,因为一个更便宜但需要更多轮次、或者失败率更高的模型,并不更便宜。当结果接近、且成本符合你按任务核算的经济性和延迟要求时,优先选择智能。质量才是驱动采用和留存的关键,也能为未来 6 个月的建设留出空间,因为模型会越来越强。

03 让它在生产环境中跑起来

记忆、安全、eval,以及如何在组织内扩展这项工作:这些决定了一个代理能否进入生产并留在生产里。

最后,我们来讲讲让代理通过生产考验并持续运行所需要的内容:记忆、安全、eval,以及如何在组织内扩展这项工作。

能跨会话存续的记忆

你与客户之间的关系和互动很重要。记忆能让代理接续上一次对话,而不是每次都从零开始。三月提到坚果过敏的购物者,不应该在六月还得再说一遍;每周一都会检查同样三个活动的商家,也不应该每次都重新报一遍名字。长期记忆,也就是那些应该跨会话保留的事实,是你需要自己构建的系统,它由三部分组成:事实如何存储、如何写入、以及如何读取。

存储记忆

记忆应该放在你的系统里,而不是放在模型里。

当 profile 很小,而且代理是唯一读取者时,扁平的 markdown profile 是可行的。大多数生产 商业智能体 都会超出这个范围,而现实中的替代方案就是你已经在运营的数据库。一个事实就是一条小的 typed record:一个键(如 shoe_sizedefault_storepreferred_report_cadence)、一个简短值、一个类别,以及它来自哪个会话。一些键你会预先决定并分配给每个用户,其余的由 extractor 发现。随着存储增长,数据库仍然可查询,允许你基于特定属性构建确定性行为,并能与你已经拥有的用户数据做关联。

对于面向商家的代理,应按“人”而不是“账号”来键控记忆。商家的登录账号经常被多个操作员共享,因此每个操作员都需要自己的 profile,而且读取时必须尊重该操作员的权限:门店经理的代理不应记起区域经理说过的事实。

在 commerce 领域,代理记忆存放的是个人数据。值得记住的那些事实,往往恰恰是监管最严格的,而不同司法辖区之间的规则也不同。要把记忆当作一个数据处理设计问题,而不仅仅是存储问题。实践中,这意味着四件事:

  • 决定你愿意持有哪些类型的记忆。 要在写入路径上强制执行,而不是只靠 prompt;每次保存都必须经过同一个 validator。
  • 给用户一种查看、更正和删除存储内容的方法。 将删除能力接入账号删除和数据请求流程。
  • 设置保留期限。 几年前的偏好很可能已经过时,因此保留期限有助于让记忆事实保持新鲜。
  • 记忆应该是按部署启用的开关。 这样无法承担这些义务的地区就可以不启用它运行。

写入记忆

记忆应异步写入。每轮结束时,或者在长会话的每几轮结束时,由另一个线程或进程中的代理读取对话,并在存储中创建、更新或删除事实,同时在会话继续进行时维护自己的工作上下文。

这不会增加对话延迟,而且在我们内部的 commerce memory eval 套件中,事实召回率提高了 13%。

显而易见的替代方案,是让代理调用一个 tool 去保存事实,但对延迟敏感的 商业智能体 来说,这不是正确做法。每次保存都是一次位于用户可见轮次中的 tool call;而且除非整个存储都在上下文里,否则一次保存前通常还需要先读一次来更新或去重,这本身又是一轮。

它还会在每一轮给代理多加一个决策,而在我们的 eval 中,这种对注意力的竞争会表现为漏记忆。

把 extractor 独立出来,还能让你更精确地提示它。它只读取用户和助手的文本,从不读取工具结果,因此产品描述或评论不会变成关于用户的事实。它的 prompt 会说明什么算事实,例如明确说出的尺码、饮食限制、履约偏好、商家常用的 materialized views;也会说明什么不算,例如列表页中的任何内容或一次性的细节。

读取记忆

以三层方式读取记忆。

始终在上下文中 一小组固定事实会在每一轮进入上下文:几乎每个请求都会依赖的那些事实,例如购物者的默认门店和履约偏好,或操作员的门店和角色。

每轮预取 与当前请求相关的事实,会根据与预加载 skill 相同的信号按轮预取:例如鞋类搜索会拉取尺码和品牌偏好,活动问题会拉取操作员常用的指标。

放在 lookup tool 后面 其余内容都放在 lookup tool 后。

由于记忆是每个用户的上下文,它全部都应放在 session 段中,位于 global cache breakpoint 之下。

安全:执行在 harness 中完成

prompt 是安全行为的起点,但在 commerce 里,它不能成为安全执行的地方。失败往往意味着财务损失,而且通常不可逆;而一条 prompt 规则,只要一次注入或一次坏样本,就可能被跳过。下面每条规则都通过代码强制执行,适用于消费者代理和商家代理,并且只定义一次,确保每个运行时都共享它。

模型负责阶段化;人或策略负责执行

没有任何模型 tool call 会直接移动资金或改变业务。下单、支付、退款、调价和活动上线,最终都要落到由 harness 控制而不是由模型控制的动作上。

在消费者侧,这是结构性的:checkout tool 会把购物车渲染出来并带上一个下单按钮,而代理所调用的后端接口根本没有 charge 方法。

在商家侧,每个写入工具都会生成一个带服务器生成 ID 的 staged change,而 apply_change 只有在通过真实界面批准后才会成功:可以是运营门户中的一个按钮、CLI 中的一次确认,或者当代理运行在 Managed Agents 上时由平台自己的工具审批提示。

这些护栏在 apply 时会依据当前限制重新检查,而不是依据变更暂存时有效的限制。无论界面如何,形态都是一样的:模型最危险的动作只是提出建议,审批则通过你业务中用于这类变更的 maker-checker 流程进行。

写入和渲染只接受服务器签发的 ID

harness 会为每个会话保留一份记录,记录服务器曾交给模型的每一个 ID,而这份记录是任何写入或渲染唯一接受的 key。

购物车只接受服务器向该会话返回的 product ID,商家工具只接受代理确实读到过的 listing 和 campaign ID。任何以其他方式到达的 ID,无论是模型幻觉出来的、用户粘贴的,还是评论里植入的,都会在后端看到它之前被拒绝。

同样的规则也适用于 UI。Presentation tools 接收 ID,由服务器自行填充产品、订单或变更记录,因此只有服务器自己填过的记录才会被渲染成卡片。

这也适用于 delegate:商家分析 subagent 可以读取数据,但不会把任何 ID 加入代理可写入的集合。

对于费用、披露和其他受监管内容,模型决定要披露哪个产品,而服务器提供每一个字,使用的是经过批准的文案。同样的费用字段也在商家代理的受保护列表中,因此柜台两侧都不能更改或改写它们,eval 会逐字节检查渲染出的字符串。

有上限的交易必须承受重复请求

大多数 commerce 界面都会限制单个用户可购买某一商品的数量,例如票务配额、促销定价或反欺诈控制;而代理会以人类点击按钮不会采用的方式进行重试、改写和并行化。

因此,这个上限应当像写入后那样在线上执行,这样第二次“再加两个”就不能把数量叠加超过上限,而且同一会话的购物车写入要串行化,这样单轮中的并行 tool call 就不会合并后突破限制。

对于费用、披露信息和其他受监管内容,模型只选择要披露哪款产品,服务器则从获批文案中逐字提供全部内容。同样的费用字段也在商家代理的受保护清单上,因此交易柜台两侧的任何一方都无法更改或改写它们,评测也会逐字节检查渲染后的字符串。

有上限的交易必须经得住反复请求

大多数商业界面都会限制单个用户可购买某件商品的数量——用于门票配额、促销定价或欺诈控制——而代理会以人类点击按钮从未有过的方式重试、改写请求并并行执行。

因此,上限会在行项目层面按写入后的状态来执行,这样第二次“再加两件”就无法叠加并突破上限;同一会话中的购物车写入也会被串行化,确保单轮交互中的并行工具调用无法合并起来超过上限。

商家侧的更改也会以同样方式检查价格变动幅度、折扣深度、补货规模和活动预算的上限,以及任何更改都不得触碰的受保护字段清单。这个规则可以泛化为:对结果状态而不是请求本身执行每一项限制,并按会话串行化写入。

第三方内容会被清洗

在商业场景中,大部分上下文都由不是你的人撰写——卖家、评论者、竞争对手——因此每一次后端读取都是不可信输入,都要经过同一个清洗器。

凡是由第三方编写的工具结果,例如商品列表、评论、政策、卖家消息和已存储记忆,都会在模型看到之前被清洗,并用固定标签包裹在围栏中。

清洗器会去除控制字符和双向文本字符,移除任何模仿围栏标记的内容,化解那些伪装成对话轮次或工具调用的文本,并限制大小;这些设计旨在阻止恶意商品列表冒充系统,或填满上下文。

提示词则承担契约的另一半:围栏中的文本是用于汇报的材料,绝不是用于执行的指令。

评测:发布一个非确定性系统

从一个小小的提示词改动到新增一个工具,任何变化都可能以难以预测的方式改变代理行为,而你正在发布的变更往往并不是导致回归的那个变更。评测(evals)就是让你在部署前发现这些问题的方法。我们此前关于代理评测的博客文章介绍了通用实践。本节介绍商业代理的具体做法。

评估快照,而不是对话

模型 API 是无状态的,因此代理的输出是系统提示词、工具和消息数组的函数。这意味着商业对话可达到的任何状态都可以被直接构造出来。因此,创建一个评测用例,就是构造测试状态、追加测试用户消息,然后让代理从那里开始运行。

然后对结果评分:最终状态和渲染出的回复,包括最后一次写入的参数。在大多数情况下,我们不建议对代理到达结果所走的路径本身评分,因为这类测试用例既脆弱又具有过强约束。

模拟用户评测,也就是由第二个模型扮演用户、再由裁判评估整段对话,并不是一种好的度量工具。两个非确定性系统交互需要更大的样本量、每次试验成本更高、更难评判,而且产生的失败也很难归因。它们适合用来发现覆盖缺口,也适合对代理做整体观感检查,所以可以用它们来发现用例,然后把每个用例写成快照。

在艰难条件下评估行为

大多数团队没有充分测试注入后的状态。一个用例应该编码失败的前置条件,而不只是任务本身。如果某种行为只有在第一轮繁忙交互、包含多次工具调用之后,或在会话早些时候出现矛盾之后才会显现,那么从干净状态开始的用例会在每一种配置下都通过,并且无法提供有意义的数据。

我们观察到,大多数测试套件都过度偏向这类干净状态用例,所以要确保你的用例中有一部分从冗长、混乱或相互矛盾的历史开始。

覆盖不同类型的商业代理评测

有效评估需要同时测试期望行为和非期望行为。

对每一个正向用例,都要写出它的反向对应项:每一个“应该服务”都要对应一个“应该拒绝”,每一个“应该直接执行”都要对应一个“应该追问”。缺少反向用例是我们在测试套件中最常发现的缺口。

请评估以下内容:

  • 核心请求,也就是构成你大部分流量的请求,因为这里的失败会影响大多数会话。这些包括简单查询、多约束请求、产品和套餐问题,以及多意图消息。对于问题类请求,要检查每一个价格、可用性和属性是否都能追溯到返回数据,并确认代理会在数据缺失时说明情况,而不是编造答案。
  • 依赖上下文的请求,例如对屏幕上内容的引用、从前几轮继承下来的约束,以及针对现有购物车的写入。记忆评估也属于这一类。要检查记忆是否被抽取、检索,并确实改变了答案。
  • 安全与品牌用例,即失败会造成金钱或信任损失的场景。这些包括注入尝试、读取其他用户数据的尝试,以及受监管表述;受监管表述要逐字节检查。把注入拆成两个用例:用户编写的注入,即指令来自用户自己的消息;以及数据平面注入,即指令被植入通过工具结果传入的产品名称、评论或网页片段中。
  • 界面评估,确保渲染了正确组件、商品数量上限得到遵守,并且面向用户的文本中没有内部标识符。也要测试超时和空结果。
  • 同时属于多种能力的请求。 运营人员问:“如果我把这个降价 15%,我的库存足够覆盖需求吗?”这同时是定价问题和库存问题。正确答案会暂存降价方案,并附上库存预测;错误答案会只做其中一个而跳过另一个。按能力分别编写的评测抓不住这个问题,因为每个评测只评分自己那一半。要为需要两个相邻能力协同处理的请求编写用例,并对答案的两部分都评分。
与领域专家一起编写评测,并使用真实事故

与能一线看到失败的主题专家合作设计测试用例,例如产品、法务、商家运营、客户服务和品类管理团队成员。真实失败是最好的评测素材,每个用户流程 50–100 个评测用例是一个不错的起点。

确保用例类型多样,如上所述。生产环境对话记录是获取新用例的绝佳来源,尤其是那些棘手用例。编码代理很擅长生成更多用例和对抗性变体。参考仓库包含一个 Claude Code 插件,其中内置了按我们推荐方法构建的评测编写技能。

在大型组织中发布

在商业企业中,代理由许多工程团队共同构建。搜索、结账、定价、营销技术、客户服务和目录平台分别拥有代理所依赖的系统,各自按自己的节奏发布,也都会想要新增或修改某个工具、技能或提示词规则。

与服务不同,代理没有严格的模块边界来保护其他部分:定价团队做出的更改会与结账共享同一个上下文窗口。

诱人的修复方案是把系统拆成许多子代理,每个业务单元一个。正如第 1 部分所讨论的,出于质量原因,我们不建议这样做。相反,我们概述了降低多团队协作风险的流程:

  • 所有权跟随系统。 每个技能和工具都有唯一的所属团队。例如,定价团队拥有促销工具和定价技能,客服团队拥有订单与退货工具以及客户服务技能。共享提示词的通用部分由一个平台级负责人负责,领域特定部分则由对应领域负责人负责。
  • 变更要带着用例一起发布,CI 会运行为它选择的一组用例。 贡献某项技能的团队也要贡献对应用例,包括反向用例,以及与相邻技能之间的边界用例。在每个拉取请求上运行完整套件太慢、太贵,无法长期维持,所以应当从中构建一组 CI 用例。该集合应由最高流量请求的核心用例和所有安全用例组成。在此基础上,再运行变更触及内容对应的用例。对于技能来说,这意味着它自己的用例以及相邻技能的边界用例。对于工具来说,则是每一个调用它的用例。对于共享提示词来说,则是完整评测套件,因为所有内容都会读取系统提示词。我们建议基于多次试验的通过率、缓存命中率以及每轮成本设置门禁。每晚以及每次发布前运行完整套件也是良好实践。跨团队回归会在这些运行中被捕获。
  • 代理也应纳入发布日历。 它是一个部署单元,所以一次糟糕的变更会同时触达每个用户。先把提示词和技能变更发布到金丝雀用户组,保留一个无需部署即可关闭单个技能的开关,并在高峰期前像冻结其他系统一样冻结代理。

关于这种安排中的人员协作侧,请参阅构建高效的人类—代理团队

展望未来

本文描述的大部分内容并不关于模型本身。工具调用的是你已经在运行的系统,技能编码的是你已经在遵循的流程,评测是以测试形式写成的产品需求文档,而运行框架会执行你对任何客户端都会执行的策略。模型会持续改进;当更好的模型发布时,我们描述的架构可以把它作为一次配置变更引入,并配合一轮评测扫描。其他一切都能继续工作。

同样重要的是,要思考你的产品界面路线图。这套架构会比聊天面板更持久。同一个代理可以通过语音工作,也可以在用户提出请求前主动针对票价下降采取行动。对于一个已经具备评测和工具的团队来说,这些都是表现层项目。再往后,你的店铺流量中有一部分会来自代表用户购物的代理。让你自己的代理保持边界内的同一套来源追踪、暂存和审批规则,也会让你能够安全地向这些代理开放工具。

商业一直奖励那些让购买流程尽可能顺畅的做法。代理让这件事容易得多。可以查看完整参考实现,其中包含消费者代理和商家代理,以及面向零售、旅行、电信和娱乐的可运行示例。

致谢

作者为 Matthew Koen 和 Ali Shazal。特别感谢 Michael Segner、Rodrigo Olivares、Amandeep Khurana、Aiza Usman、John Lopus 以及其他贡献者。

评论

(0)
未配置登录方式
暂无评论