综合调研 已修订

APEX-Agents 深度解剖:真实职场数据 + MCP 模拟层如何构建专业 Agent 评测

从 World 数据结构到 MCP Server 源码,完整拆解 APEX-Agents 的架构设计——Google Takeout 真实数据、9 大 MCP 工具服务、Rubric 评分机制与 Leaderboard 分析

APEX-Agents Agent Benchmark MCP Archipelago 专业服务 Function Calling

概览

APEX-Agents(Mercor, 2026.01)是当前最接近真实专业工作场景的 Agent 评测基准。它不用合成数据,不用模拟对话——而是直接把真实投行分析师、管理咨询顾问、公司律师的 Google Workspace 工作环境打包成评测环境,让 AI Agent 在里面完成他们的日常工作。

480 个任务,33 个虚拟工作世界,平均每个任务需要人类专家 1.82 小时完成。当前最强模型(GPT-5.2)的 Pass@1 仅 23%。

本报告基于对 APEX-Agents 数据集的实际解压分析、Archipelago 框架源码阅读、以及 MCP Server 接口文档的系统梳理,完整拆解其架构设计。


构建方法论:从调研到评测的四步流水线

APEX-Agents 的构建不是随意拼凑的。Mercor 设计了一套严谨的四步方法论,每一步都有明确的输入和产出。

第一步:行业调研(Surveys)

Mercor 调研了数百名专业服务领域的专家,受访者来自顶级机构——Goldman Sachs(高盛)、McKinsey(麦肯锡)、Cravath(克拉华律所,全美顶级律所之一)。

调研的核心问题不是”AI 能做什么”,而是“你们每天的时间花在什么任务上,哪些任务你最希望 AI 来做”。这决定了 benchmark 评测的任务分布——不是研究者想象的”典型任务”,而是从业者真实的痛点。

第二步:场景搭建(Scenarios)——Box 与 Harvey AI 合作

这是最核心的环节。Mercor 的内部专家团队——包括副总裁(VP)、董事总经理(MD)和经理(Manager)级别,拥有顶级公司 5-10 年经验的从业者——在 Google Workspace 中亲自模拟协作流程。

比如:为一家虚构的欧洲油气公司进行为期一周的咨询项目,聚焦成本削减措施。专家们真的在 Gmail 里通邮件、在 Google Sheets 里建财务模型、在 Google Chat 里讨论进展——然后通过 Google Takeout 导出全部数据。

两个外部合作方在这一步发挥了关键作用:

Box(box.com,企业级云端内容管理平台,NYSE 上市公司)与 Mercor 合作定义真实世界文件系统的样貌。Box 是企业文件管理领域的头部玩家(客户包括摩根士丹利、博通等),他们的核心贡献是确保 APEX-Agents 中的文件结构不是简化的测试数据,而是真正模拟了专业人士日常工作中的数据室(dataroom)环境——多层级目录、跨格式文件、不完整的命名约定、混合的版本管理。双方将严格的、领域特定的挑战映射到了覆盖多个行业的复杂文件结构中。

这个合作的价值在于:文件导航是 Agent 最容易失败的地方之一(详见下方失败模式分析)。一个过于整洁的文件系统会高估 Agent 的能力,Box 帮助确保了评测环境的真实复杂度。

Harvey AI(harvey.ai,AI 法律助手,估值超 30 亿美元的法律 AI 独角兽)对企业法务领域的评测世界提供了早期反馈——覆盖设计、范围和真实性三个维度。Harvey AI 确认了任务的复杂性反映了财富 500 强企业和律所中顶级律师的真实工作。

Harvey AI 的角色是验证者和质量把关者——确保法律领域的评测任务不是”玩具级”的合同分析,而是需要理解多方利益博弈、跨条款推理、合规风险评估的真实法务工作。

第三步:任务创建(Task Creation)

基于第二步搭建的场景,专家定义了480 个具体任务和1,948 条精确的评分标准。

关键细节:评分标准(rubric)的制定标准是“客户是否会接受这个交付物”——不是学术上的”答案是否正确”,而是商业上的”这个分析能不能呈给客户”。每个任务有 1-10 个 pass/fail 判定标准。

第四步:评估(Evals)

使用开源框架 Archipelago,将 Agent 部署到模拟世界中,通过 MCP 协议与环境交互,快照对比评分。


Agent 失败模式分析

APEX-Agents 博客文章明确指出,Agent 的失败不是因为”不够聪明”,而是因为三个看似基础但极其关键的能力缺失:

失败模式一:无法管理模糊性(Ambiguity)

现实工作中的指令往往不完整、含糊。比如经理在 Chat 里说”帮我更新一下那个模型”——“那个”指哪个模型?需要从邮件上下文、最近的会议记录、文件修改历史中推理出来。

大部分 Agent 在遇到模糊指令时要么瞎猜、要么停滞,缺乏主动澄清或上下文推理的能力。

失败模式二:找不到正确的文件

在复杂的文件系统中导航失败——这正是 Box 合作要解决的问题。真实的数据室可能有几十个子目录、上百个文件,命名不规范(Q3_Final_v2_CLEAN.xlsx vs q3-analysis-backup.xlsx),Agent 需要理解文件名语义、目录结构语义、以及邮件中对文件的间接引用。

失败模式三:无法在长流程中保持上下文

APEX-Agents 的平均任务耗时 1.82 小时(人类),Agent 需要几十步工具调用。在这个过程中,Agent 需要持续追踪多个信息源(邮件说了什么、Excel 里的数字是多少、之前的计算结果是什么),并在整个工作流中保持一致。

当前 Agent 的上下文窗口虽然已经很大(128K+ tokens),但有效利用率极低——大量 token 被工具输出占据,真正重要的信息被淹没。

更深层的启示

这三个失败模式指向同一个根本问题:当前的 Agent 架构是为”单次问答”设计的,不是为”多小时的项目型工作”设计的。职场场景中,信息是杂乱的、不完整的,分散在文档和聊天记录中。大部分基准测试用的是整洁的、自包含的 prompt——它们无法检测出这种能力缺失。


APEX 基准测试家族

APEX-Agents 不是孤立的产品。它加入了 Mercor 的评测生态系统:

基准测试定位评测维度
APEX v1无工具评估纯推理/知识能力(无 Agent 工具调用)
ACE消费者应用日常消费级应用场景
APEX-Agents专业 Agent投行/咨询/法务的多步骤工具使用
APEX-SWE软件工程代码修复/开发类任务

四个基准覆盖了从”纯推理”到”复杂 Agent”的完整能力谱。


第一章 · 数据集全景

1.1 基本规模

维度数值
总任务数480
总世界数33
领域分布投行 160 / 咨询 160 / 法务 160
评分标准总数1,948 条(平均 4.1 条/任务)
平均完成时间(人类专家)1.82 小时
数据集大小~8.5 GB
许可证CC-BY 4.0(仅评测,禁止训练)

1.2 任务输出类型分布

输出类型数量占比
文本回复(message_in_console)42287.9%
新建文档(make_new_doc)204.2%
编辑表格(edit_existing_sheet)163.3%
新建表格(make_new_sheet)142.9%
新建演示文稿(make_new_slide_deck)51.0%
编辑文档(edit_existing_doc)20.4%
编辑演示文稿(edit_existing_slide_deck)10.2%

87.9% 的任务要求文本回复,12.1% 要求产出文件——这意味着大部分任务考察的是信息检索 + 分析推理能力,少部分考察文档生成能力。

1.3 Rubric 评分标准

每个任务有 1-10 条二值评分标准(Met / Not Met),由 Judge 模型独立评估每条。

示例(投行任务):

任务: 计算 Kenvue 交易的 FY24 税前目标倍数
Rubric:
  ✓ States Deal value / EBIT is 24.9x
  ✓ States Deal value / EBITDA is 18.6x
  ✓ Deal value / OpFCF is 34.5x

