Claude Code Learning 学习模式怎么用:内置风格实测 4 次都没留代码给人写,自定义输出风格才管用(2.1.299)

0 阅读10分钟

导读:知乎上"作为大学生几乎丧失手搓代码的能力,需要焦虑吗"有 122 万浏览,掘金 AI 热榜第二是"AI 正在蚕食我们的编码能力"。Claude Code 自带一个 Learning 输出风格,官方说它会把有取舍的那几行代码留给你写。我拿同一个 Java 小需求在 2.1.299 上跑了 9 次:内置 Learning 一次都没留,自己写一个 20 行的风格文件才稳定生效。这篇讲怎么配、实测结果和该怎么用。


一、先说清楚:输出风格是什么,Learning 想解决什么

输出风格(output style) 是一段写给 Claude 的固定指令,决定它整场会话怎么跟你说话、怎么干活。默认风格就是"把活干完";Claude Code 另外内置了四种:

风格变化
Proactive不问琐碎决定,直接开工
Concise先给结果,少说过程
Explanatory写代码时附带 ★ Insight 解释块,讲为什么这么写
Learning在 Explanatory 基础上,把有设计取舍的几行用待写标记留给你写,然后停下等你

Learning 针对的就是"AI 全写了、自己越来越不会写"这件事:样板代码 AI 写,业务判断、边界处理这种真正练手的部分留给人。留给人的位置,文件里会是这样一行注释(下文统一叫"待写标记"):

// TODO(human): 按业务规则算出实付金额:满 200 减 20、叠加折扣券、保留两位小数、不能小于 0

这不是空担心。Anthropic 2026 年 1 月发过一项随机对照实验:52 名(大多是初级)工程师学一个没用过的 Python 库,用 AI 助手的一组事后测验平均 50 分,手写的一组 67 分,差距最大的是调试题;完成时间两组差不多。研究也提到,用 AI 时多追问、多让它解释的人,分数没掉那么多。

什么时候值得动它:

  • 在学新框架、新语言,或者带新人,想保留"自己动手"这一环
  • 觉得 AI 写的代码自己已经看不太懂了
  • 日常赶工期、只要结果的时候,不用开,默认风格就好

二、测试方法

项值
Claude Code2.1.299(/output-style 命令要 2.1.269 及以上)
运行方式claude -p 非交互模式,--setting-sources project,只读项目自己的 .claude/settings.json,排除我全局配置的干扰
模型Sonnet 7 次,Opus 1 次
需求同一句话:给 CouponCalculator.calculate 实现"满 200 减 20,可叠加折扣券,保留两位小数,不能小于 0",再写 Main 跑三个例子

这个需求故意带着没说清楚的取舍:先减后折还是先折后减(250 元配 9 折分别是 207 和 205)、"满 200"含不含 200、在哪一步舍入。按官方文档的说法,这正是 Learning 应该留给人写的地方。

起始代码只有一个空方法:

package demo;

import java.math.BigDecimal;

public class CouponCalculator {

    /**
     * 计算订单实付金额。
     *
     * @param orderAmount  订单原价
     * @param discountRate 折扣券折扣率,例如 0.9 表示 9 折;没有折扣券时传 null
     * @return 实付金额
     */
    public BigDecimal calculate(BigDecimal orderAmount, BigDecimal discountRate) {
        throw new UnsupportedOperationException("not implemented");
    }
}

三、怎么开 Learning

三种方式,效果一样:

# 1. 会话里输入(2.1.269 起支持,不区分大小写)
/output-style learning

# 2. 终端里 /config,选 Output style

第三种是直接写配置文件,项目级放 .claude/settings.json 或 .claude/settings.local.json,想所有项目都生效就写进 ~/.claude/settings.json:

{
  "outputStyle": "Learning"
}

⚠️ 配置文件里的值区分大小写,要写 Learning。官方文档说写成 learning 会静默退回默认风格,不报错。/output-style 命令则不区分大小写。


