Claude Code Output Style 能不能讓 Opus 5 不再寫得落落長?答案是:可以改善,但只靠一句「簡短一點」或一份風格檔還不夠。真正可重複的做法,是把文風拆成軟規則、量尺與閘門,再用同一批任務做 A/B 測試。
這個問題不是憑空想像。2026 年 8 月 6 日,一位使用者在 r/ClaudeAI 求助串描述:即使已用 memory、CLAUDE.md 與 documentation skill,Claude Code 仍替文件與註解寫出大段難讀文字。這是單一使用者回報,不能外推成所有 Opus 5 都有問題;但它提出了一個很實際的工程題:怎麼知道輸出真的變好,而不是模型自己說「我已經更精簡」?
本文會從零建立一個可複製的小型 lab:用 Claude Code Output Style 固定語氣、用 CLAUDE.md 放專案驗收規則,再用 Eval 檢查長度、關鍵事實與 code comments。最後把檢查接到 Stop hook、Git pre-commit 與 CI。
先說結論:Claude Code Output Style 是方向盤,不是護欄
🧭 記憶把手:可控文風=軟規則(Output Style+CLAUDE.md)+量尺(Eval)+閘門(hook/CI)。
先要求保留所有數字、條件、例外與風險;只有通過事實完整性,才有資格比較誰更短。

Anthropic 的 Opus 5 prompting guide已直接提醒:預設可見回答與寫入磁碟的文件,可能都比先前 Opus 更長,而且兩者要分開校準。effort主要控制思考量;把 effort 調低,並不會可靠地縮短使用者看見的回答。因此,effort 不是篇幅旋鈕。
先做 baseline:不要拿「感覺很囉嗦」當 Eval
先挑 5 到 20 個你真的會做的案例,保留原始 prompt 與輸出。繁體中文不要用空白切詞後就稱為「字數」,比較穩定的起點是:
開始計分前先分流:對話進度要看整個 turn 是否重複同一發現,不能只看 final;寫入磁碟的文件要對照 briefing 的原子事實與新增主張;code comments則要把註解文字和程式行為拆開。三者若共用同一個 600 字上限,短通知可能太鬆,設計文件卻會被誤殺;若共用同一個 fact regex,也根本檢查不到 AST 或註解指令。每類至少留一個含否定、例外或相反條件的邊界案例,才能抓出只會找關鍵字的假高分。
- raw non-space chars:移除空白後的原始字元數,最容易重跑,但仍包含 Markdown 符號。
- rendered visible chars:先移除標題、粗體與清單等標記,再量讀者實際看到的非空白字元;文件比較以它為主。
- p90 sentence chars:九成句子不超過多少字,用來抓少數超長句;它只是可讀性 proxy,不是閱讀理解分數。
- critical fact recall:日期、數字、前置條件、例外、限制與風險是否完整保留。
- unsupported numbers/claims:輸出是否自行新增 briefing 沒有的數字或因果說法。
- comments:註解行數與字元數只能作診斷;真正的硬條件是 AST、非註解 token 與測試結果不變。
關鍵順序是 critical_fact_recall == 100% 先過關,再比較長度。若一版少了維護時間、SLA 例外或 rollback 未授權,即使只剩一句話也應判失敗。門檻要依自家文件集的 baseline 設定,不能把「句子越短」或「術語越少」直接等同品質。
Claude Code Output Style A/B:三個案例的實測結果
我們做了一次小型 paired lab。A 組在共同控制條件下使用 Default Output Style,且不放 CLAUDE.md;B 組同時加入 Measured Concise Output Style 與候選 CLAUDE.md。三個合成案例分別是部署進度、客戶維護通知、只新增 Python 註解;每格各跑 5 次,共 30 次,A→B 與 B→A 交錯,正面範例不使用測試題內容。
兩組都請求完整 ID claude-opus-5,固定 Claude Code 2.1.226、high effort,以 --tools "" 停用內建工具、停用 auto memory,且每次開新 session。這個旗標不影響 MCP;本 lab 未使用 MCP 工具。原始 JSON 的 usage.output_tokens 在 30 次都等於 Opus 5 主回應的 output tokens;另有記錄到小量 Haiku 輔助呼叫,但沒有加進下列數字。它也不是我們對顯示文字重新切詞後的字數。
這裡的「paired」是同一 round 內相鄰跑 A、B,不共享 random seed;交錯順序只能減少固定先後偏差,不能消除生成隨機性。scorecard 與 unsupported-claim 標籤是在取回輸出後整理的探索性分析,並非預先登記的 release gate;正式採用時應先凍結 scorer,再對 held-out cases 重跑。

