状态:Draft v2(2026-05-15)—— 在 v1 基础上经 A 讨论收敛 来源:brainstorm + A 讨论(场景:用户用模糊自然语言描述「250 日线战法」/「美股炒题材→A股板块联动」等,系统协助澄清 → 实现 → 沉淀 → 每日例行筛选) 范围:本设计回答两个问题——
- opencode worker 输出的「战法产物」应该长什么样?
- 这个产物如何离开 worker,进入「每日例行执行」的生产态?
不在范围:vibe-trading 业务实现细节、上游 runtime UI、风控/下单系统。
- v1(2026-05-14):基于 brainstorm 输出,假设产物是自创 DSL bundle + 上游自建 runner,回测层属于上游。
- v2(2026-05-15):A 讨论收敛三大 reframe ——
- 产物形态 = SKILL(沿用 vibe-trading SKILL 既有约定 + 必要扩展),而非自创 DSL
- 生产态执行 = agent-led(任何能 load_skill 的 agent 即可),而非自建 runner
- vibe-trading 是起点不是终点:opencode + Prometheus 与用户多轮对话,从 vibe-trading 的 MVP signal_engine 形态逐步进化为「完整 python 脚本 + 配套 prompts」的 SKILL;prompts 是一等公民,承载事件驱动型语义判断(如「美股炒航天 → A股关注航天板块」)
- v2.1(2026-05-15):B 讨论收敛两条 ——
- 项目特有 MCP 维护在独立仓库
LegoNanoBot/mcps/{mcp-name}/(与 worker、与 vibe-trading 三方解耦) - manifest.json env_lock 强锁 tool + 字段 + 版本:agent 加载 SKILL 时调 MCP schema introspection 比对,不一致即拒绝运行
- 项目特有 MCP 维护在独立仓库
- v2.2(2026-05-15):B 讨论补充 vibe-trading fork 维度 ——
- 存在两个 vibe-trading 通道:社区版(开源,多品类)+ 内部 A 股 fork(在社区版基础上选择性删改)
- SKILL manifest 必须显式声明 channel(
community/internal-a-share),不允许"假定默认 vibe-trading" - 反哺社区的 PR 流程:内部 fork 的通用增强定期 PR 上游;A 股专属能力沉淀在内部 fork 不上推
- v2.3(2026-05-15):D 讨论收敛 ——
- 命中说明 = 每只股票推送给用户的那段解释文字(不是 SKILL.md 的 How to Invoke)
- 生成方式:每只 picks 单独 LLM 并发调用(默认 7 次并发),用 cheap model 控成本
- MVP 由 SKILL 自带 prompts/pick_explanation.md;统一品牌风格退 Phase 7
- v2.4(2026-05-15):C 讨论收敛 HITL 粒度 ——
- 研究态 HITL:用户就在旁边,随时可 HITL 讨论;不预设"关键转折点"清单(Prometheus 默认 plan_first 行为已满足)
- 生产态 = 盘前静默降级:所有异常默认走预设降级路径,不暂停推送
- 降级必须随推送报告同步输出:当日推送的选股结果必须显式标注"是否降级、哪些步骤降级、降级原因",让用户事后一眼看到
- 生产态没有 HITL 阻塞通道;用户事后读降级报告,自行决定是否回研究态修战法
- v2.5(2026-05-15):E 讨论收敛 ——
- MVP Strategy Registry = 最小形态:目录 + INDEX.json 列 active versions;无 tag / search / owner / shared components
- INDEX.json schema 写定:避免后续迁移成本;扩展字段(tags / status / dep graph)退 Phase 7
- v2.6(2026-05-15):F 组(基础能力)OQ-1 / OQ-3 / OQ-9 闭环 ——
- Meta-skill
strategy-skill-author由上游 runtime 维护,通过opencode_profile自动注入给 Prometheus(OQ-1) - Worker 实现 conversations + backtests writeback(Phase 6 增量),监听 SSE 事件按约定写入 SKILL bundle 对应目录(OQ-3)
- MCP 调用字段自动回填 manifest:worker 拦截 MCP 调用聚合实际读写字段,Prometheus 在 signal_engine 中"只读用到的字段"作为双保险(OQ-9)
- Meta-skill
- v2.7(2026-05-15):G 组(命名规范)OQ-4 / OQ-5 / OQ-8 闭环 ——
- strategy_id:kebab-case,正则
^[a-z][a-z0-9-]{2,40}$,用户起名 + LLM 校 INDEX.json 冲突(OQ-4) - 版本号:SemVer;MAJOR=env/契约/数据变;MINOR=规则/prompts/编排变;PATCH=描述/参数微调(OQ-5)
- conversations slug:worker 终态前 LLM 总结 3-4 词;用户可在 final_acceptance 中覆盖(OQ-8)
- strategy_id:kebab-case,正则
- v2.8(2026-05-15):H 组(校验机制)OQ-7 / OQ-12 闭环 ——
- env_lock validator:agent 加载时按 MCP→env→DB 顺序校验,所有失败一次性聚合返回(不 fail-fast);输出 structured error(OQ-7)
- channel mismatch:默认 hard reject;提供
--force-channel-mismatch显式开关,覆盖时入 audit log(OQ-12)
- v2.9(2026-05-15):I 组(MCP 治理)OQ-10 / OQ-11 闭环 ——
- vibe-trading 内部 fork PR 准入:维护者 + 社区 maintainer 共同 review;细则在 fork repo 的 GOVERNANCE.md;通用增强 PR 上游、A 股专属沉淀内部、删除动作不上推(OQ-10)
- 项目特有 MCP 发布:git tag +
pip install git+...@v{tag};docker image 选项留 Phase 7(OQ-11)
- v2.10(2026-05-15):J 组(品质细节)OQ-2 / OQ-13 / OQ-14 / OQ-15 / OQ-16 闭环 ——
- Offline Quantization Equivalent 强制:每个 LLM step 的 prompts/*.md 必须有该章节,顶部声明
quality: exact / approximate / partial(OQ-2) - LLM 成本预算归属上游 runtime:worker 不管,per-tenant 月度预算 + 接近阈值告警 + 超阈值降级模板(OQ-13)
- LLM 输出不合 schema MVP 不二次纠错:直接降级
fallback_template_path;Phase 7 评估 retry-with-correction(OQ-14) - degradation_report 字段英文 / 值中文:i18n 退 Phase 7(OQ-15)
- 彻底跑不了仍推空报告 + 显眼降级标识:用户每日"信号不缺席"原则(OQ-16)
- Offline Quantization Equivalent 强制:每个 LLM step 的 prompts/*.md 必须有该章节,顶部声明
- 1. 设计目标(G1~G9)
- 2. 关键决策(D1~D19)
- 3. SKILL Bundle 规格
- 4. 生命周期:研究态 → 生产态
- 5. Worker 侧改动(最小化 + Phase 6 增量)
- 6. 上游侧需要构建的组件
- 7. 回测复用
- 8. Open Questions(已全部闭环 ✅)
- 9. 分阶段交付建议(Phase X1/X2/X3)
- 10. 与 ADR 的关系
- 11. Implementation Readiness Summary
- 12. Next Step
- 13. ADR Updates 提示
| # | 目标 | 来源 |
|---|---|---|
| G1 | 产物可被任意 skill-aware agent(Claude / opencode / vibe-trading-mcp)加载执行 | A 讨论收敛 |
| G2 | 产物可被审计 / 复现:携带演进对话、回测证据、版本号 | brainstorm AC + 用户要求 |
| G3 | 用户可精准修改(一条规则 / 一段 prompt)→ 新版本,不重写整个 SKILL | brainstorm AC-3 |
| G4 | 不破坏 worker 通用契约(ADR-001) | brainstorm Q2 答案 |
| G5 | 数据访问通过上游注入的 MCP(vibe-trading + 项目特有 MCP) | brainstorm Q4 |
| G6 | 同一份产物可被回测层(vibe-trading.backtest)和生产态(每日选股)共同消费 | brainstorm Open Q3 |
| G7 | 显式声明「环境锁」:缺特定 MCP/DB/凭据时拒绝运行 | A 讨论新增 |
| G8 | 生产态执行结构化少量 LLM 调用(事件驱动语义部分),非纯确定性也非 free-form opencode | A 讨论新增 |
| G9 | MCP 升级不能静默破坏 SKILL:SKILL manifest 强锁 tool/字段/版本,agent 加载时校验 | B 讨论新增 |
| 决策 | 选择 | 替代 | 理由 |
|---|---|---|---|
| D1. 产物形态 | SKILL(vibe-trading SKILL 约定 + 扩展目录) | 自创 DSL bundle / 纯 vibe-trading run_dir | 复用既有标准;agent 原生可消费;自然支持版本与依赖声明 |
| D2. 执行核心 | vibe-trading class SignalEngine 接口 + 可调 LLMProvider |
纯函数 selector | 与 vibe-trading 兼容;同时支持事件驱动型 LLM 中介逻辑 |
| D3. 宿主目录 | LegoNanoBot/strategies/{strategy-id}/{version}/ 独立目录(不污染开源 vibe-trading) |
进 vibe-trading skills/ / 双轨 | 治理边界清;通过 strategy registry 让 agent 发现 |
| D4. 生产态执行 | agent-led:任何能 load_skill 的 agent 直接消费 SKILL |
自建 runner / 复用 worker 加新 mode | SKILL.md 本质是 agent 的 prompt,无需额外编排层 |
| D5. Worker 契约 | 零变更;artifact 元数据加 subtype: strategy_skill |
加 task mode / artifact type enum | 守住 G4;ADR-001 不动 |
| D6. 版本不可变 | {strategy-id}/{version}/ 写完即冻结;改动 = 新版本目录 |
就地编辑 + git diff | 复现性 + 审计需求 |
| D7. vibe-trading 关系 | vibe-trading run_dir 是 baseline;SKILL bundle 是其 superset(含 prompts/conversations/backtests) | 完全独立 / 完全替代 | 用户实际工作流验证:从 vibe-trading MVP 迭代演化 |
| D8. Code↔Prompts 编排 | Agent 主导:SKILL.md 用自然语言指引 agent 何时调 code、何时调 prompt | code 内嵌 LLM / orchestration.yaml DSL | SKILL.md 本身就是 prompt,不需要额外编排 schema |
| D9. 项目特有 MCP 归属 | 独立仓库 LegoNanoBot/mcps/{mcp-name}/(与 worker、vibe-trading 三方解耦) |
进 vibe-trading / worker monorepo / 监仓 | 边界清;版本治理独立;不被开源节奏拖累 |
| D10. SKILL ↔ MCP 字段锁定 | 强锁:manifest 列出每个 tool 的 input/output 必备字段 + min_version;agent 加载时通过 MCP schema introspection 校验 | 软锁(运行时报错) / 不锁(黑盒) | G9;可承担「opencode 生成时如实记录」的代价换取生产态零静默故障 |
| D11. vibe-trading 双通道 | SKILL manifest 用 channel: community | internal-a-share 显式声明依赖通道;内部 A 股 fork 单独仓库维护,定期反哺社区 |
默认社区 / 默认内部 / 不区分 | A 股深耕需要内部 fork 选择性删改,但不能让 SKILL 因此与社区脱节;显式 channel 让 portability 风险可见 |
| D12. 命中说明生成 | 每只 picks 单独 LLM 并发调用(默认 7 路并发,用 cheap model);MVP SKILL 自带 prompts/pick_explanation.md | 模板 / 批量单调用 / 分层 | 用户已采纳成本换语义自由:事件驱动战法(如「美股炒航天 → A 股板块联动」)不带 LLM 说明就失去核心价值 |
| D13. 研究态 HITL 粒度 | 用户就在旁边:worker 现有 plan_first / HITL 机制照常工作;不预设"关键转折点"清单;用户随时可介入 | 预设触发列表 / 全自动一次性终审 | 用户在场场景下,预设触发反而僵化;信任 Prometheus 默认对话节奏 |
| D14. 生产态异常处理 | 盘前静默降级:所有异常走预设降级路径,不暂停推送 | 暂停 + HITL / 分级 / 仅日志 | 用户盘前不在线,阻塞反而失去推送价值;降级 + 透明报告兼顾自动化与可见性 |
| D15. 降级报告随推送输出 | 当日选股结果必须显式包含 degradation report:是否降级 / 哪些步骤降级 / 原因 / 影响范围 | 单独通知 / 仅入 audit log | 用户读推送时一眼看到状态;事后可基于报告自行决定是否回研究态修战法 |
| D16. MVP Strategy Registry | 最小形态:目录 + INDEX.json (latest_active_version + all_versions + channel);无 tag/search/owner | 加 tag/status / 加 dep graph / 不做 registry | 当前 1-3 个战法,过度设计无意义;schema 写定避免后续迁移;扩展退 Phase 7 |
| D17. env_lock validator 行为 | Agent 加载 SKILL 时按 MCP→env→DB 顺序校验,聚合所有失败一次性返回;channel mismatch 默认 hard reject,可 --force-channel-mismatch 覆盖(入 audit log) |
fail-fast / 警告不阻塞 / 仅日志 | 一次性看清缺什么远好于反复重试;hard reject 防误用,force flag 留给紧急场景 |
| D18. 项目特有 MCP 发布形态 | git tag + pip install git+https://.../mcps/{name}.git@v{tag};opencode_profile 引用具体 tag |
docker image / pypi 私服 / monorepo | MVP 最简;docker image 等多用户 / 隔离场景再加 |
| D19. Offline Quantization Equivalent 强制 | 每个 LLM step 的 prompts/*.md 必须含该章节 + 顶部 quality: exact / approximate / partial 声明;approximate 的回测结果在报告显式标注 |
不强制 / 强制 + 单一 quality | 让回测/实盘语义脱节风险显式可见;quality 等级让用户判断回测可信度 |
LegoNanoBot/strategies/
└── {strategy-id}/ # e.g. ma250-pullback
├── REGISTRY.md # (可选)策略级元信息:用途、负责人、维护状态
└── {version}/ # e.g. 0.3.1 — 不可变
│
│ ─── SKILL 标准部分(可被 vibe-trading SKILL 加载器识别) ───
├── SKILL.md ★ frontmatter + agent-readable 指引
├── example_signal_engine.py # vibe-trading 习惯入口(可指向 code/)
├── references/ # 参考资料(学术论文链接、术语表等)
│
│ ─── 实际可执行部分 ───
├── code/
│ ├── signal_engine.py # vibe-trading SignalEngine 接口
│ └── helpers/ # 可选:拆分模块
├── config.json # vibe-trading run_dir 兼容
├── prompts/ # ★ runtime LLM 调用的 prompt 模板
│ ├── event_sector_filter.md
│ ├── candidate_ranking.md
│ └── pick_explanation.md
│
│ ─── 演进证据(worker 写入) ───
├── conversations/ # 形成本版本的对话历史
│ ├── 2026-05-14-initial.jsonl
│ └── 2026-05-15-tune-pullback.jsonl
├── backtests/ # 回测验证记录
│ └── 2026-05-15-baseline/ # 复制自 vibe-trading runs/
│ ├── config.json
│ ├── metrics.json
│ └── ...
│
│ ─── 元数据 ───
├── manifest.json ★ 机器可读:环境锁 + 依赖
├── CHANGELOG.md # 版本演进说明
└── checksums.txt # 文件 sha256
不可变性约束:每个 {version}/ 目录写完即冻结。修改 = 新版本(0.3.1 → 0.3.2),不就地改。
沿用 vibe-trading 现有 frontmatter 范式(参考 vibe-trading/agent/src/skills/harmonic/SKILL.md):
---
name: ma250-pullback
description: 250 日均线回踩战法:股价站稳 250 日线后回踩确认入场,每日筛 ≤7 只
category: strategy
version: 0.3.1
---
# 250 日均线回踩战法
## Purpose
此 SKILL 实现「250 日均线回踩」战法,并支持事件驱动的板块联动叠加。
适用于 A 股每日例行选股场景,输出 ≤7 只候选 + 命中说明。
## When to Use
- 用户已通过 conversations/ 中的对话明确同意此战法逻辑
- 当前交易日为 A 股开市日
- 必备 MCP 与凭据已就绪(见 manifest.json)
## How to Invoke
### 每日选股流程
1. **读取候选域**:通过 `vibe-trading.get_market_data` 拉取全 A 当日基础数据
2. **板块过滤(LLM 介入)**:
- 加载 `prompts/event_sector_filter.md`
- 用今日新闻(通过 `historical-news-cn` MCP 查 T-1 日 close → 当日 open 区间)作为输入
- LLM 输出当日关注板块列表
3. **确定性筛选**:调用 `code/signal_engine.py::SignalEngine.generate(data_map)`,结合板块过滤
4. **排序 + 截断**:按 `(close - ma250)/ma250` 升序,取前 7
5. **生成说明(LLM 介入)**:每只用 `prompts/pick_explanation.md` 生成命中说明
### 回测流程
直接调用 `vibe-trading.backtest(<this-skill-dir>)`,框架自动读取 `config.json` 与 `code/signal_engine.py`。
> ⚠️ 回测路径目前**不**复现板块过滤步骤的 LLM 行为;如需带语义层回测,见 prompts/event_sector_filter.md 末尾的「离线量化等价物」章节。
## Inputs / Outputs
| 输入 | 来源 | 描述 |
|---|---|---|
| `today` | scheduler | 交易日 (YYYY-MM-DD) |
| `news_window` | `historical-news-cn` MCP | T-1 close → T open 新闻 |
| `bars` | `vibe-trading.get_market_data` | 全 A 日 K 含 250 日历史 |
| 输出 | 形态 | 描述 |
|---|---|---|
| picks | `list[Pick(symbol, score, fields, reason_text)]` | ≤7 只 |
## Dependencies
见 `manifest.json` 的 `required_mcps` / `required_env` / `required_db`。
## Provenance
- 形成对话:`conversations/`
- 回测验证:`backtests/`
- 历史变更:`CHANGELOG.md`与 vibe-trading SignalEngine 兼容:
class SignalEngine:
"""
与 vibe-trading 既有 SignalEngine 接口一致:
https://github.com/.../vibe-trading/agent/backtest/runner.py
"""
def generate(self, data_map: dict[str, pd.DataFrame]) -> dict[str, pd.Series]:
"""回测路径:返回 per-symbol 信号时间序列。
signal: 1=long, -1=short, 0=stand aside(vibe-trading 约定)。"""
...生产态额外接口(非 vibe-trading 强制,但 SKILL.md 会 surface 给 agent):
def screen_today(
self,
data_map: dict[str, pd.DataFrame],
today: date,
sector_filter: list[str] | None = None, # 来自 LLM 步骤
limit: int = 7,
) -> list[Pick]:
"""生产态:当日 cross-sectional 筛选 + top-N,返回 picks 列表。"""
...关于回测 vs 生产的 mismatch(A 阶段发现的关键 gap):vibe-trading 的
generate(data_map) -> signals是 per-symbol time-series 模式,与「每日 top-N 选股」语义不直接对应。本设计采纳的方案是双入口:generate()用于回测兼容,screen_today()用于生产。SKILL.md 明确指引 agent 何时调用哪个。
每个 prompt 文件结构:
---
prompt_id: event_sector_filter
input_schema:
news_summary: string # T-1→T 区间新闻摘要
reference_markets: [string] # 例如 ["US", "HK"]
output_schema:
focus_sectors: [string]
rationale: string
model_default: deepseek/deepseek-v3.2
---
# Event-driven Sector Filter
## Task
...
## Input Format
...
## Output Format (strict JSON)
```json
{"focus_sectors": [...], "rationale": "..."}...
quality: approximate
回测时无法实时调 LLM。等价的离线信号:取 T-1 日美股 sector ETF 涨幅 >2% 且 中文新闻里同板块词频 >N 的板块。详见
code/helpers/offline_news_proxy.py。与 LLM 输出的差异:离线版只用结构化数据,可能漏掉新闻中的语义信号 (如"政策利好""龙头公司动态"),导致回测高估或低估若干板块。
`Offline Quantization Equivalent` 章节**强制必备**(D19 / OQ-2 闭环):
- **顶部声明 quality**:
- `exact` —— 离线信号与 LLM 输出在数学上一致(如纯模板化的过滤)
- `approximate` —— 离线信号近似 LLM 行为,可能有偏差
- `partial` —— 离线信号只覆盖部分场景,其他场景默认放过或保守处理
- **必须列出与 LLM 输出的差异**:让用户判断回测可信度
- **`approximate` / `partial` 的回测结果必须在报告中显式标注**,避免误导
- meta-skill `strategy-skill-author` 的 checklist 必须勾选"已写 Offline Equivalent"
### 3.5.1 Pick Explanation Prompt 的特殊形态(D12)
`prompts/pick_explanation.md` 是 SKILL 中**几乎必备**的 prompt,规范如下:
```yaml
prompt_id: pick_explanation
invocation_mode: per_pick_concurrent # 与 manifest.llm_steps 对齐
input_schema:
pick: # 单只股票上下文
symbol: string
name: string
score: float
fields: object # 命中规则用到的指标值
matched_rules: [string] # 命中的 rule_id 列表
context: # 共享上下文
today: date
sector_filter: [string] # 当日 LLM 输出的关注板块
news_window: object # T-1→T 关键新闻摘要
output_schema:
reason_text: string # 推送给用户的解释文字
confidence: enum[high, medium, low] # LLM 自评,可用于过滤
fallback_template: # LLM 失败/超时降级
path: ../reason_template.md
降级机制:若 LLM 调用失败 / 超时 / 输出不合 schema,agent 必须落回 fallback_template_path(manifest.json 字段)。降级行为 + 比例需写入 audit log。
MVP 不做二次纠错(OQ-14 闭环):
- LLM 输出不合 schema 时直接降级到
fallback_template_path,不做"corrective retry" - 理由:节省成本 + 复杂度;模板降级足够安全
- Phase 7 评估:n=1 retry with corrective prompt → 仍失败再降级
| 实体 | 规范 | 例子 |
|---|---|---|
| strategy_id | kebab-case,^[a-z][a-z0-9-]{2,40}$;用户起名;LLM 在生成时主动查 INDEX.json 撞名 |
ma250-pullback、us-aerospace-event、hk-southbound-flow |
| version | SemVer MAJOR.MINOR.PATCH |
0.3.1 |
| MAJOR bump | env_lock / SignalEngine 接口 / data_contract 任一变化 | 数据源切换、required MCP 增减、字段语义变化 |
| MINOR bump | 规则 / prompts / orchestration 变化 | 新增规则、调 ranking 公式、prompt 重写 |
| PATCH bump | 描述措辞 / 模板微调 / 参数小调 | ma 窗口 250→248、reason_template 文案 |
| version 目录 | {strategy-id}/{version}/;写完即冻结 |
ma250-pullback/0.3.1/ |
| conversations slug | worker 在任务终态前用一次轻量 LLM 调用总结 3-4 词;用户可在 final_acceptance 时覆盖 | 2026-05-15-tune-pullback、2026-05-15-add-volume-rule |
| backtests label | 默认 iter-N(N 自增),可由 prompt 上下文覆盖(如 baseline / with-event-overlay) |
2026-05-15-baseline/ |
| 目录 | 写入时机 | 写入逻辑 |
|---|---|---|
conversations/{ISO8601}-{slug}.jsonl |
Worker 任务终态时 | Worker 提取 opencode session messages → JSONL;slug 由 LLM 总结用户首条 message 得到(3-4 词,去标点连字符) |
backtests/{ISO8601}-{label}/ |
opencode 调用 vibe-trading.backtest(...) MCP 后 |
Worker 拦截 MCP 结果,复制 vibe-trading runs/ 输出目录;label 默认 iter-N(N 自增),可由 prompt 上下文覆盖 |
这两件worker-side 机制会在 Phase 6/7 实现。现阶段标注为 OQ。
sequenceDiagram
autonumber
participant U as 用户
participant UR as 上游 Runtime
participant W as Worker (本仓库)
participant OC as opencode (Prometheus/Sisyphus)
participant VT as vibe-trading MCP
participant DB as 项目特有 MCP/DB
participant SR as Strategy Registry (LegoNanoBot/strategies/)
participant SCH as Scheduler (上游)
participant AG as Agent (Claude/opencode/...)
rect rgb(240, 248, 255)
Note over U,DB: 研究态:澄清 + 实现 + 验证(opencode 主导)
U->>UR: 「250 日线战法 + 美股板块联动」
UR->>W: POST /tasks (mode=plan_first, 注入 vibe-trading + DB MCP 配置)
W->>OC: 启动 sandbox + opencode + Prometheus
loop 多轮 HITL
OC->>UR: hitl_required (规则澄清 / prompt 试写)
UR->>U: 展示
U->>UR: 反馈
UR->>W: POST /decisions
end
OC->>OC: 起草 SKILL.md + signal_engine.py + prompts/
OC->>VT: 调 vibe-trading.backtest(临时 run_dir)
VT-->>OC: 回测结果
Note right of W: Worker 拦截 → 复制到 SKILL backtests/
OC->>DB: Sisyphus 试跑当日筛选
DB-->>OC: 数据
OC->>OC: 渲染 SKILL.md「How to Invoke」、修订 prompts
W->>UR: artifact_ready (SKILL bundle)
end
rect rgb(245, 245, 245)
Note over UR,SR: 沉淀
UR->>W: GET /tasks/:id/artifacts/:id
W-->>UR: SKILL bundle (含 conversations/ + backtests/)
UR->>SR: 写入 strategies/{strategy-id}/{version}/
end
rect rgb(255, 248, 240)
Note over SCH,U: 生产态:每日例行(盘前静默降级 + 报告透明)
SCH->>AG: 触发 (load_skill: ma250-pullback@0.3.1, today=YYYY-MM-DD)
AG->>SR: 加载 SKILL
AG->>AG: 校验 manifest.json env_lock + 加载 degradation_policy
AG->>AG: 读 SKILL.md「How to Invoke」
AG->>VT: get_market_data
AG->>DB: historical-news-cn
Note right of AG: 任一步骤失败/超时/字段缺失<br/>→ 按 degradation_policy 降级<br/>→ 累积到 degradation_report
AG->>AG: 调 prompts/event_sector_filter (LLM, 失败→skip_step)
AG->>AG: 调 code/signal_engine.screen_today(...)
AG->>AG: 调 prompts/pick_explanation (LLM, 7 路并发, 失败→template)
AG->>UR: picks + degradation_report (同一响应)
UR->>U: 推送当日选股 + 降级说明
Note over U: 用户读推送时一眼看到<br/>是否降级 / 哪里降级 / 原因<br/>事后自行决定是否回研究态
end
rect rgb(248, 240, 255)
Note over U,SR: 迭代(D 阶段,回研究态)
U->>UR: 「板块过滤太激进」
UR->>W: POST /tasks (workspace=tarball of strategies/ma250-pullback/0.3.1/)
W->>OC: opencode patch SKILL → 0.3.2
end
无。 现有 Artifact schema 已支持 type=custom。
{
"type": "custom",
"subtype": "strategy_skill", // 新增标签
"name": "ma250-pullback-0.3.1.tar.gz",
"metadata": {
"strategy_id": "ma250-pullback",
"version": "0.3.1",
"skill_md_path": "SKILL.md",
"manifest_path": "manifest.json"
}
}属于 Phase 6 收尾或 Phase 7 增强;三个 hook 共享同一套 SSE 拦截基础设施:
- Conversations writer:监听 opencode SSE 的
assistant_delta/tool_call_*/decision_received事件,组装 messages 数组,任务终态时输出 JSONL 到conversations/{ISO8601}-{slug}.jsonl - Backtest interceptor:监听
tool_call_finished事件,识别vibe-trading.backtest调用,复制 vibe-trading runs/ 输出目录到 SKILL bundle 的backtests/{ISO8601}-{label}/ - MCP field auto-recorder(OQ-9):监听所有
tool_call_finished事件,按(mcp_name, tool_name)维度聚合:- 实际传入的 input field 名(从调用 args 提取)
- 实际读取的 output field 名(需 Prometheus 在生成的 signal_engine.py 中遵循"只读用到的字段"约定 — 双保险)
- 任务终态时回填到
manifest.json的env_lock.required_mcps[].tools_used[].required_*_fields
执行本设计前必须关:
- P0-5:agent 名修回 Prometheus/Sisyphus
- P0-6 / P0-7:终态事件正确性
- P1-13 / P1-14:HITL
on_timeout/auto_approve字段实装
归属:上游 runtime 维护的一个 meta-skill,名为 strategy-skill-author。
注入:通过 worker 的 opencode_profile 字段自动注入给 Prometheus,与其他业务 skills 同等待遇。
为什么不在 worker:worker 不感知业务(守 ADR-001);meta-skill 是"如何写 strategy SKILL"的业务知识,与 vibe-trading skills 体系同源。
meta-skill 内容(草稿):
- SKILL.md 必备小节(Purpose / When to Use / How to Invoke / Inputs/Outputs / Dependencies / Provenance)
- frontmatter 字段约定(name / description / category / version)
- prompts/ 命名约定与必备 schema(input_schema / output_schema / fallback)
- manifest.json env_lock 字段映射规则(含 D11 channel 显式声明)
- references/ 推荐内容(学术引用、术语表)
- 自检 checklist(Prometheus 生成 SKILL 后自查每条勾选)
演进路径:发现 SKILL 质量问题 → 改 meta-skill → 下次 Prometheus 自动跟进,无需改 worker。
LegoNanoBot/strategies/
├── INDEX.json
└── {strategy-id}/
├── REGISTRY.md # (可选)策略级元信息
└── {version}/ # 不可变
INDEX.json schema(写定,避免迁移成本):
{
"schema_version": "1.0",
"updated_at": "2026-05-15T10:30:00+08:00",
"strategies": {
"ma250-pullback": {
"latest_active_version": "0.3.1", // scheduler 默认加载这个
"all_versions": [
{
"version": "0.3.1",
"created_at": "2026-05-15T10:30:00+08:00",
"channel": "internal-a-share", // 与 manifest 一致
"is_active": true // 仅一条为 true
},
{
"version": "0.3.0",
"created_at": "2026-05-14T15:00:00+08:00",
"channel": "internal-a-share",
"is_active": false
}
]
}
}
}MVP 不包含(退 Phase 7):
- tags / categories / search index
- owner / RBAC / sharing
- dependency graph / shared components reference
- usage telemetry / last_run_date
- API(仅文件系统读写)
未来可演进为带 API 的轻服务(list / load / pin / deprecate / search)。
任何 skill-aware agent 都可作为 loader:
- Claude Code:通过
claude-code的 skill 加载机制 - opencode:通过
opencode + oh-my-opencode的 skill loader - vibe-trading-mcp:
load_skill(name)MCP 工具
Agent 加载 SKILL 时,必须按以下顺序执行校验。所有失败一次性聚合返回(不 fail-fast),让用户一眼看清环境缺什么。
1. Channel check
manifest.env_lock.required_mcps[].channel vs 当前环境实际通道
↓ 不匹配 → 收集到 mismatch[]
(除非启动时带 --force-channel-mismatch,此时 warn + audit log)
2. MCP existence + version check
for each m in required_mcps:
a. 检查 m.name 在当前 agent runtime 注册的 MCP 列表中
b. 检查 m.version >= m.min_version
↓ 失败 → 收集到 missing_mcps[] / version_too_old[]
3. MCP schema introspection (强锁的核心,OQ-9 闭环依赖)
for each m, for each tool in m.tools_used:
a. 调用 m.describe_tool(tool.tool_name)
b. 比对 tool.required_input_fields ⊆ 实际 input schema 字段
c. 比对 tool.required_output_fields ⊆ 实际 output schema 字段
↓ 失败 → 收集到 schema_diffs[]
4. Required env check
for each e in required_env:
检查 os.environ.get(e.name) is not None
↓ 失败 → 收集到 missing_env[]
5. Required DB check
for each db in required_db:
试连接 (timeout=5s)
↓ 失败 → 收集到 unreachable_db[]
Structured error 返回格式(聚合所有 stage):
{
"kind": "env_lock_validation_failed",
"skill_id": "ma250-pullback@0.3.1",
"stages_failed": ["mcp_schema", "required_env"],
"details": {
"channel_mismatch": [],
"missing_mcps": [],
"version_too_old": [],
"schema_diffs": [
{
"mcp": "trading-data-cn",
"tool": "get_security_meta",
"missing_output_fields": ["board"],
"actual_output_fields": ["symbol", "name", "industry", "is_st", "listed_date"]
}
],
"missing_env": ["DEEPSEEK_API_KEY"],
"unreachable_db": []
},
"remedy_hint": "请安装 trading-data-cn >= 1.1(含 board 字段)并配置 DEEPSEEK_API_KEY"
}remedy_hint 由 agent 根据 details 自动生成(模板化),方便用户快速定位。
不强制选型。最简单 cron + 脚本(脚本启动 agent 加载 skill)。
唯一约束:调度时机在 data_contract.asof_time 之后。
- 上游从 Registry 拉出最新 SKILL(整个
{version}/目录) - 作为
workspace.kind=tarball注入新 task messages中带「我想把 X 改成 Y」- opencode 在已有 SKILL 基础上 patch,新版本号自增
- Worker 写入新
{version}/,老版本不改
┌──────────────────────────────────────────────────────────────┐
│ 社区 vibe-trading (开源, 多品类) │
│ github.com/.../vibe-trading │
│ tools: backtest, factor_analysis, get_market_data, ... │
└──────────────────────────────────────────────────────────────┘
↑ 反哺通用增强 (定期 PR)
│
│ fork + 选择性删改 (A 股聚焦)
↓
┌──────────────────────────────────────────────────────────────┐
│ 内部 vibe-trading fork (LegoNanoBot/vibe-trading-a-share) │
│ - 删除:crypto / forex / okx loader 等非 A 股能力 │
│ - 增强:A 股财务字段 / 行业树 / 龙虎榜 等 │
│ - SKILL manifest 用 channel="internal-a-share" 引用 │
└──────────────────────────────────────────────────────────────┘
↑ 同环境内并存
│
┌──────────────────────────────────────────────────────────────┐
│ 项目特有 MCP (LegoNanoBot/mcps/) │
│ ├── trading-data-cn/ (A 股元数据 / 行业 / universe) │
│ ├── historical-news-cn/ (历史新闻、公告、事件) │
│ └── ... (按需新增) │
│ - 各自独立仓库 + stdio server + JSON Schema 自描述 │
│ - 独立 semver 发布 │
└──────────────────────────────────────────────────────────────┘
MCP 发布形态(D18 / OQ-11 闭环):
- 每个 MCP 独立 git 仓库 + git tag 标记 release
- 安装方式:
pip install git+https://github.com/LegoNanoBot/mcps-{name}.git@v{tag} opencode_profile.mcp引用具体 git tag(不允许@latest或@main,避免漂移)- Docker image 选项 Phase 7(多用户 / 隔离场景再加)
- 每个 MCP 仓库必须含:
README.md—— 工具列表 + 接入示例pyproject.toml—— 标记 entry_point(让pip install后能直接xxx-mcp --transport stdio)schemas/—— 每个 tool 的 JSON Schema(input + output),用于 D17 校验CHANGELOG.md—— SemVer 变更记录
为支持 D10 的强锁校验,所有被 SKILL 引用的 MCP 必须导出:
list_tools()—— 返回所有 tool 名 + 版本describe_tool(name)—— 返回该 tool 的 input/output JSON Schema- 顶层
version字段(与 git tag / pypi 版本一致)
Agent 加载 SKILL 时调用 describe_tool 比对 manifest 的 tools_used.required_*_fields,缺失/重命名/类型不兼容即拒绝运行并列出 diff。
变更分类(写入内部 fork CHANGELOG 必备字段):
| 类型 | 含义 | 是否上推社区 |
|---|---|---|
merge |
同步社区 master 的变更 | N/A(已在社区) |
enhance-upstream |
内部新增的通用增强(非 A 股专属) | ✅ 落地后 N 周内 PR 上游 |
internal-only |
A 股专属能力(龙虎榜 / 北向资金 / ST 治理) | ❌ 不上推 |
revert |
选择性删除社区能力(crypto / forex 等) | ❌ 不上推(社区不该缩减能力) |
PR 准入流程(OQ-10 闭环):
- 内部维护者发起 enhance-upstream PR
- 内部 review(合规 + 通用性)
- 提交社区 PR
- 社区 maintainer review
- Merge 后回写内部 fork 的 CHANGELOG(标记"已上推")
详细规则在内部 fork repo 的 GOVERNANCE.md(与本设计同步演进)。
Rebase 节奏(非强制建议):
- 内部 fork 每 4 周 rebase 社区 master 一次
- 重大社区 release(含安全补丁)即时跟进
- Rebase 冲突优先保留 internal-only 修改
SKILL 的可移植性约定(OQ-12 闭环 / D17):
- SKILL manifest 的
channel字段必须显式声明 - 默认在
internal-a-sharechannel 跑 - 若希望该 SKILL 可在社区版跑:opencode 生成时不能引用 internal-only 的 tool/字段;manifest 的
channel改为community - Agent 加载时若环境通道与 SKILL 声明不一致:
- 默认 hard reject(structured error)
- 启动时带
--force-channel-mismatch显式开关时允许,warn + 写入 audit log - audit log 至少包含:skill_id、source_channel、target_channel、forced_by、forced_at
vibe-trading 已是回测能力主体:
- 研究态:opencode 直接调
vibe-trading.backtest(skill_dir) - 历史回测:调度脚本批量调用
vibe-trading.backtest,遍历日期区间 - Mismatch 处理:prompts 中带
Offline Quantization Equivalent章节,让 LLM 步骤在回测路径中有可复现实现(详见 §3.5)
回测引擎选型不在本设计范围。
16 条 OQ 在 v2.5 ~ v2.10 中均已落地。下表保留作为决议追溯;如有反复需要重新评估,按列表 OQ-N 追溯。
| # | 问题 | 影响 | 建议 |
|---|---|---|---|
✅ 已闭环(v2.6):上游 runtime 维护 meta-skill strategy-skill-author,通过 opencode_profile 自动注入 |
|||
✅ 已闭环(v2.10 / D19):强制 + quality: exact / approximate / partial 顶部声明 + 必须列差异(详见 §3.5) |
|||
| ✅ 已闭环(v2.6):worker 做,Phase 6 增量;复用 SSE 拦截基础设施 | |||
| ✅ 已闭环(v2.7):kebab-case + 正则 + LLM 校撞名(详见 §3.6.0) | |||
| ✅ 已闭环(v2.7):SemVer 三段语义(详见 §3.6.0) | |||
| ✅ 已闭环(D16 v2.5):MVP 不做,沿用 worker MVP 单租户假设;扩展退 Phase 7 | |||
| ✅ 已闭环(v2.8 / D17):聚合所有失败一次性返回 + structured error + remedy_hint(详见 §6.2.1) | |||
| ✅ 已闭环(v2.7):LLM 默认 + 用户在 final_acceptance 中可覆盖(详见 §3.6.0) | |||
| ✅ 已闭环(v2.6):worker 拦截 + Prometheus 双保险;与 OQ-3 共享拦截基础设施 | |||
| ✅ 已闭环(v2.9):4 类变更分类 + PR 准入流程 + 4 周 rebase 节奏(详见 §6.7) | |||
| ✅ 已闭环(v2.9 / D18):git tag + pip install from VCS + 必备仓库结构(详见 §6.5) | |||
✅ 已闭环(v2.8 / D17):默认 hard reject + --force-channel-mismatch 显式开关 + audit log |
|||
| ✅ 已闭环(v2.10):上游 runtime 维护 per-tenant 预算(详见 §3.3 manifest 注释) | |||
| ✅ 已闭环(v2.10):MVP 不做,直接降级模板;Phase 7 评估 | |||
| ✅ 已闭环(v2.10):字段英文 + 值中文 + i18n 退 Phase 7 | |||
| ✅ 已闭环(v2.10):推送空 picks + 显眼标识(详见 §3.3 manifest abort_with_report_behavior) |
- 锁定本设计 §3 SKILL bundle 结构
- 上游做最小 strategy registry(文件目录 + INDEX.json)
- 上游做 strategy meta-template(OQ-1)
- Worker 修 P0-5/6/7 + P1-13/14(§5.4)
- 端到端跑通:opencode 出一份 SKILL → registry → Claude Code 手动 load → 跑出今日 picks
- DoD:一份 SKILL 在没有 worker 的环境下,被任意 skill-aware agent 加载即可跑
- Worker 实装 conversations writer + backtest interceptor(§5.3)
- 接 cron-style scheduler
- 用户迭代闭环(改一条 → 新版本)
- DoD:连续 5 个交易日自动跑、能基于反馈迭代
- Registry 加 API + 索引服务
- env_lock 校验严格化(OQ-7)
- 同时承载 ≥10 个 SKILL
- 跨 SKILL 复用 prompt / helper(如果出现)
本设计不需要新 ADR:
- 不改 worker 契约(D5)
- 不改 worker 边界(守 ADR-001)
- 新增的「strategy SKILL meta-template」是上游业务能力
但以下情况需要补 ADR:
- 若 OQ-3 决定 worker 不做 conversations/backtests writer,需新 ADR 说明 artifact 收集职责边界
- 若未来 SKILL 加载需要 worker 暴露新接口(如「在 worker 沙盒内代跑 SKILL 用于二次验证」),需新 ADR
全部 5 题 + 16 条 OQ 已闭环(v2.5 ~ v2.10)。本节给出"现在可以动工"的清单。
所有决策见 §2 表格。按归属分类:
Worker side(本仓库)— 6 条
- D5(契约零变更)/ D6(版本不可变)/ D17(env_lock validator)—— 已对齐
- F 组 OQ-3 / OQ-9 → §5.3 三个 SSE-based hooks(Phase 6 增量)
- §5.4 前置 review 修复:P0-5 / P0-6 / P0-7 / P1-13 / P1-14
上游 Runtime side — 5 条
- D3(宿主目录
LegoNanoBot/strategies/) - D4(agent-led 执行)
- D14 / D15(盘前静默降级 + 透明报告)
- D16(最小 Strategy Registry + INDEX.json schema 写定)
- F 组 OQ-1 → meta-skill
strategy-skill-author - OQ-13 → per-tenant 月度成本预算
MCP side(独立仓库 LegoNanoBot/mcps/)— 4 条
- D9(独立仓库归属)
- D10(强字段锁)
- D18(git tag + pip install + schemas/ 自描述)
- D11(vibe-trading 双通道,A 股 fork 单独维护)
SKILL bundle 自身约束 — 4 条
- D1(SKILL 形态)/ D2(SignalEngine + LLMProvider)
- D7(vibe-trading 是起点不终点)
- D8(SKILL.md 是 agent prompt)
- D12(命中说明每只 LLM)/ D19(Offline Equivalent 强制)
- G 组(命名规范,§3.6.0)
| 前置 | 归属 | 状态 |
|---|---|---|
| Worker P0-5(agent 名修回 Prometheus/Sisyphus) | 本仓库 | ✅ 闭环(commit e2e6716 + e35d858,2026-05-16) |
Worker P0-6(task_timed_out 事件类型) |
本仓库 | ✅ 闭环(commit c2a74a7,2026-05-18) |
| Worker P0-7(HITL abort 写正确终态) | 本仓库 | ✅ 闭环(commit c2a74a7) |
| Worker P1-13(HITL on_timeout 全分支) | 本仓库 | ✅ 闭环(commit c3708a2,2026-05-16) |
| Worker P1-14(HITL auto_approve 字段实装) | 本仓库 | ✅ 闭环(commit 9fbb2bf,2026-05-18) |
| W2-1 EventInterceptor 基类 + W2-2 ConversationsWriter | 本仓库 | ✅ 闭环(commit 22334db + 57745c5,2026-05-20;详见 archive/w1-w2-progress-2026-05-20.md) |
| W2-3 BacktestInterceptor + W2-4 McpFieldRecorder | 本仓库 | ✅ 闭环(2026-05-21;详见 roadmap §9.A) |
| 内部 vibe-trading fork repo 起骨架(含 GOVERNANCE.md) | 上游团队 | ⬜ 未启动 |
MCP 仓库骨架(trading-data-cn / historical-news-cn) |
MCP 团队 | ⬜ 未启动 |
meta-skill strategy-skill-author v0 草稿 |
上游团队 | ⬜ 未启动(设计 v1 已就位见 design/meta-skill-strategy-skill-author.md) |
实现任何代码前必须遵守的不变量:
- Worker 不感知业务:所有战法 / vibe-trading / MCP 知识仅通过
opencode_profile注入,worker 代码内不出现vibe-trading/strategy/signal_engine等字符串 - SKILL bundle 写完即冻结:worker writeback 一旦完成,目录禁止修改;新版本走新目录
- 强锁不可降级:D10 + D17 是配套设计,agent loader 必须严格执行;不允许"找不到字段就忽略"的 silent fallback
- 生产态零 worker 介入:D4 + D15 共同保证;worker 仅负责研究态
- 降级必须透明:D14 + D15 + OQ-16 共同保证;任何降级都进 degradation_report 并随推送出口
- 多用户 / 多租户 / RBAC(OQ-6 / D16)
- shared components reference / dep graph(D16)
- Strategy Registry API + search + tags(D16)
- LLM 输出二次纠错(OQ-14)
- degradation_report i18n(OQ-15)
- MCP docker image 发布(D18)
- worker 沙盒内代跑 SKILL 的"二次验证"模式(§10)
设计已收敛到 implementation-ready 状态。建议在新 session 中按下面顺序推进:
/sc:workflow Phase X1 worker-side backlog—— 把 §11.2 worker P0/P1 修复 + §5.3 三个 hooks 拆成具体任务/sc:design "MCP 自描述协议规范"—— 详细化schemas/目录约定 + describe_tool 输出格式(D18 落地依据)/sc:design "meta-skill strategy-skill-author 内容"—— OQ-1 细化,让上游团队有可落地的 spec
- 内部 vibe-trading fork repo 启动会议 —— GOVERNANCE.md 起草 + rebase 节奏定盘
- MCP 仓库骨架共识 ——
trading-data-cn/historical-news-cn字段表定盘
- 手工跑通一份最小 SKILL —— 用户和 Prometheus 对话产出
ma250-pullback@0.0.1,手动加载到 Claude Code 跑出当日 picks - 验证通过后启动 X1 全自动闭环
本设计不需要新 ADR(D5 守 ADR-001)。但实现期若发现:
- worker 需要新增非 SSE 的 artifact hook(如 MCP 调用拦截器)→ 可能需要补 ADR 说明 hook 边界
- meta-skill 需要 worker 暴露新接口(如"在沙盒内代跑 SKILL 用于二次验证")→ 需要新 ADR
- MCP schema introspection 协议需要 worker 强制 → 可能需要新 ADR 与 ADR-001 互锁
逐 case 评估,不预设。
{ "schema_version": "1.0", "strategy_id": "ma250-pullback", "version": "0.3.1", "provenance": { "created_by": "opencode-worker", "worker_commit": "e32c5e5", "opencode_version": "1.15.0", "ohmy_version": "4.1.2", "primary_session_id": "sess_xxx", "created_at": "2026-05-15T10:30:00+08:00" }, "skill": { "skill_md_path": "SKILL.md", "agent_compatibility": ["claude-code", "opencode", "vibe-trading-mcp"] }, "execution": { "entry_module": "code/signal_engine.py", "entry_class": "SignalEngine", "entry_method": "generate" }, "env_lock": { // G7 + G9: 缺一即拒绝运行;强锁字段 "required_mcps": [ { "name": "vibe-trading", "channel": "internal-a-share", // ★ D11:必须显式声明 "min_version": "0.2.1", // 内部 fork 版本号 "upstream_baseline": ">=0.1.7", // 此 fork 仍保持兼容的社区版基线 "source": "git:LegoNanoBot/vibe-trading-a-share@v0.2.1", "tools_used": [ { "tool_name": "get_market_data", "required_input_fields": ["codes", "start_date", "end_date", "source", "interval"], "required_output_fields": ["close_adj", "low_adj", "volume", "date"] }, { "tool_name": "backtest", "required_input_fields": ["run_dir"], "required_output_fields": ["metrics", "equity_curve"] } ] }, { "name": "trading-data-cn", // 项目特有 "min_version": "1.0", "source": "git:LegoNanoBot/mcps/trading-data-cn@v1.0.3", "tools_used": [ { "tool_name": "get_security_meta", "required_input_fields": ["symbols"], "required_output_fields": ["symbol", "name", "industry", "is_st", "listed_date", "board"] }, { "tool_name": "get_universe", "required_input_fields": ["date", "filters"], "required_output_fields": ["symbols"] } ] }, { "name": "historical-news-cn", // 项目特有 "min_version": "1.0", "source": "git:LegoNanoBot/mcps/historical-news-cn@v1.0.1", "tools_used": [ { "tool_name": "query_news", "required_input_fields": ["start_time", "end_time", "categories"], "required_output_fields": ["news_id", "title", "summary", "categories", "published_at"] } ] } ], "required_env": [ {"name": "TUSHARE_TOKEN", "purpose": "China A-share market data"}, {"name": "DEEPSEEK_API_KEY", "purpose": "LLM steps in event_sector_filter / pick_explanation"} ], "required_db": [ {"alias": "market_data_cn", "kind": "postgres", "purpose": "intraday cache"} ] }, "llm_steps": { // G8: 声明会调用 LLM 的 prompt "event_sector_filter": { "prompt_path": "prompts/event_sector_filter.md", "model": "deepseek/deepseek-v3.2", "temperature": 0.0, "max_tokens": 512, "called_by": "skill_md" // 由 SKILL.md 引导 agent 调用 }, "pick_explanation": { "prompt_path": "prompts/pick_explanation.md", "model": "deepseek/deepseek-v3.2", // cheap model 控成本 "temperature": 0.3, "max_tokens": 256, "called_by": "skill_md", "invocation_mode": "per_pick_concurrent", // ★ D12: 每只单独并发 "concurrency_limit": 7, // 与 ranking.limit 对齐 "fallback_template_path": "reason_template.md" // LLM 失败时降级模板 } }, "data_contract": { // 用于 vibe-trading.backtest 输入校验 "frequency": "daily", "fields": ["close_adj", "low_adj", "volume"], "history_days": 260, "adjustment": "forward", "asof_time": "T 16:30 CST" }, "limits": { // SKILL 自带技术限流,硬性 "production_runtime_sec": 120, "production_max_llm_calls": 20, // event_filter(1) + pick_explanation(7) + 余量 "per_step_max_llm_calls": { "event_sector_filter": 1, "pick_explanation": 10 // 上限略高于 ranking.limit 防边界 } }, // 注:成本预算(OQ-13 闭环)由上游 runtime 维护 per-tenant 月度配额, // 不在 SKILL 内声明(SKILL 不感知租户)。上游负责: // - 每次 agent 调度前查租户余额 → 不足则降级 reason_template(覆盖 SKILL 默认) // - 接近 80% 月度阈值 → 告警通知 // - 超阈值 → 强制降级模板 + 在 degradation_report 中标注 "budget_exceeded" "degradation_policy": { // ★ D14 + D15: 盘前静默降级 + 透明报告 "rules": [ { "trigger": "llm_step_failed", "step": "event_sector_filter", "action": "skip_step", // 跳过事件过滤,全 universe 进入下一步 "report_text": "事件板块过滤步骤失败,本日选股未叠加事件驱动语义" }, { "trigger": "llm_step_failed", "step": "pick_explanation", "action": "fallback_to_template", // 降到 reason_template.md "report_text": "命中说明 LLM 失败,已用模板生成" }, { "trigger": "required_field_missing", "field": "any", "action": "skip_affected_picks", "report_text": "{count} 只候选因关键字段缺失被剔除" }, { "trigger": "required_mcp_unavailable", "mcp": "any", "action": "abort_with_report", // 极端情况:彻底跑不了,仍出空报告 "report_text": "{mcp_name} 不可用,本日无法产出选股;建议人工介入" }, { "trigger": "picks_count_below_target", "action": "push_partial", // 推送 < N 只 "report_text": "本日仅命中 {actual} 只(目标 {target})" }, { "trigger": "data_freshness_violation", "max_lag_minutes": 60, "action": "push_with_warning", "report_text": "数据延迟 {lag_minutes} 分钟于 asof_time,可能影响命中精度" } ], "report_format": { "must_include": ["degraded", "degraded_steps", "reasons", "impact"], "delivery": "embedded_in_picks_response", // 与选股结果同结构同推送 "field_naming": "english", // OQ-15: 字段名英文(程序消费) "value_locale": "zh-CN", // OQ-15: 值默认中文(用户消费) "i18n_phase": 7 // OQ-15: i18n 退 Phase 7 }, "abort_with_report_behavior": { // OQ-16 闭环 "still_push_empty_picks": true, // 即使彻底跑不了,也推送空 picks "ui_emphasis": "high", // UI 必须显眼标注「今日无可推送候选」 "rationale": "用户每日「信号不缺席」原则;避免无推送被误读为正常" } }, "checksums_path": "checksums.txt" }