SDK 与 Headless:把 Claude 变成你的自动化引擎

0 阅读18分钟
  1. 软件开发行业的 AI 范式演进:从辅助工具到工程智能体
  2. Claude Code 全景入门:装好、看懂、跑起来
  3. 上下文注入:用 @ 和 ! 精准喂给 Claude 它需要的信息
  4. 记忆系统:用 CLAUDE.md 告别每次对话都要"重新认识你"
  5. 斜杠命令:把你的重复操作变成一个单词
  6. Hooks 机制:让 AI 的每一步都在你的规则里
  7. MCP:给 Claude Code 接上"外设"
  8. Skills:让 Claude 按需加载你的领域知识
  9. Sub-agents:给 Claude 分身,让专家各司其职
  10. Agent Teams:组建你的 AI 开发小队
  11. 安全与回退:给 AI 戴上"安全带"
  12. SDK 与 Headless:把 Claude 变成你的自动化引擎
  13. 上下文工程:从记忆文件到分层知识架构
  14. 模型选择与成本控制:把每一分钱花在刀刃上

qrcode_for_gh_6219b0488be5_258.jpg

前面一直在讲怎么在终端里和 Claude Code 交互——打字、确认、看结果。这个模式适合探索性的开发工作,但软件工程里还有大量需要无人值守、重复执行的任务:

  • 每次提 PR 自动跑一遍代码审查
  • 每天凌晨分析前一天的错误日志
  • 构建流水线里自动检查安全漏洞
  • 批量处理几十个文件的格式统一

这些场景不需要人坐在终端前一问一答。需要的是把 Claude Code 的能力当成一个可编程的函数来调用——给输入,拿输出,自动退出。

Claude Code 提供了两种方式实现这个目标:Headless 模式(通过 CLI 的 -p 参数)适合脚本和 CI/CD 集成;Claude Agent SDK(TypeScript / Python 库)适合在你的应用程序里编程调用。

名称变更说明:Anthropic 已将 "Claude Code SDK" 正式更名为 Claude Agent SDK(包名 @anthropic-ai/claude-agent-sdk / claude-agent-sdk)。如果你之前用的是旧包名,参考官方的 Migration Guide 迁移。

Headless 模式:让 CLI 变成自动化工具

-p 参数:一切的起点

加上 -p(或 --print),Claude Code 就从"交互式对话"变成了"执行任务然后退出":

claude -p "分析这个项目的目录结构,列出主要模块"

Claude 执行任务,把结果打印到 stdout,然后退出。没有交互式界面,没有等待输入——就像 grepjq 一样,是一个标准的命令行工具。

管道输入:处理大规模上下文

当输入内容很大时(日志文件、代码片段、测试结果),通过管道传入:

# 分析错误日志
cat /var/log/app/error.log | claude -p "总结主要的错误类型和可能的根因"

# 审查一个 PR 的变更
git diff main...HEAD | claude -p "审查这些代码变更,找出潜在问题"

# 分析测试覆盖率报告
go test -coverprofile=coverage.out ./... && \
  go tool cover -func=coverage.out | claude -p "分析覆盖率报告,指出覆盖不足的模块"

管道传入的内容成为 Claude 的上下文,结合你的 prompt 一起处理。这让 Claude Code 像 awksed 一样嵌入你的 shell 工具链——只是这个"过滤器"具备语义理解能力。

结构化输出:--output-format

纯文本输出对人友好,但对程序不友好。--output-format 解决这个问题:

text(默认):纯文本,直接给人看。

json:任务完成后输出一个完整的 JSON 对象,包含结果、执行时间、token 消耗、成本等元数据。

claude -p "检查 internal/customer/ 目录有没有安全隐患" \
  --output-format json > report.json

输出的 JSON 结构:

{
  "type": "result",
  "subtype": "success",
  "is_error": false,
  "duration_ms": 15230,
  "num_turns": 3,
  "result": "未发现严重安全隐患。有两处建议改进...",
  "session_id": "550e8400-e29b-41d4-a716-446655440000",
  "total_cost_usd": 0.08,
  "usage": {
    "input_tokens": 12500,
    "output_tokens": 890
  }
}