示例(法务任务):

任务: 供应商能否基于关税援引不可抗力条款?
Rubric:
  ✓ 正确识别 Template 1 的 Section 14 为不可抗力条款
  ✓ 指出 25% 关税阈值
  ✓ 确认 35% > 25%,供应商可以援引
  ✓ 引用了伊拉克原材料来源这一关键事实

这种设计让评测可以精确衡量 Agent 的推理链是否完整——不只是最终答案对不对,还要看关键推理步骤有没有覆盖。


第二章 · World 数据结构:真实的 Google Takeout 导出

2.1 核心发现

解压 World 文件后,发现数据不是合成的,而是真实的 Google Workspace 数据导出。证据:

  1. 邮件格式:标准 MBOX 文件,含完整的 DKIM 签名、SMTP 路由头、SPF 验证记录
  2. 日历格式:标准 iCalendar (.ics),PRODID:-//Google Inc//Google Calendar 70.9054//EN
  3. 聊天格式:Google Chat 的 JSON 导出,含 message_id、topic_id、reactions(emoji + reactor 列表)
  4. 文件系统:真实的 PDF 年报(AB InBev、Kellanova、PepsiCo)、Excel 财务模型、Word 文档、PPT

2.2 目录结构

world_xxx/
├── .apps_data/                          ← 应用数据
│   ├── calendar/
│   │   └── Calendar/
│   │       ├── user1@c-mercor.com.ics   ← iCalendar 标准格式
│   │       └── user2@c-mercor.com.ics
│   ├── chat/
│   │   └── Google Chat/
│   │       ├── Groups/
│   │       │   ├── DM xxx/messages.json  ← 私聊
│   │       │   └── Space xxx/messages.json ← 群组
│   │       └── Users/
│   │           └── User xxx/
│   │               ├── user_info.json    ← 身份 + 群组成员
│   │               └── unsentmessages.json
│   └── mail/
│       └── Mail/
│           └── All mail Including Spam and Trash.mbox  ← Gmail 导出
└── filesystem/                          ← 工作文件
    └── 4. Research/
        ├── Data_Tracker.xlsx
        ├── Assumptions_Validation.docx
        └── Company background and research/
            ├── Anheuser-Busch/   ← 12 个 PDF/Excel(年报+财报+股价)
            ├── Kellanova/        ← 15 个文件
            └── Pepsi/            ← 10 个文件

2.3 数据格式实例

邮件(MBOX)——完整的 RFC 2822 格式,含路由头:

From 1848045044579534667@xxx Thu Nov 06 12:44:54 +0000 2025
X-GM-THRID: 1848045044579534667
X-Gmail-Labels: Inbox,Opened,Category Updates
Delivered-To: world113.consulting@c-mercor.com
DKIM-Signature: v=1; a=rsa-sha256; d=google.com; ...
Subject: Your Google data is ready to download
From: Google Takeout <noreply@google.com>

日历(ICS)——标准 VEVENT:

BEGIN:VEVENT
DTSTART:20251030T160000Z
DTEND:20251030T170000Z
ORGANIZER;CN=nile.amethyst@c-mercor.com
SUMMARY:Project Alignment Meeting
ATTENDEES:...
END:VEVENT

聊天(JSON)——Google Chat 导出格式:

{
  "messages": [
    {
      "creator": {
        "name": "joss amethyst",
        "email": "joss.amethyst@c-mercor.com",
        "user_type": "Human"
      },
      "created_date": "Wednesday, November 19, 2025 at 10:28:24 AM UTC",
      "text": "Hi team, thanks for joining the space. I shared a short meeting invite...",
      "reactions": [
        {
          "emoji": { "unicode": "\ud83d\udc4d" },
          "reactor_emails": ["torin.amethyst@c-mercor.com", "jeran.amethyst@c-mercor.com"]
        }
      ]
    }
  ]
}

2.4 虚拟公司设计

所有用户使用 @c-mercor.com 域名,人名采用颜色代号(amethyst、amber 等)。这是匿名化处理——真实的专业人士在 Google Workspace 中创建了完整的工作场景,然后通过 Google Takeout 导出。


第三章 · Archipelago 框架:MCP 驱动的 Agent 执行环境

3.1 整体架构

┌───────────────────────────────────────────────────────┐
│                    Agent(被评测的 LLM)                │
│              通过 MCP 协议与环境交互                    │
└────────────────────────┬──────────────────────────────┘
                         │ MCP Protocol (JSON-RPC 2.0)
┌────────────────────────▼──────────────────────────────┐
│            Environment Container (Docker)              │
│                    port 8080                           │
│  ┌──────────────────────────────────────────────────┐ │
│  │              MCP Gateway(热插拔路由)              │ │
│  │        POST /apps → 动态配置 MCP Server           │ │
│  │        GET  /mcp  → 统一 MCP 端点                 │ │
│  └───────────┬──────────────────────────────────────┘ │
│              │ stdio                                   │
│  ┌───────────▼──────────────────────────────────────┐ │
│  │              9 个 MCP Server 进程                  │ │
│  │  mail | chat | calendar | filesystem | code       │ │
│  │  spreadsheets | documents | pdfs | presentations  │ │
│  └───────────┬──────────────────────────────────────┘ │
│              │                                         │
│  ┌───────────▼──────────────────────────────────────┐ │
│  │           数据层(World 快照)                     │ │
│  │  /.apps_data/  → mail/.mbox, calendar/.ics, ...  │ │
│  │  /filesystem/  → PDF, Excel, Word, PPT           │ │
│  └──────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────┘

3.2 MCP Server 配置方式

每个 World 启动时,通过 POST 请求动态配置 MCP Server:

config = {
    "mcpServers": {
        "mail_server": {
            "transport": "stdio",
            "command": "python",
            "args": ["main.py"],
            "cwd": "./mcp_servers/mail_server"
        },
        "calendar_server": {
            "transport": "stdio",
            "command": "python",
            "args": ["main.py"],
            "cwd": "./mcp_servers/calendar_server"
        },
        # ... 其他 server
    }
}
requests.post("http://localhost:8080/apps", json=config)

配置完成后,Agent 通过 http://localhost:8080/mcp/ 统一端点访问所有工具。

3.3 评分机制

任务开始 → 创建初始快照(tar.gz)
    ↓
Agent 执行(多步 MCP 工具调用)
    ↓
任务结束 → 创建最终快照
    ↓
Grading 系统:
  1. 对比初始/最终快照的 diff
  2. 提取 Agent 的文本输出
  3. Judge 模型逐条评估 rubric 标准
  4. 汇总二值评分

第四章 · 源码级验证:MCP Server 如何加载真实数据

以下分析基于 Archipelago 开源仓库(github.com/Mercor-Intelligence/archipelago)的实际源码,逐一验证每个关键环节。

4.1 Mail Server:Python mailbox 解析 .mbox

数据加载路径(utils/config.py):

# 数据根目录默认指向 /.apps_data/mail
_apps_data_root = os.getenv("APP_APPS_DATA_ROOT", "/.apps_data")
MAIL_DATA_ROOT = os.getenv("APP_MAIL_DATA_ROOT") or os.path.join(
    _apps_data_root, "mail"
)
MBOX_FILENAME = os.getenv("APP_MAIL_MBOX_FILENAME", "sent.mbox")

自动发现 .mbox 文件(utils/path.py):

def get_mbox_path() -> str:
    mail_dir = resolve_mail_path("")
    if os.path.exists(mail_dir):
        for root, _, files in os.walk(mail_dir):
            mbox_files = [f for f in files if f.endswith(".mbox")]
            if mbox_files:
                return os.path.join(root, mbox_files[0])  # 返回第一个找到的 .mbox
    return os.path.join(mail_dir, MBOX_FILENAME)  # 回退到默认文件名

