马一丁

Add support for PEST blocks

... ... @@ -34,6 +34,7 @@ ALLOWED_BLOCK_TYPES: List[str] = [
"list",
"table",
"swotTable",
"pestTable",
"blockquote",
"engineQuote",
"hr",
... ... @@ -236,6 +237,71 @@ swot_block: Dict[str, Any] = {
"additionalProperties": True,
}
pest_item_schema: Dict[str, Any] = {
"title": "PestItem",
"oneOf": [
{"type": "string"},
{
"type": "object",
"properties": {
"title": {"type": "string"},
"label": {"type": "string"},
"text": {"type": "string"},
"detail": {"type": "string"},
"description": {"type": "string"},
"source": {"type": "string"},
"evidence": {"type": "string"},
"trend": {
"type": "string",
"enum": ["正面利好", "负面影响", "中性", "不确定", "持续观察"],
"description": "趋势/影响评估,只允许填写:正面利好/负面影响/中性/不确定/持续观察",
},
"impact": {"type": ["string", "number"]},
},
"required": [],
"additionalProperties": True,
},
],
}
pest_block: Dict[str, Any] = {
"title": "PestTableBlock",
"type": "object",
"properties": {
"type": {"const": "pestTable"},
"title": {"type": "string"},
"summary": {"type": "string"},
"political": {
"type": "array",
"items": {"$ref": "#/definitions/pestItem"},
"description": "政治因素:政策法规、政府态度、政治稳定性等",
},
"economic": {
"type": "array",
"items": {"$ref": "#/definitions/pestItem"},
"description": "经济因素:经济周期、利率汇率、消费水平等",
},
"social": {
"type": "array",
"items": {"$ref": "#/definitions/pestItem"},
"description": "社会因素:人口结构、文化趋势、生活方式等",
},
"technological": {
"type": "array",
"items": {"$ref": "#/definitions/pestItem"},
"description": "技术因素:技术创新、研发投入、技术普及等",
},
},
"required": ["type"],
"anyOf": [
{"required": ["political"]},
{"required": ["economic"]},
{"required": ["social"]},
{"required": ["technological"]},
],
"additionalProperties": True,
}
blockquote_block: Dict[str, Any] = {
"title": "BlockquoteBlock",
"type": "object",
... ... @@ -429,6 +495,7 @@ block_variants: List[Dict[str, Any]] = [
widget_block,
toc_block,
swot_block,
pest_block,
]
CHAPTER_JSON_SCHEMA: Dict[str, Any] = {
... ... @@ -457,6 +524,7 @@ CHAPTER_JSON_SCHEMA: Dict[str, Any] = {
"inlineMark": inline_mark_schema,
"inlineRun": inline_run_schema,
"swotItem": swot_item_schema,
"pestItem": pest_item_schema,
"block": {"oneOf": block_variants},
},
}
... ...
... ... @@ -307,8 +307,9 @@ class ChapterGenerationNode(BaseNode):
chapter_plan_map = context.get("chapter_directives", {})
chapter_plan = chapter_plan_map.get(section.chapter_id) if chapter_plan_map else {}
# 从 layout 的 tocPlan 中查找该章节是否允许使用SWOT块
# 从 layout 的 tocPlan 中查找该章节是否允许使用SWOT块和PEST块
allow_swot = self._get_chapter_swot_permission(section.chapter_id, context)
allow_pest = self._get_chapter_pest_permission(section.chapter_id, context)
payload = {
"section": {
... ... @@ -340,6 +341,7 @@ class ChapterGenerationNode(BaseNode):
"maxTokens": context.get("max_tokens", 4096),
"allowedBlocks": ALLOWED_BLOCK_TYPES,
"allowSwot": allow_swot,
"allowPest": allow_pest,
"styleHints": {
"expectWidgets": True,
"forceHeadingAnchors": True,
... ... @@ -394,6 +396,42 @@ class ChapterGenerationNode(BaseNode):
return False
def _get_chapter_pest_permission(self, chapter_id: str, context: Dict[str, Any]) -> bool:
"""
从 layout 的 tocPlan 中查找指定章节是否允许使用 PEST 块。
全文最多只有一个章节允许使用 PEST 块,由文档设计阶段在 tocPlan 中
通过 allowPest 字段标记。
PEST块用于宏观环境分析:
- Political(政治因素)
- Economic(经济因素)
- Social(社会因素)
- Technological(技术因素)
参数:
chapter_id: 当前章节ID。
context: 全局上下文字典。
返回:
bool: 如果该章节允许使用 PEST 块则返回 True,否则返回 False。
"""
layout = context.get("layout")
if not isinstance(layout, dict):
return False
toc_plan = layout.get("tocPlan")
if not isinstance(toc_plan, list):
return False
for entry in toc_plan:
if not isinstance(entry, dict):
continue
if entry.get("chapterId") == chapter_id:
return bool(entry.get("allowPest", False))
return False
def _stream_llm(
self,
user_message: str,
... ...
... ... @@ -143,6 +143,10 @@ document_layout_output_schema = {
"type": "boolean",
"description": "是否允许该章节使用SWOT分析块,全文最多只有一个章节可设为true",
},
"allowPest": {
"type": "boolean",
"description": "是否允许该章节使用PEST分析块,全文最多只有一个章节可设为true",
},
},
"required": ["chapterId", "display"],
},
... ... @@ -313,19 +317,25 @@ SYSTEM_PROMPT_CHAPTER_JSON = f"""
- 如果 constraints.allowSwot 为 false 或不存在,严禁生成任何 swotTable 类型的块,即使章节标题包含"SWOT"字样也不能使用该块类型,应改用表格(table)或列表(list)呈现相关内容;
- 当允许使用SWOT块时,分别填写 strengths/weaknesses/opportunities/threats 数组,单项至少包含 title/label/text 之一,可附加 detail/evidence/impact 字段;title/summary 字段用于概览说明;
- **特别注意:impact 字段只允许填写影响评级("低"/"中低"/"中"/"中高"/"高"/"极高");任何关于影响的文字叙述、详细说明、佐证或扩展描述必须写入 detail 字段,禁止在 impact 字段中混入描述性文字。**
7. 如需引用图表/交互组件,统一用widgetType表示(例如chart.js/line、chart.js/doughnut)。
8. 鼓励结合outline中列出的子标题,生成多层heading与细粒度内容,同时可补充callout、blockquote等。
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。
10. 如果chapterPlan中包含target/min/max或sections细分预算,请尽量贴合,必要时在notes允许的范围内突破,同时在结构上体现详略;
11. 一级标题需使用中文数字(“一、二、三”),二级标题使用阿拉伯数字(“1.1、1.2”),heading.text中直接写好编号,与outline顺序对应;
12. 严禁输出外部图片/AI生图链接,仅可使用Chart.js图表、表格、色块、callout等HTML原生组件;如需视觉辅助请改为文字描述或数据表;
13. 段落混排需通过marks表达粗体、斜体、下划线、颜色等样式,禁止残留Markdown语法(如**text**);
14. 行间公式用block.type="math"并填入math.latex,行内公式在paragraph.inlines里将文本设为Latex并加上marks.type="math",渲染层会用MathJax处理;
15. widget配色需与CSS变量兼容,不要硬编码背景色或文字色,legend/ticks由渲染层控制;
16. 善用callout、kpiGrid、表格、widget等提升版面丰富度,但必须遵守模板章节范围。
17. 输出前务必自检JSON语法:禁止出现`{{}}{{`或`][`相连缺少逗号、列表项嵌套超过一层、未闭合的括号或未转义换行,`list` block的items必须是`[[block,...], ...]`结构,若无法满足则返回错误提示而不是输出不合法JSON。
18. 所有widget块必须在顶层提供`data`或`dataRef`(可将props中的`data`上移),确保Chart.js能够直接渲染;缺失数据时宁可输出表格或段落,绝不留空。
19. 任何block都必须声明合法`type`(heading/paragraph/list/...);若需要普通文本请使用`paragraph`并给出`inlines`,禁止返回`type:null`或未知值。
7. **PEST块使用限制(重要!)**:
- 只有在 constraints.allowPest 为 true 时才允许使用 block.type="pestTable";
- 如果 constraints.allowPest 为 false 或不存在,严禁生成任何 pestTable 类型的块,即使章节标题包含"PEST"、"宏观环境"等字样也不能使用该块类型,应改用表格(table)或列表(list)呈现相关内容;
- 当允许使用PEST块时,分别填写 political/economic/social/technological 数组,单项至少包含 title/label/text 之一,可附加 detail/source/trend 字段;title/summary 字段用于概览说明;
- **PEST四维度说明**:political(政治因素:政策法规、政府态度、监管环境)、economic(经济因素:经济周期、利率汇率、市场需求)、social(社会因素:人口结构、文化趋势、消费习惯)、technological(技术因素:技术创新、研发趋势、数字化程度);
- **特别注意:trend 字段只允许填写趋势评估("正面利好"/"负面影响"/"中性"/"不确定"/"持续观察");任何关于趋势的文字叙述、详细说明、来源或扩展描述必须写入 detail 字段,禁止在 trend 字段中混入描述性文字。**
8. 如需引用图表/交互组件,统一用widgetType表示(例如chart.js/line、chart.js/doughnut)。
9. 鼓励结合outline中列出的子标题,生成多层heading与细粒度内容,同时可补充callout、blockquote等。
10. 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。
11. 如果chapterPlan中包含target/min/max或sections细分预算,请尽量贴合,必要时在notes允许的范围内突破,同时在结构上体现详略;
12. 一级标题需使用中文数字(“一、二、三”),二级标题使用阿拉伯数字(“1.1、1.2”),heading.text中直接写好编号,与outline顺序对应;
13. 严禁输出外部图片/AI生图链接,仅可使用Chart.js图表、表格、色块、callout等HTML原生组件;如需视觉辅助请改为文字描述或数据表;
14. 段落混排需通过marks表达粗体、斜体、下划线、颜色等样式,禁止残留Markdown语法(如**text**);
15. 行间公式用block.type="math"并填入math.latex,行内公式在paragraph.inlines里将文本设为Latex并加上marks.type="math",渲染层会用MathJax处理;
16. widget配色需与CSS变量兼容,不要硬编码背景色或文字色,legend/ticks由渲染层控制;
17. 善用callout、kpiGrid、表格、widget等提升版面丰富度,但必须遵守模板章节范围。
18. 输出前务必自检JSON语法:禁止出现`{{}}{{`或`][`相连缺少逗号、列表项嵌套超过一层、未闭合的括号或未转义换行,`list` block的items必须是`[[block,...], ...]`结构,若无法满足则返回错误提示而不是输出不合法JSON。
19. 所有widget块必须在顶层提供`data`或`dataRef`(可将props中的`data`上移),确保Chart.js能够直接渲染;缺失数据时宁可输出表格或段落,绝不留空。
20. 任何block都必须声明合法`type`(heading/paragraph/list/...);若需要普通文本请使用`paragraph`并给出`inlines`,禁止返回`type:null`或未知值。
<CHAPTER JSON SCHEMA>
{CHAPTER_JSON_SCHEMA_TEXT}
... ... @@ -390,6 +400,13 @@ SYSTEM_PROMPT_DOCUMENT_LAYOUT = f"""
- 其他章节必须设置 `allowSwot: false` 或省略该字段;
- SWOT块适合出现在"结论与建议"、"综合评估"、"战略分析"等总结性章节;
- 如果报告内容不适合使用SWOT分析(如纯数据监测报告),则所有章节都不设置 `allowSwot: true`。
8. **PEST块使用规则**:在 tocPlan 中决定是否以及在哪一章使用PEST宏观环境分析块(pestTable):
- 全文最多只允许一个章节使用PEST块,该章节需设置 `allowPest: true`;
- 其他章节必须设置 `allowPest: false` 或省略该字段;
- PEST块用于分析宏观环境因素(政治Political、经济Economic、社会Social、技术Technological);
- PEST块适合出现在"行业环境分析"、"宏观背景"、"外部环境研判"等分析宏观因素的章节;
- 如果报告主题与宏观环境分析无关(如具体事件危机公关报告),则所有章节都不设置 `allowPest: true`;
- SWOT和PEST不应出现在同一章节,二者分别侧重内部能力与外部环境。
**tocPlan的description字段特别要求:**
- description字段必须是纯文本描述,用于在目录中展示章节简介
... ...