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 最可靠。可以固定为四个字段:

  1. **目标:**最终必须成立的外部行为。
  2. **范围:**Agent 可以修改的文件、模块、API 和依赖。
  3. **约束:**兼容性、安全、数据、风格和发布限制。
  4. **验证:**任务完成前必须通过的命令、断言和评审证据。

例如:

text
目标:过期的密码重置链接必须被拒绝,同时保持有效链接行为不变。
范围: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。

go
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 之外。

flowchart LR A["不可信任务与仓库"] --> B["隔离的 Agent 运行时"] B --> C["权限与审批门禁"] C --> D["确定性验证"] D --> E["人工评审"] E --> F["受保护的合并或部署"] G["凭证注入代理"] --> B H["Egress 白名单"] --> G

对高风险部署:

  1. 只挂载任务所需代码,编辑发生前尽量使用只读挂载。
  2. 把凭证放在 Agent 边界之外的代理中,而不是暴露原始 Secret。
  3. 限制外发目的地并校验跳转;仅有 Hostname 白名单未必能阻止所有数据外泄。
  4. 移除不必要的系统能力,并限制 CPU、内存、进程数和运行时间。
  5. 将结果写入可审查 Worktree,绝不直接进入受保护分支。
  6. 记录 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 扩展到低风险本地任务之外前:

  1. 有意识地选择 CLI、Agent SDK、Client SDK 或托管运行时。
  2. 为每类任务定义目标、范围、约束和验证。
  3. 最小化工具、文件系统、网络和 GitHub 权限。
  4. 条件允许时,把凭证留在 Agent 边界之外。
  5. 明确取消、超时、重试和部分编辑如何处理。
  6. 记录 Session、模型、策略、仓库 Revision 与验证结果。
  7. 执行确定性测试并检查 Diff,再接受完成结论。
  8. 安全、数据、部署和 Schema 变更必须经过独立评审。
  9. 提升自主性前,先评测代表性任务与对抗任务。
  10. 模型、CLI、SDK、权限或仓库规则变化后重新评测。

总结

要发挥 Claude Code 的价值,应把它看作一个能力很强的软件进程,而不是会给出正确答案的聊天框。给它精确任务契约、刚好够用的权限、隔离运行时与独立完成证据。Python 或 TypeScript 需要完整 Agent Loop 时使用 Agent SDK,其他语言使用 CLI 子进程,并把直接 API 集成视为另一类架构。这样得到的不只是更多自动化,而是可审查、可复现,也能被安全拒绝的自动化。

一手来源

相关资源