课程0基础Agent开发课 / Claude-Code实战教程 / 三种交互模式
— 17 min read

三种交互模式

> **时效说明**:本文内容以 2026 年 3 月为基准。

Claude Code:三种交互模式

时效说明:本文内容以 2026 年 3 月为基准。

6.1 Ask模式——只问不改

Claude Code有三种使用姿势,分别对应不同的场景。很多人只用其中一种,其实三种模式组合起来才是最高效的工作流。

Ask模式是纯只读的。Claude Code在这个模式下绝对不会修改任何文件,也不会执行任何写操作。它的任务就是帮你理解代码、定位问题、解释逻辑。

什么时候用Ask模式?

你接手了一个老项目,看到一段复杂的SQL,不知道它在干什么——用Ask模式让Claude解释。你发现一个NullPointerException,但堆栈太深,不知道根本原因在哪——用Ask模式让Claude帮你推理。你想知道某个接口的完整调用链——用Ask模式让Claude追踪。

示例对话:

code
你:这段代码里的 synchronized 锁的粒度怎么样?有没有性能问题?

Claude:这里锁的是整个方法,每次调用都会阻塞。根据我看到的调用频率和你
       的业务场景,建议改成ConcurrentHashMap + computeIfAbsent,
       可以把锁粒度降到key级别...(继续分析)

Ask模式不是Claude Code的默认状态,但你可以在对话里明确说"不要修改任何文件,只帮我分析",Claude会遵守这个约束。

6.1.1 Ask模式的实战示例:代码审查

代码审查是Ask模式最典型的用途之一。假设你在Code Review时发现了一段可疑的代码,想快速评估风险:

code
你:帮我审查这个登录方法,重点看安全性问题:

    @PostMapping("/login")
    public Result<String> login(@RequestParam String username,
                                @RequestParam String password) {
        String sql = "SELECT * FROM users WHERE username='"
                    + username + "' AND password='" + password + "'";
        User user = jdbcTemplate.queryForObject(sql, User.class);
        return Result.success(generateToken(user));
    }

Claude:这段代码有严重的安全问题,我从高到低列一下:

        P0(必须立即修复):
        1. SQL注入漏洞。username和password直接拼接进SQL,攻击者
           输入 admin' OR '1'='1 就能绕过密码验证,登录任意账户。
           修复:使用PreparedStatement或MyBatis的#{}参数绑定。

        2. 密码明文存储/比对。密码应该用BCrypt等算法加密存储,
           不应该在SQL里直接比对明文。

        P1(近期需要修复):
        3. 没有登录失败计数。暴力破解没有任何防护。
        4. 没有验证码或其他二次验证。

        P2(建议优化):
        5. queryForObject在查不到时会抛EmptyResultDataAccessException,
           异常处理逻辑缺失,会导致500错误暴露给用户。

这种Review,Claude几秒钟就能完成,比你一行行看要全面很多。而且在Ask模式下,它只给分析,不会动你的代码。

6.1.2 Ask模式的实战示例:性能分析

code
你:这个查询为什么慢?执行计划我粘给你看:

    EXPLAIN SELECT o.*, u.name, u.phone
    FROM orders o
    LEFT JOIN users u ON o.user_id = u.id
    WHERE o.status = 1
    AND o.create_time > '2024-01-01'
    ORDER BY o.create_time DESC
    LIMIT 20;

    执行计划:type=ALL, rows=2400000, Extra=Using filesort

Claude:问题很明显,有两个:

        1. type=ALL 表示全表扫描——orders表240万行全部扫了一遍。
           create_time和status字段上没有索引,或者索引没有被利用。

           建议:创建联合索引 INDEX idx_status_time (status, create_time)
           把status放前面,因为它的区分度低(只有几个值),
           但配合时间范围查询效果很好。

        2. Using filesort 表示结果在内存/磁盘里做了额外排序,
           说明ORDER BY字段不在索引里,或者索引没有覆盖排序。

           如果联合索引包含(status, create_time),排序也能走索引。

        预估效果:加索引后,这个查询从全表扫240万行降到扫索引匹配行,
        大约是几百到几千行,执行时间从秒级降到毫秒级。

6.1.3 Ask模式的实战示例:调用链追踪

当你面对一个陌生的大型项目,最难的是理解"一个请求进来,到底经过了哪些地方"。Ask模式可以帮你快速建立这个认知:

code
你:我想了解 POST /api/order/create 这个接口的完整处理链路,
    从Controller到数据库,帮我梳理一下

