Add Comments
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Report Engine节点处理模块
|
||||
实现报告生成的各个处理步骤
|
||||
Report Engine节点处理模块。
|
||||
|
||||
封装模板选择、章节生成、文档布局、篇幅规划等流水线节点。
|
||||
"""
|
||||
|
||||
from .base_node import BaseNode, StateMutationNode
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Report Engine节点基类
|
||||
定义所有处理节点的基础接口
|
||||
Report Engine节点基类。
|
||||
|
||||
所有高阶推理节点都继承于此,统一日志、输入校验与状态变更接口。
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
@@ -10,7 +11,12 @@ from ..state.state import ReportState
|
||||
from loguru import logger
|
||||
|
||||
class BaseNode(ABC):
|
||||
"""节点基类"""
|
||||
"""
|
||||
节点基类。
|
||||
|
||||
统一实现日志工具、输入/输出钩子以及LLM客户端依赖注入,
|
||||
便于所有节点只专注业务逻辑。
|
||||
"""
|
||||
|
||||
def __init__(self, llm_client: LLMClient, node_name: str = ""):
|
||||
"""
|
||||
@@ -19,6 +25,8 @@ class BaseNode(ABC):
|
||||
Args:
|
||||
llm_client: LLM客户端
|
||||
node_name: 节点名称
|
||||
|
||||
BaseNode 会保存节点名以便统一输出日志前缀。
|
||||
"""
|
||||
self.llm_client = llm_client
|
||||
self.node_name = node_name or self.__class__.__name__
|
||||
@@ -39,7 +47,8 @@ class BaseNode(ABC):
|
||||
|
||||
def validate_input(self, input_data: Any) -> bool:
|
||||
"""
|
||||
验证输入数据
|
||||
验证输入数据。
|
||||
默认直接通过,子类可按需覆写实现字段检查。
|
||||
|
||||
Args:
|
||||
input_data: 输入数据
|
||||
@@ -51,7 +60,8 @@ class BaseNode(ABC):
|
||||
|
||||
def process_output(self, output: Any) -> Any:
|
||||
"""
|
||||
处理输出数据
|
||||
处理输出数据。
|
||||
子类可覆写进行结构化或校验。
|
||||
|
||||
Args:
|
||||
output: 原始输出
|
||||
@@ -62,23 +72,29 @@ class BaseNode(ABC):
|
||||
return output
|
||||
|
||||
def log_info(self, message: str):
|
||||
"""记录信息日志"""
|
||||
"""记录信息日志,并自动带上节点名作为前缀。"""
|
||||
formatted_message = f"[{self.node_name}] {message}"
|
||||
logger.info(formatted_message)
|
||||
|
||||
def log_error(self, message: str):
|
||||
"""记录错误日志"""
|
||||
"""记录错误日志,便于排障。"""
|
||||
formatted_message = f"[{self.node_name}] {message}"
|
||||
logger.error(formatted_message)
|
||||
|
||||
|
||||
class StateMutationNode(BaseNode):
|
||||
"""带状态修改功能的节点基类"""
|
||||
"""
|
||||
带状态修改功能的节点基类。
|
||||
|
||||
适用于节点需要直接写入 ReportState 的场景。
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def mutate_state(self, input_data: Any, state: ReportState, **kwargs) -> ReportState:
|
||||
"""
|
||||
修改状态
|
||||
修改状态。
|
||||
|
||||
子类需返回新的状态对象或在原地修改后回传,供流水线记录。
|
||||
|
||||
Args:
|
||||
input_data: 输入数据
|
||||
|
||||
@@ -29,7 +29,7 @@ except ImportError: # pragma: no cover - optional dependency
|
||||
|
||||
|
||||
class ChapterJsonParseError(ValueError):
|
||||
"""Raised when the LLM output for a chapter cannot be parsed as valid JSON."""
|
||||
"""章节LLM输出无法解析为合法JSON时抛出的异常,附带原始文本方便排查。"""
|
||||
|
||||
def __init__(self, message: str, raw_text: Optional[str] = None):
|
||||
super().__init__(message)
|
||||
@@ -37,7 +37,15 @@ class ChapterJsonParseError(ValueError):
|
||||
|
||||
|
||||
class ChapterGenerationNode(BaseNode):
|
||||
"""负责按章节调用LLM并校验JSON结构"""
|
||||
"""
|
||||
负责按章节调用LLM并校验JSON结构。
|
||||
|
||||
核心能力:
|
||||
- 构造章节级 payload 与提示词;
|
||||
- 以流式形式写入 raw 文件并透传 delta;
|
||||
- 尝试修复/解析LLM输出,并使用 IRValidator 校验;
|
||||
- 对block结构做容错修复,确保最终JSON可渲染。
|
||||
"""
|
||||
|
||||
_COLON_EQUALS_PATTERN = re.compile(r'(":\s*)=')
|
||||
_LINE_BREAK_SENTINEL = "__LINE_BREAK__"
|
||||
|
||||
@@ -18,7 +18,11 @@ from .base_node import BaseNode
|
||||
|
||||
|
||||
class DocumentLayoutNode(BaseNode):
|
||||
"""负责生成全局标题、目录与Hero设计"""
|
||||
"""
|
||||
负责生成全局标题、目录与Hero设计。
|
||||
|
||||
结合模板切片、报告摘要与论坛讨论,指导整本书的视觉与结构基调。
|
||||
"""
|
||||
|
||||
def __init__(self, llm_client):
|
||||
"""记录LLM客户端并设置节点名字,供BaseNode日志使用"""
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""
|
||||
模板选择节点
|
||||
根据查询内容和可用模板选择最合适的报告模板
|
||||
模板选择节点。
|
||||
|
||||
综合用户查询、三引擎报告、论坛日志与本地模板库,
|
||||
调用LLM挑选最合适的报告骨架。
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -13,7 +15,12 @@ from ..prompts import SYSTEM_PROMPT_TEMPLATE_SELECTION
|
||||
|
||||
|
||||
class TemplateSelectionNode(BaseNode):
|
||||
"""模板选择处理节点"""
|
||||
"""
|
||||
模板选择处理节点。
|
||||
|
||||
负责准备模板候选列表、构建提示词、解析LLM返回结果,
|
||||
并在失败时回退到内置模板。
|
||||
"""
|
||||
|
||||
def __init__(self, llm_client, template_dir: str = "ReportEngine/report_template"):
|
||||
"""
|
||||
@@ -28,7 +35,7 @@ class TemplateSelectionNode(BaseNode):
|
||||
|
||||
def run(self, input_data: Dict[str, Any], **kwargs) -> Dict[str, Any]:
|
||||
"""
|
||||
执行模板选择
|
||||
执行模板选择。
|
||||
|
||||
Args:
|
||||
input_data: 包含查询和报告内容的字典
|
||||
@@ -37,7 +44,7 @@ class TemplateSelectionNode(BaseNode):
|
||||
- forum_logs: 论坛日志内容
|
||||
|
||||
Returns:
|
||||
选择的模板信息
|
||||
选择的模板信息,包含名称、内容与选择理由
|
||||
"""
|
||||
logger.info("开始模板选择...")
|
||||
|
||||
@@ -67,7 +74,12 @@ class TemplateSelectionNode(BaseNode):
|
||||
|
||||
def _llm_template_selection(self, query: str, reports: List[Any], forum_logs: str,
|
||||
available_templates: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
||||
"""使用LLM进行模板选择"""
|
||||
"""
|
||||
使用LLM进行模板选择。
|
||||
|
||||
构造模板列表与报告摘要 → 调用LLM → 解析JSON →
|
||||
验证模板是否存在并返回标准结构。
|
||||
"""
|
||||
logger.info("尝试使用LLM进行模板选择...")
|
||||
|
||||
# 构建模板列表
|
||||
@@ -150,7 +162,11 @@ class TemplateSelectionNode(BaseNode):
|
||||
return self._extract_template_from_text(response, available_templates)
|
||||
|
||||
def _clean_llm_response(self, response: str) -> str:
|
||||
"""清理LLM响应"""
|
||||
"""
|
||||
清理LLM响应。
|
||||
|
||||
去掉 ```json``` 包裹以及前后空白,方便 `json.loads`。
|
||||
"""
|
||||
# 移除可能的markdown代码块标记
|
||||
if '```json' in response:
|
||||
response = response.split('```json')[1].split('```')[0]
|
||||
@@ -163,7 +179,11 @@ class TemplateSelectionNode(BaseNode):
|
||||
return response
|
||||
|
||||
def _extract_template_from_text(self, response: str, available_templates: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
||||
"""从文本响应中提取模板信息"""
|
||||
"""
|
||||
从文本响应中提取模板信息。
|
||||
|
||||
当LLM未输出合法JSON时,尝试匹配模板名称关键字做降级。
|
||||
"""
|
||||
logger.info("尝试从文本响应中提取模板信息")
|
||||
|
||||
# 查找响应中是否包含模板名称
|
||||
@@ -186,7 +206,11 @@ class TemplateSelectionNode(BaseNode):
|
||||
return None
|
||||
|
||||
def _get_available_templates(self) -> List[Dict[str, Any]]:
|
||||
"""获取可用的模板列表"""
|
||||
"""
|
||||
获取可用的模板列表。
|
||||
|
||||
枚举模板目录下的 `.md` 文件并读取内容与描述字段。
|
||||
"""
|
||||
templates = []
|
||||
|
||||
if not os.path.exists(self.template_dir):
|
||||
@@ -216,7 +240,7 @@ class TemplateSelectionNode(BaseNode):
|
||||
return templates
|
||||
|
||||
def _extract_template_description(self, template_name: str) -> str:
|
||||
"""根据模板名称生成描述"""
|
||||
"""根据模板名称生成描述,方便LLM理解模板定位。"""
|
||||
if '企业品牌' in template_name:
|
||||
return "适用于企业品牌声誉和形象分析"
|
||||
elif '市场竞争' in template_name:
|
||||
@@ -235,7 +259,7 @@ class TemplateSelectionNode(BaseNode):
|
||||
|
||||
|
||||
def _get_fallback_template(self) -> Dict[str, Any]:
|
||||
"""获取备用默认模板(空模板,让LLM自行发挥)"""
|
||||
"""获取备用默认模板(空模板,让LLM自行发挥)。"""
|
||||
logger.info("未找到合适模板,使用空模板让LLM自行发挥")
|
||||
|
||||
return {
|
||||
|
||||
@@ -18,7 +18,11 @@ from .base_node import BaseNode
|
||||
|
||||
|
||||
class WordBudgetNode(BaseNode):
|
||||
"""规划各章节字数与重点"""
|
||||
"""
|
||||
规划各章节字数与重点。
|
||||
|
||||
输出总字数、全局写作准则以及每章/小节的 target/min/max 字数约束。
|
||||
"""
|
||||
|
||||
def __init__(self, llm_client):
|
||||
"""仅记录LLM客户端引用,方便run阶段发起请求"""
|
||||
|
||||
Reference in New Issue
Block a user