diff --git a/backend/app/services/report_agent.py b/backend/app/services/report_agent.py index 0a7ea81..cecd70b 100644 --- a/backend/app/services/report_agent.py +++ b/backend/app/services/report_agent.py @@ -106,7 +106,7 @@ class ReportLogger: "simulation_id": simulation_id, "graph_id": graph_id, "simulation_requirement": simulation_requirement, - "message": "报告生成任务开始" + "message": t('report.taskStarted') } ) @@ -115,7 +115,7 @@ class ReportLogger: self.log( action="planning_start", stage="planning", - details={"message": "开始规划报告大纲"} + details={"message": t('report.planningStart')} ) def log_planning_context(self, context: Dict[str, Any]): @@ -124,7 +124,7 @@ class ReportLogger: action="planning_context", stage="planning", details={ - "message": "获取模拟上下文信息", + "message": t('report.fetchSimContext'), "context": context } ) @@ -135,7 +135,7 @@ class ReportLogger: action="planning_complete", stage="planning", details={ - "message": "大纲规划完成", + "message": t('report.planningComplete'), "outline": outline_dict } ) @@ -147,7 +147,7 @@ class ReportLogger: stage="generating", section_title=section_title, section_index=section_index, - details={"message": f"开始生成章节: {section_title}"} + details={"message": t('report.sectionStart', title=section_title)} ) def log_react_thought(self, section_title: str, section_index: int, iteration: int, thought: str): @@ -160,7 +160,7 @@ class ReportLogger: details={ "iteration": iteration, "thought": thought, - "message": f"ReACT 第{iteration}轮思考" + "message": t('report.reactThought', iteration=iteration) } ) @@ -182,7 +182,7 @@ class ReportLogger: "iteration": iteration, "tool_name": tool_name, "parameters": parameters, - "message": f"调用工具: {tool_name}" + "message": t('report.toolCall', toolName=tool_name) } ) @@ -205,7 +205,7 @@ class ReportLogger: "tool_name": tool_name, "result": result, # 完整结果,不截断 "result_length": len(result), - "message": f"工具 {tool_name} 返回结果" + "message": t('report.toolResult', toolName=tool_name) } ) @@ -230,7 +230,7 @@ class ReportLogger: "response_length": len(response), "has_tool_calls": has_tool_calls, "has_final_answer": has_final_answer, - "message": f"LLM 响应 (工具调用: {has_tool_calls}, 最终答案: {has_final_answer})" + "message": t('report.llmResponse', hasToolCalls=has_tool_calls, hasFinalAnswer=has_final_answer) } ) @@ -251,7 +251,7 @@ class ReportLogger: "content": content, # 完整内容,不截断 "content_length": len(content), "tool_calls_count": tool_calls_count, - "message": f"章节 {section_title} 内容生成完成" + "message": t('report.sectionContentDone', title=section_title) } ) @@ -274,7 +274,7 @@ class ReportLogger: details={ "content": full_content, "content_length": len(full_content), - "message": f"章节 {section_title} 生成完成" + "message": t('report.sectionComplete', title=section_title) } ) @@ -286,7 +286,7 @@ class ReportLogger: details={ "total_sections": total_sections, "total_time_seconds": round(total_time_seconds, 2), - "message": "报告生成完成" + "message": t('report.reportComplete') } ) @@ -299,7 +299,7 @@ class ReportLogger: section_index=None, details={ "error": error_message, - "message": f"发生错误: {error_message}" + "message": t('report.errorOccurred', error=error_message) } ) @@ -914,7 +914,7 @@ class ReportAgent: # 控制台日志记录器(在 generate_report 中初始化) self.console_logger: Optional[ReportConsoleLogger] = None - logger.info(f"ReportAgent 初始化完成: graph_id={graph_id}, simulation_id={simulation_id}") + logger.info(t('report.agentInitDone', graphId=graph_id, simulationId=simulation_id)) def _define_tools(self) -> Dict[str, Dict[str, Any]]: """定义可用工具""" @@ -965,7 +965,7 @@ class ReportAgent: Returns: 工具执行结果(文本格式) """ - logger.info(f"执行工具: {tool_name}, 参数: {parameters}") + logger.info(t('report.executingTool', toolName=tool_name, params=parameters)) try: if tool_name == "insight_forge": @@ -1024,7 +1024,7 @@ class ReportAgent: elif tool_name == "search_graph": # 重定向到 quick_search - logger.info("search_graph 已重定向到 quick_search") + logger.info(t('report.redirectToQuickSearch')) return self._execute_tool("quick_search", parameters, report_context) elif tool_name == "get_graph_statistics": @@ -1041,7 +1041,7 @@ class ReportAgent: elif tool_name == "get_simulation_context": # 重定向到 insight_forge,因为它更强大 - logger.info("get_simulation_context 已重定向到 insight_forge") + logger.info(t('report.redirectToInsightForge')) query = parameters.get("query", self.simulation_requirement) return self._execute_tool("insight_forge", {"query": query}, report_context) @@ -1058,7 +1058,7 @@ class ReportAgent: return f"未知工具: {tool_name}。请使用以下工具之一: insight_forge, panorama_search, quick_search" except Exception as e: - logger.error(f"工具执行失败: {tool_name}, 错误: {str(e)}") + logger.error(t('report.toolExecFailed', toolName=tool_name, error=str(e))) return f"工具执行失败: {str(e)}" # 合法的工具名称集合,用于裸 JSON 兜底解析时校验 @@ -1149,7 +1149,7 @@ class ReportAgent: Returns: ReportOutline: 报告大纲 """ - logger.info("开始规划报告大纲...") + logger.info(t('report.startPlanningOutline')) if progress_callback: progress_callback("planning", 0, t('progress.analyzingRequirements')) @@ -1202,11 +1202,11 @@ class ReportAgent: if progress_callback: progress_callback("planning", 100, t('progress.outlinePlanComplete')) - logger.info(f"大纲规划完成: {len(sections)} 个章节") + logger.info(t('report.outlinePlanDone', count=len(sections))) return outline except Exception as e: - logger.error(f"大纲规划失败: {str(e)}") + logger.error(t('report.outlinePlanFailed', error=str(e))) # 返回默认大纲(3个章节,作为fallback) return ReportOutline( title="未来预测报告", @@ -1246,7 +1246,7 @@ class ReportAgent: Returns: 章节内容(Markdown格式) """ - logger.info(f"ReACT生成章节: {section.title}") + logger.info(t('report.reactGenerateSection', title=section.title)) # 记录章节开始日志 if self.report_logger: @@ -1310,7 +1310,7 @@ class ReportAgent: # 检查 LLM 返回是否为 None(API 异常或内容为空) if response is None: - logger.warning(f"章节 {section.title} 第 {iteration + 1} 次迭代: LLM 返回 None") + logger.warning(t('report.sectionIterNone', title=section.title, iteration=iteration + 1)) # 如果还有迭代次数,添加消息并重试 if iteration < max_iterations - 1: messages.append({"role": "assistant", "content": "(响应为空)"}) @@ -1330,8 +1330,7 @@ class ReportAgent: if has_tool_calls and has_final_answer: conflict_retries += 1 logger.warning( - f"章节 {section.title} 第 {iteration+1} 轮: " - f"LLM 同时输出工具调用和 Final Answer(第 {conflict_retries} 次冲突)" + t('report.sectionConflict', title=section.title, iteration=iteration+1, conflictCount=conflict_retries) ) if conflict_retries <= 2: @@ -1351,8 +1350,7 @@ class ReportAgent: else: # 第三次:降级处理,截断到第一个工具调用,强制执行 logger.warning( - f"章节 {section.title}: 连续 {conflict_retries} 次冲突," - "降级为截断执行第一个工具调用" + t('report.sectionConflictDowngrade', title=section.title, conflictCount=conflict_retries) ) first_tool_end = response.find('') if first_tool_end != -1: @@ -1392,7 +1390,7 @@ class ReportAgent: # 正常结束 final_answer = response.split("Final Answer:")[-1].strip() - logger.info(f"章节 {section.title} 生成完成(工具调用: {tool_calls_count}次)") + logger.info(t('report.sectionGenDone', title=section.title, count=tool_calls_count)) if self.report_logger: self.report_logger.log_section_content( @@ -1420,7 +1418,7 @@ class ReportAgent: # 只执行第一个工具调用 call = tool_calls[0] if len(tool_calls) > 1: - logger.info(f"LLM 尝试调用 {len(tool_calls)} 个工具,只执行第一个: {call['name']}") + logger.info(t('report.multiToolOnlyFirst', total=len(tool_calls), toolName=call['name'])) if self.report_logger: self.report_logger.log_tool_call( @@ -1489,7 +1487,7 @@ class ReportAgent: # 工具调用已足够,LLM 输出了内容但没带 "Final Answer:" 前缀 # 直接将这段内容作为最终答案,不再空转 - logger.info(f"章节 {section.title} 未检测到 'Final Answer:' 前缀,直接采纳LLM输出作为最终内容(工具调用: {tool_calls_count}次)") + logger.info(t('report.sectionNoPrefix', title=section.title, count=tool_calls_count)) final_answer = response.strip() if self.report_logger: @@ -1502,7 +1500,7 @@ class ReportAgent: return final_answer # 达到最大迭代次数,强制生成内容 - logger.warning(f"章节 {section.title} 达到最大迭代次数,强制生成") + logger.warning(t('report.sectionMaxIter', title=section.title)) messages.append({"role": "user", "content": REACT_FORCE_FINAL_MSG}) response = self.llm.chat( @@ -1513,8 +1511,8 @@ class ReportAgent: # 检查强制收尾时 LLM 返回是否为 None if response is None: - logger.error(f"章节 {section.title} 强制收尾时 LLM 返回 None,使用默认错误提示") - final_answer = f"(本章节生成失败:LLM 返回空响应,请稍后重试)" + logger.error(t('report.sectionForceFailed', title=section.title)) + final_answer = t('report.sectionGenFailedContent') elif "Final Answer:" in response: final_answer = response.split("Final Answer:")[-1].strip() else: @@ -1627,7 +1625,7 @@ class ReportAgent: ) ReportManager.save_report(report) - logger.info(f"大纲已保存到文件: {report_id}/outline.json") + logger.info(t('report.outlineSavedToFile', reportId=report_id)) # 阶段2: 逐章节生成(分章节保存) report.status = ReportStatus.GENERATING @@ -1685,7 +1683,7 @@ class ReportAgent: full_content=full_section_content.strip() ) - logger.info(f"章节已保存: {report_id}/section_{section_num:02d}.md") + logger.info(t('report.sectionSaved', reportId=report_id, sectionNum=f"{section_num:02d}")) # 更新进度 ReportManager.update_progress( @@ -1730,7 +1728,7 @@ class ReportAgent: if progress_callback: progress_callback("completed", 100, t('progress.reportComplete')) - logger.info(f"报告生成完成: {report_id}") + logger.info(t('report.reportGenDone', reportId=report_id)) # 关闭控制台日志记录器 if self.console_logger: @@ -1740,7 +1738,7 @@ class ReportAgent: return report except Exception as e: - logger.error(f"报告生成失败: {str(e)}") + logger.error(t('report.reportGenFailed', error=str(e))) report.status = ReportStatus.FAILED report.error = str(e) @@ -1786,7 +1784,7 @@ class ReportAgent: "sources": [信息来源] } """ - logger.info(f"Report Agent对话: {message[:50]}...") + logger.info(t('report.agentChat', message=message[:50])) chat_history = chat_history or [] @@ -1800,7 +1798,7 @@ class ReportAgent: if len(report.markdown_content) > 15000: report_content += "\n\n... [报告内容已截断] ..." except Exception as e: - logger.warning(f"获取报告内容失败: {e}") + logger.warning(t('report.fetchReportFailed', error=e)) system_prompt = CHAT_SYSTEM_PROMPT_TEMPLATE.format( simulation_requirement=self.simulation_requirement, @@ -2091,7 +2089,7 @@ class ReportManager: with open(cls._get_outline_path(report_id), 'w', encoding='utf-8') as f: json.dump(outline.to_dict(), f, ensure_ascii=False, indent=2) - logger.info(f"大纲已保存: {report_id}") + logger.info(t('report.outlineSaved', reportId=report_id)) @classmethod def save_section( @@ -2127,7 +2125,7 @@ class ReportManager: with open(file_path, 'w', encoding='utf-8') as f: f.write(md_content) - logger.info(f"章节已保存: {report_id}/{file_suffix}") + logger.info(t('report.sectionFileSaved', reportId=report_id, fileSuffix=file_suffix)) return file_path @classmethod @@ -2296,7 +2294,7 @@ class ReportManager: with open(full_path, 'w', encoding='utf-8') as f: f.write(md_content) - logger.info(f"完整报告已组装: {report_id}") + logger.info(t('report.fullReportAssembled', reportId=report_id)) return md_content @classmethod @@ -2443,7 +2441,7 @@ class ReportManager: with open(cls._get_report_markdown_path(report.report_id), 'w', encoding='utf-8') as f: f.write(report.markdown_content) - logger.info(f"报告已保存: {report.report_id}") + logger.info(t('report.reportSaved', reportId=report.report_id)) @classmethod def get_report(cls, report_id: str) -> Optional[Report]: @@ -2556,7 +2554,7 @@ class ReportManager: # 新格式:删除整个文件夹 if os.path.exists(folder_path) and os.path.isdir(folder_path): shutil.rmtree(folder_path) - logger.info(f"报告文件夹已删除: {report_id}") + logger.info(t('report.reportFolderDeleted', reportId=report_id)) return True # 兼容旧格式:删除单独的文件