跳到正文

构建高效的智能体自动化

参考实现详解及常见失败模式

本文目录文章开头

随着 AI 加速我们的工作,跟上节奏变得越来越困难。在 Anthropic,简单的智能体自动化经常被用来提供帮助。它们通常按计划运行,在后台收集上下文,并主动告知我们需要了解的内容。但构建有效的智能体自动化很困难:它们可能在无人察觉的情况下失去对某个数据源的访问权限,或无法遵循我们的偏好。

借助 Claude Managed Agents(测试版),我们构建了一个参考实现,它按计划读取自定义数据源(例如 Slack 和 GitHub 仓库),跟踪自上次运行以来的变化,并发布你需要了解的内容(例如发布到 Slack)。在本文中,我们将逐步讲解每个步骤,分享一个参考实现,并提供一条可在 Claude Code 中运行的命令来为你配置该智能体。

获取代码

参考实现在这里。如需交互式演示,请在 Claude Code 中运行以下命令。claude-api 技能可以帮助按照本文的指导设置智能体:

/claude-api managed-agents-onboard https://claude.dev/blog/building-effective-agent-automations/

对于此参考实现,你需要一个 Slack 应用(从清单创建)和一个 GitHub 令牌。提供的文件(如下所示)是 Claude API 资源的配置,包括智能体、其环境、记忆存储、保险库和部署。

daily-brief/
├── agent.md                        model, tools, instructions
├── deployment.md                   schedule, time zone, budget, input message
├── environment.yaml                network allowlist
├── memory_store_preferences.yaml   user preferences
├── memory_store_state.yaml         the agent's bookmarks, ledger, notes, and run records
├── vault.yaml                      the vault that holds the credentials
├── claude-lock.json                resource IDs, written by ant apply
└── slack/manifest.yaml             one bot app

ant apply 是 ant CLI 中的一个命令,它读取这些文件,在你的 Claude API 工作区(平台存储和运行这些资源的地方)中创建资源,并将 ID 记录在 claude-lock.json 中。

我们将在下面的章节中使用此命令。配置完成后,自动化将在 Anthropic 的基础设施上按计划运行,因此无需在你的机器上保持任何程序运行。

概述

我们将构建的智能体包含六个组件,按以下顺序介绍:

  • 数据源 - 要读取的位置的命名列表
  • 目标 - 智能体可以写入的一个位置
  • 智能体 - agent.md 中的模型、工具和运行步骤
  • 计划 - cron 计划任务
  • 记忆 - 你的偏好和智能体自身的记忆
  • 护栏 - 在智能体仅进行读取的地方提供只读访问权限,以及每次运行的支出上限
Architecture of the agent: a schedule wakes the agent, which reads from its sources and posts a brief to one destination for the reader. Memory holds the agent's state and the reader's preferences, and guardrails surround the agent.

数据源

智能体读取两个默认数据源:Slack 频道和 GitHub 拉取请求。频道和仓库列在你的 preferences 文件中。该模板可以扩展以使用其他数据源。

The architecture diagram with Sources highlighted. The sources are read-only, reached through MCP or HTTPS with credentials from a vault, and feed the agent.

为智能体提供其专属的、范围受限的凭据

使用 Managed Agents 时,凭据存储在保险库中。智能体可以引用这些凭据,但真实值保留在保险库中,位于 Claude 代码运行的沙箱之外(参见此处和此处):

  • MCP 服务器(GitHub)。智能体通过运行在沙箱之外的代理调用 MCP 工具。该代理会找到 URL 与服务器匹配的保险库凭据。

  • Shell(Slack)。智能体使用沙箱内的 bash 工具通过 curl 调用 Slack API。沙箱中只保存一个不透明的占位符 $SLACK_BOT_TOKEN。当请求离开沙箱时,平台会为你允许的主机替换为真实令牌。

使用 ant CLI 和仓库中的模板文件创建保险库:

ant apply vault.yaml

