跳到正文
Claude Developer Blog· Lance Martin and CJ Avilla·· 13 天前精选AI 评分83

Anthropic 详解如何构建有效的 Agent 自动化

Building effective agent automations

AI 导读

Anthropic 基于 Claude Managed Agents(beta)分享一套定时 Agent 自动化参考实现,说明如何读取来源并把简报发布到 Slack。

推荐理由

文章将定时 Agent 自动化拆解为来源书签、失败显式告知、发布确认后更新状态等环节,提供了可迁移的可靠性设计方法。

正文 · AI 翻译

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

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

获取代码

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

提示词

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

对于这个参考实现,你需要一个 Slack 应用(从 manifest 创建它)和一个 GitHub token。提供的文件(如下所示)是 Claude API 资源的配置,包括该 agent、其环境、内存存储、vault 和部署。

代码文本

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 有六个组成部分,将按以下顺序介绍:

  • 来源 - 一个带名称的读取位置列表
  • 目标位置 - agent 可写入的一个位置
  • Agent - agent.md 中的模型、工具和运行步骤
  • 计划 - 一个 cron 计划
  • 记忆 - 你的偏好以及 agent 自身的记忆
  • 护栏 - 在 agent 只读取的地方使用只读访问,并为每次运行设置支出上限
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.

来源

该 agent 会读取两个默认来源:Slack 频道和 GitHub pull request。频道和仓库列在你的 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.

为 agent 提供它自己的、范围受限的凭据

使用 Managed Agents 时,凭据存放在 vaults 中。agent 可以引用这些凭据,但真实值会保留在 vault 中,位于运行 Claude 代码的沙箱之外(见 这里 和 这里):

  • MCP 服务器(GitHub)。 agent 通过一个在沙箱外运行的代理调用 MCP 工具。该代理会找到 URL 与服务器匹配的 vault 凭据。

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

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

代码Shell

ant apply vault.yaml

这会在你的 Claude API 工作区中创建该 vault,平台会在其中存储它,并将其 ID 记录到 claude-lock.json 中。然后使用 TypeScript SDK 将各项凭据添加到 vault 中。下面是一个展示如何添加 Slack 凭据的示例:

代码TypeScript

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 },
  },
});

创建 vault 并添加每个凭证后,把该 vault 附加到部署。将 vault ID 从 claude-lock.json 复制到部署文件 deployment.md 中的 vault_ids。

从你上次停止的地方继续读取

一个常见错误是让 agent 读取像“过去 24 小时”这样的固定时间窗口。运行得晚会留下空档,运行得早会重复条目。相反,应为每个来源给 agent 一个书签。在每次运行结束时,agent 会把它从每个来源读取到的最新条目的时间戳写入一个文件 bookmarks.json,每个来源一条记录:"slack": "2026-09-14T13:02:11Z"。

下一次运行从这些书签开始,因此它的时间窗口会拉长或缩短,以覆盖自上次运行以来的全部内容。书签保存在名为 state 的 记忆存储 中:这是一个文本文件夹,平台会把它挂载到每次运行的沙箱中的 /mnt/memory/ 下,并在多次运行之间保留。agent 用普通文件工具读写它,而 agent.md 中的说明会告诉它如何操作。

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

如果某个 MCP server 宕机了,或者它的 token 已过期,运行仍会开始,只是没有那个 server 的工具。会话会记录一条错误,但 agent 看不到该来源的任何内容,并会报告“没有新内容”。

agent.md 中的三条规则有助于修复这个问题。某个来源失败时,agent 会:保持该来源的书签不变、撰写其他来源的简报,并在简报末尾用一行说明它无法读取什么(“本次运行无法获取 pull requests”),以便让读者知晓。

目标位置

我们的模板会发布到一个 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.

agent 使用其沙箱中的 bash 工具发布到 Slack,使用的正是它读取时使用的同一个 bot token。

帖子不需要任何审批。agent 用一条 bash 命令发送它,而内置 bash 工具默认会直接运行,无需请求批准。slack.com 也在 agent 的 环境 允许列表中,也就是它运行所在的沙箱。发布只需一个请求:

代码Shell

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 就会更新它已报告条目的台账和书签。如果这些记录与实际发布的内容不一致,可能会出两种问题。如果 agent 记录了一条实际上并未发布成功的帖子,书签就会继续前移,而这些条目将永远不会被报告。如果它因为不确定第一次是否发布成功而再次发布,读者就会收到两次相同的简报。

agent.md 中的三条规则可防止这种情况。第一,agent 会在频道最近的消息中查找今天的标题,如果该期已经在那里,就不会再次发布。第二,只有当 Slack 返回 "ok": true 和一个 message ts 时,这条帖子才算已发送。第三,只有在确认之后,agent 才会更新台账和书签。如果结果不明确,它会将本次运行标记为“可能已发布”,除此之外不做任何更改,这样就不会丢失任何内容。

