Claude Code 是终端原生编码 Agent:它能检查仓库、修改文件、执行命令,并在一个任务中持续维护上下文。这种组合使它比代码补全更强,也比聊天窗口更危险。生产工作流因此必须同时设计两件事:有效的任务循环,以及可以真正执行的权限边界。
本文讲清楚如何在 CLI、Agent SDK、直接 Claude API 与 GitHub Actions 之间选择,怎样编写可审查的任务契约,以及如何在不相信 Agent 自述的前提下验收结果。
核心要点
- 先根据谁负责 Agent Loop,选择 Claude Code、Agent SDK、Client SDK 或托管运行时。
- 当前自托管 Agent SDK 在 TypeScript 和 Python 中以
query()消息流为主要接口;其他语言应调用官方文档化的 CLI 子进程接口。 CLAUDE.md、Prompt 和 Tool 描述只能塑造行为,不能强制安全。- 无人值守任务需要隔离进程、最小化工具、把凭证放在 Agent 边界之外,并限制网络外发。
- 只有独立检查通过且 Diff 经受审查,任务才算完成。
- 评测应关注已验收结果、回归、成本、延迟和越权尝试,而不是演示是否成功过一次。
先选择正确的 Claude 接口
第一项设计决策不是写什么 Prompt,而是谁负责规划、工具、状态与执行。
| 需求 | 接口 | Agent Loop 由谁负责 | 主要边界 |
|---|---|---|---|
| 交互式仓库开发 | Claude Code CLI | Claude Code | 本地进程与用户批准的工具 |
| 自托管 Python 或 TypeScript Agent | Claude Agent SDK | 团队运行的 SDK 进程 | SDK 权限与运行时隔离 |
| 用其他语言实现自定义工具循环 | Claude Client SDK | 业务应用 | 自建状态机和策略层 |
| 从 Go、Java 或 Shell 使用同一 Agent Loop | claude -p 子进程 |
Claude Code | CLI 参数、进程 Sandbox、输出 Schema |
| 在 GitHub Workflow 中运行 Agent | anthropics/claude-code-action |
GitHub Action | 触发策略、GitHub 权限、分支保护 |
这一区分可以避免一种常见错误:直接调用模型 SDK,却误以为它自带 Claude Code 的文件工具、Session、Hook 和权限系统。反过来,如果一次模型请求加上一个很小的确定性工具循环已经足够,也没有必要嵌入完整 Agent SDK。
Anthropic 当前文档把 query() 定义为 TypeScript 与 Python Agent SDK 的主要接口,它返回 Claude Code Agent Loop 产生的消息流。旧文章中常见的 ClaudeAgent 类和 .run() 方法并不是当前 API 契约,不应继续复制到新项目。
委派前先定义任务契约
当任务描述同时包含目标状态和验收证据时,编码 Agent 最可靠。可以固定为四个字段:
- **目标:**最终必须成立的外部行为。
- **范围:**Agent 可以修改的文件、模块、API 和依赖。
- **约束:**兼容性、安全、数据、风格和发布限制。
- **验证:**任务完成前必须通过的命令、断言和评审证据。
例如:
目标:过期的密码重置链接必须被拒绝,同时保持有效链接行为不变。
范围:auth/reset handler、token verifier 与相关测试。
约束:不改 Schema;保持现有响应结构;禁止记录 Token。
验证:目标测试、完整 Auth 测试、Lint,以及仅涉及指定模块的 Diff。
这份契约不只是让 Prompt 更清晰。即使 Agent 采用了意料之外的实现路径,它依然是一张稳定的评审清单。长期有效的规则应进入仓库内受版本控制的文件,一次性验收条件则放在当前任务里。
用 Go 建立可审查的 CLI 边界
Agent SDK 的官方包面向 TypeScript 和 Python。Go 中有文档支持的互操作路径是通过 -p 与结构化输出调用 CLI。下面这段仅使用标准库的程序默认只打印命令,配置了超时、明确工作目录、要求 JSON 输出,并且只允许 Read 工具。确认已安装 claude 且配置 ANTHROPIC_API_KEY 后,再传入 -execute。
package main
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"os"
"os/exec"
"strings"
"time"
)
type resultEnvelope struct {
IsError bool `json:"is_error"`
Result string `json:"result"`
SessionID string `json:"session_id"`
}
func commandArgs(prompt string) []string {
return []string{
"--bare",
"-p",
prompt,
"--tools",
"Read",
"--allowedTools",
"Read",
"--output-format",
"json",
}
}
func run(ctx context.Context, repo, prompt string) (resultEnvelope, error) {
cmd := exec.CommandContext(ctx, "claude", commandArgs(prompt)...)
cmd.Dir = repo
output, err := cmd.CombinedOutput()
if err != nil {
return resultEnvelope{}, fmt.Errorf("claude failed: %w: %s", err, output)
}
var result resultEnvelope
if err := json.Unmarshal(output, &result); err != nil {
return resultEnvelope{}, fmt.Errorf("decode output: %w", err)
}
if result.IsError {
return resultEnvelope{}, errors.New("agent reported an error")
}
return result, nil
}
func main() {
repo := flag.String("repo", ".", "repository directory")
execute := flag.Bool("execute", false, "run Claude Code")
flag.Parse()
prompt := "Summarize the authentication boundary. Do not edit files."
if !*execute {
fmt.Println("claude", strings.Join(commandArgs(prompt), " "))
return
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
result, err := run(ctx, *repo, prompt)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Printf("session=%s\n%s\n", result.SessionID, result.Result)
}
这些选择分别解决不同问题:
--bare避免自动加载项目或用户级 Hook、Plugin、MCP Server、Auto-memory 与CLAUDE.md,需要的上下文由调用方显式传入。--tools Read把可用工具限制为 Read,--allowedTools Read则让该工具无需交互确认即可运行。exec.CommandContext给调用方提供取消边界。- JSON 输出先经过解析,再交给后续系统使用,避免从自然语言中截取字段。
- 工作目录显式指定,不会意外继承另一个仓库。
权限层仍然不等于 Sandbox。高风险或多租户任务应按威胁模型运行在隔离进程、Container、gVisor Sandbox 或虚拟机中。
分清 Session、Hook 与权限
三个机制经常被混在一起:
| 机制 | 解决什么问题 | 不能保证什么 |
|---|---|---|
| Session | 保留对话与 Agent 状态 | 上下文增长或仓库变化后的正确性 |
| Hook | 在生命周期节点运行确定性代码 | Hook 本身拥有宽权限时的安全 |
| 权限规则 | 允许、阻止或要求批准某个工具与命令 | 操作系统隔离,或理解每个副作用的业务语义 |
需要连续上下文时可以使用 Session,但必须同时记录 Session ID 和仓库 Revision。旧 Session 恢复到新 Commit 上时,原有假设可能已经失效。Hook 适合执行格式化、Secret 扫描或命令校验等机械策略,但 Hook 成功不能证明业务行为正确。
权限应围绕能力设计:
- 审查任务只有读取和测试命令,没有仓库写权限;
- 文档任务只能写入有限目录;
- 迁移任务运行在可丢弃环境,并连接可丢弃数据库;
- 发布任务可以提出 Release,但不能批准自己的生产发布。
把 CLAUDE.md 当作上下文,而不是强制控制
CLAUDE.md 的价值在于让稳定的仓库知识在 Session 开始时即可用。一份有效的文件应包含:
- 架构边界与模块所有权;
- 权威的构建、测试、Lint 与生成命令;
- 无法从工具配置中直接看出的代码约定;
- 哪些文件由脚本生成,不能手工修改;
- Agent 声称完成前必须提供哪些证据。
它应足够简短,让关键约束保持显著。若 Linter 或仓库文档已经可以强制执行某项规则,就不要把整份风格指南再复制一遍。
“永远不要读取生产 Secret”只是一句指令。可以执行的版本是:把 Secret 放在 Agent 文件系统之外,通过代理暴露窄操作,并切断直达 Secret Store 的网络路径。
保护运行时、凭证与网络
Agent 会处理不可信的仓库文件、Issue 文本、网页、Tool Result 和生成内容,其中任何一项都可能包含与操作者意图冲突的指令。因此,防御必须下沉到 Prompt 之外。
对高风险部署:
- 只挂载任务所需代码,编辑发生前尽量使用只读挂载。
- 把凭证放在 Agent 边界之外的代理中,而不是暴露原始 Secret。
- 限制外发目的地并校验跳转;仅有 Hostname 白名单未必能阻止所有数据外泄。
- 移除不必要的系统能力,并限制 CPU、内存、进程数和运行时间。
- 将结果写入可审查 Worktree,绝不直接进入受保护分支。
- 记录 Tool 决策和策略结果,但不记录凭证与敏感文件原文。
Anthropic 的安全部署文档明确区分权限解析器与 Sandbox。这一点很关键:允许一个语法熟悉的命令,不等于证明其目标路径或下游副作用安全。
在 GitHub Actions 中避免制造高权限 Prompt 入口
官方 GitHub Action 支持交互式 @claude 请求,也支持在 Workflow 事件上自动运行固定 Prompt。核心风险不在于 Action 能否写代码,而在于谁可以向持有仓库权限的进程提供输入。
一条可防守的 Workflow 应做到:
- 只监听真正需要的事件;
- 依赖 Action 的 Actor 检查,并按仓库风险增加更严格限制;
- 授予最小 GitHub
permissions; - 组织策略要求避免长期 API Key 时,优先考虑 Workload Identity Federation;
- 禁止来自 Fork 的不可信内容进入有写权限的 Job;
- Agent Commit 之后仍强制执行分支保护、测试和人工评审;
- 按依赖策略固定 Action 版本并审查升级。
GitHub 权限和 Claude Tool 权限解决的是不同问题。Claude 只读不能弥补 Workflow Token 被其他 Step 过度使用;GitHub Token 很窄,也不能阻止 Agent 破坏本地工作区。
用验收结果评测编码 Agent
功能演示回答“它是否成功过一次”,生产评测回答“在这些约束下,它有多大概率交付可验收变更”。
版本化任务集应包含:
- 代表性的 Bug 修复、重构、测试、文档和依赖变更;
- 隐藏验收测试或评审 Rubric;
- 干净仓库快照与固定工具权限;
- 带对抗指令的仓库内容和误导性 Issue;
- 应主动升级处理的昂贵、模糊和不可完成任务;
- 至少一个回滚或部分失败场景。
指标必须有明确分母:
| 指标 | 定义 |
|---|---|
| 任务验收率 | 被验收方案数 / 尝试任务数 |
| 回归率 | 看似完成但破坏受保护行为的变更数 / 尝试任务数 |
| 审查负担 | 每个验收任务的评审分钟数与修改轮次 |
| 越权率 | 越权尝试数 / 暴露给该边界的任务数 |
| 单个验收任务成本 | 模型与 Runner 总成本 / 验收任务数 |
| 验收耗时 | 从任务开始到评审接受的实际时长 |
不要只优化完成率。Agent 可以通过削弱测试、扩大范围或隐藏不确定性来提高表面完成度。验收门禁必须独立于 Agent 自己的判断。
生产发布清单
在把 Claude Code 扩展到低风险本地任务之外前:
- 有意识地选择 CLI、Agent SDK、Client SDK 或托管运行时。
- 为每类任务定义目标、范围、约束和验证。
- 最小化工具、文件系统、网络和 GitHub 权限。
- 条件允许时,把凭证留在 Agent 边界之外。
- 明确取消、超时、重试和部分编辑如何处理。
- 记录 Session、模型、策略、仓库 Revision 与验证结果。
- 执行确定性测试并检查 Diff,再接受完成结论。
- 安全、数据、部署和 Schema 变更必须经过独立评审。
- 提升自主性前,先评测代表性任务与对抗任务。
- 模型、CLI、SDK、权限或仓库规则变化后重新评测。
总结
要发挥 Claude Code 的价值,应把它看作一个能力很强的软件进程,而不是会给出正确答案的聊天框。给它精确任务契约、刚好够用的权限、隔离运行时与独立完成证据。Python 或 TypeScript 需要完整 Agent Loop 时使用 Agent SDK,其他语言使用 CLI 子进程,并把直接 API 集成视为另一类架构。这样得到的不只是更多自动化,而是可审查、可复现,也能被安全拒绝的自动化。
一手来源
- Claude Agent SDK 概览
- Claude Agent SDK TypeScript 参考
- 以编程方式运行 Claude Code
- AI Agent 安全部署
- Claude Code GitHub Actions