智能体 · CLAUDE

CLAUDE发布日期 2026-10-08 · 阅读约 9 分钟

构建有效的 Agent 自动化

*参考实现逐步讲解与常见故障模式*

原作者/来源:claude.dev

原文链接:https://claude.dev/blog/building-effective-agent-automations/

参考实现逐步讲解与常见故障模式

AI 让工作推进得更快,我们也越来越难跟上。在 Anthropic 内部,大家经常借助简单的 Agent 自动化来辅助工作。它们通常按计划运行,在后台收集上下文,并主动告诉我们需要知道的信息。但要构建有效的 Agent 自动化并不容易:它们可能在无人察觉的情况下失去对某个来源的访问权限,也可能无法遵循我们的偏好。

我们使用 Claude Managed Agents(beta)构建了一个参考实现:它按计划读取自定义来源(例如 Slack 和 GitHub 代码仓库),跟踪自上次运行以来的变化,并把你需要了解的信息发布出来(例如发到 Slack)。本文会逐步讲解每个环节,分享一个参考实现,并提供一条可在 Claude Code 中运行、为你配置 Agent 的命令。

获取代码

参考实现见此处。要交互式地跟着操作,请在 Claude Code 中运行下面的命令。claude-api skill 可以按照本文的指导帮助设置 Agent:

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

要使用这个参考实现,你需要一个 Slack 应用(根据 manifest 创建)和一个 GitHub token。下面展示的文件是 Claude API 资源的配置,其中包括 Agent、其环境、记忆存储、凭据库和部署。

daily-brief/
├── agent.md                      模型、工具、指令
├── deployment.md                 计划、时区、预算、首条消息
├── environment.yaml              网络允许列表
├── memory_store_preferences.yaml 用户偏好
├── memory_store_state.yaml       Agent 的书签、账本、备注和运行记录
├── vault.yaml                    保存凭据的凭据库
├── claude-lock.json              资源 ID,由 ant apply 写入
└── slack/manifest.yaml           一个机器人应用

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

我们会在下面的各个部分使用这条命令。配置完成后,自动化会按计划在 Anthropic 的基础设施上运行,因此你的机器不必一直开着。

概览

我们要构建的 Agent 有六个组成部分,本文会按以下顺序介绍:

  • 来源——一个命名列表,列出要读取的位置
  • 目的地——Agent 可以写入的一个位置
  • Agent——agent.md 中的模型、工具和运行步骤
  • 计划——一个 cron 计划
  • 记忆——你的偏好和 Agent 自己的记忆
  • 护栏——凡是 Agent 只读取的地方,都只授予只读访问权限;并为每次运行设置支出上限

Agent 自动化总览图

图示标签:

  • 计划(Schedule):已排程的部署;触发 Agent(wake)。
  • 来源(Sources):只读;通过 MCP 或 HTTPS 访问;凭据来自凭据库。Agent 读取来源(read)。
  • Agent:模型与提示词;运行循环和判断规则。
  • 护栏(Guardrails):限制轮次、时间和支出;设置有人监看的心跳检查。
  • 已发布?(Already posted?):检查内容是否已发布。
  • 目的地(Destination):预先批准的一个工具和一个位置。Agent 向此发布(post),并将简报交付给读者(deliver the brief)。
  • 记忆(Memory):包含 State 和 Preferences。Agent 读取和写入 State(read / write):按来源保存书签、已说过的内容,以及每次运行的日志。读者写入 Preferences,Agent 读取它:范围、篇幅、通知对象、静默日和停止条件。
  • 读者(Reader):接收简报并写入偏好(write)。

来源

Agent 默认读取两个来源:Slack 频道和 GitHub 拉取请求。频道和代码仓库列在你的 preferences 文件中。这个模板可以扩展,以使用其他来源。

来源部分示意图

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

在 Managed Agents 中,凭据保存在凭据库里。Agent 可以引用这些凭据,但真实值留在凭据库中,位于 Claude 运行代码的沙箱之外(参见 Anthropic 的相关文章和 X 上的帖子):

  • MCP 服务器(GitHub)。Agent 通过在沙箱外运行的代理来调用 MCP 工具。代理会查找 URL 与该服务器匹配的凭据库凭据。
  • Shell(Slack)。Agent 在沙箱内部使用 bash 工具和 curl 调用 Slack API。沙箱中只有一个不透明的占位符 $SLACK_BOT_TOKEN。请求离开沙箱时,平台会针对你允许的主机将真实 token 替换进去。

使用 ant CLI 和代码仓库中的模板文件创建凭据库:

ant apply vault.yaml

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

