terse

我受不了 Claude Code 跟我说话的方式。每一个问题回来都是一篇小作文。口号、隐喻、刚刚做过什么的重述、"这不是 X,而是 Y"。每。一。次。

Claude 自带的"简洁"模式会稍微缩短一点,但其余那些废话还是照旧。

我试过系统提示词,几轮之后就被忘掉了。我试过找插件,但没有一个能让 Claude 稳定地正常说话。有些是斜杠命令,有些是为了省 token,有些是为了适配神经多样性。没有一个能让 Claude 正常说话。

我只是想不用读小作文,让 Claude 每次都直奔重点。

所以我花了几周时间,把它每一件让我烦的事、以及我希望它改说的话都记下来,围绕这些做了一个插件。它对我有效。如果 Claude 跟你说话也是这个德性,那它就是你的了。

Same prompt on Opus 5.5: vanilla Claude Code at 636 words against terse at 180 words

这份 README 的其余部分由 Claude Code 使用这个简洁插件共同撰写。功劳我占大头,但文字一个字都不归我。

一个 Claude Code 插件,把回复长度砍掉一半,并去掉那些拿腔拿调的文风。Fable 5.1:字数减少 46%,成本降低 51%。Opus 5.5:字数减少 54%,成本降低 32%。测量方式基于 20 条提示词,针对一个真实代码库。这些写作规则适用于回复、文档、提交信息和子智能体。还附带一个用于状态栏的上下文用量表。

表格中使用以下术语。

  • Vanilla 指的是没有任何 CLAUDE.md、没有插件、没有自定义指令的 Claude Code。
  • 风格违规指的是违反其中某条规则的句子。由一位 Opus 5 评审逐条对照规则列表阅读每条回复来计数。计数按每 1,000 词计算,这样长回复和短回复可以比较。
  • 隐喻是指在存在字面说法的情况下使用比喻性措辞。比如用"一次 3,000 行的合并只配扫一眼加祈祷"来代替"一次 3,000 行的合并只花 10 分钟读一遍,不做逐行审查"。
  • "X,而不是 Y"式重构是指用对比当作标签来代替解释。比如用"这是意图,不是行为"来代替"docstring 写的是 X,代码做的是 Y"。
  • 口号是指听起来像原则、但不说明任何机制的短句。"决策本身就是功能。"
  • 节奏是指为效果而构建的韵律,比如三连排比和包袱式结尾。
  • Bloat 是指不增加任何事实的句子,包括像“我现在来看一下代码”这样的叙述。
  • 第一人称是“I”、“me”、“my”、“let me”及其缩写形式。即以作者为主语的句子。
  • 输出 token 是模型写出的内容,包括思考部分。在 Opus 和 Fable 上,输出 token 的价格约为输入 token 的五倍。

在 12 个公开提示词上测量,vanilla Claude Code 对比简洁版 1.1.0。Opus 5.5,effort high,各运行一次。

vanilla 简洁版 变化
词数,10 条聊天回复 5,959 1,793 -70%
回复中位数 636 词 181 词 -72%
输出 token,12 次运行 19,310 10,528 -45%
每 1k 词的风格违规数(Opus 评判) 11.1 1.1 -90%
每 1k 词的第一人称 2.79 0.47 -83%
每 1k 词的“X, not Y”重构 0.84 0.56 -33%
每 1k 词的口号 1.17 0.0 -100%
每 1k 词的隐喻 3.69 0.0 -100%

针对 Fable 5.1、针对 effort medium 的 Opus 5.5,以及针对 1.0.0 规则文本的表格:docs/models。方法、提示词和评判标准:docs/benchmark.md。

安装

从 Claude 目录中:打开简洁版列表并选择Add to Claude Code。该插件会保存到你的 claude.ai 账户,并在你下次以该账户登录启动 Claude Code 时下载。在正在运行的会话中,/reload-plugins会立即加载它。Anthropic 会在目录提供每个版本之前对其进行扫描和审核。

从 GitHub 安装,在 Claude Code 中:

/plugin marketplace add lowenbjer/claude-terse
/plugin install terse@terse

