首页
/ AutoGPT 挑战设计实战:从 Agent 夹具到 pytest 挑战测试的完整构建指南

AutoGPT 挑战设计实战:从 Agent 夹具到 pytest 挑战测试的完整构建指南

2026-09-06 15:11:28作者:裘晴惠Vivianne

本文基于 AutoGPT 仓库中 《Creating Challenges for AutoGPT》 官方文档展开,讲清楚如何为 AutoGPT Classic 设计一个可重复验证的挑战(Challenge):定义受测 Agent 的 pytest 夹具、编写“模拟用户输入 + 断言输出工件”的挑战测试,并结合当前仓库中的 direct_benchmark 挑战数据格式与运行入口,帮助你完成从选题、编码到跑通验证的完整闭环。

1. 什么是 AutoGPT 的“挑战”?

AutoGPT 社区用“挑战(Challenge)”来标记那些 AutoGPT 尚不能稳定完成的任务。根据 介绍文档,挑战的价值在于:识别 Agent 的能力短板,让社区成员通过“提交挑战”或“攻克挑战”两种方式来推动项目进步。

《Creating Challenges》文档 给出了挑战的核心设计原则,这是后续所有编码工作的思想基础:

  • 框架无关(agnostic):挑战不绑定特定框架,尽量保持中立;
  • 挑战即用户:挑战模拟一个“想要某事被完成”的用户,其交互模型是:
    • 输入(INPUT):用户诉求(自然语言指令)+ 文件等其他输入;
    • 输出(Output):工件(Artifact),如文件、图像、代码等。

这个“输入 → 工件”的模型直接决定了挑战测试的写法:测试代码负责注入用户输入并驱动 Agent 运行,断言部分则检查 Agent 产出的工件是否满足预期。

1.1 挑战作者的技能画像

文档明确列出了挑战作者需要的三类能力:

  • UX 设计:提升尝试挑战者的体验,参与挑战 Wiki 专区乃至独立网站的建设;
  • 编码:熟悉 Python、pytest 和 VCR(一个记录并回放 OpenAI 调用的库)是创建健壮挑战的关键——VCR 让测试可以离线重放 LLM 交互,避免依赖实时 API;
  • DevOps:GitHub CI 流水线经验(以及可能的 GCP 经验),用于自动化运行挑战。

加入方式是直接开 PR 成为 Challenge Creator。

2. 准备工作:环境与代码结构

文档给出的起步要求是:克隆 AutoGPT 仓库并检出 master 分支。在当前仓库中,挑战相关的代码全部位于 classic/original_autogpt 目录下,测试基建入口为 tests/integration/agent_factory.py,全局 fixture 定义在 tests/conftest.py

需要说明的是当前仓库的实际状态:

  1. 文档示例引用的旧模块(tests.challenges.utilstests/integration/agent_utils.py 中的 run_interaction_loop)在现行测试树中已不再以原路径存在,classic/original_autogpt/tests 下目前只有 integration/mocks/unit/ 三个子目录;
  2. 挑战的“数据 + 评测”形态已演进为 classic/direct_benchmark/challenges/ 下的声明式挑战目录,由 Direct Benchmark Harness 驱动,其 README 提供了完整 CLI;
  3. Agent 的交互式主循环现在位于 autogpt/app/main.pyrun_interaction_loop() 中,旧文档里的交互循环概念与它对应。

因此,编写挑战时应把本文第 3、4 节的文档写法理解为范式模板,而具体 fixture 与工具函数的接线方式以仓库当前源码为准。

3. 定义你的 Agent:编写挑战专用的 pytest 夹具

文档的第一道编码工序是“Defining your Agent”:在集成测试的 agent factory 中创建专属夹具,把 Agent 的人设、目标与可用命令集固定下来。以下代码完整继承自 官方文档kubernetes_agent 示例(旧版接口,保留原文用于说明设计意图):