在脚本里用 jq 提取你要的字段:

# 拿结果文本
jq -r '.result' report.json

# 检查是否成功
jq -r '.subtype' report.json

# 看成本
jq '.total_cost_usd' report.json

stream-json:实时流式输出,每一步都立即输出一个 JSON 对象(JSONL 格式)。适合需要实时反馈的场景——比如 CI 日志里想看到 Claude 执行到了哪一步。

claude -p "审查代码" --output-format stream-json --verbose

权限控制

Headless 模式下没有人在终端前点"确认",所以权限控制尤其重要。

推荐做法:通过 --allowedTools 精确指定允许的工具,配合 --permission-mode

# 只读分析——只给读权限
claude -p "分析代码结构" \
  --allowedTools "Read" "Grep" "Glob" \
  --permission-mode default

# 需要跑测试——精确授权
claude -p "运行所有单元测试并分析失败原因" \
  --allowedTools "Bash(go test *)" "Read" "Grep" \
  --permission-mode acceptEdits

如果在严格沙箱环境(容器/VM)里运行,可以用 --dangerously-skip-permissions 跳过所有权限检查,让 Claude 全速自动工作。但只在你确定环境隔离的情况下使用。

更多有用的参数

--max-turns:限制 Agent 的执行轮次。防止 Claude 在某个任务上无限循环。

claude -p "修复 lint 错误" --max-turns 10

--max-budget-usd:设置花费上限。到了就停。

claude -p "全面代码审查" --max-budget-usd 5.00

--model:指定模型。简单任务用 haiku 省钱,复杂任务用 opus。

claude -p "格式化这些文件" --model haiku

--json-schema:让输出严格匹配指定的 JSON Schema。适合需要机器解析的场景。

claude -p "列出所有 API 端点" \
  --json-schema '{"type":"object","properties":{"endpoints":{"type":"array","items":{"type":"object","properties":{"method":{"type":"string"},"path":{"type":"string"},"description":{"type":"string"}}}}}}'

--append-system-prompt:在默认 system prompt 基础上追加指令,保留 Claude Code 的默认能力。

claude -p "审查代码" --append-system-prompt "所有输出用中文"

-c / --resume:继续之前的会话。-c 继续最近一次,--resume 指定会话 ID。

# 第一次运行
claude -p "分析项目架构" --output-format json > step1.json

# 基于前一次继续
SESSION_ID=$(jq -r '.session_id' step1.json)
claude -p "基于刚才的分析,生成重构方案" --resume "$SESSION_ID"

实战:CI 中的自动代码审查

把 Headless 模式用在最典型的场景——GitHub Actions 里的 PR 自动审查。

name: AI Code Review
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Install Claude Code
        run: npm install -g @anthropic-ai/claude-code

      - name: Run AI Review
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          git diff origin/main...HEAD | claude -p \
            "审查这些 Go 代码变更。重点关注:
            1. 是否有安全隐患(SQL 注入、XSS、敏感数据泄露)
            2. 错误处理是否完善
            3. 并发安全性
            输出格式:先列高风险问题,再列建议改进。" \
            --allowedTools "Read" "Grep" "Glob" \
            --output-format json \
            --max-budget-usd 2.00 > review.json

      - name: Post Review Comment
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const report = JSON.parse(fs.readFileSync('review.json', 'utf8'));
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body: `## AI Code Review\n\n${report.result}\n\n---\n_Cost: $${report.total_cost_usd.toFixed(4)} | Turns: ${report.num_turns}_`
            });

这个流水线的关键:

  • git diff 通过管道把增量代码传给 Claude——精确的上下文
  • --allowedTools 只给读权限——CI 里不需要 Claude 改文件
  • --output-format json 拿到结构化结果——可以提取字段、判断是否有高风险问题
  • --max-budget-usd 设置花费上限——防止意外的高成本

