目录

AIOPS

切分设置

chunk_size=1024over_lap=100

标题增强

把每个txt的第一行作为文件名

给每个切分后的node加上 文件前一级路径 + 文件名

./AIOPS.assets/image-20240625145020365.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
        for node in tqdm(nodes):
            f_n = extract_txt_prefix(node.metadata['file_name'])
            f_p = extract_path_prefix(node.metadata['file_path'])

            node.text = f'{f_p}\n{f_n}\n' + node.text

def extract_txt_prefix(file_name):
    """提取.txt前面的内容"""
    if file_name.endswith('.txt'):
        return file_name[:-4]
    else:
        return file_name  # 如果不是以.txt结尾,则返回整个文件名

def extract_path_prefix(file_path):
    """提取最后两个斜杠之间的内容"""
    # 使用正则表达式匹配最后两个斜杠之间的内容
    # ([^/]+):匹配一个或多个非/的字符,并将它们作为一个捕获组。
    # (?=\/[^/]*$):是一个正向预查(lookahead),用于确保匹配的字符串后面跟随一个/,并且直到字符串末尾可以有任意数量的非/字符。
    match = re.search(r'([^/]+)(?=\/[^/]*$)', file_path)
    if match:
        return match.group(1)
    else:
        return ''
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import os

def process_files(folder_path):
    for root, _, files in os.walk(folder_path):
        for filename in files:
            if filename.endswith('.txt') and filename != 'index.txt':
                file_path = os.path.join(root, filename)
                try:
                    with open(file_path, 'r', encoding='utf-8') as file:
                        first_line = file.readline().strip()
                    new_file_name = os.path.join(root, first_line + '.txt')
                    os.rename(file_path, new_file_name)
                    print(f'Renamed "{filename}" to "{first_line}.txt"')
                except Exception as e:
                    print(f'Error processing {filename}: {str(e)}')

# Example usage:
folder_path = 'aiops-2024/data_1'  # Replace with the path to your folder
process_files(folder_path)

关键词

逐一文本匹配

大模型提取

  • 速度慢
  • 最好多线程并行
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def extra_keywords(llm, text, cnt, debug=False) -> set:
    query = f"请从以下内容中,提取{cnt}个关键词,关键词之间使用空格分割:\n{text}"
  
    response = llm.my_complete(query)

    if debug:
        logger.info(query)
        logger.info(f'提取关键词 response:\n{response}')

    # 将字符串用空格分割
    words = response.split()

    return set(words)

jieba

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from __future__ import unicode_literals

import jieba
import jieba.posseg
import jieba.analyse

def getKeyWords(sentence : str, topk : int=5) -> set:
    # for x, w in jieba.analyse.extract_tags(sentence, withWeight=True, topK=topk):
    #     print('%s %s' % (x, w))
    keywords = set(jieba.analyse.extract_tags(sentence, withWeight=False, topK=topk, withFlag=False))
    return keywords

相似度计算

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def keywords_similarity(textSet: set, querySet: set) -> float:
    """
    计算查询集合和文本集合之间的关键词相似度。

    :param text_set: 文本集合,包含所有关键词。
    :param query_set: 查询集合,包含需要比较的关键词。
    :return: 相似度分数,范围从0到1。
    """
    # 使用集合交集计算共有关键词数量,然后除以查询集合的大小
    commonKeywords = textSet.intersection(querySet)
    similarityScore = len(commonKeywords) / len(querySet) if querySet else 0

    return similarityScore

存储和加载

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def build_keywords(nodes, name, cnt=15, debug=False):
    logger.info(f'开始提取关键词')

    node_keywords = {} # id: [keys, text]

    for node in tqdm(nodes):
        # TODO 多线程调用关键词提取
        text = node.text
        # node_keywords[node.node_id] = [extra_keywords(text, cnt, False), text]
        node_keywords[node.node_id] = [getKeyWords(text, 30), text]

        if debug:
            logger.info(f'关键词提取结果:\n{node.node_id} : {node_keywords[node.node_id]}')

    file_path = name + config.NODE_KEYWORDS_PATH

    # # 确保目标目录存在
    # os.makedirs(os.path.dirname(file_path), exist_ok=True)
  
    # 序列化并保存 node_keywords 对象
    with open(file_path, 'wb') as f:
        pickle.dump(node_keywords, f)

    logger.info(f'保存 node_keywords 到 {file_path}')

    logger.info(f'关键词提取完成')
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def load_keywords(name):
    file_path = name + config.NODE_KEYWORDS_PATH

    # 检查文件是否存在
    if not os.path.exists(file_path):
        logger.error(f'{file_path} does not exist.')
        return None
  
    # 从文件中加载 nodes 对象
    with open(file_path, 'rb') as f:
        node_keywords = pickle.load(f)

    logger.info(f'从 {file_path} 加载 node_keywords')

    return node_keywords

检索

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
	q_keys = getKeyWords(query_text, 15)

    heap = []  # 创建一个空堆
      
    node_keywords = all_nodes[query_document]

    for k, v in node_keywords.items():
        similarity = keywords_similarity(q_keys, v[0])

        heapq.heappush(heap, (similarity, k))
        if len(heap) > key_topk:
            heapq.heappop(heap)  # 保持堆的大小不超过 key_topk

            key_ids = sorted(heap, reverse=True, key=lambda x: x[0])
            key_sims = [key[0] for key in key_ids]

            key_ids = [res[1] for res in key_ids]
      
      
        merged = list(dict.fromkeys([id for id in key_ids if id in vec_ids] + key_ids + vec_ids))

分类

后续,问题提供了出处所在文件夹

./AIOPS.assets/image-20240625150819315.png

  • 共四个文件夹
  • 每个文件夹建立对应的向量库、index、retriever、nodes_keywords
  • 检索时使用对应的retriever

摘要