def kubernetes_agent(
    agent_test_config, workspace: Workspace
):
    # Please choose the commands your agent will need to beat the challenges, the full list is available in the main.py
    # (we 're working on a better way to design this, for now you have to look at main.py)
    command_registry = CommandRegistry()
    command_registry.import_commands("autogpt.commands.file_operations")
    command_registry.import_commands("autogpt.app")

    # Define all the settings of our challenged agent
    ai_profile = AIProfile(
        ai_name="Kubernetes",
        ai_role="an autonomous agent that specializes in creating Kubernetes deployment templates.",
        ai_goals=[
            "Write a simple kubernetes deployment file and save it as a kube.yaml.",
        ],
    )
    ai_profile.command_registry = command_registry

    system_prompt = ai_profile.construct_full_prompt()
    agent_test_config.set_continuous_mode(False)
    agent = Agent(
        command_registry=command_registry,
        config=ai_profile,
        next_action_count=0,
        triggering_prompt=DEFAULT_TRIGGERING_PROMPT,
    )

    return agent

这段夹具体现了三个关键设计点:

  • 最小权限命令集CommandRegistry 只导入挑战所需命令模块(这里是 file_operationsapp),避免 Agent 在无关命令上浪费步骤。文档提示完整命令列表需查 main.py
  • 人设即约束AIProfileai_name / ai_role / ai_goals 直接构成系统提示词(construct_full_prompt()),挑战的“用户诉求”应写进 ai_goals,例如“生成 kube.yaml 部署文件”;
  • 非连续模式set_continuous_mode(False) 让 Agent 每步等待输入,这正是挑战测试可以用“输入生成器”逐条喂入用户指令的前提。

对照当前仓库,agent_factory.py 中的 dummy_agent 夹具展示了重构后的接线方式:

@pytest.fixture
def dummy_agent(config: AppConfig, llm_provider: MultiProvider):
    ai_profile = AIProfile(
        ai_name="Dummy Agent",
        ai_role="Dummy Role",
        ai_goals=["Dummy Task"],
    )
    agent_settings = AgentSettings(
        name=Agent.default_settings.name,
        description=Agent.default_settings.description,
        ai_profile=ai_profile,
        config=AgentConfiguration(
            fast_llm=config.fast_llm,
            smart_llm=config.smart_llm,
        ),
        history=Agent.default_settings.history.model_copy(deep=True),
    )
    file_storage = get_storage(
        config.file_storage_backend,
        root_path=Path("data"),
        restrict_to_root=restrict_to_root,
    )
    file_storage.initialize()
    agent = Agent(
        settings=agent_settings,
        llm_provider=llm_provider,
        file_storage=file_storage,
        app_config=config,
    )
    return agent

可见现行结构用 AgentSettings + AgentConfiguration(fast_llm/smart_llm 双模型配置)+ 独立 file_storage 取代了旧的 CommandRegistry 装配路径;conftest.py 则提供了 configllm_providerstorageagent 等公共 fixture——新写挑战夹具时,直接依赖这些 fixture(如 config: AppConfig, llm_provider: MultiProvider)是最贴合当前代码库的做法。

4. 编写挑战测试:模拟用户输入并断言工件

文档的第二道工序是创建挑战测试文件:放入 tests/challenges 目录,命名为 test_your_test_description.py,按类别归档,没有合适类别就新建一个。完整示例(文档原文,Kubernetes 部署文件挑战)如下:

import contextlib
from functools import wraps
from typing import Generator

import pytest
import yaml

from autogpt.commands.file_operations import read_file, write_to_file
from tests.integration.agent_utils import run_interaction_loop
from tests.challenges.utils import run_multiple_times

def input_generator(input_sequence: list) -> Generator[str, None, None]:
    """
    Creates a generator that yields input strings from the given sequence.
    :param input_sequence: A list of input strings.
    :return: A generator that yields input strings.
    """
    yield from input_sequence


@pytest.mark.skip("This challenge hasn't been beaten yet.")
@pytest.mark.vcr
@pytest.mark.requires_openai_api_key
def test_information_retrieval_challenge_a(kubernetes_agent, monkeypatch) -> None:
    """
    Test the challenge_a function in a given agent by mocking user inputs
    and checking the output file content.
    :param get_company_revenue_agent: The agent to test.
    :param monkeypatch: pytest's monkeypatch utility for modifying builtins.
    """
    input_sequence = ["s", "s", "s", "s", "s", "EXIT"]
    gen = input_generator(input_sequence)
    monkeypatch.setattr("autogpt.utils.session.prompt", lambda _: next(gen))

    with contextlib.suppress(SystemExit):
        run_interaction_loop(kubernetes_agent, None)

    # here we load the output file
    file_path = str(kubernetes_agent.workspace.get_path("kube.yaml"))
    content = read_file(file_path)

    # then we check if it's including keywords from the kubernetes deployment config
    for word in ["apiVersion", "kind", "metadata", "spec"]:
        assert word in content, f"Expected the file to contain {word}"

    content = yaml.safe_load(content)
    for word in ["Service", "Deployment", "Pod"]:
        assert word in content["kind"], f"Expected the file to contain {word}"