const vaultId = process.env.VAULT_ID!; // 凭据库 ID,见 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 复制到部署文件 deployment.md 里的 vault_ids。

从上次停下的位置继续读取

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

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

别把读取失败当成什么都没发生

如果 MCP 服务器宕机或其 token 已过期,运行仍会启动,只是没有该服务器的工具。会话会记录一条错误,但 Agent 看不到来自该来源的内容,并会报告“没有新内容”。

agent.md 中的三条规则有助于解决这个问题。当某个来源读取失败时,Agent 会:保留该来源当前的书签;根据其他来源撰写简报;并在简报末尾用一行说明无法读取的内容(“本次运行无法获取拉取请求”),让读者知道这一情况。

目的地

目的地部分示意图

我们的模板会向一个 Slack 频道发布内容,每次运行都会在那里发一条带日期的消息。

Agent 使用沙箱中的 bash 工具向 Slack 发帖,使用的就是它读取时用的同一个机器人 token。

发帖不需要任何人批准。Agent 通过 bash 命令发送消息,内置 bash 工具默认无需请求批准即可运行。slack.com 也在 Agent 环境(即它运行所在的沙箱)的允许列表中。发帖只需一个请求:

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 因为不确定第一条消息是否发出而再次发帖,读者就会看到两次相同的简报。

agent.md 中的三条规则可以避免这种情况。第一,Agent 会在频道最近的消息中查找今天的标题,如果这一期已经在那里,就不会再次发帖。第二,只有当 Slack 返回 "ok": true 和一个消息 ts 时,这条消息才算发送成功。第三,只有在得到确认后,Agent 才会更新账本和书签。如果结果不明确,它会把本次运行标记为“可能已发出”(maybe posted),除此之外不做任何更改,因此不会丢失任何内容。

Agent 会在记忆存储中保留每次运行的记录(runs/<date>.md)。它会在发帖前将运行标记为 posting,然后标记为附有消息 ID 的 posted,或者标记为 maybe posted。

Agent

在 Claude Managed Agents 中,Agent 是一份带版本管理的配置,包括模型、系统提示词和工具。每次运行都会按其运行步骤执行,然后停止。

Agent 部分示意图

在我们的参考实现中,Agent 配置文件是 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
---

[八个编号的运行步骤;完整文本见代码仓库中的 agent.md。]

frontmatter 提供 Agent 名称、模型、工具和 MCP 服务器。正文提供 Agent 指令。MCP 工具默认会请求批准,但此处无人可以批准,因此 GitHub 工具集被设为 always_allow,而 GitHub token 是只读的。

让简报保持简短

agent.md 会引导 Claude 保持简洁:

4. Decide. 当读者今天会据此采取行动,或这会改变他们即将做出的决定时,这条内容才值得占一行。不确定时就省略。多数时候只有几条,有时一条也没有。数量统计(“有 12 个待审查项”)不算一条内容;把受阻的那些链接出来。已经记在账本中且仍未解决的事项,作为一条带标记的内容延续报告(“仍在等待,第 3 天”),不要重新报告;已关闭的事项则直接删除,不必说明。不要重新提起偏好文件中已排除的话题。

在发帖前重新检查仍未解决的事项

Agent 读取来源和发帖之间,条目状态可能发生变化。就在发帖之前,agent.md 会指示 Agent 重新检查每条内容的实时状态:

5. Verify. 你阅读期间,情况已经发生变化。对于你将要报告的每条内容,都要在发帖前立即重新检查其实时来源:如果你读取后它已经解决,就删掉;如果它仍未解决但情况有变,就修改这条内容;如果无法确认,就删掉它,并将它列入运行记录的 cuts。一条过时的“还在等你处理”比漏掉十条内容更损害信任,所以不要含糊描述事项状态:要么明确断言,要么删掉。

所有链接都直接从来源自身的链接字段复制(例如拉取请求的 html_url 或 Slack 永久链接),绝不手动拼接。

计划

在 Claude Managed Agents 中,Agent 只是一个配置文件;部署负责运行它。部署会指定 Agent、环境以及每次运行时的第一条消息。它还包含计划、凭据库、记忆存储和预算。每次计划触发时,平台都会启动一个全新的 Agent 会话。

计划部分示意图

在我们的模板中,部署写在 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_...] # 在“来源”部分创建的凭据库
resources:
- path: ./memory_store_preferences.yaml
  access: read_only
  instructions: 读者的偏好。每次运行都重新读取。不要在此写入。
- path: ./memory_store_state.yaml
  access: read_write
  instructions: 你的状态。书签、账本、备注、提议和运行记录。