这会在你的 Claude API 工作区(平台存储保险库的地方)中创建保险库,并将其 ID 记录在 claude-lock.json 中。然后使用 TypeScript SDK 将每个凭据添加到保险库。以下是添加 Slack 凭据的示例:

const vaultId = process.env.VAULT_ID!; // the vault's ID, from claude-lock.json

await client.beta.vaults.credentials.create(vaultId, {
  display_name: "SLACK_BOT_TOKEN",
  auth: {
    type: "environment_variable",
    secret_name: "SLACK_BOT_TOKEN",
    secret_value: process.env.SLACK_BOT_TOKEN!,
    networking: { type: "limited", allowed_hosts: ["slack.com"] },
    injection_location: { header: true },
  },
});

创建保险库并添加每个凭据后,将保险库附加到部署中。将 claude-lock.json 中的保险库 ID 复制到部署文件的 vault_ids 中,deployment.md。

从上次中断处继续阅读

一个常见的错误是要求智能体读取固定的时间窗口,例如“过去 24 小时”。较晚的运行会留下空隙,而较早的运行会重复条目。相反,应为每个来源给智能体一个书签。在每次运行结束时,智能体将其从每个来源读取的最新条目的时间戳写入一个文件 bookmarks.json,每个来源一条记录:"slack": "2026-09-14T13:02:11Z"。

下一次运行从这些书签开始,因此其窗口会拉伸或收缩以覆盖自上次运行以来的所有内容。书签存储在名为 state 的 记忆存储中:这是一个文本文件夹,平台将其挂载到每次运行的沙盒 /mnt/memory/ 下,并在运行之间保留。智能体使用其普通的文件工具读写它,而 agent.md 中的指令告诉它如何操作。

不要将读取失败误认为是平静的一天

如果 MCP 服务器宕机或其令牌已过期,运行仍会开始,只是没有该服务器的工具。会话会记录一个错误,但智能体看不到来自该来源的任何内容,并报告“无新内容”。

agent.md 中的三条规则有助于解决这个问题。当某个来源失败时,智能体将:保持该来源的书签不变,用其他来源编写简报,并在简报末尾添加一行说明它无法读取的内容(“本次运行拉取请求不可用”),以便读者知晓。

目标

我们的模板发布到一个 Slack 频道,每次运行时发布一条带日期的帖子。

The architecture diagram with Destination highlighted. After a check that today's brief isn't already posted, the agent posts to one destination, which delivers the brief to the reader.

智能体使用其沙盒中的 bash 工具发布到 Slack,使用与其读取时相同的机器人令牌。

无需任何审批即可发布。智能体使用 bash 命令发送,而内置的 bash 工具默认无需请求批准即可运行。slack.com 也在智能体 环境(其运行的沙盒)的允许列表中。发布是一个请求:

curl -s https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" \
  -H "Content-Type: application/json; charset=utf-8" \
  -d '{"channel": "C0123456789", "text": "Daily brief, Tue Sep 15 ..."}'

在记录之前确认帖子已发布

一旦帖子得到确认,智能体就会更新其已报告条目的账本和书签。如果这些记录与实际发布的内容不符,可能会出现两种问题。如果智能体记录了一个从未发布的帖子,书签会继续前进,这些条目将永远不会被报告。如果它因为不确定第一个帖子是否已发布而再次发布,读者会收到两次相同的简报。

agent.md 中的三条规则可以防止这种情况。首先,智能体在频道的近期消息中查找今天的标题,如果该期已存在则不发布。其次,只有当 Slack 返回 "ok": true 和消息 ts 时,帖子才算已发送。第三,智能体仅在确认之后才更新账本和书签。如果结果不明确,它会将运行标记为“可能已发布”,且不做其他更改,以免丢失任何内容。

智能体在其记忆存储(runs/<date>.md)中保留运行记录。它在发布前将运行标记为“发布中”,然后标记为“已发布”并附上消息 ID,或标记为“可能已发布”。

智能体

在 Claude Managed Agents 中,智能体是一个带版本的配置:一个模型、一个系统提示词和工具。每次运行都遵循其运行步骤并停止。