逐段拆解其技术要点:

4.1 三个 pytest 标记的职责

标记 作用
@pytest.mark.skip("...") 挑战尚未被攻克时的默认状态,附一句原因;攻克后移除
@pytest.mark.vcr 启用 VCR,录制/回放 OpenAI 调用,使测试可离线稳定重复
@pytest.mark.requires_openai_api_key 声明依赖 OpenAI API Key,无 Key 的环境自动跳过

4.2 输入注入:monkeypatch + 生成器

  • input_generator 把一串用户指令转成生成器;
  • monkeypatch.setattr("autogpt.utils.session.prompt", lambda _: next(gen)) 将交互循环的 stdin 提示函数替换为“从生成器取下一条”——于是 ["s", "s", ..., "EXIT"] 分别表示“确认继续”若干轮,最后以 EXIT 结束循环;
  • contextlib.suppress(SystemExit) 吞掉 EXIT 引发的退出异常,使断言代码得以继续执行。

这套“喂输入 → 跑循环 → 收工件”的模式,正是第 1 节“挑战即用户”模型的测试化落地。当前仓库中交互循环对应 autogpt/app/main.pyrun_interaction_loop(),接线细节见该文件实现。

4.3 断言工件:从关键词到结构化校验

断言分两级:先用字符串包含检查 apiVersionkindmetadataspec 等 YAML 骨架字段,再用 yaml.safe_load 解析后对 kind 字段做业务级断言(期望出现 Service/Deployment/Pod 之一)。这提示挑战作者:断言应“足够严格以证明任务完成,足够宽松以容忍合理表达差异”。文档还提供了一个 run_multiple_times 工具,用于对同一挑战重复执行多次以统计稳定性,这与 Direct Benchmark 的 --attempts N 参数思路一致(见第 6 节)。

4.4 为挑战补充文档

提交挑战时还应配套一份说明文档,仓库提供了统一模板 challenge_template.md,要求包含四个部分:

  • Description:清晰描述挑战问题,可附示例文件;
  • Input:列出输入文件名与内容(文档模板中的示例展示了如何写入带噪音的指令文件,如 The current task_id is 4563.\n[NOISE intended to confuse the agent],考验 Agent 抗干扰能力);
  • Scope:约束、前置条件与限制;
  • Success Evaluation:成功如何衡量——这决定了第 4.3 节断言的写法。

5. 当前仓库中的挑战数据形态:direct_benchmark 数据模式

文档中的 pytest 写法是挑战的“测试驱动形态”;而在当前仓库中,挑战的主体已演进为 classic/direct_benchmark/challenges/ 下的声明式数据挑战,其模式定义见 CHALLENGE.md。每个挑战目录包含 data.json、可选的 artifacts_in/(挑战开始前放入 Agent 工作区的输入文件)、artifacts_out/(期望产出,用于 mock 验证挑战本身可运行)、custom_python/(挑战完成后拷贝进工作区执行的校验脚本)。

abilities/write_file/data.json 为例:

{
    "name": "WriteFile",
    "task": "Write the word 'Washington' to a .txt file",
    "dependencies": [],
    "cutoff": 60,
    "ground": {
        "answer": "The word 'Washington', printed to a .txt file named anything",
        "eval": { "type": "file" },
        "files": [".txt"],
        "should_contain": ["Washington"],
        "should_not_contain": []
    },
    "info": {
        "description": "Tests if the agent can write a file",
        "difficulty": "interface"
    }
}