启动时递归扫描 /.apps_data/mail/ 目录,找到 Google Takeout 导出的 All mail Including Spam and Trash.mbox 文件。

解析邮件(utils/mbox_utils.py)——使用 Python 标准库 email 模块:

def parse_message_to_dict(message) -> dict:
    to_list = parse_email_list(message.get("To", ""))
    cc_list = parse_email_list(message.get("Cc", "")) or None

    # 提取正文:优先 text/plain,其次 text/html
    body = ""
    if message.is_multipart():
        for part in message.walk():
            if part.get_content_type() == "text/plain":
                body = part.get_payload(decode=True).decode("utf-8", errors="ignore")
                break
    else:
        body = message.get_payload(decode=True).decode("utf-8", errors="ignore")

    # 提取线程信息
    thread_id = message.get("X-Thread-ID", None)
    in_reply_to = message.get("In-Reply-To", None)
    references = message.get("References", "").split() if message.get("References") else None

    return {
        "mail_id": message.get("Message-ID", ""),
        "timestamp": message.get("Date", ""),
        "from": parseaddr(message.get("From", ""))[1],
        "to": to_list,
        "subject": message.get("Subject", ""),
        "body": body,
        "thread_id": thread_id,
        "in_reply_to": in_reply_to,
        "references": references,
        # ...
    }

发送邮件 = 追加到 .mbox 文件(tools/send_mail.py)——没有 SMTP,直接写文件:

def send_mail(input: SendMailInput) -> str:
    # 构造 EmailMessage 对象
    msg = EmailMessage()
    msg["From"] = from_email
    msg["To"] = ", ".join(to_list)
    msg["Subject"] = subject
    msg["Date"] = formatdate(timestamp.timestamp(), localtime=True)
    msg["Message-ID"] = make_msgid(domain=from_email.split("@")[-1])

    msg.set_content(body)

    # 追加到 mbox 文件(不是发送到 SMTP 服务器)
    mbox_path = get_mbox_path()
    mbox = mailbox.mbox(mbox_path)
    mbox.lock()
    try:
        mbox.add(msg)    # 追加到文件末尾
        mbox.flush()     # 写入磁盘
    finally:
        mbox.unlock()
        mbox.close()

关键确认:main.py 第 30 行的 instructions 字段明确写道:

“Email stored in mbox format in a sandboxed directory. No external SMTP/IMAP.”

4.2 Calendar Server:icalendar 库解析 .ics

数据加载路径(utils/config.py):

_apps_data_root = os.getenv("APP_APPS_DATA_ROOT", "/.apps_data")
CALENDAR_DATA_ROOT = os.getenv("APP_CALENDAR_DATA_ROOT") or os.path.join(
    _apps_data_root, "calendar"
)

读取所有 .ics 文件(utils/ical.py):

def get_all_ical_files() -> list[Path]:
    calendar_dir = Path(resolve_calendar_path(""))
    return sorted(calendar_dir.rglob("*.ics"))  # 递归扫描所有 .ics

def read_calendar_from_file(file_path: Path) -> Calendar:
    with open(file_path, "rb") as f:
        return Calendar.from_ical(f.read())  # 使用 icalendar 库解析

def get_all_events() -> list[CalendarOutputEvent]:
    events = []
    for ical_file in get_all_ical_files():
        calendar = read_calendar_from_file(ical_file)
        for component in calendar.walk():
            if component.name == "VEVENT":
                event = ical_event_to_calendar_event(component)
                events.append(event)
    return events

创建/更新事件 = 修改 .ics 文件:

def add_event_to_calendar(event, calendar_file=None):
    file_path = calendar_dir / (calendar_file or "calendar.ics")
    if file_path.exists():
        calendar = read_calendar_from_file(file_path)  # 读现有日历
    else:
        calendar = Calendar()  # 或创建新的
    calendar.add_component(calendar_event_to_ical(event))  # 加事件
    write_calendar_to_file(calendar, file_path)  # 写回文件

def update_event_in_calendar(event):
    calendar, old_event, file_path = find_event_in_calendars(event.id)
    calendar.subcomponents.remove(old_event)  # 删旧
    calendar.add_component(calendar_event_to_ical(event))  # 加新
    write_calendar_to_file(calendar, file_path)  # 写回

def delete_event_from_calendar(event_id):
    calendar, event, file_path = find_event_in_calendars(event_id)
    calendar.subcomponents.remove(event)  # 删除
    write_calendar_to_file(calendar, file_path)  # 写回

关键确认:没有 Google Calendar API 调用,全部是本地 .ics 文件的读写操作。

4.3 Chat Server:直接加载 Google Chat JSON

数据加载路径(utils/config.py):

_apps_data_root = os.getenv("APP_APPS_DATA_ROOT", "/.apps_data")
CHAT_DATA_ROOT = os.getenv("APP_CHAT_DATA_ROOT") or os.path.join(
    _apps_data_root, "chat"
)
# 当前用户身份
CURRENT_USER_ID = os.getenv("CHAT_CURRENT_USER_ID", "User 000000000000000000000")
CURRENT_USER_EMAIL = os.getenv("CHAT_CURRENT_USER_EMAIL", "user@example.com")

Chat Server 直接读取 Google Chat Takeout 导出的 JSON 目录结构:

/.apps_data/chat/Google Chat/
├── Groups/
│   ├── DM xxx/messages.json      ← 私聊消息
│   └── Space xxx/messages.json   ← 群组消息
└── Users/
    └── User xxx/user_info.json   ← 用户身份和群组成员

4.4 快照机制:评分前后的状态 diff

快照创建(environment/runner/data/snapshot/main.py):

async def handle_snapshot(pre_snapshot_hooks=None):
    snapshot_id = f"snap_{uuid().hex}"

    # 快照包含两个子系统
    subsystems = [
        settings.FILESYSTEM_SUBSYSTEM_NAME,   # "filesystem"
        settings.APPS_DATA_SUBSYSTEM_NAME     # ".apps_data"
    ]

    # 创建 tar.gz 归档并流式传输
    return create_tar_gz_stream(subsystems, snapshot_id, iter_paths), filename

快照覆盖 filesystem/ 和 .apps_data/ 两个目录——也就是说,Agent 对文件系统的修改(新建文档、编辑 Excel)和对应用数据的修改(发送邮件、创建日程、发送消息)都会被完整捕获。

评分流程:

任务开始 → POST /data/snapshot → 初始快照(snap_xxx_before.tar.gz)
    ↓
Agent 执行(通过 MCP 工具修改 .mbox/.ics/.json/文件)
    ↓
任务结束 → POST /data/snapshot → 最终快照(snap_xxx_after.tar.gz)
    ↓
Grading 系统:diff(before, after) + Agent 输出 → 逐条 rubric 评估

4.5 FastMCP 框架:统一注册工具

所有 MCP Server 使用 FastMCP 框架(Python 的 MCP SDK),工具注册方式统一:

from fastmcp import FastMCP

mcp = FastMCP("mail-server", instructions="...")
mcp.add_middleware(ErrorHandlingMiddleware(include_traceback=True))
mcp.add_middleware(RetryMiddleware())
mcp.add_middleware(LoggingMiddleware())

# 注册工具(两种模式二选一)
if os.getenv("USE_INDIVIDUAL_TOOLS"):
    mcp.tool(list_mails)    # 7 个独立工具
    mcp.tool(read_mail)
    mcp.tool(send_mail)
    # ...
else:
    mcp.tool(mail)          # 2 个合并工具
    mcp.tool(mail_schema)

# 启动(stdio 或 http)
mcp.run(transport="stdio")  # 与 MCP Gateway 通过 stdin/stdout 通信