The architecture diagram with the Agent highlighted: a model plus a prompt that holds the run loop and the judgement rules.

在我们的参考实现中,智能体配置为 agent.md:

---
name: Daily brief
model: claude-sonnet-5-5
mcp_servers:
  - type: url
    name: github
    url: https://api.githubcopilot.com/mcp/
tools:
  - type: agent_toolset_20260401
    configs:
      - name: web_search
        enabled: false
      - name: web_fetch
        enabled: false
  - type: mcp_toolset
    mcp_server_name: github
    default_config:
      permission_policy:
        type: always_allow
---

[Eight numbered run steps; the full text is in agent.md in the repo.]

frontmatter 提供智能体名称、模型、工具和 MCP 服务器。正文提供智能体指令。MCP 工具默认需要批准,但没有人可以给予批准,因此 GitHub 工具集设置为 always_allow,GitHub 令牌为 read-only。

保持简报简短

agent.md 引导 Claude 保持简洁:

4. Decide. An item earns a line when the reader would act on it today, or it changes a decision they are about to make. When unsure, leave it out. Most days that is a few items, sometimes none. A count ("12 open reviews") is not an item; link the ones that are blocked. An item already in the ledger and still open is carried as one marked line ("still waiting, day 3"), not re-reported; a closed item is dropped without comment. Do not bring back a topic the preferences file has retired.

在发布之前重新检查所有仍未关闭的项目

在智能体读取来源和发布之间,项目状态可能会发生变化。就在发布之前,agent.md 会指示智能体重新检查每个项目的实时状态:

5. Verify. The world moved while you read. For every item you will report, re-check its live source just before posting: resolved since you read it, drop it; still open but changed, fix the line; cannot confirm, drop it and list it in the run record's cuts. One stale "still waiting on you" costs more trust than ten missing items, so never hedge an item's status: assert it or drop it. Every link is copied from the source's own link field (a pull request's html_url, a Slack permalink), never assembled by hand.

SCHEDULE

使用 Claude Managed Agents,智能体只是一个配置文件;由 deployment 来运行它。deployment 指定了智能体、环境以及每次运行的首条消息。它还包含调度、vault、记忆存储和预算。每次调度触发时,平台都会启动一个全新的智能体 session。

The architecture diagram with Schedule highlighted: a scheduled deployment that wakes the agent for each run.

在我们的模板中,deployment 定义在 deployment.md 中,首条消息作为其正文:

---
name: Daily brief
agent: ./agent.md
environment_id: ./environment.yaml
schedule:
  type: cron
  expression: "32 7 * * 1-5"
  timezone: America/New_York
vault_ids: [vlt_...]   # the vault you create under Sources
resources:
  - path: ./memory_store_preferences.yaml
    access: read_only
    instructions: The reader's preferences. Re-read them every run. Never write here.
  - path: ./memory_store_state.yaml
    access: read_write
    instructions: Your state. Bookmarks, ledger, notes, proposals, and run records.

---

Write today's brief.
The reader's time zone is America/New_York. Work out every date in that zone.

Follow your run steps in order. Today's edition is titled "Daily brief, <weekday> <month> <day>".

这会创建 deployment,并按路径指定其智能体、环境和记忆存储。

ant apply deployment.md

要不等调度就进行测试,可以用 ant beta:deployments run --deployment-id <id> 手动启动一次运行,使用 claude-lock.json 中的 ID。

按你的时区计算日期

一个常见的 bug 是智能体把今天早上称为“昨天”,因为它按服务器的时区计算日期。在 deployment.md 中,timezone 字段设置运行触发的时间,正文的第二行告诉智能体计算日期时应使用哪个时区。

MEMORY

每次运行都在一个全新的沙箱中开始,对上一次运行没有任何记忆。没有记忆,反馈就无法留存。然而,过时的记忆会让智能体困惑:它可能在某个项目已解决后仍报告其处于等待状态,或者因为“已经报告过”而漏掉一个仍未关闭的项目。

