AutoGPT 挑战设计实战:从 Agent 夹具到 pytest 挑战测试的完整构建指南
本文基于 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。
需要说明的是当前仓库的实际状态:
- 文档示例引用的旧模块(
tests.challenges.utils、tests/integration/agent_utils.py中的run_interaction_loop)在现行测试树中已不再以原路径存在,classic/original_autogpt/tests下目前只有integration/、mocks/、unit/三个子目录; - 挑战的“数据 + 评测”形态已演进为 classic/direct_benchmark/challenges/ 下的声明式挑战目录,由 Direct Benchmark Harness 驱动,其 README 提供了完整 CLI;
- Agent 的交互式主循环现在位于 autogpt/app/main.py 的
run_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_operations与app),避免 Agent 在无关命令上浪费步骤。文档提示完整命令列表需查main.py; - 人设即约束:
AIProfile的ai_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 则提供了 config、llm_provider、storage、agent 等公共 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.py 的 run_interaction_loop(),接线细节见该文件实现。
4.3 断言工件:从关键词到结构化校验
断言分两级:先用字符串包含检查 apiVersion、kind、metadata、spec 等 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(抗干扰、抗注入)、library、verticals(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(默认)、rewoo、plan_execute、reflexion、tree_of_thoughts。
7. 提交挑战的自检清单
综合文档与仓库现状,一份合格的挑战提交应满足:
- 模型自洽:能清晰表达为“用户输入(指令 + 文件)→ 工件”的形式,并能在 challenge_template.md 四节模板里写满 Description / Input / Scope / Success Evaluation;
- 夹具最小化:受测 Agent 的目标写入
ai_goals,命令集/能力面最小化,夹具可基于 conftest.py 的config/llm_provider/storage公共 fixture 构建; - 断言可执行:pytest 形态挑战有明确的工件断言(关键词 + 结构化校验);数据形态挑战在
data.json中给出ground,并用artifacts_out配合 mock 模式验证评测器本身能跑通(agbenchmark --test=... --mock的思路见 CHALLENGE.md); - 标记完整:
vcr与requires_openai_api_key标记就位,未攻克前保留skip并注明原因; - 类别归位:放入
abilities/alignment/library/verticals/*合适位置或新建类别,并同步更新攻克状态记录 challenges_already_beaten.json。
按此流程完成一次挑战的完整落地后,即可通过 PR 加入 Challenge Creator 行列,与社区一起把 AutoGPT 的能力边界往外推。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0625
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00