简介
Claude Code的本质是"大模型+工具调用循环+有限上下文窗口",到目前为止,工具调用循环(agent loop)的能力边界已经补齐,但循环的每一轮仍然需要人守着终端——回答权限提示、阅读输出、发起下一条指令;上一章《6.MCP》把工具集扩展到了本地之外,能力齐了,人却还没退场。
CI/CD流水线、定时任务、批处理脚本里没有这个人。把人移出循环,要回答三个子问题:
- 输入输出机器化
没有人读屏幕。输入要能从管道来,输出要能被脚本解析。 - 权限与运行边界预先声明
没有人点确认,也没有人临场把握预算。哪些工具能用、花多少钱、跑多少轮、用什么模型,必须在启动前说清楚。 - 会话可脚本化
没有人记着上下文。多步任务之间的衔接,要靠脚本传递会话。
Claude Code的答案是Headless模式(非交互模式):加上-p参数,Claude Code不启动交互界面,执行完任务、把结果写到标准输出、退出进程。本章按这三个子问题展开,最后落到CI/CD实战。
基本用法与输出格式
-p是--print的短写,表示非交互执行:返回结果后进程退出,不等待任何用户输入。
示例代码:
1 | claude -p "总结这个仓库的构建方式" |
prompt也可以从标准输入来。Claude Code能直接接在Unix管道后面,像grep、jq一样成为工具链中的一环。
示例代码:
1 | tail -200 app.log | claude -p "找出这段日志里的异常" |
交互模式的CLI参数在Headless模式下同样适用;另有一批参数只配合-p才有意义,例如--output-format、--max-turns、--json-schema、--no-session-persistence。
输出这一侧由--output-format控制,共三种格式:text、json、stream-json。
text与json
text是默认格式,直接输出纯文本响应,适合人读,或者直接重定向到文件。
json把最终结果连同执行元数据一起输出为一个JSON对象,是脚本消费的首选。关键字段:
| 字段 | 说明 |
|---|---|
result |
最终文本响应 |
session_id |
会话ID,可传给--resume继续这次会话 |
total_cost_usd |
本次运行的总费用(美元) |
model |
实际使用的模型 |
num_turns |
实际执行的轮数 |
stop_reason |
停止原因,可能值有end_turn、max_tokens、tool_use、stop_sequence |
structured_output |
结构化输出,仅在使用--json-schema时存在 |
返回的JSON形如:
1 | { |
配合jq取字段,是Headless脚本的基本功。
示例代码:
1 | claude -p "统计这段日志里各类错误出现的次数" --output-format json | jq -r '.result' |
result毕竟还是一段自然语言。如果下游程序需要固定结构的数据,用--json-schema传入一个JSON Schema,最终输出会按Schema组织,出现在返回对象的structured_output字段里,不必再用正则去猜文本格式。
示例代码:
1 | tail -200 app.log | claude -p "按类别统计错误数量" \ |
stream-json
长任务不适合等到最后一次性拿结果。--output-format stream-json逐行输出JSON事件,每行一个完整的JSON对象,可以边执行边消费。
注意:-p下使用stream-json输出必须同时指定--verbose。
主要事件类型:
| 事件 | 说明 |
|---|---|
system(subtype为init) |
会话元数据:session_id、模型、已加载插件等 |
system(subtype为api_retry) |
API重试信息 |
system(subtype为plugin_install) |
插件安装进度,需设置环境变量CLAUDE_CODE_SYNC_PLUGIN_INSTALL |
user_message |
用户消息 |
assistant_message |
助手消息,附带stop_reason与usage(token用量) |
tool_result |
工具执行结果 |
stream_event |
token级增量文本流,需另加--include-partial-messages |
围绕行协议还有三个参数:
--include-partial-messages
输出token级的中间流事件,须与-p --output-format stream-json --verbose同用。--include-hook-events
把Hook(钩子)生命周期事件也写进事件流,须与--output-format stream-json同用。--input-format stream-json
输入侧也用行协议,调用方可以向同一个进程持续发送用户消息,须配合--output-format stream-json。在此基础上,--replay-user-messages会把用户消息回显到stdout,便于审计。
运行控制参数
人在场时,花多少钱、跑多少轮、用什么模型,都是临场把握的;人退场后,这些都要变成启动前的参数。
| 分组 | 参数 | 说明 |
|---|---|---|
| 预算与轮数 | --max-turns |
限制最大交互轮数,超限时以错误码退出 |
| 预算与轮数 | --max-budget-usd |
设定本次运行的费用上限(美元),达到即停止 |
| 模型 | --model |
指定模型,接受别名(sonnet、opus、haiku、fable)或完整模型ID |
| 模型 | --fallback-model |
主模型超载时自动降级,接受逗号分隔的降级链,覆盖settings中的fallbackModel |
| 系统提示 | --append-system-prompt、--append-system-prompt-file |
在默认系统提示之后追加内容 |
| 系统提示 | --system-prompt、--system-prompt-file |
完全替换系统提示 |
| 极速启动 | --bare |
跳过各类扩展的自动发现,最小化启动 |
| 配置 | --settings |
临时覆盖设置,接受JSON文件路径或行内JSON字符串 |
| 配置 | --mcp-config |
显式加载MCP服务器,接受JSON文件或行内字符串,空格分隔多个 |
| 配置 | --strict-mcp-config |
只使用--mcp-config指定的服务器,忽略其他来源的MCP配置 |
| 缓存 | --exclude-dynamic-system-prompt-sections |
把工作目录、环境信息等动态段移入首条用户消息,改善跨用户、跨机器的prompt cache命中率 |
其中三处值得展开。
追加还是替换系统提示。 --append-system-prompt与--system-prompt的差别不在语法,在责任。追加保留Claude Code默认系统提示的全部内容——工具使用方式、安全约束、输出习惯——只在末尾加上我们的要求,适合绝大多数场景。替换则把默认内容整个删掉,模型如何正确使用工具、如何守住安全边界,从此由我们自己负责。优先级上,CLI标志高于--settings传入的值,再高于settings.json里的配置。
--bare的取舍。 --bare做极速启动:跳过Hooks、Skills(技能)、插件、MCP(Model Context Protocol,模型上下文协议)服务器、自动记忆和CLAUDE.md的自动发现,工具只保留内置的Bash、Read、Edit。CLAUDE.md的加载机制见《2.Memory》,Skills的发现机制见《3.Skills》,MCP的配置来源见《6.MCP》。收益是启动快、行为可预测、上下文干净;代价是项目约定全部丢失,prompt必须自带完整上下文。确有需要时,可以在--bare基础上用--mcp-config显式补回MCP服务器。
环境变量。 Headless场景下常用的一组:
| 环境变量 | 说明 |
|---|---|
ANTHROPIC_API_KEY |
API密钥,优先于订阅认证 |
ANTHROPIC_MODEL |
默认模型,被--model覆盖 |
API_TIMEOUT_MS |
API请求超时,默认600000毫秒 |
BASH_DEFAULT_TIMEOUT_MS |
长时间运行的Bash命令超时,默认120000毫秒 |
CLAUDE_CODE_SKIP_PROMPT_HISTORY |
禁用会话持久化,等同--no-session-persistence |
CLAUDE_CODE_SIMPLE |
标记裸模式运行,由--bare自动设置 |
CLAUDE_CODE_SYNC_PLUGIN_INSTALL |
启用plugin_install事件流,输出插件安装进度 |
CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS |
后台代理等待上限(毫秒),默认600000,0表示无限 |
无人值守的权限
交互模式下,权限系统的最后一道保险是弹给人的确认提示;-p下这道提示无处可弹。权限规则的语法(Tool(specifier))、六种权限模式的定义、deny高于ask高于allow的优先级,都在《1.概述》,本节讲无人值守场景如何把决策预先声明出来,并补充最容易写错的Bash规则匹配细节。声明的手段有四个:
--allowedTools
白名单。命中的工具调用直接放行,不再提示。逗号分隔多条规则。--disallowedTools
黑名单。支持*通配符,匹配的工具被移除或调用被阻止。--permission-mode
直接指定权限模式,取值default、acceptEdits、plan、auto、dontAsk、bypassPermissions。只读的分析类任务可以直接给plan。--dangerously-skip-permissions
跳过权限提示,等同--permission-mode bypassPermissions。
--dangerously-skip-permissions名副其实:确认环节整个消失,Claude能调用的工具就是它会调用的工具。只在严格隔离的环境里使用,比如一次性容器、不含生产凭据的CI runner。即便在这种模式下,显式的ask规则和针对root、home目录的删除操作仍不会被静默放行;管理员也可以在Managed设置里用disableBypassPermissionsMode彻底锁死这个参数。
白名单里最常用也最容易写错的是Bash规则。空格是分词符,*按词通配。
示例代码:
1 | Bash(npm run build) # 只匹配这一条命令 |
注意:Bash(ls *)和Bash(ls*)是两条不同的规则。前者把ls当作完整的命令名,匹配ls带任意参数;后者是字符级前缀,可能命中以ls开头的其他命令。另外,用&&等连接符拼起来的复合命令会按子命令拆开分别评估,写白名单时每一段都要覆盖到。
会话脚本化
-p每次调用默认开启一个全新会话,执行完保存到磁盘后退出。多步任务的衔接靠三个参数:-c(--continue)继续最近一次会话;--resume按会话ID或名字恢复指定会话;--fork-session在恢复时分叉出新的会话ID,源会话保持不变。串联多步的标准做法,是用--output-format json拿到session_id,传给下一步的--resume。
示例代码:
1 | 第一步:分析日志,记下会话ID |
不需要留痕的一次性调用,加--no-session-persistence:会话不写盘,也无法再恢复。等效的环境变量是CLAUDE_CODE_SKIP_PROMPT_HISTORY。
注意:--resume只在当前项目及其git worktrees里查找会话,换个目录就找不到了。跨目录的流水线要么固定工作目录,要么放弃会话衔接、在prompt里显式传递上一步的结论。
CI/CD实战
把前面的参数组合起来,就是CI/CD里的四种常见形态。
示例代码:
1 | 一、日志分析:定时任务里跑,只读、极速启动 |
在GitHub Actions上不必手写这些命令,官方提供了anthropics/claude-code-action@v1。
示例代码:
1 | name: claude-review |
关键字段:
| 字段 | 说明 |
|---|---|
prompt |
任务指令;留空时Action转为监听评论中的触发词 |
claude_args |
透传任意CLI参数,空格或换行分隔,本章的参数全部适用 |
plugins |
按插件名@市场名格式安装插件 |
trigger_phrase |
自定义触发词,默认@claude |
注意:从Beta版(v0.x)升级到v1时,direct_prompt改名为prompt,custom_instructions改为经claude_args传--append-system-prompt,原先的mode字段改为自动检测。
经验
--bare下必须显式提供ANTHROPIC_API_KEY
极速启动跳过了keychain与OAuth凭据的读取,缺了这个环境变量会直接认证失败。stream-json的参数是联动的
-p下要配--verbose;--include-partial-messages在此基础上才生效;--replay-user-messages要求--input-format与--output-format都是stream-json。漏掉其中一个,症状往往是"没有报错但拿不到想要的事件"。structured_output不是常驻字段
只有给了--json-schema它才存在,下游脚本取值前先判空。- 交互式命令在
-p下不可用
/login这类需要对话框的命令无法执行,凭据改用环境变量或--settings传入。不过prompt里可以直接写/skill-name调用Skill,见《3.Skills》。 - 交互专属的Hook要改挂载点
PermissionRequest这类只在交互模式下工作的Hook,在Headless下改挂到PreToolUse上,协议见《5.Hooks》。 - 权限声明宁细勿粗
优先写白名单、少用黑名单,Bash规则精确到子命令,--dangerously-skip-permissions只留给真正隔离的环境。
最后是Headless模式的天花板:它是进程外的整只CLI,控制粒度到参数为止,进出都是字符串。当我们需要进程内的控制——把自定义工具注册进循环、拿到类型化的消息对象、用代码逐次决策权限——就该换Claude Agent SDK了,下一章《8.SDK》展开。