Browse Source

clean_agent

丁云鹏 1 week ago
parent
commit
e5e76590c1
3 changed files with 47 additions and 48 deletions
  1. 1 1
      agents/clean_agent/agent.py
  2. 11 10
      agents/clean_agent/tools.py
  3. 35 37
      prompt/extraction.md

+ 1 - 1
agents/clean_agent/agent.py

@@ -47,7 +47,7 @@ prompt="""
 {input}
 
 ### 输出:
-根据执行结果,输出相应的字符串 ("success", "no data", 或 "其他")
+如果处理成功返回success,如果处理失败返回failed
 """
 
 class State(TypedDict):

+ 11 - 10
agents/clean_agent/tools.py

@@ -123,8 +123,7 @@ def batch_evaluate_content(contents: list, db: Session, request_id: str, query_w
     try:
         # 批量调用大模型进行评估
         evaluation_results_raw = batch_call_llm_for_evaluation(contents, query_word)
-        
-        print(evaluation_results_raw)
+
         # 处理评估结果
         evaluation_results = []
         
@@ -168,7 +167,7 @@ def batch_extract_and_save_content(evaluation_results: list, db: Session, reques
         success_ids = []
         failed_ids = []
         
-        for i, extraction_data in enumerate(extraction_data_list):
+        for i, (extracted_data, clean_reason) in enumerate(extraction_data_list):
             try:
                 evaluation_result = evaluation_results[i]
                 parsing_id = evaluation_result.get("parsing_id")
@@ -176,7 +175,8 @@ def batch_extract_and_save_content(evaluation_results: list, db: Session, reques
                 if "extraction_content" in evaluation_result and parsing_id:
                     # 更新已有对象的data字段和状态
                     extraction_content = evaluation_result["extraction_content"]
-                    extraction_content.data = extraction_data
+                    extraction_content.data = extracted_data
+                    extraction_content.clean_reason = clean_reason
                     extraction_content.status = 2  # 处理完成
                     success_ids.append(parsing_id)
             except Exception as e:
@@ -216,10 +216,8 @@ evaluation_prompt_path = os.path.join(project_root, 'prompt', 'evaluation.md')
 extraction_prompt_path = os.path.join(project_root, 'prompt', 'extraction.md')
 
 # 打印路径信息,用于调试
-logger.info(f"评估提示词路径: {evaluation_prompt_path}")
 EVALUATION_PROMPT = read_prompt_file(evaluation_prompt_path)
 
-logger.info(f"抽取提示词路径: {extraction_prompt_path}")
 EXTRACTION_PROMPT = read_prompt_file(extraction_prompt_path)
 
 def batch_call_llm_for_evaluation(contents: list, query_word: str) -> list:
@@ -245,7 +243,7 @@ def batch_call_llm_for_evaluation(contents: list, query_word: str) -> list:
             parsing_id = contents[i].id
             parsing_data = contents[i].parsing_data   
             score = result.get("score", -2)
-            score_reason = result.get("score_reason", "")
+            score_reason = result.get("reason", "")
             
             evaluation_results.append((parsing_id, score, score_reason, parsing_data))
         
@@ -265,8 +263,7 @@ def batch_call_llm_for_extraction(evaluation_results: list, query_word: str) ->
             "query_word": query_word,
             "content": parsing_data
         })
-    
-    logger.info(f"批量抽取内容: {extraction_contents}")
+
     try:
         # 批量调用 Gemini 进行抽取
         results = gemini_processor.batch_process(extraction_contents, EXTRACTION_PROMPT)
@@ -275,7 +272,11 @@ def batch_call_llm_for_extraction(evaluation_results: list, query_word: str) ->
         extraction_results = []
         for i, result in enumerate(results):
             result = re.sub(r'^\s*```json|\s*```\s*$', '', result, flags=re.MULTILINE).strip()
-            extraction_results.append(result)
+            result = json.loads(result)
+            extracted_data = result.get("extracted_content", "未提取到内容")
+            clean_reason = result.get("analysis_reason", "未返回原因")
+            
+            extraction_results.append((extracted_data, clean_reason))
         
         return extraction_results
         

+ 35 - 37
prompt/extraction.md

@@ -1,13 +1,14 @@
-# Prompt: 知识库内容清洗与原文提取
-
 ## 你的角色 (Role)
-你是一个“知识库内容分析师”,具备极高的准确性、对无关信息的零容忍,以及对动态主题的快速适应能力
+你是一个“高级知识库内容分析师”,精通大型语言模型在RAG(检索增强生成)系统中的应用原理,具备极高的准确性、对RAG系统干扰信息的零容忍,以及根据动态主题智能调整清洗和提取策略的能力。你的核心目标是为RAG系统提供最优化的高质量知识片段。
 
 ## 任务目标 (Goal)
-你的核心任务是对给定的原始数据进行深度清洗,并根据一个特定的“查询意图”(Query),从清洗后的文本中精准提取出所有与该意图直接相关的原文片段。这些提取出的原文片段将用于构建高质量的知识库。
+你的核心任务是:
+1.  对给定的原始数据进行深度**智能清洗**,去除所有不利于RAG系统检索和生成的内容。
+2.  根据一个特定的“查询意图”(Query),从清洗后的文本中**精准提取**出所有与该意图直接相关且对RAG系统有正面增益的原文片段。
+3.  最终的输出将包含提取出的内容和清晰的分析原因,以提高透明度和可信度。
 
 ## 背景信息 (Context)
-你处理的知识库内容领域和主题是动态的,完全由用户提供的“查询意图”来决定。你需要根据Query词来理解当前任务所需的知识背景。
+你处理的知识库内容领域和主题是动态的,完全由用户提供的“查询意图”来决定。你需要根据`query_word`来深刻理解当前任务所需的知识背景,并以此为核心指导清洗和提取过程,确保最终输出的内容最优化RAG的效果
 
 ## 输入 (Input)
 当接收任务时,你会收到一个JSON对象作为输入,其中包含两部分:
@@ -15,40 +16,37 @@
 2.  **content:** 对应本次任务的“原始数据”,即一段未经处理的Markdown格式文本。
 
 ## 输出要求 (Output)
-1.  **格式 (Format):** 你需要返回一个纯文本内容。所有提取出的相关原文片段应拼接在一起,形成一个连续的文本流,段落之间保留自然换行。
-2.  **边界情况处理 (Edge Case):** 如果在原始数据中经过清洗和筛选后,未找到任何与“查询意图”直接相关的内容,你必须明确输出以下提示信息:“未找到相关信息”。
+你需要返回一个JSON对象,结构如下:
+```json
+{
+  "extracted_content": "这里是所有清洗并提取出的、与查询意图直接相关的原文片段,以连续文本流形式。如果未找到相关内容,此字段为空字符串。",
+  "analysis_reason": "这里是AI对本次提取(或未提取)结果的简洁概括原因分析和解释。"
+}
+```
+*   **extracted_content:** 所有提取出的相关原文片段应拼接在一起,形成一个连续的文本流,段落之间保留自然换行。
+*   **analysis_reason:** 简洁概括本次任务的执行过程,包括主要清洗操作、相关性判断依据,以及未找到信息时的原因。
 
 ## 约束条件 (Constraints)
-1.  **内容完整性:** 你不能对原文内容进行任何总结、改写、简化或添加额外信息。所有提取的都必须是原始文本的精确片段。
-2.  **严格清洗:** 在判断相关性之前,必须严格执行以下数据清洗规则:
-    *   **Markdown 格式标记去除:**
-        *   标题符号 (`#`, `##`, `###` 等)
-        *   加粗/斜体标记 (`**bold**`, `*italic*`, `_italic_`)
-        *   代码块标记 (```` ` ``, ``` `)
-        *   列表标记 (`-`, `*`, `1.`, `.` 等)
-        *   引用块标记 (`>`)
-        *   分割线 (`---`, `***` 等)
-        *   链接语法:保留链接文本,但去除URL部分 (`[链接文本](URL)` -> `链接文本`)
-        *   图片语法:彻底去除图片引用 (`![alt文本](URL)`)
-    *   **结构性噪音去除:**
-        *   页眉/页脚信息
-        *   目录/大纲列表
-        *   导航菜单/侧边栏内容
-        *   版权声明/免责声明等法律或标准文本
-        *   广告、促销或其他非知识性推广信息
-    *   **非文本或冗余内容去除:**
-        *   代码块内容:去除整个代码块及其内部文本。
-        *   特殊字符和符号:去除非标准、乱码字符或连续的无意义符号(如`[大笑R]`, `[氛围感R]`等表情符号或应用特定标记)。
-        *   过多空白符:将连续的多个空格、换行符、制表符压缩为单个空格或自然换行。
-        *   重复内容:识别并去除完全重复的段落或句子。
-        *   评论区内容:若有,需去除。
-3.  **精确相关性:** 只有与“查询意图”直接相关的文本片段才应被保留。任何与意图完全不相关的内容,即使在清洗后仍然存在,也应被过滤掉。
+1.  **RAG优化优先:** 你的所有清洗、过滤和提取决策都必须以“优化RAG系统的检索准确性和生成质量”为最高原则。任何可能干扰RAG系统或导致误导性回答的内容都应被视为噪音。
+2.  **内容完整性 (提取部分):** `extracted_content`字段内的内容必须是原始文本的精确片段,你不能对其进行任何总结、改写、简化或添加额外信息。`analysis_reason`字段允许包含非原文信息。
+3.  **智能清洗与噪音判断 (核心):** 你将不再依赖固定的清洗列表,而是根据以下五项原则,并结合`query_word`和RAG优化目标,智能判断并移除噪音。这些原则的目的是确保内容**与Query意图相关**且**不会影响RAG的效果**:
+    *   **与主题无关性:** 任何与`query_word`核心意图不直接相关且不提供知识内容的信息,应视为噪音。
+    *   **通用结构性噪音:** 具备典型文档结构特征(如页眉/页脚、目录、大纲列表、导航菜单/侧边栏、评论区、版权声明/免责声明、广告、促销信息等),且与`query_word`无关的内容,应视为噪音。你需要智能识别这些结构。
+    *   **冗余性:** 完全重复的段落或句子,或不包含新信息、对理解无增益的内容,应视为噪音。
+    *   **非内容性噪音:** 纯粹的功能性文本、特殊应用标记、表情符号、乱码字符、连续的无意义符号等,不属于核心知识内容的部分,应视为噪音。
+    *   **格式标记性噪音:** 仅为文本格式化目的存在的元素,而非内容本身。通常包括:
+        *   Markdown格式标记(`#`, `##`, `**bold**`, `*italic*`, ``` ` ````, ```` ``` ````, `-`, `1.`, `>`, `---`, `***` 等)。
+        *   链接语法:保留链接文本,去除URL部分 (`[链接文本](URL)` -> `链接文本`)。
+        *   图片语法:彻底去除图片引用 (`![alt文本](URL)`)。
+        *   **例外原则:** 如果`query_word`明确指向或包含对这些格式标记的讨论(例如,`query_word`是“Markdown语法”),则这些标记及其上下文可能不再被视为噪音,而应被保留,因为它们此时是核心知识内容。你需要智能地进行这种判断。
+4.  **精确相关性:** 只有与“查询意图”直接相关,且符合RAG优化目标,对RAG检索和生成有直接帮助的文本片段才应被保留。任何与意图完全不相关的内容,即使在清洗后仍然存在,也应被过滤掉。
 
 ## 工作流程 (Workflow)
 请严格按照以下步骤完成任务:
-1.  **分析Query:** 首先,仔细分析并透彻理解用户提供的“查询意图”,明确本次任务需要提取的核心信息和主题方向。
-2.  **预处理数据:** 接着,逐行或逐段地通读“原始数据”Markdown文本,并严格按照上述“约束条件”中定义的所有“数据清洗规则”,对文本进行预处理。在这一步,只关注格式和结构性噪音的去除,不进行相关性判断。
-3.  **判断相关性:** 然后,在清洗后的文本中,逐句(或根据上下文判断,逐小段)地分析每个文本单元。判断该文本单元是否与第一步中理解的“查询意图”直接相关。
-4.  **整合输出:**
-    *   将所有判断为“直接相关”的、未经任何修改的原文片段,按照其在原始文本中的先后顺序,拼接成一个纯文本流,作为最终输出。段落间保留自然换行。
-    *   如果经过以上步骤,最终没有找到任何直接相关的原文片段,则输出“未找到相关信息”。
+1.  **分析Query与RAG目标:** 首先,透彻理解`query_word`,明确本次任务需要提取的核心信息和主题方向。同时,将“优化RAG系统的检索准确性和生成质量”作为所有后续步骤的最高指导原则。
+2.  **智能预处理与噪音判断:** 逐行或逐段通读`content`文本。在这一步,根据上述“智能清洗与噪音判断”的五项原则,并结合`query_word`,智能地识别并移除所有可能干扰RAG系统或与主题无关的噪音。这包括格式标记、结构性噪音、冗余内容和非内容性元素。
+3.  **判断相关性:** 在清洗后的文本中,逐句(或根据上下文判断,逐小段)地分析每个文本单元。判断该文本单元是否与第一步中理解的`query_word`直接相关,且对RAG系统有正面增益。
+4.  **整合输出与原因分析:**
+    *   将所有判断为“直接相关”的、未经任何修改的原文片段,按照其在原始文本中的先后顺序,拼接成一个纯文本流,作为JSON对象中的`extracted_content`。
+    *   如果经过以上步骤,最终没有找到任何直接相关的原文片段,则`extracted_content`字段应为空字符串。
+    *   根据实际处理情况,生成一份**简洁概括**的`analysis_reason`,说明清洗和提取的主要过程、判断依据,以及最终结果。