4.6 总结:完整的数据流

Google Takeout 导出
    │
    ▼
World 数据包(.mbox + .ics + messages.json + PDF/Excel/Word)
    │
    ▼ 部署到 Docker Container
    │
/.apps_data/mail/  ← Mail MCP Server 用 mailbox.mbox() 读写
/.apps_data/calendar/ ← Calendar MCP Server 用 icalendar 库读写
/.apps_data/chat/  ← Chat MCP Server 直接读写 JSON
/filesystem/       ← Filesystem/Spreadsheets/Documents/PDFs Server 操作
    │
    ▼ MCP 协议(JSON-RPC 2.0 over stdio)
    │
MCP Gateway(port 8080,热插拔路由)
    │
    ▼ MCP 协议
    │
Agent(被评测的 LLM)

没有一行代码调用了 Gmail API / Google Calendar API / Google Chat API。

所有操作都是对本地文件的 CRUD——Python 标准库 mailbox 读写 .mbox,icalendar 库读写 .ics,标准 json 模块读写 messages.json。MCP 协议只是把这些文件操作包装成了 Agent 可调用的工具接口。

4.1 核心 9 服务(所有 World 共有)

MCP Server工具数数据源具体 Tools
Mail7.mboxlist_mails, read_mail, search_mail, send_mail, reply_mail, reply_all_mail, forward_mail
Chat9messages.jsonlist_channels, get_channel_history, get_thread_replies, get_user_profile, get_users, post_message, reply_to_thread, add_reaction, delete_post
Calendar5.icslist_events, read_event, create_event, update_event, delete_event
Filesystem-目录树文件读写、目录浏览
Spreadsheets-.xlsxExcel 读写
Documents-.docxWord 读写
PDFs-.pdfPDF 解析提取
Presentations-.pptxPPT 操作
Code-Python 环境代码执行

4.2 投行专用服务(2 个 World 额外配置)

MCP Server用途
Edgar SECSEC 文件检索(10-K、10-Q、8-K 等)
FMPFinancial Modeling Prep——金融市场数据 API

4.3 App 覆盖率

9 个核心服务在所有 33 个 World 中100% 覆盖。Edgar SEC 和 FMP 仅出现在 2 个投行 World 中(6%)。

4.4 Mail Server 工具详解

Mail Server 是最复杂的 MCP 服务之一,提供两种操作模式:

默认模式(7 个独立工具):

工具参数说明
list_mailslimit(1-100), offset分页列出邮件
read_mailmail_id按 Message-ID 读取完整邮件
search_mailfrom_email, to_email, subject, body多字段部分匹配搜索
send_mailfrom, to, subject, body, attachments发送新邮件
reply_mailoriginal_mail_id, body回复单人
reply_all_mailoriginal_mail_id, body回复所有人
forward_mailoriginal_mail_id, to_email转发

合并模式(Consolidated Mode)——所有操作通过一个 mail 工具入口:

mail(action="send", to_email="alice@c-mercor.com", subject="Q3 Review", body="...")
mail(action="search", subject="budget", after_date="2025-11-01")
mail(action="list", limit=20)

合并模式的设计意图:减少 Agent 需要记住的工具数量,用 action 参数做路由。这降低了工具选择的认知负担,但增加了参数组合的复杂度。

4.5 Chat Server 工具详解

Chat Server 模拟了类 Slack/Google Chat 的群组消息系统:

工具说明
list_channels列出频道(DM + Space)
get_channel_history获取频道消息历史(默认 30 条)
get_thread_replies获取帖子回复
get_user_profile获取用户资料
get_users列出所有用户
post_message发送消息到频道
reply_to_thread回复帖子
add_reaction添加 emoji 反应
delete_post删除帖子

4.6 Calendar Server 工具详解

标准 CRUD:

工具参数
list_eventslimit
read_eventevent_id
create_eventsummary, start, end, attendees, location, reminders
update_eventevent_id + 任意字段
delete_eventevent_id

第六章 · 典型任务解剖

5.1 投行任务——交易倍数计算

Prompt: 计算 Kenvue 交易的 FY24 税前目标倍数,使用中位数潜在交易价值。
        返回 Deal value/EBITDA、Deal value/EBIT、Deal value/OpFCF,保留一位小数。

需要的能力链:
  1. filesystem → 找到相关 Excel 模型
  2. spreadsheets → 读取财务数据(EBITDA、EBIT、OpFCF)
  3. spreadsheets → 找到中位数交易价值
  4. code → 计算三个比率
  5. 输出文本结果

Gold: Deal value/EBIT: 24.9x, Deal value/EBITDA: 18.6x, Deal value/OpFCF: 34.5x

5.2 法务任务——合同条款分析

Prompt: 供应商原材料来自伊拉克。特朗普 7 月 20 日宣布额外关税。
        供应商能否基于 Master Supply Agreement Template 1 援引不可抗力条款?

需要的能力链:
  1. filesystem → 找到 Master Supply Agreement 文档
  2. documents/pdfs → 读取合同全文
  3. 定位 Section 14(不可抗力条款)
  4. 理解 25% 关税阈值条件
  5. 推理:35% > 25% → 可以援引
  6. 输出分析结论

Gold: 可以。Section 14 规定关税增幅超过 25% 即可援引。
      伊拉克商品关税 35% > 25%,供应商有权援引。

5.3 咨询任务——投资组合分析

Prompt: Rakling 的强卖建议会为 HP 带来多少欧元现金?
        假设以 2025 年 10 月 30 日价格出售,无交易成本。

需要的能力链:
  1. mail/chat → 理解 Rakling 是谁、强卖建议指什么
  2. filesystem → 找到投资组合数据
  3. spreadsheets → 读取持仓和价格数据
  4. code → 计算总收益(EUR 77,977,663.40)
  5. 输出精确数字

Gold: EUR 77,977,663.40

第七章 · Leaderboard 分析

6.1 当前排名(2026.04)

模型Pass@1Pass@8Mean ScoreIB咨询法务
GPT-5.223.0%40.0%38.7%27.3%22.7%18.9%
Gemini 3 Flash24.0%36.7%39.5%26.7%19.3%25.9%
Claude Opus 4.518.4%34.0%34.8%21.6%13.2%20.2%
Gemini 3 Pro18.4%37.3%34.1%18.8%12.4%23.9%
GPT-518.3%31.0%32.9%27.3%12.3%15.3%
Grok 415.2%32.9%30.3%17.0%12.0%16.5%
Kimi K2 Thinking4.0%14.4%11.5%1.2%2.9%8.0%
GPT-OSS-120B4.7%11.5%14.5%2.7%3.5%7.8%

6.2 关键观察

整体水平极低:最强模型 Pass@1 仅 23%,意味着 77% 的专业任务 Agent 无法一次做对。

领域差异显著:

  • 投行任务(数值计算为主)相对最好——GPT-5 和 GPT-5.2 均达 27.3%
  • 咨询任务(综合分析)最难——即使 GPT-5.2 也仅 22.7%
  • 法务任务(合同推理)中间——Gemini 3 Flash 表现最好(25.9%),可能受益于长上下文能力

Pass@1 vs Pass@8 差距大:GPT-5.2 从 23% 到 40%,说明模型有能力但不稳定——同样的任务跑 8 次,至少有一次对的概率翻了近一倍。

开源模型差距悬殊:GPT-OSS-120B(推测为开源 120B 模型)仅 4.7%,与商业模型差距 5 倍以上。

6.3 为什么这么难

  1. 长程依赖:平均 1.82 小时的人类任务,Agent 需要数十步工具调用
  2. 跨应用推理:答案散落在邮件、聊天、Excel、PDF 中,需要交叉验证
  3. 隐性知识:投行的”中位数交易价值”、法务的”不可抗力条款触发条件”等专业概念
  4. 精确性要求:财务计算要求精确到分(EUR 77,977,663.40),不是”大约 7800 万”

