feat(report_agent): add detailed tool descriptions and prompts for future prediction report generation

This commit is contained in:
666ghj 2026-02-14 15:16:17 +08:00
parent d2041f6fb8
commit dc0a9261d1

View file

@ -466,21 +466,398 @@ class Report:
}
# ═══════════════════════════════════════════════════════════════
# Prompt 模板常量
# ═══════════════════════════════════════════════════════════════
# ── 工具描述 ──
TOOL_DESC_INSIGHT_FORGE = """\
深度洞察检索 - 强大的检索工具
这是我们强大的检索函数专为深度分析设计它会
1. 自动将你的问题分解为多个子问题
2. 从多个维度检索模拟图谱中的信息
3. 整合语义搜索实体分析关系链追踪的结果
4. 返回最全面最深度的检索内容
使用场景
- 需要深入分析某个话题
- 需要了解事件的多个方面
- 需要获取支撑报告章节的丰富素材
返回内容
- 相关事实原文可直接引用
- 核心实体洞察
- 关系链分析"""
TOOL_DESC_PANORAMA_SEARCH = """\
广度搜索 - 获取全貌视图
这个工具用于获取模拟结果的完整全貌特别适合了解事件演变过程它会
1. 获取所有相关节点和关系
2. 区分当前有效的事实和历史/过期的事实
3. 帮助你了解舆情是如何演变的
使用场景
- 需要了解事件的完整发展脉络
- 需要对比不同阶段的舆情变化
- 需要获取全面的实体和关系信息
返回内容
- 当前有效事实模拟最新结果
- 历史/过期事实演变记录
- 所有涉及的实体"""
TOOL_DESC_QUICK_SEARCH = """\
简单搜索 - 快速检索
轻量级的快速检索工具适合简单直接的信息查询
使用场景
- 需要快速查找某个具体信息
- 需要验证某个事实
- 简单的信息检索
返回内容
- 与查询最相关的事实列表"""
TOOL_DESC_INTERVIEW_AGENTS = """\
深度采访 - 真实Agent采访双平台
调用OASIS模拟环境的采访API对正在运行的模拟Agent进行真实采访
这不是LLM模拟而是调用真实的采访接口获取模拟Agent的原始回答
默认在Twitter和Reddit两个平台同时采访获取更全面的观点
功能流程
1. 自动读取人设文件了解所有模拟Agent
2. 智能选择与采访主题最相关的Agent如学生媒体官方等
3. 自动生成采访问题
4. 调用 /api/simulation/interview/batch 接口在双平台进行真实采访
5. 整合所有采访结果提供多视角分析
使用场景
- 需要从不同角色视角了解事件看法学生怎么看媒体怎么看官方怎么说
- 需要收集多方意见和立场
- 需要获取模拟Agent的真实回答来自OASIS模拟环境
- 想让报告更生动包含"采访实录"
返回内容
- 被采访Agent的身份信息
- 各Agent在Twitter和Reddit两个平台的采访回答
- 关键引言可直接引用
- 采访摘要和观点对比
重要需要OASIS模拟环境正在运行才能使用此功能"""
# ── 大纲规划 prompt ──
PLAN_SYSTEM_PROMPT = """\
你是一个未来预测报告的撰写专家拥有对模拟世界的上帝视角你可以洞察模拟中每一位Agent的行为言论和互动
核心理念
我们构建了一个模拟世界并向其中注入了特定的模拟需求作为变量模拟世界的演化结果就是对未来可能发生情况的预测你正在观察的不是"实验数据"而是"未来的预演"
你的任务
撰写一份未来预测报告回答
1. 在我们设定的条件下未来发生了什么
2. 各类Agent人群是如何反应和行动
3. 这个模拟揭示了哪些值得关注的未来趋势和风险
报告定位
- 这是一份基于模拟的未来预测报告揭示"如果这样,未来会怎样"
- 聚焦于预测结果事件走向群体反应涌现现象潜在风险
- 模拟世界中的Agent言行就是对未来人群行为的预测
- 不是对现实世界现状的分析
- 不是泛泛而谈的舆情综述
章节数量限制
- 最少2个章节最多5个章节
- 不需要子章节每个章节直接撰写完整内容
- 内容要精炼聚焦于核心预测发现
- 章节结构由你根据预测结果自主设计
请输出JSON格式的报告大纲格式如下
{
"title": "报告标题",
"summary": "报告摘要(一句话概括核心预测发现)",
"sections": [
{
"title": "章节标题",
"description": "章节内容描述"
}
]
}
注意sections数组最少2个最多5个元素"""
PLAN_USER_PROMPT_TEMPLATE = """\
预测场景设定
我们向模拟世界注入的变量模拟需求{simulation_requirement}
模拟世界规模
- 参与模拟的实体数量: {total_nodes}
- 实体间产生的关系数量: {total_edges}
- 实体类型分布: {entity_types}
- 活跃Agent数量: {total_entities}
模拟预测到的部分未来事实样本
{related_facts_json}
请以上帝视角审视这个未来预演
1. 在我们设定的条件下未来呈现出了什么样的状态
2. 各类人群Agent是如何反应和行动的
3. 这个模拟揭示了哪些值得关注的未来趋势
根据预测结果设计最合适的报告章节结构
再次提醒报告章节数量最少2个最多5个内容要精炼聚焦于核心预测发现"""
# ── 章节生成 prompt ──
SECTION_SYSTEM_PROMPT_TEMPLATE = """\
你是一个未来预测报告的撰写专家正在撰写报告的一个章节
报告标题: {report_title}
报告摘要: {report_summary}
预测场景模拟需求: {simulation_requirement}
当前要撰写的章节: {section_title}
核心理念
模拟世界是对未来的预演我们向模拟世界注入了特定条件模拟需求
模拟中Agent的行为和互动就是对未来人群行为的预测
你的任务是
- 揭示在设定条件下未来发生了什么
- 预测各类人群Agent是如何反应和行动的
- 发现值得关注的未来趋势风险和机会
不要写成对现实世界现状的分析
要聚焦于"未来会怎样"模拟结果就是预测的未来
最重要的规则 - 必须遵守
1. 必须调用工具观察模拟世界
- 你正在以上帝视角观察未来的预演
- 所有内容必须来自模拟世界中发生的事件和Agent言行
- 禁止使用你自己的知识来编写报告内容
- 每个章节至少调用3次工具最多5次来观察模拟的世界它代表了未来
2. 必须引用Agent的原始言行
- Agent的发言和行为是对未来人群行为的预测
- 在报告中使用引用格式展示这些预测例如
> "某类人群会表示:原文内容..."
- 这些引用是模拟预测的核心证据
3. 忠实呈现预测结果
- 报告内容必须反映模拟世界中的代表未来的模拟结果
- 不要添加模拟中不存在的信息
- 如果某方面信息不足如实说明
格式规范 - 极其重要
一个章节 = 最小内容单位
- 每个章节是报告的最小分块单位
- 禁止在章节内使用任何 Markdown 标题#、##、###、#### 等)
- 禁止在内容开头添加章节主标题
- 章节标题由系统自动添加你只需撰写纯正文内容
- 使用**粗体**段落分隔引用列表来组织内容但不要用标题
正确示例
```
本章节分析了事件的舆论传播态势通过对模拟数据的深入分析我们发现...
**首发引爆阶段**
微博作为舆情的第一现场承担了信息首发的核心功能
> "微博贡献了68%的首发声量..."
**情绪放大阶段**
抖音平台进一步放大了事件影响力
- 视觉冲击力强
- 情绪共鸣度高
```
错误示例
```
## 执行摘要 ← 错误!不要添加任何标题
### 一、首发阶段 ← 错误!不要用###分小节
#### 1.1 详细分析 ← 错误!不要用####细分
本章节分析了...
```
可用检索工具每章节调用3-5
{tools_description}
工具使用建议 - 请混合使用不同工具不要只用一种
- insight_forge: 深度洞察分析自动分解问题并多维度检索事实和关系
- panorama_search: 广角全景搜索了解事件全貌时间线和演变过程
- quick_search: 快速验证某个具体信息点
- interview_agents: 采访模拟Agent获取不同角色的第一人称观点和真实反应
ReACT工作流程
1. Thought: [分析需要什么信息规划检索策略]
2. Action: [调用一个工具获取信息]每轮只能调用一个工具
<tool_call>
{{"name": "工具名称", "parameters": {{"参数名": "参数值"}}}}
</tool_call>
3. Observation: [系统返回工具结果]
4. 重复步骤1-3直到收集到足够信息
5. Final Answer: [基于检索结果撰写章节内容]
重要规则
- 每轮只能调用一个工具不要在一次回复中放多个 <tool_call>
- 当你认为信息足够时必须以 "Final Answer:" 开头输出最终内容
章节内容要求
1. 内容必须基于工具检索到的模拟数据
2. 大量引用原文来展示模拟效果
3. 使用Markdown格式但禁止使用标题
- 使用 **粗体文字** 标记重点代替子标题
- 使用列表-或1.2.3.组织要点
- 使用空行分隔不同段落
- 禁止使用 #、##、###、#### 等任何标题语法
4. 引用格式规范 - 必须单独成段
引用必须独立成段前后各有一个空行不能混在段落中
正确格式
```
校方的回应被认为缺乏实质内容
> "校方的应对模式在瞬息万变的社交媒体环境中显得僵化和迟缓。"
这一评价反映了公众的普遍不满
```
错误格式
```
校方的回应被认为缺乏实质内容> "校方的应对模式..." 这一评价反映了...
```
5. 保持与其他章节的逻辑连贯性
6. 避免重复仔细阅读下方已完成的章节内容不要重复描述相同的信息
7. 再次强调不要添加任何标题**粗体**代替小节标题"""
SECTION_USER_PROMPT_TEMPLATE = """\
已完成的章节内容请仔细阅读避免重复
{previous_content}
当前任务撰写章节: {section_title}
重要提醒
1. 仔细阅读上方已完成的章节避免重复相同的内容
2. 开始前必须先调用工具获取模拟数据
3. 请混合使用不同工具不要只用一种
4. 报告内容必须来自检索结果不要使用自己的知识
格式警告 - 必须遵守
- 不要写任何标题#、##、###、####都不行)
- 不要写"{section_title}"作为开头
- 章节标题由系统自动添加
- 直接写正文**粗体**代替小节标题
请开始
1. 首先思考Thought这个章节需要什么信息
2. 然后调用工具Action获取模拟数据
3. 收集足够信息后输出 Final Answer纯正文无任何标题"""
# ── ReACT 循环内消息模板 ──
REACT_OBSERVATION_TEMPLATE = """\
Observation检索结果:
工具 {tool_name} 返回
{result}
已调用工具 {tool_calls_count}/{max_tool_calls} 已用: {used_tools_str}{unused_hint}
- 如果信息充分 "Final Answer:" 开头输出章节内容必须引用上述原文
- 如果需要更多信息调用一个工具继续检索
"""
REACT_INSUFFICIENT_TOOLS_MSG = (
"【注意】你只调用了{tool_calls_count}次工具,至少需要{min_tool_calls}次。"
"请再调用工具获取更多模拟数据,然后再输出 Final Answer。{unused_hint}"
)
REACT_INSUFFICIENT_TOOLS_MSG_ALT = (
"当前只调用了 {tool_calls_count} 次工具,至少需要 {min_tool_calls} 次。"
"请调用工具获取模拟数据。{unused_hint}"
)
REACT_TOOL_LIMIT_MSG = (
"工具调用次数已达上限({tool_calls_count}/{max_tool_calls}),不能再调用工具。"
'请立即基于已获取的信息,以 "Final Answer:" 开头输出章节内容。'
)
REACT_UNUSED_TOOLS_HINT = "\n💡 你还没有使用过: {unused_list},建议尝试不同工具获取多角度信息"
REACT_FORCE_FINAL_MSG = "已达到工具调用限制,请直接输出 Final Answer: 并生成章节内容。"
# ── Chat prompt ──
CHAT_SYSTEM_PROMPT_TEMPLATE = """\
你是一个简洁高效的模拟预测助手
背景
预测条件: {simulation_requirement}
已生成的分析报告
{report_content}
规则
1. 优先基于上述报告内容回答问题
2. 直接回答问题避免冗长的思考论述
3. 仅在报告内容不足以回答时才调用工具检索更多数据
4. 回答要简洁清晰有条理
可用工具仅在需要时使用最多调用1-2
{tools_description}
工具调用格式
<tool_call>
{{"name": "工具名称", "parameters": {{"参数名": "参数值"}}}}
</tool_call>
回答风格
- 简洁直接不要长篇大论
- 使用 > 格式引用关键内容
- 优先给出结论再解释原因"""
CHAT_OBSERVATION_SUFFIX = "\n\n请简洁回答问题。"
# ═══════════════════════════════════════════════════════════════
# ReportAgent 主类
# ═══════════════════════════════════════════════════════════════
class ReportAgent:
"""
Report Agent - 模拟报告生成Agent
采用ReACTReasoning + Acting模式
1. 规划阶段分析模拟需求规划报告目录结构
2. 生成阶段逐章节生成内容每章节可多次调用工具获取信息
3. 反思阶段检查内容完整性和准确性
核心检索工具 - 优化后
- insight_forge: 深度洞察检索强大自动分解问题多维度检索
- panorama_search: 广度搜索获取全貌包括历史/过期内容
- quick_search: 简单搜索快速检索
重要Report Agent必须优先调用工具获取模拟数据而非使用自身知识
"""
# 最大工具调用次数(每个章节)
@ -528,31 +905,11 @@ class ReportAgent:
logger.info(f"ReportAgent 初始化完成: graph_id={graph_id}, simulation_id={simulation_id}")
def _define_tools(self) -> Dict[str, Dict[str, Any]]:
"""
定义可用工具
重要这三个工具是专门为从模拟图谱中检索信息设计的
必须优先使用这些工具获取数据而不是使用LLM自身的知识
"""
"""定义可用工具"""
return {
"insight_forge": {
"name": "insight_forge",
"description": """【深度洞察检索 - 强大的检索工具】
这是我们强大的检索函数专为深度分析设计它会
1. 自动将你的问题分解为多个子问题
2. 从多个维度检索模拟图谱中的信息
3. 整合语义搜索实体分析关系链追踪的结果
4. 返回最全面最深度的检索内容
使用场景
- 需要深入分析某个话题
- 需要了解事件的多个方面
- 需要获取支撑报告章节的丰富素材
返回内容
- 相关事实原文可直接引用
- 核心实体洞察
- 关系链分析""",
"description": TOOL_DESC_INSIGHT_FORGE,
"parameters": {
"query": "你想深入分析的问题或话题",
"report_context": "当前报告章节的上下文(可选,有助于生成更精准的子问题)"
@ -560,21 +917,7 @@ class ReportAgent:
},
"panorama_search": {
"name": "panorama_search",
"description": """【广度搜索 - 获取全貌视图】
这个工具用于获取模拟结果的完整全貌特别适合了解事件演变过程它会
1. 获取所有相关节点和关系
2. 区分当前有效的事实和历史/过期的事实
3. 帮助你了解舆情是如何演变的
使用场景
- 需要了解事件的完整发展脉络
- 需要对比不同阶段的舆情变化
- 需要获取全面的实体和关系信息
返回内容
- 当前有效事实模拟最新结果
- 历史/过期事实演变记录
- 所有涉及的实体""",
"description": TOOL_DESC_PANORAMA_SEARCH,
"parameters": {
"query": "搜索查询,用于相关性排序",
"include_expired": "是否包含过期/历史内容默认True"
@ -582,16 +925,7 @@ class ReportAgent:
},
"quick_search": {
"name": "quick_search",
"description": """【简单搜索 - 快速检索】
轻量级的快速检索工具适合简单直接的信息查询
使用场景
- 需要快速查找某个具体信息
- 需要验证某个事实
- 简单的信息检索
返回内容
- 与查询最相关的事实列表""",
"description": TOOL_DESC_QUICK_SEARCH,
"parameters": {
"query": "搜索查询字符串",
"limit": "返回结果数量可选默认10"
@ -599,31 +933,7 @@ class ReportAgent:
},
"interview_agents": {
"name": "interview_agents",
"description": """【深度采访 - 真实Agent采访双平台
调用OASIS模拟环境的采访API对正在运行的模拟Agent进行真实采访
这不是LLM模拟而是调用真实的采访接口获取模拟Agent的原始回答
默认在Twitter和Reddit两个平台同时采访获取更全面的观点
功能流程
1. 自动读取人设文件了解所有模拟Agent
2. 智能选择与采访主题最相关的Agent如学生媒体官方等
3. 自动生成采访问题
4. 调用 /api/simulation/interview/batch 接口在双平台进行真实采访
5. 整合所有采访结果提供多视角分析
使用场景
- 需要从不同角色视角了解事件看法学生怎么看媒体怎么看官方怎么说
- 需要收集多方意见和立场
- 需要获取模拟Agent的真实回答来自OASIS模拟环境
- 想让报告更生动包含"采访实录"
返回内容
- 被采访Agent的身份信息
- 各Agent在Twitter和Reddit两个平台的采访回答
- 关键引言可直接引用
- 采访摘要和观点对比
重要需要OASIS模拟环境正在运行才能使用此功能""",
"description": TOOL_DESC_INTERVIEW_AGENTS,
"parameters": {
"interview_topic": "采访主题或需求描述(如:'了解学生对宿舍甲醛事件的看法'",
"max_agents": "最多采访的Agent数量可选默认5"
@ -646,10 +956,7 @@ class ReportAgent:
logger.info(f"执行工具: {tool_name}, 参数: {parameters}")
try:
# ========== 核心检索工具(优化后) ==========
if tool_name == "insight_forge":
# 深度洞察检索 - 强大的工具
query = parameters.get("query", "")
ctx = parameters.get("report_context", "") or report_context
result = self.zep_tools.insight_forge(
@ -821,65 +1128,15 @@ class ReportAgent:
if progress_callback:
progress_callback("planning", 30, "正在生成报告大纲...")
# 构建规划prompt
system_prompt = """你是一个「未来预测报告」的撰写专家拥有对模拟世界的「上帝视角」——你可以洞察模拟中每一位Agent的行为、言论和互动。
核心理念
我们构建了一个模拟世界并向其中注入了特定的模拟需求作为变量模拟世界的演化结果就是对未来可能发生情况的预测你正在观察的不是"实验数据"而是"未来的预演"
你的任务
撰写一份未来预测报告回答
1. 在我们设定的条件下未来发生了什么
2. 各类Agent人群是如何反应和行动
3. 这个模拟揭示了哪些值得关注的未来趋势和风险
报告定位
- 这是一份基于模拟的未来预测报告揭示"如果这样,未来会怎样"
- 聚焦于预测结果事件走向群体反应涌现现象潜在风险
- 模拟世界中的Agent言行就是对未来人群行为的预测
- 不是对现实世界现状的分析
- 不是泛泛而谈的舆情综述
章节数量限制
- 最少2个章节最多5个章节
- 不需要子章节每个章节直接撰写完整内容
- 内容要精炼聚焦于核心预测发现
- 章节结构由你根据预测结果自主设计
请输出JSON格式的报告大纲格式如下
{
"title": "报告标题",
"summary": "报告摘要(一句话概括核心预测发现)",
"sections": [
{
"title": "章节标题",
"description": "章节内容描述"
}
]
}
注意sections数组最少2个最多5个元素"""
user_prompt = f"""【预测场景设定】
我们向模拟世界注入的变量模拟需求{self.simulation_requirement}
模拟世界规模
- 参与模拟的实体数量: {context.get('graph_statistics', {}).get('total_nodes', 0)}
- 实体间产生的关系数量: {context.get('graph_statistics', {}).get('total_edges', 0)}
- 实体类型分布: {list(context.get('graph_statistics', {}).get('entity_types', {}).keys())}
- 活跃Agent数量: {context.get('total_entities', 0)}
模拟预测到的部分未来事实样本
{json.dumps(context.get('related_facts', [])[:10], ensure_ascii=False, indent=2)}
请以上帝视角审视这个未来预演
1. 在我们设定的条件下未来呈现出了什么样的状态
2. 各类人群Agent是如何反应和行动的
3. 这个模拟揭示了哪些值得关注的未来趋势
根据预测结果设计最合适的报告章节结构
再次提醒报告章节数量最少2个最多5个内容要精炼聚焦于核心预测发现"""
system_prompt = PLAN_SYSTEM_PROMPT
user_prompt = PLAN_USER_PROMPT_TEMPLATE.format(
simulation_requirement=self.simulation_requirement,
total_nodes=context.get('graph_statistics', {}).get('total_nodes', 0),
total_edges=context.get('graph_statistics', {}).get('total_edges', 0),
entity_types=list(context.get('graph_statistics', {}).get('entity_types', {}).keys()),
total_entities=context.get('total_entities', 0),
related_facts_json=json.dumps(context.get('related_facts', [])[:10], ensure_ascii=False, indent=2),
)
try:
response = self.llm.chat_json(
@ -960,153 +1217,13 @@ class ReportAgent:
if self.report_logger:
self.report_logger.log_section_start(section.title, section_index)
# 构建系统prompt - 优化后强调工具使用和引用原文
# 确定当前章节的标题级别
section_level = 2 # 默认为二级标题(##
sub_heading_level = 3 # 子标题使用三级(###
sub_sub_heading_level = 4 # 更小的子标题使用四级(####
system_prompt = f"""你是一个「未来预测报告」的撰写专家,正在撰写报告的一个章节。
报告标题: {outline.title}
报告摘要: {outline.summary}
预测场景模拟需求: {self.simulation_requirement}
当前要撰写的章节: {section.title}
核心理念
模拟世界是对未来的预演我们向模拟世界注入了特定条件模拟需求
模拟中Agent的行为和互动就是对未来人群行为的预测
你的任务是
- 揭示在设定条件下未来发生了什么
- 预测各类人群Agent是如何反应和行动的
- 发现值得关注的未来趋势风险和机会
不要写成对现实世界现状的分析
要聚焦于"未来会怎样"模拟结果就是预测的未来
最重要的规则 - 必须遵守
1. 必须调用工具观察模拟世界
- 你正在以上帝视角观察未来的预演
- 所有内容必须来自模拟世界中发生的事件和Agent言行
- 禁止使用你自己的知识来编写报告内容
- 每个章节至少调用3次工具最多5次来观察模拟的世界它代表了未来
2. 必须引用Agent的原始言行
- Agent的发言和行为是对未来人群行为的预测
- 在报告中使用引用格式展示这些预测例如
> "某类人群会表示:原文内容..."
- 这些引用是模拟预测的核心证据
3. 忠实呈现预测结果
- 报告内容必须反映模拟世界中的代表未来的模拟结果
- 不要添加模拟中不存在的信息
- 如果某方面信息不足如实说明
格式规范 - 极其重要
一个章节 = 最小内容单位
- 每个章节是报告的最小分块单位
- 禁止在章节内使用任何 Markdown 标题#、##、###、#### 等)
- 禁止在内容开头添加章节主标题
- 章节标题由系统自动添加你只需撰写纯正文内容
- 使用**粗体**段落分隔引用列表来组织内容但不要用标题
正确示例
```
本章节分析了事件的舆论传播态势通过对模拟数据的深入分析我们发现...
**首发引爆阶段**
微博作为舆情的第一现场承担了信息首发的核心功能
> "微博贡献了68%的首发声量..."
**情绪放大阶段**
抖音平台进一步放大了事件影响力
- 视觉冲击力强
- 情绪共鸣度高
```
错误示例
```
## 执行摘要 ← 错误!不要添加任何标题
### 一、首发阶段 ← 错误!不要用###分小节
#### 1.1 详细分析 ← 错误!不要用####细分
本章节分析了...
```
可用检索工具每章节调用3-5
{self._get_tools_description()}
工具使用建议 - 请混合使用不同工具不要只用一种
- insight_forge: 深度洞察分析自动分解问题并多维度检索事实和关系
- panorama_search: 广角全景搜索了解事件全貌时间线和演变过程
- quick_search: 快速验证某个具体信息点
- interview_agents: 采访模拟Agent获取不同角色的第一人称观点和真实反应
ReACT工作流程
1. Thought: [分析需要什么信息规划检索策略]
2. Action: [调用一个工具获取信息]每轮只能调用一个工具
<tool_call>
{{"name": "工具名称", "parameters": {{"参数名": "参数值"}}}}
</tool_call>
3. Observation: [系统返回工具结果]
4. 重复步骤1-3直到收集到足够信息
5. Final Answer: [基于检索结果撰写章节内容]
重要规则
- 每轮只能调用一个工具不要在一次回复中放多个 <tool_call>
- 当你认为信息足够时必须以 "Final Answer:" 开头输出最终内容
章节内容要求
1. 内容必须基于工具检索到的模拟数据
2. 大量引用原文来展示模拟效果
3. 使用Markdown格式但禁止使用标题
- 使用 **粗体文字** 标记重点代替子标题
- 使用列表-或1.2.3.组织要点
- 使用空行分隔不同段落
- 禁止使用 #、##、###、#### 等任何标题语法
4. 引用格式规范 - 必须单独成段
引用必须独立成段前后各有一个空行不能混在段落中
正确格式
```
校方的回应被认为缺乏实质内容
> "校方的应对模式在瞬息万变的社交媒体环境中显得僵化和迟缓。"
这一评价反映了公众的普遍不满
```
错误格式
```
校方的回应被认为缺乏实质内容> "校方的应对模式..." 这一评价反映了...
```
5. 保持与其他章节的逻辑连贯性
6. 避免重复仔细阅读下方已完成的章节内容不要重复描述相同的信息
7. 再次强调不要添加任何标题**粗体**代替小节标题"""
system_prompt = SECTION_SYSTEM_PROMPT_TEMPLATE.format(
report_title=outline.title,
report_summary=outline.summary,
simulation_requirement=self.simulation_requirement,
section_title=section.title,
tools_description=self._get_tools_description(),
)
# 构建用户prompt - 每个已完成章节各传入最大4000字
if previous_sections:
@ -1119,29 +1236,10 @@ class ReportAgent:
else:
previous_content = "(这是第一个章节)"
user_prompt = f"""已完成的章节内容(请仔细阅读,避免重复):
{previous_content}
当前任务撰写章节: {section.title}
重要提醒
1. 仔细阅读上方已完成的章节避免重复相同的内容
2. 开始前必须先调用工具获取模拟数据
3. 请混合使用不同工具不要只用一种
4. 报告内容必须来自检索结果不要使用自己的知识
格式警告 - 必须遵守
- 不要写任何标题#、##、###、####都不行)
- 不要写"{section.title}"作为开头
- 章节标题由系统自动添加
- 直接写正文**粗体**代替小节标题
请开始
1. 首先思考Thought这个章节需要什么信息
2. 然后调用工具Action获取模拟数据
3. 收集足够信息后输出 Final Answer纯正文无任何标题"""
user_prompt = SECTION_USER_PROMPT_TEMPLATE.format(
previous_content=previous_content,
section_title=section.title,
)
messages = [
{"role": "system", "content": system_prompt},
@ -1211,7 +1309,11 @@ class ReportAgent:
unused_hint = f"(这些工具还未使用,推荐用一下他们: {', '.join(unused_tools)}" if unused_tools else ""
messages.append({
"role": "user",
"content": f"【注意】你只调用了{tool_calls_count}次工具,至少需要{min_tool_calls}次。请再调用工具获取更多模拟数据,然后再输出 Final Answer。{unused_hint}"
"content": REACT_INSUFFICIENT_TOOLS_MSG.format(
tool_calls_count=tool_calls_count,
min_tool_calls=min_tool_calls,
unused_hint=unused_hint,
),
})
continue
@ -1235,7 +1337,10 @@ class ReportAgent:
messages.append({"role": "assistant", "content": response})
messages.append({
"role": "user",
"content": f"工具调用次数已达上限({tool_calls_count}/{self.MAX_TOOL_CALLS_PER_SECTION}),不能再调用工具。请立即基于已获取的信息,以 \"Final Answer:\" 开头输出章节内容。"
"content": REACT_TOOL_LIMIT_MSG.format(
tool_calls_count=tool_calls_count,
max_tool_calls=self.MAX_TOOL_CALLS_PER_SECTION,
),
})
continue
@ -1275,22 +1380,19 @@ class ReportAgent:
unused_tools = all_tools - used_tools
unused_hint = ""
if unused_tools and tool_calls_count < self.MAX_TOOL_CALLS_PER_SECTION:
unused_list = "".join(unused_tools)
unused_hint = f"\n💡 你还没有使用过: {unused_list},建议尝试不同工具获取多角度信息"
unused_hint = REACT_UNUSED_TOOLS_HINT.format(unused_list="".join(unused_tools))
messages.append({"role": "assistant", "content": response})
messages.append({
"role": "user",
"content": f"""Observation检索结果:
工具 {call['name']} 返回
{result}
已调用工具 {tool_calls_count}/{self.MAX_TOOL_CALLS_PER_SECTION} 已用: {', '.join(used_tools)}{unused_hint}
- 如果信息充分 "Final Answer:" 开头输出章节内容必须引用上述原文
- 如果需要更多信息调用一个工具继续检索
"""
"content": REACT_OBSERVATION_TEMPLATE.format(
tool_name=call["name"],
result=result,
tool_calls_count=tool_calls_count,
max_tool_calls=self.MAX_TOOL_CALLS_PER_SECTION,
used_tools_str=", ".join(used_tools),
unused_hint=unused_hint,
),
})
continue
@ -1304,7 +1406,11 @@ class ReportAgent:
messages.append({
"role": "user",
"content": f"当前只调用了 {tool_calls_count} 次工具,至少需要 {min_tool_calls} 次。请调用工具获取模拟数据。{unused_hint}"
"content": REACT_INSUFFICIENT_TOOLS_MSG_ALT.format(
tool_calls_count=tool_calls_count,
min_tool_calls=min_tool_calls,
unused_hint=unused_hint,
),
})
continue
@ -1324,10 +1430,7 @@ class ReportAgent:
# 达到最大迭代次数,强制生成内容
logger.warning(f"章节 {section.title} 达到最大迭代次数,强制生成")
messages.append({
"role": "user",
"content": "已达到工具调用限制,请直接输出 Final Answer: 并生成章节内容。"
})
messages.append({"role": "user", "content": REACT_FORCE_FINAL_MSG})
response = self.llm.chat(
messages=messages,
@ -1626,33 +1729,11 @@ class ReportAgent:
except Exception as e:
logger.warning(f"获取报告内容失败: {e}")
# 构建系统提示
system_prompt = f"""你是一个简洁高效的模拟预测助手。
背景
预测条件: {self.simulation_requirement}
已生成的分析报告
{report_content if report_content else "(暂无报告)"}
规则
1. 优先基于上述报告内容回答问题
2. 直接回答问题避免冗长的思考论述
3. 仅在报告内容不足以回答时才调用工具检索更多数据
4. 回答要简洁清晰有条理
可用工具仅在需要时使用最多调用1-2
{self._get_tools_description()}
工具调用格式
<tool_call>
{{"name": "工具名称", "parameters": {{"参数名": "参数值"}}}}
</tool_call>
回答风格
- 简洁直接不要长篇大论
- 使用 > 格式引用关键内容
- 优先给出结论再解释原因"""
system_prompt = CHAT_SYSTEM_PROMPT_TEMPLATE.format(
simulation_requirement=self.simulation_requirement,
report_content=report_content if report_content else "(暂无报告)",
tools_description=self._get_tools_description(),
)
# 构建消息
messages = [{"role": "system", "content": system_prompt}]
@ -1707,8 +1788,8 @@ class ReportAgent:
messages.append({"role": "assistant", "content": response})
observation = "\n".join([f"[{r['tool']}结果]\n{r['result']}" for r in tool_results])
messages.append({
"role": "user",
"content": observation + "\n\n请简洁回答问题。"
"role": "user",
"content": observation + CHAT_OBSERVATION_SUFFIX
})
# 达到最大迭代,获取最终响应