对照 CHALLENGE.md 的字段说明,关键项为:

  • task:即“用户诉求”,等价于文档模型中的用户输入;
  • ground.answer / should_contain / should_not_contain:标准答案与必须包含/禁止包含的字符串,直接对应文档 4.3 节的手写断言,但被数据化后可被评测引擎自动执行;
  • ground.files:指定参与评分的文件或扩展名(如 .txt);
  • ground.eval.type:三种评测器——file(默认,按 should_contain/should_not_contain 比对文件)、python(运行指定文件并捕获 print 输出评分)、llm(用 LLM 按 rubric/reference/custom 模板 + percentage/scale/binary 计分规则评分);
  • dependencies:声明前置挑战(如写作类挑战依赖写文件能力),--no-dep 可忽略依赖关系全量执行。

挑战按 abilities(读写文件)、alignment(抗干扰、抗注入)、libraryverticals(code/data/scrape/synthesize)分类组织,与文档“放入合适类别文件夹,没有就新建”的指引一脉相承。挑战攻克状态集中记录在 challenges_already_beaten.json 中(true 表示稳定攻克),它驱动下文 --maintain / --improve / --explore 三种筛选模式。

6. 运行与验证挑战

6.1 用 pytest 运行单个挑战测试

classic/original_autogpt 测试环境中,按文档范式编写的挑战测试可直接用 pytest 触发:

# 运行文档风格的挑战测试(示例命令形态)
poetry run pytest tests/integration -k test_information_retrieval_challenge_a

注意 requires_openai_api_key 标记要求配置 OPENAI_API_KEY;启用 VCR 后首次运行会录制 OpenAI 交互,之后可离线回放,这正是文档强调 VCR 技能的原因。

6.2 用 Direct Benchmark Harness 运行数据挑战

当前仓库的正式入口是 classic/direct_benchmark,它绕过 HTTP 服务开销直接实例化 Agent,支持并行与多次尝试。从 classic/ 目录执行:

cd classic
poetry install

# 默认配置运行基准
poetry run direct-benchmark run

# 指定策略与模型,并行 4 路
poetry run direct-benchmark run \
    --strategies one_shot,rewoo \
    --models claude,openai \
    --parallel 4

# 只跑单个挑战,重复 3 次取统计结果
poetry run direct-benchmark run \
    --strategies one_shot \
    --tests WriteFile \
    --attempts 3

# 三种筛选模式
poetry run direct-benchmark run --maintain   # 只跑已稳定攻克的回归测试
poetry run direct-benchmark run --improve     # 只跑未稳定攻克的挑战
poetry run direct-benchmark run --explore    # 只跑从未攻克的挑战

# 查看可用挑战 / 模型预设 / 策略
poetry run direct-benchmark list-challenges
poetry run direct-benchmark list-models
poetry run direct-benchmark list-strategies

常用参数(摘自 README):--attempts, -N 每个挑战重复次数;--parallel, -p 并行数(默认 4);--timeout 单挑战超时(默认 300 秒,--no-cutoff 关闭);--max-steps 每挑战最大步数(默认 50);--json 输出 JSON 供 CI 消费。报告生成于 ./reports/ 下,按 {timestamp}_{strategy}_{model} 组织并附策略对比 JSON。可用的推理策略包括 one_shot(默认)、rewooplan_executereflexiontree_of_thoughts

7. 提交挑战的自检清单

综合文档与仓库现状,一份合格的挑战提交应满足:

  1. 模型自洽:能清晰表达为“用户输入(指令 + 文件)→ 工件”的形式,并能在 challenge_template.md 四节模板里写满 Description / Input / Scope / Success Evaluation;
  2. 夹具最小化:受测 Agent 的目标写入 ai_goals,命令集/能力面最小化,夹具可基于 conftest.pyconfig / llm_provider / storage 公共 fixture 构建;
  3. 断言可执行:pytest 形态挑战有明确的工件断言(关键词 + 结构化校验);数据形态挑战在 data.json 中给出 ground,并用 artifacts_out 配合 mock 模式验证评测器本身能跑通(agbenchmark --test=... --mock 的思路见 CHALLENGE.md);
  4. 标记完整vcrrequires_openai_api_key 标记就位,未攻克前保留 skip 并注明原因;
  5. 类别归位:放入 abilities / alignment / library / verticals/* 合适位置或新建类别,并同步更新攻克状态记录 challenges_already_beaten.json

按此流程完成一次挑战的完整落地后,即可通过 PR 加入 Challenge Creator 行列,与社区一起把 AutoGPT 的能力边界往外推。

登录后查看全文
热门项目推荐
相关项目推荐