zhang пре 2 недеља
родитељ
комит
235d1b404c

+ 1252 - 0
README_myself.md

@@ -0,0 +1,1252 @@
+# SupplyAgent 业务框架设计
+
+## 1. 项目定位
+
+SupplyAgent 是一个面向平台内容供给的需求汇总工具。
+
+它要解决的核心问题不是“从数据中找出若干热门词”,而是:
+
+> 汇总来自不同渠道、不同时间尺度和不同验证阶段的需求信号,将其统一挂靠到一棵全局分类树上,形成一张可追溯、可解释、可持续反馈的需求关系图,最终输出数百条平台级需求。
+
+最终结果同时服务于两种业务视角:
+
+1. 从全局分类树和关系图观察平台需求版图、层级、覆盖和交织关系。
+2. 从排序后的需求清单直接开展内容发现、内容生产、供给调度和效果验证。
+
+项目最终交付的不是一张扁平需求表,而是:
+
+- 一棵稳定的全局分类树;
+- 数百条挂靠在树上的平台需求;
+- 需求与多个树节点之间的关系线;
+- 需求背后的多维数据证据;
+- 需求与内容、线上表现之间的反馈闭环。
+
+---
+
+## 2. 对现有项目业务结构的理解
+
+当前项目已经形成了需求汇总的基础业务链路。
+
+### 2.1 上游需求池
+
+项目从上游策略需求池接收不同类型的需求信号。目前已经体现出的主要维度包括:
+
+- 外部热度;
+- 平台持续热度;
+- 平台去年同期热度;
+- 平台近期供需缺口;
+- 近 7 日真实 ROV;
+- 近 7 日真实 VOV。
+
+前四类数据主要回答“什么需求可能值得做”,属于先验信号。
+
+真实 ROV、VOV 主要回答“需求被内容承接并上线后是否真的有效”,属于后验反馈。
+
+### 2.2 全局分类树
+
+`global_tree_category` 所表达的是一棵统一的全局语义分类树,而不是多棵相互独立的人物树、事件树或情感树。
+
+分类树从左向右按层级展开,例如:
+
+```text
+L1        L2        L3          L4          L5
+知识  →   历史  →   历史时期  →  古代史
+
+事件  →   社会事件  →  人物故事  →  个人经历
+                              →  名人故事
+```
+
+树上的正式节点是稳定、抽象、可治理的业务分类。具体的人物、事件、情感和需求表达,不一定都要成为正式树节点。
+
+### 2.3 需求词归类
+
+上游需求中的词语或短语会被挂靠到全局分类树的合适节点上。
+
+归类的业务意义是为需求建立稳定坐标,使需求可以:
+
+- 沿树向上汇总;
+- 在同类需求间比较;
+- 观察不同分支的需求覆盖;
+- 计算分类节点在不同信号维度下的强度;
+- 支撑后续平台需求生成。
+
+### 2.4 分类节点强度
+
+当前项目会把需求词级别的数据向分类树节点及其祖先聚合。
+
+因此分类树不仅承担知识组织,还承担需求统计和强度观察:
+
+- 叶子或挂载节点反映局部、具体需求;
+- 中间节点反映某个业务方向的整体强度;
+- 高层节点反映平台需求版图中的大方向。
+
+### 2.5 平台需求生成
+
+现有需求生成 Agent 已经体现了以下层次:
+
+```text
+来源维度 → 整体方向 → 汇总事件 → 原始需求名称
+```
+
+这个结构可以作为初始生成方式,但最终业务模型需要进一步升级为:
+
+```text
+全局分类树 + 需求节点 + 跨分支关系 + 多维证据 + 线上反馈闭环
+```
+
+---
+
+## 3. 核心设计原则
+
+### 3.1 一棵树,而不是多棵业务树
+
+全局分类树是整个系统唯一的正式分类骨架。
+
+人物、事件、情感、知识、历史、行为等概念分布在同一棵树的不同分支中。平台需求通过同时连接多个分支,表达真实用户兴趣的交织关系。
+
+### 3.2 以树确定归属,以图表达关系
+
+树内父子关系负责表达:
+
+- 层级;
+- 上下位关系;
+- 统计汇总路径;
+- 业务覆盖;
+- 正式分类治理。
+
+图上的跨节点关系负责表达:
+
+- 一个需求同时涉及哪些分类分支;
+- 人物、事件、行为、情感等元素如何共同组成需求;
+- 不同需求之间是否存在重合、包含、相似或关联;
+- 哪些上游信号共同支持同一个需求。
+
+### 3.3 每条需求必须挂树
+
+每条正式平台需求至少挂靠一个真实存在的分类树节点。
+
+一条需求可以同时挂靠多个正式树节点。多个挂靠点共同定义需求,不要求在业务语义上强制区分主次。
+
+例如同一条需求可以同时挂靠:
+
+- 人物故事;
+- 个人经历;
+- 历史解读;
+- 诗词创作;
+- 与具体战争时期相关的历史分类。
+
+如果某些统计、展示或资源分配场景必须使用唯一口径,可以额外指定一个“统计主归属节点”。统计主归属只用于避免重复计数,不代表其他挂靠点在业务语义上更次要。
+
+### 3.4 用户意图优先
+
+需求的多个挂靠点应共同表达“用户为什么对此感兴趣”,而不是只机械选择最具体的对象节点。
+
+例如需求:
+
+> 毛泽东在抗日战争和解放战争时期的诗词创作
+
+可能涉及:
+
+- 人物对象:毛泽东;
+- 历史背景:抗日战争、解放战争;
+- 行为:写诗、诗词创作;
+- 内容意图:人物经历、作品背景、历史解读。
+
+需求可以同时挂靠“人物故事”“个人经历”“历史解读”或与诗词创作相关的正式节点,具体取决于上游数据实际支持了哪些用户意图。
+
+“毛泽东”“抗日战争”“解放战争”“诗词创作”等相关语义通过多个挂靠点、需求元素和关系线共同表达。
+
+### 3.5 数据驱动为主,自由推演为辅
+
+平台需求必须主要来源于上游数据。
+
+建议的总体比例是:
+
+- 80%~90% 为上游数据直接支持的核心需求;
+- 10%~20% 为基于已有数据、节点和关联关系形成的邻近机会需求。
+
+自由推演不得:
+
+- 凭空创造没有来源的人物、事件、情感或主题;
+- 用模型常识替代上游证据;
+- 将弱关联包装成确定关系;
+- 与数据支持的需求混淆展示。
+
+推演需求必须单独标记,并能说明它是从哪些已有数据和关系扩展而来。
+
+### 3.6 原始事实与语义推断分离
+
+上游目前主要提供“关联”关系。
+
+系统必须永久保留原始“关联”边,不得覆盖或篡改。
+
+系统可以在有依据时,把普通关联扩展解释为新的语义关系,例如:
+
+- 人物参与事件;
+- 事件发生于某个历史时期;
+- 人物在某个时期进行诗词创作;
+- 作品表达某种精神或情感;
+- 某个历史背景影响作品主题。
+
+所有推断语义关系必须附带:
+
+- 推断说明;
+- 原始关联证据;
+- 数据来源;
+- 置信度;
+- 是否经过人工确认;
+- 创建或更新时间。
+
+证据不足时继续保留“关联”,不强行解释。
+
+---
+
+## 4. 整体业务图结构
+
+整张图由六类核心业务对象组成:
+
+- 分类树节点;
+- 细节元素;
+- 视频或内容实例;
+- 明确语言观点与长段讨论;
+- 平台需求;
+- 证据、状态与反馈。
+
+### 4.1 分类树节点
+
+正式、稳定、可治理的分类节点。
+
+分类树节点之间保留原始父子关系,并从左向右按层级展开。
+
+### 4.2 需求元素节点
+
+从上游需求中识别出的具体业务元素,例如:
+
+- 人物:毛泽东;
+- 历史事件:抗日战争、解放战争;
+- 行为:写诗;
+- 作品或内容:战争时期诗词;
+- 情感或精神:革命乐观主义、家国情怀。
+
+需求元素首先来自上游数据。系统只负责归一别名、识别类型并保留来源。
+
+### 4.3 平台需求节点
+
+平台需求是最终业务输出的核心实体,不等同于某个分类节点,也不等同于某个原始需求词。
+
+例如:
+
+> 毛泽东在抗日战争时期创作过哪些诗词
+
+> 毛泽东的战争诗词如何反映当时的历史环境
+
+> 从抗日战争到解放战争,毛泽东诗词主题发生了什么变化
+
+这些需求可以共同涉及“毛泽东、抗日战争、解放战争、写诗”等元素,但它们表达的是不同用户意图,因此应当是不同的平台需求节点。
+
+### 4.4 内容节点
+
+能够承接某条需求的真实内容,包括:
+
+- 已有内容;
+- 搜索发现的内容;
+- 新生产的内容;
+- 已上线并获得真实反馈的内容。
+
+需求和内容之间允许多对多关系:
+
+- 一条需求可以由多条内容承接;
+- 一条内容也可能同时承接多个需求。
+
+但需要区分内容的主需求和辅助需求,避免效果归因失真。
+
+### 4.5 证据与状态节点
+
+记录需求在各个独立维度上的证据、状态和变化,包括:
+
+- 外部热度;
+- 平台持续热度;
+- 去年同期热度;
+- 近期供需缺口;
+- 真实 ROV、VOV;
+- 样本量;
+- 数据日期;
+- 来源可信度;
+- 生命周期状态;
+- 推演标记;
+- 风险或抑制原因。
+
+### 4.6 分类节点下的证据子图
+
+正式分类树不是信息下钻的终点。
+
+每一个分类节点下面,还可以继续挂载与它相关的细节元素、真实视频和语言化内容,形成一套“节点下证据子图”:
+
+```text
+正式分类节点
+   ↓
+细节元素
+   ↓
+真实视频实例
+   ↓
+明确语言观点或问题
+   ↓
+长段讨论
+   ↓
+平台需求
+```
+
+这里的“继续下钻”是产品浏览和证据展开,不是继续增加正式分类树层级。
+
+元素、视频、短观点和长段讨论不应被强行定义成 L6、L7、L8 分类节点。它们挂在正式分类节点下面,但属于不同类型的业务对象。
+
+这样可以同时保证:
+
+- 全局分类树结构稳定、纯净;
+- 节点可以持续承载越来越丰富的细节信息;
+- 用户能够从抽象分类一路下卷到真实内容;
+- 平台需求能够追溯到具体视频和语言证据;
+- 真实内容表现能够逐层回流到元素、需求和分类节点。
+
+### 4.7 细节元素
+
+细节元素是分类节点下面更具体的语义索引。
+
+例如某个“人物故事”或“历史人物”分类节点下面,可以挂载:
+
+- 毛泽东;
+- 抗日战争;
+- 解放战争;
+- 写诗;
+- 诗词创作;
+- 战争时期诗词;
+- 革命乐观主义;
+- 家国情怀。
+
+元素来自真实上游数据、视频解析、内容标签或人工治理。
+
+一个元素可以:
+
+- 挂在多个正式分类节点下;
+- 与其他元素保留原始“关联”关系;
+- 连接多条真实视频;
+- 被多个平台需求共同引用;
+- 根据视频数量、出现次数和线上表现形成元素强度。
+
+分类节点下的元素列表应展示:
+
+- 元素名称;
+- 元素类型;
+- 出现次数;
+- 关联视频数量;
+- 支持的平台需求数量;
+- 线上表现摘要;
+- 数据来源;
+- 更新时间。
+
+### 4.8 视频实例
+
+元素可以继续下钻到具体视频。
+
+视频不是单纯的播放链接,而是需求提取和效果反馈的事实实例。每条视频应尽量保留:
+
+- 视频标题;
+- 视频链接;
+- 作者和发布时间;
+- 视频转写或内容描述;
+- 命中的分类节点;
+- 命中的细节元素;
+- 对应的平台需求;
+- 点赞、评论、分享、收藏等表现;
+- 真实 ROV、VOV;
+- 内容质量或解析可信度;
+- 是否属于主承接内容。
+
+同一条视频可以连接多个元素和需求,但需要区分主要承接和辅助承接。
+
+### 4.9 明确语言观点
+
+视频解析后,可以从内容中提取能够独立表达的短语言单元,例如:
+
+- 一个明确事实;
+- 一个观点;
+- 一个判断;
+- 一个问题;
+- 一句可用于需求命名的话;
+- 一条用户容易理解和传播的内容结论。
+
+例如:
+
+> 战争环境并未中断诗词创作,反而强化了作品的历史表达。
+
+> 毛泽东为什么在战争时期持续进行诗词创作?
+
+短语言单元可以用于:
+
+- 快速理解视频贡献了什么;
+- 比较不同视频是否表达同一观点;
+- 形成需求候选名称;
+- 聚合同义需求;
+- 支撑需求关系说明。
+
+所有语言提取都要保留对应视频、原始片段和提取说明,避免模型生成的语言脱离真实内容。
+
+### 4.10 长段讨论
+
+多个视频、观点和元素还可以进一步形成长段讨论。
+
+长段讨论用于表达短句无法承载的内容,例如:
+
+- 一个问题的完整背景;
+- 多个视频观点之间的共同点和差异;
+- 历史过程和人物行为之间的联系;
+- 一个需求为什么值得形成;
+- 某个需求存在什么争议;
+- 内容供给可以从哪些角度展开;
+- 线上反馈为什么支持或否定该需求。
+
+例如可以围绕以下主题形成讨论:
+
+> 从抗日战争到解放战争,毛泽东的诗词既记录时代环境,也呈现政治理想、个人情感和历史判断。不同视频分别提供作品背景、创作动机、表达方式和受众理解方面的证据。
+
+长段讨论不是脱离数据的自由文章。它必须引用实际元素、视频和明确观点,并标识:
+
+- 讨论依据;
+- 主要证据;
+- 推断内容;
+- 不确定点;
+- 支持或关联的平台需求。
+
+### 4.11 节点下钻的产品形态
+
+用户从全局分类树继续下卷时,建议按以下顺序展开:
+
+```text
+分类节点概览
+→ 高频或高价值元素
+→ 元素关联的视频
+→ 视频转写与明确观点
+→ 多视频形成的长段讨论
+→ 从证据中提取的平台需求
+→ 需求对应的线上验证结果
+```
+
+每一级都要能够返回上一级,并保留:
+
+- 来源;
+- 出现次数;
+- 贡献度;
+- 时间;
+- 置信度;
+- 真实线上表现;
+- 与需求之间的关系。
+
+节点下钻同时服务两个目的:
+
+1. 展示:让业务人员理解这个分类节点下具体有什么。
+2. 提取:让系统从元素、视频和语言证据中发现、生成、合并和验证需求。
+
+---
+
+## 5. 图中的关系类型
+
+### 5.1 树内父子关系
+
+正式分类树原有的层级关系,是全图最稳定的结构。
+
+### 5.2 需求多挂靠关系
+
+每条平台需求可以同时挂靠多个正式分类节点。每条挂靠关系都应记录:
+
+- 挂靠节点;
+- 挂靠理由;
+- 支持该挂靠的上游数据;
+- 关系置信度;
+- 是否属于正式挂靠或待审核挂靠;
+- 生效和更新时间。
+
+多个挂靠点共同表达需求的完整语义和跨分支交织关系。
+
+例如“毛泽东的战争诗词如何反映当时的历史环境”可以同时挂靠:
+
+- 历史解读;
+- 人物故事;
+- 个人经历;
+- 与诗词创作相关的正式节点;
+- 与抗日战争、解放战争所处历史时期相关的正式节点。
+
+当主题统计需要避免重复计数时,可以为需求设置一个“统计主归属节点”,或者按明确的分摊规则将需求计入多个节点。该统计属性与业务挂靠关系分开管理。
+
+### 5.4 原始关联关系
+
+来自上游数据的事实关系,关系类型统一为“关联”。
+
+原始关联边必须保留完整的来源和时间信息。
+
+### 5.5 推断语义关系
+
+系统根据多个原始关联、树上位置和上下文推断出的补充关系。
+
+推断边只用于:
+
+- 增强解释;
+- 帮助聚类;
+- 提供低权重排序增益;
+- 辅助发现邻近机会需求。
+
+推断边不能取代原始关联边。
+
+### 5.6 需求与内容关系
+
+表达内容承接了哪个需求,以及承接程度:
+
+- 主承接;
+- 辅助承接;
+- 部分覆盖;
+- 待验证;
+- 已上线;
+- 已形成有效样本。
+
+### 5.7 内容表现回流关系
+
+表达某批内容的真实线上表现如何反向影响需求强度。
+
+### 5.8 分类节点与元素关系
+
+表达某个细节元素下挂在哪些正式分类节点下。
+
+该关系应记录元素为何属于该分类、来源和出现次数。一个元素允许同时下挂多个分类节点。
+
+### 5.9 元素与视频关系
+
+表达某条视频包含、体现或讨论了哪些元素。
+
+上游只有“关联”时保留原始关联;需要扩展为“人物出现、事件涉及、行为发生、观点表达”等语义时,必须附带说明和置信度。
+
+### 5.10 视频与语言关系
+
+表达明确观点、问题和长段讨论是从哪些视频或视频片段中提取出来的。
+
+语言内容必须能回看原始视频或转写依据。
+
+### 5.11 语言与需求关系
+
+表达某个明确观点、问题或长段讨论支持、形成或验证了哪些平台需求。
+
+同一个需求可以由多个语言证据共同支持,同一个语言观点也可以被多个需求引用。
+
+---
+
+## 6. 需求汇总业务流程
+
+整体不是一次性的线性任务,而是持续循环的业务系统。
+
+```text
+上游需求信号
+    ↓
+数据归一与来源保留
+    ↓
+需求元素识别
+    ↓
+挂靠全局分类树
+    ↓
+保留原始关联并构建关系图
+    ↓
+连接真实视频并解析内容
+    ↓
+提取明确观点和长段讨论
+    ↓
+合并为唯一平台需求
+    ↓
+计算先验需求强度
+    ↓
+形成主题方向与需求池
+    ↓
+搜索或生产承接内容
+    ↓
+内容上线并产生真实表现
+    ↓
+归因到平台需求
+    ↓
+更新需求验证强度与生命周期
+    ↓
+影响下一周期的需求排序和内容供给
+```
+
+### 6.1 数据归一
+
+统一处理:
+
+- 同义词;
+- 简称与全称;
+- 人物别名;
+- 事件的不同表达;
+- 错别字;
+- 中英文表达;
+- 时间和周期口径。
+
+归一不等于覆盖原始数据。每个标准表达都必须能追溯到原始名称和来源。
+
+### 6.2 意图挂树
+
+优先挂靠现有正式树节点。
+
+如果现有树无法准确表达需求:
+
+1. 挂到当前最可靠的上级节点;
+2. 标记表达缺口;
+3. 进入候选节点治理区;
+4. 由人工审核是否需要增加正式节点。
+
+Agent 不直接修改正式分类树。
+
+### 6.3 需求合并
+
+多种来源命中同一个需求时,不按来源拆成重复需求,而是合并为一个平台需求节点。
+
+建议以以下组合判断需求是否相同:
+
+```text
+核心用户意图 + 关键对象 + 适用范围或约束
+```
+
+例如:
+
+- “毛泽东抗战时期写的诗”
+- “毛泽东在抗日战争阶段创作的诗词”
+
+可以合并为同一平台需求,并保留两个原始表达。
+
+但下面两条不应简单合并:
+
+- “毛泽东在抗日战争时期创作过哪些诗词”
+- “毛泽东的抗战诗词表达了什么情感”
+
+前者关注作品事实,后者关注作品解读,用户意图不同。
+
+### 6.4 少量邻近推演
+
+只有在已有数据能够支持时,才允许生成邻近机会需求。
+
+例如上游同时反复出现:
+
+- 毛泽东;
+- 抗日战争;
+- 解放战争;
+- 诗词创作;
+
+系统可以提出:
+
+> 从抗日战争到解放战争,毛泽东诗词主题发生了什么变化
+
+但必须说明:
+
+- 它由哪些上游关联组合而来;
+- 上游是否直接出现过该完整需求;
+- 哪些部分属于模型推断;
+- 当前属于核心需求还是机会需求。
+
+### 6.5 从视频和语言证据中提取需求
+
+需求不仅可以直接来自上游需求名称,也可以从节点下挂的真实内容中被发现。
+
+提取过程应遵循:
+
+1. 从分类节点下的高频、高表现元素开始;
+2. 找到承载这些元素的真实视频;
+3. 从视频转写和解析结果中抽取明确事实、观点和问题;
+4. 对多个视频的语言单元进行归并和比较;
+5. 形成有真实内容依据的需求候选;
+6. 将候选需求挂回一个或多个正式分类节点;
+7. 与已有平台需求去重或合并;
+8. 标记数据来源、视频证据和推断部分。
+
+例如,“毛泽东、抗日战争、解放战争、诗词创作”这些元素分别在多条视频中共同出现,并形成以下语言证据:
+
+- 毛泽东为何在战争时期持续进行诗词创作;
+- 战争环境如何影响诗词主题;
+- 抗日战争和解放战争时期的作品表达有何变化。
+
+系统可以据此提取平台需求,但不能只凭模型常识生成。每一条需求都必须能够回到具体视频和语言证据。
+
+---
+
+## 7. 需求—内容—线上表现闭环
+
+真实线上反馈不是普通的附属指标,而是需求汇总系统的核心后验闭环。
+
+```text
+平台需求
+   ↓
+找到或生产内容
+   ↓
+内容上线和分发
+   ↓
+获得真实线上表现
+   ↓
+校正并归因到需求
+   ↓
+更新需求强度
+   ↓
+调整下一周期供给
+```
+
+### 7.1 建立需求与内容的映射
+
+如果需求和内容之间没有明确映射,线上表现就无法准确回流。
+
+每条上线内容至少需要说明:
+
+- 主要承接哪个平台需求;
+- 是否辅助承接其他需求;
+- 对需求的覆盖程度;
+- 内容上线时间;
+- 当前是否形成有效样本。
+
+### 7.2 线上表现不能直接等同于需求表现
+
+内容表现还会受以下因素影响:
+
+- 内容本身质量;
+- 标题、封面和表达方式;
+- 分发流量;
+- 发布时间;
+- 平台环境;
+- 同类内容竞争;
+- 供给数量;
+- 样本量是否充足。
+
+因此需要把真实表现经过归因校正后,再反向更新需求强度。
+
+不能因为一条质量较差的内容表现不好,就直接判定需求不存在。
+
+### 7.3 反馈影响需求强度
+
+真实线上反馈应当能够:
+
+- 验证一个新需求是否真实成立;
+- 提高持续表现良好需求的强度;
+- 降低持续表现较差需求的优先级;
+- 判断某个需求是否已经过度供给;
+- 发现先验热度不高但真实表现很好的潜在需求;
+- 影响分类树中相关节点的整体强度;
+- 影响下一周期的主题配额和供给策略。
+
+---
+
+## 8. 需求强度模型
+
+需求不能只保留一个不可解释的总分。
+
+每条需求应至少保留“四类分数 + 一个综合等级”。
+
+### 8.1 先验需求分
+
+回答:
+
+> 在内容上线验证之前,这个需求有多值得尝试?
+
+主要来源:
+
+- 外部热度;
+- 平台近期供需缺口;
+- 平台持续热度;
+- 去年同期或周期性热度;
+- 上游来源数量;
+- 样本量和数据时效性。
+
+这些维度应保持正交,分别展示,然后再形成先验综合判断。
+
+### 8.2 线上验证分
+
+回答:
+
+> 内容实际承接该需求后,这个需求是否真实成立?
+
+主要来源:
+
+- 真实 ROV;
+- 真实 VOV;
+- 消费、互动或转化表现;
+- 多条内容的一致性;
+- 多个周期的稳定性;
+- 归因校正结果。
+
+### 8.3 关系增益分
+
+回答:
+
+> 图上的关系是否进一步增强了该需求成立的可能性?
+
+关系增益只作为低权重补充,不能代替原始数据。
+
+### 8.4 风险抑制分
+
+主要风险包括:
+
+- 后验效果持续较差;
+- 数据维度之间明显冲突;
+- 样本量不足;
+- 数据过期;
+- 内容承接失败;
+- 同类需求过度重复;
+- 某个主题过度集中;
+- 需求主要来自自由推演;
+- 关系推断置信度较低。
+
+### 8.5 综合需求强度
+
+业务上可以将其理解为:
+
+```text
+最终需求强度
+= 先验需求强度
++ 经归因校正的线上验证强度
++ 低权重关系增益
+- 风险抑制
+```
+
+具体权重不应在业务框架阶段固定死,应根据历史验证逐步校准。
+
+无论最终采用什么权重,都必须能够展开查看各个组成部分,避免形成黑盒分数。
+
+---
+
+## 9. 需求生命周期
+
+需求是动态实体,不是一经生成就永久有效。
+
+建议设置以下生命周期状态。
+
+### 9.1 新机会
+
+先验信号明显,但尚未有足够内容和线上反馈。
+
+### 9.2 待验证
+
+已经找到或安排了承接内容,正在等待有效线上样本。
+
+### 9.3 增长需求
+
+先验信号增强,或者真实线上表现持续改善。
+
+### 9.4 已验证需求
+
+已经由足量内容、有效样本或多个周期的表现证明。
+
+### 9.5 观察需求
+
+存在一定信号,但样本较少、数据冲突或结论不稳定。
+
+### 9.6 衰退需求
+
+需求热度、真实表现或用户兴趣持续下降。
+
+### 9.7 抑制需求
+
+经过验证后确认当前不值得继续投入,或供给已经明显过量。
+
+### 9.8 周期需求
+
+当前强度下降,但预计会在特定日期、节日、纪念日或社会周期重新激活。
+
+### 9.9 新老需求公平
+
+没有后验数据不等于后验表现差。
+
+为了避免旧需求永久占据高位:
+
+- 新需求主要依据先验分进入探索区;
+- 老需求的历史表现需要时间衰减;
+- 每个主题保留一定探索额度;
+- 只有形成有效样本后才提高后验权重;
+- 已验证需求也要接受持续复验。
+
+---
+
+## 10. 主题方向与数百条需求的组织
+
+最终输出采用两层结构。
+
+### 10.1 上层:主题方向
+
+主题方向不是另建一棵脱离现有分类树的新树。
+
+它应主要来自:
+
+- 全局分类树的中高层节点;
+- 某个树分支下的需求聚类;
+- 多个相关分支共同形成的稳定业务方向。
+
+例如:
+
+- 历史人物故事;
+- 历史人物个人经历;
+- 战争历史与人物;
+- 历史人物作品解读;
+- 历史事件影响;
+- 人物精神与情感。
+
+每个主题方向应包含:
+
+- 主题名称;
+- 对应的分类树范围;
+- 覆盖的用户意图;
+- 需求数量;
+- 各生命周期数量;
+- 整体需求强度;
+- 主要数据来源;
+- 趋势;
+- 需求集中度;
+- 探索需求占比。
+
+### 10.2 下层:平台需求
+
+每条平台需求应包含:
+
+- 唯一需求 ID;
+- 标准需求名称;
+- 原始需求表达;
+- 多个正式挂靠节点;
+- 可选的统计主归属节点;
+- 每个挂靠点的理由和证据;
+- 关联需求元素;
+- 原始关联边;
+- 推断语义边及说明;
+- 所属主题方向;
+- 各维先验信号;
+- 线上验证结果;
+- 关联内容及归因情况;
+- 需求强度;
+- 生命周期;
+- 是否属于推演需求;
+- 风险和抑制原因;
+- 数据更新时间;
+- 强度变化历史。
+
+### 10.3 需求数量分配
+
+数百条需求不按主题平均分配,也不能完全被少数热门主题占满。
+
+采用:
+
+> 数据驱动为主,最低覆盖和最高集中度约束为辅。
+
+具体原则:
+
+- 强信号主题可以拥有更多需求;
+- 弱主题允许较少需求;
+- 无真实信号的主题可以暂时为空;
+- 重要主题保留最低覆盖;
+- 单一主题设置最高集中度;
+- 每个主题保留少量探索位;
+- 推演需求总量保持在受控范围。
+
+---
+
+## 11. 最终展示方式
+
+最终使用同一份需求数据提供两个入口。
+
+### 11.1 全局树图入口
+
+从左向右浏览:
+
+```text
+L1 → L2 → L3 → L4 → L5 → 挂载需求
+```
+
+树图中需要保留:
+
+- 原有树内父子线;
+- 分类节点的需求数量和强度;
+- 平台需求与多个正式树节点之间的挂靠线;
+- 可选的统计主归属标记;
+- 跨分支关联线;
+- 原始关联与推断关系的区别;
+- 需求与内容的承接关系;
+- 线上反馈回流关系。
+
+用户点击某条需求后,应能看到完整追溯链路:
+
+```text
+上游数据
+→ 原始需求表达
+→ 标准化过程
+→ 多个挂靠节点及各自理由
+→ 关联元素
+→ 原始及推断关系
+→ 关联内容
+→ 线上表现
+→ 需求强度变化
+```
+
+### 11.2 需求清单入口
+
+以业务执行为目的,支持按以下维度查看:
+
+- 主题方向;
+- 需求强度;
+- 生命周期;
+- 多个挂靠节点;
+- 可选的统计主归属节点;
+- 数据来源;
+- 是否已经验证;
+- 是否已经有内容承接;
+- 是否存在供需缺口;
+- 是否属于推演需求;
+- 更新时间。
+
+树图入口和清单入口指向同一批需求实体,不维护两套独立结果。
+
+---
+
+## 12. 分类树治理
+
+现有分类树是基础权威骨架,但不是永远不变。
+
+### 12.1 正式节点
+
+已经审核并进入全局分类树,可以作为需求挂靠节点和统计归属节点。
+
+### 12.2 候选节点
+
+当大量上游需求反复出现,而现有树无法准确表达其核心意图时,可以提出候选节点。
+
+候选节点必须说明:
+
+- 哪些需求无法被现有节点准确表达;
+- 当前临时挂靠在哪里;
+- 出现频率和持续周期;
+- 预期父节点;
+- 与现有节点的差异;
+- 新增后能解决什么业务问题。
+
+### 12.3 人工审核
+
+Agent 只能提出候选节点,不能直接修改正式树。
+
+人工审核后可以:
+
+- 接受为正式节点;
+- 与现有节点合并;
+- 继续观察;
+- 拒绝新增。
+
+---
+
+## 13. 业务质量控制
+
+### 13.1 可追溯
+
+任何平台需求必须能够追溯到上游数据。
+
+### 13.2 可解释
+
+必须解释:
+
+- 为什么形成该需求;
+- 为什么挂在这些节点;
+- 为什么与其他表达合并或不合并;
+- 为什么获得当前强度和生命周期;
+- 线上反馈如何影响了它。
+
+### 13.3 防止过度聚合
+
+一个平台需求应表达一个清晰的用户意图。
+
+不能将大量弱相关人物、事件、作品和情感塞入一个“大合集需求”。
+
+### 13.4 防止过度拆分
+
+同一用户意图仅因为来源、措辞或时间不同,不应被拆成多个重复需求。
+
+### 13.5 防止自由推演失控
+
+所有推演需求必须:
+
+- 有数据起点;
+- 有推演路径;
+- 有置信度;
+- 有独立标识;
+- 有比例限制;
+- 可以被人工拒绝。
+
+### 13.6 防止后验误判
+
+线上反馈必须经过归因和样本校正,避免将内容质量、流量不足或供给不足误认为需求无效。
+
+---
+
+## 14. 项目业务模块的总体框架
+
+从业务职责看,整个项目可以划分为九个部分。
+
+### 14.1 信号接入
+
+负责接收内外部、近期远期、周期性和真实反馈数据。
+
+### 14.2 需求理解
+
+负责标准化原始表达、识别需求元素、保留来源和原始关联。
+
+### 14.3 分类树挂靠
+
+负责为需求建立正式分类坐标,并发现分类树表达缺口。
+
+### 14.4 树图汇总
+
+负责把分类树、需求元素、需求节点和关系边组织成统一业务图。
+
+### 14.5 元素与内容下钻
+
+负责展示分类节点下的细节元素,并将元素连接到真实视频、内容标签、转写和线上表现。
+
+### 14.6 语言观点与讨论提取
+
+负责从真实视频中提取明确事实、观点、问题和长段讨论,并完整保留视频依据和推断说明。
+
+### 14.7 需求生成与合并
+
+负责形成唯一平台需求,控制需求粒度,避免重复和过度聚合。
+
+### 14.8 需求决策
+
+负责计算需求强度、生命周期、主题配额和内容供给优先级。
+
+### 14.9 内容反馈闭环
+
+负责把需求与内容连接起来,并将内容真实线上表现回流到需求和分类节点。
+
+整体业务关系是:
+
+```text
+信号接入
+   ↓
+需求理解
+   ↓
+分类树挂靠
+   ↓
+树图汇总
+   ↓
+元素与视频下钻
+   ↓
+语言观点与讨论提取
+   ↓
+需求生成与合并
+   ↓
+需求决策
+   ↓
+内容承接和线上反馈
+   └──────────────→ 回到需求决策和树图强度
+```
+
+---
+
+## 15. 一句话总结
+
+SupplyAgent 的最终业务框架是:
+
+> 以一棵全局分类树作为稳定骨架,把上游多源需求统一挂树;以平台需求作为独立图节点连接多个分类分支和具体需求元素;以原始数据决定需求是否成立,以低权重语义推断补充关系,以内容真实线上表现持续反向更新需求强度,最终形成几十个主题方向和数百条可执行、可解释、可追溯的平台需求。
+
+---
+
+## 16. 业务可视化资料与服务
+
+本次业务框架讨论过程中形成的全部可视化材料统一保存在:
+
+```text
+visualization/
+```
+
+目录中同时包含:
+
+- 业务图结构方案;
+- 从左向右的树图示意;
+- 业务节点和关系模型;
+- 需求—内容—线上表现反馈闭环;
+- 最终主题和平台需求输出结构;
+- 对话过程中的阶段性可视页面;
+- 用户提供的全局分类树局部参考图;
+- 用于本地浏览全部图稿的可视化服务。
+
+目录结构如下:
+
+```text
+visualization/
+├── README.md
+├── run.sh
+├── assets/
+│   └── tree.jpeg
+├── pages/
+│   ├── graph-structure-options.html
+│   ├── graph-structure-left-to-right.html
+│   ├── business-graph-model.html
+│   ├── demand-feedback-loop.html
+│   ├── final-output-structure.html
+│   ├── node-drilldown-evidence-chain.html
+│   ├── waiting-business-scope.html
+│   ├── waiting-demand-pipeline.html
+│   └── waiting-strength-lifecycle.html
+└── service/
+    └── serve.py
+```
+
+### 16.1 启动可视化服务
+
+在项目根目录执行:
+
+```bash
+bash visualization/run.sh
+```
+
+服务默认使用:
+
+```text
+http://localhost:8765/
+```
+
+需要指定端口时:
+
+```bash
+bash visualization/run.sh --port 9000
+```
+
+如果不希望自动打开浏览器:
+
+```bash
+python3 visualization/service/serve.py
+```
+
+服务只依赖 Python 标准库,不要求安装额外前端依赖。
+
+### 16.2 图稿与最终业务口径的关系
+
+`visualization/pages/` 保留了整个讨论过程,因此早期图稿可能包含后来被修正的中间方案。
+
+最终应以本文档的业务定义为准:
+
+- 整体只有一棵全局分类树;
+- 分类树从左向右按层级展开;
+- 平台需求是独立的图节点;
+- 一条需求可以同时挂靠多个正式树节点;
+- 多个挂靠点共同定义需求,不强制区分业务主次;
+- 如统计需要唯一口径,可以另设“统计主归属节点”;
+- 上游原始“关联”边永久保留;
+- 推断语义关系必须附带说明、来源和置信度;
+- 内容的真实线上表现必须反向更新需求强度。
+
+### 16.3 各图稿用途
+
+| 图稿 | 主要用途 |
+|---|---|
+| `graph-structure-options.html` | 记录单一大树、自由图和树骨架关系图三种方案的比较 |
+| `graph-structure-left-to-right.html` | 确认整体从左向右的阅读方式 |
+| `business-graph-model.html` | 说明分类节点、需求元素、平台需求、关系和证据 |
+| `demand-feedback-loop.html` | 说明需求如何找到或生产内容,以及线上表现如何回流 |
+| `final-output-structure.html` | 说明主题方向、数百条平台需求和证据卡如何组织 |
+| `node-drilldown-evidence-chain.html` | 说明分类节点如何继续下挂元素、视频、明确观点、长段讨论并提取需求 |
+| `waiting-*.html` | 保留对话过程中的阶段性页面 |
+| `assets/tree.jpeg` | 作为现有全局分类树结构和阅读方式的参考 |
+| `assets/tree2.png` | 作为分类节点下挂大量细节元素的参考 |
+| `assets/case.jpeg` | 作为元素扩展为明确语言、长段讨论和最终选题的参考 |