第八章 · 对比分析

7.1 APEX-Agents vs 其他 Agent Benchmark

维度APEX-AgentsGAIASWE-benchOSWorldTheAgentCompany
领域金法医通用软件工程操作系统企业软件
数据来源真实 Google Takeout人工构造GitHub Issue手动设计手动设计
交互协议MCPFunction CallingCode-as-ActionGUI/Code混合
工具数量9-11 个 MCP Server按需Shell + 编辑器整个 OS企业 SaaS
平均任务时长1.82 小时分钟级分钟级分钟级小时级
评分方式Rubric 多标准精确匹配测试通过率状态检查Rubric
最高 Pass@124%~70%~53%~22%~25%
环境真实性高(真实数据)低高(真实代码库)最高(真实 VM)高

7.2 APEX-Agents 的独特价值

  1. 真实数据而非合成:Google Takeout 导出保留了真实的邮件上下文、团队动态、文件版本历史
  2. 专业深度:由投行分析师、律师、顾问亲自设计的任务,不是通用的”帮我搜索一下”
  3. MCP 标准化:工具接口使用 MCP 协议,可直接对接任何支持 MCP 的 Agent 框架
  4. 精确可验证:rubric 标准是二值的(对/错),不需要主观判断

7.3 局限性

  1. 仅三个领域:金融、法律、咨询——不覆盖医疗、科研、工程等
  2. 单次尝试评测:没有错误恢复/多次尝试的机制
  3. 无协作维度:Agent 独自工作,没有人机交互或多 Agent 协作
  4. Web 搜索禁用:为了可复现性,禁止了 Web 搜索——但真实工作中搜索是核心能力
  5. CC-BY 4.0 但禁止训练:明确禁止用于模型训练,仅限评测

第九章 · 对数据合成的启示

8.1 World 构建方法论可复用

APEX-Agents 的 World 构建方法本身就是一套数据合成方法论:

真实专家 + Google Workspace → Google Takeout 导出 → 匿名化 → World 数据包

这个流程可以复制到其他领域:

  • 医疗:临床医生的邮件 + 病历系统 + 电子处方
  • 科研:研究员的论文管理 + 实验记录 + 通讯协作
  • 教育:教师的 LMS + 学生作业 + 家长沟通

8.2 MCP Server 是 Harness 反转的好载体

APEX-Agents 的 9 个 MCP Server 定义了清晰的工具接口规范。这些规范可以直接用来生成训练数据:

工具接口可生成的训练数据
mail(action="search", subject="budget")邮件检索 + 条件过滤任务
spreadsheets.read_cell(sheet, range)Excel 数据提取 + 计算任务
calendar.create_event(...)日程管理 + 冲突检测任务
多工具组合跨应用推理链(邮件→Excel→回复)

8.3 Rubric 设计模式值得借鉴

APEX-Agents 的 rubric 不检查”最终答案是否等于 gold”,而是检查推理链的关键节点:

不是: answer == "EUR 77,977,663.40"
而是:
  ✓ 正确识别了 Rakling 的强卖建议
  ✓ 找到了正确的持仓数据
  ✓ 使用了 10 月 30 日的价格
  ✓ 最终金额正确

这种设计让评测更加鲁棒——即使最终数字因四舍五入略有差异,只要推理链正确,仍能获得部分分数。


核心论文与参考索引

项目详情
APEX-Agents 论文arXiv:2601.14242 (Mercor, 2026.01)
Archipelago 框架github.com/Mercor-Intelligence/archipelago
数据集huggingface.co/datasets/mercor/apex-agents
Leaderboardmercor.com/apex/apex-agents-leaderboard
MCP 协议modelcontextprotocol.io

第十章 · 关键设计决策分析:为什么这样而不那样

APEX-Agents 的架构中有一系列非显而易见的设计决策。这些决策不是随意的——每一个都在评测可信度、工程成本、安全性、可扩展性之间做了精心的 trade-off。本章逐一分析。

10.1 为什么用 Google Takeout 真实数据,而不是合成数据

决策:从真实专业人士的 Google Workspace 导出数据,匿名化后作为评测环境。

替代方案:用 LLM 合成虚拟邮件/聊天/文件(TheAgentCompany 就是这么做的)。

选择真实数据的原因:

  1. 信息密度不可合成。真实投行分析师的收件箱有几十封邮件构成的因果链——A 发给 B 一个 Excel 模型,B 回复说第三行有误,A 修改后抄送 C 确认。LLM 合成这种多人多轮、跨应用的信息网络,容易出现逻辑断裂。

  2. 隐性上下文真实。真实数据中,聊天里会出现”那个文件你看了吗”但不说是哪个文件——因为上下文对真实用户是显然的。这种省略正是 Agent 需要从邮件和文件系统中推理出来的。合成数据往往过于显式。

  3. 文件生态真实。真实年报是 200 页 PDF,真实财务模型是多 sheet 的 Excel,真实备忘录用了特定的 Word 模板。这些格式细节(扫描页、嵌套公式、DUMMYFUNCTION 伪公式)是 Agent 在真实工作中必然遇到的。

代价:数据收集成本高(需要招募专业人士、配置 Workspace、执行真实工作流、Google Takeout 导出、匿名化处理)。CC-BY 4.0 但禁止训练,也限制了数据的二次利用。

10.2 为什么用 MCP 模拟层,而不是跑真实服务

决策:用 Python 解析 .mbox/.ics/.json 文件,通过 MCP 协议暴露标准化 CRUD 工具。不跑真实的 Gmail/Calendar/Chat 服务。

替代方案:在 Docker 中部署 Roundcube(Web Mail)+ Radicale(CalDAV)+ Rocket.Chat 等真实服务,让 Agent 通过 API 或 GUI 操作。

选择模拟层的原因:

  1. 快照可行性。评分需要对比初始和最终状态的 diff。对文件系统做 tar.gz 快照是毫秒级的;对运行中的 IMAP 服务器做状态快照几乎不可能——你需要 dump 整个邮件数据库。

  2. 确定性与可复现。同一个 World 的每次评测必须从完全相同的初始状态开始。文件快照天然满足这个要求;而真实服务有后台进程、缓存、时间戳等不可控因素。

  3. 部署成本。MCP Server 是单个 Python 进程,用 fastmcp 几百行代码就能实现完整的 CRUD。而部署一套企业邮件+日历+聊天系统需要多个容器、数据库、认证服务——仅 Roundcube 一个就需要 Postfix + Dovecot + MySQL。

  4. 安全隔离。Agent 的”发送邮件”操作只是在 .mbox 文件末尾追加一条记录,不会真的发出去。如果用真实 SMTP,需要额外的安全措施防止 Agent 向外部发送邮件。

代价:Agent 不需要处理真实的认证流程、网络延迟、API 限流等工程问题。推测这会高估 Agent 在真实部署时的表现——但在评测”理解任务并正确操作”这个核心能力上,模拟层已经足够。

10.3 为什么提供合并模式(Consolidated Mode)

决策:每个 Server 提供两种工具注册模式——7+ 个独立工具 vs 2 个 meta-tools(如 mail(action="send", ...))。

分析:

这不是一个可有可无的功能,而是一个经过实验验证的设计。源码中 main.py 的注释明确区分了两种场景:

USE_INDIVIDUAL_TOOLS=true → "7 individual tools for UI display"
USE_INDIVIDUAL_TOOLS=false → "2 meta-tools for LLM agents"

对 LLM Agent 的影响:

