---
title: "AI Agent 改完代码后，如何做回归测试？从缺陷复现到行为评估"
source: https://www.iowill.com/blog/2026/11/regression-testing-ai-agents/
date: 2026-11-03
updated: 2026-11-03
tags: [AI Agent, 回归测试, 自动化测试, 软件测试]
categories: [编程开发]
---

# AI Agent 改完代码后，如何做回归测试？从缺陷复现到行为评估

AI Agent 改完代码后，回归测试用于确认：**这次修改满足新要求的同时，已有的关键行为仍然符合约定。** 新增搜索功能能够找到结果，还需要检查原有筛选、默认参数、排序和数据处理是否保持正常。

[IBM 对回归测试的介绍](https://www.ibm.com/cn-zh/think/topics/regression-testing)强调了代码修改后检查已有功能的目的。把这个原则用于 AI 辅助开发，重点是建立可重复的验证依据，而不是依赖 Agent 的“已修复”说明。

本文主要讨论编码 Agent 修改软件后的验证流程，后半部分再讨论 **Agent 本身作为被测应用** 的情况。Python 示例为作者编写的本地教学案例，没有运行真实模型，也不代表某个商业 Agent 的检出能力或真实项目事故。资料核对日期为 **2026 年 11 月 3 日**。

## 回归测试、缺陷复测与测试层次

一次修改通常需要回答两类问题：原来的缺陷是否已经修复，以及与它有关的既有行为是否受到影响。二者都重要，覆盖范围却不相同。

| 验证目的 | 关注的问题 | 列表筛选示例 |
| --- | --- | --- |
| 缺陷复测 | 原先失败的条件是否恢复正确 | 停用状态筛选是否只返回停用项 |
| 回归测试 | 修改后，已有约定是否仍成立 | 默认查询、启用筛选、顺序和输入数据是否保持正常 |
| 新功能测试 | 新要求是否实现 | 关键词搜索及与状态筛选的组合是否正确 |

这些目的可能由同一组测试共同覆盖，不必为每个概念复制一套相同用例。一个缺陷修复后的测试，也可以长期保留为回归用例。

“回归”说明为什么测试；单元、集成、端到端说明测试覆盖的层次；手动与自动化说明如何执行。一个端到端用例可以承担回归验证，单元测试也可以保护原有接口。IBM 的文章涉及多种方法，实际选择应结合变更影响、风险与执行成本，不需要每次更新都套用全部分类。

## Agent 修改代码时需要保护哪些约定

编码 Agent 可以阅读实现、生成补丁、补充测试并调用工具。但它仍可能漏掉调用方依赖的隐含条件，尤其是默认值、空值、布尔值、兼容字段和异常路径。

例如，需求是“给任务列表增加关键词搜索”，原有行为包括：

-   `enabled=None` 不按状态筛选。
-   `enabled=True` 只返回启用项，`enabled=False` 只返回停用项。
-   未填写关键词时，保持原有查询结果和顺序。
-   查询不修改传入的数据。

如果验收只检查“输入 Alpha 后能搜到任务”，便可能漏掉 `False` 筛选失效的问题。测试依据应来自需求、公共接口和已确认的行为；旧版本中的已知缺陷不应该因为“以前就是这样”而被固定成正确答案。

[Google Testing Blog 关于行为测试的文章](https://testing.googleblog.com/2013/08/testing-on-toilet-test-behavior-not.html)建议主要围绕公共接口验证行为，让测试不依赖经常变化的实现细节。对 Agent 编写的补丁，同样应检查业务结果，而不只是验证某个辅助函数是否被调用。

## 布尔筛选的回归测试示例

这个例子只处理内存中的虚构任务。参数范围限定为 `enabled` 取 `None`、`True` 或 `False`，`keyword` 为字符串，不展开完整接口的输入校验。

新增搜索时，一种可能的错误是把条件写成：

```python
if enabled:
    result = [task for task in result if task["enabled"] is enabled]
```

`False` 和 `None` 都不会进入这段分支，但它们在约定中代表不同含义。只测试 `enabled=True` 就发现不了这个问题。

在独立示例目录中创建修正后的 `example.py`。空关键词不筛选名称，非空关键词忽略大小写，返回结果沿用输入顺序：

```python
def select_tasks(tasks, *, enabled=None, keyword=""):
    result = tasks
    if enabled is not None:
        result = [task for task in result if task["enabled"] is enabled]
    if keyword:
        needle = keyword.casefold()
        result = [task for task in result if needle in task["name"].casefold()]
    return [task["id"] for task in result]
```

将以下内容保存为同一目录中的 `test_example.py`：

```python
from copy import deepcopy
import unittest
from example import select_tasks

TASKS = [
    {"id": "t1", "name": "Alpha", "enabled": True},
    {"id": "t2", "name": "Alpha backup", "enabled": False},
    {"id": "t3", "name": "Beta", "enabled": True},
]

class SelectTasksTests(unittest.TestCase):
    def test_filter_combinations(self):
        cases = [
            (None, "", ["t1", "t2", "t3"]),
            (True, "", ["t1", "t3"]),
            (False, "", ["t2"]),
            (None, "ALPHA", ["t1", "t2"]),
            (False, "alpha", ["t2"]),
            (True, "beta", ["t3"]),
            (False, "beta", []),
            (None, "missing", []),
        ]
        for enabled, keyword, expected in cases:
            with self.subTest(enabled=enabled, keyword=keyword):
                self.assertEqual(
                    select_tasks(deepcopy(TASKS), enabled=enabled, keyword=keyword),
                    expected,
                )

    def test_does_not_modify_input(self):
        tasks = deepcopy(TASKS)
        before = deepcopy(tasks)
        select_tasks(tasks, enabled=False, keyword="alpha")
        self.assertEqual(tasks, before)

if __name__ == "__main__":
    unittest.main()
```

在这两个文件所在目录运行：

```bash
python -m unittest -v
```

示例只使用 Python 标准库。`subTest` 把不同参数组合单独标记，某个组合失败时能够定位相应条件；其行为见 [Python unittest 文档](https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests)。这里有两个测试方法，其中一个包含多组子测试，不能把方法数量直接当作覆盖范围。

作者在隔离临时目录中核对了三个版本：新增关键词之前的基线、刻意使用 `if enabled:` 的错误版本，以及上面的修正版本。原有筛选用例在基线中通过，在错误版本中能够暴露 `False` 的退化；修正后，原有约定和新增组合均通过。错误版本是人工构造的演示，不是某个 Agent 的实际输出。

这些结果只验证列出的输入组合和输入不被修改的约定，没有验证界面、数据库、并发或真实模型行为。为更大的应用增加回归覆盖时，需要继续沿调用链选择测试。

![False筛选退化的旧约定、错误条件、修正和验证范围](https://img.iowill.com/file/%E7%BC%96%E7%A8%8B%E5%BC%80%E5%8F%91/%E5%9B%9E%E5%BD%92%E6%B5%8B%E8%AF%95%E4%B8%8EAIAgent/body1.png)

*图1：作者原创教学示意，对应上面的虚构任务案例；不是Agent实际输出或运行截图。*

## 按变更影响选择回归范围

回归范围应由依赖关系和风险决定，不能只按修改了几行代码来判断。共享解析函数的一行改动，可能同时影响导入、导出和界面展示。

| 修改位置 | 适合优先检查的行为 | 可能需要补充的验证 |
| --- | --- | --- |
| 筛选、排序或参数转换 | 默认值、空结果、边界与旧调用方式 | 组合条件和调用方集成 |
| 配置或数据格式 | 旧格式读取、保存后重读、失败时的数据保留 | 迁移与恢复路径 |
| 界面和浏览器交互 | 可见状态、按钮操作、错误提示 | 不同窗口尺寸、主题与真实交互路径 |
| 后台任务与资源管理 | 启动、取消、完成和重复执行 | 监听器、线程、连接及退出清理 |
| 模型、提示词或工具定义 | 既有任务能否继续完成 | 工具参数、权限边界、最终状态与质量评估 |

这张表是选测建议，不是已经在某个项目上执行的测试清单。局部纯函数可以从快速用例开始；涉及共享接口、数据迁移或核心流程时，再扩大到集成与关键端到端路径。发布前还需要符合项目约定的完整检查。

界面回归应验证用户实际看到和操作的结果。[Playwright 的最佳实践](https://playwright.dev/docs/best-practices)强调用户可见行为、测试隔离与稳定定位。针对界面布局的改动，仅通过一个纯函数测试，不能证明按钮可点击或窄屏内容没有被遮挡。

## 测试失败时核对原因与基线

修改前运行相关测试，可以帮助区分已有失败与本次回归。已经完成修改时，也可以在独立工作目录中检查变更起点，保留当前现场；不应为了制造干净基线而丢弃未提交内容。

失败需要结合证据分类：

-   **预期未变，结果改变**：检查实现或调用关系是否退化。
-   **需求明确改变**：依据批准后的新约定修改测试，同时评估兼容影响。
-   **测试绑定实现细节**：调整不再适用的设置或断言，但继续保护真实行为。
-   **结果偶发变化**：检查共享状态、执行顺序、清理、网络依赖，也排查产品中的竞态。

[pytest 的不稳定测试说明](https://docs.pytest.org/en/stable/explanation/flaky.html)列出了系统状态未受控、隔离不足及断言过严等原因。偶发失败不应自动归咎于环境，也不能靠重复运行到通过一次就宣告修复。

Agent 同时修改实现和测试时，需要检查断言差异。删除失败用例、放宽阈值或让预期值跟着错误实现变化，都会削弱原有保障。合理调整测试当然可以进行，但应能解释行为约定为什么改变，并留下相应依据。

## Agent 本身需要额外的行为评估

如果应用本身包含 Agent，仅保护外围 Python 函数还不够。模型、提示词、工具描述或编排方式变化，都可能影响同一任务的完成方式。

[Anthropic 对 Agent 评估的介绍](https://www.anthropic.com/research/demystifying-evals-for-ai-agents)区分任务、单次尝试、执行记录和最终结果，也区分能力探索与回归评估。对已能处理的任务保留回归集，有助于发现新版本的退化；这不意味着所有 Agent 都使用同一阈值。

以下为作者建议的检查对象，需按实际应用定义成功条件：

| 检查对象 | 可验证的结果示例 |
| --- | --- |
| 工具选择与参数 | 使用适合当前任务的工具，必填字段与目标对象正确 |
| 权限边界 | 只读任务没有发起写入调用，相关业务状态保持不变 |
| 错误处理 | 工具超时后按约定停止或有限重试，没有假报成功 |
| 最终状态 | 需要生成的文件或记录确实存在，内容符合约定 |
| 回答质量 | 事实有依据，完整性与表达满足明确的评价标准 |

“任务已完成”这句回复不能替代文件、数据库或工具执行结果。相反，也不必要求每次尝试采用完全相同的工具顺序：只有业务确实依赖的顺序才应作为硬约束。

对于能够明确判定的结果，优先使用代码和状态断言；对开放式回答，可以结合清晰的评分标准、模型评审与人工抽查。模型评审也需要校准，不能让一个平均质量分掩盖越权调用等硬性失败。

比较版本时，应记录代码提交、模型标识、提示词与工具定义版本、测试数据、初始状态、运行条件和评价器。对存在随机性的任务安排多次尝试，记录失败分布及具体案例；一次成功不代表持续稳定，参数相同也不保证输出逐字一致。这是评估设计建议，本文没有实际调用模型来测量通过率。

![软件回归测试与Agent行为评估的对象、证据和结论范围](https://img.iowill.com/file/%E7%BC%96%E7%A8%8B%E5%BC%80%E5%8F%91/%E5%9B%9E%E5%BD%92%E6%B5%8B%E8%AF%95%E4%B8%8EAIAgent/body2.png)

*图2：作者原创概念示意；软件行为与Agent行为分别验证，本文未进行真实模型评测。*

## 把回归检查接入 Agent 开发流程

一套可执行的协作流程可以按以下顺序组织：

1.  明确新要求、需要保留的行为和不在本次范围内的内容。
2.  记录变更基线与已有失败，为实际缺陷保留能复现它的用例。
3.  实现最小相关修改，补齐新行为及受影响旧行为的测试。
4.  在隔离环境中运行相关检查，按证据处理失败，再检查最终差异。
5.  根据影响扩大必要验证；Agent应用另比较任务数据集、工具调用与最终状态。
6.  汇报通过、失败和未覆盖范围，按项目权限决定合并或发布。

测试环境应使用脱敏样本、临时目录与受控工具。不要为了验证重试或取消行为，直接反复执行真实发布、私信、付款等外部操作；模拟工具可以验证本地控制逻辑，真实服务的行为需要另外安排验证。

[LangSmith 对评估类型的说明](https://docs.langchain.com/langsmith/evaluation-types)将发布前的离线评估与生产中的在线观察分开。上线后发现的真实失败可以脱敏后加入后续回归集；生产监测也不能反过来替代发布前检查。

让 Agent 参与生成用例、分析影响和整理结果，可以减少部分重复工作，但通过率、检出率或节省时间都需要实际测量。本文引用 IBM 关于AI辅助测试的讨论作为背景，没有把它当作特定产品的效率证明。

需要把上述流程与审查、任务隔离连接起来，可以继续阅读[AI Code Review与人工代码审查](https://www.iowill.com/blog/2026/10/ai-code-review-vs-human-code-review/)和[什么时候创建Git任务分支](https://www.iowill.com/blog/2026/10/when-to-create-git-branch/)。回归测试提供已验证行为的证据，合并与生产发布仍按项目约定执行。

## 参考资料

资料于2026年11月3日核对。IBM为用户指定的概念参考，工具官方文档用于说明相应能力；Anthropic与LangSmith资料属于其评估实践。本文流程与教学代码为作者整理，不宣称复现了这些机构的产品效果。

1.  [IBM：什么是回归测试？](https://www.ibm.com/cn-zh/think/topics/regression-testing)，概念、影响分析与方法背景。
2.  [Google Testing Blog：Test Behavior, Not Implementation](https://testing.googleblog.com/2013/08/testing-on-toilet-test-behavior-not.html)，围绕公共行为设计测试。
3.  [Python：unittest](https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests)，标准库测试与子测试。
4.  [Playwright：Best Practices](https://playwright.dev/docs/best-practices)，用户可见行为与测试隔离。
5.  [pytest：Flaky tests](https://docs.pytest.org/en/stable/explanation/flaky.html)，不稳定测试的原因与处理思路。
6.  [Anthropic：Demystifying evals for AI agents](https://www.anthropic.com/research/demystifying-evals-for-ai-agents)，Agent评估的对象、尝试、结果与回归集。
7.  [LangChain：Evaluation types](https://docs.langchain.com/langsmith/evaluation-types)，离线与在线评估的分工。
