AI 编程代理可以参与 Python 调试,但真正可靠的结果并不是“把报错贴给模型”这么简单。更稳妥的协作方式是:先用失败测试复现问题,再提供足够的代码上下文,最后运行测试和检查结果,确认修复没有引入新的回归。
先把问题变成失败测试
调试的第一步不是询问 AI,而是固定问题。一个好的失败测试应当说明输入、预期行为和当前实际行为。这样,AI 代理有明确的执行目标,也能在修改代码后自动验证结果。
下面是一个可以直接运行的最小示例。假设 discount_price 在折扣为 0 时应该返回原价:
# test_discount.py
from discount import discount_price
def test_zero_discount_keeps_original_price():
assert discount_price(100, 0) == 100
# discount.py
def discount_price(price: float, discount: float) -> float:
if not discount:
return 0
return price * (1 - discount)
在同一目录下运行:
python -m pip install pytest
python -m pytest -q
测试会失败,因为实现把 discount=0 当成了需要返回 0 的情况。这个失败结果就是交给 AI 代理的第一个可靠上下文:它包含了可复现输入、预期值和实际行为。
给代理足够但聚焦的上下文
AI 代理需要知道的不只是错误信息。可以把以下内容一次性提供给它:
- 失败测试的完整输出
- 相关函数及其调用方式
- 项目使用的 Python 版本和测试命令
- 业务规则,例如“没有折扣时价格保持不变”
- 修改范围,例如只允许调整
discount.py,不要改测试
可以这样写调试提示:
请调试这个 Python 问题。
目标:让 pytest 中的失败测试通过,并保持现有行为不变。
失败测试:
test_zero_discount_keeps_original_price
输入:price=100, discount=0
期望:100
实际:0
相关代码:
def discount_price(price: float, discount: float) -> float:
if not discount:
return 0
return price * (1 - discount)
约束:
1. 先解释根因,再提出最小修改。
2. 不要修改测试来掩盖问题。
3. 修改后运行 python -m pytest -q。
4. 同时检查正常折扣和边界输入是否仍然合理。
这样的提示把调试任务拆成了“定位根因、实施最小修改、运行验证”三个动作。代理可以提出如下修复:
# discount.py
def discount_price(price: float, discount: float) -> float:
if discount is None:
return price
return price * (1 - discount)
这里的代码只是示例,实际项目是否接受 None 需要根据接口契约决定。关键点在于,不要让代理凭空猜业务规则;应当通过测试、类型定义、调用方和文档补齐上下文。
让验证成为调试闭环
代理给出“看起来合理”的补丁后,仍然需要由本地工具验证。最小闭环可以这样执行:
python -m pytest -q
python -m compileall -q .
git diff -- discount.py test_discount.py
如果项目有格式检查和静态检查,也应加入同一流程:
python -m ruff check .
python -m mypy .
验证时不要只关注原来的失败测试。至少补充几个相关场景:
# test_discount.py
from discount import discount_price
def test_zero_discount_keeps_original_price():
assert discount_price(100, 0) == 100
def test_regular_discount_is_applied():
assert discount_price(100, 0.2) == 80
def test_none_discount_keeps_original_price():
assert discount_price(100, None) == 100
这些测试能帮助发现代理是否只是针对单个报错做了特判。若修复扩大了行为范围,例如改变了负数折扣、超过 100% 折扣或字符串输入的处理方式,就需要明确决定:这是本次修复的一部分,还是应该单独建立需求和测试。
AI 适合定位问题,不负责替你定义契约
AI 代理在整理堆栈、追踪调用关系、提出候选修复和生成回归测试方面很有帮助。但它无法自动知道所有隐含业务规则,尤其是以下情况:
- 错误输入是否应该抛出异常还是返回默认值
None、空字符串和数字0是否具有不同含义- 修复是否会影响兼容性或数据迁移
- 某个边界行为是 bug,还是历史约定
因此,调试时应保留人工判断:由开发者确认问题定义、修改边界和验收标准;由代理协助搜索、分析和编写候选代码;由测试、静态检查和代码审查确认结果。
一套可重复的协作清单
可以把下面的流程用于日常 Python 调试:
- 写出一个能稳定失败的最小测试。
- 记录完整错误输出和复现命令。
- 向代理提供相关文件、调用路径、环境信息和业务约束。
- 要求代理先解释根因,再提交最小修改。
- 运行原测试、回归测试和项目检查命令。
- 查看
git diff,确认没有修改测试来掩盖问题,也没有产生无关重构。 - 对边界行为和公共接口进行人工复核。
这套方法的价值不在于让 AI 代理替代调试,而在于把代理放进一个可复现、可验证的工程流程中。失败测试负责描述问题,上下文负责限制猜测,自动化检查负责判断修复是否真的成立。