---
写下今天的简报。
读者的时区是 America/New_York。每个日期都按这个时区计算。

按顺序执行你的运行步骤。今天这一期的标题是“Daily brief, <weekday> <month> <day>”。

这会根据文件路径创建部署,并创建其中指定的 Agent、环境和记忆存储。

ant apply deployment.md

要在不等待计划触发的情况下测试,可以使用 ant beta:deployments run --deployment-id <id> 手动启动一次运行;<id> 使用 claude-lock.json 中的 ID。

按你的时区计算日期

一个常见错误是,Agent 把今天早上称作“昨天”,因为它使用服务器时区计算日期。在 deployment.md 中,timezone 字段设置计划何时触发,而正文第二行则告诉 Agent 计算日期时要使用哪个时区。

记忆

每次运行都会在一个全新的沙箱中启动,不记得上次运行的内容。没有记忆,反馈就无法持续生效。不过,过时的记忆也会让 Agent 混淆:某个事项已经解决,它却仍报告该事项正在等待处理;或者某个事项仍未解决,它却因为“已经报告过”而将其遗漏。

记忆部分示意图

我们的模板保留两个记忆存储,它们是挂载在 /mnt/memory/ 下的文件夹(见“来源”):

  • preferences(属于你,Agent 只读):要读取哪些频道和代码仓库、要排除哪些内容、长度上限、目的地,以及何时停止。
  • state(属于 Agent,读写):书签、已报告内容的账本、每次运行的一条记录、它提议对你的偏好所做的更改,以及关于各个来源行为的备注(“只返回最新的 50 条内容”)。

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

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

一个常见问题是把偏好的副本直接写进提示中,这样一来,即使你已经更改规则,Agent 仍会继续应用旧规则。让 Agent 每次运行时都重新读取文件。如果它无法读取该文件,就应该停止并说明情况,而不是按照默认值运行。

记录已报告的内容,并报告变化

Agent 会在 ledger.md 中记录每一条已经报告的内容,避免简报重复。每一行记录该条内容的报告时间、来源、不会变化的 ID(Slack 消息时间戳或拉取请求编号),以及它最后一次已知的状态:

2026-09-09 slack:C0123456789 1788963600.000100 退款讨论串:客户正等待处理决定
2026-09-11 github 481 审查受阻,第 2 天(仍在等待)
2026-09-11 slack:C0234567891 1789117333.000300 企业升级处理:已指定负责人,正在处理中

护栏

由于我们的自动化按计划在“后台”运行,我们会限制 Agent 能做什么以及能花多少钱。

护栏部分示意图

限制它能做什么

Agent 会读取其他人写的消息和问题,而这些文本可能会被当作指令。要限制 Agent 如果遵循这些指令所能做的事。在我们的示例中,GitHub token 和 preferences 存储都是只读的,环境只能访问允许列表中的主机。恶意植入的指令仍然可能改变简报内容,包括通过 Agent 在两次运行之间保存的备注来影响内容。但它无法写入 GitHub,也无法修改你的规则。

Slack 是个例外:同一个 token 负责发帖,因此只把机器人邀请到它需要读取或发帖的位置。

根据实际运行设定支出上限

支出上限可以保护你免受成本失控的影响。先把上限设为一次正常运行费用的三到五倍,然后随着你看到真实数字再逐步收紧。运行达到上限时会暂停,而不是失败,因此,上限太低时,简报看起来就像突然停更。上限就是 deployment.md 中的 budget。每次运行都能使用全部预算;运行达到预算时会暂停,并以 budget_reached 作为停止原因:

budget:
  type: limit
  max_list_cost:
    amount: "500" # 一个字符串,单位为美分:"500" 即 $5.00
    currency: USD

开始使用

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

  1. 从书签继续读取每个来源,不要使用固定时间窗口。
  2. 将读取失败报告为无法读取,绝不要说成今天没有新内容。
  3. 发帖前立即重新检查每一条内容。
  4. 只有 Slack 确认发帖成功,才算消息已发送;然后更新书签和账本。
  5. 每次运行都从 Agent 无法编辑的存储中重新读取你的偏好。
  6. 对 Agent 只读取的内容授予只读访问权限,并限制每次运行的支出。

Claude Code 可以按照本文提供的指导带你完成操作。首先,更新:

claude update

然后,使用 claude-api skill:

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

claude-api skill 会读取这篇文章,提出一套设置方案,在你的项目中的 agents/ 文件夹写入文件,并通过 ant apply 创建资源。把这当作起点,然后根据你的来源、目的地或记忆偏好自定义 Agent。