方面独立模式(7+ 工具)合并模式(2 工具)
工具选择准确率低——Agent 需要从 7+ 工具中选对一个高——只需选 mail,action 参数在描述中列出
参数填写复杂度低——每个工具参数明确高——需要根据 action 填写不同的参数组合
Token 消耗高——所有工具的 schema 都要塞进 system prompt低——只有 2 个工具的 schema
调试可读性高——直接看工具名就知道做了什么低——需要看 action 参数

OpenAI 的研究表明,当工具数超过 20 个时,模型的工具选择准确率显著下降。APEX-Agents 全量 9 个 Server × 平均 8 个工具 = 72 个工具——这远超 20 个的临界点。合并模式把 72 个工具压缩到 18 个(9 × 2),大幅降低了 Agent 的认知负担。

10.4 为什么 Filesystem 是只读的

决策:filesystem_server 只提供 list/read/search/metadata/tree 操作,没有 write/create/delete。

看似矛盾:Agent 的某些任务需要创建新文件(12.1% 的任务要求文件输出)。

实际设计:文件写入由其他 Server 负责——documents_server 创建 Word,sheets_server 创建 Excel,slides_server 创建 PPT,pdf_server 创建 PDF,code_execution_server 可以执行任意 shell 命令(包括 touch/echo 等)。

这种分离的原因:

  1. 最小权限原则。文件浏览器只需要读权限,不应该能写。把读和写分到不同的 Server,可以对每个 Server 设置不同的安全策略。

  2. 结构化写入。直接给 Agent 一个 write_file(path, content) 工具,Agent 可能直接写入原始文本而不遵循文档格式。通过 documents_server 的 create_document 工具,Agent 被迫使用结构化的参数(段落、标题、表格),输出的 .docx 更可能是格式正确的。

  3. 快照粒度。读操作不改变状态,写操作改变状态。分离后,评分系统可以只 diff 那些有写入权限的 Server 管理的目录。

10.5 为什么 Code Execution 用 LD_PRELOAD 沙箱

决策:通过 LD_PRELOAD=sandbox_fs.so 拦截 libc 文件系统调用来实现沙箱,屏蔽 /app 和 /.apps_data 目录。

替代方案:Docker 容器隔离(已有)、proot、gVisor、seccomp、chroot。

选择 LD_PRELOAD 的原因(源码注释明确说明):

“Unlike proot, this approach has no ptrace overhead, works on unprivileged container platforms (Fargate, Modal, Fly.io), and is purely userspace with no kernel privileges needed.”

翻译:

  1. 零 ptrace 开销。proot 通过 ptrace 拦截系统调用,每个文件操作都需要内核上下文切换,性能损失 30-50%。LD_PRELOAD 在用户空间拦截 libc 调用,几乎零开销。

  2. 兼容无特权容器。Fargate、Modal、Fly.io 等 serverless 平台不允许 ptrace 或修改内核。LD_PRELOAD 只需要一个 .so 文件,任何 Linux 环境都能用。

  3. Fail-closed 设计。源码中每次执行前都检查 sandbox_fs.so 是否存在——如果不存在,拒绝执行而不是无沙箱运行。这比 LD_PRELOAD 的默认行为(找不到库就跳过,打个警告继续执行)安全得多。

10.6 为什么 Chat 的删除是软删除

决策:delete_post 设置 message_state="DELETED" + 清空内容,但保留在 messages.json 中。

原因:

  1. 评分可追溯。如果 Agent 删除了一条不该删的消息,评分系统需要能检测到”曾经存在过一条消息被错误删除了”。硬删除后这个信息就丢了。

  2. 模拟真实行为。Google Chat 和 Slack 的删除本身就是软删除——消息显示”此消息已被删除”,但 admin 仍可查看。

  3. 防止数据丢失。Agent 的操作不可逆(没有 undo 机制),软删除相当于内置了一层安全网。

10.7 为什么 Spreadsheets 需要 LibreOffice

决策:openpyxl 读写 Excel,但需要调用 soffice --headless 来重新计算公式。

根因:openpyxl 的 data_only=True 模式只读取上次保存时缓存的公式结果。如果 Agent 修改了某个单元格的值,依赖它的公式不会自动重算——openpyxl 不是计算引擎。

解决方案:修改后调用 LibreOffice 做一次 headless 转换,LibreOffice 的计算引擎会重新计算所有公式并缓存结果。

这对 Agent 评测的影响:Agent 修改了 Excel 后,read_tab 能读到正确的公式结果(而不是过期的缓存值)。这对财务分析类任务至关重要——一个错误的中间值会导致后续所有计算出错。

10.8 为什么 PDF 需要双引擎 + OCR

决策:read_pdf_pages 先用 PyMuPDF(fitz),空则回退 pypdf layout mode,再空则 OCR fallback。

原因:World 数据包含真实的公司年报——这些 PDF 的复杂度远超普通文档:

  • 有些页是扫描件(纯图片,没有文本层)→ 需要 OCR
  • 有些用了复杂的排版(多栏、表格、页眉页脚)→ fitz 比 pypdf 处理得好
  • 有些是 Google Docs 导出的 PDF → pypdf 的 layout mode 可能更准

双引擎 + OCR 保证了 Agent 在任何格式的 PDF 上都能提取到文本。大 PDF(>5 页)还用了 ThreadPoolExecutor(4 workers)并行搜索——因为投行年报动辄 200+ 页,串行搜索太慢。

10.9 综合:设计哲学总结

原则体现
真实数据 > 合成数据Google Takeout 导出而非 LLM 生成
文件操作 > 服务调用mbox/ics/json CRUD 而非 Gmail API
状态可快照tar.gz 归档 filesystem + .apps_data
Fail-closed沙箱库缺失则拒绝执行
最小权限Filesystem 只读,写入由专用 Server 负责
可追溯Chat 软删除保留记录
工具数量控制合并模式 72→18 工具
格式兼容性PDF 双引擎+OCR,Excel LibreOffice 重算

这套设计哲学的核心是:让评测环境尽可能接近真实工作场景,同时保持工程上的可控性和可复现性。每一个”不够真实”的决策(如用文件替代服务)都有明确的工程理由,而每一个增加复杂度的决策(如双引擎 PDF、LibreOffice 公式重算)都是为了避免评测失真。


第十一章 · 开源的边界:480 个任务中有多少真正可复现

在前面的分析中,我们多次提到 FMP、EDGAR 等非公开服务。本章将这些发现系统化,回答一个关键问题:拿到开源数据集和框架后,研究者到底能复现多少?

11.1 从源码中泄露的完整服务清单

Archipelago 仓库中每个 MCP Server 的 scripts/extract_tools.py 都包含了一段环境变量设置代码。这段代码的本意是在 CI 中提取工具 schema 时跳过真实 API 调用——但它无意中泄露了 Mercor 内部使用的全部服务列表:

# extract_tools.py — 每个 MCP Server 目录下都有这份相同的代码

# Set offline mode for all services to avoid API key requirements
os.environ["OFFLINE_MODE"] = "true"
os.environ["TERRAPIN_OFFLINE"] = "1"           # 未知内部服务
os.environ["DATAGOV_OFFLINE_MODE"] = "true"    # 政府公开数据 API
os.environ["XERO_OFFLINE_MODE"] = "true"       # Xero 会计 SaaS
os.environ["CANVAS_OFFLINE"] = "true"          # Canvas LMS(学习管理)
os.environ["EDGAR_OFFLINE_MODE"] = "true"      # SEC EDGAR 上市公司文件
os.environ["FMP_OFFLINE_MODE"] = "true"        # Financial Modeling Prep
os.environ["COURT_LISTENER_OFFLINE"] = "true"  # CourtListener 法院判例

