Files
worldquant-alpha-system/docs/mcp-research.md
T
yuxuanhui 7e990b9a69
Deploy production / deploy (push) Successful in 57s
feat: 增加仅检查 MCP 工具并完善已提交 Alpha 指标展示
2026-09-11 13:14:27 +08:00

149 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MCP 研究接入
MCP 让外部助手直接查询数据元信息、查回测历史、提交固定候选、执行本地自相关检查并读取结果。无需先建立特征、模板、变体或 QuantFlow。执行仍由网页共用的持久队列负责,回测页保存同一个运行。
## 启用与令牌
应用依赖官方 `mcp==2.2.0`,锁文件固定版本。增量迁移 `0010` 增加 `mcp_tokens`、`research_requests` 和 `mcp_audits`,不重写旧业务记录。
默认 `MCP_ENABLED=false`,入口返回 404。需要启用时,在目标部署配置中显式设置为 true,按该环境既有升级流程迁移并重建后端。生产仍限一个后端 worker。入口为 `PUBLIC_ORIGIN/api/v1/mcp/`;无尾斜杠地址会重定向。公网须使用 HTTPS,代理保留原始 Host。现有 Caddy 已保持 Host;Vite 开发代理也显式保持。
本功能不实现 OAuth;客户端必须能够自行配置 `Authorization: Bearer <PAT>`。PAT 不能作为 Cookie,也不能访问网页管理接口。无需添加 `X-WQ-Request`。若请求包含 Origin,须与 PUBLIC_ORIGIN 一致。
先在网页连接并确认 WorldQuant 账户身份。进入侧栏 **系统管理 → MCP Key**,填写名称、有效期并选择权限,即可创建 Key。默认只读、90 天;创建后明文仅显示一次,关闭、刷新或离开页面后无法找回。列表支持分页查看权限、到期时间和状态,并可撤销 Key。页面同时显示连接地址和 MCP 服务是否启用;管理页面不会自动打开服务开关。
管理接口沿用管理员 Cookie 会话和网页请求保护,MCP PAT 不能创建、查看或撤销 Key。明文不写入浏览器存储,也不传给网页研究助手。
也可在所需环境执行管理命令。以下是命令模板,不会由文档自动执行:
```bash
# 默认只读,90 天;--days 支持 1–365。
python -m app.cli mcp-token-create --name research-reader
# 需要对应业务权限时显式列出;必须包含 research:read。
python -m app.cli mcp-token-create --name research-executor \
--scope research:read --scope research:refresh \
--scope backtests:execute --scope backtests:control
python -m app.cli mcp-token-list
python -m app.cli mcp-token-revoke TOKEN_ID
```
容器中使用相应 Compose 配置的 `exec backend` 执行。签发成功后明文只输出一次,保管在客户端凭据存储中,不放入聊天、仓库或请求 ID。数据库只保存令牌哈希及绑定信息。每次 HTTP 请求重新验证过期、撤销、管理员和平台账户绑定。密码重置同时撤销 PAT;仅撤销 PAT 不会取消已受理的回测。
| 权限 | 可调用能力 |
| --- | --- |
| research:read | 能力、数据目录、元数据、历史、运行、结果、证据、刷新及自相关任务查询、自相关结果读取 |
| research:refresh | 显式更新元数据及 PnL 缓存、发起本地自相关检查、使用已保存凭据重新连接或继续认证;同时要求 read |
| backtests:execute | 直接提交固定候选;同时要求 read |
| backtests:control | 暂停、继续、停止、恢复采集;同时要求 read |
`tools/list` 只显示当前令牌可用的工具,实际调用仍再次验证权限。缺失/失效令牌返回 401,缺权限返回 403。
## 工具输入与返回
工具 schema 由 `tools/list` 提供,未知参数拒绝。所有业务返回为 `structuredContent`,并保留等价文本 JSON。业务失败置 `isError=true`,error 包含 code、message、retryable、retry_after、affected_items;HTTP 认证错误不伪装为正常工具结果。
| 工具 | 输入要点 |
| --- | --- |
| get_worldquant_connection | `{job_id?}`;只读连接状态及认证任务状态,不返回密码、Cookie 或验证链接 |
| authenticate_worldquant | `{action?:"connect"或"verify"}`;默认 connect,使用已保存凭据异步认证,返回 job_id;要求 research:refresh |
| get_research_capabilities | `{}`,含单次候选上限及完整候选 schema |
| search_catalog | `{filters:{region,universe,delay,...},dataset_id?}`;省略 dataset_id 查数据集,提供则查字段 |
| get_research_metadata | `{query:{kind,...}}`;kind 为 scopes/settings/operators/field_availability |
| refresh_research_data | `{query:{kind,...}}`;kind 为 catalog/operators/settings/field_availability/pnl |
| get_submission_check | `{alpha_id}`;读取检查上下文、snapshot 和缓存结果,不发起检查 |
| check_submission | `{alpha_id,snapshot,descriptions}`;写回已确认描述并异步检查,绝不正式提交;要求 research:refresh |
| get_refresh_job | `{job_id,limit?,offset?}`;查询刷新、自相关或平台检查任务,错误列表独立分页 |
| check_self_correlation | `{alpha_ids:[...]}`,1–100 个已导入 Alpha ID;异步返回 job_id |
| get_self_correlation | `{alpha_id}`;只读最新本地结果,含缓存和 stale 状态 |
| search_backtests | 来源、reference、status、带时区起止时间、scope、q、候选精确匹配及分页 |
| submit_backtests | `{name,candidates,idempotency_key,duplicate_policy?,source?}` |
| get_backtest | `{run_id,after?,event_limit?}`,after 为事件游标 |
| get_backtest_results | `{run_id,item_ids?,limit?,offset?}` |
| get_backtest_artifact | `{item_id,kind,limit?,offset?,date_from?,date_to?}`,kind 为 snapshot/pnl |
| control_backtest | `{run_id,action,expected_version,idempotency_key}` |
metadata 的 operators 支持 q/category 和分页;settings 支持分页;field_availability 要求 field_id 和 scope。refresh 的 catalog 要求 scope,可选 dataset_id;pnl 要求 alpha_ids;availability 与读取使用相同范围字段。目录和 PnL 刷新返回 job_id,查询不会隐式刷新;另外三种刷新最多等待 30 秒,成功只返回快照引用,完整内容用读取工具获取。失败不发布半成品。
列表默认 25 项、最多 100 项;返回 total、offset、limit、has_more。快照证据按顶层 key/value 分页,嵌套内容完整保留;PnL 按日期过滤和分页。元数据仅提供字段与算子资料,不提供原始财务时间序列。
## 连接恢复
遇到未连接时,调用 `authenticate_worldquant`,再以返回的 `job_id` 调用 `get_worldquant_connection`。受理不等于已连接;等待任务完成及连接状态为 connected。正在排队或执行的认证任务会复用。人工验证期间 connect 不替换验证会话:用户在返回的系统网页入口完成验证后调用 `{"action":"verify"}`。验证失败或缺少凭据时在网页账户面板处理。该入口不修改凭据,不签发或刷新 MCP PAT;PAT 失效仍需在管理页面处理。已有 research:refresh Key 可用,只读 Key 只能查询。
## 本地自相关检查
目标 Alpha 必须已导入;比较基准为本地已同步的同地区已提交 Alpha,排除自身。建议先在网页全量同步已提交 Alpha。MCP 不隐式导入 Alpha 或同步基准列表。
1. 调用 `check_self_correlation`,例如 `{"alpha_ids":["LL977PqL"]}`,获取 `job_id`。目标去重排序,相同目标集合的活动任务会复用;完成后再次调用会重新计算。
2. 用 `get_refresh_job` 查询进度、失败原因与分页错误。受理后客户端断开不影响后台任务;取消及重试可在网页任务面板执行。
3. 完成后调用 `get_self_correlation`,例如 `{"alpha_id":"LL977PqL"}`。返回 `source=local`,`status` 为 `not_cached`、`available` 或 `stale`;没有结果时 `cached=false`、`result=null`,读取不会隐式启动检查。
检查优先使用已有 PnL 缓存,缺失时由后台自动补取并落库,等待遵循平台 Retry-After。计算使用累计 PnL 的日变化、目标最新数据日前四年的共同窗口、至少 30 个有效样本,取带符号最大的 Pearson 相关系数,阈值为 0.7。结果包含比较数、跳过数、最相关 Alpha、窗口、样本和缓存时间;最多列出前 10 个匹配及前 100 个跳过原因。缺少基准或有效样本会明确报告数据不足,不能当作通过。
`result.stale=true` 表示缓存已待重算;新任务执行期间读取仍可能返回上一次结果,应同时查看任务状态和 `calculated_at`。这是本地研究规则,不调用 WorldQuant 的提交检查,也不代表平台提交资格。`get_research_capabilities.self_correlation` 提供工具名及当前规则。已有包含 `research:refresh` 的 Key 可直接发起检查;只读 Key 只能查看结果。
## 平台检查(不提交)
先调用 `get_submission_check({alpha_id})` 读取缓存表达式、Description 和 snapshot。核对三段描述后,调用 `check_submission({alpha_id, snapshot, descriptions})`;descriptions 为 `regular` 或 `selection`/`combo` 到完整文本的映射。此操作要求 `research:refresh`,仅写回描述并调用平台 `GET /alphas/{id}/check`。复用网页的本地自相关门槛、快照冲突保护和任务去重。不会调用 `/submit`,检查通过也不会正式提交 Alpha。
使用返回的 job_id 调用 `get_refresh_job` 查看执行状态,再通过 `get_submission_check` 读取缓存结果和 checked_at。任务 completed 表示检查执行完毕,不代表所有检查通过;以 checks 中的结果为准。读取工具不自动访问平台。
## 一轮研究示例
先读取能力和设置快照,发现字段并检查历史。用户授权本批执行后,提交以下形态的固定输入;设置仅为结构示例,实际范围需依据平台选项选择:
```json
{
"name": "价格排序基线",
"idempotency_key": "research-round-001",
"duplicate_policy": "reject",
"source": {"reference": "conversation-reference", "hypothesis": "基线对照"},
"candidates": [{
"client_item_id": "baseline",
"expression": "rank(close)",
"alpha_type": "REGULAR",
"settings": {
"instrumentType": "EQUITY", "region": "USA", "universe": "TOP3000", "delay": 1,
"decay": 0, "neutralization": "INDUSTRY", "truncation": 0.08,
"pasteurization": "ON", "unitHandling": "VERIFY", "nanHandling": "OFF",
"language": "FASTEXPR", "visualization": false, "maxTrade": "OFF", "maxPosition": "OFF"
}
}]
}
```
单次 1–100 项,client_item_id 唯一,每项设置完整,不做批次级参数合并。输入结构及已缓存设置组合参与校验;缺设置缓存时返回 validation=unknown,不宣称验证了平台组合、表达式语义或字段可用性。可先显式刷新设置消除这类未知。
提交自动保存固定预览和运行,返回 backtest_run_id、输入摘要、数量和 web_url。打开 web_url 可定位网页详情;来源显示“MCP 研究”,可以按来源筛选。后续 get_backtest 和 get_backtest_results 读取进度与结果。PnL 未缓存时返回 not_cached,通过显式刷新再读取。下一批可设置 source.parent_run_id 关联前一批。
## 幂等、重复与状态
- 成功请求按账户、操作和幂等键固定首次响应。相同请求重试返回原响应,不重新提交;换令牌不改变该范围。同键不同内容返回 IDEMPOTENCY_CONFLICT。失败校验不占用幂等键。
- 幂等判断先于重复查询。规范化摘要包含完整候选、顺序、设置、名称、来源说明和重复策略,不包含链路请求 ID 和令牌。
- 重复按平台完整输入指纹匹配,不判断数学等价。默认 reject 整批拒绝;返回历史记录及其状态,历史失败或跳过也属于输入匹配。明确 rerun 才创建新运行,不自动复用或跳过。匹配记录超页时通过 search_backtests 继续读取。
- 返回 ID 表示已持久化受理,不表示平台完成。执行继续使用原来的限流、退避与重启恢复;客户端断开不取消已受理运行。
- 保留平台、采集和持久化三层状态。completed 不等于检查 PASS。检查包含原始非通过内容及未知状态;指标缺失为 null。
- 历史指标来自当次固定快照,后续 Alpha 同步不改写。PnL 是另行采集的缓存,必须同时阅读 fetched_at,不能视为当次回测同时抓取。
- pause 阻止后续新提交;stop 跳过未提交项,已提交模拟继续采集,不能远程取消。停止后的剩余项须明确新建重跑。
- recover 只根据已有回执恢复采集,不重发未知模拟。无回执的 submission_unknown 仍需在网页核对原模拟。
- resume 保留解除账户级无期限调度阻塞的行为,响应 impact 明确是否解除;不会清除 Retry-After 截止时间。
- 控制使用 expected_version 和独立幂等键;成功重试不重复追加控制事件。统计区分候选、尝试、POST 请求和确认/未知接受数量,实际平台额度消耗保持未知。
审计只记录身份 ID、输入摘要、哈希后的链路请求标识、业务引用、结果码和耗时。关闭 MCP 开关不删除记录,也不停止已受理队列。备份需包含新增表;恢复后旧成功幂等键仍有效。
## 验证和限制
本地测试只使用模拟平台。后端 `uv run pytest -q tests/test_mcp.py` 包含官方 MCP ClientSession 的 HTTP 工具往返;浏览器测试在临时数据库里验证实际 MCP 提交的运行链接和来源。
独立 PostgreSQL 检查只接受本机 `wq_mcp_test` 数据库:
```bash
# backend 目录;变量必须指向专用、可丢弃的测试库。
MCP_TEST_DATABASE_URL=postgresql+asyncpg://USER:PASSWORD@127.0.0.1:PORT/wq_mcp_test \
uv run python -m tests.mcp_postgres
```
该脚本执行迁移、并发提交/控制、重启重放、回退及重升级,不用于个人库或生产库。生产启用、真实平台兼容性、真实额度和客户端实际凭据配置仍需另行授权验证。本功能不会恢复任何定时研究。