两种途径安装的是相同文件。写作规则适用于每个新会话(在/clear之后),以及你退出后用claude --resume恢复的现有会话。

对于上下文计量器,运行/terse:install-meter一次。它会把 26 行的计量脚本复制到插件的数据目录(插件更新会保留该目录),并向你的statusLine添加一个~/.claude/settings.json条目。该条目在保存时生效。

你会得到什么

规则是一种回复形态,22 条规则和 18 组前后对照,877 个词。形态:一句话给出答案,然后是一个列表或表格,然后至多一句提醒,然后停止。每个句子都以主题作主语:不用“我”,不叙述检查了什么或将要做什么。项目符号以事实开头,所以没有加粗引导语,也没有标签行。用平实的词代替生造的名称。不用破折号,不用口号,不用“X,而非 Y”的框架,不用自我标签,不用结尾的主动提议。聊天回复在 150 词以内停止,除非你要求一份文档或一次讲解。发现清单每条发现一到两句话。重做只发送增量。当某条规则有正面形式时,文本在禁令之前先陈述该形式(“最后一句是事实。”)。全文:rules/RULES.md。

这些规则以输出风格的形式发布。它在插件启用时生效,并且是主智能体系统提示词的一部分。子智能体运行自己的系统提示词,所以一个SubagentStart钩子会把同样的文本交给它们。没有任何钩子重写或拦截任何内容。

这条提醒之所以存在,是因为输出风格是上下文的第一部分。经过 20 或 30 轮之后,它距离当前提示词已有 100k token。模型自己最近的回复成为它最近的风格示例。从那时起,回复会把答案放在最后一行,出现“我检查了”的句子、破折号、“X,而非 Y”和口号。一个UserPromptSubmit钩子会把整套规则压缩到 576 个字符,加到你的文本旁边的每一条提示词里。每轮成本约为 145 个输入 token。文本是rules/REMINDER.md。

仪表显示模型名称和上下文填充百分比。低于 37% 为绿色,37% 起为黄色,49% 起为橙色,60% 起为骷髅。这些阈值设定在通常的 50、65 和 80 百分比之下。三项研究表明,检索质量在大窗口填满之前很久就开始下降:

  • Chroma 的Context Rot报告:18 个模型,在 200k 窗口上从 50k token 起开始退化。
  • NoLiMa:13 个模型中有 11 个在 32k token 时降到基线的一半。
  • Lost in the Middle:长上下文中间的内容比开头和结尾得到的注意力更少。

这些阈值是statusline/context-meter.js顶部附近的三个数字。

前后对照

提示词,在 Opus 5.5 上运行,每种设置各跑一次:"我们有一个带 Postgres 数据库的 Flask API。每天大约 09:00 左右,请求在负载下会超时。你会先从哪里查,为什么?"

原版,636 词:

针对 09:00 超时我会先从哪里查

09:00 这个时间点是最有用的线索。在看任何代码之前,先弄清楚下面两种情况中的哪一种正在发生:

  1. 流量在 09:00 上升。工作日的开始带来了登录、仪表盘和同步。这是容量问题。
  2. 流量持平,但有别的东西在 09:00 运行。那是来自某个定时任务的争用。

把 09:00 的请求速率图和其它时段对比一下。仅这一项检查就能告诉你自己处在问题的哪一半。

1. 检查 09:00 有什么在运行

看看 crontabs、pg_cron、Celery beat、Kubernetes CronJobs、备份(pg_dump)、ETL 和报表任务,以及REFRESH MATERIALIZED VIEW。还要检查是否有缓存条目在同一时刻全部过期,因为那会让每个请求同时打到数据库。

小心时区。本地 09:00 在调度器配置里可能是 07:00 UTC,所以两个都要 grep。

常见问题:

  • 一个REFRESH MATERIALIZED VIEW没有CONCURRENTLY,一次批量UPDATE,或一次迁移。这些都会加锁,API 查询随后就得在锁后面等待。
  • 备份或批处理任务占满了磁盘 I/O。

2. 抓取该时间窗口内 Postgres 在做什么

