Prechádzať zdrojové kódy

feat: 补充独立评审 Agent

xyliu3 7 mesiacov pred
rodič
commit
b96ac9bab6

+ 14 - 6
Co-creation-projects/melxy1997-ColumnWriter/README.md

@@ -4,12 +4,12 @@
 
 ## ▸ 项目简介
 
-我们的智能体系统模拟了一个专业的创作者团队,包括:
+这个智能体模拟了一个专业的创作者团队,包括:
 - **策划专家**:负责顶层设计和内容规划
 - **写作专家**:负责具体内容的撰写和工具调用
 - **评审专家**:负责内容质量把控和反馈
 
-系统支持树形递归生成,可以创作出结构严谨、内容详实的长篇技术专栏。
+支持树形递归生成专栏目录,可以创作出结构严谨、内容详实的长篇技术专栏。
 
 ## ▸ 核心功能
 
@@ -50,11 +50,12 @@
 | 模块 | 文件 | 说明 |
 |------|------|------|
 | **Orchestrator** | `orchestrator.py` | **主控中心**。负责协调各个 Agent 的工作流程,管理状态流转,组合最终结果。 |
-| **Agents** | `agents.py` | **智能体实现**。包含 `PlannerAgent` (规划)、`WriterAgent` (写作)、`ReflectionWriterAgent` (反思写作) 等核心类。 |
+| **Agents** | `agents.py` | **智能体实现**。包含 `PlannerAgent` (规划)、`WriterAgent` (写作)、`ReviewerAgent` (评审)、`RevisionAgent` (修改)、`ReflectionWriterAgent` (反思写作) 等核心类。 |
 | **Models** | `models.py` | **数据建模**。定义了 `ContentNode` (内容树)、`ColumnPlan` (规划)、`ReviewResult` (评审结果) 等数据结构。 |
 | **Tools** | `agents.py` | **工具**。集成了 `SearchTool` (Tavily/SerpApi) 和 `MCPTool` (GitHub),赋予 Agent 联网和代码库访问能力。 |
 | **Prompts** | `prompts.py` | **提示词**。包含规划、写作、评审、修改等各个环节的 Prompt Template。 |
-| **Config** | `config.py` | **配置管理**。处理环境变量、模型参数、阈值设置等。 |
+| **Config** | `config.py` | **配置管理**。处理环境变量、模型参数、评审阈值等。 |
+| **Utils** | `utils.py` | **工具函数**。包含 `JSONExtractor` (JSON 提取)、`parse_react_output` (ReAct 输出解析) 等公共工具。 |
 
 ## ▸ 基本流程 (Workflow)
 
@@ -81,7 +82,7 @@
         *   `github`: (可选) 搜索 GitHub 仓库,读取真实项目代码。
 
 4.  **评审与优化 (Review & Refine)**
-    *   **ReAct 模式**: 内容生成后,独立的 Reviewer 进行评分。如果分数低于阈值,触发修改流程
+    *   **ReAct 模式 + 独立评审**: 内容生成后,`ReviewerAgent` 进行多维度评分(内容质量、结构逻辑、语言表达、格式规范)。如果分数低于阈值(默认75分),`RevisionAgent` 根据评审意见进行修改,循环直到通过或达到最大修改次数
     *   **Reflection 模式**: 使用 `ReflectionAgent`,Agent 生成初稿后立即自我反思 (Self-Reflection) 并自动优化,一步到位。
 
 5.  **组装与导出 (Assembly & Export)**
@@ -108,13 +109,20 @@
 *   **原理**: 生成内容 -> 自我评估 (Critic) -> 优化内容 (Refine)。
 *   **优势**: 显著提升内容质量,模拟人类"写完读一遍再改"的创作习惯。
 