你还可以更进一步:用 --json-schema 让 Claude 输出结构化的审查结果,然后在脚本里判断是否有高风险问题——有的话让 CI 流水线失败,强制修复后才能合入。

Claude Agent SDK:在你的程序里编程调用

Headless 模式通过 CLI 调用,适合脚本和 CI。但如果你在开发一个应用程序——比如一个内部的代码审查平台、一个自动化运维工具——需要在代码里直接调用 Claude 的能力,就需要 Agent SDK。

Agent SDK 和 Anthropic 的 Client SDK(直接调 API 的 @anthropic-ai/sdk)不是同一个东西。Client SDK 给你原始的 API 调用——你自己实现 tool loop。Agent SDK 给你完整的 Agent 引擎——内置工具执行、上下文管理、会话持久化,Claude 自己决定怎么用工具完成任务。

# Client SDK:你自己实现 tool loop
response = client.messages.create(...)
while response.stop_reason == "tool_use":
    result = your_tool_executor(response.tool_use)
    response = client.messages.create(tool_result=result, **params)

# Agent SDK:Claude 自己处理工具调用
async for message in query(prompt="修复 auth.py 的 bug"):
    print(message)  # Claude 自己读文件、找 bug、改代码

安装

# TypeScript
npm install @anthropic-ai/claude-agent-sdk

# Python
pip install claude-agent-sdk

需要 Node.js 18+(TypeScript)或 Python 3.10+(Python)。认证方式:ANTHROPIC_API_KEY 环境变量,也支持 AWS Bedrock(CLAUDE_CODE_USE_BEDROCK=1)、Google Vertex AI(CLAUDE_CODE_USE_VERTEX=1)和 Azure AI Foundry(CLAUDE_CODE_USE_FOUNDRY=1)。

内置工具

Agent SDK 自带一套开箱即用的工具,不需要你自己实现:

工具能力
Read读取工作目录下的任何文件
Write创建新文件
Edit精确编辑已有文件
Bash执行终端命令、脚本、git 操作
Glob按模式查找文件
Grep正则搜索文件内容
WebSearch搜索网页获取最新信息
WebFetch抓取并解析网页内容
AskUserQuestion向用户提问澄清问题

通过 allowedTools 控制 Agent 能用哪些工具——权限最小化原则。

query():一次性任务

给一个 prompt,Claude 自主决定使用工具完成任务,流式返回中间过程和最终结果。

TypeScript:

import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "分析 internal/customer/ 目录的代码质量",
  options: {
    allowedTools: ["Read", "Grep", "Glob"],
    permissionMode: "acceptEdits",
  },
})) {
  if (message.type === "assistant") {
    for (const block of message.message.content) {
      if ("text" in block) console.log(block.text);
      else if ("name" in block) console.log(`工具: ${block.name}`);
    }
  } else if (message.type === "result") {
    console.log("结果:", message.result);
    console.log("成本:", message.total_cost_usd);
  }
}

Python:

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage

async def main():
    async for message in query(
        prompt="分析 internal/customer/ 目录的代码质量",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Grep", "Glob"],
            permission_mode="acceptEdits",
        ),
    ):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if hasattr(block, "text"):
                    print(block.text)
                elif hasattr(block, "name"):
                    print(f"工具: {block.name}")
        elif isinstance(message, ResultMessage):
            print(f"结果: {message.result}")
            print(f"成本: {message.total_cost_usd}")

asyncio.run(main())

query() 返回一个 async generator(TS)或 async iterator(Python),流式 yield 消息。消息类型包括:

  • assistant:Claude 的推理和工具调用
  • result:最终结果,包含 resulttotal_cost_usdsession_id

ClaudeSDKClient:多轮对话

维护会话状态,支持多次来回。适合交互式应用、需要上下文延续的场景。

Python(推荐用法——async context manager):

import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, ResultMessage