Claude:我来追踪一下...
        (读取 OrderController.java)
        (读取 OrderService.java)
        (读取 OrderMapper.java)

        链路如下:

        1. OrderController.createOrder()
           - 参数校验(@Valid + OrderCreateRequest)
           - 调用 orderService.createOrder(request, userId)

        2. OrderService.createOrder()
           - 查库存:inventoryService.checkAndDeduct(items)
           - 计算价格:priceService.calculate(items, couponId)
           - 创建订单主记录:orderMapper.insert(order)
           - 创建订单明细:orderItemMapper.batchInsert(items)
           - 发MQ消息:rocketMQTemplate.send("order-created", event)

           注意:步骤2-5在同一个@Transactional事务里,MQ发送在事务外

        3. OrderMapper.insert()
           → 写 orders 表

        4. OrderItemMapper.batchInsert()
           → 写 order_items 表(批量插入)

        关键风险点:我注意到库存扣减和订单创建不在同一个分布式事务里,
        如果订单写成功但消息发送失败,库存会扣了但订单消息丢失,
        可能导致下游服务认为没有新订单。你们有做消息补偿吗?

Ask模式给你的不只是代码层面的答案,还有业务层面的风险提示——而且它一行代码都没动。


6.2 Plan模式——先规划再执行

Plan模式下,Claude会先给你一份详细的执行计划,等你确认之后才开始真正动手。

这个模式在以下情况特别有价值:多个文件需要联动修改时;你不确定改动范围有多大时;涉及数据库Schema变更等高风险操作时;架构调整前需要对齐思路时。

6.2.1 Plan模式的完整工作流

Plan模式的核心价值是在执行前充分沟通。下面是一个完整的工作流示例。

场景:你接到需求,要在现有的电商系统里加一个"限时秒杀"功能。

第一轮:让Claude规划

code
你:我需要给订单服务加一个限时秒杀功能。秒杀商品有特殊的库存扣减逻辑
    (需要用Redis做预减库存),支付超时后需要自动释放库存。先给我一个
    改动计划,不要动代码。

Claude:好,我来分析改动范围:

        需要新增的文件(5个):
        - SeckillController.java:秒杀入口接口
        - SeckillService.java:核心业务逻辑
        - SeckillInventoryService.java:Redis库存管理
        - SeckillOrderConsumer.java:处理超时释放
        - SeckillConfig.java:秒杀相关配置

        需要修改的文件(3个):
        - OrderService.java:增加秒杀订单的特殊分支逻辑
        - pom.xml:增加 redisson 依赖(分布式锁)
        - application.yml:增加秒杀相关配置项

        需要新增的数据库表(1个):
        - seckill_activity:秒杀活动表

        预估改动量:约400-600行新代码,修改约80行现有代码

第二轮:讨论方案细节

code
你:库存用Redis的话,如果Redis宕机怎么办?你用什么方案保证可用性?

Claude:这是个好问题。主要有两种方案:

        方案A(降级到数据库):Redis宕机时,自动降级到直接扣数据库库存,
        用数据库行锁保证不超卖。缺点是秒杀期间并发高,数据库可能扛不住。

        方案B(Redis高可用 + 本地缓存兜底):用Redis Cluster保证可用性,
        同时维护一个本地原子计数器,Redis不可用时用本地计数,
        请求量打到数据库之前先用本地计数过滤大部分请求。

        我建议方案B。你们的Redis是单机还是集群部署?

你:现在是单机,短期不会改集群

Claude:了解。那建议用方案A加一个简单的熔断:
        Redis宕机超过5秒,自动切换到数据库模式,同时限流,
        让QPS从10000降到500,数据库可以扛住。
        熔断恢复后再切回Redis模式。这样既保证可用性,又不改动部署架构。
        要用这个方案吗?确认后我再给出具体的代码改动计划。

第三轮:确认后执行

code
你:好,就用这个方案,开始改代码吧

Claude:明白,开始执行...

这个过程可以来回好几轮。Plan模式的价值就在于,你们把分歧和风险在"动手之前"就解决掉了,而不是改了一半发现方向不对。

6.2.2 切换到Plan模式的方式

两种方式可以进入Plan模式:

方式一:按 Shift+Tab 循环切换,直到右上角显示 Plan 模式

方式二:在消息里直接说明:

code
先给我一个改动计划,不要动任何代码,我确认后再执行

Claude会把这当作一个软约束遵守。注意:Plan模式下,Claude仍然会读取文件(这是只读操作),但不会写文件或执行命令。

6.2.3 Plan模式的常见误用

误用1:简单任务也用Plan模式

如果你要做的是"把这个方法的返回值从String改成Integer",根本不需要Plan模式。Plan模式的价值在于复杂任务,简单任务用它只是浪费时间。

误用2:确认计划后不检查执行过程

Plan模式只是确保开始方向是对的,并不保证执行过程完全符合预期。确认计划后切回执行模式,还是要观察每一步的diff。


6.3 Edit模式——直接执行