这能给你最快的真实答案。从 08:55 到 09:15 每 10–15 秒做一次快照:pg_stat_activityevery 10–15 seconds from 08:55 to 09:15:

SELECT now(), state, wait_event_type, wait_event,
       now() - xact_start AS xact_age, left(query, 120) AS query, application_name
FROM pg_stat_activity
WHERE datname = current_database()
ORDER BY xact_start NULLS LAST;

这样读结果:

  • 大量wait_event_type = 'Lock':有东西正持有锁。与pg_locks(或使用pg_blocking_pids())连接,找出阻塞的会话。通常就是第 1 步里的那个定时任务。
  • 大量idle in transaction:应用开启事务后没有很快关闭。这会占住连接和锁。
  • 许多active会话在跑同一个查询:某个查询在规模上很慢,通常是缺索引或执行计划过期。检查那个时间窗口的pg_stat_statements。
  • 会话数接近max_connections:你的连接快用完了(见第 3 步)。

还要设置log_min_duration_statement(例如 500ms)和log_lock_waits = on,这样下次再发生时会留下记录。

3. 检查应用侧的连接池耗尽

这是 Flask + SQLAlchemy 应用中超时最常见的原因。在 09:00 前后搜索应用日志中的:

QueuePool limit of size 5 overflow 10 reached, connection timed out

然后算一下:gunicorn workers × threads ×(pool_size+max_overflow)× 实例数,与 Postgresmax_connections当流量激增时,请求会排队等待池化连接,直到超时。在此期间,Postgres 看起来几乎处于空闲状态。

还要检查那些没有归还到连接池的会话。常见原因是缺少db.session.remove()或清理处理器,或者是在事务内进行了缓慢的外部 HTTP 调用。

4. 找出是哪个超时在触发

负载均衡器(通常是 60s)、gunicorn--timeout(默认 30s)和 SQLAlchemypool_timeout(默认 30s)各自指向不同的位置:

  • GunicornWORKER TIMEOUT消息意味着 worker 被阻塞了。它们要么在等待数据库,要么同步 worker 数量太少。
  • 负载均衡器返回 504 但没有 gunicorn 错误,意味着请求在到达 worker 之前就在排队。

我的建议

运行pg_stat_activity在明天 09:00 运行快照循环。同时,在应用日志中 grep 连接池错误,并列出 09:00 附近的所有定时任务。这很可能将问题缩小到两种原因之一:某个任务持有锁,或者早高峰流量下连接池耗尽。两者的修复方式截然不同:移动或重写该任务,或者调整连接池大小并添加连接池器,例如 PgBouncer。

如果你分享调度器配置、SQLAlchemy 引擎设置和 gunicorn 命令,我可以帮你核对连接数计算。

terse 1.2.0,180 词:

从 09:00 运行的内容开始,因为固定的每日时间点指向定时任务或登录高峰,而不是随机负载。

  • 检查 08:55 到 09:15 的 pg_stat_activity。长查询、"idle in transaction" 会话或锁等待会显示是哪一侧在阻塞。
  • 列出 09:00 附近调度的 cron 任务、Celery beat 任务、pg_cron 条目和备份。报告、ETL 或 pg_dump 可能持有锁或使磁盘 I/O 饱和。
  • 将 Gunicorn workers 乘以 threads 与 SQLAlchemy 的 pool_size 加 max_overflow 进行比较。超出该数量的请求会排队等待 pool_timeout(默认 30 s),看起来就像超时。
  • 将 Postgres 的 max_connections 与所有应用实例的总和进行比较。耗尽它会直接拒绝新连接。
  • 开启 log_min_duration_statement(例如 500 ms)和 log_lock_waits。下一次 09:00 运行时就会记录慢语句及其阻塞者。
  • 检查最繁忙表上的 autovacuum 和 ANALYZE 时机。夜间批量加载后统计信息陈旧,可能让执行计划切换为顺序扫描。

这两个日志设置今天开启成本很低,一个上午之后就能把数据库侧的原因和应用侧连接池上限区分开。

