Skip to content

第三部分:代码调试(Debugging)专项技巧

代码调试是软件开发过程中的重要环节,AI 可以成为开发者的得力助手。以下是几种有效的代码调试提示词技巧:

1. 报错喂入法:正确处理 StackTrace 和日志

基本原理

报错喂入法是指将完整的错误信息、堆栈跟踪(StackTrace)和相关日志提供给 AI,让 AI 分析错误原因并提供修复方案。

关键要素

  • 完整的错误信息:包括错误类型、错误消息和完整的堆栈跟踪
  • 相关代码:与错误相关的代码片段
  • 运行环境:运行时环境、依赖版本等信息
  • 重现步骤:如何重现该错误

实战示例

输入示例

Role: 你是一位资深的 JavaScript 调试专家,擅长分析和解决各种运行时错误。

Context:
- 技术栈:Node.js 16 + Express
- 错误信息:

TypeError: Cannot read properties of undefined (reading 'name') at getUserDetails (/path/to/app.js:15:20) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async /path/to/app.js:25:10

- 相关代码:
```javascript
// app.js
const express = require('express');
const app = express();

function getUserDetails(user) {
  return user.name; // 第15行
}

app.get('/user/:id', async (req, res) => {
  const user = await fetchUser(req.params.id);
  const details = getUserDetails(user); // 第25行
  res.json(details);
});

async function fetchUser(id) {
  // 模拟数据库查询
  if (id === '123') {
    return { id: '123', name: 'John Doe' };
  }
  return null; // 当用户不存在时返回 null
}

Task:

  • 分析错误原因
  • 提供修复方案
  • 确保代码在用户不存在时能够正确处理

Output Format:

  • 首先分析错误原因
  • 然后提供修复后的代码
  • 最后解释修复方案

#### 输出示例

错误分析

错误类型:TypeError: Cannot read properties of undefined (reading 'name')

错误原因:

  • getUserDetails 函数中,尝试访问 user.name,但 user 参数为 undefined
  • fetchUser 函数返回 null 时(用户不存在),getUserDetails(null) 会导致此错误

修复方案

javascript
// app.js
const express = require('express');
const app = express();

function getUserDetails(user) {
  if (!user) {
    return { error: 'User not found' };
  }
  return user.name;
}

app.get('/user/:id', async (req, res) => {
  const user = await fetchUser(req.params.id);
  if (!user) {
    return res.status(404).json({ error: 'User not found' });
  }
  const details = getUserDetails(user);
  res.json(details);
});

async function fetchUser(id) {
  // 模拟数据库查询
  if (id === '123') {
    return { id: '123', name: 'John Doe' };
  }
  return null; // 当用户不存在时返回 null
}

修复解释

  1. getUserDetails 函数中:添加了空值检查,当 usernullundefined 时返回错误信息
  2. 在路由处理函数中:添加了用户存在性检查,当用户不存在时直接返回 404 状态码和错误信息
  3. 这样处理:避免了尝试访问不存在对象的属性,同时提供了更友好的错误提示

### 提示词模板

Role: 你是一位资深的 [语言/框架] 调试专家,擅长分析和解决各种运行时错误。

Context:

  • 技术栈:[技术栈信息]
  • 错误信息:
    [完整的错误信息和堆栈跟踪]
  • 相关代码:
    [与错误相关的代码片段]

Task:

  • 分析错误原因
  • 提供修复方案
  • 确保代码能够正确处理类似情况

Output Format:

  • 首先分析错误原因
  • 然后提供修复后的代码
  • 最后解释修复方案

## 2. 假定修复法:引导 AI 提出多个解决方案

### 基本原理
假定修复法是指引导 AI 分析问题并提出多个可能的解决方案,然后对这些方案进行评估和比较,选择最佳方案。

### 实施步骤
1. **描述问题**:详细描述代码中存在的问题
2. **要求多个解决方案**:明确要求 AI 提供多个可能的修复方案
3. **评估标准**:提供评估解决方案的标准(如性能、可读性、维护性等)
4. **方案比较**:要求 AI 对各个方案进行比较和分析
5. **推荐方案**:要求 AI 推荐最佳解决方案

### 实战示例

#### 输入示例

Role: 你是一位资深的 Python 性能优化专家,擅长分析和解决代码性能问题。

Context:

  • 技术栈:Python 3.9
  • 问题描述:以下代码在处理大型列表时性能较差
  • 相关代码:
    python
    def process_large_list(data):
        result = []
        for item in data:
            if item % 2 == 0:
                squared = item ** 2
                result.append(squared)
        return result
    
    # 调用示例
    large_list = list(range(1000000))
    result = process_large_list(large_list)

Task:

  • 分析代码性能问题
  • 提供至少 3 种不同的优化方案
  • 对每种方案进行性能、可读性和维护性的评估
  • 推荐最佳解决方案

Output Format:

  • 首先分析性能问题
  • 然后提供 3 种优化方案
  • 对每种方案进行评估
  • 最后推荐最佳方案

#### 输出示例

性能问题分析

当前代码的性能问题:

  • 使用了显式的 for 循环,Python 中的 for 循环相对较慢
  • 对每个元素进行条件判断和计算
  • 列表 append 操作虽然高效,但仍有优化空间

优化方案

方案 1:使用列表推导式

python
def process_large_list(data):
    return [item ** 2 for item in data if item % 2 == 0]

评估

  • 性能:显著提升,列表推导式比显式 for 循环更快
  • 可读性:代码更简洁,可读性好
  • 维护性:代码量减少,维护成本降低

方案 2:使用 filter 和 map 函数

python
def process_large_list(data):
    return list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, data)))

评估

  • 性能:与列表推导式相当
  • 可读性:函数式编程风格,对一些开发者来说可能不如列表推导式直观
  • 维护性:代码简洁,但逻辑可能不如列表推导式清晰

方案 3:使用 NumPy 库

python
import numpy as np

def process_large_list(data):
    arr = np.array(data)
    even_arr = arr[arr % 2 == 0]
    return (even_arr ** 2).tolist()

评估

  • 性能:对于大型列表,NumPy 通常比纯 Python 实现快得多
  • 可读性:代码清晰,表达意图明确
  • 维护性:依赖 NumPy 库,增加了项目依赖

推荐方案

推荐方案 1:使用列表推导式

理由:

  • 性能提升显著,与其他方案相当或更好
  • 代码简洁易读,符合 Python 风格
  • 不引入额外依赖,保持代码轻量
  • 维护成本低,逻辑清晰

对于特别大的数据集(如超过 1000 万元素),可以考虑方案 3 使用 NumPy 进一步提升性能。


### 提示词模板

Role: 你是一位资深的 [语言/框架] 专家,擅长分析和解决 [具体问题类型] 问题。

Context:

  • 技术栈:[技术栈信息]
  • 问题描述:[详细描述问题]
  • 相关代码:
    [相关代码片段]

Task:

  • 分析问题原因
  • 提供至少 3 种不同的解决方案
  • 对每种方案进行 [评估标准] 的评估
  • 推荐最佳解决方案

Output Format:

  • 首先分析问题
  • 然后提供多种解决方案
  • 对每种方案进行评估
  • 最后推荐最佳方案

## 3. 逐步排查法:利用单元测试复现 Bug

### 基本原理
逐步排查法是指利用提示词让 AI 编写单元测试来复现 Bug,然后通过测试来验证修复方案的有效性。

### 实施步骤
1. **描述 Bug**:详细描述 Bug 的现象和预期行为
2. **编写测试**:要求 AI 编写单元测试来复现 Bug
3. **实现修复**:基于测试结果,实现修复方案
4. **验证修复**:运行测试验证 Bug 是否被修复
5. **回归测试**:确保修复不会引入新的问题

### 实战示例

#### 输入示例

Role: 你是一位资深的 Java 测试专家,擅长编写单元测试和调试代码。

Context:

  • 技术栈:Java 11 + JUnit 5
  • Bug 描述:当输入为空字符串时,StringUtils.capitalize() 方法会抛出 NullPointerException
  • 相关代码:
    java
    public class StringUtils {
        public static String capitalize(String str) {
            if (str.length() == 0) {
                return str;
            }
            return Character.toUpperCase(str.charAt(0)) + str.substring(1);
        }
    }

Task:

  • 编写单元测试来复现这个 Bug
  • 修复 StringUtils.capitalize() 方法
  • 验证修复是否成功
  • 确保修复不会破坏现有功能

Output Format:

  • 首先编写单元测试来复现 Bug
  • 然后提供修复后的代码
  • 最后验证修复结果

#### 输出示例

单元测试

java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class StringUtilsTest {
    
    @Test
    public void testCapitalizeWithEmptyString() {
        // 测试空字符串,应该返回空字符串而不是抛出异常
        assertDoesNotThrow(() -> {
            String result = StringUtils.capitalize("");
            assertEquals("", result);
        });
    }
    
    @Test
    public void testCapitalizeWithNull() {
        // 测试 null,应该返回 null 或抛出明确的异常
        assertDoesNotThrow(() -> {
            String result = StringUtils.capitalize(null);
            assertNull(result);
        });
    }
    
    @Test
    public void testCapitalizeWithNormalString() {
        // 测试正常字符串,应该正确大写首字母
        assertEquals("Hello", StringUtils.capitalize("hello"));
        assertEquals("World", StringUtils.capitalize("World"));
    }
}

修复方案

java
public class StringUtils {
    public static String capitalize(String str) {
        if (str == null) {
            return null;
        }
        if (str.length() == 0) {
            return str;
        }
        return Character.toUpperCase(str.charAt(0)) + str.substring(1);
    }
}

验证结果

  1. 测试空字符串:现在可以正确处理空字符串,返回空字符串而不是抛出异常
  2. 测试 null:现在可以正确处理 null,返回 null 而不是抛出异常
  3. 测试正常字符串:功能保持不变,仍然可以正确大写首字母

修复成功,同时保持了向后兼容性。


### 提示词模板

Role: 你是一位资深的 [语言/框架] 测试专家,擅长编写单元测试和调试代码。

Context:

  • 技术栈:[技术栈信息]
  • Bug 描述:[详细描述 Bug 现象]
  • 相关代码:
    [相关代码片段]

Task:

  • 编写单元测试来复现这个 Bug
  • 修复 [类名].[方法名] 方法
  • 验证修复是否成功
  • 确保修复不会破坏现有功能

Output Format:

  • 首先编写单元测试来复现 Bug
  • 然后提供修复后的代码
  • 最后验证修复结果

通过这些专项技巧,开发者可以更有效地利用 AI 进行代码调试,提高开发效率和代码质量。