+### 4. Independent Review (独立评审)
+*   **应用**: `ReviewerAgent` + `RevisionAgent`
+*   **原理**: 
+    - `ReviewerAgent`: 对生成的内容进行多维度评审(内容质量40分、结构逻辑30分、语言表达20分、格式规范10分),输出详细的评分和修改建议。
+    - `RevisionAgent`: 根据评审意见进行针对性修改,保留优点、修复问题。
+*   **优势**: 专业分工,评审标准统一,可追溯评审历史,支持多轮修改直到达标。
+
 ## ▸️ 模型与工具 (Models & Tools)
 
 ### 模型支持
 通过 `config.py` 配置,支持多种 LLM 后端:
 - **其他兼容模型**: 任何支持 OpenAI 接口格式的模型
 
-### 内置工具
+### 模型工具
 1.  **SearchTool (联网搜索)**
     *   支持后端: Tavily (推荐), SerpApi, DuckDuckGo 等。
     *   功能: 提供实时信息检索,解决大模型幻觉和知识滞后问题。

+ 4 - 2
Co-creation-projects/melxy1997-ColumnWriter/config.py

@@ -29,10 +29,12 @@ class Settings(BaseSettings):
     
     # 系统配置
     max_depth: int = 3
-    approval_threshold: int = 75
-    revision_threshold: int = 60
+    approval_threshold: int = 75  # 评审通过阈值(分数 >= 此值则通过)
+    revision_threshold: int = 60  # 修改阈值(分数 < 此值则需要重写)
     enable_parallel: bool = False
     enable_search: bool = True  # 是否启用搜索功能
+    enable_review: bool = True  # 是否启用评审功能(仅 ReAct 模式)
+    max_revisions: int = 2  # 最大修改次数
     
     # 服务器配置(可选,用于 API 服务)
     host: str = "0.0.0.0"

+ 32 - 3
Co-creation-projects/melxy1997-ColumnWriter/main.py

@@ -27,13 +27,25 @@ def main():
     
     # 选择模式
     print("\n请选择写作模式:")
-    print("1. ReActAgent 模式 (默认) - 推理、行动、工具调用")
-    print("2. ReflectionAgent 模式 - 自我反思、自动优化")
+    print("1. ReActAgent 模式 (默认) - 推理、行动、工具调用 + 独立评审")
+    print("2. ReflectionAgent 模式 - 自我反思、自动优化(内置评审)")
     mode_choice = input("> ").strip()
     use_reflection = mode_choice == "2"
     
+    # 如果选择 ReAct 模式,询问是否启用评审
+    if not use_reflection:
+        print("\n是否启用独立评审流程?(评审后可自动修改优化)")
+        print("1. 启用评审 (默认) - 生成后评审,不合格自动修改")
+        print("2. 禁用评审 - 仅生成内容,不进行评审")
+        review_choice = input("> ").strip()
+        if review_choice == "2":
+            settings.enable_review = False
+            print("▸ 已禁用评审流程")
+        else:
+            print(f"▸ 已启用评审流程(通过阈值: {settings.approval_threshold}分)")
+    
     try:
-        # 创建主理人
+        # 创建编排器
         orchestrator = ColumnWriterOrchestrator(use_reflection_mode=use_reflection)
         
         # 创建专栏
@@ -53,10 +65,27 @@ def main():
         print(f"总字数: {stats['total_words']:,}")
         print(f"平均字数: {stats['avg_words_per_article']:,}")
         
+        # 显示创作统计
+        if 'creation_stats' in result:
+            creation = result['creation_stats']
+            print(f"\n创作流程:")
+            print(f"  生成次数: {creation.get('total_generations', 0)}")
+            if creation.get('total_reviews', 0) > 0:
+                print(f"  评审次数: {creation.get('total_reviews', 0)}")
+                print(f"  一次通过: {creation.get('approved_first_try', 0)}")
+            if creation.get('total_revisions', 0) > 0:
+                print(f"  修改次数: {creation.get('total_revisions', 0)}")
+            if creation.get('total_rewrites', 0) > 0:
+                print(f"  重写次数: {creation.get('total_rewrites', 0)}")
+        
         if 'agent_modes' in result:
             print(f"\nAgent 模式:")
             print(f"  Planner: {result['agent_modes']['planner']}")
             print(f"  Writer: {result['agent_modes']['writer']}")
