mirror of
https://github.com/primedigitaltech/market-assistant.git
synced 2026-07-22 08:01:34 +08:00
feat(pipeline): S1 策略稿与第九章对齐(report_strategy_excerpt)
新增 load_report_strategy_excerpt:优先 strategy_opportunities_llm.json 的 markdown,否则从 competitor_analysis.md 截取第九章;runner 成功时落盘 markdown 字段。STRATEGY_SYSTEM 收紧与 report_strategy_excerpt 一致及业务冲突标注。策略稿 API 返回节选来源与字符数;补充单测与 OpenAPI、规划文档。 Made-with: Cursor
This commit is contained in:
parent
224e7d0c16
commit
6e113bbdfe
@ -751,6 +751,8 @@ def write_competitor_analysis_for_run_dir(
|
|||||||
strategy_opp_llm_rec["prior_chapter_narrative_keys"] = sorted(
|
strategy_opp_llm_rec["prior_chapter_narrative_keys"] = sorted(
|
||||||
_strategy_narratives.keys()
|
_strategy_narratives.keys()
|
||||||
)
|
)
|
||||||
|
if (llm_strategy_opp_md or "").strip():
|
||||||
|
strategy_opp_llm_rec["markdown"] = llm_strategy_opp_md
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
strategy_opp_llm_rec["ok"] = False
|
strategy_opp_llm_rec["ok"] = False
|
||||||
strategy_opp_llm_rec["error"] = str(e)
|
strategy_opp_llm_rec["error"] = str(e)
|
||||||
|
|||||||
@ -12,7 +12,9 @@ from .llm_client import call_llm, estimate_chat_input_tokens, llm_context_window
|
|||||||
STRATEGY_SYSTEM = """你是市场策略顾问,根据**结构化监测摘要**与业务侧填写的**决策字段**,把「规则底稿」润色为可读的策略 Markdown。
|
STRATEGY_SYSTEM = """你是市场策略顾问,根据**结构化监测摘要**与业务侧填写的**决策字段**,把「规则底稿」润色为可读的策略 Markdown。
|
||||||
|
|
||||||
**规则**:
|
**规则**:
|
||||||
- 输入 JSON 含 `rules_draft_markdown`(规则引擎生成的底稿,与同任务数据一致)、`structured_brief`(摘要子集)、`strategy_decisions`、`business_notes` 等;
|
- 输入 JSON 含 `rules_draft_markdown`(规则引擎生成的底稿,与同任务数据一致)、`structured_brief`(摘要子集)、`strategy_decisions`、`business_notes` 等;可选 `report_strategy_excerpt`(与同任务宿主报告**第九章**「策略与机会」正文同源,来自已落盘产物)。
|
||||||
|
- **与报告第九章对齐(硬性,当 `report_strategy_excerpt` 非空时)**:润色后的全文在**战略方向与主要判断**上须与该节选**一致**,不得写出与之**明显矛盾**的结论(如定价档位、差异化主线、主要风险判断);若 `business_notes` 或 `strategy_decisions` 与节选冲突,须在正文适当位置简短说明「与报告第九章策略归纳不一致之处见业务备注」类表述,且**不得**把节选与 `structured_brief` 中均未出现的数字当作既定事实。
|
||||||
|
- **当 `report_strategy_excerpt` 为空或仅为占位说明**:表示报告侧未生成或未重跑第九章大模型正文,仅依据 `rules_draft_markdown` 与 `structured_brief` 润色,**不得编造**与可能存在的报告第九章结论。
|
||||||
- **不得编造**输入中不存在的销量、占比、价格数字;若底稿与摘要中有数字,须保持一致;表述集中度时用「第一大……份额」「前三家合计」等中文,**不要用** CR1、CR3 等缩写;
|
- **不得编造**输入中不存在的销量、占比、价格数字;若底稿与摘要中有数字,须保持一致;表述集中度时用「第一大……份额」「前三家合计」等中文,**不要用** CR1、CR3 等缩写;
|
||||||
- 若 `structured_brief` 含 `matrix_overview_for_llm` 或矩阵相关字段,策略中应**呼应**细分类目分组与竞品矩阵结论,不得无故删光矩阵相关建议;
|
- 若 `structured_brief` 含 `matrix_overview_for_llm` 或矩阵相关字段,策略中应**呼应**细分类目分组与竞品矩阵结论,不得无故删光矩阵相关建议;
|
||||||
- 可调整段落衔接、标题层级、列表与表格呈现,使更易读;可补充「建议」「待业务确认」类表述,但不虚构竞品名称或数据;
|
- 可调整段落衔接、标题层级、列表与表格呈现,使更易读;可补充「建议」「待业务确认」类表述,但不虚构竞品名称或数据;
|
||||||
@ -29,7 +31,12 @@ def generate_strategy_draft_markdown_llm(
|
|||||||
business_notes: str,
|
business_notes: str,
|
||||||
generated_at_iso: str,
|
generated_at_iso: str,
|
||||||
strategy_decisions: dict[str, Any],
|
strategy_decisions: dict[str, Any],
|
||||||
|
report_strategy_excerpt: str | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
"""
|
||||||
|
``report_strategy_excerpt``:与同任务宿主报告第九章「策略与机会」正文对齐的节选(见
|
||||||
|
``reporting.report_strategy_excerpt.load_report_strategy_excerpt``);空字符串表示未生成或未重跑第九章大模型。
|
||||||
|
"""
|
||||||
rules_md = build_strategy_draft_markdown(
|
rules_md = build_strategy_draft_markdown(
|
||||||
job_id=job_id,
|
job_id=job_id,
|
||||||
keyword=keyword,
|
keyword=keyword,
|
||||||
@ -39,6 +46,7 @@ def generate_strategy_draft_markdown_llm(
|
|||||||
strategy_decisions=strategy_decisions,
|
strategy_decisions=strategy_decisions,
|
||||||
)
|
)
|
||||||
compact = compact_brief_for_llm(brief, max_chars=80_000)
|
compact = compact_brief_for_llm(brief, max_chars=80_000)
|
||||||
|
excerpt = (report_strategy_excerpt or "").strip()
|
||||||
payload = {
|
payload = {
|
||||||
"job_id": job_id,
|
"job_id": job_id,
|
||||||
"keyword": keyword,
|
"keyword": keyword,
|
||||||
@ -47,6 +55,7 @@ def generate_strategy_draft_markdown_llm(
|
|||||||
"business_notes": business_notes,
|
"business_notes": business_notes,
|
||||||
"structured_brief": compact,
|
"structured_brief": compact,
|
||||||
"rules_draft_markdown": rules_md,
|
"rules_draft_markdown": rules_md,
|
||||||
|
"report_strategy_excerpt": excerpt,
|
||||||
}
|
}
|
||||||
raw = json.dumps(payload, ensure_ascii=False)
|
raw = json.dumps(payload, ensure_ascii=False)
|
||||||
if len(raw) > 500_000:
|
if len(raw) > 500_000:
|
||||||
|
|||||||
72
backend/pipeline/reporting/report_strategy_excerpt.py
Normal file
72
backend/pipeline/reporting/report_strategy_excerpt.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
"""
|
||||||
|
从任务 run 目录加载「报告第九章 · 策略与机会」正文,供策略稿 LLM 与宿主报告对齐(阶段 S1)。
|
||||||
|
|
||||||
|
优先顺序:
|
||||||
|
|
||||||
|
1. ``strategy_opportunities_llm.json`` 中的 ``markdown``(与 runner 落盘一致);
|
||||||
|
2. 否则从 ``competitor_analysis.md`` 截取 ``## 九、策略与机会提示`` 至 ``## 附录`` 之前。
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
|
CHAPTER_NINE_HEADING = "## 九、策略与机会提示"
|
||||||
|
|
||||||
|
|
||||||
|
def extract_chapter_nine_strategy_markdown(full_md: str) -> str:
|
||||||
|
"""
|
||||||
|
提取宿主报告中第九章策略块(含章节标题行),不含「附录」及之后内容。
|
||||||
|
|
||||||
|
若未找到标题,返回空字符串。
|
||||||
|
"""
|
||||||
|
t = full_md or ""
|
||||||
|
if not t.strip():
|
||||||
|
return ""
|
||||||
|
key = CHAPTER_NINE_HEADING
|
||||||
|
i = t.find(key)
|
||||||
|
if i == -1:
|
||||||
|
return ""
|
||||||
|
chunk = t[i:]
|
||||||
|
j = chunk.find("\n## 附录")
|
||||||
|
if j != -1:
|
||||||
|
chunk = chunk[:j]
|
||||||
|
return chunk.rstrip()
|
||||||
|
|
||||||
|
|
||||||
|
def load_report_strategy_excerpt(
|
||||||
|
run_dir: Path | str,
|
||||||
|
*,
|
||||||
|
max_chars: int = 24_000,
|
||||||
|
) -> tuple[str, Literal["json_markdown", "competitor_analysis_md", "none"]]:
|
||||||
|
"""
|
||||||
|
返回 ``(节选正文, 来源标签)``。节选可能为空(未生成第九章或未找到标题)。
|
||||||
|
"""
|
||||||
|
root = Path(run_dir)
|
||||||
|
cap = max(512, int(max_chars))
|
||||||
|
|
||||||
|
json_path = root / "strategy_opportunities_llm.json"
|
||||||
|
if json_path.is_file():
|
||||||
|
try:
|
||||||
|
data = json.loads(json_path.read_text(encoding="utf-8"))
|
||||||
|
except (json.JSONDecodeError, OSError):
|
||||||
|
data = None
|
||||||
|
if isinstance(data, dict):
|
||||||
|
md = (data.get("markdown") or "").strip()
|
||||||
|
if md:
|
||||||
|
s = md if len(md) <= cap else md[: cap - 80].rstrip() + "\n\n…(已截断)\n"
|
||||||
|
return s, "json_markdown"
|
||||||
|
|
||||||
|
md_path = root / "competitor_analysis.md"
|
||||||
|
if md_path.is_file():
|
||||||
|
try:
|
||||||
|
full = md_path.read_text(encoding="utf-8")
|
||||||
|
except OSError:
|
||||||
|
return "", "none"
|
||||||
|
block = extract_chapter_nine_strategy_markdown(full)
|
||||||
|
if block.strip():
|
||||||
|
s = block if len(block) <= cap else block[: cap - 80].rstrip() + "\n\n…(已截断)\n"
|
||||||
|
return s, "competitor_analysis_md"
|
||||||
|
|
||||||
|
return "", "none"
|
||||||
68
backend/pipeline/tests/test_report_strategy_excerpt.py
Normal file
68
backend/pipeline/tests/test_report_strategy_excerpt.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
"""``report_strategy_excerpt``:第九章正文加载与截取。"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from pipeline.reporting.report_strategy_excerpt import (
|
||||||
|
CHAPTER_NINE_HEADING,
|
||||||
|
extract_chapter_nine_strategy_markdown,
|
||||||
|
load_report_strategy_excerpt,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_extract_chapter_nine_stops_before_appendix() -> None:
|
||||||
|
md = f"""# x
|
||||||
|
|
||||||
|
{CHAPTER_NINE_HEADING}(假设清单,待验证)
|
||||||
|
|
||||||
|
正文一段。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录 A:数据留存说明
|
||||||
|
|
||||||
|
尾部。
|
||||||
|
"""
|
||||||
|
out = extract_chapter_nine_strategy_markdown(md)
|
||||||
|
assert CHAPTER_NINE_HEADING in out
|
||||||
|
assert "正文一段" in out
|
||||||
|
assert "附录" not in out
|
||||||
|
assert "尾部" not in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_prefers_json_markdown(tmp_path: Path) -> None:
|
||||||
|
(tmp_path / "strategy_opportunities_llm.json").write_text(
|
||||||
|
json.dumps(
|
||||||
|
{"schema_version": 1, "ok": True, "markdown": "JSON 内第九章正文"},
|
||||||
|
ensure_ascii=False,
|
||||||
|
),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
(tmp_path / "competitor_analysis.md").write_text(
|
||||||
|
f"{CHAPTER_NINE_HEADING}\n\n从 MD 来\n\n## 附录 A\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
text, src = load_report_strategy_excerpt(tmp_path)
|
||||||
|
assert src == "json_markdown"
|
||||||
|
assert "JSON 内第九章" in text
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_falls_back_to_competitor_md(tmp_path: Path) -> None:
|
||||||
|
(tmp_path / "strategy_opportunities_llm.json").write_text(
|
||||||
|
json.dumps({"schema_version": 1, "ok": True}, ensure_ascii=False),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
(tmp_path / "competitor_analysis.md").write_text(
|
||||||
|
f"{CHAPTER_NINE_HEADING}(假设清单,待验证)\n\n从 MD 截取。\n\n## 附录 A\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
text, src = load_report_strategy_excerpt(tmp_path)
|
||||||
|
assert src == "competitor_analysis_md"
|
||||||
|
assert "从 MD 截取" in text
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_none_when_missing(tmp_path: Path) -> None:
|
||||||
|
text, src = load_report_strategy_excerpt(tmp_path)
|
||||||
|
assert src == "none"
|
||||||
|
assert text == ""
|
||||||
@ -22,6 +22,7 @@ from ..llm.generate import generate_strategy_draft_markdown_llm
|
|||||||
from ..models import JobStatus, PipelineJob
|
from ..models import JobStatus, PipelineJob
|
||||||
from ..reporting.brief_pack import build_brief_pack_zip_bytes
|
from ..reporting.brief_pack import build_brief_pack_zip_bytes
|
||||||
from ..reporting.md_document_export import markdown_to_docx_bytes, markdown_to_pdf_bytes
|
from ..reporting.md_document_export import markdown_to_docx_bytes, markdown_to_pdf_bytes
|
||||||
|
from ..reporting.report_strategy_excerpt import load_report_strategy_excerpt
|
||||||
from ..reporting.strategy_draft import build_strategy_draft_markdown
|
from ..reporting.strategy_draft import build_strategy_draft_markdown
|
||||||
from ..serializers import PipelineJobSerializer, StrategyDraftRequestSerializer
|
from ..serializers import PipelineJobSerializer, StrategyDraftRequestSerializer
|
||||||
from .common import job_run_dir_usable
|
from .common import job_run_dir_usable
|
||||||
@ -151,6 +152,12 @@ class JobStrategyDraftView(APIView):
|
|||||||
|
|
||||||
gen_at = timezone.now().isoformat()
|
gen_at = timezone.now().isoformat()
|
||||||
generator = (vd.get("generator") or "rules").strip()
|
generator = (vd.get("generator") or "rules").strip()
|
||||||
|
excerpt_src = "none"
|
||||||
|
report_excerpt = ""
|
||||||
|
try:
|
||||||
|
report_excerpt, excerpt_src = load_report_strategy_excerpt(job.run_dir)
|
||||||
|
except OSError:
|
||||||
|
report_excerpt, excerpt_src = "", "none"
|
||||||
try:
|
try:
|
||||||
if generator == "llm":
|
if generator == "llm":
|
||||||
md = generate_strategy_draft_markdown_llm(
|
md = generate_strategy_draft_markdown_llm(
|
||||||
@ -160,6 +167,7 @@ class JobStrategyDraftView(APIView):
|
|||||||
business_notes=notes,
|
business_notes=notes,
|
||||||
generated_at_iso=gen_at,
|
generated_at_iso=gen_at,
|
||||||
strategy_decisions=strategy_decisions,
|
strategy_decisions=strategy_decisions,
|
||||||
|
report_strategy_excerpt=report_excerpt,
|
||||||
)
|
)
|
||||||
src = "llm_text_ai_crawler_v1"
|
src = "llm_text_ai_crawler_v1"
|
||||||
else:
|
else:
|
||||||
@ -179,16 +187,17 @@ class JobStrategyDraftView(APIView):
|
|||||||
{"detail": f"大模型网关错误:{e}"},
|
{"detail": f"大模型网关错误:{e}"},
|
||||||
status=status.HTTP_502_BAD_GATEWAY,
|
status=status.HTTP_502_BAD_GATEWAY,
|
||||||
)
|
)
|
||||||
return Response(
|
body: dict[str, object] = {
|
||||||
{
|
"schema_version": 1,
|
||||||
"schema_version": 1,
|
"job_id": job.id,
|
||||||
"job_id": job.id,
|
"keyword": job.keyword,
|
||||||
"keyword": job.keyword,
|
"generated_at": gen_at,
|
||||||
"generated_at": gen_at,
|
"source": src,
|
||||||
"source": src,
|
"markdown": md,
|
||||||
"markdown": md,
|
"report_strategy_excerpt_source": excerpt_src,
|
||||||
}
|
"report_strategy_excerpt_chars": len(report_excerpt or ""),
|
||||||
)
|
}
|
||||||
|
return Response(body)
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(csrf_exempt, name="dispatch")
|
@method_decorator(csrf_exempt, name="dispatch")
|
||||||
|
|||||||
@ -585,3 +585,10 @@ components:
|
|||||||
example: structured_summary_rules_v1
|
example: structured_summary_rules_v1
|
||||||
markdown:
|
markdown:
|
||||||
type: string
|
type: string
|
||||||
|
report_strategy_excerpt_source:
|
||||||
|
type: string
|
||||||
|
description: none=未找到第九章正文;json_markdown=strategy_opportunities_llm.json 的 markdown;competitor_analysis_md=从 competitor_analysis.md 截取
|
||||||
|
enum: [none, json_markdown, competitor_analysis_md]
|
||||||
|
report_strategy_excerpt_chars:
|
||||||
|
type: integer
|
||||||
|
description: 参与对齐的第九章节选字符数(供策略稿 LLM 的 report_strategy_excerpt;generator=rules 时仅元数据)
|
||||||
|
|||||||
@ -7,7 +7,8 @@
|
|||||||
| 能力 | 位置 |
|
| 能力 | 位置 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 规则策略底稿 | `pipeline/reporting/strategy_draft.py` → `build_strategy_draft_markdown` |
|
| 规则策略底稿 | `pipeline/reporting/strategy_draft.py` → `build_strategy_draft_markdown` |
|
||||||
| 策略稿 LLM 润色 | `pipeline/llm/generate_strategy.py` → `generate_strategy_draft_markdown_llm` |
|
| 策略稿 LLM 润色 | `pipeline/llm/generate_strategy.py` → `generate_strategy_draft_markdown_llm`(payload 含 `report_strategy_excerpt`) |
|
||||||
|
| 第九章节选加载(S1) | `pipeline/reporting/report_strategy_excerpt.py` → `load_report_strategy_excerpt` |
|
||||||
| 报告第九章策略归纳 | `generate_strategy_opportunities_llm`(与 `competitor_brief` + 各章节选对齐) |
|
| 报告第九章策略归纳 | `generate_strategy_opportunities_llm`(与 `competitor_brief` + 各章节选对齐) |
|
||||||
| 策略稿 API / 导出 | `pipeline/views/job_report_views.py` → `JobStrategyDraftView` |
|
| 策略稿 API / 导出 | `pipeline/views/job_report_views.py` → `JobStrategyDraftView` |
|
||||||
| 简报与压缩 | `pipeline/reporting/brief_compact.py` |
|
| 简报与压缩 | `pipeline/reporting/brief_compact.py` |
|
||||||
@ -26,15 +27,16 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. 策略稿:与报告内「策略建议」对齐(阶段 S1)
|
## 2. 策略稿:与报告内「策略建议」对齐(阶段 S1)— **已落地**
|
||||||
|
|
||||||
**目标**:独立下载的策略稿与宿主报告第九章**方向一致**,避免与已发布报告矛盾。
|
**目标**:独立下载的策略稿与宿主报告第九章**方向一致**,避免与已发布报告矛盾。
|
||||||
|
|
||||||
**拟议实现要点**:
|
**实现要点(与代码一致)**:
|
||||||
|
|
||||||
1. 策略稿生成/润色前,加载本任务 **`strategy_opportunities_llm.json`**(若存在),或从 **`competitor_analysis.md`** 抽取第九章相关正文片段,作为 **`report_strategy_excerpt`** 并入 LLM 的 JSON payload。
|
1. **`load_report_strategy_excerpt(run_dir)`**:优先读 `strategy_opportunities_llm.json` 的 **`markdown`**(runner 在第九章大模型成功时写入);否则从 **`competitor_analysis.md`** 截取 `## 九、策略与机会提示` 至 `## 附录` 之前。
|
||||||
2. 在 `STRATEGY_SYSTEM` 中明确:**须与 `report_strategy_excerpt` 策略方向一致**;若业务备注与报告冲突,文中标注「与报告不一致处见业务备注」。
|
2. **`STRATEGY_SYSTEM`**:当 `report_strategy_excerpt` 非空时,润色稿须与节选**战略方向一致**;若 `business_notes` / `strategy_decisions` 与节选冲突,须标注「与报告第九章策略归纳不一致之处见业务备注」类表述。节选为空时不得编造第九章结论。
|
||||||
3. 产品侧:`generator: rules | llm` 区分展示;**规则版**可作为审计与对外发送前的对照底稿。
|
3. **API**:`POST /api/jobs/{id}/strategy-draft/` 响应增加 `report_strategy_excerpt_source`、`report_strategy_excerpt_chars`(`generator=rules` 时亦返回,便于核对是否加载到节选)。
|
||||||
|
4. 产品侧:`generator: rules | llm` 仍为既有行为;规则版作审计底稿。
|
||||||
|
|
||||||
**验收**:抽样任务核对「第九章要点 ↔ 策略稿 bullet」可对应;数字仅来自 brief/底稿。
|
**验收**:抽样任务核对「第九章要点 ↔ 策略稿 bullet」可对应;数字仅来自 brief/底稿。
|
||||||
|
|
||||||
@ -73,7 +75,7 @@
|
|||||||
|
|
||||||
| 阶段 | 内容 | 产出 |
|
| 阶段 | 内容 | 产出 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| **S1** | 策略稿 payload 增加 `report_strategy_excerpt`,收紧 `STRATEGY_SYSTEM` | 策略稿与第九章可对照 |
|
| **S1** | 策略稿 payload 增加 `report_strategy_excerpt`,收紧 `STRATEGY_SYSTEM` | ✅ 已合并:`report_strategy_excerpt.py`、runner 落盘 `markdown`、OpenAPI 响应字段 |
|
||||||
| **S2** | 营销模块 v1:单模板 + 强约束提示词 + 落盘 + 复用 Word/PDF 导出 | 可演示的营销初稿 |
|
| **S2** | 营销模块 v1:单模板 + 强约束提示词 + 落盘 + 复用 Word/PDF 导出 | 可演示的营销初稿 |
|
||||||
| **S3** | 前端:任务页入口、受众与渠道、生成历史 | 产品闭环 |
|
| **S3** | 前端:任务页入口、受众与渠道、生成历史 | 产品闭环 |
|
||||||
| **S4**(可选) | 轻量校验:输出中数字与 brief 同源性启发式检查 | 降低明显幻觉 |
|
| **S4**(可选) | 轻量校验:输出中数字与 brief 同源性启发式检查 | 降低明显幻觉 |
|
||||||
@ -92,5 +94,6 @@
|
|||||||
| 日期 | 说明 |
|
| 日期 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 2026-04-17 | 首版:对齐原则、S1~S4、代码锚点、API 示意。 |
|
| 2026-04-17 | 首版:对齐原则、S1~S4、代码锚点、API 示意。 |
|
||||||
|
| 2026-04-18 | S1 落地:`load_report_strategy_excerpt`、`STRATEGY_SYSTEM` 对齐条款、`strategy_opportunities_llm.json.markdown`、策略稿 API 响应字段。 |
|
||||||
|
|
||||||
后续变更请在本表追加一行,并在正文相应章节修改。
|
后续变更请在本表追加一行,并在正文相应章节修改。
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user