async def main():
    options = ClaudeAgentOptions(
        allowed_tools=["Read", "Edit", "Glob", "Grep"],
    )

    async with ClaudeSDKClient(options=options) as client:
        # 第一轮:client 自动保存 session ID
        await client.query("分析 auth 模块的架构")
        async for message in client.receive_response():
            if isinstance(message, ResultMessage):
                print(message.result)

        # 第二轮:自动延续同一个 session
        await client.query("基于刚才的分析,重构为 JWT 方案")
        async for message in client.receive_response():
            if isinstance(message, ResultMessage):
                print(message.result)

asyncio.run(main())

TypeScript(使用 continue: true):

import { query } from "@anthropic-ai/claude-agent-sdk";

// 第一轮:创建新 session
for await (const message of query({
  prompt: "分析 auth 模块的架构",
  options: { allowedTools: ["Read", "Glob", "Grep"] },
})) {
  if (message.type === "result") console.log(message.result);
}

// 第二轮:continue: true 自动找到最近的 session 继续
for await (const message of query({
  prompt: "基于刚才的分析,重构为 JWT 方案",
  options: {
    continue: true,
    allowedTools: ["Read", "Edit", "Write", "Glob", "Grep"],
  },
})) {
  if (message.type === "result") console.log(message.result);
}

会话管理:continue / resume / fork

SDK 的会话管理比 CLI 的 --resume 更完整,提供三种模式:

模式场景说明
continue单进程多轮对话自动续接当前目录最近的 session,不需要 ID
resume跨进程恢复指定会话通过 session ID 恢复,适合多用户 / 后台任务
fork保留原始线索、探索新方向复制原始会话历史并分叉,两个 session 独立演进

Fork 是个特别有用的模式。比如 SCRM 里分析了一个客户转化漏斗,想同时尝试两种优化策略——fork 出一个新 session 试 A 方案,原始 session 继续 B 方案,两边互不影响:

# 原始分析
session_id = None
async for message in query(
    prompt="分析客户转化漏斗的瓶颈",
    options=ClaudeAgentOptions(allowed_tools=["Read", "Grep"]),
):
    if isinstance(message, ResultMessage):
        session_id = message.session_id

# Fork:基于同样的分析上下文,尝试不同方案
async for message in query(
    prompt="尝试用标签体系重构方案优化转化率",
    options=ClaudeAgentOptions(resume=session_id, fork_session=True),
):
    if isinstance(message, ResultMessage):
        forked_id = message.session_id  # 新的 session ID

# 原始 session 不受影响,继续另一个方向
async for message in query(
    prompt="尝试用客户生命周期分层方案优化",
    options=ClaudeAgentOptions(resume=session_id),
):
    pass

Session 文件存在 ~/.claude/projects/<编码后的cwd>/<session-id>.jsonl。跨机器恢复需要把这个文件迁移过去,且 cwd 路径要一致。

权限模式

SDK 支持四种权限模式,和 CLI 的权限体系对应:

模式行为适用场景
acceptEdits自动批准文件编辑,其他操作需确认可信的开发工作流
dontAsk(仅 TS)不在 allowedTools 里的直接拒绝无人值守的锁定 Agent
bypassPermissions所有工具无需确认直接执行沙箱/CI 环境
default通过 canUseTool 回调自定义审批需要精细控制的场景

SDK Hooks:Agent 生命周期拦截

SDK 的 Hooks 和 CLI 的 shell 命令 Hooks 不同——它是回调函数,直接在你的代码里定义,可以拦截、阻止、修改 Agent 的行为。

可用的 Hook 事件:

事件触发时机典型用途
PreToolUse工具调用前阻止危险操作、修改输入
PostToolUse工具执行后记录审计日志、追加上下文
StopAgent 停止时保存状态、发通知
NotificationAgent 状态消息转发到 Slack/钉钉
SubagentStart/StopSub-agent 启停追踪并行任务
PermissionRequest权限确认前自定义审批逻辑

一个实际的例子——SCRM 系统里保护敏感文件:

from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher

