Showing
2 changed files
with
50 additions
and
2 deletions
| @@ -306,6 +306,10 @@ class ChapterGenerationNode(BaseNode): | @@ -306,6 +306,10 @@ class ChapterGenerationNode(BaseNode): | ||
| 306 | # 章节篇幅规划(来自WordBudgetNode),用于指导字数与强调点 | 306 | # 章节篇幅规划(来自WordBudgetNode),用于指导字数与强调点 |
| 307 | chapter_plan_map = context.get("chapter_directives", {}) | 307 | chapter_plan_map = context.get("chapter_directives", {}) |
| 308 | chapter_plan = chapter_plan_map.get(section.chapter_id) if chapter_plan_map else {} | 308 | chapter_plan = chapter_plan_map.get(section.chapter_id) if chapter_plan_map else {} |
| 309 | + | ||
| 310 | + # 从 layout 的 tocPlan 中查找该章节是否允许使用SWOT块 | ||
| 311 | + allow_swot = self._get_chapter_swot_permission(section.chapter_id, context) | ||
| 312 | + | ||
| 309 | payload = { | 313 | payload = { |
| 310 | "section": { | 314 | "section": { |
| 311 | "chapterId": section.chapter_id, | 315 | "chapterId": section.chapter_id, |
| @@ -335,6 +339,7 @@ class ChapterGenerationNode(BaseNode): | @@ -335,6 +339,7 @@ class ChapterGenerationNode(BaseNode): | ||
| 335 | "language": "zh-CN", | 339 | "language": "zh-CN", |
| 336 | "maxTokens": context.get("max_tokens", 4096), | 340 | "maxTokens": context.get("max_tokens", 4096), |
| 337 | "allowedBlocks": ALLOWED_BLOCK_TYPES, | 341 | "allowedBlocks": ALLOWED_BLOCK_TYPES, |
| 342 | + "allowSwot": allow_swot, | ||
| 338 | "styleHints": { | 343 | "styleHints": { |
| 339 | "expectWidgets": True, | 344 | "expectWidgets": True, |
| 340 | "forceHeadingAnchors": True, | 345 | "forceHeadingAnchors": True, |
| @@ -359,6 +364,36 @@ class ChapterGenerationNode(BaseNode): | @@ -359,6 +364,36 @@ class ChapterGenerationNode(BaseNode): | ||
| 359 | payload["globalContext"]["sectionBudgets"] = chapter_plan["sections"] | 364 | payload["globalContext"]["sectionBudgets"] = chapter_plan["sections"] |
| 360 | return payload | 365 | return payload |
| 361 | 366 | ||
| 367 | + def _get_chapter_swot_permission(self, chapter_id: str, context: Dict[str, Any]) -> bool: | ||
| 368 | + """ | ||
| 369 | + 从 layout 的 tocPlan 中查找指定章节是否允许使用 SWOT 块。 | ||
| 370 | + | ||
| 371 | + 全文最多只有一个章节允许使用 SWOT 块,由文档设计阶段在 tocPlan 中 | ||
| 372 | + 通过 allowSwot 字段标记。 | ||
| 373 | + | ||
| 374 | + 参数: | ||
| 375 | + chapter_id: 当前章节ID。 | ||
| 376 | + context: 全局上下文字典。 | ||
| 377 | + | ||
| 378 | + 返回: | ||
| 379 | + bool: 如果该章节允许使用 SWOT 块则返回 True,否则返回 False。 | ||
| 380 | + """ | ||
| 381 | + layout = context.get("layout") | ||
| 382 | + if not isinstance(layout, dict): | ||
| 383 | + return False | ||
| 384 | + | ||
| 385 | + toc_plan = layout.get("tocPlan") | ||
| 386 | + if not isinstance(toc_plan, list): | ||
| 387 | + return False | ||
| 388 | + | ||
| 389 | + for entry in toc_plan: | ||
| 390 | + if not isinstance(entry, dict): | ||
| 391 | + continue | ||
| 392 | + if entry.get("chapterId") == chapter_id: | ||
| 393 | + return bool(entry.get("allowSwot", False)) | ||
| 394 | + | ||
| 395 | + return False | ||
| 396 | + | ||
| 362 | def _stream_llm( | 397 | def _stream_llm( |
| 363 | self, | 398 | self, |
| 364 | user_message: str, | 399 | user_message: str, |
| @@ -139,6 +139,10 @@ document_layout_output_schema = { | @@ -139,6 +139,10 @@ document_layout_output_schema = { | ||
| 139 | "anchor": {"type": "string"}, | 139 | "anchor": {"type": "string"}, |
| 140 | "display": {"type": "string"}, | 140 | "display": {"type": "string"}, |
| 141 | "description": {"type": "string"}, | 141 | "description": {"type": "string"}, |
| 142 | + "allowSwot": { | ||
| 143 | + "type": "boolean", | ||
| 144 | + "description": "是否允许该章节使用SWOT分析块,全文最多只有一个章节可设为true", | ||
| 145 | + }, | ||
| 142 | }, | 146 | }, |
| 143 | "required": ["chapterId", "display"], | 147 | "required": ["chapterId", "display"], |
| 144 | }, | 148 | }, |
| @@ -304,7 +308,11 @@ SYSTEM_PROMPT_CHAPTER_JSON = f""" | @@ -304,7 +308,11 @@ SYSTEM_PROMPT_CHAPTER_JSON = f""" | ||
| 304 | 3. 所有段落都放入paragraph.inlines,混排样式通过marks表示(bold/italic/color/link等)。 | 308 | 3. 所有段落都放入paragraph.inlines,混排样式通过marks表示(bold/italic/color/link等)。 |
| 305 | 4. 所有heading必须包含anchor,锚点与编号保持模板一致,比如section-2-1。 | 309 | 4. 所有heading必须包含anchor,锚点与编号保持模板一致,比如section-2-1。 |
| 306 | 5. 表格需给出rows/cells/align,KPI卡请使用kpiGrid,分割线用hr。 | 310 | 5. 表格需给出rows/cells/align,KPI卡请使用kpiGrid,分割线用hr。 |
| 307 | -6. SWOT分析必须优先使用 block.type="swotTable":分别填写 strengths/weaknesses/opportunities/threats 数组,单项至少包含 title/label/text 之一,可附加 detail/evidence/impact 字段;title/summary 字段用于概览说明。**特别注意:impact 字段只允许填写影响评级("低"/"中低"/"中"/"中高"/"高"/"极高");任何关于影响的文字叙述、详细说明、佐证或扩展描述必须写入 detail 字段,禁止在 impact 字段中混入描述性文字。** | 311 | +6. **SWOT块使用限制(重要!)**: |
| 312 | + - 只有在 constraints.allowSwot 为 true 时才允许使用 block.type="swotTable"; | ||
| 313 | + - 如果 constraints.allowSwot 为 false 或不存在,严禁生成任何 swotTable 类型的块,即使章节标题包含"SWOT"字样也不能使用该块类型,应改用表格(table)或列表(list)呈现相关内容; | ||
| 314 | + - 当允许使用SWOT块时,分别填写 strengths/weaknesses/opportunities/threats 数组,单项至少包含 title/label/text 之一,可附加 detail/evidence/impact 字段;title/summary 字段用于概览说明; | ||
| 315 | + - **特别注意:impact 字段只允许填写影响评级("低"/"中低"/"中"/"中高"/"高"/"极高");任何关于影响的文字叙述、详细说明、佐证或扩展描述必须写入 detail 字段,禁止在 impact 字段中混入描述性文字。** | ||
| 308 | 7. 如需引用图表/交互组件,统一用widgetType表示(例如chart.js/line、chart.js/doughnut)。 | 316 | 7. 如需引用图表/交互组件,统一用widgetType表示(例如chart.js/line、chart.js/doughnut)。 |
| 309 | 8. 鼓励结合outline中列出的子标题,生成多层heading与细粒度内容,同时可补充callout、blockquote等。 | 317 | 8. 鼓励结合outline中列出的子标题,生成多层heading与细粒度内容,同时可补充callout、blockquote等。 |
| 310 | 9. engineQuote 仅用于呈现单Agent的原话:使用 block.type="engineQuote",engine 取值 insight/media/query,title 必须固定为对应Agent名字(insight->Insight Agent,media->Media Agent,query->Query Agent,不可自定义),内部 blocks 只允许 paragraph,paragraph.inlines 的 marks 仅可使用 bold/italic(可留空),禁止在 engineQuote 中放表格/图表/引用/公式等;当 reports 或 forumLogs 中有明确的文字段落、结论、数字/时间等可直接引用时,优先分别从 Query/Media/Insight 三个 Agent 摘出关键原文或文字版数据放入 engineQuote,尽量覆盖三类 Agent 而非只用单一来源,严禁臆造内容或把表格/图表改写进 engineQuote。 | 318 | 9. engineQuote 仅用于呈现单Agent的原话:使用 block.type="engineQuote",engine 取值 insight/media/query,title 必须固定为对应Agent名字(insight->Insight Agent,media->Media Agent,query->Query Agent,不可自定义),内部 blocks 只允许 paragraph,paragraph.inlines 的 marks 仅可使用 bold/italic(可留空),禁止在 engineQuote 中放表格/图表/引用/公式等;当 reports 或 forumLogs 中有明确的文字段落、结论、数字/时间等可直接引用时,优先分别从 Query/Media/Insight 三个 Agent 摘出关键原文或文字版数据放入 engineQuote,尽量覆盖三类 Agent 而非只用单一来源,严禁臆造内容或把表格/图表改写进 engineQuote。 |
| @@ -376,7 +384,12 @@ SYSTEM_PROMPT_DOCUMENT_LAYOUT = f""" | @@ -376,7 +384,12 @@ SYSTEM_PROMPT_DOCUMENT_LAYOUT = f""" | ||
| 376 | 3. 输出 tocPlan,一级目录固定用中文数字("一、二、三"),二级目录用"1.1/1.2",可在description里说明详略;如需定制目录标题,请填写 tocTitle; | 384 | 3. 输出 tocPlan,一级目录固定用中文数字("一、二、三"),二级目录用"1.1/1.2",可在description里说明详略;如需定制目录标题,请填写 tocTitle; |
| 377 | 4. 根据模板结构和素材密度,为 themeTokens / layoutNotes 提出字体、字号、留白建议(需特别强调目录、正文一级标题字号保持统一),如需色板或暗黑模式兼容也在此说明; | 385 | 4. 根据模板结构和素材密度,为 themeTokens / layoutNotes 提出字体、字号、留白建议(需特别强调目录、正文一级标题字号保持统一),如需色板或暗黑模式兼容也在此说明; |
| 378 | 5. 严禁要求外部图片或AI生图,推荐Chart.js图表、表格、色块、KPI卡等可直接渲染的原生组件; | 386 | 5. 严禁要求外部图片或AI生图,推荐Chart.js图表、表格、色块、KPI卡等可直接渲染的原生组件; |
| 379 | -6. 不随意增删章节,仅优化命名或描述;若有排版或章节合并提示,请放入 layoutNotes,渲染层会严格遵循。 | 387 | +6. 不随意增删章节,仅优化命名或描述;若有排版或章节合并提示,请放入 layoutNotes,渲染层会严格遵循; |
| 388 | +7. **SWOT块使用规则**:在 tocPlan 中决定是否以及在哪一章使用SWOT分析块(swotTable): | ||
| 389 | + - 全文最多只允许一个章节使用SWOT块,该章节需设置 `allowSwot: true`; | ||
| 390 | + - 其他章节必须设置 `allowSwot: false` 或省略该字段; | ||
| 391 | + - SWOT块适合出现在"结论与建议"、"综合评估"、"战略分析"等总结性章节; | ||
| 392 | + - 如果报告内容不适合使用SWOT分析(如纯数据监测报告),则所有章节都不设置 `allowSwot: true`。 | ||
| 380 | 393 | ||
| 381 | **tocPlan的description字段特别要求:** | 394 | **tocPlan的description字段特别要求:** |
| 382 | - description字段必须是纯文本描述,用于在目录中展示章节简介 | 395 | - description字段必须是纯文本描述,用于在目录中展示章节简介 |
-
Please register or login to post a comment