Edit模式是最直接的,你说改什么,Claude就改什么。在它真正写入文件之前,会展示一个diff让你确认。

code
你:把登录接口的成功响应码从200改为201

Claude:我将修改 UserController.java:

- return ResponseEntity.ok(result);
+ return ResponseEntity.status(HttpStatus.CREATED).body(result);

确认修改吗?[y/N]

你看到diff没问题,按y确认,文件才会真正被改。如果改得不对,直接回复"不对,xxx地方有问题",Claude会重新给出修改方案。

6.3.1 diff界面详解:每个选项的含义

在Edit模式下,Claude展示diff时,你有几个操作选项:

选项 操作 说明
y 接受并应用 确认这个改动,写入文件
n 拒绝 跳过这个改动,文件不变
a 全部接受 接受后续所有改动,不再逐个确认
e 手动编辑 打开编辑器自己修改这段代码
d 查看diff详情 展示更多上下文
s 跳过 暂时跳过,后面再决定

实际使用中,ya用得最多。e在你觉得Claude的改法不完全对、但你知道应该怎么改时很有用——不用让Claude重来,直接自己动手微调。

6.3.2 多文件修改的处理方式

当一个任务涉及多个文件时,Claude会按顺序展示每个文件的diff。你可以对每个文件单独决定:

code
Claude:需要修改以下文件:

        1/3 - OrderService.java
        [diff展示]
        接受吗?[y/N/a]

        > y

        2/3 - OrderMapper.java
        [diff展示]
        接受吗?[y/N/a]

        > n(这个我不想动)

        3/3 - OrderServiceTest.java
        [diff展示]
        接受吗?[y/N/a]

        > y

第2个文件你拒绝了,Claude会重新评估——因为Mapper没改,Service里对Mapper的调用方式可能也需要调整。这时候Claude会提示你,或者重新出一个不依赖Mapper改动的方案。

6.3.3 Edit模式的最佳使用姿势

指令要具体,不要模糊:

code
# 模糊(Claude可能改很多地方)
帮我优化这个服务

# 具体(Claude知道边界在哪里)
帮我把 OrderService 里的 getOrdersByStatus 方法
从循环查询改成一次批量查询,只改这一个方法

告诉Claude不要动哪些地方

code
帮我添加缓存到 getUserById 方法,
但不要修改方法签名,也不要改其他任何方法

先问再改:对于复杂的修改,可以先在Ask模式里确认Claude的理解,再切到Edit模式执行。


6.4 如何在对话中切换模式

6.4.1 通过Shift+Tab切换

这是最直接的方式。每按一次 Shift+Tab,在三种模式间循环切换:

code
Ask → Plan → Edit(默认确认) → Ask → ...

右上角会显示当前是哪个模式。

6.4.2 通过对话语言软切换

你不需要手动切模式,在消息里用自然语言描述你想要的行为,Claude会理解:

你说的 Claude的行为
"只分析,不要改" Ask模式行为
"先给我一个计划" Plan模式行为
"直接改,别问我了" Edit模式 + 减少确认
"每个文件改之前都告诉我" Edit模式 + 逐步确认

这种方式比切模式更灵活——你可以在一条消息里同时指定:"先给我分析,如果没问题再帮我改,改之前每个文件都让我确认"。


6.5 不同任务类型选哪种模式

这张决策表帮你快速判断:

任务类型 推荐模式 原因
接手陌生代码、理解逻辑 Ask 只需要信息,不需要改动
Code Review Ask 审查不需要修改
性能分析、找瓶颈 Ask 分析阶段,不动代码
安全漏洞检查 Ask 先找漏洞,另起对话再修
新功能开发(>3个文件) Plan → Edit 复杂任务先规划
架构重构 Plan → Edit 风险高,必须先对齐方案
数据库Schema变更 Plan → Edit 不可逆操作
小范围Bug修复 Edit 明确知道要改什么
单文件改动 Edit 范围小,直接改
批量重命名、格式化 Edit + 自动接受 低风险、高频操作

6.6 模式组合的高效工作流:从发现Bug到修复验证

下面是一个完整的案例,展示如何把三种模式串联起来处理一个真实问题。

场景:线上报告,用户反馈下单后偶尔会出现"订单金额与支付金额不一致",但概率很低。

第一阶段:Ask模式定位问题

code
你:线上有个概率性问题:用户下单后订单金额和支付金额不一致,
    概率大约千分之三。帮我分析可能的原因。