+            if result['agent_modes'].get('reviewer'):
+                print(f"  Reviewer: {result['agent_modes']['reviewer']}")
+            if result['agent_modes'].get('revision'):
+                print(f"  Revision: {result['agent_modes']['revision']}")
         
         print(f"\n{'='*70}")
         print(f"▸ 专栏创建完成!")

+ 137 - 10
Co-creation-projects/melxy1997-ColumnWriter/orchestrator.py

@@ -1,12 +1,14 @@
 """使用多 Agent 模式的主系统编排逻辑"""
 
 from datetime import datetime
-from typing import Dict, Any, List
-from models import ContentNode, ContentLevel, ColumnPlan
+from typing import Dict, Any, List, Optional
+from models import ContentNode, ContentLevel, ColumnPlan, ReviewResult
 from agents import (
     PlannerAgent,
     WriterAgent,
-    ReflectionWriterAgent
+    ReflectionWriterAgent,
+    ReviewerAgent,
+    RevisionAgent
 )
 from config import get_settings, get_word_count
 
@@ -44,9 +46,22 @@ class ColumnWriterOrchestrator:
         if use_reflection_mode:
             self.writer = ReflectionWriterAgent()
             print("   WriterAgent: ReflectionAgent(内置评审优化)")
+            self.reviewer = None
+            self.revision_agent = None
         else:
             self.writer = WriterAgent(enable_search=self.settings.enable_search)
             print("   WriterAgent: ReActAgent(推理-行动-搜索)")
+            
+            # 评审和修改 Agent(仅 ReAct 模式下可用)
+            if self.settings.enable_review:
+                self.reviewer = ReviewerAgent()
+                self.revision_agent = RevisionAgent()
+                print(f"   ReviewerAgent: 已启用(通过阈值: {self.settings.approval_threshold})")
+                print(f"   RevisionAgent: 已启用(最大修改次数: {self.settings.max_revisions})")
+            else:
+                self.reviewer = None
+                self.revision_agent = None
+                print("   ReviewerAgent: 已禁用")
         
         # 统计信息
         self.stats = {
@@ -54,6 +69,7 @@ class ColumnWriterOrchestrator:
             'total_reviews': 0,
             'total_revisions': 0,
             'total_rewrites': 0,
+            'approved_first_try': 0,
             'start_time': None,
             'end_time': None
         }
@@ -107,7 +123,9 @@ class ColumnWriterOrchestrator:
         full_column['creation_stats'] = self.stats
         full_column['agent_modes'] = {
             'planner': 'PlanAndSolveAgent',
-            'writer': 'ReflectionAgent' if self.use_reflection_mode else 'ReActAgent'
+            'writer': 'ReflectionAgent' if self.use_reflection_mode else 'ReActAgent',
+            'reviewer': 'ReviewerAgent' if (self.reviewer and not self.use_reflection_mode) else None,
+            'revision': 'RevisionAgent' if (self.revision_agent and not self.use_reflection_mode) else None
         }
         
         return full_column
@@ -211,23 +229,132 @@ class ColumnWriterOrchestrator:
         level: int,
         indent: str
     ):
-        """使用 ReActAgent 模式写作"""
+        """使用 ReActAgent 模式写作(可选评审)"""
         print(f"{indent}▸️  使用 ReActAgent 生成内容(推理-行动)...")
         
         content_data = self.writer.generate_content(node, context, level)
         self.stats['total_generations'] += 1
         
-        node.content = content_data['content']
-        node.metadata = content_data.get('metadata', {})
-        node.metadata['agent_mode'] = 'ReActAgent'
-        
-        word_count = content_data.get('word_count', len(content_data['content']))
+        current_content = content_data['content']
+        word_count = content_data.get('word_count', len(current_content))
         print(f"{indent}   字数:{word_count}")
         print(f"{indent}▸ ReActAgent 完成推理和行动")
         
