feat: 在标题生成Agent中增加AI审核交互节点
1. 新增ApplyReviewSuggestionsNode节点 - 展示AI审查建议给用户 - 支持用户选择要应用的建议 - 支持交互/静默/程序化三种模式 2. 新增AdjustChaptersNode节点 - 根据用户选择的AI建议调整章节结构 - 完全基于AI的智能调整,无硬编码逻辑 - 安全的错误处理和回退机制 3. 更新工作流架构 - 新流程: ReviewStructure → ApplyReviewSuggestions → AdjustChapters → FinalizeChapters - 专业的单一职责原则,每个节点功能明确 - 完善的状态传递和错误处理 4. 修复AnalysisAgent集成问题 - 保留TocAgent的完整审查结果 - 正确使用调整后的章节结构 - 修复interaction_handler传递问题 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
bc3ea2e439
commit
52733d68c2
@ -307,10 +307,18 @@ def generate_toc_with_agent_node(state: AnalysisAgentState) -> AnalysisAgentStat
|
|||||||
|
|
||||||
logger.info(f"目录生成完成: {len(chapters)}个章节")
|
logger.info(f"目录生成完成: {len(chapters)}个章节")
|
||||||
|
|
||||||
# 更新状态
|
# 更新状态 - 使用TocAgent的完整结果
|
||||||
state["preliminary_chapters"] = chapters
|
state["preliminary_chapters"] = chapters
|
||||||
state["technical_criteria"] = technical_criteria
|
state["technical_criteria"] = technical_criteria
|
||||||
state["structure_review"] = {} # TocAgent已包含审查
|
|
||||||
|
# 保留TocAgent的审查结果和其他状态
|
||||||
|
if result.get("structure_review"):
|
||||||
|
state["structure_review"] = result["structure_review"]
|
||||||
|
if result.get("approved_suggestions"):
|
||||||
|
state["approved_suggestions"] = result["approved_suggestions"]
|
||||||
|
if result.get("adjusted_chapters"):
|
||||||
|
state["adjusted_chapters"] = result["adjusted_chapters"]
|
||||||
|
|
||||||
state["current_step"] = "generate_toc"
|
state["current_step"] = "generate_toc"
|
||||||
state["progress"] = 0.85
|
state["progress"] = 0.85
|
||||||
|
|
||||||
@ -335,12 +343,17 @@ def finalize_structure_node(state: AnalysisAgentState) -> AnalysisAgentState:
|
|||||||
preliminary_chapters = state["preliminary_chapters"]
|
preliminary_chapters = state["preliminary_chapters"]
|
||||||
structure_review = state.get("structure_review", {})
|
structure_review = state.get("structure_review", {})
|
||||||
|
|
||||||
|
# 获取最终章节(优先使用调整后的章节)
|
||||||
|
final_chapters = state.get("adjusted_chapters")
|
||||||
|
if not final_chapters:
|
||||||
|
final_chapters = state.get("final_chapters", preliminary_chapters)
|
||||||
|
|
||||||
# 创建最终的标书结构
|
# 创建最终的标书结构
|
||||||
bid_structure = BidStructure(
|
bid_structure = BidStructure(
|
||||||
project_name=f"标书项目-{Path(state['source_file']).stem}",
|
project_name=f"标书项目-{Path(state['source_file']).stem}",
|
||||||
scoring_criteria=technical_criteria,
|
scoring_criteria=technical_criteria,
|
||||||
deviation_items=state["deviation_items"],
|
deviation_items=state["deviation_items"],
|
||||||
chapters=preliminary_chapters, # 使用TocAgent生成的章节
|
chapters=final_chapters, # 使用最终的章节(可能是调整后的)
|
||||||
scoring_file=state["source_file"]
|
scoring_file=state["source_file"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,8 @@ from ...nodes.toc import (
|
|||||||
GenerateFirstLevelNode,
|
GenerateFirstLevelNode,
|
||||||
GenerateSubChaptersNode,
|
GenerateSubChaptersNode,
|
||||||
ReviewStructureNode,
|
ReviewStructureNode,
|
||||||
|
ApplyReviewSuggestionsNode,
|
||||||
|
AdjustChaptersNode,
|
||||||
FinalizeChaptersNode
|
FinalizeChaptersNode
|
||||||
)
|
)
|
||||||
from ...nodes.toc.base_mixins import WorkflowUtilsMixin
|
from ...nodes.toc.base_mixins import WorkflowUtilsMixin
|
||||||
@ -45,6 +47,8 @@ class TocAgentBuilder(AgentBuilder):
|
|||||||
.add_node(GenerateFirstLevelNode()) \
|
.add_node(GenerateFirstLevelNode()) \
|
||||||
.add_node(GenerateSubChaptersNode()) \
|
.add_node(GenerateSubChaptersNode()) \
|
||||||
.add_node(ReviewStructureNode()) \
|
.add_node(ReviewStructureNode()) \
|
||||||
|
.add_node(ApplyReviewSuggestionsNode()) \
|
||||||
|
.add_node(AdjustChaptersNode()) \
|
||||||
.add_node(FinalizeChaptersNode())
|
.add_node(FinalizeChaptersNode())
|
||||||
|
|
||||||
# 设置入口点
|
# 设置入口点
|
||||||
@ -80,6 +84,18 @@ class TocAgentBuilder(AgentBuilder):
|
|||||||
self.add_conditional_edge(
|
self.add_conditional_edge(
|
||||||
"review_structure",
|
"review_structure",
|
||||||
should_continue,
|
should_continue,
|
||||||
|
{"continue": "apply_suggestions", "end": "END"}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add_conditional_edge(
|
||||||
|
"apply_suggestions",
|
||||||
|
should_continue,
|
||||||
|
{"continue": "adjust_chapters", "end": "END"}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add_conditional_edge(
|
||||||
|
"adjust_chapters",
|
||||||
|
should_continue,
|
||||||
{"continue": "finalize_chapters", "end": "END"}
|
{"continue": "finalize_chapters", "end": "END"}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -134,6 +150,9 @@ class TocAgent(BaseAgent, BaseAgentFactory):
|
|||||||
if template_file:
|
if template_file:
|
||||||
initial_state["preset_template_file"] = template_file
|
initial_state["preset_template_file"] = template_file
|
||||||
|
|
||||||
|
# 添加交互处理器到状态中
|
||||||
|
initial_state["interaction_handler"] = self.builder.interaction_handler
|
||||||
|
|
||||||
return await self.execute(initial_state)
|
return await self.execute(initial_state)
|
||||||
|
|
||||||
def generate_toc_sync(self,
|
def generate_toc_sync(self,
|
||||||
|
|||||||
@ -8,6 +8,8 @@ from .group_criteria import GroupCriteriaNode
|
|||||||
from .generate_first_level import GenerateFirstLevelNode
|
from .generate_first_level import GenerateFirstLevelNode
|
||||||
from .generate_sub_chapters import GenerateSubChaptersNode
|
from .generate_sub_chapters import GenerateSubChaptersNode
|
||||||
from .review_structure import ReviewStructureNode
|
from .review_structure import ReviewStructureNode
|
||||||
|
from .apply_suggestions import ApplyReviewSuggestionsNode
|
||||||
|
from .adjust_chapters import AdjustChaptersNode
|
||||||
from .finalize_chapters import FinalizeChaptersNode
|
from .finalize_chapters import FinalizeChaptersNode
|
||||||
|
|
||||||
# 辅助组件
|
# 辅助组件
|
||||||
@ -27,6 +29,8 @@ __all__ = [
|
|||||||
"GenerateFirstLevelNode",
|
"GenerateFirstLevelNode",
|
||||||
"GenerateSubChaptersNode",
|
"GenerateSubChaptersNode",
|
||||||
"ReviewStructureNode",
|
"ReviewStructureNode",
|
||||||
|
"ApplyReviewSuggestionsNode",
|
||||||
|
"AdjustChaptersNode",
|
||||||
"FinalizeChaptersNode",
|
"FinalizeChaptersNode",
|
||||||
|
|
||||||
# 辅助组件
|
# 辅助组件
|
||||||
|
|||||||
234
src/bidmaster/nodes/toc/adjust_chapters.py
Normal file
234
src/bidmaster/nodes/toc/adjust_chapters.py
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
"""AI调整章节结构的节点
|
||||||
|
|
||||||
|
专门负责根据用户选择的AI建议调整章节结构。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Dict, List, Any
|
||||||
|
|
||||||
|
from ..base import BaseNode, NodeContext
|
||||||
|
from ...tools.parser import DocumentChapter
|
||||||
|
from .base_mixins import TocNodeBase
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AdjustChaptersNode(BaseNode, TocNodeBase):
|
||||||
|
"""AI调整章节结构的节点"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return "adjust_chapters"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self) -> str:
|
||||||
|
return "AI调整章节结构"
|
||||||
|
|
||||||
|
def execute(self, state: Dict[str, Any], context: NodeContext) -> Dict[str, Any]:
|
||||||
|
"""执行AI章节调整"""
|
||||||
|
return self.safe_execute(self._do_adjust_chapters, state, "AI调整章节结构")
|
||||||
|
|
||||||
|
def _do_adjust_chapters(self, state: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""执行实际的AI章节调整逻辑"""
|
||||||
|
# 验证必需字段
|
||||||
|
required_fields = ["preliminary_chapters"]
|
||||||
|
if not self.validate_required_fields(state, required_fields):
|
||||||
|
raise ValueError("缺少初步章节数据")
|
||||||
|
|
||||||
|
preliminary_chapters = state["preliminary_chapters"]
|
||||||
|
approved_suggestions = state.get("approved_suggestions", [])
|
||||||
|
|
||||||
|
if not approved_suggestions:
|
||||||
|
# 没有用户选择的建议,直接返回原章节结构
|
||||||
|
logger.info("用户未选择任何AI建议,保持原有章节结构")
|
||||||
|
return self._update_state(state, adjusted_chapters=preliminary_chapters)
|
||||||
|
|
||||||
|
# 使用AI根据建议调整章节结构
|
||||||
|
adjusted_chapters = self._ai_adjust_chapters_with_suggestions(
|
||||||
|
preliminary_chapters, approved_suggestions)
|
||||||
|
|
||||||
|
self.log_step_info("adjust_chapters",
|
||||||
|
f"AI根据{len(approved_suggestions)}条建议调整章节,生成{len(adjusted_chapters)}个章节")
|
||||||
|
|
||||||
|
return self._update_state(state, adjusted_chapters=adjusted_chapters)
|
||||||
|
|
||||||
|
def _ai_adjust_chapters_with_suggestions(self,
|
||||||
|
preliminary_chapters: List[DocumentChapter],
|
||||||
|
approved_suggestions: List[Dict[str, Any]]) -> List[DocumentChapter]:
|
||||||
|
"""让AI根据用户选择的建议调整章节结构
|
||||||
|
|
||||||
|
Args:
|
||||||
|
preliminary_chapters: 原始章节结构
|
||||||
|
approved_suggestions: 用户批准的AI建议
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
AI调整后的章节结构
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 构建给AI的提示词
|
||||||
|
adjustment_prompt = self._build_adjustment_prompt(preliminary_chapters, approved_suggestions)
|
||||||
|
|
||||||
|
# 调用LLM进行章节调整
|
||||||
|
from .llm_helper import LLMHelper
|
||||||
|
response = LLMHelper.call_llm_with_retry(adjustment_prompt)
|
||||||
|
|
||||||
|
if not response:
|
||||||
|
logger.error("AI章节调整失败,使用原始章节结构")
|
||||||
|
return preliminary_chapters
|
||||||
|
|
||||||
|
# 解析AI返回的调整结果
|
||||||
|
adjusted_data = LLMHelper.parse_ai_json_response(response)
|
||||||
|
adjusted_chapters = self._parse_adjusted_chapters(adjusted_data)
|
||||||
|
|
||||||
|
if adjusted_chapters:
|
||||||
|
logger.info(f"AI成功调整章节结构,共{len(adjusted_chapters)}个章节")
|
||||||
|
return adjusted_chapters
|
||||||
|
else:
|
||||||
|
logger.warning("AI调整结果解析失败,使用原始章节结构")
|
||||||
|
return preliminary_chapters
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"AI章节调整过程失败: {e}")
|
||||||
|
return preliminary_chapters
|
||||||
|
|
||||||
|
def _build_adjustment_prompt(self,
|
||||||
|
chapters: List[DocumentChapter],
|
||||||
|
suggestions: List[Dict[str, Any]]) -> str:
|
||||||
|
"""构建AI章节调整的提示词
|
||||||
|
|
||||||
|
Args:
|
||||||
|
chapters: 原始章节列表
|
||||||
|
suggestions: 用户选择的建议
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
给AI的提示词
|
||||||
|
"""
|
||||||
|
# 格式化原始章节结构
|
||||||
|
chapters_text = self._format_chapters_for_prompt(chapters)
|
||||||
|
|
||||||
|
# 格式化用户选择的建议
|
||||||
|
suggestions_text = self._format_suggestions_for_prompt(suggestions)
|
||||||
|
|
||||||
|
prompt = f"""
|
||||||
|
请根据用户选择的建议调整以下标书章节结构。
|
||||||
|
|
||||||
|
【当前章节结构】:
|
||||||
|
{chapters_text}
|
||||||
|
|
||||||
|
【用户选择应用的建议】:
|
||||||
|
{suggestions_text}
|
||||||
|
|
||||||
|
【调整要求】:
|
||||||
|
1. 根据用户选择的建议对章节结构进行合理调整
|
||||||
|
2. 保持章节的逻辑性和完整性
|
||||||
|
3. 每个章节都要有明确的标题和层级
|
||||||
|
4. 保持专业的标书格式
|
||||||
|
|
||||||
|
返回JSON格式:
|
||||||
|
{{
|
||||||
|
"adjusted_chapters": [
|
||||||
|
{{
|
||||||
|
"id": "chapter_1_technical_solution",
|
||||||
|
"title": "技术方案",
|
||||||
|
"level": 1,
|
||||||
|
"score": 0,
|
||||||
|
"children": [
|
||||||
|
{{
|
||||||
|
"id": "chapter_2_1_architecture",
|
||||||
|
"title": "系统架构设计",
|
||||||
|
"level": 2,
|
||||||
|
"score": 0,
|
||||||
|
"children": []
|
||||||
|
}}
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
|
||||||
|
只返回JSON:"""
|
||||||
|
|
||||||
|
return prompt
|
||||||
|
|
||||||
|
def _format_chapters_for_prompt(self, chapters: List[DocumentChapter]) -> str:
|
||||||
|
"""格式化章节结构用于提示词"""
|
||||||
|
lines = []
|
||||||
|
for chapter in chapters:
|
||||||
|
lines.append(f"{chapter.title} (level: {chapter.level})")
|
||||||
|
# 递归显示子章节
|
||||||
|
self._format_children_for_prompt(chapter.children, lines, 1)
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
def _format_children_for_prompt(self, children: List[DocumentChapter], lines: List[str], indent: int):
|
||||||
|
"""递归格式化子章节"""
|
||||||
|
for child in children:
|
||||||
|
indent_str = " " * indent
|
||||||
|
lines.append(f"{indent_str}{child.title} (level: {child.level})")
|
||||||
|
if child.children:
|
||||||
|
self._format_children_for_prompt(child.children, lines, indent + 1)
|
||||||
|
|
||||||
|
def _format_suggestions_for_prompt(self, suggestions: List[Dict[str, Any]]) -> str:
|
||||||
|
"""格式化建议列表用于提示词"""
|
||||||
|
lines = []
|
||||||
|
for i, suggestion in enumerate(suggestions, 1):
|
||||||
|
# 动态格式化建议内容,不预设字段
|
||||||
|
suggestion_text = f"{i}. "
|
||||||
|
for key, value in suggestion.items():
|
||||||
|
if value:
|
||||||
|
suggestion_text += f"{key}: {value}, "
|
||||||
|
lines.append(suggestion_text.rstrip(", "))
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
def _parse_adjusted_chapters(self, adjusted_data: Dict[str, Any]) -> List[DocumentChapter]:
|
||||||
|
"""解析AI调整后的章节数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
adjusted_data: AI返回的调整数据
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
解析后的章节列表
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
chapters_data = adjusted_data.get("adjusted_chapters", [])
|
||||||
|
chapters = []
|
||||||
|
|
||||||
|
for chapter_data in chapters_data:
|
||||||
|
chapter = self._create_chapter_from_data(chapter_data)
|
||||||
|
if chapter:
|
||||||
|
chapters.append(chapter)
|
||||||
|
|
||||||
|
return chapters
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"解析AI调整章节失败: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def _create_chapter_from_data(self, chapter_data: Dict[str, Any]) -> DocumentChapter:
|
||||||
|
"""从数据创建章节对象
|
||||||
|
|
||||||
|
Args:
|
||||||
|
chapter_data: 章节数据
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
创建的章节对象
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
chapter = DocumentChapter(
|
||||||
|
id=chapter_data.get("id", ""),
|
||||||
|
title=chapter_data.get("title", ""),
|
||||||
|
level=chapter_data.get("level", 1),
|
||||||
|
score=chapter_data.get("score", 0),
|
||||||
|
children=[]
|
||||||
|
)
|
||||||
|
|
||||||
|
# 递归处理子章节
|
||||||
|
children_data = chapter_data.get("children", [])
|
||||||
|
for child_data in children_data:
|
||||||
|
child_chapter = self._create_chapter_from_data(child_data)
|
||||||
|
if child_chapter:
|
||||||
|
chapter.children.append(child_chapter)
|
||||||
|
|
||||||
|
return chapter
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"创建章节对象失败: {e}")
|
||||||
|
return None
|
||||||
124
src/bidmaster/nodes/toc/apply_suggestions.py
Normal file
124
src/bidmaster/nodes/toc/apply_suggestions.py
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
"""应用AI审查建议的节点
|
||||||
|
|
||||||
|
让用户选择是否应用AI审查建议,完全依赖AI的分类结果,不做任何人为预设。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Dict, List, Any
|
||||||
|
|
||||||
|
from ..base import BaseNode, NodeContext
|
||||||
|
from .base_mixins import TocNodeBase
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ApplyReviewSuggestionsNode(BaseNode, TocNodeBase):
|
||||||
|
"""应用AI审查建议的节点"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return "apply_suggestions"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self) -> str:
|
||||||
|
return "应用AI审查建议"
|
||||||
|
|
||||||
|
def execute(self, state: Dict[str, Any], context: NodeContext) -> Dict[str, Any]:
|
||||||
|
"""执行建议应用交互"""
|
||||||
|
return self.safe_execute(self._do_apply_suggestions, state, "应用AI审查建议")
|
||||||
|
|
||||||
|
def _do_apply_suggestions(self, state: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""执行实际的建议应用逻辑"""
|
||||||
|
logger.info("🔥🔥🔥 ApplyReviewSuggestionsNode 正在执行!!!")
|
||||||
|
# 验证必需字段
|
||||||
|
required_fields = ["preliminary_chapters", "structure_review"]
|
||||||
|
if not self.validate_required_fields(state, required_fields):
|
||||||
|
raise ValueError("缺少章节数据或审查结果")
|
||||||
|
|
||||||
|
structure_review = state["structure_review"]
|
||||||
|
|
||||||
|
# 获取交互处理器
|
||||||
|
interaction_handler = state.get("interaction_handler")
|
||||||
|
|
||||||
|
if not interaction_handler:
|
||||||
|
# 如果没有交互处理器,跳过用户选择,不应用任何建议
|
||||||
|
logger.warning("未找到交互处理器,跳过建议应用")
|
||||||
|
approved_suggestions = []
|
||||||
|
else:
|
||||||
|
# 用户选择要应用的建议
|
||||||
|
approved_suggestions = self._get_user_approval(structure_review, interaction_handler)
|
||||||
|
|
||||||
|
# 将用户选择的建议保存到状态中,供FinalizeChaptersNode使用
|
||||||
|
self.log_step_info("apply_suggestions",
|
||||||
|
f"用户选择应用{len(approved_suggestions)}条AI建议")
|
||||||
|
|
||||||
|
return self._update_state(state, approved_suggestions=approved_suggestions)
|
||||||
|
|
||||||
|
def _get_user_approval(self, structure_review: Dict[str, Any],
|
||||||
|
interaction_handler) -> List[Dict[str, Any]]:
|
||||||
|
"""获取用户对AI建议的批准
|
||||||
|
|
||||||
|
Args:
|
||||||
|
structure_review: AI审查结果(完全使用原始数据)
|
||||||
|
interaction_handler: 交互处理器
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
用户批准的建议列表
|
||||||
|
"""
|
||||||
|
# 直接使用AI返回的原始数据,不做任何预设
|
||||||
|
suggestions = structure_review.get("suggestions", [])
|
||||||
|
overall_assessment = structure_review.get("overall_assessment", "")
|
||||||
|
optimization_score = structure_review.get("optimization_score", "")
|
||||||
|
|
||||||
|
if not suggestions:
|
||||||
|
logger.info("AI未提供优化建议")
|
||||||
|
return []
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 展示AI的完整评估结果
|
||||||
|
assessment_text = f"AI审查评估:\n"
|
||||||
|
if overall_assessment:
|
||||||
|
assessment_text += f"总体评价: {overall_assessment}\n"
|
||||||
|
if optimization_score:
|
||||||
|
assessment_text += f"优化评分: {optimization_score}\n"
|
||||||
|
assessment_text += f"AI识别出{len(suggestions)}条建议"
|
||||||
|
|
||||||
|
# 询问用户是否要查看并选择建议
|
||||||
|
view_suggestions = interaction_handler(
|
||||||
|
interaction_type="confirm",
|
||||||
|
prompt=f"{assessment_text}\n是否查看具体建议并选择应用?",
|
||||||
|
default=True,
|
||||||
|
key="view_ai_suggestions"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not view_suggestions:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# 逐个展示AI建议,让用户选择
|
||||||
|
approved_suggestions = []
|
||||||
|
for i, suggestion in enumerate(suggestions, 1):
|
||||||
|
# 完全使用AI提供的原始建议内容,不做任何格式化预设
|
||||||
|
suggestion_text = f"AI建议{i}:\n"
|
||||||
|
|
||||||
|
# 动态展示AI建议中的所有字段,不预设字段名
|
||||||
|
for key, value in suggestion.items():
|
||||||
|
if value: # 只显示有值的字段
|
||||||
|
suggestion_text += f"{key}: {value}\n"
|
||||||
|
|
||||||
|
# 让用户选择是否应用这条AI建议
|
||||||
|
apply_suggestion = interaction_handler(
|
||||||
|
interaction_type="confirm",
|
||||||
|
prompt=f"{suggestion_text}是否应用此AI建议?",
|
||||||
|
default=False, # 默认不应用,让用户主动选择
|
||||||
|
key=f"apply_ai_suggestion_{i}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if apply_suggestion:
|
||||||
|
approved_suggestions.append(suggestion)
|
||||||
|
|
||||||
|
return approved_suggestions
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取用户建议选择失败: {e}")
|
||||||
|
# 出错时不应用任何建议,保持安全
|
||||||
|
return []
|
||||||
@ -153,9 +153,9 @@ class WorkflowUtilsMixin:
|
|||||||
Returns:
|
Returns:
|
||||||
"continue" 如果应该继续,"end" 如果应该停止
|
"continue" 如果应该继续,"end" 如果应该停止
|
||||||
"""
|
"""
|
||||||
if state.get("error") or not state.get("should_continue", True):
|
result = "end" if (state.get("error") or not state.get("should_continue", True)) else "continue"
|
||||||
return "end"
|
logger.info(f"🔥🔥🔥 should_continue_workflow: {result}, error={state.get('error')}, should_continue={state.get('should_continue', True)}")
|
||||||
return "continue"
|
return result
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_required_fields(state: Dict[str, Any],
|
def validate_required_fields(state: Dict[str, Any],
|
||||||
|
|||||||
@ -35,11 +35,11 @@ class FinalizeChaptersNode(BaseNode, TocNodeBase):
|
|||||||
if not self.validate_required_fields(state, ["preliminary_chapters"]):
|
if not self.validate_required_fields(state, ["preliminary_chapters"]):
|
||||||
raise ValueError("缺少初步章节数据")
|
raise ValueError("缺少初步章节数据")
|
||||||
|
|
||||||
preliminary_chapters = state["preliminary_chapters"]
|
# 获取调整后的章节(如果有)或原始章节
|
||||||
structure_review = state.get("structure_review", {})
|
adjusted_chapters = state.get("adjusted_chapters", state["preliminary_chapters"])
|
||||||
|
|
||||||
# 应用审查建议并最终确定
|
# 应用最终确定逻辑(主要是确保标准章节存在)
|
||||||
final_chapters = self._finalize_with_review(preliminary_chapters, structure_review)
|
final_chapters = adjusted_chapters
|
||||||
|
|
||||||
# 确保标准章节存在
|
# 确保标准章节存在
|
||||||
final_chapters = self._ensure_standard_chapters(final_chapters)
|
final_chapters = self._ensure_standard_chapters(final_chapters)
|
||||||
@ -50,47 +50,6 @@ class FinalizeChaptersNode(BaseNode, TocNodeBase):
|
|||||||
final_chapters=final_chapters,
|
final_chapters=final_chapters,
|
||||||
should_continue=False)
|
should_continue=False)
|
||||||
|
|
||||||
def _finalize_with_review(self,
|
|
||||||
preliminary_chapters: List[DocumentChapter],
|
|
||||||
structure_review: Dict[str, Any]) -> List[DocumentChapter]:
|
|
||||||
"""根据审查结果最终确定章节
|
|
||||||
|
|
||||||
Args:
|
|
||||||
preliminary_chapters: 初步章节列表
|
|
||||||
structure_review: AI审查结果
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
最终章节列表
|
|
||||||
"""
|
|
||||||
final_chapters = preliminary_chapters.copy()
|
|
||||||
|
|
||||||
# 应用高优先级建议
|
|
||||||
suggestions = structure_review.get("suggestions", [])
|
|
||||||
high_priority_suggestions = [
|
|
||||||
s for s in suggestions
|
|
||||||
if s.get("priority") == "high"
|
|
||||||
]
|
|
||||||
|
|
||||||
for suggestion in high_priority_suggestions:
|
|
||||||
suggestion_type = suggestion.get("type", "")
|
|
||||||
description = suggestion.get("description", "")
|
|
||||||
|
|
||||||
if suggestion_type == "add":
|
|
||||||
logger.info(f"应用高优先级建议-添加: {description}")
|
|
||||||
# 这里可以根据具体建议内容添加章节
|
|
||||||
# 当前版本保持简单,只记录日志
|
|
||||||
|
|
||||||
elif suggestion_type == "modify":
|
|
||||||
logger.info(f"应用高优先级建议-修改: {description}")
|
|
||||||
# 这里可以根据具体建议内容修改章节
|
|
||||||
# 当前版本保持简单,只记录日志
|
|
||||||
|
|
||||||
elif suggestion_type == "reorder":
|
|
||||||
logger.info(f"应用高优先级建议-重排: {description}")
|
|
||||||
# 这里可以根据具体建议内容重新排序
|
|
||||||
# 当前版本保持简单,只记录日志
|
|
||||||
|
|
||||||
return final_chapters
|
|
||||||
|
|
||||||
def _ensure_standard_chapters(self, chapters: List[DocumentChapter]) -> List[DocumentChapter]:
|
def _ensure_standard_chapters(self, chapters: List[DocumentChapter]) -> List[DocumentChapter]:
|
||||||
"""确保包含标准章节
|
"""确保包含标准章节
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user