From 8f8e54e0bd6564b827672fc6f07820a0bb2f8a5a Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 6 Oct 2025 15:13:19 +0800 Subject: [PATCH] update --- .../department_base@1.1.0.json | 31 ++++++++++ .../department_base@1.2.0.json | 31 ++++++++++ .../prompt_templates/momentum_dept@1.1.0.json | 28 ++++++++++ app/llm/templates.py | 56 +++++++++++++++++++ app/ui/views/backtest.py | 42 ++++++++++++++ 5 files changed, 188 insertions(+) create mode 100644 app/data/prompt_templates/department_base@1.1.0.json create mode 100644 app/data/prompt_templates/department_base@1.2.0.json create mode 100644 app/data/prompt_templates/momentum_dept@1.1.0.json diff --git a/app/data/prompt_templates/department_base@1.1.0.json b/app/data/prompt_templates/department_base@1.1.0.json new file mode 100644 index 0000000..6482434 --- /dev/null +++ b/app/data/prompt_templates/department_base@1.1.0.json @@ -0,0 +1,31 @@ +{ + "department_base": { + "name": "部门基础模板", + "description": "审慎分析骨架(数据校验强化版)", + "template": "部门:{title}\n股票代码:{ts_code}\n交易日:{trade_date}\n\n【角色定位】\n- 角色说明:{description}\n- 行动守则:{instruction}\n\n【数据校验】\n1. 逐条确认提供的数据是否符合 expected schema,缺失项需记录在 `risks`。\n2. 如出现冲突数据,请在 `signals` 中说明解决思路,并指出最可信的来源。\n\n【数据边界】\n- 可用字段:\n{data_scope}\n- 核心特征:\n{features}\n- 市场背景:\n{market_snapshot}\n- 追加数据:\n{supplements}\n\n【分析步骤】\n1. 评估数据完整度,必要时建议补充 `fetch_data` 的具体调用参数。\n2. 基于量化证据拆分 2-3 个关键支撑与潜在风险,注明对应字段。\n3. 提炼操作建议并给出可执行的风险对冲或观察指标。\n\n【输出要求】\n仅返回一个 JSON 对象,不要添加额外文本:\n{\n \"action\": \"BUY|BUY_S|BUY_M|BUY_L|SELL|HOLD\",\n \"confidence\": 0-1 之间的小数,\n \"summary\": \"一句话结论\",\n \"signals\": [\"字段+现象\", \"...\"],\n \"risks\": [\"字段+风险\", \"...\"]\n}\n如需说明未完成的数据请求,请在 `risks` 中补充。", + "variables": [ + "title", + "ts_code", + "trade_date", + "description", + "instruction", + "data_scope", + "features", + "market_snapshot", + "supplements" + ], + "max_length": 4000, + "required_context": [ + "ts_code", + "trade_date", + "features", + "market_snapshot" + ], + "version": "1.1.0", + "metadata": { + "label": "quality_guard", + "notes": "强调数据校验与风险映射。" + }, + "activate": false + } +} \ No newline at end of file diff --git a/app/data/prompt_templates/department_base@1.2.0.json b/app/data/prompt_templates/department_base@1.2.0.json new file mode 100644 index 0000000..26b0d51 --- /dev/null +++ b/app/data/prompt_templates/department_base@1.2.0.json @@ -0,0 +1,31 @@ +{ + "department_base": { + "name": "部门基础模板", + "description": "审慎分析骨架(结构化输出版)", + "template": "部门:{title}\n股票代码:{ts_code}\n交易日:{trade_date}\n\n【角色定位】\n- 研究职责:{description}\n- 执行要点:{instruction}\n\n【量化检查】\n- 数据覆盖:\n{data_scope}\n- 模型特征:\n{features}\n- 市场切片:\n{market_snapshot}\n- 附加观察:\n{supplements}\n\n【分析流程】\n1. 使用事实句描述当前市场位置(包含至少 1 项相对指标)。\n2. 归纳 2-3 条策略信号,每条需关联具体字段并给出方向。\n3. 对每条信号列出对应的风险验证指标与触发阈值。\n4. 最终给出操作建议,并在 `confidence` 中量化判断依据。\n\n【输出格式】\n仅输出 JSON,不包含额外文字:\n{\n \"action\": \"BUY|BUY_S|BUY_M|BUY_L|SELL|HOLD\",\n \"confidence\": 小数,\n \"summary\": \"一句话\",\n \"signals\": [\n {\n \"statement\": \"现象描述\",\n \"evidence\": \"对应字段或数据来源\",\n \"direction\": \"bullish|bearish|neutral\"\n }\n ],\n \"risks\": [\n {\n \"threat\": \"风险描述\",\n \"monitor\": \"观测指标\",\n \"threshold\": \"触发阈值\"\n }\n ]\n}\n若缺少关键数据,请在 `risks` 中新增一项 `monitor` 为 `data_gap`。", + "variables": [ + "title", + "ts_code", + "trade_date", + "description", + "instruction", + "data_scope", + "features", + "market_snapshot", + "supplements" + ], + "max_length": 4000, + "required_context": [ + "ts_code", + "trade_date", + "features", + "market_snapshot" + ], + "version": "1.2.0", + "metadata": { + "label": "structured_v2", + "notes": "增加信号与风险的结构化字段。" + }, + "activate": true + } +} \ No newline at end of file diff --git a/app/data/prompt_templates/momentum_dept@1.1.0.json b/app/data/prompt_templates/momentum_dept@1.1.0.json new file mode 100644 index 0000000..b10368d --- /dev/null +++ b/app/data/prompt_templates/momentum_dept@1.1.0.json @@ -0,0 +1,28 @@ +{ + "momentum_dept": { + "name": "动量研究部门模板", + "description": "多层级动量评估版本", + "template": "部门:动量研究部门\n股票代码:{ts_code}\n交易日:{trade_date}\n\n【角色定位】\n- 追踪价格与成交量动量的共振与切换。\n- 识别趋势衰减与阶段性反转信号。\n\n【分层动量视角】\n1. 超短线(1-3 日)动量:\n{features}\n2. 短线(5-10 日)趋势斜率:\n{market_snapshot}\n3. 中线(20+ 日)位置与乖离:\n{data_scope}\n\n【执行指引】\n- 对每个时间层给出强度评级(strong/weak/neutral)。\n- 若评级存在冲突,需在 `risks` 说明优先采信的层级。\n- 必须评估量价背离与成交密集区。\n\n【输出要求】\n仅输出 JSON:\n{\n \"action\": \"BUY|BUY_S|BUY_M|BUY_L|SELL|HOLD\",\n \"confidence\": 小数,\n \"summary\": \"一句话\",\n \"signals\": [\"层级+观察\", \"...\"],\n \"risks\": [\"层级+风险\", \"...\"]\n}\n若需进一步数据,请在 `risks` 中注明具体字段。", + "variables": [ + "ts_code", + "trade_date", + "data_scope", + "features", + "market_snapshot", + "supplements" + ], + "max_length": 4000, + "required_context": [ + "ts_code", + "trade_date", + "features", + "market_snapshot" + ], + "version": "1.1.0", + "metadata": { + "label": "multiframe", + "notes": "分层动量分析,强调评级冲突解释。" + }, + "activate": false + } +} \ No newline at end of file diff --git a/app/llm/templates.py b/app/llm/templates.py index e83d7d1..a86e969 100644 --- a/app/llm/templates.py +++ b/app/llm/templates.py @@ -3,6 +3,7 @@ from __future__ import annotations import json import logging +from pathlib import Path from dataclasses import dataclass from typing import Any, Dict, List, Optional, TYPE_CHECKING @@ -633,4 +634,59 @@ def register_default_templates() -> None: # Auto-register default templates on module import +EXTERNAL_TEMPLATE_DIR = Path(__file__).resolve().parents[1] / "data" / "prompt_templates" + + +def load_external_template_configs(directory: Path | str = EXTERNAL_TEMPLATE_DIR) -> None: + """Load additional template versions from JSON files in the given directory.""" + + directory_path = Path(directory) + if not directory_path.exists() or not directory_path.is_dir(): + return + + for file_path in sorted(directory_path.glob("*.json")): + try: + raw_data = file_path.read_text(encoding="utf-8") + except OSError: + logging.warning("无法读取提示模板配置文件 %s", file_path) + continue + + try: + payload = json.loads(raw_data) + except json.JSONDecodeError as exc: + logging.warning("提示模板配置文件 %s 解析失败:%s", file_path, exc) + continue + + enriched_payload = {} + for template_id, cfg in payload.items(): + if not isinstance(cfg, dict): + logging.warning( + "提示模板配置文件 %s 中的 %s 配置无效(应为对象)", + file_path, + template_id, + ) + continue + metadata = cfg.get("metadata") or {} + if not isinstance(metadata, dict): + metadata = {} + metadata.setdefault("source", file_path.name) + enriched_payload[template_id] = { + **cfg, + "metadata": metadata, + } + + if not enriched_payload: + continue + + try: + TemplateRegistry.load_from_json(json.dumps(enriched_payload, ensure_ascii=False)) + except Exception as exc: # noqa: BLE001 + logging.warning( + "注册提示模板配置 %s 失败:%s", + file_path, + exc, + ) + + register_default_templates() +load_external_template_configs() diff --git a/app/ui/views/backtest.py b/app/ui/views/backtest.py index 2d0e538..cebfe54 100644 --- a/app/ui/views/backtest.py +++ b/app/ui/views/backtest.py @@ -25,6 +25,7 @@ from app.ingest.tushare import run_ingestion from app.llm.client import run_llm from app.llm.metrics import reset as reset_llm_metrics from app.llm.metrics import snapshot as snapshot_llm_metrics +from app.llm.templates import TemplateRegistry from app.utils import alerts from app.utils.config import get_config, save_config from app.utils.tuning import log_tuning_result @@ -464,6 +465,47 @@ def render_backtest_review() -> None: spec_labels.append(f"department:{dept_code}:tool_choice") action_values.append(tool_value) + template_id = (settings.prompt_template_id or f"{dept_code}_dept").strip() + versions = [ver for ver in TemplateRegistry.list_versions(template_id) if isinstance(ver, str)] + if versions: + active_version = TemplateRegistry.get_active_version(template_id) + default_version = ( + settings.prompt_template_version + or active_version + or versions[0] + ) + try: + default_index = versions.index(default_version) + except ValueError: + default_index = 0 + version_choice = st.selectbox( + "提示模板版本", + versions, + index=default_index, + key=f"{prefix}_template_version", + help="离散动作将按版本列表顺序映射,可用于强化学习优化。", + ) + selected_index = versions.index(version_choice) + ratio = ( + 0.0 + if len(versions) == 1 + else selected_index / (len(versions) - 1) + ) + specs.append( + ParameterSpec( + name=f"dept_prompt_version_{dept_code}", + target=f"department.{dept_code}.prompt_template_version", + values=list(versions), + ) + ) + spec_labels.append(f"department:{dept_code}:prompt_version") + action_values.append(ratio) + st.caption( + f"激活版本:{active_version or '默认'} | 当前选择:{version_choice}" + ) + else: + st.caption("当前模板未注册可选提示词版本,继续沿用激活版本。") + if specs: st.caption("动作维度顺序:" + ",".join(spec_labels))