- 部署進度:B/A 的 paired median 為 0.981,raw non-space chars 只少 1.9%,output tokens 反而多 3.9%;篇幅近乎持平。10 份都保留 6/6 原子事實,但 5 份把「18 項中 2 項失敗」進一步寫成「16 項通過」。這可由算術推出,卻不排除其他項目是 skipped/warning,因此只能算未授權推論,不能直接當成已知事實。
- 客戶文件:B/A 為 1.134,raw non-space chars 反而多 13.4%,output tokens 多 11.4%。10 份都命中 9/9 字串/關係規則;人工覆核仍抓到把「建議重試」改成「會重試」的模態變化,以及「本通知已符合七天」等新增主張。B 的五份輸出也都擴寫了 backoff,部分加入服務條款、webhook 定義,甚至「webhook 不會遺失」的保證。規則要求解釋術語,正是變長的可見機制。
- 程式註解:B/A 為 0.676,總輸出的 raw non-space chars 少 32.4%,output tokens 少 5.5%;若只看 comment chars,paired median 少 57.5%。十份程式的 AST、非註解 token 與 3/3 必要 rationale 都通過。事後的 closed-world checker 在每份都標到白名單外解釋,但「未授權」只表示 fixture 沒提供,不等同主張為假;四次呼叫、bucket 邊界等可由程式推出,B3 的「不會造成重複副作用」才是明確危險保證。事後套用的 8%–22% 註解行區間也把 10 份全判失敗,回頭看才發現它對 9 行小程式幾乎只容許 1 行註解,門檻本身就失真。
改用兩組各自中位數再相除,三類 raw chars 方向仍相同,依序是 −3.4%、+10.0%、−30.0%,但幅度不同。這仍不是模型 benchmark:案例是合成資料,B 同時換了兩個變因,不能把差異歸因給其中一項,也不能外推成 Opus 5 的平均表現。它真正驗證的是方法:全域規則可能互相拉扯,regex 會有假陽性與假陰性,門檻也需要用真實 baseline 校準。
步驟一:建立 Measured Concise Output Style
在專案建立 .claude/output-styles/measured-concise.md。依 Claude Code Output Styles 官方文件,custom style 預設會省略內建 software-engineering instructions;仍要寫程式時,務必保留下面的 keep-coding-instructions: true。
---
name: Measured concise
description: Outcome-first concise writing without losing conditions
keep-coding-instructions: true
---
Use information-dense Traditional Chinese.
- Lead with the outcome. Add detail only when it changes a decision.
- Remove repetition, filler, ceremonial introductions, and duplicate summaries.
- Never shorten by deleting a number, condition, exception, uncertainty, risk, or required next action.
- Match document length to the task. Do not add invented headings or boilerplate.
- In code, comments explain intent, invariants, tradeoffs, or surprising edge cases. Do not narrate obvious syntax.
<example>
已匯入 24 筆資料;3 筆因缺少日期而跳過,原始檔未修改。
</example>
<example>
# Stable sort preserves arrival order when two jobs have the same priority.
</example>
互動使用時可在 /config 選擇 style;可重跑 lab 則把 B 組的 .claude/settings.json 明確寫成下面這行。這是必要的,因為後面的 --setting-sources project 不會讀 .claude/settings.local.json:
{"outputStyle": "Measured concise"}
在 Claude Code 執行 /config,選擇 Measured concise,接著執行 /clear 或開新 session。舊的 /output-style 已移除。還要注意:一般 subagent 有自己的 system prompt,不會自動繼承主對話的 style;只有 fork 類型例外,所以會產出客戶文件的 subagent 也要另外測。
步驟二:把專案驗收規則放進 CLAUDE.md
Output Style 放跨任務的語氣與格式;CLAUDE.md 放這個 repo 才成立的規則。依 Claude Code memory 官方文件,CLAUDE.md 是 system prompt 後的 user context,不是強制政策。下面是 lab 的 B 組實際檔案,方便原樣重跑:
# 可見輸出驗收規則
「簡潔」是刪除重複與空話,不是刪除數字、條件、例外、風險或下一步。
- 完成回覆先說結果,再列未解風險與下一步。
- 客戶文件必須保留 briefing 的全部原子事實,不得自行新增數字。
- comment-only 任務不得改變 AST 或非註解 token。
- 程式註解只解釋 why、invariant、tradeoff 或反直覺 edge case。
- 專有名詞第一次出現時用一句白話解釋;必要的領域詞不要硬換成模糊同義詞。
這份規則正好示範為何要 A/B:最後一條讓 B 組客戶文件變長。正式專案可把它移到 .claude/rules/customer-docs.md,改成「只替 briefing 標記為 audience-unknown 的術語補一句解釋,不自行定義 503、backoff 或 webhook」。若只想套用在客戶文件,還要在該 rule 的 YAML frontmatter 加上 paths: ["docs/customer/**"];沒有 paths 就會全域載入。用 /context 確認 session 實際載入哪些指令檔;單次流程則放 skill 或 task prompt。想理解這些層次,可先看《Claude、Claude Code 與 Cowork 差在哪》。
步驟三:建立不會獎勵「刪掉事實」的 Eval
Anthropic 的 Eval 指南建議先定義可量測成功條件,測試集要貼近真實任務並包含 edge cases;可以用程式判分的部分先自動化。最小檔案結構如下:
eval/
├── fixtures/
│ ├── progress/
│ ├── customer-doc/
│ └── comments/
├── manifests/ # 原子事實、允許數字與門檻
├── eval-writing.py # 文件/進度 scorecard
└── eval-python-comments.py # AST、token、註解檢查
每個 manifest 把一項完整命題當成一個 fact。例如「提前至少 7 天通知,維護才排除於 SLA」必須連在一起檢查,不能只找 7、SLA、排除 三個詞。核心判斷可先寫成:
eligible = (
critical_fact_recall == 1.0
and condition_recall == 1.0
and unsupported_numbers == []
and unsupported_claims == []
)
if not eligible:
fail("先修復事實完整性;不得用較短篇幅抵銷缺漏")
compare(visible_chars, p90_sentence_chars)
unsupported_claims很難只靠 regex 完成。先用允許數字清單與否定極性規則擋明顯錯誤,再對高風險文件做盲評人工抽查;若使用另一個 LLM 當 judge,也要先用人工標記集校準,避免同一模型家族共享偏誤。code comments 則加上 AST、非註解 token、測試與會改變工具行為的 noqa/type: ignore 檢查。
步驟四:用 Stop hook、pre-commit 與 CI 接住失敗
Stop hook 適合檢查最後聊天回答。把下面設定加入 .claude/settings.json:
{
"outputStyle": "Measured concise",
"hooks": {
"Stop": [{
"hooks": [{
"type": "command",
"command": "python3",
"args": ["${CLAUDE_PROJECT_DIR}/.claude/hooks/check-final.py"]
}]
}]
}
}
將下段存成 .claude/hooks/check-final.py;它會讀取 stdin JSON,若 stop_hook_active 已是 true 就放行,否則量 last_assistant_message。以下 600 字只是示範起始值,不是 Anthropic 官方門檻,執行環境也要能在 PATH 找到 python3:
#!/usr/bin/env python3
import json
import re
import sys
event = json.load(sys.stdin)
if event.get("stop_hook_active"):
raise SystemExit(0)
message = event.get("last_assistant_message", "")
chars = len(re.sub(r"\s+", "", message))
if chars > 600:
print(json.dumps({
"decision": "block",
"reason": (
f"最後回答為 {chars} 個非空白字元;請移除重複內容,"
"但保留所有數字、條件、例外與風險。"
)
}, ensure_ascii=False))
依 Claude Code hooks 官方文件,Stop 在主 agent 完成回答時觸發;Stop input 直接提供 last_assistant_message,但若要檢查文件,腳本仍需自行讀檔或 diff。連續阻擋 8 次後會強制結束。本文範例沒有實作檔案回滾,因此不會自動撤回先前寫入的文件;文件與註解要由真正的 Git hook 檢查 staged content:
#!/bin/sh
set -eu
gate_tmp=$(mktemp -d /tmp/writing-gate.XXXXXX)
trap 'rm -rf "$gate_tmp"' EXIT HUP INT TERM
if git diff --cached --name-only --diff-filter=ACMR |
grep -qx 'docs/orion-maintenance.md'; then
git show ':docs/orion-maintenance.md' > "$gate_tmp/orion.md"
python3 eval/eval-writing.py \
"$gate_tmp/orion.md" eval/manifests/customer-doc.json
fi
if git diff --cached --name-only --diff-filter=ACMR |
grep -qx 'src/retry_policy.py'; then
git show ':src/retry_policy.py' > "$gate_tmp/retry_policy.py"
python3 eval/eval-python-comments.py \
eval/fixtures/comments/base.py \
"$gate_tmp/retry_policy.py" \
eval/manifests/comments.json
fi
python3 -m unittest discover -s tests
將它存成 .git/hooks/pre-commit,再執行 chmod +x .git/hooks/pre-commit。依 Git hooks 官方文件,本機 pre-commit 可用 --no-verify 跳過,所以同一組命令還要在 CI 再跑一次;若只用 Claude Code 的 PreToolUse 攔 git commit,也抓不到人從另一個終端機或 IDE 發出的提交。

