通过 AWS_SDK_UA_APP_ID 环境变量在 CloudTrail 中标记 Agent 调用,Bedrock 推理本身也会被记录,零成本实现全链路可见性。
Layer 2 — Label:让 CloudTrail 中的 Agent 流量可见
这一层成本低,容易被忽视,价值也正如其名:支持遥测,而非强制执行。
AWS_SDK_UA_APP_ID 是一个官方支持的设置,它会将一个应用标识符附加到 AWS CLI 和支持的 AWS SDK 发出的调用上。AWS 文档说明客户可以用它来识别是哪个应用程序发出了一组调用。这也让它成为一个用于 AI Agent 的尽力而为的取证标签。
export AWS_SDK_UA_APP_ID="ai-agent_mydepartment"
它也可以作为 sdk_ua_app_id 写在共享的 ~/.aws/config profile 中。无论哪种方式,由支持的客户端携带该标识发出的调用,在 CloudTrail 的 userAgent 字段中都会包含 ai-agent_mydepartment——而且它覆盖的范围不只是 shell 活动:如果你的 Agent 用 Claude Code 对接 Bedrock,它们自身的模型调用也是 AWS API 调用,所以推理流量也会带上这个标签。(已验证:设置了 app ID 的 Bedrock 模型调用在 CloudTrail 的 Converse 事件中会携带它。Converse 被记录为管理事件,所以即使在默认的 trail 上也会出现。)粗糙但有效:
在机器级别设置一次即可,无需每个工具单独配置。通过 MDM / 设备管理方式部署它,这样每台托管笔记本上的 Claude Code、Codex 或 OpenClaw 进程都会继承它。对于 Claude Code,具体做法是在 ~/.claude/settings.json——或者项目的 .claude/settings.json,或者 MDM 下发的组织托管设置——中加入一个 env 块,为每个会话设置它:{"env": {"AWS_SDK_UA_APP_ID": "ai-agent_mydepartment"}}。Agent 随后必须主动移除它,而这是大多数工具不会费心去做的摩擦。
了解标签会在哪里失效。广泛支持但非 universal:旧版 SDK 大版本(Go 1.x、Java 1.x、JavaScript 2.x)没有实现它,非 SDK 工具则携带自己的 User-Agent——例如 Terraform 的 AWS provider 使用 TF_APPEND_USER_AGENT。app ID 有 50 字符上限,且对特殊字符有明确规定。未携带标签的调用意味着"此客户端不参与",而不是"这是人类发出的"。如果流量经过 AWS MCP 服务器,AWS 会从请求路径中设置 aws:CalledViaAWSMCP / aws:ViaAWSMCPService——这是调用者无法简单删除的信号,尽管它只覆盖那条路径。
把它当作遥测,而非控制手段。AWS_SDK_UA_APP_ID 只是一个环境变量——Agent 可以取消设置它或覆盖它。AWS 警告调用者控制着 aws:UserAgent,因此不能用它来阻止未授权的直接请求。用这个标签来观察,绝不要用来授权。缺失标签是异常发生的信号,不是请求来自人类的证明。我在写这篇文章时测试了控制形状的版本:一个权限边界,在 Bot 的 User-Agent 出现时明确拒绝 KMS 读取——它的表现与文档完全一致,直到调用者移除一个环境变量,或者简单地发送一个不同的 app ID。这就是整个论点,一次实验足以说明。
CloudTrail 变得可过滤了。你现在可以用一个简单查询回答"这个 Agent 在做什么?",而不用猜测。

