diff --git a/offline-backend-20260512/backend/app/gateway/routers/flow_extract.py b/offline-backend-20260512/backend/app/gateway/routers/flow_extract.py index cfd07c0f..ab0065c0 100644 --- a/offline-backend-20260512/backend/app/gateway/routers/flow_extract.py +++ b/offline-backend-20260512/backend/app/gateway/routers/flow_extract.py @@ -429,25 +429,45 @@ _STRUCTURE = ( "② 从第二层起按层级逐层展开:每层在报告/交付里通常列了多条具体条目(如 目的层有 目的1/2/3、行为体层有各自对应行为体,后面还有 预期行为·驱动因素、叙事…)," "把每一条具体条目各画成一个节点,不要把整层合并成一个笼统节点;" "③ 每个节点 group 填它所在层级名称(取自报告里该层级真实名称);" - "④ **连线**:(a)group 标对层级;(b)各层内条目按**相同对应顺序**排列(相互对应的一组在各层都排同一位次);" - "(c)**每个非根节点都必须填 from**,连到上一层级里对应的那个节点(目的i连任务、行为体i连目的i、预期行为i连行为体i、叙事i连预期行为i…)——**最常见错误就是漏 from 导致平铺,务必每个都填**;系统还会按层级校正/补全连线兜底;" + "④ **连线**:(a)group 标对层级;(b)**每个非根节点都必须填 from**,连到它在上一层级里**所属/对应**的那个节点;" + "层间关系(哪层一对一、哪层一对多)**以本轮给出的业务链规范为准**:**一对一**的相邻层按位次配对(下层第i条连上层第i条);" + "**一对多**的相邻层里,上层一条可对应下层多条——下层每条连到它**所属**的那一条上层节点,**绝不能把多条上层节点汇聚连到同一个下层节点**(那是把一对多画反成多对一);" + "(c)最常见错误是漏 from 导致平铺,或把一对多画成多对一,务必每个都填对;系统还会按层级校正/补全连线兜底;" "⑤ 务必把每层每一条都输出成节点、不要漏层、不要只输出开头一两个节点就停(典型约13-16个节点);" "⑥ 只画这些层级实体节点,不要把『综合分析/核心洞察/执行路线/建议/结论』及『阶段①②③④』等汇总段落/阶段名画成节点。" ) def _material(req: FlowExtractRequest) -> str: + """抽取的数据来源:**报告为主 + 各席位原始交付为补充**一起喂进去。 + + 痛点:报告常把某层级的多条目压缩进一段(如「确定的3个核心行为体」「每个行为体都有具体预期行为」), + 只读报告会导致抽取时把一层多条目合并成一条。把各席位**原始交付**一并带上,模型在报告条目不足/被压缩时 + 可回到交付里把该层每一条抽全(报告与交付冲突时以报告为准)。 + """ # xdfx:把本次「行动名称」作为材料抬头喂进去(根节点就是它),让抽取贴着这条行动展开。 action = (req.root_label or "").strip() prefix = f"【本次行动】{action}\n\n" if action else "" report = (req.report_md or "").strip() - if report: - return prefix + "===== 方案总结报告全文(数据来源,逐条抽取,勿改写)=====\n" + report + "\n===== 报告结束 =====" seats = [s for s in req.seat_deliveries if (s.content or "").strip()] + parts: list[str] = [] + if report: + parts.append("===== 方案总结报告全文(主数据来源,逐条抽取,勿改写)=====\n" + report + "\n===== 报告结束 =====") if seats: body = "\n\n".join(f"## 席位{i + 1}:{s.sender or '(未具名席位)'}\n{s.content.strip()}" for i, s in enumerate(seats)) - return prefix + "===== 各席位交付产物(数据来源,逐条抽取,勿改写)=====\n" + body + "\n===== 交付结束 =====" - return "" + if report: + parts.append( + "===== 各席位原始交付产物(补充数据来源)=====\n" + "用途:报告里若某层级的条目被压缩 / 概括 / 数量不足(例如某行为体名下的多条预期行为被并成一句、" + "候选目的或候选叙事没列全),**请回到这里把该层级的每一条逐条抽全**;与报告冲突时以报告为准。\n" + + body + + "\n===== 交付结束 =====" + ) + else: + parts.append("===== 各席位交付产物(数据来源,逐条抽取,勿改写)=====\n" + body + "\n===== 交付结束 =====") + if not parts: + return "" + return prefix + "\n\n".join(parts) def _hierarchy_hint(req: FlowExtractRequest) -> str: @@ -516,6 +536,10 @@ def _extract_messages(req: FlowExtractRequest) -> list[Any]: + "\n\n" + _hierarchy_hint(req) + _root_label_hint(req) + + "\n\n【务必逐条抽全(最重要)】每个层级里出现的**每一条**都要各自成一个节点:" + + "候选清单 / Top-N / 「①②③」/「(确定·选用)」式的**多个候选项全部都要抽**(不是只抽被选中的那一条,落选项也是节点);" + + "某上层节点名下列了几条下层条目就出几个节点,**不许把多条合并 / 概括成一条**;" + + "不要只照搬报告结尾「方案总纲 / 最终框架」里被精简的主干,要回到正文把每层全部条目抽全。" + "\n\n请分析以上数据来源,按你的职责设定输出一个完整的 ```flow-json 代码块。" ) return [SystemMessage(content=_system_prompt()), HumanMessage(content=user)] @@ -548,13 +572,17 @@ def _validate_messages(req: FlowExtractRequest, extracted: str) -> list[Any]: + "\n===== 待校验结束 =====\n\n请对照数据来源,**逐个节点检查上面这份 flow-json 是不是缺字段**、并修复它:" + "\n**校验要点(务必遵守)**:" + "① **连接关系 `from`(最重要,必查必补)**:**逐个**检查上面每个节点——除根任务外," - + "**只要某个节点缺了 `from`(或 `from` 为空),就必须给它补上正确的 `from`**:连到它在上一层级里对应的那个节点" - + "(目的i 连 任务、行为体i 连 目的i、预期行为i 连 行为体i、叙事i 连 预期行为i…)。" - + "缺 `from` 是这份 JSON 最常见、最致命的错漏(会导致流程图平铺),**绝不能让任何非根节点悬空**;" - + "② **每层每条都在、没漏层没漏条目**,节点数只能 ≥ 原图、不能变少;" - + "③ 每个节点的 `group`(层级名)标对;各层内条目按相同对应顺序排列(相互对应的一组在各层排同一位次);" - + "④ 每个节点的 `detail` **原样保留、不要简化或精简理由**——理由要完整,不要删减内容;" - + "⑤ 没有汇总/阶段编号节点;只在确有错漏时才动,没问题就原样规范化输出。" + + "**只要某个节点缺了 `from`(或 `from` 为空),就必须给它补上正确的 `from`**:连到它在上一层级里**所属/对应**的那个节点。" + + "层间关系(哪层一对一、哪层一对多)**以上面给出的业务链规范为准**:一对一层级按位次配对(下层第i条连上层第i条);" + + "**一对多**层级里下层每条连到它**所属**的那一条上层节点,**绝不能把多条上层节点汇聚到同一个下层节点**(那是把一对多画反成多对一,是本次要重点纠正的错)。" + + "缺 `from` / 把一对多画成多对一 是最常见错漏,**绝不能让任何非根节点悬空、也不要张冠李戴**;" + + "② **候选 / 多条目是否抽全(必查必补)**:对照数据来源逐层核对——候选目的、候选叙事、某行为体名下的多条预期行为等," + + "**报告/交付里有几条就该有几个节点**;发现被漏抽的候选项(尤其是没被选中的候选)、或被合并成一条的多个条目," + + "**必须拆/补成独立节点**(落选候选项也要在,detail 注明候选/未选用)。报告条目被压缩时回到《各席位原始交付产物》补全。" + + "③ **每层每条都在、没漏层没漏条目**,节点数只能 ≥ 原图、不能变少;尤其一对多的下层(如某层一条对应下层多条)不要被合并成一条;" + + "④ 每个节点的 `group`(层级名)标对;一对一层级内条目按相同位次对齐,一对多层级下层每条紧挨其所属上层条目分组;" + + "⑤ 每个节点的 `detail` **原样保留、不要简化或精简理由**——理由要完整,不要删减内容;" + + "⑥ 没有汇总/阶段编号节点;只在确有错漏时才动,没问题就原样规范化输出。" + "\n输出修复后的完整 ```flow-json 代码块(含全部节点 + 每个非根节点的 from)。" ) return [SystemMessage(content=_system_prompt()), HumanMessage(content=user)] diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/agents/roundtable_orchestrator/extraction_spec.py b/offline-backend-20260512/backend/packages/harness/deerflow/agents/roundtable_orchestrator/extraction_spec.py index 200e48d7..9228e7fd 100644 --- a/offline-backend-20260512/backend/packages/harness/deerflow/agents/roundtable_orchestrator/extraction_spec.py +++ b/offline-backend-20260512/backend/packages/harness/deerflow/agents/roundtable_orchestrator/extraction_spec.py @@ -38,7 +38,23 @@ _NAMING_NOTE = ( "节点 label 必须用该层级下的**具体实体 / 概念短名**(≤12 字)," "**绝不能含「步骤 / 阶段 / 环节 / 第N步 / 阶段①②③」等流程或编号词**,也不要拿层级名本身当 label。" ) -_SOURCE_NOTE = "**优先**从《方案总结报告》抽取;没有报告时再用各席位交付内容。逐条抽取、勿改写、勿精简理由。" +_SOURCE_NOTE = ( + "数据来源同时给了《方案总结报告》与《各席位原始交付产物》:**以报告为主**逐条抽取、勿改写、勿精简理由;" + "但报告常把某层级的多条目**压缩/概括进一句话**(只说「确定了 N 个…」却不逐条列出)," + "**一旦报告某层级条目被压缩或数量不足,必须回到《各席位原始交付产物》把该层的每一条抽全**——交付里有最细的逐条清单。" +) +# 「逐条抽全 / 候选项也要抽」—— 本次最关键的准确性约束(治「同一层级里有 3 条却只抽 1 条」「多条被合并成 1 条」)。 +# 业务无关措辞(不出现任何具体业务的层级名),通用规范与写实规范都复用它。 +_COMPLETENESS_NOTE = ( + "**逐条抽全、候选项也要抽(最重要,必须做到)**:\n" + " · 每个层级里出现的**每一条**都要各自成一个独立节点。报告里以「Top N 候选」「N 组候选方案」「①②③」" + "「(确定 / 选用 / 优选)」等形式列出的**多个候选项,全部都要抽成节点**——**不是只抽被标「确定 / 选用」的那一条**," + "落选的候选项同样是该层级的节点(可在它的 detail 里注明「候选 / 未选用」或「最终选用」),数量一个都不能少;\n" + " · 某个上层节点名下若列了多条下层条目(同一上层名下罗列了好几条)," + "**报告/交付里有几条就出几个节点,绝对不许合并 / 概括 / 只取代表性的一条**;\n" + " · **不要只照搬报告结尾「方案总纲 / 最终框架 / 共识方案」里被精简的主干链**(那里通常只画被选中的一条主线)——" + "要回到正文逐层把每个层级的**全部条目**抽全。" +) # ── rwfx(6BF):六步法完整链(线性) ───────────────────────────────────────── @@ -47,12 +63,21 @@ _RWFX = ChainSpec( label="rwfx", levels=["任务", "目的", "行为体", "预期行为", "驱动因素", "叙事"], relations_note=( - "层间关系:**「预期行为」与「驱动因素」一一对应**(两层数量必须相等、一个预期行为恰对一个驱动因素," - "按相同位次配对);其余相邻层级(任务→目的、目的→行为体、行为体→预期行为、驱动因素→叙事)均为" - "**一对多**(上层一条可带下层多条)。" + "层间关系(务必分清一对一 / 一对多,连线别画反):\n" + "① **「预期行为」与「驱动因素」一一对应**——两层数量必须相等、一个预期行为恰对一个驱动因素,按相同位次配对;\n" + "② **「驱动因素」→「叙事」是一对多**——**一个驱动因素对应多个叙事**(每个驱动因素抽出它名下的若干条叙事)," + "每条叙事的 from **连到它所属的那一个驱动因素**(叙事的上一层是驱动因素,不是预期行为);" + "**绝不能把多个驱动因素汇聚连到同一个叙事节点**(那就把一对多画反成多对一了,是常见错误);也不要把全部叙事合并成一个节点;\n" + "③ 其余相邻层级(任务→目的、目的→行为体、行为体→预期行为)均为**一对多**(上层一条可带下层多条,下层每条连其所属上层条目)。" ), completeness_note=( - "以上 6 个层级都要出节点;一一对应的层级(预期行为↔驱动因素)两层节点数必须相等并按位次配对。" + "以上 6 个层级都要出节点,且每层的**全部条目都要抽全**:\n" + " · **目的**:报告若列了多个候选目的(如「Top 3 候选目的」「从8个优选出3个」),就把**这些候选目的全部**抽成" + "目的节点(被标「确定」的也只是其中一个),不要只抽被选中的那一个;\n" + " · **行为体 / 预期行为**:每个行为体名下报告列了几条预期行为(常 3-5 条)就出几个预期行为节点,**不许把一个行为体的多条预期行为合并成一条**;\n" + " · 预期行为↔驱动因素 两层节点数必须相等并按位次配对(一个预期行为恰对一个驱动因素);\n" + " · **叙事**:报告若列了多个候选叙事方案(如「3 组候选叙事」),**全部**抽成叙事节点(含未选用的),不要只出一两个;" + "叙事层通常比驱动因素层**更宽**,每条叙事连到它所属/对接的那一个驱动因素——不要让多个驱动因素指向同一叙事。" ), ) @@ -123,7 +148,7 @@ def build_structure_directive(business_code: str | None) -> str: "- 本业务**没有固定层级模板**:请严格按《方案总结报告》/ 各席位交付里**真实出现的层级**," "自「任务」为根逐层完整展开,每层每一条都出成节点,不要只抽开头一两层就停;\n" "- **不要套用「目的→行为体」之类的固定示例层级**——层级名一律取自本次报告 / 交付里的真实层级;\n" - f"- {_NAMING_NOTE}\n- {_SOURCE_NOTE}" + f"- {_COMPLETENESS_NOTE}\n- {_NAMING_NOTE}\n- {_SOURCE_NOTE}" ) arrows = " → ".join(spec.levels) completeness = spec.completeness_note or f"以上 {len(spec.levels)} 个层级都要出节点,不得缺层。" @@ -131,6 +156,7 @@ def build_structure_directive(business_code: str | None) -> str: f"【{spec.label} 业务链抽取规范(强制遵守,优先级高于系统提示里的任何默认/示例层级)】", f"- **完整层级**(自上而下,必须逐层抽全、不得缺层、不得停在中间层):{arrows};", f"- {spec.relations_note}", + f"- {_COMPLETENESS_NOTE}", f"- {_NAMING_NOTE}", f"- {_SOURCE_NOTE}", f"- **完整性**:{completeness}",