# Set dummy API keys to prevent startup failures
os.environ.setdefault("TERRAPIN_API_KEY", "dummy_key_for_extraction")
os.environ.setdefault("FMP_API_KEY", "dummy_key_for_extraction")
os.environ.setdefault("XERO_CLIENT_ID", "dummy_client_id")
os.environ.setdefault("XERO_CLIENT_SECRET", "dummy_secret")
os.environ.setdefault("CANVAS_API_KEY", "dummy_key")
os.environ.setdefault("DATAGOV_API_KEY", "dummy_key")
os.environ.setdefault("SEARCH_MCP_GOOGLE_API_KEY", "dummy_key_for_extraction")
os.environ.setdefault("SEARCH_MCP_GOOGLE_CSE_ID", "dummy_cse_id_for_extraction")

从这段代码可以确认:至少存在 7 个非公开的 MCP Server + 1 个 Google 搜索服务。

Dockerfile 中还额外暴露了两个可选的 Python 包组:

INSTALL_MEDICINE=false INSTALL_SCICOMP=false

对应推介材料中 Medicine 和 Data Science 两个尚未公开的领域。

11.2 开源 vs 非公开的完整对比

服务类型开源仓库推介材料数据集用到
Calendar通用✅✅✅ 33/33 worlds
Chat通用✅✅✅ 33/33 worlds
Mail通用✅✅✅ 33/33 worlds
Filesystem通用✅✅✅ 33/33 worlds
Spreadsheets通用✅✅✅ 33/33 worlds
Documents通用✅✅✅ 33/33 worlds
PDFs通用✅✅✅ 33/33 worlds
Presentations通用✅✅✅ 33/33 worlds
Code Execution通用✅✅✅ 33/33 worlds
FMP金融❌✅⚠️ 2/33 worlds
EDGAR SEC金融❌✅⚠️ 2/33 worlds
Xero会计❌✅—
CourtListener法律❌——
DataGov政府❌——
Canvas教育❌——
Terrapin内部❌——
Google Search搜索❌——

11.3 受影响的 28 个任务

公开数据集中,2 个投行 World 声明了 FMP 和 EDGAR SEC 依赖:

  • world_5970ed13783a463181bdf38337f0cad1(16 个 task)
  • world_43a921f91f0f4d2c85d8bd2774f9e681(12 个 task)

这 28 个 task 中,部分需要实时金融数据——例如特定日期的股票收盘价和美国国债利率:

“Update the base-case DCF model of KVUE with U.S. total equity risk premium of 4.33%, the risk free rate with the 5-Year Treasury rate and the KVUE Close share price on 2025-12-15.”

这类数据只能通过 FMP MCP Server 获取。在开源环境中:

  • FMP Server 不存在 → 无法查询股价和利率
  • INTERNET_ENABLED=false(Dockerfile 第 63 行)→ Code Execution 也无法联网
  • Web 搜索被禁用 → “Web search is disabled to keep evaluations reproducible”

Agent 在执行这些 task 时会找不到任何数据源,必然失败。

11.4 OFFLINE_MODE:缓存注入机制

extract_tools.py 中每个非公开服务都有 xxx_OFFLINE_MODE 环境变量。这证实了这些服务支持离线模式——不调真实 API,从本地缓存读数据。

结合 Archipelago 的 POST /data/populate 机制(支持从 S3 下载数据到容器 + 执行 lifecycle hooks),完整的工作流推测如下:

一次性准备(Mercor 内部):
  FMP API(付费)→ 查询所需股价/利率 → 缓存为文件 → 上传到 S3

每次评测运行:
  Orchestrator → POST /data/populate → Docker 容器
    ├── source 1: World zip(filesystem + .apps_data)← 这部分已开源
    ├── source 2: FMP 缓存数据 → /.apps_data/fmp/  ← 这部分未开源
    └── hook: 启动 FMP Server(OFFLINE_MODE=true)

验证:解压两个有 FMP/EDGAR 依赖的 World zip 后,.apps_data/ 目录下只有 calendar/chat/mail——没有 fmp 或 edgar 目录。这确认了 FMP 数据不在 HuggingFace 公开的 zip 包里,而是通过 S3 populate 另行注入的。

example.py 中还泄露了真实的 S3 Bucket 名:

"url": "s3://rl-studio-snapshots-prod/worlds/snap_xxx/filesystem/"

rl-studio-snapshots-prod 是 Mercor 的私有 S3 存储桶,外部无法访问。

11.5 文档中的模糊表述

HuggingFace 数据集页面只有一句含糊的说明:

“Some worlds include additional finance data applications.”

没有说明:这些 “additional finance data applications” 具体是什么?开源版本是否包含?缺少它们哪些 task 无法执行?

Archipelago README 提供了一个名为 mcp_config_all_oss_servers.json 的配置文件——文件名中 oss(Open Source Software)暗示这不是全部 Server,但文档没有任何进一步解释。

HuggingFace 示例说 ./run.sh 42 可以运行 0-479 任意索引的 task——没有警告某些 task 可能因缺少非公开 Server 而失败。

11.6 实际可复现比例

类别任务数占比可完整复现
仅依赖 9 个开源 Server45294.2%✅
依赖 FMP + EDGAR285.8%❌
合计480100%94.2%

94.2% 的可复现率对 benchmark 来说其实还可以,但问题在于:

  1. 研究者不会事先知道哪 28 个 task 会失败——错误表现为 Agent 找不到数据源(或瞎编一个数字),而不是框架报错”缺少 FMP Server”
  2. 这 28 个 task 全部来自投行领域——如果只评测投行 Agent,受影响比例远高于 5.8%
  3. Leaderboard 上的成绩是用完整环境跑的——开源用户无法精确复现官方排行榜的数字

11.7 对研究者的建议

  1. 跳过 2 个 FMP/EDGAR World。在评测时过滤掉 world_5970ed13 和 world_43a921f9,用 452 个 task 评测
  2. 自建 FMP Server。FMP API 有免费额度(每天 250 次),可以自己写一个 MCP Server 实现离线缓存模式
  3. 比较时注意口径。论文报告的 Pass@1 是基于 480 个 task 的,自己跑的是 452 个——需要分别计算
  4. 关注 Mercor 后续开源动态。推介材料中列出的 QuickBooks、Workday、OpenEMR 等服务如果后续开源,可执行的 task 会进一步增加

附录 · 9 个 MCP Server 实现逻辑详解

以下基于 Archipelago 仓库源码(github.com/Mercor-Intelligence/archipelago/mcp_servers/)的逐文件分析。

通用架构模式

所有 9 个 Server 共享同一套架构:

  • 框架:FastMCP + Pydantic 数据模型 + 中间件栈(ErrorHandling → Retry → Logging → ValidationErrorSanitizer)
  • 双模式注册:USE_INDIVIDUAL_TOOLS=true 暴露独立工具(UI 友好),否则暴露 2 个 meta-tools(LLM 友好)
  • 异步执行:所有工具函数通过 @make_async_background(基于 asyncer.asyncify)在后台线程执行
  • 传输:支持 HTTP(host=0.0.0.0)和 stdio 双模式
  • 沙箱安全:所有路径操作均有 resolve_under_root + realpath symlink 解析 + 路径遍历检查

A.1 Calendar Server

属性值
数据根/.apps_data/calendar
数据格式iCalendar .ics
核心库icalendar(iCal 解析/生成)
工具数5:create_event / read_event / update_event / delete_event / list_events

实现要点:

  • get_all_ical_files():递归扫描 *.ics,解析所有 VEVENT 组件
  • create_event:生成 YYYYMMDD_HHMMSS_microseconds_random6 格式 ID → 构造 CalendarOutputEvent → 转为 iCal VEVENT → 追加到 .ics 文件
  • update_event:遍历所有 .ics 查找 UID → calendar.subcomponents.remove(old) → 加入更新后的 VEVENT → 回写
  • delete_event:查找并 remove → 回写
  • list_events:加载所有事件 → 按 start time 排序 → 分页
  • 支持:all-day / timed 事件、attendees、RFC 5545 RRULE 循环、VALARM 提醒