+        # 如果启用评审,进行评审和可能的修改
+        if self.reviewer and self.settings.enable_review:
+            current_content, review_metadata = self._review_and_revise(
+                node, current_content, content_data, level, indent
+            )
+            content_data['content'] = current_content
+            content_data['metadata'] = {**content_data.get('metadata', {}), **review_metadata}
+        
+        node.content = current_content
+        node.metadata = content_data.get('metadata', {})
+        node.metadata['agent_mode'] = 'ReActAgent'
+        
         # 处理子节点
         self._process_children(node, content_data, context, level, indent)
     
+    def _review_and_revise(
+        self,
+        node: ContentNode,
+        content: str,
+        content_data: Dict[str, Any],
+        level: int,
+        indent: str
+    ) -> tuple:
+        """
+        评审并根据需要修改内容
+        
+        Args:
+            node: 当前节点
+            content: 当前内容
+            content_data: 完整的内容数据
+            level: 层级
+            indent: 缩进
+            
+        Returns:
+            (最终内容, 评审元数据)
+        """
+        target_word_count = get_word_count(level)
+        key_points = content_data.get('metadata', {}).get('keywords', [])
+        if not key_points:
+            key_points = [node.title, node.description]
+        
+        revision_count = 0
+        final_content = content
+        review_history = []
+        
+        while revision_count <= self.settings.max_revisions:
+            # 评审
+            print(f"{indent}▸ 开始评审(第 {revision_count + 1} 轮)...")
+            review_result = self.reviewer.review_content(
+                content=final_content,
+                level=level,
+                target_word_count=target_word_count,
+                key_points=key_points
+            )
+            self.stats['total_reviews'] += 1
+            
+            review_history.append({
+                'round': revision_count + 1,
+                'score': review_result.score,
+                'grade': review_result.grade,
+                'needs_revision': review_result.needs_revision
+            })
+            
+            print(f"{indent}   评审结果: {review_result.score}/100 ({review_result.grade})")
+            
+            # 检查是否通过评审
+            if review_result.score >= self.settings.approval_threshold:
+                print(f"{indent}▸ 内容通过评审!")
+                if revision_count == 0:
+                    self.stats['approved_first_try'] += 1
+                break
+            
+            # 检查是否还能修改
+            if revision_count >= self.settings.max_revisions:
+                print(f"{indent}▸️  达到最大修改次数 ({self.settings.max_revisions}),使用当前版本")
+                break
+            
+            # 检查是否需要重写(分数太低)
+            if review_result.score < self.settings.revision_threshold:
+                print(f"{indent}▸️  分数过低 ({review_result.score} < {self.settings.revision_threshold}),需要重写")
+                self.stats['total_rewrites'] += 1
+                # 重新生成内容
+                new_content_data = self.writer.generate_content(
+                    node, 
+                    {'review_feedback': review_result.reviewer_notes}, 
+                    level,
+                    additional_requirements=f"请注意避免以下问题: {review_result.reviewer_notes}"
+                )
+                self.stats['total_generations'] += 1
+                final_content = new_content_data['content']
+            else:
+                # 修改内容
+                print(f"{indent}▸ 根据评审意见修改内容...")
+                revised_data = self.revision_agent.revise_content(
+                    original_content=final_content,
+                    review_result=review_result,
+                    target_word_count=target_word_count
+                )
+                self.stats['total_revisions'] += 1
+                final_content = revised_data.get('revised_content', final_content)
+            
+            revision_count += 1
+        
+        # 构建评审元数据
+        final_review = review_history[-1] if review_history else {}
+        review_metadata = {
+            'review_score': final_review.get('score'),
+            'review_grade': final_review.get('grade'),
+            'review_rounds': len(review_history),
+            'review_history': review_history,
+            'reviewed': True
+        }
+        
+        return final_content, review_metadata
+    
     def _process_children(
         self,
         node: ContentNode,