跳转到内容

结构化输出

人看自然语言,机器看 JSON。当 Claude Code 要嵌进 CI、被脚本调用、和别的服务对接时,你需要的不是「它说了一段话」,而是「它吐了一个符合 schema 的对象」。这一页讲清楚怎么让 Claude 的输入输出都是结构化的、可程序化解析的、契约化的——这是 headless 集成的核心。

--output-format 决定 Claude 怎么吐结果。三种格式从「给人看」到「给机器看」逐档升级:

Terminal window
# text:默认,纯文本,给人看
claude -p "Explain file Header.tsx"
# json:一次性结构化对象,含 cost/session_id 等元信息
claude -p "How does the data layer work?" --output-format json
# stream-json:流式逐条 JSON,每行一个事件
claude -p "Build an application" --output-format stream-json
格式 像什么 适合
text 人话 直接给人看
json 一张表 一次性结果,脚本解析
stream-json 一条流水 实时追踪进度、UI 渲染

json 模式跑完输出一个完整对象;stream-json 模式输出多行 JSON(JSONL 格式),每行一个事件——assistant 消息、tool_usetool_result,最后一条是带统计信息的 result。每段对话以一条 init 系统消息开场。

「让模型随便吐 JSON 然后祈祷它符合格式」是 headless 集成最大的痛苦。--json-schema 解决这个问题——你给一个 JSON Schema,输出会被验证符合 schema 才返回,不符合就重试或报错。

Terminal window
claude -p "分析这个 PR 的风险点" \
--json-schema '{
"type": "object",
"properties": {
"risk_level": {"type": "string", "enum": ["low","medium","high"]},
"issues": {"type": "array", "items": {"type": "string"}}
},
"required": ["risk_level","issues"]
}'

输出会是一个严格匹配 schema 的 JSON 对象:

{
"risk_level": "medium",
"issues": [
"未对用户输入做 SQL 转义",
"密码以明文形式记到日志"
]
}

这种「契约化输出」让 Claude 真正可以嵌进 CI 检查、自动分类、数据抽取这类下游需要确定结构的流程——下游代码不再需要写「如果格式不对就降级」的兜底逻辑。

--input-format 决定怎么把内容喂给 Claude。

Terminal window
# text:默认,单次输入
claude -p "解释这个函数" --input-format text
# stream-json:多轮对话,按 JSON 流喂入
echo '{"type":"user","message":{"role":"user","content":[{"type":"text","text":"Explain this code"}]}}' | \
claude -p --output-format stream-json --input-format stream-json --verbose

stream-json 输入是 JSONL 格式——每行一个完整的 JSON 对象,对应一轮用户消息。这样可以多轮对话而不重启 claude 进程,也能在 Claude 处理请求时继续往里喂引导信息。

几个 flag 让流式输出更精细:

Flag 作用 必需搭配
--include-partial-messages stream-json 里包含增量片段 --print + --output-format stream-json
--include-hook-events 包含 hook 触发的事件 --output-format stream-json
--replay-user-messages 在输出里回显用户消息,便于对齐对话 --input-format stream-json + --output-format stream-json
Terminal window
# 调试 hooks 时看完整事件流
claude -p "跑测试" \
--output-format stream-json \
--include-hook-events | jq .
# 多轮对话回显用户消息,便于对齐
claude -p \
--input-format stream-json \
--output-format stream-json \
--replay-user-messages

有些场景你不希望会话被存到磁盘——例如一次性 webhook、隐私敏感的一次性查询。--no-session-persistence 让 Claude 跑完即弃,不留 session_id,无法 --resume 接续。

Terminal window
claude -p --no-session-persistence "process this webhook payload"

只对 print 模式有效。环境变量 CLAUDE_NO_SESSION_PERSISTENCE 在任何模式下都能起到相同作用。

把这些 flag 组合起来,Claude Code 就是一个完全可程序化的函数

Terminal window
# 1. CI 里做代码审查,输出契约化结论
claude -p "审查这个 diff 的风险" \
--output-format json \
--json-schema '{"type":"object","properties":{"risk":{"type":"string"},"issues":{"type":"array","items":{"type":"string"}}}}' \
--max-budget-usd 2
# 2. 流式监控长任务,只看工具调用
claude -p "重构 src/legacy 模块" \
--output-format stream-json \
--include-partial-messages | jq 'select(.type=="tool_use")'
# 3. 多轮对话脚本(输入输出都用 stream-json)
echo '{"type":"user","message":{"role":"user","content":[{"type":"text","text":"先列出所有 TODO"}]}}' | \
claude -p --input-format stream-json --output-format stream-json

--output-format json 跑完输出的对象包含这些字段(详见 Headless 模式):

字段 含义
type 消息类型,最终结果为 result
subtype 子类型,success 表示成功
total_cost_usd 本次花费
is_error 是否出错
duration_ms 总耗时
num_turns agentic 轮数
result 最终回答文本
session_id 会话 ID,可用于 --resume 接续

jq 把字段抠出来:

Terminal window
claude -p "列出所有 TODO" --output-format json | jq -r '.result'

结构化输出的核心是契约--output-format json 让 Claude 吐结构化对象,--json-schema 让结构被验证,--input-format stream-json 让多轮对话可程序化。这套组合把 Claude 从「会说话的助手」变成「可调用的函数」。

下一篇看 GitHub Actions,把这套 headless + 结构化输出能力嵌进 CI。🤖