docs: LLM执行协议开发规范+可复用JSON schema

- docs/llm-execution-protocol.md: 从重评流程提取的通用协议(输入/prompt/解析/校验/写入/错误处理)
- specs/schemas/reassess-output.json: 重评LLM输出节标题schema
- specs/schemas/trade-capture-output.json: 截图识别LLM输出schema
- 可复用组件: _section_line/snapshot_strategy_history/write_holding_strategy/门禁模式
This commit is contained in:
xxm
2026-08-21 00:08:09 +08:00
parent 6a036faa28
commit 61c3cb52b2
3 changed files with 278 additions and 0 deletions
+254
View File
@@ -0,0 +1,254 @@
# LLM 执行协议(开发规范)
> 任何"LLM 输出 → 代码校验 → 写入 DB"的场景,必须遵循本协议。
> 本协议从重评流程(batch_reassess)提取,适用于所有 LLM→DB 交互。
---
## 一、协议总览
```
输入数据 → Prompt模板 → LLM输出 → 节标题解析 → 代码校验 → DB写入 → 确认反馈
```
**核心原则**
1. **LLM 只输出文本**,不直接写 DB
2. **代码层做所有校验**LLM 输出不可信)
3. **写入前快照**,失败可恢复
4. **白名单控制写入权限**(不是所有 source 都能写所有字段)
---
## 二、输入数据层
### 2.1 数据来源规范
| 数据类型 | 来源 | 层级 | 示例 |
|---|---|---|---|
| 价格 | stock_daily.close / live_prices.price | 采集层 | 收盘价/盘中价 |
| 基本面 | stock_fundamentals.pe/mcap | 加工层 | PE/市值 |
| 技术指标 | ta.full_analysis() / stock_indicators | 加工层 | 支撑阻力/MA/RSI |
| 策略参数 | holding_strategies | 业务层 | entry/stop/tp |
| 消息面 | signal_news | 采集层 | 新闻摘要 |
| 资金流 | capital_flow_cache | 采集层 | 净流入/主力 |
| 组合状态 | portfolio_summary + holdings | 业务层 | 总资产/持仓股数 |
**铁律**:使用层不得直接调 API 采集数据。所有数据必须从 DB 表读取(由采集层写入)。
### 2.2 数据收集函数
每个 LLM 任务实现一个 `collect_data(code)` 函数:
- 从各表读取数据
- 填充 `data` dict
- 返回给 build_prompt 使用
---
## 三、Prompt 模板规范
### 3.1 结构
```
角色定义(一句话)
【输入数据段1】(系统提供,非 LLM 生成)
【输入数据段2】
...
【输出格式要求】(节标题列表 + 每个字段的约束)
⚠️ 输出纪律(必须遵守)
1. 直接以【节标题】开头
2. 禁止寒暄/开场白
3. 所有【】节标题一个都不能少
```
### 3.2 节标题设计原则
- **每个字段一个节标题**(如 `【买入区间】``【建议止损】`
- **节标题行首匹配**(不使用关键词包含)
- **一个字段一个值**(禁止节标题内嵌套多个值)
- **"无"显式表达**(如 `【买入区间】无`,不写 `0.0~0.0`
### 3.3 输出纪律
prompt 末尾必须包含:
```
⚠️ 输出纪律:
1. 直接以【xxx】开头
2. 禁止寒暄
3. 所有【】节标题一个都不能少
4. 每个字段只填一个值
```
---
## 四、解析层(parse_response
### 4.1 节标题解析器
```python
def _section_line(text, name):
"""从 LLM 输出中提取指定节标题的内容"""
for line in text.split("\n"):
if re.match(r'^\s*【' + name + r'', line):
return line
return ""
```
**规则**
- 行首匹配(可含空白)
- 只认精确节标题(不用 "包含xxx的第一行"
- 返回整行内容(含节标题本身)
### 4.2 解析约束
每个字段的解析必须包含:
- **类型校验**:数字字段必须是数字
- **范围校验**:如仓位必须 1-30%
- **一致性校验**:止损 < 区间下沿 < 区间上沿 < 止盈
- **空值处理**:显式 "无" → 清空;空字符串 → 跳过(保留原值)
---
## 五、校验层(save_result / 门禁)
### 5.1 门禁清单
| 门禁 | 规则 | 失败行为 |
|---|---|---|
| 空输出 | LLM 返回空/无节标题 | 拒绝写入 |
| 区间合理性 | 下沿 < 上沿 < 下沿 × 3 | 跳过区间写入 |
| 区间偏离 | 区上沿 < 现价 × 0.5 或 区下沿 > 现价 × 1.5 | 拒写并清空 |
| 止损锚定 | 止损 ≥ 区间下沿 | 用技术位修正 / 拒写 |
| 信号一致性 | 操作建议否定买入但信号是"买入" | 降级为"关注" |
| 数量正整数 | 股数必须 > 0 且为整数 | 拒绝写入 |
| 代码存在性 | 股票代码必须在 stock_daily 中存在 | 拒绝写入 |
### 5.2 门禁输出
每个门禁失败必须:
1. 打印 `⚠️` 日志(含具体原因和数值)
2. 跳过该字段写入(保留原值)或拒绝整个写入
3. **不能静默失败**
---
## 六、写入层
### 6.1 写入前操作
```python
snapshot_strategy_history(conn, code, source_trigger) # 快照
```
### 6.2 写入权限(白名单)
```python
_PARAM_WHITELIST = ('per_stock_12d', 'batch_12d', 'promote')
if source_trigger not in _PARAM_WHITELIST:
# 非白名单 source:保留 DB 当前参数值
```
### 6.3 写入后操作
- 返回确认信息给调用方
- 触发必要的联动(如快照、信号同步)
---
## 七、错误处理
### 7.1 LLM 调用失败
```
重试 → 升级模型 → 最终失败则跳过该股票
```
### 7.2 解析失败
```
节标题未找到 → 该字段跳过(保留原值)
所有字段跳过 → 拒绝写入
```
### 7.3 写入失败
```
DB 异常 → 回滚到快照 → 打印错误
```
---
## 八、可复用组件
### 8.1 `_section_line(text, name)` — 节标题解析器
- 位置:batch_reassess.py
- 用途:从 LLM 输出提取指定节内容
- 通用性:✅ 任何 LLM 输出解析都可复用
### 8.2 `snapshot_strategy_history(conn, code, source)` — 写入前快照
- 位置:mofin_db.py
- 用途:记录修改前状态,失败可恢复
- 通用性:✅ 任何 DB 写入都应先快照
### 8.3 `write_holding_strategy(..., source_trigger)` — 白名单写入
- 位置:mofin_db.py
- 用途:按 source_trigger 控制写入权限
- 通用性:✅ 所有策略卡写入都走这个函数
### 8.4 门禁模式
```python
# 区间合理性门禁
if _el > 0 and _eh > _el and _eh < _el * 3:
# 合理,写入
elif _el > 0 or _eh > 0:
print("⚠️ 解析异常,跳过")
```
通用性:✅ 任何数值字段都可套用此模式
---
## 九、新场景接入清单
新增"LLM 输出 → 写入 DB"场景时,按以下清单逐项完成:
- [ ] 实现 `collect_data(code)` — 收集输入数据
- [ ] 实现 `build_prompt(data)` — 定义输出格式(节标题)
- [ ] 定义节标题列表 — 每个字段一个标题
- [ ] 实现 `parse_response(text)` — 按节标题解析
- [ ] 实现门禁逻辑 — 校验每个字段的合理性
- [ ] 选择写入函数 — 复用已有或新建
- [ ] 定义 source_trigger — 写入权限控制
- [ ] 测试:正常输入 → 正常输出
- [ ] 测试:异常输入(空/越界/格式错误)→ 正确拒绝
- [ ] 测试:并发写入 → 不互相覆盖
---
## 十、示例:截图识别更新持仓
### 输入
用户发交易截图 → 知微 LLM 解析
### 输出格式
```
【交易动作】买入 / 卖出
【股票代码】600262
【股票名称】北方稀土
【交易数量】500
【交易价格】15.20
【交易时间】2026-08-20 14:30
```
### 门禁
- 交易动作:必须是"买入"或"卖出"
- 股票代码:6位数字,必须在 stock_daily 中存在
- 交易数量:正整数,卖出时不超过当前持仓
- 交易价格:正数,与现价偏差不超过 ±10%
### 写入
- 买入 → INSERT OR REPLACE holdingsshares += 交易数量)
- 卖出 → UPDATE holdingsshares -= 交易数量,shares=0 时标记 is_active=0
- 写入后自动调用 clean_watchlist(自选池进出管理)