fix: 修复用户反馈优化流程中AI返回结果被丢失的问题
问题: - 用户提供目录优化反馈后,AI返回了正确的优化结果,但最终显示时优化内容丢失 - 验证逻辑缺少对"质量安全"等章节的关键词识别 修复: 1. 修复数据流:optimize_with_feedback更新adjusted_chapters,finalize_chapters从adjusted_chapters读取 2. 添加"质量安全"关键词到验证逻辑 3. 清理调试日志(删除🔥符号和JSON打印) 影响: - 用户反馈优化功能现在能正确保留AI的修改结果 - 验证逻辑能准确识别更多章节类型 - 日志输出更简洁 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e20768fa4e
commit
922c43e65b
@ -29,7 +29,6 @@ class ApplyReviewSuggestionsNode(BaseNode, TocNodeBase):
|
||||
|
||||
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):
|
||||
|
||||
@ -154,7 +154,6 @@ class WorkflowUtilsMixin:
|
||||
"continue" 如果应该继续,"end" 如果应该停止
|
||||
"""
|
||||
result = "end" if (state.get("error") or not state.get("should_continue", True)) else "continue"
|
||||
logger.info(f"🔥🔥🔥 should_continue_workflow: {result}, error={state.get('error')}, should_continue={state.get('should_continue', True)}")
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
|
||||
@ -48,7 +48,7 @@ class FinalizeChaptersNode(BaseNode, TocNodeBase):
|
||||
|
||||
return self._update_state(state,
|
||||
final_chapters=final_chapters,
|
||||
should_continue=False)
|
||||
should_continue=True) # 让后续user_feedback节点决定是否继续
|
||||
|
||||
|
||||
def _ensure_standard_chapters(self, chapters: List[DocumentChapter]) -> List[DocumentChapter]:
|
||||
|
||||
343
src/bidmaster/nodes/toc/optimize_with_feedback.py
Normal file
343
src/bidmaster/nodes/toc/optimize_with_feedback.py
Normal file
@ -0,0 +1,343 @@
|
||||
"""根据用户反馈优化目录节点
|
||||
|
||||
基于用户反馈和当前目录结构,使用AI进行优化调整。
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Dict, List, Any, Optional
|
||||
|
||||
from ..base import BaseNode, NodeContext
|
||||
from ...tools.parser import DocumentChapter
|
||||
from .base_mixins import TocNodeBase
|
||||
from .llm_helper import LLMHelper
|
||||
from .factories import ChapterFactory
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OptimizeWithFeedbackNode(BaseNode, TocNodeBase):
|
||||
"""根据用户反馈优化目录"""
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "optimize_with_feedback"
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
return "根据用户反馈优化目录"
|
||||
|
||||
def execute(self, state: Dict[str, Any], context: NodeContext) -> Dict[str, Any]:
|
||||
"""执行目录优化"""
|
||||
return self.safe_execute(self._do_optimize, state, "根据用户反馈优化目录")
|
||||
|
||||
def _do_optimize(self, state: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""执行实际的目录优化逻辑"""
|
||||
# 验证必需字段
|
||||
required_fields = ["final_chapters", "user_feedback"]
|
||||
if not self.validate_required_fields(state, required_fields):
|
||||
raise ValueError("缺少目录数据或用户反馈")
|
||||
|
||||
final_chapters = state.get("final_chapters", [])
|
||||
user_feedback = state.get("user_feedback", "")
|
||||
|
||||
if not user_feedback.strip():
|
||||
# 没有反馈,直接返回
|
||||
logger.warning("用户反馈为空,跳过优化")
|
||||
return self._update_state(state,
|
||||
needs_optimization=False)
|
||||
|
||||
# 构建优化prompt
|
||||
optimized_chapters = self._optimize_with_ai(final_chapters, user_feedback)
|
||||
|
||||
if optimized_chapters:
|
||||
# 验证是否真的有修改
|
||||
if self._verify_modifications(final_chapters, optimized_chapters, user_feedback):
|
||||
self.log_step_info("optimize_with_feedback", f"根据用户反馈优化了{len(optimized_chapters)}个章节")
|
||||
return self._update_state(state,
|
||||
adjusted_chapters=optimized_chapters,
|
||||
user_feedback="", # 清空反馈
|
||||
needs_optimization=False)
|
||||
else:
|
||||
logger.warning("AI优化结果未按用户要求修改,保持原有结构")
|
||||
return self._update_state(state,
|
||||
user_feedback="",
|
||||
needs_optimization=False)
|
||||
else:
|
||||
# 优化失败,保持原有结构
|
||||
logger.warning("AI优化失败,保持原有结构")
|
||||
return self._update_state(state,
|
||||
user_feedback="",
|
||||
needs_optimization=False)
|
||||
|
||||
def _optimize_with_ai(self, chapters: List[DocumentChapter], feedback: str) -> Optional[List[DocumentChapter]]:
|
||||
"""使用AI优化目录结构
|
||||
|
||||
Args:
|
||||
chapters: 当前章节列表
|
||||
feedback: 用户反馈
|
||||
|
||||
Returns:
|
||||
优化后的章节列表,失败时返回None
|
||||
"""
|
||||
try:
|
||||
# 格式化当前目录
|
||||
current_toc = self._format_chapters_for_prompt(chapters)
|
||||
|
||||
prompt = f"""
|
||||
你是一个专业的标书目录结构优化助手。
|
||||
|
||||
**⚠️ 警告:如果你不按照用户的要求修改目录结构,将被视为任务失败!⚠️**
|
||||
|
||||
**🔥 强制要求:你必须完全执行用户的修改要求,不能返回未修改的结构!🔥**
|
||||
|
||||
当前目录结构:
|
||||
{current_toc}
|
||||
|
||||
用户反馈意见:
|
||||
{feedback}
|
||||
|
||||
**执行步骤(必须严格遵守):**
|
||||
1. **理解要求**:分析用户具体要求什么修改
|
||||
2. **执行修改**:必须按要求修改目录结构,不能保持原样
|
||||
3. **确认修改**:检查确保已经按用户要求进行了修改
|
||||
|
||||
**重要修改规则:**
|
||||
- 如果用户说"内容太少,多补充"或"多增加",必须增加新的同级别子章节
|
||||
- 如果用户指定某个章节,只修改该章节
|
||||
- 如果用户要求"调整顺序",必须重新排列
|
||||
- 如果用户要求"修改标题",必须更新标题
|
||||
|
||||
**具体示例:**
|
||||
|
||||
示例1:用户说"售后服务章节多增加一个子标题"
|
||||
- 在"售后服务"下新增子章节(如"技术支持服务"、"培训服务"等)
|
||||
|
||||
示例2:用户说"合规响应的内容太少,多补充一些"
|
||||
- 在"合规响应"下除了"技术实力",还要增加:
|
||||
* "资质认证展示"
|
||||
* "成功案例介绍"
|
||||
* "行业认可度说明"
|
||||
* "合规承诺声明"
|
||||
|
||||
**最终检查:**
|
||||
修改完成后,你必须确认已经按用户要求进行了实际修改,不能返回相同的结构!
|
||||
|
||||
请返回优化后的目录结构JSON,格式如下:
|
||||
{{
|
||||
"chapters": [
|
||||
{{
|
||||
"id": "chapter_1",
|
||||
"title": "章节标题",
|
||||
"level": 1,
|
||||
"score": 0.0,
|
||||
"children": [
|
||||
{{
|
||||
"id": "chapter_1_1",
|
||||
"title": "子章节标题",
|
||||
"level": 2,
|
||||
"score": 0.0,
|
||||
"children": []
|
||||
}}
|
||||
]
|
||||
}}
|
||||
]
|
||||
}}
|
||||
|
||||
只返回JSON,不要其他内容。
|
||||
"""
|
||||
|
||||
# 调用AI优化
|
||||
logger.info("开始调用AI进行目录优化")
|
||||
response = LLMHelper.call_llm_with_retry(prompt, max_retries=2)
|
||||
if not response:
|
||||
logger.error("AI优化无响应")
|
||||
return None
|
||||
|
||||
logger.debug(f"AI响应: {response[:200]}...")
|
||||
|
||||
# 解析AI返回的JSON
|
||||
return self._parse_optimized_structure(response)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"AI优化失败: {e}")
|
||||
return None
|
||||
|
||||
def _format_chapters_for_prompt(self, chapters: List[DocumentChapter]) -> str:
|
||||
"""格式化章节用于AI prompt
|
||||
|
||||
Args:
|
||||
chapters: 章节列表
|
||||
|
||||
Returns:
|
||||
格式化的章节字符串
|
||||
"""
|
||||
def format_chapter(chapter: DocumentChapter, level: int = 0) -> str:
|
||||
indent = " " * level
|
||||
score_text = f" ({chapter.score}分)" if chapter.score and chapter.score > 0 else ""
|
||||
result = f"{indent}- {chapter.title}{score_text}"
|
||||
|
||||
if chapter.children:
|
||||
for child in chapter.children:
|
||||
result += "\n" + format_chapter(child, level + 1)
|
||||
|
||||
return result
|
||||
|
||||
return "\n".join([format_chapter(ch) for ch in chapters])
|
||||
|
||||
def _parse_optimized_structure(self, json_response: str) -> Optional[List[DocumentChapter]]:
|
||||
"""解析AI返回的优化结构
|
||||
|
||||
Args:
|
||||
json_response: AI返回的JSON字符串
|
||||
|
||||
Returns:
|
||||
解析后的章节列表,失败时返回None
|
||||
"""
|
||||
try:
|
||||
# 清理响应,提取JSON部分
|
||||
json_str = json_response.strip()
|
||||
if json_str.startswith("```json"):
|
||||
json_str = json_str[7:]
|
||||
if json_str.endswith("```"):
|
||||
json_str = json_str[:-3]
|
||||
json_str = json_str.strip()
|
||||
|
||||
# 解析JSON
|
||||
data = json.loads(json_str)
|
||||
chapters_data = data.get("chapters", [])
|
||||
|
||||
# 转换为DocumentChapter对象
|
||||
chapters = []
|
||||
for ch_data in chapters_data:
|
||||
chapter = self._create_chapter_from_data(ch_data)
|
||||
if chapter:
|
||||
chapters.append(chapter)
|
||||
|
||||
logger.info(f"解析完成,生成了{len(chapters)}个章节")
|
||||
|
||||
return chapters
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"解析AI返回的JSON失败: {e}")
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"处理优化结构失败: {e}")
|
||||
return None
|
||||
|
||||
def _create_chapter_from_data(self, data: Dict[str, Any]) -> Optional[DocumentChapter]:
|
||||
"""从数据字典创建DocumentChapter
|
||||
|
||||
Args:
|
||||
data: 章节数据字典
|
||||
|
||||
Returns:
|
||||
DocumentChapter对象,失败时返回None
|
||||
"""
|
||||
try:
|
||||
chapter = DocumentChapter(
|
||||
id=data.get("id", ""),
|
||||
title=data.get("title", ""),
|
||||
level=data.get("level", 1),
|
||||
score=float(data.get("score", 0.0))
|
||||
)
|
||||
|
||||
# 处理子章节
|
||||
children_data = data.get("children", [])
|
||||
for child_data in children_data:
|
||||
child = self._create_chapter_from_data(child_data)
|
||||
if child:
|
||||
chapter.children.append(child)
|
||||
|
||||
return chapter
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"创建章节失败: {e}")
|
||||
return None
|
||||
|
||||
def _verify_modifications(self, original_chapters: List[DocumentChapter],
|
||||
optimized_chapters: List[DocumentChapter],
|
||||
user_feedback: str) -> bool:
|
||||
"""验证AI优化是否按用户要求进行了修改
|
||||
|
||||
Args:
|
||||
original_chapters: 原始章节列表
|
||||
optimized_chapters: 优化后的章节列表
|
||||
user_feedback: 用户反馈内容
|
||||
|
||||
Returns:
|
||||
是否按要求进行了修改
|
||||
"""
|
||||
try:
|
||||
# 基本检查:结构是否有变化
|
||||
if len(original_chapters) != len(optimized_chapters):
|
||||
logger.info("检测到章节数量变化,认为修改有效")
|
||||
return True
|
||||
|
||||
# 特殊检查:如果用户要求增加子标题
|
||||
if "增加" in user_feedback and ("子标题" in user_feedback or "子章节" in user_feedback):
|
||||
return self._verify_subchapter_addition(original_chapters, optimized_chapters, user_feedback)
|
||||
|
||||
# 检查标题是否有变化
|
||||
for orig, opt in zip(original_chapters, optimized_chapters):
|
||||
if orig.title != opt.title:
|
||||
logger.info(f"检测到标题变化: {orig.title} -> {opt.title}")
|
||||
return True
|
||||
|
||||
# 递归检查子章节
|
||||
if self._has_structural_changes(orig, opt):
|
||||
return True
|
||||
|
||||
logger.warning("未检测到明显的结构变化")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"验证修改失败: {e}")
|
||||
return True # 出错时默认认为有修改,避免误判
|
||||
|
||||
def _verify_subchapter_addition(self, original_chapters: List[DocumentChapter],
|
||||
optimized_chapters: List[DocumentChapter],
|
||||
user_feedback: str) -> bool:
|
||||
"""验证是否按要求增加了子章节"""
|
||||
# 查找用户提到的章节
|
||||
target_keywords = []
|
||||
if "售后服务" in user_feedback or "售后" in user_feedback:
|
||||
target_keywords = ["售后服务", "售后", "after_sales"]
|
||||
elif "合规响应" in user_feedback or "合规" in user_feedback:
|
||||
target_keywords = ["合规响应", "合规", "compliance"]
|
||||
elif "质量安全" in user_feedback or "质量" in user_feedback:
|
||||
target_keywords = ["质量安全", "质量", "quality_safety"]
|
||||
|
||||
for orig, opt in zip(original_chapters, optimized_chapters):
|
||||
# 检查是否是目标章节
|
||||
if target_keywords:
|
||||
is_target = any(keyword in orig.title or keyword in orig.id for keyword in target_keywords)
|
||||
else:
|
||||
# 如果没有特定关键词,检查所有章节
|
||||
is_target = True
|
||||
|
||||
if is_target and len(opt.children) > len(orig.children):
|
||||
logger.info(f"检测到{orig.title}章节子标题增加: {len(orig.children)} -> {len(opt.children)}")
|
||||
return True
|
||||
|
||||
# 递归检查子章节的子章节
|
||||
if orig.children and opt.children:
|
||||
if self._verify_subchapter_addition(orig.children, opt.children, user_feedback):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _has_structural_changes(self, orig: DocumentChapter, opt: DocumentChapter) -> bool:
|
||||
"""递归检查章节结构是否有变化"""
|
||||
if len(orig.children) != len(opt.children):
|
||||
logger.info(f"检测到{orig.title}子章节数量变化: {len(orig.children)} -> {len(opt.children)}")
|
||||
return True
|
||||
|
||||
for orig_child, opt_child in zip(orig.children, opt.children):
|
||||
if orig_child.title != opt_child.title:
|
||||
logger.info(f"检测到子章节标题变化: {orig_child.title} -> {opt_child.title}")
|
||||
return True
|
||||
if self._has_structural_changes(orig_child, opt_child):
|
||||
return True
|
||||
|
||||
return False
|
||||
Loading…
Reference in New Issue
Block a user