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 b59a5046..c9fa9caa 100644 --- a/offline-backend-20260512/backend/app/gateway/routers/flow_extract.py +++ b/offline-backend-20260512/backend/app/gateway/routers/flow_extract.py @@ -501,11 +501,12 @@ _STRUCTURE = ( def _material(req: FlowExtractRequest) -> str: - """抽取的数据来源:**报告为主 + 各席位原始交付为补充**一起喂进去。 + """抽取的数据来源:**严格以《方案总结报告》及其文件内容为准**;各席位原始交付仅作兜底。 - 痛点:报告常把某层级的多条目压缩进一段(如「确定的3个核心行为体」「每个行为体都有具体预期行为」), - 只读报告会导致抽取时把一层多条目合并成一条。把各席位**原始交付**一并带上,模型在报告条目不足/被压缩时 - 可回到交付里把该层每一条抽全(报告与交付冲突时以报告为准)。 + 报告是各席位交付经总控综合提炼、**已做取舍**后的最终结论(如从 10 个候选目的里锁定 3 个)。 + 因此抽取**只按报告最终呈现的条目**逐条抽全——既不要漏(报告列了 3 条别只抽 1 条),也不要多 + (别去席位产物里把报告已淘汰的候选也抽进来)。各席位原始交付**仅在报告某层整层缺失 / 报告明确 + 说有 N 条却没逐条列出时**才用来补全那一层;与报告冲突时一律以报告为准。 """ # xdfx:把本次「行动名称」作为材料抬头喂进去(根节点就是它),让抽取贴着这条行动展开。 action = (req.root_label or "").strip() @@ -514,19 +515,25 @@ def _material(req: FlowExtractRequest) -> str: seats = [s for s in req.seat_deliveries if (s.content or "").strip()] parts: list[str] = [] if report: - parts.append("===== 方案总结报告全文(主数据来源,逐条抽取,勿改写)=====\n" + report + "\n===== 报告结束 =====") + 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)) if report: parts.append( - "===== 各席位原始交付产物(补充数据来源)=====\n" - "用途:报告里若某层级的条目被压缩 / 概括 / 数量不足(例如某行为体名下的多条预期行为被并成一句、" - "候选目的或候选叙事没列全),**请回到这里把该层级的每一条逐条抽全**;与报告冲突时以报告为准。\n" + "===== 各席位原始交付产物(**兜底参考,仅报告不足时才用**)=====\n" + "用途:**只有当报告某个层级整层缺失、或报告明确说有 N 条却没逐条列出时**,才回到这里把那一层补全。\n" + "⚠️ **绝不要用这里去扩充报告已经取舍过的层级**——报告里被淘汰 / 未采用的候选项(典型:席位列了 10 个候选目的," + "但报告最终只锁定 3 个),**一律以报告最终采用的为准,不要把被淘汰的候选也抽进来**。与报告冲突时以报告为准。\n" + body + "\n===== 交付结束 =====" ) else: - parts.append("===== 各席位交付产物(数据来源,逐条抽取,勿改写)=====\n" + body + "\n===== 交付结束 =====") + # 没有报告时,席位产物才是主来源(此时按席位最终采用的结论抽,候选池同样别全抽)。 + parts.append("===== 各席位交付产物(数据来源,逐条抽取、勿改写;按其最终采用的结论抽,不要把被淘汰的候选都抽进来)=====\n" + body + "\n===== 交付结束 =====") if not parts: return "" return prefix + "\n\n".join(parts) @@ -598,10 +605,13 @@ def _extract_messages(req: FlowExtractRequest) -> list[Any]: + "\n\n" + _hierarchy_hint(req) + _root_label_hint(req) - + "\n\n【务必逐条抽全(最重要)】每个层级里出现的**每一条**都要各自成一个节点:" - + "候选清单 / Top-N / 「①②③」/「(确定·选用)」式的**多个候选项全部都要抽**(不是只抽被选中的那一条,落选项也是节点);" + + "\n\n【按报告逐条抽全,不漏也不多(最重要)】**严格以《方案总结报告》最终呈现的条目为准**:" + + "报告某层级用表格 / 列表 / ①②③ 逐条列了几条,就出几个节点、**一条都不能漏**(不要只抽被标「确定 / 第1名」的那条而漏掉同列的其余);" + "某上层节点名下列了几条下层条目就出几个节点,**不许把多条合并 / 概括成一条**;" - + "不要只照搬报告结尾「方案总纲 / 最终框架」里被精简的主干,要回到正文把每层全部条目抽全。" + + "⚠️ 但**不要无中生有**:报告已明确取舍、最终只采用 N 条的层级(如「从 10 个候选目的中锁定 3 个」「最终选定方案」)," + + "**只抽报告最终采用的那 N 条**,**绝不要去席位产物或报告的「候选池 / 被淘汰项」里把没被采用的也抽出来凑数**;" + + "报告把某层多条目压缩成一句话、没逐条列出时(如只说「12 条预期行为」却没列),才回《各席位原始交付产物》把那一层补全。" + + "不要只照搬报告结尾「方案总纲 / 最终框架」里被精简的主干,要回到正文(如「各席位观点与交付」)把报告列出的每层条目抽全。" + "\n\n请分析以上数据来源,按你的职责设定输出一个完整的 ```flow-json 代码块。" ) return [SystemMessage(content=_system_prompt()), HumanMessage(content=user)] @@ -643,10 +653,12 @@ def _validate_messages(req: FlowExtractRequest, extracted: str) -> list[Any]: + "**绝不允许多条上层节点共用 / 汇聚到同一个下层节点**(多对一),也不允许同一条上层节点被多个下层节点指向" + "(即 1:1 两层里任何节点都不能冒出 / 汇入多条连线)。若发现下层节点偏少(被合并)导致只能多对一," + "**必须按上层逐条拆分 / 补出等量的下层节点**(每条上层各配一个,内容综合提炼),使两层等量后再逐一配对;" - + "③ **候选 / 多条目是否抽全(必查必补)**:对照数据来源逐层核对——候选目的、候选叙事、某行为体名下的多条预期行为等," - + "**报告/交付里有几条就该有几个节点**;发现被漏抽的候选项(尤其是没被选中的候选)、或被合并成一条的多个条目," - + "**必须拆/补成独立节点**(落选候选项也要在,detail 注明候选/未选用)。报告条目被压缩时回到《各席位原始交付产物》补全。" - + "④ **每层每条都在、没漏层没漏条目**,节点数只能 ≥ 原图、不能变少;尤其一对多的下层(如某层一条对应下层多条)不要被合并成一条;" + + "③ **逐层条目数对齐《方案总结报告》(不漏也不多,必查)**:以报告为准——报告某层列了几条就该有几个节点。" + + "漏抽的补齐(报告列了 3 条却只抽了 1 条 → 补到 3;某行为体名下多条预期行为被并成一句 → 拆全);" + + "**但绝不要新增报告未采用的候选**——报告已取舍、最终只采用 N 条的层级(如从 10 个候选目的里锁定 3 个)," + + "**只保留报告最终采用的那 N 条;若图里混进了报告未采用 / 被淘汰的候选(从席位候选池里抽出来的),把它们删掉**。" + + "仅当报告把某层压缩成一句话、没逐条列时,才回《各席位原始交付产物》补全那一层;" + + "④ **不要漏报告列出的层 / 条目**;一对多的下层(某上层对应多条下层)不要被错误合并成一条;" + "⑤ 每个节点的 `group`(层级名)标对;一对一层级内条目按相同位次对齐,一对多层级下层每条紧挨其所属上层条目分组;" + "⑥ 每个节点的 `detail` **原样保留、不要简化或精简理由**——理由要完整,不要删减内容;" + "⑦ 没有汇总/阶段编号节点;只在确有错漏时才动,没问题就原样规范化输出。" 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 24e7237f..83da6bc1 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 @@ -43,21 +43,23 @@ _NAMING_NOTE = ( "**绝不能含「步骤 / 阶段 / 环节 / 第N步 / 阶段①②③」等流程或编号词**,也不要拿层级名本身当 label。" ) _SOURCE_NOTE = ( - "数据来源同时给了《方案总结报告》与《各席位原始交付产物》:**以报告为主**逐条抽取、勿改写、勿精简理由;" - "但报告常把某层级的多条目**压缩/概括进一句话**(只说「确定了 N 个…」却不逐条列出)," - "**一旦报告某层级条目被压缩或数量不足,必须回到《各席位原始交付产物》把该层的每一条抽全**——交付里有最细的逐条清单。" + "**严格以《方案总结报告》及其文件内容为准**逐条抽取、勿改写、勿精简理由(报告是各席位交付经总控综合提炼、" + "**已做取舍**后的最终结论)。**《各席位原始交付产物》仅作兜底**:只有当报告某层级**整层缺失、或明确说有 N 条却没逐条列出**时," + "才回席位产物把那一层补全;**绝不要用席位产物去扩充报告已取舍过的层级**——报告淘汰 / 未采用的候选一律不抽。与报告冲突时以报告为准。" ) -# 「逐条抽全 / 候选项也要抽」—— 本次最关键的准确性约束(治「同一层级里有 3 条却只抽 1 条」「多条被合并成 1 条」)。 +# 「按报告逐条抽全、不漏也不多」—— 本次最关键的准确性约束(治「报告列 3 条却只抽 1 条」与「报告只采用 3 条却抽出一堆候选」)。 # 业务无关措辞(不出现任何具体业务的层级名),通用规范与写实规范都复用它。 _COMPLETENESS_NOTE = ( - "**逐条抽全、候选项也要抽(最重要,必须做到)**:\n" - " · 每个层级里出现的**每一条**都要各自成一个独立节点。报告里以「Top N 候选」「N 组候选方案」「①②③」" - "「(确定 / 选用 / 优选)」等形式列出的**多个候选项,全部都要抽成节点**——**不是只抽被标「确定 / 选用」的那一条**," - "落选的候选项同样是该层级的节点(可在它的 detail 里注明「候选 / 未选用」或「最终选用」),数量一个都不能少;\n" + "**按报告逐条抽全、不漏也不多(最重要,必须做到)**:\n" + " · 报告某层级用表格 / 列表 / 「①②③」逐条列出的条目,**每条都要各自成一个独立节点、一条都不能漏**" + "(不要只抽被标「确定 / 第1名 / 选用」的那一条而漏掉报告同列展示的其余条目);\n" " · 某个上层节点名下若列了多条下层条目(同一上层名下罗列了好几条)," - "**报告/交付里有几条就出几个节点,绝对不许合并 / 概括 / 只取代表性的一条**;\n" + "**报告里有几条就出几个节点,绝对不许合并 / 概括 / 只取代表性的一条**;\n" + " · ⚠️ **但绝不要无中生有 / 多抽**:报告**已取舍、最终只采用 N 条**的层级(如「从 10 个候选目的里锁定 3 个」" + "「最终选定的方案」),**只抽报告最终采用的那 N 条**——**不要去席位产物、也不要去报告的「候选池 / 被淘汰项」" + "里把没被采用的候选也抽出来凑数**(这是本次要重点纠正的「抽出一堆目的」错);\n" " · **不要只照搬报告结尾「方案总纲 / 最终框架 / 共识方案」里被精简的主干链**(那里通常只画被选中的一条主线)——" - "要回到正文逐层把每个层级的**全部条目**抽全。" + "要回到正文把报告该层级列出的**全部条目**抽全。" ) @@ -86,15 +88,16 @@ _RWFX = ChainSpec( "④ **全链是一棵树:除根「任务」外,每个节点有且仅有一个父节点(一个 from)**——任何节点都不会从多个上层节点汇入(无多对一 / 多对多 / 汇聚)。" ), completeness_note=( - "以上 6 个层级都要出节点,且每层的**全部条目都要抽全**:\n" - " · **目的**:报告若列了多个候选目的(如「Top 3 候选目的」「从8个优选出3个」),就把**这些候选目的全部**抽成" - "目的节点(被标「确定」的也只是其中一个),不要只抽被选中的那一个;\n" + "以上 6 个层级都要出节点,且每层**严格按报告最终呈现的条目抽全——不漏也不多**:\n" + " · **目的**:抽报告**最终采用 / 锁定的那几个目的**(通常 3 个左右)。报告说「从 N 个候选里锁定 3 个」就" + "**只抽这最终的 3 个、绝不要把那 N 个候选都抽进来**(这是本次「抽出一堆目的」的错);若报告把这几个目的并列成表展示," + "就把表里列出的都抽全、别只抽第 1 个;\n" " · **行为体 / 预期行为**:每个行为体名下报告列了几条预期行为(常 3-5 条)就出几个预期行为节点,**不许把一个行为体的多条预期行为合并成一条**;\n" " · **驱动因素(1:1 特例,按上面①处理)**:驱动因素层节点数 **= 预期行为层节点数**——每条预期行为各配一个核心驱动因素," "**绝不能因为报告把多个原因挤在一处 / 留空,就只抽 1 个驱动因素去连多条预期行为(多对一,本次最严重的错)**,也不要抽得比预期行为多;务必两层等量、位次一一配对;\n" - " · **叙事**:报告若列了多个候选叙事方案(如「3 组候选叙事」),**全部**抽成叙事节点(含未选用的),不要只出一两个;" - "叙事层通常比驱动因素层**更宽**,每条叙事连到它所属/对接的那一个驱动因素——不要让多个驱动因素指向同一叙事;" - "**每条叙事只有一个父驱动因素(单 from)**,绝不要把一条叙事连到多个驱动因素。" + " · **叙事**:抽报告**最终呈现的那几条核心叙事**(通常 2-4 条,含报告并列展示的备选方案)全部抽成节点,不要只出一两个," + "也不要去席位候选池里把报告没展示的也抽进来;每条叙事连到它所属/对接的那一个驱动因素——" + "**每条叙事只有一个父驱动因素(单 from)**,绝不要把一条叙事连到多个驱动因素、也不要让多个驱动因素指向同一叙事。" ), single_parent=True, # rwfx 是纯树:每个非根节点恰一个父节点(后端确定性兜底收敛)。 )