如何把一次 smoke test 升級成可信 A/B
- 固定完整 model ID、Claude Code 版本、effort、工具權限、repo commit、來源文件與 task prompt。
- A 是現行設定;B 是候選 Claude Code Output Style+
CLAUDE.md。若要知道是哪一層有效,再加「只換 style」的消融組。 - 每個案例每組至少跑 5 次,奇數輪 A→B、偶數輪 B→A,全部使用新 session。
- 先鎖定門檻再看結果,保存原始 JSON;以 paired median 與失敗率判斷,不挑最好看的一次。
- 先在合成 fixture 除錯,再加入真實文件與保留不公開的 held-out cases,避免對測試題過擬合。
A、B 各放在獨立 Git root。以下是單一 fixture 的最小命令;把 stdout JSON 與抽出的 result 都保存,重複 5 輪:
CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 \
claude -p "$(cat eval/fixtures/customer-doc/prompt.txt)" \
--model claude-opus-5 \
--effort high \
--tools "" \
--setting-sources project \
--settings .claude/settings.json \
--no-session-persistence \
--output-format json
若你的目標只是少用 token,還要把「輸出變短」和「總成本下降」分開量;style 與規則本身也會增加輸入 context。可搭配《Claude 怎麼省 token》建立成本 baseline。若要把同樣的 gate 擴展到多步驟 agent,再讀《AI Agent Harness 是什麼》。
常見失敗:量錯東西,模型就會鑽錯方向
- 只壓字數:模型可能先刪例外與風險;用 critical fact gate 防守。
- 只禁術語:模型可能換成更長、更模糊的同義句;改量「未解釋術語」,並保留必要領域詞。
- 只壓句長:輸出可能變成大量殘句;同看 p90、完整命題與人工清晰度。
- 把註解密度當分數:模型能靠拆行灌高或灌低;使用區間、AST、token 與測試。
- 相信模型自評:「已簡化」不是證據;scorecard、diff 與 CI 才能重跑。
FAQ:Claude Code Output Style 與文件 Eval
降低 effort 會讓 Opus 5 回答變短嗎?
不可靠。effort 主要控制思考量;可見篇幅仍應用明確輸出規格與 Eval 控制。
Output Style 和 CLAUDE.md 要選哪一個?
兩者分工。固定語氣與格式放 Output Style;repo 的文件、測試與註解驗收規則放 CLAUDE.md 或 path-scoped rules。
為什麼 custom style 要設 keep-coding-instructions?
因為預設是 false。沒有設為 true 時,custom style 會省略 Claude Code 內建的 software-engineering instructions。
改完 style 為什麼看不出差異?
先開新 session。用 /config 選取後執行 /clear,再以同一 prompt 比較;不要在帶有舊對話的 session 做 A/B。
Output Style 會套用到 subagent 嗎?
一般不會。普通 subagent 有自己的 system prompt;fork 才會繼承父層完整提示。
Stop hook 能阻止冗長文件寫入嗎?
本文範例不會自動撤回寫入。它能要求 Claude 繼續修正,卻沒有實作檔案回滾;文件要讀實際 diff,並由 pre-commit/CI 擋住不合格版本。
fact recall 100% 就代表沒有幻覺嗎?
不代表。它只能證明預登記事實被檢出,無法自動證明沒有新增主張;高風險文件仍要做 unsupported-claim 檢查與人工抽查。
多少字、多少註解才算合格?
沒有通用數字。先量你自己的文件類型與 repo baseline,再設區間;操作通知、設計文件和 API reference 不該共用同一上限。
接著閱讀
左右滑動查看更多推薦
結論:先保真,再求短
要馴服冗長,不必和 Opus 5 每一輪討價還價。今天先選 5 個真實任務,列出不可遺失的原子事實,保存 A 組 baseline;接著加入 Measured Concise style 與專案規則,用新 session 跑 B 組。只要任何數字、條件、例外或風險遺失,就直接失敗;通過後才比較長度。當這套 scorecard 能穩定重跑,再接上 hook 與 CI——這時「簡潔」才從一句願望,變成可驗收的工程規格。想把這種測試思維延伸到完整 AI 工作流,也可從 AlphaLab 的AI 實戰課程繼續練習。






