mirror of
https://github.com/agentuniverse-ai/agentUniverse.git
synced 2026-02-09 01:59:19 +08:00
refactor: mv DocumentClassifier to third party dir
This commit is contained in:
@@ -1,281 +0,0 @@
|
||||
# !/usr/bin/env python3
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
# @Time : 2024/12/19 10:30
|
||||
# @Author : AI Assistant
|
||||
# @Email : assistant@agentuniverse.ai
|
||||
# @FileName: chinese_document_classifier.py
|
||||
|
||||
import jieba
|
||||
import re
|
||||
from typing import List, Optional, Dict, Any
|
||||
from collections import Counter
|
||||
|
||||
from agentuniverse.agent.action.knowledge.store.query import Query
|
||||
from agentuniverse.agent.action.knowledge.store.document import Document
|
||||
from agentuniverse.agent.action.knowledge.doc_processor.document_classifier import (
|
||||
DocumentClassifier,
|
||||
ClassificationStrategy
|
||||
)
|
||||
|
||||
|
||||
class ChineseDocumentClassifier(DocumentClassifier):
|
||||
"""中文文档分类器
|
||||
|
||||
专门针对中文文档优化的分类器,支持:
|
||||
- 中文关键词匹配
|
||||
- 中文正则表达式匹配
|
||||
- 基于jieba分词的关键词提取
|
||||
- 中文语义相似度计算
|
||||
- 中文LLM分类
|
||||
|
||||
Attributes:
|
||||
use_jieba: 是否使用jieba分词
|
||||
min_word_length: 最小词长度
|
||||
stop_words: 停用词列表
|
||||
similarity_threshold: 语义相似度阈值
|
||||
"""
|
||||
|
||||
name: str = "chinese_document_classifier"
|
||||
description: str = "中文文档智能分类器,支持多种中文分类策略"
|
||||
|
||||
# 中文特定配置
|
||||
use_jieba: bool = True
|
||||
min_word_length: int = 2
|
||||
stop_words: List[str] = []
|
||||
similarity_threshold: float = 0.6
|
||||
|
||||
# 中文停用词(常用)
|
||||
DEFAULT_STOP_WORDS = [
|
||||
'的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '一个', '上', '也', '很', '到', '说', '要', '去', '你', '会', '着', '没有', '看', '好', '自己', '这', '那', '里', '来', '下', '过', '他', '她', '它', '们', '我们', '你们', '他们', '这个', '那个', '这些', '那些', '什么', '怎么', '为什么', '哪里', '什么时候', '多少', '几个', '一些', '很多', '非常', '特别', '比较', '更', '最', '还', '又', '再', '已经', '正在', '将要', '可以', '能够', '应该', '必须', '需要', '想要', '希望', '觉得', '认为', '知道', '了解', '明白', '清楚', '记得', '忘记', '学习', '工作', '生活', '时间', '地方', '事情', '问题', '方法', '结果', '原因', '条件', '情况', '时候', '地方', '方面', '部分', '内容', '形式', '方式', '过程', '阶段', '步骤', '环节', '因素', '影响', '作用', '意义', '价值', '目的', '目标', '计划', '安排', '组织', '管理', '控制', '监督', '检查', '评估', '分析', '研究', '调查', '统计', '数据', '信息', '资料', '材料', '文件', '报告', '总结', '结论', '建议', '意见', '看法', '观点', '态度', '立场', '角度', '层面', '水平', '程度', '范围', '规模', '大小', '多少', '长短', '高低', '深浅', '粗细', '厚薄', '宽窄', '远近', '新旧', '好坏', '对错', '真假', '美丑', '善恶', '正邪', '是非', '黑白', '红绿', '蓝黄', '紫粉', '灰棕', '金银', '铜铁', '木石', '水火', '土气', '风雷', '雨雪', '冰霜', '云雾', '阳光', '月光', '星光', '灯光', '火光', '电光', '声音', '音乐', '歌曲', '舞蹈', '绘画', '书法', '雕塑', '建筑', '设计', '艺术', '文化', '历史', '传统', '现代', '未来', '过去', '现在', '今天', '明天', '昨天', '今年', '明年', '去年', '春天', '夏天', '秋天', '冬天', '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日', '上午', '下午', '晚上', '夜里', '凌晨', '早晨', '中午', '傍晚', '深夜', '半夜', '整天', '全天', '部分', '全部', '一半', '三分之一', '四分之一', '五分之一', '十分之一', '百分之一', '千分之一', '万分之一', '亿分之一', '无限', '有限', '有限制', '无限制', '有条件', '无条件', '有可能', '不可能', '有希望', '无希望', '有前途', '无前途', '有发展', '无发展', '有进步', '无进步', '有改善', '无改善', '有提高', '无提高', '有提升', '无提升', '有增长', '无增长', '有减少', '无减少', '有下降', '无下降', '有上升', '无上升', '有增加', '无增加', '有减少', '无减少', '有变化', '无变化', '有改变', '无改变', '有改善', '无改善', '有提高', '无提高', '有提升', '无提升', '有增长', '无增长', '有减少', '无减少', '有下降', '无下降', '有上升', '无上升', '有增加', '无增加', '有减少', '无减少', '有变化', '无变化', '有改变', '无改变'
|
||||
]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
# 初始化停用词
|
||||
if not self.stop_words:
|
||||
self.stop_words = self.DEFAULT_STOP_WORDS.copy()
|
||||
|
||||
# 初始化jieba
|
||||
if self.use_jieba:
|
||||
jieba.initialize()
|
||||
|
||||
def _classify_by_keywords(self, text: str) -> tuple[str, float]:
|
||||
"""基于中文关键词匹配进行分类
|
||||
|
||||
Args:
|
||||
text: 文档文本
|
||||
|
||||
Returns:
|
||||
tuple: (分类结果, 置信度)
|
||||
"""
|
||||
if not self.keyword_rules:
|
||||
return self.default_category, 0.0
|
||||
|
||||
# 对文本进行分词处理
|
||||
if self.use_jieba:
|
||||
words = self._extract_keywords(text)
|
||||
text_processed = " ".join(words)
|
||||
else:
|
||||
text_processed = text
|
||||
|
||||
category_scores = {}
|
||||
|
||||
for category, keywords in self.keyword_rules.items():
|
||||
score = 0
|
||||
total_keywords = len(keywords)
|
||||
|
||||
for keyword in keywords:
|
||||
# 支持模糊匹配
|
||||
if self._fuzzy_match(keyword, text_processed):
|
||||
score += 1
|
||||
|
||||
if total_keywords > 0:
|
||||
confidence = score / total_keywords
|
||||
category_scores[category] = confidence
|
||||
|
||||
if category_scores:
|
||||
best_category = max(category_scores, key=category_scores.get)
|
||||
best_confidence = category_scores[best_category]
|
||||
|
||||
if best_confidence >= self.confidence_threshold:
|
||||
return best_category, best_confidence
|
||||
|
||||
return self.default_category, 0.0
|
||||
|
||||
def _extract_keywords(self, text: str) -> List[str]:
|
||||
"""使用jieba提取关键词
|
||||
|
||||
Args:
|
||||
text: 输入文本
|
||||
|
||||
Returns:
|
||||
List[str]: 关键词列表
|
||||
"""
|
||||
if not self.use_jieba:
|
||||
return [text]
|
||||
|
||||
# 分词
|
||||
words = jieba.lcut(text)
|
||||
|
||||
# 过滤停用词和短词
|
||||
filtered_words = []
|
||||
for word in words:
|
||||
word = word.strip()
|
||||
if (len(word) >= self.min_word_length and
|
||||
word not in self.stop_words and
|
||||
not re.match(r'^[0-9\s\W]+$', word)): # 过滤纯数字和特殊字符
|
||||
filtered_words.append(word)
|
||||
|
||||
return filtered_words
|
||||
|
||||
def _fuzzy_match(self, keyword: str, text: str) -> bool:
|
||||
"""模糊匹配关键词
|
||||
|
||||
Args:
|
||||
keyword: 关键词
|
||||
text: 文本
|
||||
|
||||
Returns:
|
||||
bool: 是否匹配
|
||||
"""
|
||||
# 精确匹配
|
||||
if keyword in text:
|
||||
return True
|
||||
|
||||
# 分词后匹配
|
||||
if self.use_jieba:
|
||||
keyword_words = jieba.lcut(keyword)
|
||||
text_words = jieba.lcut(text)
|
||||
|
||||
# 检查关键词的所有词是否都在文本中
|
||||
keyword_set = set(keyword_words)
|
||||
text_set = set(text_words)
|
||||
|
||||
if keyword_set.issubset(text_set):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _build_classification_prompt(self, text: str) -> str:
|
||||
"""构建中文LLM分类提示
|
||||
|
||||
Args:
|
||||
text: 文档文本
|
||||
|
||||
Returns:
|
||||
str: 分类提示
|
||||
"""
|
||||
categories_str = "、".join(self.categories)
|
||||
|
||||
prompt = f"""请对以下中文文档进行分类。
|
||||
|
||||
可选分类类别:{categories_str}
|
||||
|
||||
文档内容:
|
||||
{text[:2000]}
|
||||
|
||||
请仔细分析文档内容,选择最合适的分类。请按照以下JSON格式返回分类结果:
|
||||
{{
|
||||
"category": "分类结果",
|
||||
"confidence": 0.95,
|
||||
"reason": "分类理由(简要说明为什么选择这个分类)"
|
||||
}}
|
||||
|
||||
如果文档内容无法明确分类,请选择"{self.default_category}"。
|
||||
请确保分类结果准确,置信度要合理。
|
||||
"""
|
||||
return prompt
|
||||
|
||||
def add_chinese_keyword_rule(self, category: str, keywords: List[str]) -> None:
|
||||
"""添加中文关键词分类规则
|
||||
|
||||
Args:
|
||||
category: 分类类别
|
||||
keywords: 中文关键词列表
|
||||
"""
|
||||
# 对关键词进行预处理
|
||||
processed_keywords = []
|
||||
for keyword in keywords:
|
||||
if self.use_jieba:
|
||||
# 使用jieba分词处理关键词
|
||||
words = jieba.lcut(keyword)
|
||||
processed_keywords.extend([w for w in words if len(w) >= self.min_word_length])
|
||||
else:
|
||||
processed_keywords.append(keyword)
|
||||
|
||||
self.add_keyword_rule(category, processed_keywords)
|
||||
|
||||
def add_chinese_regex_rule(self, category: str, pattern: str) -> None:
|
||||
"""添加中文正则表达式分类规则
|
||||
|
||||
Args:
|
||||
category: 分类类别
|
||||
pattern: 中文正则表达式模式
|
||||
"""
|
||||
self.add_regex_rule(category, pattern)
|
||||
|
||||
def get_text_statistics(self, text: str) -> Dict[str, Any]:
|
||||
"""获取文本统计信息
|
||||
|
||||
Args:
|
||||
text: 输入文本
|
||||
|
||||
Returns:
|
||||
Dict: 文本统计信息
|
||||
"""
|
||||
if self.use_jieba:
|
||||
words = self._extract_keywords(text)
|
||||
word_count = Counter(words)
|
||||
|
||||
return {
|
||||
"total_chars": len(text),
|
||||
"total_words": len(words),
|
||||
"unique_words": len(word_count),
|
||||
"most_common_words": word_count.most_common(10),
|
||||
"avg_word_length": sum(len(w) for w in words) / len(words) if words else 0
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"total_chars": len(text),
|
||||
"total_words": len(text.split()),
|
||||
"unique_words": len(set(text.split())),
|
||||
"most_common_words": Counter(text.split()).most_common(10),
|
||||
"avg_word_length": len(text) / len(text.split()) if text.split() else 0
|
||||
}
|
||||
|
||||
def classify_by_content_type(self, text: str) -> str:
|
||||
"""根据内容类型进行基础分类
|
||||
|
||||
Args:
|
||||
text: 输入文本
|
||||
|
||||
Returns:
|
||||
str: 内容类型分类
|
||||
"""
|
||||
# 检测文档类型的关键词
|
||||
type_keywords = {
|
||||
"技术文档": ["代码", "API", "接口", "函数", "方法", "类", "模块", "配置", "安装", "部署", "调试", "错误", "异常", "日志"],
|
||||
"学术论文": ["摘要", "引言", "方法", "实验", "结果", "讨论", "结论", "参考文献", "引用", "研究", "分析", "数据"],
|
||||
"新闻资讯": ["报道", "消息", "新闻", "事件", "发生", "时间", "地点", "人物", "原因", "影响", "结果"],
|
||||
"商业报告": ["报告", "分析", "数据", "统计", "趋势", "预测", "建议", "策略", "市场", "销售", "收入", "利润"],
|
||||
"法律文档": ["法律", "法规", "条例", "条款", "权利", "义务", "责任", "合同", "协议", "争议", "诉讼"],
|
||||
"教育材料": ["学习", "教学", "课程", "教材", "练习", "考试", "作业", "知识点", "概念", "原理", "方法"],
|
||||
"医疗文档": ["疾病", "症状", "诊断", "治疗", "药物", "手术", "检查", "化验", "病历", "处方", "康复"],
|
||||
"其他": []
|
||||
}
|
||||
|
||||
text_lower = text.lower()
|
||||
max_score = 0
|
||||
best_type = "其他"
|
||||
|
||||
for doc_type, keywords in type_keywords.items():
|
||||
if doc_type == "其他":
|
||||
continue
|
||||
|
||||
score = sum(1 for keyword in keywords if keyword in text_lower)
|
||||
if score > max_score:
|
||||
max_score = score
|
||||
best_type = doc_type
|
||||
|
||||
return best_type
|
||||
@@ -1,257 +0,0 @@
|
||||
name: 'chinese_document_classifier'
|
||||
description: '中文文档智能分类器,支持多种中文分类策略'
|
||||
strategy: 'keyword_matching'
|
||||
confidence_threshold: 0.7
|
||||
default_category: '未分类'
|
||||
llm_name: null
|
||||
use_jieba: true
|
||||
min_word_length: 2
|
||||
similarity_threshold: 0.6
|
||||
|
||||
categories:
|
||||
- '技术文档'
|
||||
- '学术论文'
|
||||
- '新闻资讯'
|
||||
- '商业报告'
|
||||
- '法律文档'
|
||||
- '教育材料'
|
||||
- '医疗文档'
|
||||
- '其他'
|
||||
|
||||
# 中文关键词分类规则
|
||||
keyword_rules:
|
||||
技术文档:
|
||||
- '代码'
|
||||
- 'API'
|
||||
- '接口'
|
||||
- '函数'
|
||||
- '方法'
|
||||
- '类'
|
||||
- '模块'
|
||||
- '配置'
|
||||
- '安装'
|
||||
- '部署'
|
||||
- '调试'
|
||||
- '错误'
|
||||
- '异常'
|
||||
- '日志'
|
||||
- '编程'
|
||||
- '开发'
|
||||
- '软件'
|
||||
- '系统'
|
||||
- '数据库'
|
||||
- '算法'
|
||||
- '程序'
|
||||
- '技术'
|
||||
- '实现'
|
||||
- '架构'
|
||||
- '框架'
|
||||
|
||||
学术论文:
|
||||
- '摘要'
|
||||
- '引言'
|
||||
- '方法'
|
||||
- '实验'
|
||||
- '结果'
|
||||
- '讨论'
|
||||
- '结论'
|
||||
- '参考文献'
|
||||
- '引用'
|
||||
- '研究'
|
||||
- '分析'
|
||||
- '数据'
|
||||
- '论文'
|
||||
- '学术'
|
||||
- '理论'
|
||||
- '模型'
|
||||
- '假设'
|
||||
- '验证'
|
||||
- '统计'
|
||||
- '显著性'
|
||||
- '科学'
|
||||
- '实验'
|
||||
- '观察'
|
||||
- '测量'
|
||||
|
||||
新闻资讯:
|
||||
- '报道'
|
||||
- '消息'
|
||||
- '新闻'
|
||||
- '事件'
|
||||
- '发生'
|
||||
- '时间'
|
||||
- '地点'
|
||||
- '人物'
|
||||
- '原因'
|
||||
- '影响'
|
||||
- '结果'
|
||||
- '记者'
|
||||
- '采访'
|
||||
- '现场'
|
||||
- '突发'
|
||||
- '最新'
|
||||
- '今日'
|
||||
- '昨日'
|
||||
- '明天'
|
||||
- '发布'
|
||||
- '宣布'
|
||||
- '透露'
|
||||
- '透露'
|
||||
|
||||
商业报告:
|
||||
- '报告'
|
||||
- '分析'
|
||||
- '数据'
|
||||
- '统计'
|
||||
- '趋势'
|
||||
- '预测'
|
||||
- '建议'
|
||||
- '策略'
|
||||
- '市场'
|
||||
- '销售'
|
||||
- '收入'
|
||||
- '利润'
|
||||
- '财务'
|
||||
- '预算'
|
||||
- '投资'
|
||||
- '收益'
|
||||
- '成本'
|
||||
- '风险'
|
||||
- '机会'
|
||||
- '业务'
|
||||
- '经营'
|
||||
- '管理'
|
||||
- '决策'
|
||||
|
||||
法律文档:
|
||||
- '法律'
|
||||
- '法规'
|
||||
- '条例'
|
||||
- '条款'
|
||||
- '权利'
|
||||
- '义务'
|
||||
- '责任'
|
||||
- '合同'
|
||||
- '协议'
|
||||
- '争议'
|
||||
- '诉讼'
|
||||
- '法院'
|
||||
- '判决'
|
||||
- '律师'
|
||||
- '当事人'
|
||||
- '证据'
|
||||
- '证人'
|
||||
- '辩护'
|
||||
- '上诉'
|
||||
- '司法'
|
||||
- '审判'
|
||||
- '裁决'
|
||||
- '执行'
|
||||
|
||||
教育材料:
|
||||
- '学习'
|
||||
- '教学'
|
||||
- '课程'
|
||||
- '教材'
|
||||
- '练习'
|
||||
- '考试'
|
||||
- '作业'
|
||||
- '知识点'
|
||||
- '概念'
|
||||
- '原理'
|
||||
- '方法'
|
||||
- '教育'
|
||||
- '培训'
|
||||
- '指导'
|
||||
- '说明'
|
||||
- '步骤'
|
||||
- '示例'
|
||||
- '习题'
|
||||
- '答案'
|
||||
- '学生'
|
||||
- '老师'
|
||||
- '课堂'
|
||||
- '课程'
|
||||
|
||||
医疗文档:
|
||||
- '疾病'
|
||||
- '症状'
|
||||
- '诊断'
|
||||
- '治疗'
|
||||
- '药物'
|
||||
- '手术'
|
||||
- '检查'
|
||||
- '化验'
|
||||
- '病历'
|
||||
- '处方'
|
||||
- '康复'
|
||||
- '患者'
|
||||
- '医生'
|
||||
- '医院'
|
||||
- '护理'
|
||||
- '健康'
|
||||
- '医疗'
|
||||
- '临床'
|
||||
- '病理'
|
||||
- '医学'
|
||||
- '生理'
|
||||
- '心理'
|
||||
- '康复'
|
||||
|
||||
# 中文正则表达式分类规则
|
||||
regex_rules:
|
||||
技术文档: '(代码|API|接口|函数|方法|类|模块|配置|安装|部署|调试|错误|异常|日志|编程|开发|软件|系统|数据库|算法|程序|技术|实现|架构|框架)'
|
||||
学术论文: '(摘要|引言|方法|实验|结果|讨论|结论|参考文献|引用|研究|分析|数据|论文|学术|理论|模型|假设|验证|统计|显著性|科学|实验|观察|测量)'
|
||||
新闻资讯: '(报道|消息|新闻|事件|发生|时间|地点|人物|原因|影响|结果|记者|采访|现场|突发|最新|今日|昨日|明天|发布|宣布|透露)'
|
||||
商业报告: '(报告|分析|数据|统计|趋势|预测|建议|策略|市场|销售|收入|利润|财务|预算|投资|收益|成本|风险|机会|业务|经营|管理|决策)'
|
||||
法律文档: '(法律|法规|条例|条款|权利|义务|责任|合同|协议|争议|诉讼|法院|判决|律师|当事人|证据|证人|辩护|上诉|司法|审判|裁决|执行)'
|
||||
教育材料: '(学习|教学|课程|教材|练习|考试|作业|知识点|概念|原理|方法|教育|培训|指导|说明|步骤|示例|习题|答案|学生|老师|课堂|课程)'
|
||||
医疗文档: '(疾病|症状|诊断|治疗|药物|手术|检查|化验|病历|处方|康复|患者|医生|医院|护理|健康|医疗|临床|病理|医学|生理|心理|康复)'
|
||||
|
||||
# 停用词列表(部分)
|
||||
stop_words:
|
||||
- '的'
|
||||
- '了'
|
||||
- '在'
|
||||
- '是'
|
||||
- '我'
|
||||
- '有'
|
||||
- '和'
|
||||
- '就'
|
||||
- '不'
|
||||
- '人'
|
||||
- '都'
|
||||
- '一'
|
||||
- '一个'
|
||||
- '上'
|
||||
- '也'
|
||||
- '很'
|
||||
- '到'
|
||||
- '说'
|
||||
- '要'
|
||||
- '去'
|
||||
- '你'
|
||||
- '会'
|
||||
- '着'
|
||||
- '没有'
|
||||
- '看'
|
||||
- '好'
|
||||
- '自己'
|
||||
- '这'
|
||||
- '那'
|
||||
- '里'
|
||||
- '来'
|
||||
- '下'
|
||||
- '过'
|
||||
- '他'
|
||||
- '她'
|
||||
- '它'
|
||||
- '们'
|
||||
|
||||
classification_key: 'classification'
|
||||
confidence_key: 'classification_confidence'
|
||||
|
||||
metadata:
|
||||
type: 'DOC_PROCESSOR'
|
||||
module: 'agentuniverse.agent.action.knowledge.doc_processor.chinese_document_classifier'
|
||||
class: 'ChineseDocumentClassifier'
|
||||
@@ -1,396 +0,0 @@
|
||||
# !/usr/bin/env python3
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
# @Time : 2024/12/19 10:00
|
||||
# @Author : AI Assistant
|
||||
# @Email : assistant@agentuniverse.ai
|
||||
# @FileName: document_classifier.py
|
||||
|
||||
import re
|
||||
import json
|
||||
from abc import abstractmethod
|
||||
from typing import List, Optional, Dict, Any, Set, Union
|
||||
from enum import Enum
|
||||
|
||||
from agentuniverse.agent.action.knowledge.store.query import Query
|
||||
from agentuniverse.agent.action.knowledge.store.document import Document
|
||||
from agentuniverse.agent.action.knowledge.doc_processor.doc_processor import DocProcessor
|
||||
from agentuniverse.llm.llm_manager import LLMManager
|
||||
|
||||
|
||||
class ClassificationStrategy(Enum):
|
||||
"""文档分类策略枚举"""
|
||||
KEYWORD_MATCHING = "keyword_matching" # 关键词匹配
|
||||
REGEX_PATTERN = "regex_pattern" # 正则表达式匹配
|
||||
LLM_CLASSIFICATION = "llm_classification" # 大语言模型分类
|
||||
HYBRID = "hybrid" # 混合策略
|
||||
|
||||
|
||||
class DocumentClassifier(DocProcessor):
|
||||
"""文档分类器基类
|
||||
|
||||
提供多种文档分类策略,支持基于关键词、正则表达式、LLM等方法的文档自动分类。
|
||||
分类结果将存储在文档的metadata中,便于后续检索和处理。
|
||||
|
||||
Attributes:
|
||||
categories: 分类类别列表
|
||||
strategy: 分类策略
|
||||
confidence_threshold: 置信度阈值
|
||||
default_category: 默认分类
|
||||
llm_name: 用于LLM分类的模型名称
|
||||
"""
|
||||
|
||||
component_type = DocProcessor.component_type
|
||||
name: Optional[str] = "document_classifier"
|
||||
description: Optional[str] = "智能文档分类器,支持多种分类策略"
|
||||
|
||||
# 分类配置
|
||||
categories: List[str] = []
|
||||
strategy: ClassificationStrategy = ClassificationStrategy.KEYWORD_MATCHING
|
||||
confidence_threshold: float = 0.7
|
||||
default_category: str = "未分类"
|
||||
llm_name: Optional[str] = None
|
||||
|
||||
# 关键词分类规则
|
||||
keyword_rules: Dict[str, List[str]] = {}
|
||||
|
||||
# 正则表达式分类规则
|
||||
regex_rules: Dict[str, str] = {}
|
||||
|
||||
# 分类结果存储键名
|
||||
classification_key: str = "classification"
|
||||
confidence_key: str = "classification_confidence"
|
||||
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
def _process_docs(self, origin_docs: List[Document], query: Query = None) -> List[Document]:
|
||||
"""处理文档列表,为每个文档添加分类信息
|
||||
|
||||
Args:
|
||||
origin_docs: 原始文档列表
|
||||
query: 查询对象(可选)
|
||||
|
||||
Returns:
|
||||
List[Document]: 处理后的文档列表,包含分类信息
|
||||
"""
|
||||
processed_docs = []
|
||||
|
||||
for doc in origin_docs:
|
||||
# 对单个文档进行分类
|
||||
classified_doc = self._classify_document(doc)
|
||||
processed_docs.append(classified_doc)
|
||||
|
||||
return processed_docs
|
||||
|
||||
def _classify_document(self, document: Document) -> Document:
|
||||
"""对单个文档进行分类
|
||||
|
||||
Args:
|
||||
document: 待分类的文档
|
||||
|
||||
Returns:
|
||||
Document: 包含分类信息的文档
|
||||
"""
|
||||
# 确保metadata存在
|
||||
if document.metadata is None:
|
||||
document.metadata = {}
|
||||
|
||||
# 获取文档文本
|
||||
text = document.text or ""
|
||||
|
||||
# 根据策略进行分类
|
||||
if self.strategy == ClassificationStrategy.KEYWORD_MATCHING:
|
||||
category, confidence = self._classify_by_keywords(text)
|
||||
elif self.strategy == ClassificationStrategy.REGEX_PATTERN:
|
||||
category, confidence = self._classify_by_regex(text)
|
||||
elif self.strategy == ClassificationStrategy.LLM_CLASSIFICATION:
|
||||
category, confidence = self._classify_by_llm(text)
|
||||
elif self.strategy == ClassificationStrategy.HYBRID:
|
||||
category, confidence = self._classify_by_hybrid(text)
|
||||
else:
|
||||
category, confidence = self.default_category, 0.0
|
||||
|
||||
# 存储分类结果
|
||||
document.metadata[self.classification_key] = category
|
||||
document.metadata[self.confidence_key] = confidence
|
||||
|
||||
# 将分类结果添加到keywords中
|
||||
if category != self.default_category:
|
||||
document.keywords.add(f"分类:{category}")
|
||||
|
||||
return document
|
||||
|
||||
def _classify_by_keywords(self, text: str) -> tuple[str, float]:
|
||||
"""基于关键词匹配进行分类
|
||||
|
||||
Args:
|
||||
text: 文档文本
|
||||
|
||||
Returns:
|
||||
tuple: (分类结果, 置信度)
|
||||
"""
|
||||
if not self.keyword_rules:
|
||||
return self.default_category, 0.0
|
||||
|
||||
text_lower = text.lower()
|
||||
category_scores = {}
|
||||
|
||||
for category, keywords in self.keyword_rules.items():
|
||||
score = 0
|
||||
total_keywords = len(keywords)
|
||||
|
||||
for keyword in keywords:
|
||||
if keyword.lower() in text_lower:
|
||||
score += 1
|
||||
|
||||
if total_keywords > 0:
|
||||
confidence = score / total_keywords
|
||||
category_scores[category] = confidence
|
||||
|
||||
if category_scores:
|
||||
best_category = max(category_scores, key=category_scores.get)
|
||||
best_confidence = category_scores[best_category]
|
||||
|
||||
if best_confidence >= self.confidence_threshold:
|
||||
return best_category, best_confidence
|
||||
|
||||
return self.default_category, 0.0
|
||||
|
||||
def _classify_by_regex(self, text: str) -> tuple[str, float]:
|
||||
"""基于正则表达式进行分类
|
||||
|
||||
Args:
|
||||
text: 文档文本
|
||||
|
||||
Returns:
|
||||
tuple: (分类结果, 置信度)
|
||||
"""
|
||||
if not self.regex_rules:
|
||||
return self.default_category, 0.0
|
||||
|
||||
for category, pattern in self.regex_rules.items():
|
||||
if re.search(pattern, text, re.IGNORECASE | re.MULTILINE):
|
||||
return category, 1.0
|
||||
|
||||
return self.default_category, 0.0
|
||||
|
||||
def _classify_by_llm(self, text: str) -> tuple[str, float]:
|
||||
"""基于大语言模型进行分类
|
||||
|
||||
Args:
|
||||
text: 文档文本
|
||||
|
||||
Returns:
|
||||
tuple: (分类结果, 置信度)
|
||||
"""
|
||||
if not self.llm_name or not self.categories:
|
||||
return self.default_category, 0.0
|
||||
|
||||
try:
|
||||
# 构建分类提示
|
||||
prompt = self._build_classification_prompt(text)
|
||||
|
||||
# 获取LLM实例
|
||||
llm_manager = LLMManager()
|
||||
llm = llm_manager.get_instance_by_name(self.llm_name)
|
||||
|
||||
if not llm:
|
||||
return self.default_category, 0.0
|
||||
|
||||
# 调用LLM进行分类
|
||||
response = llm.run(prompt)
|
||||
|
||||
# 解析LLM响应
|
||||
category, confidence = self._parse_llm_response(response)
|
||||
|
||||
return category, confidence
|
||||
|
||||
except Exception as e:
|
||||
print(f"LLM分类出错: {e}")
|
||||
return self.default_category, 0.0
|
||||
|
||||
def _classify_by_hybrid(self, text: str) -> tuple[str, float]:
|
||||
"""混合策略分类
|
||||
|
||||
Args:
|
||||
text: 文档文本
|
||||
|
||||
Returns:
|
||||
tuple: (分类结果, 置信度)
|
||||
"""
|
||||
# 先尝试关键词匹配
|
||||
keyword_category, keyword_confidence = self._classify_by_keywords(text)
|
||||
|
||||
# 如果关键词匹配置信度足够高,直接返回
|
||||
if keyword_confidence >= self.confidence_threshold:
|
||||
return keyword_category, keyword_confidence
|
||||
|
||||
# 尝试正则表达式匹配
|
||||
regex_category, regex_confidence = self._classify_by_regex(text)
|
||||
|
||||
# 如果正则表达式匹配成功,返回结果
|
||||
if regex_confidence > 0:
|
||||
return regex_category, regex_confidence
|
||||
|
||||
# 最后尝试LLM分类
|
||||
if self.llm_name:
|
||||
llm_category, llm_confidence = self._classify_by_llm(text)
|
||||
if llm_confidence >= self.confidence_threshold:
|
||||
return llm_category, llm_confidence
|
||||
|
||||
# 如果所有策略都失败,返回关键词匹配结果(即使置信度较低)
|
||||
return keyword_category, keyword_confidence
|
||||
|
||||
def _build_classification_prompt(self, text: str) -> str:
|
||||
"""构建LLM分类提示
|
||||
|
||||
Args:
|
||||
text: 文档文本
|
||||
|
||||
Returns:
|
||||
str: 分类提示
|
||||
"""
|
||||
categories_str = "、".join(self.categories)
|
||||
|
||||
prompt = f"""请对以下文档进行分类。
|
||||
|
||||
可选分类类别:{categories_str}
|
||||
|
||||
文档内容:
|
||||
{text[:2000]} # 限制文本长度避免token过多
|
||||
|
||||
请按照以下JSON格式返回分类结果:
|
||||
{{
|
||||
"category": "分类结果",
|
||||
"confidence": 0.95,
|
||||
"reason": "分类理由"
|
||||
}}
|
||||
|
||||
如果文档内容无法明确分类,请选择"{self.default_category}"。
|
||||
"""
|
||||
return prompt
|
||||
|
||||
def _parse_llm_response(self, response: str) -> tuple[str, float]:
|
||||
"""解析LLM响应
|
||||
|
||||
Args:
|
||||
response: LLM响应文本
|
||||
|
||||
Returns:
|
||||
tuple: (分类结果, 置信度)
|
||||
"""
|
||||
try:
|
||||
# 尝试解析JSON响应
|
||||
if "{" in response and "}" in response:
|
||||
json_start = response.find("{")
|
||||
json_end = response.rfind("}") + 1
|
||||
json_str = response[json_start:json_end]
|
||||
|
||||
result = json.loads(json_str)
|
||||
category = result.get("category", self.default_category)
|
||||
confidence = float(result.get("confidence", 0.0))
|
||||
|
||||
# 验证分类结果是否在有效类别中
|
||||
if category not in self.categories and category != self.default_category:
|
||||
category = self.default_category
|
||||
confidence = 0.0
|
||||
|
||||
return category, confidence
|
||||
|
||||
except (json.JSONDecodeError, ValueError, KeyError) as e:
|
||||
print(f"解析LLM响应失败: {e}")
|
||||
|
||||
# 如果JSON解析失败,尝试简单的文本匹配
|
||||
response_lower = response.lower()
|
||||
for category in self.categories:
|
||||
if category.lower() in response_lower:
|
||||
return category, 0.8 # 给予中等置信度
|
||||
|
||||
return self.default_category, 0.0
|
||||
|
||||
def add_keyword_rule(self, category: str, keywords: List[str]) -> None:
|
||||
"""添加关键词分类规则
|
||||
|
||||
Args:
|
||||
category: 分类类别
|
||||
keywords: 关键词列表
|
||||
"""
|
||||
if category not in self.keyword_rules:
|
||||
self.keyword_rules[category] = []
|
||||
self.keyword_rules[category].extend(keywords)
|
||||
|
||||
# 确保类别在分类列表中
|
||||
if category not in self.categories:
|
||||
self.categories.append(category)
|
||||
|
||||
def add_regex_rule(self, category: str, pattern: str) -> None:
|
||||
"""添加正则表达式分类规则
|
||||
|
||||
Args:
|
||||
category: 分类类别
|
||||
pattern: 正则表达式模式
|
||||
"""
|
||||
self.regex_rules[category] = pattern
|
||||
|
||||
# 确保类别在分类列表中
|
||||
if category not in self.categories:
|
||||
self.categories.append(category)
|
||||
|
||||
def set_categories(self, categories: List[str]) -> None:
|
||||
"""设置分类类别列表
|
||||
|
||||
Args:
|
||||
categories: 分类类别列表
|
||||
"""
|
||||
self.categories = categories
|
||||
|
||||
def get_classification_summary(self, documents: List[Document]) -> Dict[str, Any]:
|
||||
"""获取分类结果统计摘要
|
||||
|
||||
Args:
|
||||
documents: 已分类的文档列表
|
||||
|
||||
Returns:
|
||||
Dict: 分类统计摘要
|
||||
"""
|
||||
summary = {
|
||||
"total_documents": len(documents),
|
||||
"category_counts": {},
|
||||
"confidence_stats": {
|
||||
"average": 0.0,
|
||||
"min": 1.0,
|
||||
"max": 0.0
|
||||
},
|
||||
"unclassified_count": 0
|
||||
}
|
||||
|
||||
total_confidence = 0.0
|
||||
confidence_count = 0
|
||||
|
||||
for doc in documents:
|
||||
if doc.metadata:
|
||||
category = doc.metadata.get(self.classification_key, self.default_category)
|
||||
confidence = doc.metadata.get(self.confidence_key, 0.0)
|
||||
|
||||
# 统计分类数量
|
||||
if category not in summary["category_counts"]:
|
||||
summary["category_counts"][category] = 0
|
||||
summary["category_counts"][category] += 1
|
||||
|
||||
# 统计置信度
|
||||
if confidence > 0:
|
||||
total_confidence += confidence
|
||||
confidence_count += 1
|
||||
summary["confidence_stats"]["min"] = min(summary["confidence_stats"]["min"], confidence)
|
||||
summary["confidence_stats"]["max"] = max(summary["confidence_stats"]["max"], confidence)
|
||||
|
||||
# 统计未分类文档
|
||||
if category == self.default_category:
|
||||
summary["unclassified_count"] += 1
|
||||
|
||||
# 计算平均置信度
|
||||
if confidence_count > 0:
|
||||
summary["confidence_stats"]["average"] = total_confidence / confidence_count
|
||||
|
||||
return summary
|
||||
@@ -1,185 +0,0 @@
|
||||
name: 'document_classifier'
|
||||
description: '智能文档分类器,支持多种分类策略'
|
||||
strategy: 'keyword_matching' # keyword_matching, regex_pattern, llm_classification, hybrid
|
||||
confidence_threshold: 0.7
|
||||
default_category: '未分类'
|
||||
llm_name: null # 用于LLM分类的模型名称,如 'gpt-4', 'claude-3' 等
|
||||
categories:
|
||||
- '技术文档'
|
||||
- '学术论文'
|
||||
- '新闻资讯'
|
||||
- '商业报告'
|
||||
- '法律文档'
|
||||
- '教育材料'
|
||||
- '医疗文档'
|
||||
- '其他'
|
||||
|
||||
# 关键词分类规则
|
||||
keyword_rules:
|
||||
技术文档:
|
||||
- '代码'
|
||||
- 'API'
|
||||
- '接口'
|
||||
- '函数'
|
||||
- '方法'
|
||||
- '类'
|
||||
- '模块'
|
||||
- '配置'
|
||||
- '安装'
|
||||
- '部署'
|
||||
- '调试'
|
||||
- '错误'
|
||||
- '异常'
|
||||
- '日志'
|
||||
- '编程'
|
||||
- '开发'
|
||||
- '软件'
|
||||
- '系统'
|
||||
- '数据库'
|
||||
- '算法'
|
||||
|
||||
学术论文:
|
||||
- '摘要'
|
||||
- '引言'
|
||||
- '方法'
|
||||
- '实验'
|
||||
- '结果'
|
||||
- '讨论'
|
||||
- '结论'
|
||||
- '参考文献'
|
||||
- '引用'
|
||||
- '研究'
|
||||
- '分析'
|
||||
- '数据'
|
||||
- '论文'
|
||||
- '学术'
|
||||
- '理论'
|
||||
- '模型'
|
||||
- '假设'
|
||||
- '验证'
|
||||
- '统计'
|
||||
- '显著性'
|
||||
|
||||
新闻资讯:
|
||||
- '报道'
|
||||
- '消息'
|
||||
- '新闻'
|
||||
- '事件'
|
||||
- '发生'
|
||||
- '时间'
|
||||
- '地点'
|
||||
- '人物'
|
||||
- '原因'
|
||||
- '影响'
|
||||
- '结果'
|
||||
- '记者'
|
||||
- '采访'
|
||||
- '现场'
|
||||
- '突发'
|
||||
- '最新'
|
||||
- '今日'
|
||||
- '昨日'
|
||||
- '明天'
|
||||
|
||||
商业报告:
|
||||
- '报告'
|
||||
- '分析'
|
||||
- '数据'
|
||||
- '统计'
|
||||
- '趋势'
|
||||
- '预测'
|
||||
- '建议'
|
||||
- '策略'
|
||||
- '市场'
|
||||
- '销售'
|
||||
- '收入'
|
||||
- '利润'
|
||||
- '财务'
|
||||
- '预算'
|
||||
- '投资'
|
||||
- '收益'
|
||||
- '成本'
|
||||
- '风险'
|
||||
- '机会'
|
||||
|
||||
法律文档:
|
||||
- '法律'
|
||||
- '法规'
|
||||
- '条例'
|
||||
- '条款'
|
||||
- '权利'
|
||||
- '义务'
|
||||
- '责任'
|
||||
- '合同'
|
||||
- '协议'
|
||||
- '争议'
|
||||
- '诉讼'
|
||||
- '法院'
|
||||
- '判决'
|
||||
- '律师'
|
||||
- '当事人'
|
||||
- '证据'
|
||||
- '证人'
|
||||
- '辩护'
|
||||
- '上诉'
|
||||
|
||||
教育材料:
|
||||
- '学习'
|
||||
- '教学'
|
||||
- '课程'
|
||||
- '教材'
|
||||
- '练习'
|
||||
- '考试'
|
||||
- '作业'
|
||||
- '知识点'
|
||||
- '概念'
|
||||
- '原理'
|
||||
- '方法'
|
||||
- '教育'
|
||||
- '培训'
|
||||
- '指导'
|
||||
- '说明'
|
||||
- '步骤'
|
||||
- '示例'
|
||||
- '习题'
|
||||
- '答案'
|
||||
|
||||
医疗文档:
|
||||
- '疾病'
|
||||
- '症状'
|
||||
- '诊断'
|
||||
- '治疗'
|
||||
- '药物'
|
||||
- '手术'
|
||||
- '检查'
|
||||
- '化验'
|
||||
- '病历'
|
||||
- '处方'
|
||||
- '康复'
|
||||
- '患者'
|
||||
- '医生'
|
||||
- '医院'
|
||||
- '护理'
|
||||
- '健康'
|
||||
- '医疗'
|
||||
- '临床'
|
||||
- '病理'
|
||||
|
||||
# 正则表达式分类规则
|
||||
regex_rules:
|
||||
技术文档: '(?i)(代码|API|接口|函数|方法|类|模块|配置|安装|部署|调试|错误|异常|日志|编程|开发|软件|系统|数据库|算法)'
|
||||
学术论文: '(?i)(摘要|引言|方法|实验|结果|讨论|结论|参考文献|引用|研究|分析|数据|论文|学术|理论|模型|假设|验证|统计|显著性)'
|
||||
新闻资讯: '(?i)(报道|消息|新闻|事件|发生|时间|地点|人物|原因|影响|结果|记者|采访|现场|突发|最新|今日|昨日|明天)'
|
||||
商业报告: '(?i)(报告|分析|数据|统计|趋势|预测|建议|策略|市场|销售|收入|利润|财务|预算|投资|收益|成本|风险|机会)'
|
||||
法律文档: '(?i)(法律|法规|条例|条款|权利|义务|责任|合同|协议|争议|诉讼|法院|判决|律师|当事人|证据|证人|辩护|上诉)'
|
||||
教育材料: '(?i)(学习|教学|课程|教材|练习|考试|作业|知识点|概念|原理|方法|教育|培训|指导|说明|步骤|示例|习题|答案)'
|
||||
医疗文档: '(?i)(疾病|症状|诊断|治疗|药物|手术|检查|化验|病历|处方|康复|患者|医生|医院|护理|健康|医疗|临床|病理)'
|
||||
|
||||
# 分类结果存储键名
|
||||
classification_key: 'classification'
|
||||
confidence_key: 'classification_confidence'
|
||||
|
||||
metadata:
|
||||
type: 'DOC_PROCESSOR'
|
||||
module: 'agentuniverse.agent.action.knowledge.doc_processor.document_classifier'
|
||||
class: 'DocumentClassifier'
|
||||
Reference in New Issue
Block a user