A.2 Chat Server

属性值
数据根/.apps_data/chat(含 Google Chat 子目录 fallback)
数据格式JSON(Google Chat Takeout 格式)
当前用户CHAT_CURRENT_USER_EMAIL 环境变量
工具数9

目录映射:Groups/{group_id}/messages.json + group_info.json,Users/{user_id}/user_info.json

实现要点:

  • list_channels:扫描 Groups/ 目录 → 加载 group_info.json 和 messages.json → 统计成员和消息数
  • get_channel_history:加载 messages.json → 过滤只取 root messages(topic_id == message_id 末段)→ 逆序分页
  • post_message:生成 topic_id + message_id → 构造 ChatMessage → 追加到 messages.json
  • reply_to_thread:找到父消息 → 复用其 topic_id → 新 message_id(is_reply=True)→ 追加
  • add_reaction:找到消息 → 添加 EmojiReaction 到 reactions 数组 → 去重检查
  • delete_post:软删除——设置 message_state="DELETED",清空 text/reactions/annotations,保留在文件中
  • get_users:扫描 Users/ 目录 → 读 user_info.json → 分页

A.3 Code Execution Server

属性值
工作目录/filesystem
沙箱LD_PRELOAD=sandbox_fs.so 拦截 libc 文件系统调用
屏蔽路径/app, /.apps_data
工具数1:code_exec

实现要点:

  • 接收的是 shell 命令(非 Python 代码)——有 Python 检测逻辑(import/from/def/class/@),误传 Python 会提示用 python -c 包裹
  • run_sandboxed_command:subprocess.Popen(["sh", "-c", command]) + start_new_session=True(新进程组便于超时杀死)
  • Fail-closed:每次执行前检查 sandbox_fs.so 是否存在,不存在则拒绝执行
  • 超时默认 300 秒,输出限制 100KB(HTML 限 2KB)
  • 环境隔离:清除 PYTHONPATH,设置 HOME=/tmp,过滤 mise/venv 路径

A.4 Documents Server

属性值
数据根APP_FS_ROOT(/filesystem)
数据格式Word .docx
核心库python-docx
工具数15

工具列表:create_document / delete_document / get_document_overview / read_document_content / read_image / add_content_text / edit_content_text / delete_content_text / add_image / modify_image / apply_formatting / header_footer / page_margins / page_orientation / comments

实现要点:

  • 稳定元素标识符:helpers/identifier.py 实现 body.p.0 格式的路径定位,使 Agent 可以精确引用文档内任意元素
  • 数据模型 37.9KB(utils/models.py),覆盖 Word 文档的所有结构化操作
  • 文件锁机制避免并发写入

A.5 Filesystem Server

属性值
数据根/filesystem
权限只读
工具数6

工具列表:list_files / read_text_file / search_files / read_image_file / get_file_metadata / get_directory_tree

实现要点:

  • read_text_file:白名单扩展名检查(~50 种:py/json/csv/md/xml/yaml/html/…)→ symlink 安全验证 → 读取
  • search_files:os.walk + fnmatch glob 匹配 → followlinks=False 防逃逸 → 最多 100 条
  • read_image_file:支持 png/jpg/gif/webp → magic bytes 检测实际格式 → 返回 Image 对象
  • get_directory_tree:递归 ASCII 树(深度 1-10,最多 500 条目)
  • 安全:多层路径检查(resolve_under_root + realpath + is_path_within_sandbox)

A.6 Mail Server

属性值
数据根/.apps_data/mail
数据格式mbox(Python mailbox 标准库)
工具数7

详见第四章源码分析。核心:mailbox.mbox() 读写,mbox.add(msg) + mbox.flush() 实现”发送”。

A.7 PDF Server

属性值
数据根APP_FS_ROOT(/filesystem)
核心库pypdf + PyMuPDF (fitz) + reportlab + Pillow
工具数5

工具列表:create_pdf / read_pdf_pages / search_pdf / read_image / read_page_as_image

实现要点:

  • read_pdf_pages:双引擎策略——先用 fitz 提取文本,空则回退 pypdf layout mode,再空则 OCR fallback。同时提取嵌入图片(pypdf XObject)和 StrikeOut 注释
  • search_pdf:大 PDF(>5 页)使用 ThreadPoolExecutor(4 workers)并行搜索。支持大小写敏感、全词匹配、上下文字符数配置
  • create_pdf:基于 reportlab 从结构化 block 列表(paragraph/heading/bullet_list/numbered_list/table/page_break/spacer)生成 PDF
  • read_page_as_image:fitz 渲染单页为 144 DPI PNG → PIL 转 JPEG
  • 图片缓存:read_pdf_pages 提取的图片存入 IMAGE_CACHE,read_image 通过 annotation key 取出

A.8 Presentations Server

属性值
数据根APP_FS_ROOT(/filesystem)
核心库python-pptx + MarkItDown
工具数13

工具列表:create_deck / delete_deck / add_slide / edit_slides / add_image / modify_image / insert_chart / insert_table / add_shape / read_slides / read_completedeck / read_individualslide / read_image

实现要点:

  • create_deck:Presentation() → 清除模板幻灯片 → 按 layout map 添加(title/title_and_content/section_header/two_content/title_only/blank)→ 填充 title/subtitle/bullets/table/notes
  • add_slide:在指定 index 插入(通过 XML 操作 _sldIdLst 移动位置)
  • edit_slides:578 行,支持文本格式(字体/颜色/大小/粗体/斜体)、hyperlink、table cell 格式、对齐等
  • read_slides:用 MarkItDown 库将 .pptx 转 Markdown → 返回字符范围(最大 10,000 chars)
  • 数据模型巨大:slide_edit.py 18KB,tool_inputs.py 17KB

A.9 Spreadsheets Server

属性值
数据根APP_FS_ROOT(/filesystem)
核心库openpyxl + LibreOffice(soffice --headless)
工具数12

工具列表:create_spreadsheet / delete_spreadsheet / read_tab / read_csv / list_tabs_in_spreadsheet / add_tab / delete_tab / edit_spreadsheet / add_content_text / delete_content_cell / create_chart / filter_tab

实现要点:

  • read_tab:openpyxl.load_workbook(data_only=True) → 若数据为空尝试 LibreOffice 重新计算 → normalize_cell_value 处理日期/DUMMYFUNCTION → trim_empty_trailing_data
  • edit_spreadsheet(30KB,833 行):支持 SetCell / AppendRows / FormatCells / MergeCells / FreezePanes / SetColumnWidth / SetRowHeight / SetNumberFormat / AddConditionalFormatting / AddDataValidation / AddImage / AddNamedRange / SetAutoFilter / RenameSheet 等
  • recalculate_formulas:调用 soffice --headless --calc --convert-to xlsx 触发 LibreOffice 公式重算
  • normalize_cell_value:处理 openpyxl datetime、Excel serial date(1-2,958,465 范围)、Google Sheets DUMMYFUNCTION 伪公式
  • 数据模型最大:sheet.py 43.3KB,response.py 18.6KB

第三方库汇总

Server关键库用途
CalendaricalendariCal 解析/生成
Chat纯标准库(json/os)JSON 读写
Codesubprocess + LD_PRELOAD沙箱命令执行
Documentspython-docxWord 文档操作
Filesystem纯标准库只读文件浏览
Mailmailbox(标准库)mbox 读写
PDFpypdf + fitz + reportlab + PillowPDF 解析/生成/OCR
Presentationspython-pptx + MarkItDownPPT 操作
Spreadsheetsopenpyxl + sofficeExcel 操作 + 公式重算

所有 Server 共用:FastMCP, Pydantic, loguru, asyncer, mcp_schema