async def protect_sensitive_files(input_data, tool_use_id, context):
    """阻止修改 .env 和客户数据配置文件"""
    file_path = input_data["tool_input"].get("file_path", "")
    blocked_patterns = [".env", "secrets", "credentials", "customer_keys"]

    if any(p in file_path for p in blocked_patterns):
        return {
            "hookSpecificOutput": {
                "hookEventName": input_data["hook_event_name"],
                "permissionDecision": "deny",
                "permissionDecisionReason": f"禁止修改敏感文件: {file_path}",
            }
        }
    return {}  # 空对象 = 放行

async def audit_logger(input_data, tool_use_id, context):
    """记录所有工具调用到审计日志"""
    with open("./agent_audit.log", "a") as f:
        f.write(f"{input_data['tool_name']}: {input_data['tool_input']}\n")
    return {}

async def main():
    async for message in query(
        prompt="重构客户管理模块",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Edit", "Write", "Glob", "Grep"],
            permission_mode="acceptEdits",
            hooks={
                "PreToolUse": [
                    HookMatcher(matcher="Write|Edit", hooks=[protect_sensitive_files]),
                    HookMatcher(hooks=[audit_logger]),  # 无 matcher = 匹配所有工具
                ],
            },
        ),
    ):
        pass

Hook 回调的返回值决定 Agent 的行为:

  • {} 空对象:放行,不干预
  • permissionDecision: "deny":阻止这次工具调用
  • permissionDecision: "allow":自动批准(跳过权限确认)
  • updatedInput:修改工具输入参数(必须同时返回 permissionDecision: "allow"
  • systemMessage:注入一条消息到对话上下文(Agent 能看到)

优先级:deny > ask > allow。多个 Hook 只要有一个返回 deny,操作就被阻止。

自定义工具和 MCP

SDK 支持连接外部 MCP 服务器,给 Agent 注入额外能力:

# Python:连接 Playwright MCP 让 Agent 能操作浏览器
async for message in query(
    prompt="打开 example.com 并描述页面内容",
    options=ClaudeAgentOptions(
        mcp_servers={
            "playwright": {"command": "npx", "args": ["@playwright/mcp@latest"]}
        }
    ),
):
    if hasattr(message, "result"):
        print(message.result)
// TypeScript:同样的 MCP 连接
for await (const message of query({
  prompt: "打开 example.com 并描述页面内容",
  options: {
    mcpServers: {
      playwright: { command: "npx", args: ["@playwright/mcp@latest"] }
    }
  }
})) {
  if ("result" in message) console.log(message.result);
}

你还可以把自己的业务 API 包装成 MCP 工具。比如在 SCRM 系统里,把客户数据查询暴露给 Agent:

import { query, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";

const scrmTools = createSdkMcpServer({
  tools: {
    get_customer_data: {
      description: "查询 SCRM 系统的客户数据",
      parameters: {
        customer_id: { type: "string", description: "客户 ID" },
      },
      handler: async ({ customer_id }) => {
        const data = await fetchCustomer(customer_id);
        return JSON.stringify(data);
      },
    },
  },
});

for await (const message of query({
  prompt: "查询客户 C001 的跟进记录,分析最近的互动趋势",
  options: { mcpServers: [scrmTools] },
})) {
  if ("result" in message) console.log(message.result);
}

Sub-agents:Agent 里的 Agent

SDK 支持定义专门化的 Sub-agents,由主 Agent 按需委派任务。注意 allowedTools 必须包含 Agent 才能触发 Sub-agent 调用:

from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition

async for message in query(
    prompt="用 code-reviewer 审查这个项目",
    options=ClaudeAgentOptions(
        allowed_tools=["Read", "Glob", "Grep", "Agent"],
        agents={
            "code-reviewer": AgentDefinition(
                description="资深代码审查员,关注质量和安全。",
                prompt="分析代码质量并提出改进建议。",
                tools=["Read", "Glob", "Grep"],
            )
        },
    ),
):
    if hasattr(message, "result"):
        print(message.result)

Sub-agent 内部的消息会带有 parent_tool_use_id 字段,方便追踪哪些消息属于哪个 Sub-agent 的执行上下文。

加载项目配置

SDK 默认不加载 CLAUDE.md、Skills、斜杠命令等项目级配置。需要显式指定 settingSources

const messages = query({
  prompt: "审查代码",
  options: {
    settingSources: ["project"],  // 加载 CLAUDE.md、.claude/skills/、.claude/commands/
  },
});
options = ClaudeAgentOptions(
    setting_sources=["project"],  # 同样效果
)

结构化输出

SDK 支持 JSON Schema 约束输出格式,确保结果可以直接被程序解析:

for await (const message of query({
  prompt: "列出这个项目的所有 API 端点",
  options: {
    jsonSchema: {
      type: "object",
      properties: {
        endpoints: {
          type: "array",
          items: {
            type: "object",
            properties: {
              method: { type: "string" },
              path: { type: "string" },
              description: { type: "string" },
            },
          },
        },
      },
    },
  },
})) {
  if (message.type === "result") console.log(JSON.parse(message.result));
}

Headless vs SDK:怎么选

Headless (-p)Agent SDK
调用方式CLI 命令TypeScript / Python 函数
适合Shell 脚本、CI/CD、一次性任务应用程序集成、复杂工作流
会话管理--resume / -ccontinue / resume / fork
Hooksshell 命令(settings.json)回调函数(可拦截、修改、阻止)
自定义工具通过 MCP config 文件createSdkMcpServer 编程定义
输出处理jq 解析 JSON直接在代码里处理对象
项目配置自动加载 CLAUDE.md需要 settingSources: ["project"]
部署复杂度低(只要装了 Claude Code)中(需要 Node.js/Python 项目)

简单规则:能用 CLI 解决的用 Headless,需要嵌入应用程序的用 SDK。 两者底层引擎完全相同,SDK 额外提供了编程级的 Hooks 拦截和会话 fork 能力。

几条实践建议

从只读开始。 自动化任务的第一版,只给 Read, Grep, Glob 权限。确认效果后再逐步开放写权限。SDK 里用 allowedTools,CLI 里用 --allowedTools

设置花费上限。 无人值守的任务一定要设 --max-budget-usd(CLI)或 maxBudgetUsd(SDK)。CI 里一个 PR 审查 2-5 美元通常够了。忘了设上限可能因为死循环烧掉大量预算。

设置轮次上限。 --max-turns / maxTurns 防止 Agent 无限循环。大多数任务 10-20 轮足够。

用结构化输出做决策。 在 CI 里不要只打印文本——用 --output-format json--json-schema 拿到结构化结果,程序判断是通过还是失败。

会话续接省成本。 多步骤的自动化流程,用 --resume 或 SDK 的 continue 复用前一步的会话,避免重新加载上下文。

用 Hooks 做审计。 SDK 的 PostToolUse Hook 天然适合记录审计日志——每次工具调用都能拿到工具名、参数、结果。比在 CLI 的 json 输出里事后分析更实时。

敏感文件加 PreToolUse 守护。 在 SDK 里用 PreToolUse Hook 拦截对 .envcredentialssecrets 的写操作。这比只靠 allowedTools 更精细——你可以只阻止特定路径,而不是完全禁用 Write 工具。

项目配置别忘了显式加载。 SDK 默认不加载 CLAUDE.md 和 Skills。如果你的项目依赖这些配置,记得加 settingSources: ["project"]


到这里,系列的核心内容全部覆盖了。从第一篇的 CLAUDE.md 记忆系统,到这一篇的 SDK 和自动化集成——Claude Code 从一个交互式工具变成了一个完整的 AI 工程平台。

回顾整个体系:CLAUDE.md 管记忆,斜杠命令管流程复用,Hooks 管硬约束,MCP 管能力扩展,Skills 管按需知识,Sub-agents 管上下文隔离,Agent Teams 管多实例协作,权限 + 沙箱 + Checkpointing 管安全,Headless + SDK 管自动化集成。

不需要全部用上。从你的实际痛点出发——上下文被塞满了?用 Sub-agents。规范老是被遗忘?用 Skills。CI 里想加 AI 审查?用 Headless。按需取用,逐步深入。

下篇见。