Claude:(读取 OrderService.java、PaymentService.java、
         OrderController.java)

        这个概率性不一致通常有几个来源:

        1. 并发问题:我注意到 calculateTotalAmount() 方法里,
           先查优惠券再计算金额,中间没有加锁。如果用户在这个窗口期
           修改了订单(加购、换优惠券),金额就会不一致。

        2. 浮点数精度:我看到 discountAmount 是 double 类型,
           参与多次乘除后有精度误差。应该用 BigDecimal。

        3. 缓存问题:价格是从缓存读的,TTL是5分钟。如果商品价格
           刚刚改了,有5分钟内的订单可能用的还是旧价格。

        千分之三的概率指向并发问题,因为浮点精度误差通常不会
        这么低频,缓存问题应该会更集中在价格变更后的短时间内。

        建议先排查并发场景——问题最可能在这里。

你:并发那个场景,你能重现条件吗?

Claude:可以模拟。下单流程是:
        1. 用户加入购物车(此时快照价格A)
        2. 打开结算页,calculateTotalAmount()计算出金额X(用了优惠券C)
        3. 如果用户在2秒内换了一张优惠券,再次触发计算,
           优惠券C失效,新优惠券D生效,金额变成Y
        4. 但用户提交的是金额X,实际扣款用的是Y

        这个时间窗口只有前端渲染结算页到用户点支付的这段时间,
        所以概率不高,但存在。

此时我们已经定位到根本原因,但一行代码都没动。

第二阶段:Plan模式制定修复方案

code
你:好,并发那个问题确认了。给我一个修复方案,
    不要改代码,我们先讨论。

Claude:修复方案有两个思路:

        方案A(服务端二次校验):
        在支付前,服务端重新计算一次当前金额,
        如果与用户提交的金额误差超过0.01元,拒绝支付,提示用户重新结算。
        改动量:OrderService + PaymentService,约50行

        方案B(版本号锁):
        结算页生成时带一个版本号,提交时校验版本号是否仍然有效。
        如果期间有任何价格/优惠券变化,版本号失效,要求重新结算。
        改动量:OrderService + OrderController + 前端,约120行

        方案A改动小,但用户体验略差(支付时才报错)。
        方案B体验好(结算时就能发现问题),但需要前端配合。

        建议方案A,因为:1.改动范围可控;2.支付拦截是更安全的兜底。

你:方案A可以,但校验阈值不一定是0.01,金额大的订单误差也会大,
    用百分比更合适,比如误差超过0.1%就拒绝

Claude:完全正确,用相对误差更严谨。
        修改:校验逻辑改为 |实际金额 - 提交金额| / 提交金额 > 0.001
        这样不论订单大小,误差容忍度都是0.1%。
        确认这个方案吗?

你:确认,开始改

第三阶段:Edit模式执行修改

code
Claude:我将修改以下文件:

        1/2 - PaymentService.java
        在 processPayment() 方法里增加金额校验:

        [diff展示]
        + // 二次校验金额,防止并发导致的金额不一致
        + BigDecimal submittedAmount = order.getSubmittedAmount();
        + BigDecimal actualAmount = orderService.recalculateAmount(order);
        + BigDecimal diff = submittedAmount.subtract(actualAmount).abs();
        + if (diff.divide(submittedAmount, 4, HALF_UP).compareTo(THRESHOLD) > 0) {
        +     throw new AmountMismatchException("订单金额已变化,请重新结算");
        + }

        接受吗?[y/N]

        > y

        2/2 - OrderService.java
        新增 recalculateAmount() 方法:

        [diff展示]
        接受吗?[y/N]

        > y

Claude:修改完成。建议运行以下测试验证:
        mvn test -Dtest=PaymentServiceTest -pl order-service

你:测试通过了,帮我补充一个并发测试用例,覆盖刚才修复的场景

Claude:(继续在Edit模式里补测试)

这个完整流程大约花了20分钟,从问题定位到修复加测试,三种模式各司其职,没有一步是多余的。


6.7 常见问题

Q:我在Edit模式里说"帮我改",Claude改了但方向不对,怎么办?

Ctrl+Z 不能撤销Claude的文件修改(Claude Code不提供内置的撤销功能)。在开始大范围修改前,先用git提交或者做备份:

bash
git stash  # 把当前改动暂存,方便回滚

如果Claude改错了,你可以:

  1. git checkout -- 文件名 恢复单个文件
  2. git stash pop 恢复改动前的状态
  3. 直接告诉Claude"你刚才改错了,帮我恢复到修改前",它能生成一个反向diff

Q:Plan模式里讨论了很久,上下文快满了怎么办?

在切到执行模式前,先在CLAUDE.md里记录已确认的方案要点。这样就算 /compact 压缩了历史,Claude在执行时仍然能参考方案文档。

Q:Edit模式下Claude改了20个文件,我来不及每个都仔细看怎么办?

a(全部接受)先让它改完,然后用 git diff 完整审查所有改动,发现问题再告诉Claude修正。这比在Claude每改一个文件时盯着看更高效——因为你可以对着完整的改动做系统性评估。


本页目录