agent 会在它的记忆存储(runs/<date>.md)中保留一份运行记录。它会在发布前把运行标记为“posting”,然后标记为带有 message ID 的“posted”,或者标记为“maybe posted”。

AGENT

在 Claude Managed Agents 中,agent 是一种有版本的配置:模型、system prompt 和工具。每次运行都会遵循其运行步骤并结束。

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

在我们的参考实现中,agent 配置是 agent.md:

代码Markdown

---
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 提供 agent 名称、模型、工具和 MCP servers。正文提供 agent 指令。MCP 工具默认会请求批准,而当时不会有人来批准,所以 GitHub 工具集被设置为 always_allow,GitHub token 则是 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.

调度

使用 Claude Managed Agents 时,代理本身只是一个配置文件;运行它的是一个部署。部署会指定代理、环境以及每次运行的第一条消息。它还包含调度、保管库、记忆存储和预算。每次调度触发时,平台都会启动一个全新的代理会话。

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

在我们的模板中,部署记录在 deployment.md 中,其正文就是第一条消息:

代码Markdown

---
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>".

这会使用其按路径指定的代理、环境和记忆存储来创建部署。

代码Shell

ant apply deployment.md

要在不等待调度的情况下测试它,可使用来自 claude-lock.json 的 ID,通过 ant beta:deployments run --deployment-id <id> 手动启动一次运行。

按你的时区计算日期

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

记忆

每次运行都会在一个全新的沙箱中启动,不会记住上一次的内容。没有记忆,反馈就无法留存。不过,过时的记忆也会让代理困惑:它会把某个事项报告成仍在等待,尽管它已经解决;或者因为“已经报告过了”而漏掉一个其实仍未处理的事项。

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.

我们的模板保留了两个记忆存储,也就是挂载在 /mnt/memory/ 下的文件夹(见 Sources):

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

  • state(代理的,可读可写):书签、它已报告内容的台账、每次运行一条记录、它对你的 preferences 提出的修改,以及关于各个来源行为的备注(“只返回最新的 50 个项目”)。

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

在每次运行开始时重新读取你的 preferences

一个常见问题是,preferences 的副本被烘焙进了提示词里,导致它会持续应用你已经改过的规则。让代理在每次运行时都重新读取这个文件。如果它无法读取该文件,它就应该停止并说明情况,而不是按默认值继续运行。

保存一份你已经报告过内容的台账,并报告变化

代理会维护一份台账 ledger.md,记录它报告过的每一个事项,这样简报就不会重复。每一行都会记录该事项何时被报告、来自哪里、一个不会变化的 ID(Slack 消息时间戳或拉取请求编号),以及它最后已知的状态:

代码文本

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

防护措施

由于我们的自动化会按计划在“后台”运行,我们对代理能做什么以及它能花多少钱都设置了限制。

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 令牌和 preferences 存储都是只读的,而且环境只能访问其允许列表上的主机。植入的指令仍然可以改变简报的内容,包括通过代理在多次运行之间保留的备注来实现。它不能向 GitHub 写入,也不能修改你的规则。

Slack 是个例外:发帖使用的是同一个令牌,所以只在机器人需要读取或发帖的地方邀请它。

根据真实运行情况设置支出上限

支出上限可以保护你免于失控的成本。先把它设为一次正常运行成本的三到五倍,然后在你看到真实数字后再逐步收紧。一次运行达到上限时会暂停,而不是失败,所以把上限设得过低,看起来就像一份简报突然没了动静。这个上限就是 deployment.md 里的 budget。每次运行都会获得这整笔额度,而达到额度的运行会以 budget_reached 停止原因暂停:

代码YAML

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

入门

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

  • 从书签读取每个来源,而不是根据固定时间窗口读取。
  • 将读取失败报告为不可读,绝不要报告为平静的一天。
  • 在发帖前重新检查每一项内容。
  • 只有在 Slack 确认后,才将一条帖子计为已发送,然后更新书签和账本。
  • 每次运行时都重新读取你的偏好设置,来源应是代理无法编辑的存储。
  • 在代理只需要读取的地方,只给它只读权限,并限制每次运行的可支出额度。

Claude Code 可以带你逐步了解本文提供的指导。首先,更新:

代码Shell

claude update

然后,使用 claude-api 技能:

提示词

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

claude-api 技能会读取这篇文章,提出一套设置方案,把文件写入你项目中的 agents/ 文件夹,并使用 ant apply 创建这些资源。请将其视为一个起点,并根据你的来源、目标位置或记忆偏好自定义该代理。

来源:Claude Developer Blog · claude.dev