The architecture diagram with Memory highlighted. State is what the agent reads and writes, and Preferences is what the reader writes and the agent only reads.

我们的模板保留了两个 memory stores,即挂载在 /mnt/memory/ 下的文件夹(见 Sources):

  • preferences(你的,对智能体只读):要读取哪些频道和仓库、要排除什么、长度上限、目标位置,以及何时停止。

  • state(智能体的,可读写):书签、它所报告内容的账本(每次运行一条记录)、它对你偏好的修改建议,以及关于每个来源行为的备注(“只返回最新的 50 条项目”)。

ant apply deployment.md 会创建 preferences 存储,但不会创建其中的文件。在首次运行之前,用仓库的 scripts/seed-preferences.sh 在那里写入你的 preferences.md。

每次运行开始时重新读取你的偏好

一个常见问题是偏好的一份副本被固化在提示词中,导致它持续应用你已经更改过的规则。让智能体每次运行都重新读取文件。如果它无法读取文件,它应该停下来并说明情况,而不是按默认值继续运行。

维护一份你已报告内容的账本,并报告变化

智能体维护一份账本 ledger.md,记录它报告过的每个项目,这样简报就不会重复。每一行记录该项目何时被报告、来自哪里、一个不会变化的 ID(Slack 消息时间戳或 pull request 编号),以及其最后已知状态:

2026-09-09 slack:C0123456789 1788963600.000100 refund thread: customer waiting on a decision
2026-09-11 github 481 review blocked, day 2 (still waiting)
2026-09-11 slack:C0234567891 1789117333.000300 enterprise escalation: owner named, in progress

GUARDRAILS

因为我们的自动化按调度在“后台”运行,我们对智能体能做什么以及能花费多少设定了限制。

The architecture diagram with Guardrails highlighted: a boundary around the agent with caps on turns, time and spend, and a heartbeat that someone watches.

限制它能做什么

智能体读取其他人编写的消息和 issue,而这些文本可能被解释为指令。限制智能体如果遵循这些指令时可能做的事。在我们的例子中,GitHub token 和 preferences 存储是只读的,环境只能访问其允许列表中的主机。被植入的指令仍然可以改变简报的内容,包括通过智能体在运行之间保留的备注。它无法写入 GitHub 或编辑你的规则。

Slack 是例外:使用相同的令牌发布,因此只在需要读取或发布的地方邀请机器人。

根据实际运行情况设置消费上限

消费上限可以防止成本失控。先从正常运行成本的三到五倍开始,然后根据实际数据逐步收紧。达到上限的运行会暂停而不是失败,因此上限设置过低看起来就像简报突然安静了。上限是 deployment.md 中的 budget。每次运行都会获得全额额度,达到额度的运行会以 budget_reached 停止原因暂停:

budget:
  type: limit
  max_list_cost:
    amount: "500" # a string, in cents: "500" is $5.00
    currency: USD

入门指南

我们的参考实现归结为六条规则:

  • 从书签读取每个来源,而不是固定的时间窗口。
  • 将读取失败报告为不可读,绝不报告为安静的一天。
  • 在发布前重新检查每个项目。
  • 只有在 Slack 确认后才将帖子计为已发送,然后更新书签和账本。
  • 每次运行都从智能体无法编辑的存储中重新读取您的偏好设置。
  • 在智能体仅进行读取的地方授予只读权限,并限制每次运行的花费额度。

Claude Code 可以引导您完成本文提供的指南。首先,更新:

claude update

然后,使用 claude-api 技能:

/claude-api managed-agents-onboard https://claude.dev/blog/building-effective-agent-automations/

claude-api 技能会读取这篇文章,提出设置方案,将文件写入您项目中的 agents/ 文件夹,并使用 ant apply 创建资源。将其作为起点,并根据您的来源、目标或记忆偏好自定义智能体。

阅读显示设置

调整正文排版

当前浏览器记住选择,应用于文章正文。手机上正文宽度以屏幕可用空间为限。

图表完整性提示

普通文章遇到图表保存问题时显示提示。