用 MDM 滚动部署标签
一个显而易见的反对意见:"我们不可能让 200 个开发者都去 export 一个环境变量。"说得有理。但如果组织已经在管理笔记本电脑,每个主要的编码工具都有托管配置通道——它们对这个特定技巧的支持程度差异比你想象的要大。
Claude Code 有最干净的路径。托管设置(由 MDM 下发、系统目录中的 managed-settings.json,或管理员控制台下发的服务器托管设置)无法被用户文件覆盖,它们支持一个 env 块:
{
"env": {
"AWS_SDK_UA_APP_ID": "ai-agent_mydepartment"
}
}
因为托管设置位于优先级栈的顶部,每个会话都会获得这个变量,不管用户是否要求。如果你在 Claude Team/Enterprise 计划中并标准化使用 Claude Code,这是十分钟就能完成 rollout 的事。
Codex 走的是另一条路。它的 shell_environment_policy 控制它向生成的命令传递哪些环境变量,并且它可以直接注入值:
[shell_environment_policy]
inherit = "core"
set = { AWS_SDK_UA_APP_ID =
"ai-agent_mydepartment" }
这个配置可以通过托管配置传递——在 macOS 上通过 MDM profile 在 com.openai.codex 偏好域中(config_toml_base64),在 Linux/macOS 上通过 /etc/codex/managed_config.toml,或者作为工作区云托管的 requirements。一个诚实的警告:托管默认值只是默认值——用户可以在会话中途更改它们,客户端在下次启动时会重新应用它们。(强制策略在 requirements.toml 中,它控制的是 approval/sandbox 设置而非环境变量。)所以这是一个强默认,不是锁。
Cursor 是比较别扭的一个。它支持 MDM 策略(允许的团队 ID、扩展、更新模式、工作区信任),你可以推送一个托管的 ~/.cursor/permissions.json 来控制终端和 MCP 允许列表——但这个策略文件只接受静态 JSON,且它的文档明确对"策略中的环境变量"回答了"否"。它的 Agent 没有文档化的托管-env 机制。
GitHub Copilot 介于两者之间:企业托管设置存在(.github-private 仓库中的 managed-settings.json,或通过 registry/macOS 托管偏好原生 MDM 下发),但它们针对的是 AI 设置如模型选择——而不是向 Agent 运行的命令中注入环境变量。
所以,回答这个显而易见的问题:如果你的公司标准化使用一个编码计划,这是完全可以实现的——Claude Code 和 Codex 都有文档化的、通过 MDM 传递的途径,让标签进入 Agent 的调用中。在混合集群上你逐个工具去做,在 Cursor/Copilot 上你退回到在 OS/shell 级别设置变量,并接受它不会在每个工具层面被强制执行。即使在强制执行的地方,记住它能给你带来什么:更好的遥测。标签仍然由调用者控制,上面的实验就是证明。
示例——查找过去一小时内所有由 Agent 发起的调用:
START_MS=$(( ($(date +%s) - 3600) * 1000 ))
aws logs filter-log-events \
--log-group-name "CloudTrail/DefaultLogGroup" \
--filter-pattern \
'{ $.userAgent = "*ai-agent_mydepartment*" }' \
--start-time "$START_MS"
使用 JSON 模式而非裸引号形式。引号形式在事件中任意位置匹配字符串——包括 requestParameters——而 $.userAgent 选择器只针对实际携带标签的字段。(两种形式在写这篇文章时都对测试日志组运行过:在一个也在参数中回显标签的事件上,引号形式返回了误报,而 JSON 选择器没有。)SDK 在 User-Agent 字符串中将 app ID 渲染为 app/ai-agent_mydepartment,所以需要子字符串匹配。filter-log-events 的分页大小是 1 MB——繁忙的一小时可能需要在信任计数之前需要一个 nextToken 循环。该命令假设你的 trail 配置为交付到 CloudWatch Logs 组;使用你实际的日志组名。对于全集群查询,CloudTrail bucket 上的 Athena 表是更好的选择。
接下来:那一层在以上全部失效时仍然有效——一个 SCP 后盾,即使管理员会话也无法覆盖。
AWS SDKs and Tools — Application ID (AWS_SDK_UA_APP_ID)
Amazon Bedrock — Log API calls using AWS CloudTrail (Converse / InvokeModel are management events)
AWS Identity and Access Management — aws:UserAgent condition key (warning: caller-provided, not for authorization)
AWS Identity and Access Management — Global condition keys (aws:CalledViaAWSMCP, aws:ViaAWSMCPService)
Claude Code — Settings (env block, managed settings)
Codex — Advanced configuration (shell_environment_policy)
Codex — Managed configuration (MDM, managed_config.toml)
Cursor — Deployment patterns (MDM policies, permissions.json)
GitHub — Enterprise managed settings for Copilot
Terraform AWS Provider — Custom user-agent information
AWS CLI — filter-log-events time range