两条回复都提到了定时任务、锁等待和连接池耗尽。普通版加了六个标题、一个重复步骤 1 到 3 的建议部分,以及主动提出检查配置。简洁版给出了连接池算术、采样窗口和两个日志设置,没有一句关于写入方的话。1.2.0 文本把这个提示词跑了四次:148、176、180 和 189 词。四次中有两次用加粗引导语开头列点,违反了规则 21。这里展示的是 180 词那次。

实测上限

  • Opus 5.5 和 Fable 5.1 是实测的主智能体。Opus 5 和 Sonnet 5 只作为子智能体运行。在上下文中有规则的情况下,Opus 5 每 1k 词保留 13 个 em dash,Sonnet 在 5 条回复中有 1 条用了破折号。两者作为主模型都没有数据。各模型表格:docs/models。
  • 隐喻在公开集上降到 0,在私有集上减半。Opus 5.5 配 1.1.0 文本:12 条公开提示词上每 1k 词从 3.69 降到 0.0,20 条私有提示词上从 3.09 降到 1.57。Fable 5.1 配 1.0.0 文本:公开集上从 7.2 降到 3.3。
  • 150 词上限缩短了回复,但并未强制住限制。在两套提示词集和两个模型上,聊天词数下降了 46% 到 70%。长分析类问题仍然会超。
  • 子智能体的输出跟随子智能体模型。在一个 Fable 5.1 会话中,Explore 类子智能体运行 Opus 5,在上下文中有规则的情况下每 1k 词保留 13 个 em dash。Fable 子智能体在没有任何规则的情况下产出每 1k 词 0.1 个。在一个 Opus 5.5 会话中,子智能体运行 Opus 5.5,没有规则时每 1k 词写 0.4 个,有规则时为 0。要选择子智能体模型,设置CLAUDE_CODE_SUBAGENT_MODEL为你的主模型,或者要求使用 general-purpose 子智能体,它们会继承主模型。
  • 回复形态和规则 21 只移除了它们所指的那两种形式,没有移除其他任何东西。检查方式是在两个模型上用相同提示词进行 10 轮对话。加粗引导语在 Fable 5.1 上从 20 降到 0,在 Opus 5.5 上从 36 降到 0。标签行从 10 降到 4,从 8 降到 0。由评判模型计数的隐喻、节奏和冗赘保持持平。回复长度保持持平。表格见docs/benchmark.md。1.2.0 文本没有公开集重跑。上面的表格是 1.1.0 文本。
  • 成本下降幅度小于字数下降幅度。输出 token 下降 42% 到 55%。上下文 token 占每次调用成本的大部分。在短公开集上,使用 1.1.0 文本时总成本下降 2%,使用 1.0.0 文本时下降 9% 到 12%。该提醒每轮增加约 100 个未缓存 token。思考 token 从使用 1.0.0 文本时 12 次运行的 2,150 个上升到使用 1.1.0 时的 4,155 个。在针对带有大型 CLAUDE.md 的真实代码库的 20 个聊天提示词上,使用 1.0.0 文本时 Fable 5.1 的成本下降 51%。在 Opus 5.5 上使用 1.1.0 文本时下降 32%。

添加你的模型

目录位于docs/models,每个模型一个文件。一行大约花费 $1 和一个 pull request:通过bench/run.py运行 12 个公开提示词,分别在启用和不启用该插件的情况下,粘贴两份摘要,写 2 到 5 条发现。步骤见docs/models/CONTRIBUTING.md。违反规则的回复放入 issue,使用 "Post your worst reply" 模板。

数据

该插件不进行网络调用,也不收集任何内容。它的 hook 读取插件目录内的一个文本文件,并将其交给 Claude Code。安装 skill 会向你的设置文件写入一个键。详情见docs/privacy.md。

卸载

从目录安装:在 claude.ai 上打开Customize > Plugins下的插件,并从其菜单中选择Remove。

从 GitHub 安装:

/plugin uninstall terse@terse

移除statusLine条目,来自~/.claude/settings.json,如果你安装了计量器。卸载会删除插件的数据目录,计量器副本就存放在那里。

许可证

MIT。