Anthropic 用 Claude Managed Agents 搭建定时智能体自动化
Building effective agent automations
Anthropic 发布基于 Claude Managed Agents(beta)的参考实现,让智能体按计划读取 Slack 和 GitHub 等自定义来源、追踪上次运行后的变化,并把要点推送到 Slack。
Anthropic 官方给出定时智能体的六条工程规则和可运行参考实现,可迁移到自建自动化流程。
随着 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 token。所提供的文件(如下所示)是 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 appant apply 是 ant CLI 中的一条命令,它会读取这些文件,在你的 Claude API 工作区(平台存储和运行它们的地方)中创建资源,并将 ID 记录在 claude-lock.json 中。
我们将在以下各节中使用此命令。配置完成后,该自动化会在 Anthropic 的基础设施上按计划运行,因此你的机器上无需保持任何进程运行。
概述
我们将构建的智能体有六个组件,按以下顺序介绍:
- 来源 - 要读取的位置的命名列表
- 目标位置 - 智能体可以写入的一个位置
- 智能体 - agent.md 中的模型、工具和运行步骤
- 计划 - 一个 cron 计划
- 记忆 - 你的偏好和智能体自身的记忆
- 护栏 - 在智能体仅进行读取的地方使用只读访问权限,以及每次运行的支出上限

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

为智能体提供其专属的、限定范围的凭证
使用 Managed Agents 时,凭证存放在保险库中。智能体可以引用这些凭证,但真实值保留在保险库中,位于 Claude 代码运行的沙箱之外(参见此处和此处):
-
MCP 服务器(GitHub)。智能体通过一个在沙箱外运行的代理调用 MCP 工具。该代理会查找 URL 与服务器匹配的保险库凭证。
-
Shell(Slack)。智能体在沙箱内使用 bash 工具通过 curl 调用 Slack API。沙箱中仅保存一个不透明的占位符
$SLACK_BOT_TOKEN。当请求离开沙箱时,平台会为你允许的主机替换为真实 token。
使用 ant CLI 和仓库中的模板文件创建保险库:
代码Shell
ant apply vault.yaml这会在你的 Claude API 工作区(平台存储它的地方)中创建保险库,并将其 ID 记录在 claude-lock.json 中。然后使用 TypeScript SDK 将每个凭证添加到保险库中。以下是一个添加 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 的 memory store 中:这是一个文本文件文件夹,平台会将其挂载到每次运行的沙箱中的 /mnt/memory/ 下,并在运行之间保留。agent 使用其普通文件工具读写它,agent.md 中的指令告诉它如何操作。
不要把读取失败误认为风平浪静
如果某个 MCP 服务器宕机或其 token 已过期,运行仍会启动,只是没有该服务器的工具。会话会记录一个错误,但 agent 从该来源看不到任何内容,并报告“没有新内容”。
agent.md 中的三条规则有助于解决这个问题。当某个来源失败时,agent 将:保持该来源的书签不变,用其他来源撰写简报,并在简报末尾用一行说明它无法读取的内容(“本次运行无法获取 pull requests”),以便让读者知晓。
目标
我们的模板发布到一个 Slack 频道,每次运行时发布一条带日期的帖子。

agent 使用其沙箱中的 bash 工具发布到 Slack,使用与读取时相同的 bot token。
帖子无需任何审批。agent 通过 bash 命令发送它,而内置的 bash 工具默认无需请求审批即可运行。slack.com 也在 agent 的 environment(它运行的沙箱)的允许列表中。该帖子是一个请求:
CODEShell
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 和消息 ts 时,帖子才算已发送。第三,agent 只有在确认之后才更新账本和书签。如果结果不明确,它会将该次运行标记为“可能已发布”,不做其他更改,这样就不会丢失任何内容。
agent 在其 memory store(runs/<date>.md)中保留运行记录。它在发布前将运行标记为“posting”,然后标记为“posted”并附上消息 ID,或标记为“maybe posted”。
AGENT
在 Claude Managed Agents 中,agent 是一种带版本的配置:一个模型、一个系统提示和若干工具。每次运行都遵循其运行步骤然后停止。

在我们的参考实现中,agent 配置为 agent.md:
CODEMarkdown
--- 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 服务器。正文提供 agent 指令。MCP 工具默认请求审批,而没有人可以给予审批,因此 GitHub 工具集设置为 always_allow,GitHub token 为 read-only。
保持简报简短
agent.md 引导 Claude 力求简洁:
CODEText
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 读取来源到发布之间可能发生变化。就在发布前,agent.md 会指示 agent 重新检查每个条目的实时状态:
CODEText
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 时,agent 只是一个配置文件;由 deployment 来运行它。deployment 指定 agent、environment 以及每次运行的起始消息。它还持有 schedule、vault、memory stores 和 budget。每当 schedule 触发时,平台都会启动一个全新的 agent session。