四、实测:内置 Learning 4 次,一次都没留待写标记

先跑默认风格做对照,再跑 Learning:

第几次风格模型文件里有待写标记回复里有 Insight花费(美元)
1DefaultSonnet无无0.180
2LearningSonnet无有0.195
3LearningSonnet无有0.096
4LearningSonnet无有0.115
5LearningOpus无有0.370

风格确实生效了:4 次 Learning 的回复末尾都有 ★ Insight 块,讲了先减后折和先折后减的差别、new BigDecimal("0.9") 和 new BigDecimal(0.9) 的区别。但 calculate 每次都被 Claude 直接写完了,编译运行通过,然后告诉我"这几处需求没写明,我按下面的方式定了"。Opus 那次最接近"教学",结尾补了一句建议我自己加个例子、改改先后顺序看结果怎么变,但代码还是它写的。

所以 Learning 在我这个场景下,实际效果只等于 Explanatory。

为什么不留?我没找到确切原因,只能说两个可能:

  • 官方文档自己就写了,输出风格只是给 Claude 的指令,不保证某件事一定发生
  • 我的需求里写了"编译运行验证",等于要求它交付能跑的结果,和"留一半给人写"冲突。另外我只测了 claude -p,交互式会话里它能停下来等你,表现可能不一样,这个我没测

不管原因是哪个,结论是:想稳定地让 AI 把代码留给你,别指望内置 Learning 自己判断,把规则写死。


五、自己写一个输出风格:3 次都留了

输出风格就是一个 Markdown 文件,放在项目的 .claude/output-styles/ 下(个人全局放 ~/.claude/output-styles/)。我写的这个只有 20 行,规则写得很具体:哪些行留给人、留下后说什么、然后停下:

---
name: Java练手
description: 业务规则和边界处理留给我自己写,其余照常完成
keep-coding-instructions: true
---

每次实现功能时,把包含业务规则判断的那几行(金额计算顺序、边界条件、异常处理)留给用户自己写:

1. 在文件里写好方法签名、调用方和测试代码,只在需要用户写的位置放一行 `// TODO(human): <要写什么>`,不要替用户写出这几行
2. 在回复里说明:已经做好了什么、用户要写哪几行、要考虑哪些取舍(不给答案)
3. 然后停下来,等用户说写好了再继续:编译运行、检查用户的写法、指出问题

样板代码(getter、main 方法、编译命令)照常直接写完。

keep-coding-instructions: true 一定要加,它保留 Claude Code 默认的软件工程指令,否则 Claude 就不按写代码的方式工作了。

启用时 outputStyle 填 frontmatter 里的 name:

{
  "outputStyle": "Java练手"
}

同一句需求跑 3 次(Sonnet):

第几次文件里有待写标记Main 写好了花费(美元)
1有有0.084
2有有,每个例子打印 OK / FAIL0.080
3有有0.083

每次 calculate 里都只剩一行待写标记加一个占位的 throw,回复里列出要写的几行和要权衡的点。第 3 次给的提示我觉得最有用:

恰好 200 算不算满?用 compareTo,不要用 equals,因为 200.0 和 200.00 的 equals 是 false。

它只给提示、不给答案,这正是规则里第 2 条要的。顺带一提,因为少写了业务代码,这 3 次每次只花 0.08 美元左右,比 Learning 和默认风格都便宜。


六、写完交回去:它抓出了我故意埋的边界 bug

接着在第 1 次的会话里,我自己把待写标记填上,故意把"满 200"写成大于(应该是大于等于):

public BigDecimal calculate(BigDecimal orderAmount, BigDecimal discountRate) {
    BigDecimal amount = orderAmount;
    if (amount.compareTo(new BigDecimal("200")) > 0) {   // 故意写错:恰好 200 元不会减
        amount = amount.subtract(new BigDecimal("20"));
    }
    if (discountRate != null) {
        amount = amount.multiply(discountRate);
    }
    return amount.max(BigDecimal.ZERO).setScale(2, java.math.RoundingMode.HALF_UP);
}

