AIOPS
大约 3 分钟
切分设置
chunk_size=1024
,over_lap=100
标题增强
把每个txt的第一行作为文件名
给每个切分后的node加上 文件前一级路径
+ 文件名
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 ''
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)
关键词
逐一文本匹配
大模型提取
- 速度慢
- 最好多线程并行
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
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
相似度计算
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
存储和加载
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'关键词提取完成')
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
检索
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))
分类
后续,问题提供了出处所在文件夹
- 共四个文件夹
- 每个文件夹建立对应的向量库、index、retriever、nodes_keywords
- 检索时使用对应的retriever