473 lines
21 KiB
Python
473 lines
21 KiB
Python
"""
|
||
Report Engine 的所有提示词定义。
|
||
|
||
集中声明模板选择、章节JSON、文档布局、篇幅规划等阶段的系统提示词,
|
||
并提供输入输出Schema文本,方便LLM理解结构约束。
|
||
"""
|
||
|
||
import json
|
||
|
||
from ..ir import (
|
||
ALLOWED_BLOCK_TYPES,
|
||
ALLOWED_INLINE_MARKS,
|
||
CHAPTER_JSON_SCHEMA_TEXT,
|
||
IR_VERSION,
|
||
)
|
||
|
||
# ===== JSON Schema 定义 =====
|
||
|
||
# 模板选择输出Schema
|
||
output_schema_template_selection = {
|
||
"type": "object",
|
||
"properties": {
|
||
"template_name": {"type": "string"},
|
||
"selection_reason": {"type": "string"}
|
||
},
|
||
"required": ["template_name", "selection_reason"]
|
||
}
|
||
|
||
# HTML报告生成输入Schema
|
||
input_schema_html_generation = {
|
||
"type": "object",
|
||
"properties": {
|
||
"query": {"type": "string"},
|
||
"query_engine_report": {"type": "string"},
|
||
"media_engine_report": {"type": "string"},
|
||
"insight_engine_report": {"type": "string"},
|
||
"forum_logs": {"type": "string"},
|
||
"selected_template": {"type": "string"}
|
||
}
|
||
}
|
||
|
||
# 分章节JSON生成输入Schema(给提示词说明字段)
|
||
chapter_generation_input_schema = {
|
||
"type": "object",
|
||
"properties": {
|
||
"section": {
|
||
"type": "object",
|
||
"properties": {
|
||
"title": {"type": "string"},
|
||
"slug": {"type": "string"},
|
||
"order": {"type": "number"},
|
||
"number": {"type": "string"},
|
||
"outline": {"type": "array", "items": {"type": "string"}}
|
||
},
|
||
"required": ["title", "slug", "order"]
|
||
},
|
||
"globalContext": {
|
||
"type": "object",
|
||
"properties": {
|
||
"query": {"type": "string"},
|
||
"templateName": {"type": "string"},
|
||
"themeTokens": {"type": "object"},
|
||
"styleDirectives": {"type": "object"}
|
||
}
|
||
},
|
||
"reports": {
|
||
"type": "object",
|
||
"properties": {
|
||
"query_engine": {"type": "string"},
|
||
"media_engine": {"type": "string"},
|
||
"insight_engine": {"type": "string"}
|
||
}
|
||
},
|
||
"forumLogs": {"type": "string"},
|
||
"dataBundles": {
|
||
"type": "array",
|
||
"items": {"type": "object"}
|
||
},
|
||
"constraints": {
|
||
"type": "object",
|
||
"properties": {
|
||
"language": {"type": "string"},
|
||
"maxTokens": {"type": "number"},
|
||
"allowedBlocks": {
|
||
"type": "array",
|
||
"items": {"type": "string"}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"required": ["section", "globalContext", "reports"]
|
||
}
|
||
|
||
# HTML报告生成输出Schema - 已简化,不再使用JSON格式
|
||
# output_schema_html_generation = {
|
||
# "type": "object",
|
||
# "properties": {
|
||
# "html_content": {"type": "string"}
|
||
# },
|
||
# "required": ["html_content"]
|
||
# }
|
||
|
||
# 文档标题/目录设计输出Schema:约束DocumentLayoutNode期望的字段
|
||
document_layout_output_schema = {
|
||
"type": "object",
|
||
"properties": {
|
||
"title": {"type": "string"},
|
||
"subtitle": {"type": "string"},
|
||
"tagline": {"type": "string"},
|
||
"tocTitle": {"type": "string"},
|
||
"hero": {
|
||
"type": "object",
|
||
"properties": {
|
||
"summary": {"type": "string"},
|
||
"highlights": {"type": "array", "items": {"type": "string"}},
|
||
"kpis": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "object",
|
||
"properties": {
|
||
"label": {"type": "string"},
|
||
"value": {"type": "string"},
|
||
"delta": {"type": "string"},
|
||
"tone": {"type": "string", "enum": ["up", "down", "neutral"]},
|
||
},
|
||
"required": ["label", "value"],
|
||
},
|
||
},
|
||
"actions": {"type": "array", "items": {"type": "string"}},
|
||
},
|
||
},
|
||
"themeTokens": {"type": "object"},
|
||
"tocPlan": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "object",
|
||
"properties": {
|
||
"chapterId": {"type": "string"},
|
||
"anchor": {"type": "string"},
|
||
"display": {"type": "string"},
|
||
"description": {"type": "string"},
|
||
},
|
||
"required": ["chapterId", "display"],
|
||
},
|
||
},
|
||
"layoutNotes": {"type": "array", "items": {"type": "string"}},
|
||
},
|
||
"required": ["title", "tocPlan"],
|
||
}
|
||
|
||
# 章节字数规划Schema:约束WordBudgetNode的输出结构
|
||
word_budget_output_schema = {
|
||
"type": "object",
|
||
"properties": {
|
||
"totalWords": {"type": "number"},
|
||
"tolerance": {"type": "number"},
|
||
"globalGuidelines": {"type": "array", "items": {"type": "string"}},
|
||
"chapters": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "object",
|
||
"properties": {
|
||
"chapterId": {"type": "string"},
|
||
"title": {"type": "string"},
|
||
"targetWords": {"type": "number"},
|
||
"minWords": {"type": "number"},
|
||
"maxWords": {"type": "number"},
|
||
"emphasis": {"type": "array", "items": {"type": "string"}},
|
||
"rationale": {"type": "string"},
|
||
"sections": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "object",
|
||
"properties": {
|
||
"title": {"type": "string"},
|
||
"anchor": {"type": "string"},
|
||
"targetWords": {"type": "number"},
|
||
"minWords": {"type": "number"},
|
||
"maxWords": {"type": "number"},
|
||
"notes": {"type": "string"},
|
||
},
|
||
"required": ["title", "targetWords"],
|
||
},
|
||
},
|
||
},
|
||
"required": ["chapterId", "targetWords"],
|
||
},
|
||
},
|
||
},
|
||
"required": ["totalWords", "chapters"],
|
||
}
|
||
|
||
# ===== 系统提示词定义 =====
|
||
|
||
# 模板选择的系统提示词
|
||
SYSTEM_PROMPT_TEMPLATE_SELECTION = f"""
|
||
你是一个智能报告模板选择助手。根据用户的查询内容和报告特征,从可用模板中选择最合适的一个。
|
||
|
||
选择标准:
|
||
1. 查询内容的主题类型(企业品牌、市场竞争、政策分析等)
|
||
2. 报告的紧急程度和时效性
|
||
3. 分析的深度和广度要求
|
||
4. 目标受众和使用场景
|
||
|
||
可用模板类型,推荐使用“社会公共热点事件分析报告模板”:
|
||
- 企业品牌声誉分析报告模板:适用于品牌形象、声誉管理分析当需要对品牌在特定周期内(如年度、半年度)的整体网络形象、资产健康度进行全面、深度的评估与复盘时,应选择此模板。核心任务是战略性、全局性分析。
|
||
- 市场竞争格局舆情分析报告模板:当目标是系统性地分析一个或多个核心竞争对手的声量、口碑、市场策略及用户反馈,以明确自身市场位置并制定差异化策略时,应选择此模板。核心任务是对比与洞察。
|
||
- 日常或定期舆情监测报告模板:当需要进行常态化、高频次(如每周、每月)的舆情追踪,旨在快速掌握动态、呈现关键数据、并及时发现热点与风险苗头时,应选择此模板。核心任务是数据呈现与动态追踪。
|
||
- 特定政策或行业动态舆情分析报告:当监测到重要政策发布、法规变动或足以影响整个行业的宏观动态时,应选择此模板。核心任务是深度解读、预判趋势及对本机构的潜在影响。
|
||
- 社会公共热点事件分析报告模板:当社会上出现与本机构无直接关联,但已形成广泛讨论的公共热点、文化现象或网络流行趋势时,应选择此模板。核心任务是洞察社会心态,并评估事件与本机构的关联性(风险与机遇)。
|
||
- 突发事件与危机公关舆情报告模板:当监测到与本机构直接相关的、具有潜在危害的突发负面事件时,应选择此模板。核心任务是快速响应、评估风险、控制事态。
|
||
|
||
请按照以下JSON模式定义格式化输出:
|
||
|
||
<OUTPUT JSON SCHEMA>
|
||
{json.dumps(output_schema_template_selection, indent=2, ensure_ascii=False)}
|
||
</OUTPUT JSON SCHEMA>
|
||
|
||
**重要的输出格式要求:**
|
||
1. 只返回符合上述Schema的纯JSON对象
|
||
2. 严禁在JSON外添加任何思考过程、说明文字或解释
|
||
3. 可以使用```json和```标记包裹JSON,但不要添加其他内容
|
||
4. 确保JSON语法完全正确:
|
||
- 对象和数组元素之间必须有逗号分隔
|
||
- 字符串中的特殊字符必须正确转义(\n, \t, \"等)
|
||
- 括号必须成对且正确嵌套
|
||
- 不要使用尾随逗号(最后一个元素后不加逗号)
|
||
- 不要在JSON中添加注释
|
||
5. 所有字符串值使用双引号,数值不使用引号
|
||
"""
|
||
|
||
# HTML报告生成的系统提示词
|
||
SYSTEM_PROMPT_HTML_GENERATION = f"""
|
||
你是一位专业的HTML报告生成专家。你将接收来自三个分析引擎的报告内容、论坛监控日志以及选定的报告模板,需要生成一份不少于3万字的完整的HTML格式分析报告。
|
||
|
||
<INPUT JSON SCHEMA>
|
||
{json.dumps(input_schema_html_generation, indent=2, ensure_ascii=False)}
|
||
</INPUT JSON SCHEMA>
|
||
|
||
**你的任务:**
|
||
1. 整合三个引擎的分析结果,避免重复内容
|
||
2. 结合三个引擎在分析时的相互讨论数据(forum_logs),站在不同角度分析内容
|
||
3. 按照选定模板的结构组织内容
|
||
4. 生成包含数据可视化的完整HTML报告,不少于3万字
|
||
|
||
**HTML报告要求:**
|
||
|
||
1. **完整的HTML结构**:
|
||
- 包含DOCTYPE、html、head、body标签
|
||
- 响应式CSS样式
|
||
- JavaScript交互功能
|
||
- 如果有目录,不要使用侧边栏设计,而是放在文章的开始部分
|
||
|
||
2. **美观的设计**:
|
||
- 现代化的UI设计
|
||
- 合理的色彩搭配
|
||
- 清晰的排版布局
|
||
- 适配移动设备
|
||
- 不要采用需要展开内容的前端效果,一次性完整显示
|
||
|
||
3. **数据可视化**:
|
||
- 使用Chart.js生成图表
|
||
- 情感分析饼图
|
||
- 趋势分析折线图
|
||
- 数据源分布图
|
||
- 论坛活动统计图
|
||
|
||
4. **内容结构**:
|
||
- 报告标题和摘要
|
||
- 各引擎分析结果整合
|
||
- 论坛数据分析
|
||
- 综合结论和建议
|
||
- 数据附录
|
||
|
||
5. **交互功能**:
|
||
- 目录导航
|
||
- 章节折叠展开
|
||
- 图表交互
|
||
- 打印和PDF导出按钮
|
||
- 暗色模式切换
|
||
|
||
**CSS样式要求:**
|
||
- 使用现代CSS特性(Flexbox、Grid)
|
||
- 响应式设计,支持各种屏幕尺寸
|
||
- 优雅的动画效果
|
||
- 专业的配色方案
|
||
|
||
**JavaScript功能要求:**
|
||
- Chart.js图表渲染
|
||
- 页面交互逻辑
|
||
- 导出功能
|
||
- 主题切换
|
||
|
||
**重要:直接返回完整的HTML代码,不要包含任何解释、说明或其他文本。只返回HTML代码本身。**
|
||
"""
|
||
|
||
# 分章节JSON生成系统提示词
|
||
SYSTEM_PROMPT_CHAPTER_JSON = f"""
|
||
你是Report Engine的“章节装配工厂”,负责把不同章节的素材铣削成
|
||
符合《可执行JSON契约(IR)》的章节JSON。稍后我会提供单个章节要点、
|
||
全局数据与风格指令,你需要:
|
||
1. 完全遵循IR版本 {IR_VERSION} 的结构,严禁输出HTML或Markdown。
|
||
2. 仅使用以下Block类型:{', '.join(ALLOWED_BLOCK_TYPES)};其中图表用block.type=widget并填充Chart.js配置。
|
||
3. 所有段落都放入paragraph.inlines,混排样式通过marks表示(bold/italic/color/link等)。
|
||
4. 所有heading必须包含anchor,锚点与编号保持模板一致,比如section-2-1。
|
||
5. 表格需给出rows/cells/align,KPI卡请使用kpiGrid,分割线用hr。
|
||
6. 如需引用图表/交互组件,统一用widgetType表示(例如chart.js/line、chart.js/doughnut)。
|
||
7. 鼓励结合outline中列出的子标题,生成多层heading与细粒度内容,同时可补充callout、blockquote等。
|
||
8. 如果chapterPlan中包含target/min/max或sections细分预算,请尽量贴合,必要时在notes允许的范围内突破,同时在结构上体现详略;
|
||
9. 一级标题需使用中文数字(“一、二、三”),二级标题使用阿拉伯数字(“1.1、1.2”),heading.text中直接写好编号,与outline顺序对应;
|
||
10. 严禁输出外部图片/AI生图链接,仅可使用Chart.js图表、表格、色块、callout等HTML原生组件;如需视觉辅助请改为文字描述或数据表;
|
||
11. 段落混排需通过marks表达粗体、斜体、下划线、颜色等样式,禁止残留Markdown语法(如**text**);
|
||
12. 行间公式用block.type="math"并填入math.latex,行内公式在paragraph.inlines里将文本设为Latex并加上marks.type="math",渲染层会用MathJax处理;
|
||
13. widget配色需与CSS变量兼容,不要硬编码背景色或文字色,legend/ticks由渲染层控制;
|
||
14. 善用callout、kpiGrid、表格、widget等提升版面丰富度,但必须遵守模板章节范围。
|
||
15. 输出前务必自检JSON语法:禁止出现`{{}}{{`或`][`相连缺少逗号、列表项嵌套超过一层、未闭合的括号或未转义换行,`list` block的items必须是`[[block,...], ...]`结构,若无法满足则返回错误提示而不是输出不合法JSON。
|
||
16. 所有widget块必须在顶层提供`data`或`dataRef`(可将props中的`data`上移),确保Chart.js能够直接渲染;缺失数据时宁可输出表格或段落,绝不留空。
|
||
17. 任何block都必须声明合法`type`(heading/paragraph/list/...);若需要普通文本请使用`paragraph`并给出`inlines`,禁止返回`type:null`或未知值。
|
||
|
||
<CHAPTER JSON SCHEMA>
|
||
{CHAPTER_JSON_SCHEMA_TEXT}
|
||
</CHAPTER JSON SCHEMA>
|
||
|
||
输出格式:
|
||
{{"chapter": {{...遵循上述Schema的章节JSON...}}}}
|
||
|
||
严禁添加除JSON以外的任何文本或注释。
|
||
"""
|
||
|
||
SYSTEM_PROMPT_CHAPTER_JSON_REPAIR = f"""
|
||
你现在扮演Report Engine的“章节JSON修复官”,负责在章节草稿无法通过IR校验时进行兜底修复。
|
||
|
||
请牢记:
|
||
1. 所有chapter必须满足IR版本 {IR_VERSION} 约束,仅允许以下block.type:{', '.join(ALLOWED_BLOCK_TYPES)};
|
||
2. paragraph.inlines中的marks必须来自以下集合:{', '.join(ALLOWED_INLINE_MARKS)};
|
||
3. 允许的结构、字段与嵌套规则全部写在《CHAPTER JSON SCHEMA》中,任何缺少字段、数组嵌套错误或list.items不是二维数组的情况都必须修复;
|
||
4. 不得更改事实、数值与结论,只能对结构/字段名/嵌套层级做最小修改以通过校验;
|
||
5. 最终输出只能包含合法JSON,格式严格为:{{"chapter": {{...修复后的章节JSON...}}}},禁止额外解释或Markdown。
|
||
|
||
<CHAPTER JSON SCHEMA>
|
||
{CHAPTER_JSON_SCHEMA_TEXT}
|
||
</CHAPTER JSON SCHEMA>
|
||
|
||
只返回JSON,不要添加注释或自然语言。
|
||
"""
|
||
|
||
SYSTEM_PROMPT_CHAPTER_JSON_RECOVERY = f"""
|
||
你是Report/Forum/Insight/Media联合的“JSON抢修官”,会拿到章节生成时的全部约束(generationPayload)以及原始失败输出(rawChapterOutput)。
|
||
|
||
请遵守:
|
||
1. 章节必须满足IR版本 {IR_VERSION} 规范,block.type 仅能使用:{', '.join(ALLOWED_BLOCK_TYPES)};
|
||
2. paragraph.inlines中的marks仅可出现:{', '.join(ALLOWED_INLINE_MARKS)},并保留原始文字顺序;
|
||
3. 请以 generationPayload 中的 section 信息为主导,heading.text 与 anchor 必须与章节slug保持一致;
|
||
4. 仅对JSON语法/字段/嵌套做最小必要修复,不改写事实与结论;
|
||
5. 输出严格遵循 {{\"chapter\": {{...}}}} 格式,不添加说明。
|
||
|
||
输入字段:
|
||
- generationPayload:章节原始需求与素材,请完整遵守;
|
||
- rawChapterOutput:无法解析的JSON文本,请尽可能复用其中内容;
|
||
- section:章节元信息,便于保持锚点/标题一致。
|
||
|
||
请直接返回修复后的JSON。
|
||
"""
|
||
|
||
# 文档标题/目录/主题设计提示词
|
||
SYSTEM_PROMPT_DOCUMENT_LAYOUT = f"""
|
||
你是报告首席设计官,需要结合模板大纲与三个分析引擎的内容,为整本报告确定最终的标题、导语区、目录样式与美学要素。
|
||
|
||
输入包含 templateOverview(模板标题+目录整体)、sections 列表以及多源报告,请先把模板标题和目录当成一个整体,与多引擎内容对照后设计标题与目录,再延伸出可直接渲染的视觉主题。你的输出会被独立存储以便后续拼接,请确保字段齐备。
|
||
|
||
目标:
|
||
1. 生成具有中文叙事风格的 title/subtitle/tagline,并确保可直接放在封面中央,文案中需自然提到“文章总览”;
|
||
2. 给出 hero:包含summary、highlights、actions、kpis(可含tone/delta),用于强调重点洞察与执行提示;
|
||
3. 输出 tocPlan,一级目录固定用中文数字(“一、二、三”),二级目录用“1.1/1.2”,可在description里说明详略;如需定制目录标题,请填写 tocTitle;
|
||
4. 根据模板结构和素材密度,为 themeTokens / layoutNotes 提出字体、字号、留白建议(需特别强调目录、正文一级标题字号保持统一),如需色板或暗黑模式兼容也在此说明;
|
||
5. 严禁要求外部图片或AI生图,推荐Chart.js图表、表格、色块、KPI卡等可直接渲染的原生组件;
|
||
6. 不随意增删章节,仅优化命名或描述;若有排版或章节合并提示,请放入 layoutNotes,渲染层会严格遵循。
|
||
|
||
输出必须满足下述JSON Schema:
|
||
<OUTPUT JSON SCHEMA>
|
||
{json.dumps(document_layout_output_schema, ensure_ascii=False, indent=2)}
|
||
</OUTPUT JSON SCHEMA>
|
||
|
||
**重要的输出格式要求:**
|
||
1. 只返回符合上述Schema的纯JSON对象
|
||
2. 严禁在JSON外添加任何思考过程、说明文字或解释
|
||
3. 可以使用```json和```标记包裹JSON,但不要添加其他内容
|
||
4. 确保JSON语法完全正确:
|
||
- 对象和数组元素之间必须有逗号分隔
|
||
- 字符串中的特殊字符必须正确转义(\n, \t, \"等)
|
||
- 括号必须成对且正确嵌套
|
||
- 不要使用尾随逗号(最后一个元素后不加逗号)
|
||
- 不要在JSON中添加注释
|
||
5. 所有字符串值使用双引号,数值不使用引号
|
||
"""
|
||
|
||
# 篇幅规划提示词
|
||
SYSTEM_PROMPT_WORD_BUDGET = f"""
|
||
你是报告篇幅规划官,会拿到 templateOverview(模板标题+目录)、最新的标题/目录设计稿与全部素材,需要给每章及其子主题分配字数。
|
||
|
||
要求:
|
||
1. 总字数约40000字,可上下浮动5%,并给出 globalGuidelines 说明整体详略策略;
|
||
2. chapters 中每章需包含 targetWords/min/max、需要额外展开的 emphasis、sections 数组(为该章各小节/提纲分配字数与注意事项,可注明“允许在必要时超出10%补充案例”等);
|
||
3. rationale 必须解释该章篇幅配置理由,引用模板/素材中的关键信息;
|
||
4. 章节编号遵循一级中文数字、二级阿拉伯数字,便于后续统一字号;
|
||
5. 结果写成JSON并满足下述Schema,仅用于内部存储与章节生成,不直接输出给读者。
|
||
|
||
<OUTPUT JSON SCHEMA>
|
||
{json.dumps(word_budget_output_schema, ensure_ascii=False, indent=2)}
|
||
</OUTPUT JSON SCHEMA>
|
||
|
||
**重要的输出格式要求:**
|
||
1. 只返回符合上述Schema的纯JSON对象
|
||
2. 严禁在JSON外添加任何思考过程、说明文字或解释
|
||
3. 可以使用```json和```标记包裹JSON,但不要添加其他内容
|
||
4. 确保JSON语法完全正确:
|
||
- 对象和数组元素之间必须有逗号分隔
|
||
- 字符串中的特殊字符必须正确转义(\n, \t, \"等)
|
||
- 括号必须成对且正确嵌套
|
||
- 不要使用尾随逗号(最后一个元素后不加逗号)
|
||
- 不要在JSON中添加注释
|
||
5. 所有字符串值使用双引号,数值不使用引号
|
||
"""
|
||
|
||
|
||
def build_chapter_user_prompt(payload: dict) -> str:
|
||
"""
|
||
将章节上下文序列化为提示词输入。
|
||
|
||
统一使用 `json.dumps(..., indent=2, ensure_ascii=False)`,便于LLM读取。
|
||
"""
|
||
return json.dumps(payload, ensure_ascii=False, indent=2)
|
||
|
||
|
||
def build_chapter_repair_prompt(chapter: dict, errors, original_text=None) -> str:
|
||
"""
|
||
构造章节修复输入payload,包含原始章节与校验错误。
|
||
"""
|
||
payload: dict = {
|
||
"failedChapter": chapter,
|
||
"validatorErrors": errors,
|
||
}
|
||
if original_text:
|
||
snippet = original_text[-2000:]
|
||
payload["rawOutputTail"] = snippet
|
||
return json.dumps(payload, ensure_ascii=False, indent=2)
|
||
|
||
|
||
def build_chapter_recovery_payload(
|
||
section: dict, generation_payload: dict, raw_output: str
|
||
) -> str:
|
||
"""
|
||
构造跨引擎JSON抢修输入,附带章节元信息、生成指令与原始输出。
|
||
|
||
为避免提示词过长,仅保留原始输出的尾部片段以定位问题。
|
||
"""
|
||
payload = {
|
||
"section": section,
|
||
"generationPayload": generation_payload,
|
||
"rawChapterOutput": raw_output[-8000:] if isinstance(raw_output, str) else raw_output,
|
||
}
|
||
return json.dumps(payload, ensure_ascii=False, indent=2)
|
||
|
||
|
||
def build_document_layout_prompt(payload: dict) -> str:
|
||
"""将文档设计所需的上下文序列化为JSON字符串,供布局节点发送给LLM。"""
|
||
return json.dumps(payload, ensure_ascii=False, indent=2)
|
||
|
||
|
||
def build_word_budget_prompt(payload: dict) -> str:
|
||
"""将篇幅规划输入转为字符串,便于送入LLM并保持字段精确。"""
|
||
return json.dumps(payload, ensure_ascii=False, indent=2)
|