Files
MoFin/docs/dev-spec.md
T

282 lines
17 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.
# MoFin 开发规范
> 版本: v2.1 | 更新: 2026-07-20 | 基于 AgentsMeeting 样板重构 + 冗余事件复盘
>
> 📋 样板参考: [AgentsMeeting TEMPLATE-GUIDE.md](../AgentsMeeting/docs/TEMPLATE-GUIDE.md)
---
## 十条红线
1. **先读/写 Spec,再写代码** — 新增功能先写 spec 再实现;修改已有功能先读对应 spec 了解架构和约束再动手。没有 spec 的模块在 Dashboard 不可见,视为未完成
2. **部署必验** — 部署后不打开 Dashboard F Tab 验证 = 部署未完成
3. **不可见即不存在** — 组件不在 Dashboard 中显示 = 等于没部署。离线不告警 = 监控缺陷
4. **实现后同步 Spec** — 每轮开发完毕后,必须将 `specs/{module}.json` 更新为与实际实现一致的状态。文档过期 = 等于没写
5. **部署目标即验收标准** — 所有代码必须以部署目标环境(Linux 246)为基准编写和测试。禁止使用 Windows 专属 API`tasklist``netstat``schtasks``wmic`)在 246 部署的代码中
6. **单一事实源(SSOT** — 每个文件全系统只有一个权威位置,其他位置只允许硬链接(同 inode),**禁止独立副本**。权威位置:`deploy/profile-scripts/`cron 脚本)、`/home/hmo/MoFin/`(被 import 的库)、`deploy/bot/`(XMPP bot)。硬链接破坏只会发生在部署动作(scp 替换文件 / git checkout/merge),因此部署链路自带三层自动修复,不需要靠记性:
- **systemd path watcher**`profile-scripts-sync.path`):监听 `deploy/profile-scripts/` 目录变化,任何 scp/替换自动触发 `sync_profile_scripts.sh`
- **git hooks**`.git/hooks/post-merge` + `post-checkout`):git 操作后自动重链
- **手动兜底**`bash deploy/profile-scripts/sync_profile_scripts.sh`(改完文件随手跑)
- 同步日志:`gateway/logs/link_sync.log`;断链检测同时是 L2 卫生审计的检查项
- **部署守卫**`deploy_guard.py`,每 15 分钟全天候):①被跟踪代码文件出现未提交改动 → 自动回滚+重链+告警(git 提交不受影响)②`session-work` 领先 master 且可快进 → 自动 merge+重链+(触及 dashboard 时)重启服务 ③幂等重链 ④cron 引用完整性检查。状态落盘 `gateway/logs/deploy_guard_status.json`,有动作即 XMPP 报备。**禁止直接编辑 246 上任何被跟踪的代码文件**——所有改动必须经 git(详见 `docs/zhiwei-ops-discipline.md`
7. **数据路径必须绝对** — 引用数据文件/数据库时,必须写绝对路径并指向权威位置(`/home/hmo/MoFin/data/`)。**禁止**用 `Path(__file__).parent / "data"` 这类相对解析——同一个模块被硬链接到不同位置时会解析出不同的数据库(2026-07-20 三库事件的根因)
8. **备份/遗留物禁止留在生产数据目录**`.bak``decisions_backup_*`、迁移残留 JSON、废弃 DB,必须在迁移/变更完成时移到 `archive/`。生产数据目录(`MoFin/data` = `web-dashboard/data`)只放活文件。监控脚本扫描生产目录时,遗留物就是未来的假警报
9. **文件居住宪法(源头防副本)** — 新文件只允许落在三处 canonical:cron 脚本 → `deploy/profile-scripts/`;被 import 的库 → repo 根目录;独立服务 → `deploy/bot/`。**禁止**在 `scripts/` 下新建 cron 脚本副本或库副本(`scripts/` 只放被系统引用的工具)。一次性诊断/修复脚本 → 用完即归档 `archive/YYYYMMDD-*/` 或放 `temp/`。违者由 L2 卫生审计的**自动收尸**处理:`scripts/` 下的影子副本(deploy 同名)和零引用孤儿(mtime>7天)会被自动 `git mv``archive/YYYYMM-auto/` 并自动提交——不需要人工清理,也不会再堆积(2026-07-22 起,scripts/ 从 186 个文件收尸到个位数)
10. **死模块必须收尸** — 宣布模块废弃时,必须在同一轮操作中完成收尸六步:①杀进程 ②stop+disable systemd 服务 ③删 cron job ④归档脚本到 `archive/` ⑤归档数据文件 ⑥从期望矩阵/监控中移除。只说"已废弃"不收尸 = 没废弃(小果 bot 以 root 白跑 8 天 2.5GB 的教训)
10. **监控查"活"不查"在"** — 健康检查必须验证**数据新鲜度**(DB 表 MAX(时间列))而非"文件存在/进程存在"。文件 mtime、进程存活都不构成健康证据——数据 24h 不更新才是事故。禁止拿遗留文件的 mtime 当管道健康指标("数据管道停滞14天"假警报的根因)
11. **批量 LLM 调用禁止走 hermes gateway agent 通道** — hermes gateway 的 `/v1/chat/completions` **不是透传**,是完整 agent 运行时:每个请求创建带工具(terminal/websearch/patch)的 agent 会话,可能螺旋几十轮、累积 150k+ token,客户端超时后服务端仍空转,重试会叠加新会话形成自我 DDoS2026-07-21 603288 事件:单次重评螺旋 35 分钟、44 次 terminal 调用)。所有批量/脚本化 LLM 调用必须经 `llm_client.call_llm()`:**OCG 上游直连为主**(裸 completionkey 运行时从 hermes config.yaml 读取,不落盘),gateway 仅作应急兜底。新增 LLM 调用点一律复用 `llm_client`,禁止手写 HTTP 调用
12. **告警信噪比纪律** — 所有系统 XMPP 告警必须经 `alert_helper.notify()`,禁止直 POST :5805。两级通道:**ACTION**(买入信号/重点推荐/需人工核查)直通不限速、🚨 前缀独立成条,**永不被限速**;**INFO**(部署/卫生/修复/监控报备)同类 30 分钟限 1 条、≤8 行、24h 内容去重(同一问题不重复轰炸)。有意义的信号(重点推荐操作)绝不允许被纯通知淹没;通知型信息零问题 = 零消息(沉默即正常)
13. **股票代码是唯一权威标识,名称只是显示属性** — 任何匹配/join/去重/外键必须基于 `code`,禁止基于 `name`。名称随时可变:ST 加摘帽、XD/XR/DR 除权息标记(当日所有行情源的简称都被截断,拿不到干净名)、公司改名。写库时 name 缺失必须先查 stocks 已有真名、再腾讯 quote 兜底(`mofin_db.fetch_stock_name_tencent`),**禁止拿 code 冒充 name**;名称劣化由 `stock_name_hygiene` 周频任务自愈(2026-08-22 名称治理确立,当日审计确认全系统表结构/代码/前端均无 name 匹配点)
---
## 一、双轨同源规范体系
每新增/修改一个独立功能模块,必须先写 `specs/{module}.json`
一个来源同时产出两套文档:
```
specs/{module}.json
├── human_help → ? 按钮(人类看说明/排错)
└── ai_spec → § 按钮(AI 看接口/约束/依赖)
```
### 什么算一个模块
满足以下任一条件即视为独立模块,必须写 spec:
- 暴露独立的 HTTP API 端点
- 在 Dashboard 上有独立 UI 面板(`?` + `§` 按钮)
- 有独立的配置文件 / 数据文件
- 可独立部署(如定时任务、数据采集脚本)
### Spec 字段标准
```json
{
"module": "模块名(与 Dashboard 引用名一致)",
"version": "1.0",
"purpose": "一句话说明这个模块干什么",
"human_help": {
"title": "面向人类的标题",
"description": ["说明段落数组"],
"usage": ["使用步骤数组"],
"troubleshooting": ["常见问题数组"]
},
"ai_spec": {
"apis": [
{"method": "GET", "path": "/api/xxx", "returns": "返回值说明"}
],
"dependencies": ["依赖的服务或文件"],
"constraints": ["AI 必须遵守的约束"],
"must_not": ["AI 绝对不能做的事"],
"tests": [{"id": "T1", "name": "测试用例名"}],
"related_files": ["实现文件路径"]
}
}
```
### 当前模块清单
| 模块 | spec 路径 | 说明 | 状态 |
|------|----------|------|------|
| portfolio | `specs/portfolio.json` | 持仓数据 + 总览 | ✅ |
| watchlist | `specs/watchlist.json` | 自选股管理 | ✅ |
| decisions | `specs/decisions.json` | 策略决策库 | ✅ |
| market | `specs/market.json` | 市场观察数据 | ✅ |
| signals | `specs/signals.json` | 信号 + 全市场扫描 | ✅ |
| scanner | `specs/scanner.json` | 全市场选股机制 | ✅ |
| evaluation | `specs/evaluation.json` | 策略评估 | ✅ |
| prompts | `specs/prompts.json` | 提示词版本管理 | ✅ |
| reports | `specs/reports.json` | 分析报告管理 | ✅ |
| dashboard | `specs/dashboard.json` | Dashboard 自身 | ✅ |
| health | `specs/health.json` | 健康监控管线 | ✅ |
| xmpp_monitor | `specs/xmpp_monitor.json` | XMPP 通信可观测性 | ✅ |
| hygiene | `specs/hygiene.json` | 系统卫生审计(防冗余) | ✅ |
| price_monitor | `specs/price_monitor.json` | 价格监控 cron | 📋 |
| strategy_lifecycle | `specs/strategy_lifecycle.json` | 策略生命周期 | 📋 |
> 状态: ✅ = spec 已完成 | 📋 = 待编写
---
## 二、文件位置宪法(2026-07-20 冗余事件后确立)
| 内容类型 | 唯一权威位置 | 其他位置的合法形态 |
|---------|-------------|------------------|
| cron 脚本(被调度直接执行) | `deploy/profile-scripts/` | profile scripts 目录硬链接(经 `sync_profile_scripts.sh` 同步) |
| 被 import 的库(mo_*/mofin_*/strategy_*/technical_* | `/home/hmo/MoFin/`(根目录) | 禁止副本;deploy/profile-scripts 中的同名库文件只能是对 root 的硬链接 |
| XMPP bot | `deploy/bot/` | `/home/hmo/xmpp_zhiwei_bot.py` 符号链接 |
| Dashboard 服务 | `web-dashboard/server.py`= `/home/hmo/MoFin/server.py` 硬链接) | — |
| 数据文件/数据库 | `/home/hmo/MoFin/data/`= `web-dashboard/data` 硬链接) | **禁止**任何第二个数据目录 |
| 归档 | `archive/<主题>-<日期>/` | — |
| 待销毁 | `trashbox/` | 定期人工清空 |
**禁止出现的位置**`MoFin/scripts/*.py``MoFin/` 根目录的 cron 脚本副本、`.hermes/*/scripts/data/`(任何 profile 本地 data 目录存业务数据)、`projects/` 下与生产同名的项目副本。
### 死模块收尸清单(红线9 的执行版)
```
宣布模块 X 废弃时,同一轮操作内必须完成:
□ 杀进程:pkill 或 systemctl stop(确认 ps 无残留)
□ 服务:systemctl disable + rm unit 文件 + daemon-reload
□ cron:两个 jobs.json 中删除 X 的 job,确认无残留
□ 脚本:移到 archive/<模块>-retired-<日期>/
□ 数据文件:同上
□ 监控:从期望矩阵/健康检查注册表中移除 X
□ 记录:CHANGELOG 写明收尸动作
```
---
## 三、验证闭环
```
┌────────────┐ ┌──────────┐ ┌──────────┐
│ G: 规范体系 │────→│ K: 测试 │────→│ F: 健康 │
│ 定义期望 │ │ 验证实现 │ │ 持续监控 │
└────────────┘ └──────────┘ └──────────┘
↑ ↑ │
└────────────────┼────────────────┘
┌────────┴────────┐
│ F 异常 → 触发 K │
│ K 失败 → 更新 G │
└─────────────────┘
```
### 核心反馈链路
| 方向 | 触发条件 | 动作 |
|------|---------|------|
| G → K | 新增/修改 spec | 对应测试 ID 必须新增/更新 |
| K → F | 测试全部通过 | F Tab 组件标记为已验证 |
| **F → K** | **F Tab 发现异常** | 触发对应测试重跑,确认是服务故障还是测试过期 |
| **F → G** | **F Tab 持续异常但测试通过** | 期望矩阵或 spec 过时,应更新 G 和对应 spec |
### F — 系统健康度(Dashboard F Tab
- **期望矩阵**:应该运行的服务 vs 实际状态
- **监控数据**L05min 心跳)/ L1-L2(功能与卫生)作为实时状态输入,详见「四、自检体系责任矩阵」
- **服务拓扑**:所有服务的健康、端口状态
- **?§ 覆盖**:F Tab 中的每条服务必须有对应的 ?human_help)和 §(ai_spec)按钮
---
## 四、自检体系责任矩阵(L0-L4)
> 2026-07-20 确立。原则:**每层职责单一,不重叠不疏漏;判据是"功能是否达成",不是"进程是否活着"**。
| 层 | 组件 | 频率 | 职责(唯一) | 判据 |
|---|------|------|-------------|------|
| L0 执行心跳 | `agents_health_check.py`(系统 crontab | 5min | 端口/HTTP/DB 存活 + auto_heal 执行(gateway 重启/key 切换/bot 重启) | 端口通 + HTTP 200 + DB 可写 |
| L1 功能健康 | `functional_health_check.py`hermes cron | 交易时段 15min | 核心模块**功能是否达成**:检查输出物新鲜度/有效性(live_prices/market_snapshots/mtf_cache/macro_context/bot/LLM/cron引擎) | 每模块注册表判据(REGISTRY),输出 `functional_health.json` |
| L2 系统卫生 | `system_hygiene_audit.py`hermes cron | 每日 08:20 | 分叉副本/断裂硬链接/僵尸进程/孤儿文件/死cron/DB新鲜度(红线6-10 enforcement | 输出 `hygiene_report.json` |
| L3 修复循环 | `self_repair.py`hermes cron | 30min | 读 L1/L2 失败项 → LLM 诊断 → **白名单动作直接修复(报备制)**`repair_log.jsonl` + XMPP | LLM 只能选白名单动作;每模块每天≤2次防循环 |
| L4 元监控 | `meta_watchdog.py`hermes cron | 每小时 | **自检系统的自检**:L0-L3 输出物新鲜度 + L3 注册状态 + XMPP 桥 | 任何一层死亡直接 XMPP 点名(最后兜底) |
**已退休(职责被合并)**`Cron监护-高频`cron_watchdog,并入 L3)、`全局cron健康监控-每10分`cron_health_monitor,并入 L1)。
### 报备制(L3 的核心纪律)
发现问题 → **直接修复** → 记录日志 → XMPP 报备。不是"发现问题→报告→等指示"。LLM 介入诊断但**只能执行白名单动作**rerun_script/restart_service/sync_links/switch_llm_key/none),不允许任意代码执行。白名单兜不住的,在 XMPP 里明确说"需要人工"。
---
## 五、开发流程
### 新增功能流程
```
确定模块边界
├─ 1. 创建 specs/{module}.json
│ human_help + ai_spec
├─ 2. 实现功能代码
│ 包含 /health 端点 + PID 锁(proc_guard
├─ 3. 注册到系统
│ - 端口注册
│ - 添加到期望矩阵(F Tab 自动检测)
├─ 4. 编写测试
│ - ai_spec.tests 添加对应测试标识
├─ 5. 同步更新 Spec
│ - 将 specs/{module}.json 更新为与实际实现一致
└─ 6. 提交 → 部署 → 验证
```
### 修改已有功能流程
```
识别要修改的模块(查看模块清单确定 module 名)
├─ 1. 读 specs/{module}.json
│ 重点读 ai_specapis / constraints / dependencies / must_not
├─ 2. 确认理解
│ - 如果 spec 描述与代码实际行为不一致,优先怀疑 spec 过期
├─ 3. 修改功能代码
│ 只改动需求直接涉及的部分,不顺手优化无关代码
├─ 4. 同步更新 Spec
└─ 5. 提交 → 部署 → 验证
```
### Git 操作规范
| # | 规则 | 说明 |
|---|------|------|
| 1 | 开工前必 pull | `git pull --rebase` |
| 2 | 改完即 commit | 一个逻辑单元一次提交。禁止含密钥 |
| 3 | 推前必拉 + 配代理 | push 前 `git pull --rebase`。远程操作前配 `:15000` 代理 |
| 4 | trunk-based | 日常在 main。仅长周期大改开分支 |
### 已有编码规范
MoFin 已有的编码规范见 `docs/DEVELOPMENT_STANDARDS.md`,包含:
- 代码结构(mo_models → mo_data → mofin_db 三层)
- 数据规范(币种、汇率、数据源)
- DB 规范(表设计、迁移)
- LLM Prompt 规范
- Cron 规范(独立运行、幂等性)
- 测试要求(`run_all_tests.py`
以上规范与本文件互补,不冲突。本文件侧重"先 spec 后代码"和"通过 Dashboard 保证可见性"。
---
## 六、部署环境
| 项目 | 值 |
|------|-----|
| **生产环境** | Linux 192.168.1.246 |
| **代码目录** | `/home/hmo/MoFin/` |
| **数据库** | `/home/hmo/web-dashboard/data/mofin.db`SQLite |
| **Flask API** | `server.py``:8899`(含 Dashboard |
| **Python** | 系统 Python 3 |
---
## 七、文档索引
| 文档 | 用途 |
|------|------|
| `docs/dev-spec.md` | 本文件 — 开发规范(含十条红线) |
| `docs/DEVELOPMENT_STANDARDS.md` | 编码规范(已有) |
| `SYSTEM_ARCHITECTURE.md` | 系统架构(已有) |
| `docs/cron-catalog.md` | Cron 任务清单(已有) |
| `docs/DEPLOY.md` | 部署指南 |
| `docs/QUICKSTART.md` | 快速操作 |
| `docs/DASHBOARD.md` | Dashboard API 参考 |
| `docs/HEALTH-PIPELINE.md` | 健康管线文档 |
| `docs/learned.md` | 经验教训记录 |
| `docs/decisions/` | 架构决策日志 |