BIN
visualization/assets/case.jpeg


BIN
visualization/assets/tree.jpeg


BIN
visualization/assets/tree2.png


+ 127 - 0
visualization/pages/business-graph-model.html

@@ -0,0 +1,127 @@
+<style>
+  .stage { margin-top:18px; padding:16px; background:#f8fafc; border:1px solid #dbe3ec; border-radius:16px; overflow:auto; }
+  .rules { display:grid; grid-template-columns:repeat(2,1fr); gap:12px; margin-top:16px; }
+  .rule { background:#fff; border:1px solid #e2e8f0; border-radius:12px; padding:13px 15px; color:#475569; font-size:13px; }
+  .rule strong { color:#0f172a; }
+  .decision { margin-top:16px; padding:14px 16px; border-radius:12px; background:#eff6ff; border:1px solid #bfdbfe; color:#1e3a8a; }
+  @media(max-width:850px){.rules{grid-template-columns:1fr}}
+</style>
+
+<h2>设计第 1 部分:业务图的节点与边</h2>
+<p class="subtitle">分类树负责“用户意图”的稳定分层;对象图负责具体人物、事件、情感等交织关系;平台需求是连接二者并承载证据的业务实体。</p>
+
+<div class="stage">
+<svg viewBox="0 0 1160 570" width="100%" style="min-width:980px" role="img" aria-label="业务图节点与边模型">
+  <defs>
+    <marker id="a1" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#64748b"/></marker>
+    <marker id="a2" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#ef4444"/></marker>
+    <marker id="a3" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#8b5cf6"/></marker>
+  </defs>
+
+  <g font-family="sans-serif">
+    <text x="145" y="32" text-anchor="middle" font-size="13" fill="#64748b">① 用户意图树:正式骨架</text>
+    <text x="475" y="32" text-anchor="middle" font-size="13" fill="#64748b">② 对象与事实图:上游关联</text>
+    <text x="790" y="32" text-anchor="middle" font-size="13" fill="#64748b">③ 平台需求:最终业务实体</text>
+    <text x="1050" y="32" text-anchor="middle" font-size="13" fill="#64748b">④ 证据与状态</text>
+  </g>
+
+  <!-- Tree -->
+  <g fill="none" stroke="#94a3b8" stroke-width="2">
+    <path d="M145 92 L145 125 M145 125 L75 166 M145 125 L215 166"/>
+    <path d="M75 204 L48 246 M75 204 L110 246"/>
+    <path d="M215 204 L180 246 M215 204 L250 246"/>
+  </g>
+  <g font-family="sans-serif" font-size="13" text-anchor="middle">
+    <rect x="82" y="56" width="126" height="38" rx="11" fill="#1d4ed8"/><text x="145" y="80" fill="#fff">近现代历史人物</text>
+    <rect x="25" y="166" width="100" height="38" rx="10" fill="#dbeafe" stroke="#60a5fa"/><text x="75" y="190">历史经历</text>
+    <rect x="165" y="166" width="100" height="38" rx="10" fill="#dbeafe" stroke="#60a5fa"/><text x="215" y="190">作品表达</text>
+    <rect x="9" y="246" width="78" height="36" rx="9" fill="#fff" stroke="#60a5fa"/><text x="48" y="269">战争经历</text>
+    <rect x="91" y="246" width="78" height="36" rx="9" fill="#fff" stroke="#60a5fa"/><text x="130" y="269">历史背景</text>
+    <rect x="171" y="246" width="78" height="36" rx="9" fill="#fff" stroke="#60a5fa"/><text x="210" y="269">诗词创作</text>
+    <rect x="253" y="246" width="78" height="36" rx="9" fill="#fff" stroke="#60a5fa"/><text x="292" y="269">家国表达</text>
+  </g>
+
+  <!-- Candidate tree node -->
+  <g font-family="sans-serif" text-anchor="middle">
+    <path d="M130 282 C130 326 172 334 172 372" fill="none" stroke="#f59e0b" stroke-width="2" stroke-dasharray="5 4"/>
+    <rect x="102" y="372" width="140" height="48" rx="11" fill="#fffbeb" stroke="#f59e0b" stroke-dasharray="5 4"/>
+    <text x="172" y="393" font-size="12" fill="#92400e">候选意图节点</text><text x="172" y="410" font-size="12">“战争与诗词创作”</text>
+    <text x="172" y="442" font-size="10" fill="#92400e">反复被数据证明后,人工审核入树</text>
+  </g>
+
+  <!-- Object/fact graph -->
+  <g fill="none" stroke="#64748b" stroke-width="2" marker-end="url(#a1)">
+    <path d="M420 104 L488 104"/><path d="M534 127 L534 183"/><path d="M505 219 L443 270"/>
+    <path d="M563 219 L625 270"/><path d="M443 306 C474 337 557 337 625 306"/>
+  </g>
+  <g font-family="sans-serif" text-anchor="middle">
+    <circle cx="385" cy="104" r="38" fill="#dbeafe" stroke="#3b82f6"/><text x="385" y="109" font-size="14">毛泽东</text>
+    <circle cx="534" cy="104" r="45" fill="#dcfce7" stroke="#22c55e"/><text x="534" y="101" font-size="13">抗日战争</text><text x="534" y="118" font-size="11" fill="#475569">上游:关联</text>
+    <circle cx="534" cy="219" r="39" fill="#dcfce7" stroke="#22c55e"/><text x="534" y="224" font-size="13">解放战争</text>
+    <circle cx="415" cy="286" r="35" fill="#fae8ff" stroke="#d946ef"/><text x="415" y="291" font-size="13">家国情怀</text>
+    <circle cx="653" cy="286" r="35" fill="#fae8ff" stroke="#d946ef"/><text x="653" y="291" font-size="13">诗词创作</text>
+  </g>
+
+  <!-- Inferred edges -->
+  <g fill="none" stroke="#ef4444" stroke-width="2" stroke-dasharray="6 4" marker-end="url(#a2)">
+    <path d="M407 127 C432 165 471 188 501 207"/>
+    <path d="M554 141 C601 170 638 213 650 250"/>
+  </g>
+  <g font-family="sans-serif" font-size="10" fill="#b91c1c">
+    <text x="443" y="166">推断:人物参与历史事件</text>
+    <text x="615" y="188">推断:战争背景影响创作</text>
+  </g>
+
+  <!-- Demands -->
+  <g fill="none" stroke="#f59e0b" stroke-width="2.4">
+    <path d="M87 264 C370 390 622 166 730 151"/>
+    <path d="M130 264 C395 464 658 344 730 323"/>
+    <path d="M385 142 C493 164 627 157 730 157"/>
+    <path d="M534 149 C582 174 658 185 730 171"/>
+    <path d="M653 321 C697 321 708 321 730 321"/>
+  </g>
+  <g font-family="sans-serif" text-anchor="middle">
+    <rect x="730" y="112" width="178" height="94" rx="15" fill="#fff7ed" stroke="#f59e0b" stroke-width="2"/>
+    <text x="819" y="136" font-size="12" fill="#9a3412">平台需求 D-001</text>
+    <text x="819" y="159" font-size="14">毛泽东的战争时期经历</text>
+    <text x="819" y="181" font-size="11" fill="#475569">多挂靠:历史经历 / 战争背景</text>
+
+    <rect x="730" y="276" width="178" height="94" rx="15" fill="#fff7ed" stroke="#f59e0b" stroke-width="2"/>
+    <text x="819" y="300" font-size="12" fill="#9a3412">平台需求 D-002</text>
+    <text x="819" y="323" font-size="14">毛泽东为何在战争中写诗</text>
+    <text x="819" y="345" font-size="11" fill="#475569">多挂靠:作品表达 / 诗词创作</text>
+  </g>
+
+  <!-- Evidence -->
+  <g fill="none" stroke="#8b5cf6" stroke-width="1.8" stroke-dasharray="4 4" marker-end="url(#a3)">
+    <path d="M908 143 L993 91"/><path d="M908 159 L993 151"/><path d="M908 177 L993 211"/>
+    <path d="M908 306 L993 303"/><path d="M908 326 L993 363"/><path d="M908 346 L993 423"/>
+  </g>
+  <g font-family="sans-serif" text-anchor="middle">
+    <rect x="993" y="72" width="130" height="38" rx="19" fill="#ede9fe" stroke="#8b5cf6"/><text x="1058" y="96" font-size="12">外部热度:高</text>
+    <rect x="993" y="132" width="130" height="38" rx="19" fill="#ede9fe" stroke="#8b5cf6"/><text x="1058" y="156" font-size="12">近期缺口:中</text>
+    <rect x="993" y="192" width="130" height="38" rx="19" fill="#ede9fe" stroke="#8b5cf6"/><text x="1058" y="216" font-size="12">ROV/VOV:已验证</text>
+    <rect x="993" y="284" width="130" height="38" rx="19" fill="#ede9fe" stroke="#8b5cf6"/><text x="1058" y="308" font-size="12">持续热度:中</text>
+    <rect x="993" y="344" width="130" height="38" rx="19" fill="#ede9fe" stroke="#8b5cf6"/><text x="1058" y="368" font-size="12">关系增益:低权重</text>
+    <rect x="993" y="404" width="130" height="38" rx="19" fill="#f1f5f9" stroke="#94a3b8"/><text x="1058" y="428" font-size="12">状态:增长需求</text>
+  </g>
+
+  <!-- Bottom labels -->
+  <g font-family="sans-serif">
+    <rect x="350" y="482" width="470" height="55" rx="12" fill="#fff" stroke="#e2e8f0"/>
+    <text x="585" y="504" text-anchor="middle" font-size="12" fill="#334155">灰色实线 = 原始“关联”事实边,永久保留</text>
+    <text x="585" y="523" text-anchor="middle" font-size="12" fill="#b91c1c">红色虚线 = 语义推断边,必须带说明、来源、置信度与审核状态</text>
+  </g>
+</svg>
+</div>
+
+<div class="rules">
+  <div class="rule"><strong>节点分四类:</strong>意图分类节点、对象/事件/情感节点、平台需求节点、证据/状态节点。</div>
+  <div class="rule"><strong>需求唯一:</strong>多维数据命中同一用户意图时合并为一个需求实体,不按来源复制。</div>
+  <div class="rule"><strong>多个正式挂靠:</strong>每条需求可以挂靠多个共同表达用户意图的正式树节点,并连接多个具体对象。</div>
+  <div class="rule"><strong>原始与推断分离:</strong>上游“关联”是事实;推断语义只做补充,低权重且完整留痕。</div>
+</div>
+
+<div class="decision">
+  已确认的基本模型:平台需求本身作为独立节点存在,而不是直接等同于某个树节点;它可以挂靠多个正式分类节点,同时连接多个对象和证据节点。
+</div>

+ 115 - 0
visualization/pages/demand-feedback-loop.html

@@ -0,0 +1,115 @@
+<style>
+  .stage { margin-top:18px; padding:16px; border:1px solid #dbe3ec; border-radius:16px; background:#f8fafc; overflow:auto; }
+  .points { display:grid; grid-template-columns:repeat(3,1fr); gap:12px; margin-top:16px; }
+  .point { padding:13px 14px; background:#fff; border:1px solid #e2e8f0; border-radius:12px; color:#475569; font-size:13px; }
+  .point strong { color:#0f172a; }
+  .formula { margin-top:14px; padding:14px 16px; border-radius:12px; background:#fff7ed; border:1px solid #fed7aa; color:#9a3412; text-align:center; font-size:14px; }
+  @media(max-width:900px){.points{grid-template-columns:1fr}}
+</style>
+
+<h2>修订后的业务流水线:需求—内容—表现闭环</h2>
+<p class="subtitle">线上表现不是需求旁边的普通证据,而是需求被内容实际承接之后产生的验证结果;它反向更新需求强度,并驱动下一周期的供给决策。</p>
+
+<div class="stage">
+<svg viewBox="0 0 1160 535" width="100%" style="min-width:980px" role="img" aria-label="需求内容表现反馈闭环">
+  <defs>
+    <marker id="blue" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#2563eb"/></marker>
+    <marker id="green" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#16a34a"/></marker>
+    <marker id="orange" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#f59e0b"/></marker>
+    <marker id="red" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#dc2626"/></marker>
+  </defs>
+
+  <!-- top main flow -->
+  <g fill="none" stroke="#2563eb" stroke-width="3" marker-end="url(#blue)">
+    <path d="M185 138 L260 138"/><path d="M430 138 L505 138"/><path d="M675 138 L750 138"/><path d="M920 138 L995 138"/>
+  </g>
+
+  <!-- feedback loop -->
+  <path d="M1070 194 C1070 410 885 450 577 450 C275 450 93 407 93 194"
+        fill="none" stroke="#dc2626" stroke-width="4" marker-end="url(#red)"/>
+  <text x="580" y="482" text-anchor="middle" font-family="sans-serif" font-size="15" fill="#b91c1c">
+    真实线上反馈:按需求归因后,回写需求强度、状态与置信度
+  </text>
+
+  <!-- inner operational links -->
+  <g fill="none" stroke="#16a34a" stroke-width="2.5" marker-end="url(#green)">
+    <path d="M345 194 L345 272"/><path d="M590 194 L590 272"/><path d="M835 194 L835 272"/>
+  </g>
+  <g fill="none" stroke="#f59e0b" stroke-width="2.5" marker-end="url(#orange)">
+    <path d="M425 321 L505 321"/><path d="M675 321 L755 321"/>
+  </g>
+
+  <g font-family="sans-serif" text-anchor="middle">
+    <!-- top nodes -->
+    <rect x="15" y="82" width="170" height="112" rx="16" fill="#eff6ff" stroke="#3b82f6" stroke-width="2"/>
+    <text x="100" y="111" font-size="13" fill="#1d4ed8">上游信号</text>
+    <text x="100" y="136" font-size="14">内部 / 外部</text>
+    <text x="100" y="157" font-size="14">近期 / 持续 / 周期</text>
+    <text x="100" y="178" font-size="11" fill="#64748b">形成需求先验</text>
+
+    <rect x="260" y="82" width="170" height="112" rx="16" fill="#fff7ed" stroke="#f59e0b" stroke-width="2"/>
+    <text x="345" y="111" font-size="13" fill="#9a3412">平台需求</text>
+    <text x="345" y="137" font-size="15">毛泽东为何坚持写诗</text>
+    <text x="345" y="160" font-size="12" fill="#475569">先验强度:72</text>
+    <text x="345" y="180" font-size="11" fill="#64748b">多挂靠 + 对象关系 + 数据证据</text>
+
+    <rect x="505" y="82" width="170" height="112" rx="16" fill="#f0fdf4" stroke="#22c55e" stroke-width="2"/>
+    <text x="590" y="111" font-size="13" fill="#15803d">内容承接</text>
+    <text x="590" y="137" font-size="14">找到已有内容</text>
+    <text x="590" y="158" font-size="14">或指导生产内容</text>
+    <text x="590" y="180" font-size="11" fill="#64748b">需求 ↔ 内容必须可追踪</text>
+
+    <rect x="750" y="82" width="170" height="112" rx="16" fill="#ecfeff" stroke="#06b6d4" stroke-width="2"/>
+    <text x="835" y="111" font-size="13" fill="#0e7490">线上分发</text>
+    <text x="835" y="137" font-size="14">曝光与消费</text>
+    <text x="835" y="158" font-size="14">供给量与匹配度</text>
+    <text x="835" y="180" font-size="11" fill="#64748b">需考虑内容质量与流量环境</text>
+
+    <rect x="995" y="82" width="150" height="112" rx="16" fill="#fef2f2" stroke="#ef4444" stroke-width="2"/>
+    <text x="1070" y="111" font-size="13" fill="#b91c1c">真实线上表现</text>
+    <text x="1070" y="137" font-size="14">真实 ROV / VOV</text>
+    <text x="1070" y="158" font-size="14">消费、互动、转化</text>
+    <text x="1070" y="180" font-size="11" fill="#64748b">形成需求后验</text>
+
+    <!-- lower details -->
+    <rect x="265" y="272" width="160" height="98" rx="14" fill="#fff" stroke="#f59e0b"/>
+    <text x="345" y="298" font-size="13" fill="#9a3412">需求当前状态</text>
+    <text x="345" y="322" font-size="13">新机会 / 增长</text>
+    <text x="345" y="343" font-size="13">已验证 / 观察 / 抑制</text>
+    <text x="345" y="359" font-size="10" fill="#64748b">状态随周期变化</text>
+
+    <rect x="505" y="272" width="170" height="98" rx="14" fill="#fff" stroke="#22c55e"/>
+    <text x="590" y="298" font-size="13" fill="#15803d">内容归因关系</text>
+    <text x="590" y="322" font-size="13">一条需求 → 多条内容</text>
+    <text x="590" y="343" font-size="13">一条内容 → 多个需求</text>
+    <text x="590" y="359" font-size="10" fill="#64748b">记录主需求与辅助需求</text>
+
+    <rect x="755" y="272" width="160" height="98" rx="14" fill="#fff" stroke="#06b6d4"/>
+    <text x="835" y="298" font-size="13" fill="#0e7490">归因校正</text>
+    <text x="835" y="322" font-size="13">供给量、内容质量</text>
+    <text x="835" y="343" font-size="13">流量、时间、样本量</text>
+    <text x="835" y="359" font-size="10" fill="#64748b">避免把内容问题误判为需求问题</text>
+  </g>
+
+  <!-- strength split -->
+  <g font-family="sans-serif">
+    <rect x="20" y="392" width="245" height="51" rx="11" fill="#fff" stroke="#cbd5e1"/>
+    <text x="142" y="413" text-anchor="middle" font-size="12" fill="#334155">需求先验强度</text>
+    <text x="142" y="432" text-anchor="middle" font-size="11" fill="#64748b">由上游多维信号决定“值得试什么”</text>
+
+    <rect x="895" y="392" width="245" height="51" rx="11" fill="#fff" stroke="#ef4444"/>
+    <text x="1017" y="413" text-anchor="middle" font-size="12" fill="#b91c1c">需求验证强度</text>
+    <text x="1017" y="432" text-anchor="middle" font-size="11" fill="#64748b">由真实内容表现决定“实际是否成立”</text>
+  </g>
+</svg>
+</div>
+
+<div class="points">
+  <div class="point"><strong>需求与内容必须建立映射:</strong>否则内容表现无法准确回流到需求。允许一对多和多对多,但要区分主需求与辅助需求。</div>
+  <div class="point"><strong>反馈需要校正:</strong>线上表现同时受内容质量、供给数量、流量和时效影响,不能把一条差内容直接等同于需求无效。</div>
+  <div class="point"><strong>强度是动态状态:</strong>每个周期重新计算,需求可以从新机会变为已验证,也可以因持续表现差被降级或抑制。</div>
+</div>
+
+<div class="formula">
+  最终需求强度 = 先验需求强度 + 经归因校正的线上验证强度 + 低权重关系增益 − 风险抑制
+</div>

+ 152 - 0
visualization/pages/final-output-structure.html

@@ -0,0 +1,152 @@
+<style>
+  .stage { margin-top:18px; padding:16px; border:1px solid #dbe3ec; border-radius:16px; background:#f8fafc; overflow:auto; }
+  .fields { display:grid; grid-template-columns:repeat(2,1fr); gap:12px; margin-top:16px; }
+  .field { padding:14px 15px; background:#fff; border:1px solid #e2e8f0; border-radius:12px; color:#475569; font-size:13px; }
+  .field strong { color:#0f172a; }
+  .decision { margin-top:16px; padding:14px 16px; border-radius:12px; background:#eff6ff; border:1px solid #bfdbfe; color:#1e3a8a; }
+  @media(max-width:900px){.fields{grid-template-columns:1fr}}
+</style>
+
+<h2>设计第 4 部分:最终输出物</h2>
+<p class="subtitle">同一份结果提供两种入口:从主题树看全局结构,从需求清单看具体执行;二者指向同一批需求实体,不维护两套数据。</p>
+
+<div class="stage">
+<svg viewBox="0 0 1160 600" width="100%" style="min-width:980px" role="img" aria-label="最终主题与需求输出结构">
+  <defs>
+    <marker id="g" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#94a3b8"/></marker>
+    <marker id="o" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#f59e0b"/></marker>
+    <marker id="r" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#ef4444"/></marker>
+  </defs>
+
+  <g font-family="sans-serif">
+    <text x="132" y="30" text-anchor="middle" font-size="13" fill="#64748b">主题方向层:几十个</text>
+    <text x="415" y="30" text-anchor="middle" font-size="13" fill="#64748b">意图分层与对象关系</text>
+    <text x="760" y="30" text-anchor="middle" font-size="13" fill="#64748b">平台需求层:数百条</text>
+    <text x="1040" y="30" text-anchor="middle" font-size="13" fill="#64748b">需求证据卡</text>
+  </g>
+
+  <!-- main connections -->
+  <g fill="none" stroke="#94a3b8" stroke-width="2.3" marker-end="url(#g)">
+    <path d="M220 142 L300 111"/><path d="M220 142 L300 206"/>
+    <path d="M220 332 L300 317"/><path d="M220 332 L300 412"/>
+  </g>
+  <g fill="none" stroke="#f59e0b" stroke-width="2.4" marker-end="url(#o)">
+    <path d="M533 111 C590 111 600 106 646 106"/>
+    <path d="M533 206 C590 206 600 204 646 204"/>
+    <path d="M533 317 C590 317 600 319 646 319"/>
+    <path d="M533 412 C590 412 600 420 646 420"/>
+  </g>
+  <g fill="none" stroke="#ef4444" stroke-width="2" stroke-dasharray="6 4" marker-end="url(#r)">
+    <path d="M452 127 C542 150 557 189 646 202"/>
+    <path d="M452 333 C550 360 560 402 646 416"/>
+    <path d="M452 222 C565 257 571 296 646 315"/>
+  </g>
+  <g fill="none" stroke="#8b5cf6" stroke-width="2">
+    <path d="M874 106 L940 106"/><path d="M874 204 L940 204"/><path d="M874 319 L940 319"/><path d="M874 420 L940 420"/>
+  </g>
+
+  <!-- Theme nodes -->
+  <g font-family="sans-serif" text-anchor="middle">
+    <rect x="25" y="86" width="195" height="112" rx="17" fill="#1d4ed8"/>
+    <text x="122" y="116" font-size="12" fill="#bfdbfe">主题 T-01</text>
+    <text x="122" y="143" font-size="17" fill="#fff">毛泽东的历史经历</text>
+    <text x="122" y="169" font-size="11" fill="#dbeafe">需求 46 条 · 已验证 18 条</text>
+    <text x="122" y="186" font-size="11" fill="#dbeafe">数据强度高 · 集中度正常</text>
+
+    <rect x="25" y="276" width="195" height="112" rx="17" fill="#4338ca"/>
+    <text x="122" y="306" font-size="12" fill="#c7d2fe">主题 T-02</text>
+    <text x="122" y="333" font-size="17" fill="#fff">毛泽东的诗词表达</text>
+    <text x="122" y="359" font-size="11" fill="#e0e7ff">需求 31 条 · 新机会 8 条</text>
+    <text x="122" y="376" font-size="11" fill="#e0e7ff">保留探索位 3 条</text>
+  </g>
+
+  <!-- Intent and object groups -->
+  <g font-family="sans-serif" text-anchor="middle">
+    <rect x="300" y="72" width="153" height="78" rx="14" fill="#dbeafe" stroke="#60a5fa"/>
+    <text x="376" y="99" font-size="13" fill="#1d4ed8">意图节点</text>
+    <text x="376" y="124" font-size="15">战争经历</text>
+    <rect x="300" y="167" width="153" height="78" rx="14" fill="#dbeafe" stroke="#60a5fa"/>
+    <text x="376" y="194" font-size="13" fill="#1d4ed8">意图节点</text>
+    <text x="376" y="219" font-size="15">历史背景</text>
+    <rect x="300" y="278" width="153" height="78" rx="14" fill="#dbeafe" stroke="#60a5fa"/>
+    <text x="376" y="305" font-size="13" fill="#1d4ed8">意图节点</text>
+    <text x="376" y="330" font-size="15">作品解读</text>
+    <rect x="300" y="373" width="153" height="78" rx="14" fill="#dbeafe" stroke="#60a5fa"/>
+    <text x="376" y="400" font-size="13" fill="#1d4ed8">意图节点</text>
+    <text x="376" y="425" font-size="15">创作动机</text>
+
+    <circle cx="500" cy="111" r="33" fill="#fff" stroke="#3b82f6"/><text x="500" y="116" font-size="13">毛泽东</text>
+    <circle cx="500" cy="206" r="33" fill="#fff" stroke="#22c55e"/><text x="500" y="211" font-size="12">抗日战争</text>
+    <circle cx="500" cy="317" r="33" fill="#fff" stroke="#d946ef"/><text x="500" y="322" font-size="13">解放战争</text>
+    <circle cx="500" cy="412" r="33" fill="#fff" stroke="#d946ef"/><text x="500" y="417" font-size="13">诗词创作</text>
+  </g>
+
+  <!-- Demand cards -->
+  <g font-family="sans-serif">
+    <rect x="646" y="66" width="228" height="80" rx="14" fill="#fff7ed" stroke="#f59e0b" stroke-width="2"/>
+    <text x="663" y="89" font-size="11" fill="#9a3412">D-001 · 已验证需求 · 强度 88</text>
+    <text x="663" y="113" font-size="14" fill="#0f172a">毛泽东的战争时期经历</text>
+    <text x="663" y="133" font-size="11" fill="#64748b">多挂靠:战争经历、历史背景|对象:毛泽东</text>
+
+    <rect x="646" y="164" width="228" height="80" rx="14" fill="#fff7ed" stroke="#f59e0b" stroke-width="2"/>
+    <text x="663" y="187" font-size="11" fill="#9a3412">D-002 · 增长需求 · 强度 81</text>
+    <text x="663" y="211" font-size="14" fill="#0f172a">抗日战争如何影响毛泽东创作</text>
+    <text x="663" y="231" font-size="11" fill="#64748b">多挂靠:抗日战争、作品表达、诗词创作</text>
+
+    <rect x="646" y="279" width="228" height="80" rx="14" fill="#fff7ed" stroke="#f59e0b" stroke-width="2"/>
+    <text x="663" y="302" font-size="11" fill="#9a3412">D-003 · 新机会 · 强度 69</text>
+    <text x="663" y="326" font-size="14" fill="#0f172a">战争诗词如何表达历史判断</text>
+    <text x="663" y="346" font-size="11" fill="#64748b">多挂靠:作品解读、家国表达|线上样本不足</text>
+
+    <rect x="646" y="380" width="228" height="80" rx="14" fill="#fff7ed" stroke="#f59e0b" stroke-width="2"/>
+    <text x="663" y="403" font-size="11" fill="#9a3412">D-004 · 待验证 · 强度 66</text>
+    <text x="663" y="427" font-size="14" fill="#0f172a">毛泽东为何在战争时期持续写诗</text>
+    <text x="663" y="447" font-size="11" fill="#64748b">多挂靠:创作动机、战争经历|已安排内容承接</text>
+  </g>
+
+  <!-- Evidence cards -->
+  <g font-family="sans-serif">
+    <rect x="940" y="57" width="195" height="98" rx="14" fill="#fff" stroke="#8b5cf6"/>
+    <text x="957" y="80" font-size="12" fill="#6d28d9">证据摘要</text>
+    <text x="957" y="101" font-size="11" fill="#334155">先验 76 · 后验 91</text>
+    <text x="957" y="119" font-size="11" fill="#334155">关系增益 4 · 风险 2</text>
+    <text x="957" y="137" font-size="10" fill="#64748b">关联内容 23 条 · 3 个周期验证</text>
+
+    <rect x="940" y="155" width="195" height="98" rx="14" fill="#fff" stroke="#8b5cf6"/>
+    <text x="957" y="178" font-size="12" fill="#6d28d9">证据摘要</text>
+    <text x="957" y="199" font-size="11" fill="#334155">外部热度高 · 近期缺口中</text>
+    <text x="957" y="217" font-size="11" fill="#334155">真实 VOV 连续上升</text>
+    <text x="957" y="235" font-size="10" fill="#64748b">原始关联 8 条 · 推断关系 2 条</text>
+
+    <rect x="940" y="270" width="195" height="98" rx="14" fill="#fff" stroke="#8b5cf6"/>
+    <text x="957" y="293" font-size="12" fill="#6d28d9">证据摘要</text>
+    <text x="957" y="314" font-size="11" fill="#334155">先验 72 · 后验暂无</text>
+    <text x="957" y="332" font-size="11" fill="#334155">探索位:是</text>
+    <text x="957" y="350" font-size="10" fill="#64748b">禁止将“无后验”视为负反馈</text>
+
+    <rect x="940" y="371" width="195" height="98" rx="14" fill="#fff" stroke="#8b5cf6"/>
+    <text x="957" y="394" font-size="12" fill="#6d28d9">内容承接状态</text>
+    <text x="957" y="415" font-size="11" fill="#334155">待生产 5 条 · 已上线 2 条</text>
+    <text x="957" y="433" font-size="11" fill="#334155">有效样本尚未形成</text>
+    <text x="957" y="451" font-size="10" fill="#64748b">下一周期继续观察</text>
+  </g>
+
+  <!-- relation explanation -->
+  <g font-family="sans-serif">
+    <rect x="270" y="500" width="620" height="58" rx="12" fill="#fff" stroke="#e2e8f0"/>
+    <text x="580" y="522" text-anchor="middle" font-size="12" fill="#334155">树内父子关系决定层级与覆盖;需求多挂靠表达跨分支语义;红色虚线展示节点交织。</text>
+    <text x="580" y="542" text-anchor="middle" font-size="12" fill="#64748b">点击任一需求,应能追溯上游信号、对象关系、关联内容、线上表现及历次强度变化。</text>
+  </g>
+</svg>
+</div>
+
+<div class="fields">
+  <div class="field"><strong>主题方向包含:</strong>主题名称、覆盖意图、需求数量、状态分布、整体强度、数据来源构成、集中度与趋势。</div>
+  <div class="field"><strong>平台需求包含:</strong>唯一名称、多个挂靠节点、关联对象、原始关系、推断关系、生命周期、当前强度和解释。</div>
+  <div class="field"><strong>证据卡包含:</strong>各正交先验维度、线上验证、样本量、关联内容、关系增益、风险项和更新时间。</div>
+  <div class="field"><strong>可追溯链路:</strong>需求必须能回看来源数据、为何合并、挂树理由、内容承接、线上表现和强度变化历史。</div>
+</div>
+
+<div class="decision">
+  请确认最终结果采用“同一数据、两个入口”:既可沿主题树和关系图浏览,也可按排序后的需求清单直接用于业务执行。
+</div>

+ 164 - 0
visualization/pages/graph-structure-left-to-right.html

@@ -0,0 +1,164 @@
+<style>
+  .stage {
+    margin-top: 18px;
+    padding: 14px;
+    border: 1px solid #dbe3ec;
+    border-radius: 16px;
+    background: #f8fafc;
+    overflow-x: auto;
+  }
+  .notes {
+    display: grid;
+    grid-template-columns: repeat(3, 1fr);
+    gap: 12px;
+    margin-top: 14px;
+  }
+  .note {
+    padding: 12px 14px;
+    border-radius: 12px;
+    background: #fff;
+    border: 1px solid #e2e8f0;
+    color: #475569;
+    font-size: 13px;
+  }
+  .note strong { color: #0f172a; }
+  .question {
+    margin-top: 16px;
+    padding: 14px 16px;
+    border-radius: 12px;
+    background: #eff6ff;
+    border: 1px solid #bfdbfe;
+    color: #1e3a8a;
+  }
+  @media (max-width: 900px) {
+    .notes { grid-template-columns: 1fr; }
+  }
+</style>
+
+<h2>确认后的结构:从左到右阅读</h2>
+<p class="subtitle">树提供稳定坐标,关系线表达交织,平台需求是最终可执行输出;同一需求汇聚多种正交证据,但业务实体只保留一条。</p>
+
+<div class="stage">
+  <svg viewBox="0 0 1160 500" width="100%" style="min-width:980px" role="img" aria-label="从左到右的树骨架关系图">
+    <defs>
+      <marker id="grayArrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 z" fill="#94a3b8"/></marker>
+      <marker id="redArrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 z" fill="#ef4444"/></marker>
+      <marker id="orangeArrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 z" fill="#f59e0b"/></marker>
+    </defs>
+
+    <g font-family="sans-serif">
+      <text x="70" y="30" text-anchor="middle" font-size="12" fill="#64748b">稳定主题方向</text>
+      <text x="330" y="30" text-anchor="middle" font-size="12" fill="#64748b">分类树骨架</text>
+      <text x="625" y="30" text-anchor="middle" font-size="12" fill="#64748b">具体业务节点与关系</text>
+      <text x="910" y="30" text-anchor="middle" font-size="12" fill="#64748b">平台需求输出</text>
+      <text x="1082" y="30" text-anchor="middle" font-size="12" fill="#64748b">正交证据</text>
+    </g>
+
+    <!-- Main left-to-right flow -->
+    <g fill="none" stroke="#94a3b8" stroke-width="2.2" marker-end="url(#grayArrow)">
+      <path d="M142 244 C180 244 190 116 238 116"/>
+      <path d="M142 244 C180 244 190 244 238 244"/>
+      <path d="M142 244 C180 244 190 372 238 372"/>
+      <path d="M402 116 L485 116"/>
+      <path d="M402 244 L485 244"/>
+      <path d="M402 372 L485 372"/>
+    </g>
+
+    <!-- Tree branches -->
+    <g fill="none" stroke="#94a3b8" stroke-width="2">
+      <path d="M570 116 L610 86 M570 116 L610 146"/>
+      <path d="M570 244 L610 214 M570 244 L610 274"/>
+      <path d="M570 372 L610 342 M570 372 L610 402"/>
+    </g>
+
+    <!-- Cross-tree semantic relationships -->
+    <g fill="none" stroke="#ef4444" stroke-width="2.2" stroke-dasharray="7 5" marker-end="url(#redArrow)">
+      <path d="M702 146 C735 158 730 203 702 214"/>
+      <path d="M702 214 C755 230 755 327 702 342"/>
+      <path d="M702 146 C790 178 793 320 702 342"/>
+      <path d="M702 274 C746 292 746 382 702 402"/>
+    </g>
+
+    <!-- Nodes to demands -->
+    <g fill="none" stroke="#f59e0b" stroke-width="2.4" marker-end="url(#orangeArrow)">
+      <path d="M704 146 C770 146 779 142 822 142"/>
+      <path d="M704 214 C762 205 782 168 822 154"/>
+      <path d="M704 342 C775 317 778 181 822 163"/>
+      <path d="M704 146 C772 190 778 293 822 315"/>
+      <path d="M704 274 C770 278 780 310 822 326"/>
+      <path d="M704 402 C765 390 779 350 822 338"/>
+    </g>
+
+    <!-- Evidence links -->
+    <g fill="none" stroke="#8b5cf6" stroke-width="1.7" stroke-dasharray="4 4">
+      <path d="M988 142 L1027 100"/><path d="M988 142 L1027 164"/><path d="M988 142 L1027 228"/>
+      <path d="M988 326 L1027 292"/><path d="M988 326 L1027 356"/><path d="M988 326 L1027 420"/>
+    </g>
+
+    <!-- Stable direction -->
+    <rect x="18" y="210" width="124" height="68" rx="16" fill="#1d4ed8"/>
+    <text x="80" y="239" text-anchor="middle" font-family="sans-serif" font-size="15" fill="#fff">近现代历史人物</text>
+    <text x="80" y="260" text-anchor="middle" font-family="sans-serif" font-size="15" fill="#fff">兴趣方向</text>
+
+    <!-- Trees -->
+    <g font-family="sans-serif" font-size="14" text-anchor="middle">
+      <rect x="238" y="88" width="164" height="56" rx="14" fill="#dbeafe" stroke="#60a5fa"/><text x="320" y="121">全局树 · 人物分支</text>
+      <rect x="238" y="216" width="164" height="56" rx="14" fill="#dcfce7" stroke="#4ade80"/><text x="320" y="249">全局树 · 事件分支</text>
+      <rect x="238" y="344" width="164" height="56" rx="14" fill="#fae8ff" stroke="#d946ef"/><text x="320" y="377">全局树 · 表达分支</text>
+
+      <rect x="485" y="91" width="85" height="50" rx="12" fill="#eff6ff" stroke="#60a5fa"/><text x="528" y="121">历史人物</text>
+      <rect x="485" y="219" width="85" height="50" rx="12" fill="#f0fdf4" stroke="#4ade80"/><text x="528" y="249">人生事件</text>
+      <rect x="485" y="347" width="85" height="50" rx="12" fill="#fdf4ff" stroke="#d946ef"/><text x="528" y="377">人物情感</text>
+    </g>
+
+    <!-- Concrete nodes -->
+    <g font-family="sans-serif" font-size="13" text-anchor="middle">
+      <rect x="610" y="66" width="94" height="40" rx="10" fill="#fff" stroke="#60a5fa"/><text x="657" y="91">毛泽东</text>
+      <rect x="610" y="126" width="94" height="40" rx="10" fill="#fff" stroke="#60a5fa"/><text x="657" y="151">朱德</text>
+      <rect x="610" y="194" width="94" height="40" rx="10" fill="#fff" stroke="#4ade80"/><text x="657" y="219">抗日战争</text>
+      <rect x="610" y="254" width="94" height="40" rx="10" fill="#fff" stroke="#4ade80"/><text x="657" y="279">解放战争</text>
+      <rect x="610" y="322" width="94" height="40" rx="10" fill="#fff" stroke="#d946ef"/><text x="657" y="347">家国情怀</text>
+      <rect x="610" y="382" width="94" height="40" rx="10" fill="#fff" stroke="#d946ef"/><text x="657" y="407">诗词创作</text>
+    </g>
+
+    <!-- Demand nodes -->
+    <g font-family="sans-serif" text-anchor="middle">
+      <rect x="822" y="105" width="166" height="74" rx="14" fill="#fff7ed" stroke="#f59e0b" stroke-width="2"/>
+      <text x="905" y="130" font-size="13" fill="#9a3412">平台需求 001</text>
+      <text x="905" y="150" font-size="14">毛泽东的</text>
+      <text x="905" y="168" font-size="14">战争时期经历</text>
+
+      <rect x="822" y="289" width="166" height="74" rx="14" fill="#fff7ed" stroke="#f59e0b" stroke-width="2"/>
+      <text x="905" y="314" font-size="13" fill="#9a3412">平台需求 002</text>
+      <text x="905" y="334" font-size="14">毛泽东为何在战争中</text>
+      <text x="905" y="352" font-size="14">持续进行诗词创作</text>
+    </g>
+
+    <!-- Evidence nodes -->
+    <g font-family="sans-serif" font-size="11" text-anchor="middle">
+      <rect x="1027" y="82" width="108" height="36" rx="18" fill="#ede9fe" stroke="#8b5cf6"/><text x="1081" y="104">外部热度</text>
+      <rect x="1027" y="146" width="108" height="36" rx="18" fill="#ede9fe" stroke="#8b5cf6"/><text x="1081" y="168">平台近期缺口</text>
+      <rect x="1027" y="210" width="108" height="36" rx="18" fill="#ede9fe" stroke="#8b5cf6"/><text x="1081" y="232">后验效果</text>
+      <rect x="1027" y="274" width="108" height="36" rx="18" fill="#ede9fe" stroke="#8b5cf6"/><text x="1081" y="296">长期持续性</text>
+      <rect x="1027" y="338" width="108" height="36" rx="18" fill="#ede9fe" stroke="#8b5cf6"/><text x="1081" y="360">去年同期</text>
+      <rect x="1027" y="402" width="108" height="36" rx="18" fill="#ede9fe" stroke="#8b5cf6"/><text x="1081" y="424">真实 ROV/VOV</text>
+    </g>
+
+    <!-- Edge labels -->
+    <g font-family="sans-serif" font-size="10" fill="#b91c1c">
+      <text x="720" y="177">经历</text>
+      <text x="739" y="279">引发</text>
+      <text x="765" y="231">人物—事件—情感</text>
+    </g>
+  </svg>
+</div>
+
+<div class="notes">
+  <div class="note"><strong>多挂靠:</strong>每条平台需求可以连接多个正式分类节点;统计去重需要时另设统计主归属。</div>
+  <div class="note"><strong>关系线:</strong>同一需求可连接多个人物、事件、情感等节点;节点之间也保留“经历、引发、关联”等语义边。</div>
+  <div class="note"><strong>证据合并:</strong>内外部、时间周期、供需缺口和后验结果不拆成重复需求,而是作为同一需求的独立证据。</div>
+</div>
+
+<div class="question">
+  已确认:平台需求以多挂靠表达跨分支语义,人物、战争事件、诗词创作和家国表达可以共同参与定义需求。
+</div>

+ 198 - 0
visualization/pages/graph-structure-options.html

@@ -0,0 +1,198 @@
+<style>
+  .diagram-grid {
+    display: grid;
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+    gap: 16px;
+    margin-top: 20px;
+  }
+  .graph-card {
+    border: 1px solid #dbe3ec;
+    border-radius: 16px;
+    background: #fff;
+    overflow: hidden;
+    cursor: pointer;
+    transition: .18s ease;
+  }
+  .graph-card:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 12px 30px rgba(15, 23, 42, .10);
+  }
+  .graph-card.selected {
+    border-color: #2563eb;
+    box-shadow: 0 0 0 3px rgba(37, 99, 235, .13);
+  }
+  .graph-head {
+    padding: 15px 16px 10px;
+    border-bottom: 1px solid #eef2f7;
+  }
+  .graph-head h3 { margin: 0 0 5px; }
+  .graph-head p { margin: 0; color: #64748b; font-size: 13px; }
+  .canvas { padding: 12px; background: #f8fafc; }
+  .graph-body { padding: 13px 16px 16px; }
+  .graph-body ul { margin: 8px 0 0; padding-left: 18px; color: #475569; font-size: 13px; }
+  .recommend {
+    display: inline-block;
+    margin-left: 7px;
+    padding: 2px 7px;
+    border-radius: 999px;
+    color: #1d4ed8;
+    background: #dbeafe;
+    font-size: 11px;
+    vertical-align: 2px;
+  }
+  .legend {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 12px;
+    margin-top: 16px;
+    padding: 12px 14px;
+    border-radius: 12px;
+    background: #f8fafc;
+    color: #475569;
+    font-size: 12px;
+  }
+  .dot { width: 10px; height: 10px; display: inline-block; border-radius: 50%; margin-right: 5px; }
+  @media (max-width: 1000px) {
+    .diagram-grid { grid-template-columns: 1fr; }
+  }
+</style>
+
+<h2>平台需求应该怎样组织成“一张图”?</h2>
+<p class="subtitle">以“对近现代历史人物感兴趣”为例。点击一个方案;重点比较树骨架、跨节点连线和最终需求的表达能力。</p>
+
+<div class="diagram-grid">
+  <div class="graph-card" data-choice="a-tree-only" onclick="toggleSelect(this)">
+    <div class="graph-head">
+      <h3>A. 单一大树</h3>
+      <p>所有对象都强行放进一棵层级树</p>
+    </div>
+    <div class="canvas">
+      <svg viewBox="0 0 320 300" width="100%" role="img" aria-label="单一大树方案">
+        <g stroke="#94a3b8" stroke-width="2" fill="none">
+          <path d="M160 38 L160 72 M160 72 L66 108 M160 72 L160 108 M160 72 L254 108"/>
+          <path d="M66 136 L45 174 M66 136 L100 174"/>
+          <path d="M160 136 L145 174 M160 136 L190 174"/>
+          <path d="M254 136 L235 174 M254 136 L282 174"/>
+          <path d="M45 202 L45 238 M145 202 L145 238 M235 202 L235 238"/>
+        </g>
+        <g font-family="sans-serif" font-size="12" text-anchor="middle">
+          <rect x="110" y="10" width="100" height="30" rx="9" fill="#1d4ed8"/><text x="160" y="30" fill="white">毛泽东历史兴趣</text>
+          <rect x="23" y="108" width="86" height="30" rx="8" fill="#dbeafe"/><text x="66" y="128">具体人物</text>
+          <rect x="117" y="108" width="86" height="30" rx="8" fill="#dcfce7"/><text x="160" y="128">人物事件</text>
+          <rect x="211" y="108" width="86" height="30" rx="8" fill="#fae8ff"/><text x="254" y="128">人物情感</text>
+          <rect x="10" y="174" width="70" height="28" rx="8" fill="white" stroke="#60a5fa"/><text x="45" y="193">毛泽东</text>
+          <rect x="82" y="174" width="70" height="28" rx="8" fill="white" stroke="#60a5fa"/><text x="117" y="193">朱德</text>
+          <rect x="110" y="174" width="70" height="28" rx="8" fill="white" stroke="#4ade80"/><text x="145" y="193">抗日战争</text>
+          <rect x="182" y="174" width="70" height="28" rx="8" fill="white" stroke="#4ade80"/><text x="217" y="193">解放战争</text>
+          <rect x="200" y="174" width="70" height="28" rx="8" fill="white" stroke="#d946ef"/><text x="235" y="193">家国情怀</text>
+          <rect x="272" y="174" width="42" height="28" rx="8" fill="white" stroke="#d946ef"/><text x="293" y="193">乐观</text>
+          <rect x="9" y="238" width="72" height="34" rx="8" fill="#fff7ed" stroke="#fb923c"/><text x="45" y="252">毛泽东的</text><text x="45" y="266">战争经历</text>
+          <rect x="102" y="238" width="86" height="34" rx="8" fill="#fff7ed" stroke="#fb923c"/><text x="145" y="252">抗战诗词的</text><text x="145" y="266">历史背景</text>
+          <rect x="196" y="238" width="78" height="34" rx="8" fill="#fff7ed" stroke="#fb923c"/><text x="235" y="252">毛泽东为何</text><text x="235" y="266">坚持写诗</text>
+        </g>
+      </svg>
+    </div>
+    <div class="graph-body">
+      <strong>优点:</strong>层级清楚,统计方便。
+      <ul>
+        <li>一个事件只能被迫选一个父节点</li>
+        <li>“毛泽东—抗日战争—诗词创作”之间的真实联系会丢失</li>
+      </ul>
+    </div>
+  </div>
+
+  <div class="graph-card" data-choice="b-free-graph" onclick="toggleSelect(this)">
+    <div class="graph-head">
+      <h3>B. 完全自由图</h3>
+      <p>人物、事件、情感和需求全部自由连接</p>
+    </div>
+    <div class="canvas">
+      <svg viewBox="0 0 320 300" width="100%" role="img" aria-label="完全自由图方案">
+        <defs>
+          <marker id="arrowB" markerWidth="7" markerHeight="7" refX="6" refY="3.5" orient="auto"><path d="M0,0 L7,3.5 L0,7 z" fill="#64748b"/></marker>
+        </defs>
+        <g stroke="#64748b" stroke-width="1.8" fill="none" marker-end="url(#arrowB)">
+          <path d="M74 75 L148 74"/><path d="M171 90 L112 145"/><path d="M192 91 L241 142"/>
+          <path d="M91 169 L57 222"/><path d="M128 172 L169 222"/><path d="M226 169 L182 223"/>
+          <path d="M231 163 C286 174 289 230 244 245"/><path d="M68 58 C114 18 214 21 243 55"/>
+          <path d="M83 75 C44 112 43 176 57 221"/>
+        </g>
+        <g font-family="sans-serif" font-size="12" text-anchor="middle">
+          <circle cx="62" cy="68" r="35" fill="#dbeafe" stroke="#3b82f6"/><text x="62" y="72">毛泽东</text>
+          <circle cx="177" cy="70" r="39" fill="#dcfce7" stroke="#22c55e"/><text x="177" y="67">抗日</text><text x="177" y="82">战争</text>
+          <circle cx="255" cy="67" r="31" fill="#fae8ff" stroke="#d946ef"/><text x="255" y="71">家国</text>
+          <circle cx="100" cy="160" r="35" fill="#fae8ff" stroke="#d946ef"/><text x="100" y="164">诗词创作</text>
+          <circle cx="246" cy="158" r="37" fill="#dcfce7" stroke="#22c55e"/><text x="246" y="155">解放</text><text x="246" y="170">战争</text>
+          <rect x="19" y="222" width="83" height="42" rx="10" fill="#fff7ed" stroke="#fb923c"/><text x="60" y="240">毛泽东的</text><text x="60" y="255">战争经历</text>
+          <rect x="128" y="222" width="92" height="42" rx="10" fill="#fff7ed" stroke="#fb923c"/><text x="174" y="240">毛泽东为何</text><text x="174" y="255">坚持写诗</text>
+          <rect x="230" y="230" width="77" height="36" rx="10" fill="#fff7ed" stroke="#fb923c"/><text x="268" y="245">解放战争与</text><text x="268" y="258">诗词主题</text>
+        </g>
+      </svg>
+    </div>
+    <div class="graph-body">
+      <strong>优点:</strong>关系表达最自由。
+      <ul>
+        <li>缺少统一骨架,数百条需求容易变成“蜘蛛网”</li>
+        <li>难以保证每条需求都有稳定归属和完整覆盖</li>
+      </ul>
+    </div>
+  </div>
+
+  <div class="graph-card" data-choice="c-tree-backed-graph" onclick="toggleSelect(this)">
+    <div class="graph-head">
+      <h3>C. 树骨架上的关系图 <span class="recommend">推荐</span></h3>
+      <p>分类树负责归属;关系线负责交织;需求节点负责输出</p>
+    </div>
+    <div class="canvas">
+      <svg viewBox="0 0 320 300" width="100%" role="img" aria-label="树骨架上的关系图方案">
+        <defs>
+          <marker id="arrowC" markerWidth="7" markerHeight="7" refX="6" refY="3.5" orient="auto"><path d="M0,0 L7,3.5 L0,7 z" fill="#ef4444"/></marker>
+        </defs>
+        <g stroke="#94a3b8" stroke-width="2" fill="none">
+          <path d="M160 36 L160 66 M160 66 L58 101 M160 66 L160 101 M160 66 L262 101"/>
+          <path d="M58 129 L40 166 M58 129 L91 166"/>
+          <path d="M160 129 L144 166 M160 129 L193 166"/>
+          <path d="M262 129 L241 166 M262 129 L291 166"/>
+        </g>
+        <g stroke="#ef4444" stroke-width="2" stroke-dasharray="5 4" fill="none" marker-end="url(#arrowC)">
+          <path d="M92 180 C115 145 122 143 144 180"/>
+          <path d="M167 180 C196 139 217 140 241 180"/>
+          <path d="M91 192 C131 224 188 221 238 192"/>
+        </g>
+        <g stroke="#f59e0b" stroke-width="2.2" fill="none">
+          <path d="M91 194 L91 237"/><path d="M145 194 L159 237"/><path d="M241 194 L211 237"/>
+        </g>
+        <g font-family="sans-serif" font-size="12" text-anchor="middle">
+          <rect x="105" y="8" width="110" height="30" rx="9" fill="#1d4ed8"/><text x="160" y="28" fill="white">毛泽东历史主题</text>
+          <rect x="17" y="101" width="82" height="30" rx="8" fill="#dbeafe"/><text x="58" y="121">人物分支</text>
+          <rect x="119" y="101" width="82" height="30" rx="8" fill="#dcfce7"/><text x="160" y="121">事件分支</text>
+          <rect x="221" y="101" width="82" height="30" rx="8" fill="#fae8ff"/><text x="262" y="121">表达分支</text>
+          <rect x="9" y="166" width="62" height="28" rx="8" fill="white" stroke="#60a5fa"/><text x="40" y="185">毛泽东</text>
+          <rect x="61" y="166" width="62" height="28" rx="8" fill="white" stroke="#60a5fa"/><text x="92" y="185">朱德</text>
+          <rect x="112" y="166" width="66" height="28" rx="8" fill="white" stroke="#4ade80"/><text x="145" y="185">抗日战争</text>
+          <rect x="180" y="166" width="66" height="28" rx="8" fill="white" stroke="#4ade80"/><text x="213" y="185">解放战争</text>
+          <rect x="213" y="166" width="56" height="28" rx="8" fill="white" stroke="#d946ef"/><text x="241" y="185">写诗</text>
+          <rect x="271" y="166" width="42" height="28" rx="8" fill="white" stroke="#d946ef"/><text x="292" y="185">家国</text>
+          <rect x="43" y="237" width="96" height="44" rx="10" fill="#fff7ed" stroke="#f59e0b"/><text x="91" y="255">毛泽东的</text><text x="91" y="270">战争时期经历</text>
+          <rect x="143" y="237" width="111" height="44" rx="10" fill="#fff7ed" stroke="#f59e0b"/><text x="198" y="255">毛泽东为何在战争中</text><text x="198" y="270">持续进行诗词创作</text>
+        </g>
+      </svg>
+    </div>
+    <div class="graph-body">
+      <strong>核心:</strong>一个需求可以挂靠多个正式树节点,并连接人物、事件、表达和证据元素。
+      <ul>
+        <li>保留树的分层、覆盖与统计能力</li>
+        <li>保留跨分支关系线,允许一条需求表达多个对象的组合</li>
+      </ul>
+    </div>
+  </div>
+</div>
+
+<div class="legend">
+  <span><i class="dot" style="background:#60a5fa"></i>人物节点</span>
+  <span><i class="dot" style="background:#4ade80"></i>事件节点</span>
+  <span><i class="dot" style="background:#d946ef"></i>情感节点</span>
+  <span><i class="dot" style="background:#f59e0b"></i>平台需求节点</span>
+  <span style="color:#ef4444">- - 红色虚线:跨分支语义关系</span>
+  <span style="color:#64748b">灰色实线:树内父子关系</span>
+</div>

+ 226 - 0
visualization/pages/node-drilldown-evidence-chain.html

@@ -0,0 +1,226 @@
+<style>
+  .stage {
+    margin-top: 18px;
+    padding: 16px;
+    border: 1px solid #dbe3ec;
+    border-radius: 16px;
+    background: #f8fafc;
+    overflow-x: auto;
+  }
+  .rules {
+    display: grid;
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+    gap: 12px;
+    margin-top: 16px;
+  }
+  .rule {
+    padding: 13px 14px;
+    border: 1px solid #e2e8f0;
+    border-radius: 12px;
+    background: #fff;
+    color: #475569;
+    font-size: 13px;
+  }
+  .rule strong { color: #0f172a; }
+  .summary {
+    margin-top: 16px;
+    padding: 14px 16px;
+    border: 1px solid #bfdbfe;
+    border-radius: 12px;
+    background: #eff6ff;
+    color: #1e3a8a;
+  }
+  @media (max-width: 900px) {
+    .rules { grid-template-columns: 1fr; }
+  }
+</style>
+
+<h2>分类节点继续下钻:元素—视频—语言—讨论—需求</h2>
+<p class="subtitle">
+  正式分类树到分类节点为止;节点下方挂载可持续扩展的证据子图。用户从树上继续下卷时,可以逐层看到具体元素、真实视频、明确观点和长段讨论。
+</p>
+
+<div class="stage">
+<svg viewBox="0 0 1450 720" width="100%" style="min-width:1180px" role="img" aria-label="分类节点下钻证据链">
+  <defs>
+    <marker id="grayA" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#94a3b8"/></marker>
+    <marker id="blueA" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#2563eb"/></marker>
+    <marker id="greenA" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#16a34a"/></marker>
+    <marker id="purpleA" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#7c3aed"/></marker>
+    <marker id="orangeA" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8z" fill="#f59e0b"/></marker>
+  </defs>
+
+  <!-- headings -->
+  <g font-family="sans-serif" fill="#64748b" font-size="13" text-anchor="middle">
+    <text x="112" y="30">全局分类树</text>
+    <text x="345" y="30">分类节点详情</text>
+    <text x="590" y="30">细节元素</text>
+    <text x="825" y="30">真实视频实例</text>
+    <text x="1060" y="30">语言提取</text>
+    <text x="1320" y="30">平台需求</text>
+  </g>
+
+  <!-- left-to-right primary flow -->
+  <g fill="none" stroke="#94a3b8" stroke-width="2.4" marker-end="url(#grayA)">
+    <path d="M190 170 L260 170"/>
+    <path d="M430 170 L500 170"/>
+    <path d="M680 170 L735 170"/>
+    <path d="M920 170 L970 170"/>
+  </g>
+  <g fill="none" stroke="#f59e0b" stroke-width="2.8" marker-end="url(#orangeA)">
+    <path d="M1160 135 C1210 122 1235 115 1260 115"/>
+    <path d="M1160 205 C1210 228 1235 245 1260 255"/>
+    <path d="M1160 485 C1210 447 1230 405 1260 390"/>
+  </g>
+
+  <!-- classification tree -->
+  <g fill="none" stroke="#94a3b8" stroke-width="2">
+    <path d="M105 92 L105 120 M105 120 L62 148 M105 120 L148 148"/>
+    <path d="M148 188 L148 222 M148 222 L108 254 M148 222 L188 254"/>
+  </g>
+  <g font-family="sans-serif" text-anchor="middle">
+    <rect x="55" y="58" width="100" height="36" rx="10" fill="#1d4ed8"/><text x="105" y="81" font-size="13" fill="#fff">知识</text>
+    <rect x="20" y="148" width="84" height="40" rx="9" fill="#dbeafe" stroke="#60a5fa"/><text x="62" y="173" font-size="13">历史</text>
+    <rect x="106" y="148" width="84" height="40" rx="9" fill="#dbeafe" stroke="#60a5fa"/><text x="148" y="173" font-size="13">人物故事</text>
+    <rect x="66" y="254" width="84" height="40" rx="9" fill="#fff" stroke="#60a5fa"/><text x="108" y="279" font-size="12">个人经历</text>
+    <rect x="152" y="254" width="84" height="40" rx="9" fill="#fff" stroke="#60a5fa"/><text x="194" y="279" font-size="12">名人故事</text>
+  </g>
+
+  <!-- category detail -->
+  <g font-family="sans-serif">
+    <rect x="260" y="70" width="170" height="200" rx="16" fill="#fff" stroke="#3b82f6" stroke-width="2"/>
+    <text x="280" y="99" font-size="12" fill="#1d4ed8">正式分类节点</text>
+    <text x="280" y="128" font-size="18" fill="#0f172a">人物故事</text>
+    <text x="280" y="153" font-size="11" fill="#64748b">路径:事件 / 社会事件 / 人物故事</text>
+    <line x1="280" y1="170" x2="410" y2="170" stroke="#e2e8f0"/>
+    <text x="280" y="194" font-size="12" fill="#334155">下挂元素:137</text>
+    <text x="280" y="216" font-size="12" fill="#334155">关联视频:2,438</text>
+    <text x="280" y="238" font-size="12" fill="#334155">已提取需求:46</text>
+    <text x="280" y="258" font-size="10" fill="#64748b">节点统计由下方证据逐层汇总</text>
+  </g>
+
+  <!-- element nodes -->
+  <g fill="none" stroke="#2563eb" stroke-width="2.2" marker-end="url(#blueA)">
+    <path d="M430 130 C470 116 480 99 500 94"/>
+    <path d="M430 160 L500 160"/>
+    <path d="M430 195 C470 211 480 228 500 232"/>
+    <path d="M430 225 C470 255 480 300 500 310"/>
+  </g>
+  <g font-family="sans-serif" text-anchor="middle">
+    <rect x="500" y="68" width="180" height="52" rx="12" fill="#eff6ff" stroke="#60a5fa"/>
+    <text x="590" y="90" font-size="12" fill="#1d4ed8">人物元素</text><text x="590" y="108" font-size="14">毛泽东</text>
+    <rect x="500" y="134" width="180" height="52" rx="12" fill="#eff6ff" stroke="#60a5fa"/>
+    <text x="590" y="156" font-size="12" fill="#1d4ed8">事件元素</text><text x="590" y="174" font-size="14">抗日战争</text>
+    <rect x="500" y="206" width="180" height="52" rx="12" fill="#eff6ff" stroke="#60a5fa"/>
+    <text x="590" y="228" font-size="12" fill="#1d4ed8">事件元素</text><text x="590" y="246" font-size="14">解放战争</text>
+    <rect x="500" y="284" width="180" height="52" rx="12" fill="#eff6ff" stroke="#60a5fa"/>
+    <text x="590" y="306" font-size="12" fill="#1d4ed8">行为元素</text><text x="590" y="324" font-size="14">写诗 / 诗词创作</text>
+  </g>
+
+  <!-- element relations -->
+  <g fill="none" stroke="#ef4444" stroke-width="1.9" stroke-dasharray="6 4">
+    <path d="M590 120 L590 134"/>
+    <path d="M620 120 C685 145 690 205 635 226"/>
+    <path d="M590 258 L590 284"/>
+  </g>
+  <g font-family="sans-serif" fill="#b91c1c" font-size="10">
+    <text x="600" y="131">关联</text>
+    <text x="676" y="176">推断关系需说明</text>
+    <text x="600" y="278">关联</text>
+  </g>
+
+  <!-- video instances -->
+  <g fill="none" stroke="#16a34a" stroke-width="2.2" marker-end="url(#greenA)">
+    <path d="M680 94 C715 94 715 100 735 100"/>
+    <path d="M680 160 C715 160 715 170 735 170"/>
+    <path d="M680 232 C715 232 715 240 735 240"/>
+    <path d="M680 310 C715 310 715 310 735 310"/>
+  </g>
+  <g font-family="sans-serif">
+    <rect x="735" y="66" width="185" height="68" rx="13" fill="#f0fdf4" stroke="#22c55e"/>
+    <text x="750" y="89" font-size="11" fill="#15803d">视频 V-0182</text>
+    <text x="750" y="109" font-size="12">《沁园春·雪》的时代背景</text>
+    <text x="750" y="125" font-size="10" fill="#64748b">命中:毛泽东、写诗、历史背景</text>
+    <rect x="735" y="142" width="185" height="68" rx="13" fill="#f0fdf4" stroke="#22c55e"/>
+    <text x="750" y="165" font-size="11" fill="#15803d">视频 V-0276</text>
+    <text x="750" y="185" font-size="12">抗战时期毛泽东诗词赏析</text>
+    <text x="750" y="201" font-size="10" fill="#64748b">真实播放与互动表现可回流</text>
+    <rect x="735" y="214" width="185" height="68" rx="13" fill="#f0fdf4" stroke="#22c55e"/>
+    <text x="750" y="237" font-size="11" fill="#15803d">视频 V-0413</text>
+    <text x="750" y="257" font-size="12">解放战争时期诗词主题变化</text>
+    <text x="750" y="273" font-size="10" fill="#64748b">命中:解放战争、作品解读</text>
+    <rect x="735" y="286" width="185" height="68" rx="13" fill="#f0fdf4" stroke="#22c55e"/>
+    <text x="750" y="309" font-size="11" fill="#15803d">视频 V-0655</text>
+    <text x="750" y="329" font-size="12">毛泽东为何在战争中写诗</text>
+    <text x="750" y="345" font-size="10" fill="#64748b">可展开原文、转写与评论</text>
+  </g>
+
+  <!-- language extraction -->
+  <g fill="none" stroke="#7c3aed" stroke-width="2.2" marker-end="url(#purpleA)">
+    <path d="M920 100 C950 100 950 115 970 115"/>
+    <path d="M920 170 C950 170 950 185 970 185"/>
+    <path d="M920 240 C950 265 950 410 970 445"/>
+    <path d="M920 320 C950 340 950 455 970 485"/>
+  </g>
+  <g font-family="sans-serif">
+    <rect x="970" y="72" width="190" height="86" rx="13" fill="#faf5ff" stroke="#a855f7"/>
+    <text x="987" y="96" font-size="11" fill="#7e22ce">明确语言观点</text>
+    <text x="987" y="119" font-size="13">战争环境并未中断诗词创作,</text>
+    <text x="987" y="139" font-size="13">反而强化了作品的历史表达。</text>
+
+    <rect x="970" y="170" width="190" height="86" rx="13" fill="#faf5ff" stroke="#a855f7"/>
+    <text x="987" y="194" font-size="11" fill="#7e22ce">可提问语言</text>
+    <text x="987" y="217" font-size="13">毛泽东为什么在战争时期</text>
+    <text x="987" y="237" font-size="13">持续进行诗词创作?</text>
+
+    <rect x="970" y="386" width="190" height="150" rx="13" fill="#faf5ff" stroke="#a855f7"/>
+    <text x="987" y="410" font-size="11" fill="#7e22ce">长段讨论</text>
+    <text x="987" y="435" font-size="12">从抗日战争到解放战争,</text>
+    <text x="987" y="454" font-size="12">诗词既记录时代环境,也呈现</text>
+    <text x="987" y="473" font-size="12">政治理想、个人情感和历史判断。</text>
+    <text x="987" y="492" font-size="12">不同视频对作品背景、表达方式</text>
+    <text x="987" y="511" font-size="12">和受众理解给出了互补证据。</text>
+  </g>
+
+  <!-- demand nodes -->
+  <g font-family="sans-serif">
+    <rect x="1260" y="72" width="170" height="86" rx="14" fill="#fff7ed" stroke="#f59e0b" stroke-width="2"/>
+    <text x="1277" y="96" font-size="11" fill="#9a3412">平台需求 D-101</text>
+    <text x="1277" y="119" font-size="13">毛泽东为何在战争时期</text>
+    <text x="1277" y="139" font-size="13">持续进行诗词创作</text>
+
+    <rect x="1260" y="210" width="170" height="96" rx="14" fill="#fff7ed" stroke="#f59e0b" stroke-width="2"/>
+    <text x="1277" y="234" font-size="11" fill="#9a3412">平台需求 D-102</text>
+    <text x="1277" y="257" font-size="13">抗日战争与解放战争</text>
+    <text x="1277" y="277" font-size="13">时期诗词主题的变化</text>
+    <text x="1277" y="295" font-size="10" fill="#64748b">多挂靠:历史、人物故事、作品解读</text>
+
+    <rect x="1260" y="344" width="170" height="96" rx="14" fill="#fff7ed" stroke="#f59e0b" stroke-width="2"/>
+    <text x="1277" y="368" font-size="11" fill="#9a3412">平台需求 D-103</text>
+    <text x="1277" y="391" font-size="13">毛泽东战争诗词如何</text>
+    <text x="1277" y="411" font-size="13">表达历史判断与情感</text>
+    <text x="1277" y="429" font-size="10" fill="#64748b">由多条视频与长段讨论共同支撑</text>
+  </g>
+
+  <!-- drill-down display -->
+  <g font-family="sans-serif">
+    <rect x="250" y="585" width="950" height="82" rx="15" fill="#fff" stroke="#cbd5e1"/>
+    <text x="275" y="610" font-size="12" fill="#334155">页面下钻建议:</text>
+    <text x="275" y="633" font-size="12" fill="#64748b">树节点默认展示数量与强度 → 点击展开高频元素 → 点击元素查看相关视频 → 点击视频查看转写、明确观点和长段讨论 → 查看由这些证据提取出的平台需求。</text>
+    <text x="275" y="654" font-size="11" fill="#64748b">每一级都保留来源、出现次数、时间、贡献度和真实线上表现,避免展示内容与需求决策脱节。</text>
+  </g>
+</svg>
+</div>
+
+<div class="rules">
+  <div class="rule"><strong>分类树保持纯净:</strong>元素、视频和文本不是正式分类层级,不占用 L6、L7;它们以证据子图形式挂在分类节点下。</div>
+  <div class="rule"><strong>元素是连接枢纽:</strong>同一元素可以挂到多个分类节点,也可以连接多条视频和多个平台需求。</div>
+  <div class="rule"><strong>语言可分两级:</strong>短观点用于快速理解和需求命名,长段讨论用于解释、比较、争议分析和需求深化。</div>
+  <div class="rule"><strong>视频是事实实例:</strong>保留标题、链接、转写、内容标签、线上表现和命中的需求元素。</div>
+  <div class="rule"><strong>下钻同时用于展示与提取:</strong>用户可以浏览证据,系统也可以从相同证据链中生成、合并和验证需求。</div>
+  <div class="rule"><strong>逐层聚合:</strong>视频表现回流元素,元素回流分类节点和需求,使树上的数量与强度有真实内容支撑。</div>
+</div>
+
+<div class="summary">
+  推荐模型:分类树是目录和坐标;元素是细节索引;视频是实例证据;短观点和长讨论是语言化解释;平台需求是从这些证据中抽取出的可执行结论。
+</div>

+ 6 - 0
visualization/pages/waiting-business-scope.html

@@ -0,0 +1,6 @@
+<div style="display:flex;align-items:center;justify-content:center;min-height:60vh;text-align:center">
+  <div>
+    <h2>图结构方向已确认</h2>
+    <p class="subtitle">接下来在对话中确认需求池的生成边界与筛选原则。</p>
+  </div>
+</div>

+ 6 - 0
visualization/pages/waiting-demand-pipeline.html

@@ -0,0 +1,6 @@
+<div style="display:flex;align-items:center;justify-content:center;min-height:60vh;text-align:center">
+  <div>
+    <h2>业务图基本模型已确认</h2>
+    <p class="subtitle">正在对话中确认需求汇总流水线、评分分级和最终输出结构。</p>
+  </div>
+</div>

+ 6 - 0
visualization/pages/waiting-strength-lifecycle.html

@@ -0,0 +1,6 @@
+<div style="display:flex;align-items:center;justify-content:center;min-height:60vh;text-align:center">
+  <div>
+    <h2>需求反馈闭环已确认</h2>
+    <p class="subtitle">正在对话中确认需求强度、生命周期与新老需求公平机制。</p>
+  </div>
+</div>

+ 5 - 0
visualization/run.sh

@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+exec python3 "$SCRIPT_DIR/service/serve.py" --open "$@"

+ 278 - 0
visualization/service/serve.py

@@ -0,0 +1,278 @@
+#!/usr/bin/env python3
+"""Serve the SupplyAgent business-design visualizations locally."""
+
+from __future__ import annotations
+
+import argparse
+import html
+import mimetypes
+import threading
+import webbrowser
+from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
+from pathlib import Path
+from urllib.parse import unquote, urlparse
+
+
+VISUALIZATION_DIR = Path(__file__).resolve().parents[1]
+PAGES_DIR = VISUALIZATION_DIR / "pages"
+
+PAGES: tuple[tuple[str, str, str], ...] = (
+    (
+        "graph-structure-options.html",
+        "01 · 图结构方案比较",
+        "比较单一大树、自由图和树骨架关系图。",
+    ),
+    (
+        "graph-structure-left-to-right.html",
+        "02 · 从左到右的树图结构",
+        "展示主题、分类树、具体节点、平台需求和证据的阅读方向。",
+    ),
+    (
+        "business-graph-model.html",
+        "03 · 业务图节点与边",
+        "展示分类节点、需求元素、平台需求、关系和证据。",
+    ),
+    (
+        "demand-feedback-loop.html",
+        "04 · 需求—内容—表现闭环",
+        "展示真实线上表现如何反向更新需求强度。",
+    ),
+    (
+        "final-output-structure.html",
+        "05 · 最终输出结构",
+        "展示主题方向、平台需求和证据卡的组织方式。",
+    ),
+    (
+        "node-drilldown-evidence-chain.html",
+        "06 · 节点下钻证据链",
+        "展示分类节点如何下挂元素、视频、短观点、长讨论并提取平台需求。",
+    ),
+    (
+        "waiting-business-scope.html",
+        "过程页 A · 业务范围确认",
+        "对话过程中的可视化等待页。",
+    ),
+    (
+        "waiting-demand-pipeline.html",
+        "过程页 B · 需求流水线确认",
+        "对话过程中的可视化等待页。",
+    ),
+    (
+        "waiting-strength-lifecycle.html",
+        "过程页 C · 强度生命周期确认",
+        "对话过程中的可视化等待页。",
+    ),
+)
+
+
+BASE_CSS = """
+* { box-sizing: border-box; }
+html, body { min-height: 100%; }
+body {
+  margin: 0;
+  color: #0f172a;
+  background: #f5f7fb;
+  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
+    "Segoe UI", sans-serif;
+  line-height: 1.5;
+}
+a { color: inherit; }
+.topbar {
+  position: sticky;
+  top: 0;
+  z-index: 50;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 16px;
+  padding: 12px 24px;
+  color: #e2e8f0;
+  background: rgba(15, 23, 42, .96);
+  border-bottom: 1px solid #334155;
+}
+.topbar a { text-decoration: none; }
+.topbar .brand { font-weight: 700; color: #fff; }
+.topbar .meta { color: #94a3b8; font-size: 13px; }
+.content {
+  width: min(1480px, calc(100% - 32px));
+  margin: 0 auto;
+  padding: 28px 0 48px;
+}
+h1, h2, h3 { line-height: 1.25; }
+h2 { margin: 0 0 8px; font-size: 26px; }
+.subtitle { color: #64748b; margin: 0 0 20px; }
+.options { display: flex; flex-direction: column; gap: 12px; }
+.option, .card { cursor: pointer; }
+.option.selected, .card.selected {
+  border-color: #2563eb !important;
+  box-shadow: 0 0 0 3px rgba(37, 99, 235, .14) !important;
+}
+.gallery-title { margin: 4px 0 8px; font-size: 30px; }
+.gallery-intro { max-width: 880px; color: #64748b; margin: 0 0 24px; }
+.gallery {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
+  gap: 16px;
+}
+.gallery-card {
+  display: block;
+  padding: 18px;
+  text-decoration: none;
+  background: #fff;
+  border: 1px solid #dbe3ec;
+  border-radius: 15px;
+  transition: .18s ease;
+}
+.gallery-card:hover {
+  transform: translateY(-2px);
+  border-color: #60a5fa;
+  box-shadow: 0 12px 28px rgba(15, 23, 42, .09);
+}
+.gallery-card h3 { margin: 0 0 7px; font-size: 17px; }
+.gallery-card p { margin: 0; color: #64748b; font-size: 13px; }
+.reference {
+  margin-top: 24px;
+  padding: 18px;
+  border: 1px solid #dbe3ec;
+  border-radius: 15px;
+  background: #fff;
+}
+.reference img {
+  display: block;
+  width: min(100%, 650px);
+  height: auto;
+  margin-top: 12px;
+  border: 1px solid #e2e8f0;
+}
+"""
+
+
+def _page_metadata(filename: str) -> tuple[str, str] | None:
+    for item_filename, title, description in PAGES:
+        if filename == item_filename:
+            return title, description
+    return None
+
+
+def _document(title: str, body: str, *, subtitle: str = "") -> bytes:
+    subtitle_html = (
+        f'<div class="meta">{html.escape(subtitle)}</div>' if subtitle else ""
+    )
+    document = f"""<!doctype html>
+<html lang="zh-CN">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <title>{html.escape(title)} · SupplyAgent</title>
+  <style>{BASE_CSS}</style>
+</head>
+<body>
+  <header class="topbar">
+    <a class="brand" href="/">SupplyAgent 业务可视化</a>
+    {subtitle_html}
+  </header>
+  <main class="content">{body}</main>
+  <script>
+    window.toggleSelect = function (el) {{
+      const container = el.closest('.options') || el.closest('.cards') || el.parentElement;
+      if (container) {{
+        container.querySelectorAll('[data-choice]').forEach(function (item) {{
+          item.classList.remove('selected');
+        }});
+      }}
+      el.classList.add('selected');
+    }};
+  </script>
+</body>
+</html>"""
+    return document.encode("utf-8")
+
+
+def _gallery() -> bytes:
+    cards = []
+    for filename, title, description in PAGES:
+        cards.append(
+            f"""<a class="gallery-card" href="/view/{html.escape(filename)}">
+  <h3>{html.escape(title)}</h3>
+  <p>{html.escape(description)}</p>
+</a>"""
+        )
+    body = f"""
+<h1 class="gallery-title">SupplyAgent 业务框架可视化</h1>
+<p class="gallery-intro">
+  本目录保存需求汇总业务设计过程中形成的全部可视化材料。
+  正式业务结论以 README_myself.md 为准;图稿用于解释树、图、需求、内容和反馈之间的关系。
+</p>
+<section class="gallery">{"".join(cards)}</section>
+<section class="reference">
+  <h2>用户提供的参考图</h2>
+  <p class="subtitle">用于理解全局分类树、分类节点下挂元素,以及元素如何扩展成明确语言和长段讨论。</p>
+  <a href="/assets/tree.jpeg" target="_blank" rel="noreferrer"><img src="/assets/tree.jpeg" alt="全局分类树局部参考图"></a>
+  <a href="/assets/tree2.png" target="_blank" rel="noreferrer"><img src="/assets/tree2.png" alt="分类节点下挂元素参考图"></a>
+  <a href="/assets/case.jpeg" target="_blank" rel="noreferrer"><img src="/assets/case.jpeg" alt="元素扩展为语言和讨论参考图"></a>
+</section>
+"""
+    return _document("可视化目录", body, subtitle="本地设计资料")
+
+
+class VisualizationHandler(SimpleHTTPRequestHandler):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, directory=str(VISUALIZATION_DIR), **kwargs)
+
+    def do_GET(self) -> None:  # noqa: N802
+        path = unquote(urlparse(self.path).path)
+        if path == "/":
+            self._send_html(_gallery())
+            return
+
+        if path.startswith("/view/"):
+            filename = Path(path.removeprefix("/view/")).name
+            metadata = _page_metadata(filename)
+            page_path = PAGES_DIR / filename
+            if metadata is None or not page_path.is_file():
+                self.send_error(404, "Visualization not found")
+                return
+            title, description = metadata
+            fragment = page_path.read_text(encoding="utf-8")
+            self._send_html(_document(title, fragment, subtitle=description))
+            return
+
+        super().do_GET()
+
+    def _send_html(self, payload: bytes) -> None:
+        self.send_response(200)
+        self.send_header("Content-Type", "text/html; charset=utf-8")
+        self.send_header("Content-Length", str(len(payload)))
+        self.end_headers()
+        self.wfile.write(payload)
+
+    def log_message(self, format: str, *args: object) -> None:
+        print(f"[visualization] {self.address_string()} - {format % args}")
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument("--host", default="127.0.0.1")
+    parser.add_argument("--port", type=int, default=8765)
+    parser.add_argument("--open", action="store_true", dest="open_browser")
+    args = parser.parse_args()
+
+    mimetypes.add_type("image/jpeg", ".jpeg")
+    server = ThreadingHTTPServer((args.host, args.port), VisualizationHandler)
+    url_host = "localhost" if args.host in {"127.0.0.1", "0.0.0.0"} else args.host
+    url = f"http://{url_host}:{server.server_port}/"
+    print(f"SupplyAgent visualization server: {url}")
+
+    if args.open_browser:
+        threading.Timer(0.3, webbrowser.open, args=(url,)).start()
+
+    try:
+        server.serve_forever()
+    except KeyboardInterrupt:
+        pass
+    finally:
+        server.server_close()
+
+
+if __name__ == "__main__":
+    main()