feat: add MCP research access and browser key management
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
| Variables(必填) | `DATABASE_NETWORK` | PostgreSQL 所在的现有 Docker 网络,例如 `1panel-network` |
|
||||
| Variables(必填) | `PUBLIC_ORIGIN` | 实际 HTTPS 来源,例如 `https://alpha.your-domain.com`,不带路径或末尾 `/` |
|
||||
| Variables(可选) | `ADMIN_USERNAME` | 初始管理员账号,默认 `admin` |
|
||||
| Variables(可选) | `MCP_ENABLED` | 设置 `true` 启用 MCP;未配置时默认关闭 |
|
||||
|
||||
端口与 AI 限制使用 `compose.production.yaml` 的默认值,不需要在 Gitea 配置:`WEB_PORT=8112`、`AI_REQUEST_LIMIT=12`、`AI_TOOL_LIMIT=12`、`AI_OUTPUT_TOKENS=4096`、`AI_TIMEOUT=180`。需要调整时修改 Compose 中对应默认值;工作流不再读取这些同名 Gitea Variables。
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
# 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_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_refresh_job | `{job_id,limit?,offset?}`;错误列表独立分页 |
|
||||
| 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 按日期过滤和分页。元数据仅提供字段与算子资料,不提供原始财务时间序列。
|
||||
|
||||
## 一轮研究示例
|
||||
|
||||
先读取能力和设置快照,发现字段并检查历史。用户授权本批执行后,提交以下形态的固定输入;设置仅为结构示例,实际范围需依据平台选项选择:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
该脚本执行迁移、并发提交/控制、重启重放、回退及重升级,不用于个人库或生产库。生产启用、真实平台兼容性、真实额度和客户端实际凭据配置仍需另行授权验证。本功能不会恢复任何定时研究。
|
||||
Reference in New Issue
Block a user