然后用 --resume 接上原来的会话,只说一句"写好了,你检查一下":

# 在项目目录执行,<session-id> 是第一次运行时 JSON 输出里的 session_id
claude -p "写好了,你检查一下" --resume <session-id> --model sonnet

它的回复(节选):

编译通过,三个例子的结果都对,但有一个边界 bug。

订单=200, 折扣券=null -> 实付=200.00   ← 有问题
最后一行是我在 Main 里新加的边界例子,恰好 200 元。

CouponCalculator.java:16 用的是 compareTo(...) > 0。"满 200" 应该包含 200 元,
所以订单恰好 200 元时应该减 20,实付 180.00,现在却是 200.00。把比较改成"大于等于"就行。

原来的三个例子都碰不到这个边界,是它自己往 Main 里加了一个 200 元的用例跑出来的。另外它没有动我写的代码,只指出问题让我自己改,还提了两个"不影响结果"的点:RoundingMode 可以 import,orderAmount 为 null 没处理。

这一轮花了 0.135 美元。整个"AI 搭架子、人写关键几行、AI 跑测试挑错"的循环是通的。


七、怎么用才不变成摆设

几条建议:

  • 规则写具体。"留给用户写"太模糊,写清楚哪类代码留(业务规则、边界、异常)、哪类不留(样板、main、编译命令),以及留下后要说什么
  • 按项目放,不要全局开。练手的项目放项目级 .claude/output-styles/,赶工的项目用默认风格;全局开着很快就会被你关掉
  • 留的量要小。一次留一个方法里的几行最合适,留整个类就变成你在写、它在看
  • 交回时要它跑边界用例。这次它是主动加的,你也可以把"检查时补边界用例"写进风格文件第 3 条
  • 团队带新人可以把风格文件提交到仓库,新人在项目里打开 Claude Code 就是这个模式

输出风格做不到的事也要知道:它是"建议"不是"强制"。要保证每次都发生的事(比如编辑后必须跑格式化、禁止某个命令),官方建议用 hook,不是输出风格。


八、速查表

要做的事怎么做
临时切换风格会话里 /output-style learning(2.1.269+)
项目里固定风格.claude/settings.json 写 "outputStyle": "Learning"(区分大小写)
所有项目默认~/.claude/settings.json,项目配置优先级更高
自定义风格.claude/output-styles/java-practice.md,frontmatter 写 name、description、keep-coding-instructions: true
只要解释不要留代码用 Explanatory
换回默认/output-style default 或删掉 outputStyle

九、常见问题

Q:会话中途切风格,什么时候生效?

官方文档说从下一条消息开始生效(2.1.251 之前要 /clear 或开新会话)。文档另有一节讲切换后第一条消息对提示词缓存的影响,这部分我没测花费。

Q:Learning 在交互模式下会不会表现好一些?

可能会,我只测了 claude -p。如果你在交互会话里用 Learning 稳定看到过 ● Learn by Doing 和待写标记,欢迎在评论区说一下你的版本和需求类型。

Q:自定义风格会不会让 Claude 写代码变差?

加了 keep-coding-instructions: true 的话,默认的工程指令保留,只是追加你的规则。我这 3 次搭的架子(Main、编译命令、例子)都能直接用。

Q:桌面版 App 里怎么设置?

桌面版里 /config 会打开设置页而不是菜单,文档建议直接在 .claude/settings.local.json 里写 outputStyle。


参考链接


你用 AI 写代码时,会刻意留一部分自己写吗?留的是哪类代码:业务逻辑、SQL,还是测试?评论区说说,我把好的做法整理进风格文件里。

觉得有用的话点个赞、收个藏,下次想练手时翻得到。