在我们的模板中,deployment 被记录在 deployment.md 中,起始消息作为其正文:
CODEMarkdown
--- 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,并带上它按路径指定的 agent、environment 和 memory stores。
CODEShell
ant apply deployment.md若不想等待 schedule 就进行测试,可用 ant beta:deployments run --deployment-id <id> 手动启动一次运行,使用 claude-lock.json 中的 ID。
按你的时区计算日期
一个常见的 bug 是 agent 把今天早上称为“昨天”,因为它按服务器的时区计算日期。在 deployment.md 中,timezone 字段设定运行触发的时间,正文第二行则告诉 agent 使用哪个时区来计算日期。
MEMORY
每次运行都从一个全新的沙箱开始,对上一次运行毫无记忆。没有记忆,反馈就无法留存。然而,过时的记忆也可能误导 agent:它会把已解决的条目报告为仍在等待,或者因为“已经报告过”而漏掉一个仍未关闭的条目。

我们的模板保留两个 memory stores,即挂载在 /mnt/memory/ 下的文件夹(见 Sources):
-
preferences(你的,对 agent 只读):读取哪些频道和仓库、要排除什么、长度上限、目标位置,以及何时停止。
-
state(agent 的,可读写):书签、它报告过内容的账本、每次运行一条记录、它提议对你 preferences 所做的更改,以及关于每个来源行为方式的备注(“只返回最新的 50 个条目”)。
ant apply deployment.md 会创建 preferences store,但不会创建其中的文件。首次运行前,请用仓库中的 scripts/seed-preferences.sh 把 preferences.md 写入那里。
每次运行开始时重新读取你的 preferences
一个常见问题是把 preferences 的副本固化进 prompt,导致它继续应用你已经修改过的规则。让 agent 每次运行都重新读取该文件。如果它无法读取该文件,应当停止并说明,而不是按默认值继续运行。
保留一份已报告内容的账本,并报告变更
agent 会保留一份账本 ledger.md,记录它报告过的每个条目,这样简报就不会重复。每一行记录该条目被报告的时间、来源、一个不变的 ID(Slack 消息时间戳或 pull request 编号),以及其最后已知状态:
CODEText
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 progressGUARDRAILS
由于我们的自动化是按 schedule 在“后台”运行的,我们对 agent 能做什么以及能花费什么设置了限制。

对它可做之事的限制
agent 会读取其他人写的消息和 issue,而这些文本可能被解读为指令。要限制 agent 在听从这些指令后可能做的事。在我们的示例中,GitHub token 和 preferences store 是只读的,environment 也只能访问其 allowlist 上的主机。植入的指令仍可能改变简报的内容,包括通过 agent 在运行之间保留的备注。但它无法写入 GitHub 或修改你的规则。
Slack 是例外:同一个 token 既能发帖,所以只把机器人邀请到它需要读取或发帖的地方。
根据真实运行设定支出上限
支出上限能保护你免于成本失控。先从正常一次运行成本的三到五倍开始,等你看到真实数字后再收紧。一次运行触及上限时会暂停而不是失败,所以上限设得太低看起来就像简报突然没了声音。上限就是 deployment.md 中的 budget。每次运行都会获得全额额度,而触及上限的运行会以 budget_reached 停止原因暂停:
CODEYAML
budget: type: limit max_list_cost: amount: "500" # a string, in cents: "500" is $5.00 currency: USD
入门指南
我们的参考实现归结为六条规则:
- 从书签读取每个来源,而不是固定的时间窗口。
- 把读取失败报告为不可读,绝不要报告为平静无事的一天。
- 发帖前重新检查每一项内容。
- 只有当 Slack 确认后才把帖子计为已发送,然后更新书签和账本。
- 每次运行都重新读取你的偏好设置,且从 agent 无法编辑的存储中读取。
- 在 agent 只读取的地方给它只读访问权限,并限制每次运行可花费的额度。
Claude Code 可以带你了解本文提供的指导。首先,更新:
CODEShell
claude update然后,使用 claude-api 技能:
PROMPT
/claude-api managed-agents-onboard https://claude.dev/blog/building-effective-agent-automations/claude-api 技能会读取这篇文章,提出一套配置方案,把文件写入你项目的 agents/ 文件夹,并用 ant apply 创建资源。把这当作起点,并根据你的来源、目标或记忆偏好来定制这个 agent。
来源:Claude.dev 开发者博客 · claude.dev