CoolFace
Apppublic

FinRAG-Agent/FinRAG-Pro

sourceHugging Facemitupdated 4mo agoView on Hugging Face
1likes
build_knowledge_base.py177 linesDownload Raw Back to backend
1"""2金融投研RAG系统 - 知识库构建模块 v5 (精准制导版)3完全适配 reports/annual 和 reports/industry 目录结构4"""5 6import os7import re8import json9import pickle10import numpy as np11 12BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))13# 精准定位你的两个子文件夹14ANNUAL_DIR = os.path.join(BASE_DIR, "reports", "annual")15INDUSTRY_DIR = os.path.join(BASE_DIR, "reports", "industry")16 17PROCESSED_DIR = os.path.join(BASE_DIR, "data", "processed")18INDEX_DIR = os.path.join(BASE_DIR, "data", "index")19 20os.makedirs(PROCESSED_DIR, exist_ok=True)21os.makedirs(INDEX_DIR, exist_ok=True)22 23def load_specific_directory(directory: str, source_type: str) -> list[dict]:24    """精准读取指定目录下的 TXT 和 PDF"""25    documents = []26    27    if not os.path.exists(directory):28        print(f"[WARN] 找不到文件夹: {directory}")29        return documents30 31    print(f"\n📁 正在精准扫描目录: {directory}")32    files = os.listdir(directory)33    34    for filename in files:35        file_path = os.path.join(directory, filename)36        if not os.path.isfile(file_path):37            continue38 39        name_no_ext, ext = os.path.splitext(filename)40        ext = ext.lower()  # 无视大小写41 42        if ext not in ['.txt', '.pdf']:43            continue44 45        # --- 读取 TXT 文件 ---46        if ext == '.txt':47            try:48                with open(file_path, "r", encoding="utf-8") as f:49                    content = f.read()50                documents.append({51                    "id": name_no_ext, "filename": filename, "title": name_no_ext,52                    "content": content, "source": filename, 53                    "source_type": source_type, "source_dir": os.path.basename(directory)54                })55                print(f"  ✅ [TXT] 成功读取: {filename}")56            except Exception as e:57                print(f"  ❌ [TXT] 读取失败 {filename}: {e}")58 59        # --- 读取 PDF 文件 ---60        elif ext == '.pdf':61            try:62                import fitz  # PyMuPDF63                pdf_doc = fitz.open(file_path)64                content_parts = []65                for page in pdf_doc:66                    page_text = page.get_text()67                    if page_text.strip():68                        content_parts.append(page_text)69                content = "\n\n".join(content_parts)70                pdf_doc.close()71 72                documents.append({73                    "id": name_no_ext, "filename": filename, "title": name_no_ext,74                    "content": content, "source": filename, 75                    "source_type": source_type, "source_dir": os.path.basename(directory)76                })77                print(f"  ✅ [PDF] 成功读取: {filename}")78            except Exception as e:79                print(f"  ❌ [PDF] 读取失败 {filename}: {e}")80 81    return documents82 83def clean_text(text: str) -> str:84    text = re.sub(r'={5,}.*?={5,}', '', text)85    text = re.sub(r'\n{3,}', '\n\n', text)86    lines = [line.strip() for line in text.split('\n')]87    text = '\n'.join(lines)88    text = re.sub(r'[ \t]+', ' ', text)89    text = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '', text)90    return text.strip()91 92def chunk_text(text: str) -> list[str]:93    from langchain_text_splitters import RecursiveCharacterTextSplitter94    splitter = RecursiveCharacterTextSplitter(95        chunk_size=800, chunk_overlap=150,96        separators=["\n\n", "\n", "。", "!", "?", ",", " "],97        length_function=len98    )99    chunks = splitter.split_text(text)100    return [c.strip() for c in chunks if c.strip()]101 102def build_index():103    print("=" * 60)104    print("  金融投研RAG - 知识库构建 v5(精准制导版)")105    print("=" * 60)106 107    # 1. 加载文档 (分两批精准加载)108    print("\n[步骤1] 按分类加载文档...")109    docs_annual = load_specific_directory(ANNUAL_DIR, "官方年报")110    docs_industry = load_specific_directory(INDUSTRY_DIR, "第三方研报")111    112    all_documents = docs_annual + docs_industry113    114    if not all_documents:115        print("[ERROR] 苍天啊!文件依然没读到,请检查路径!")116        return False117 118    print(f"\n  🎉 共成功加载 {len(all_documents)} 份文档!")119 120    # 2. 清洗121    print("\n[步骤2] 清洗文本...")122    for doc in all_documents:123        doc["content"] = clean_text(doc["content"])124 125    # 3. 切分126    print("\n[步骤3] 切分文本chunks...")127    all_chunks = []128    chunk_metadata = []129 130    for doc in all_documents:131        chunks = chunk_text(doc["content"])132        for i, chunk in enumerate(chunks):133            chunk_id = f"{doc['id']}_chunk_{i:04d}"134            all_chunks.append(chunk)135            chunk_metadata.append({136                "chunk_id": chunk_id, "doc_id": doc["id"], "doc_title": doc["title"],137                "source": doc["source"], "source_type": doc["source_type"], 138                "source_dir": doc["source_dir"], "chunk_index": i,139            })140 141    # 保存Chunks142    chunks_path = os.path.join(PROCESSED_DIR, "chunks.json")143    meta_path = os.path.join(PROCESSED_DIR, "chunk_metadata.json")144    with open(chunks_path, "w", encoding="utf-8") as f:145        json.dump(all_chunks, f, ensure_ascii=False, indent=2)146    with open(meta_path, "w", encoding="utf-8") as f:147        json.dump(chunk_metadata, f, ensure_ascii=False, indent=2)148 149    # 4. 生成向量150    print("\n[步骤4] 生成向量 (TF-IDF)...")151    from sklearn.feature_extraction.text import TfidfVectorizer152    vectorizer = TfidfVectorizer(max_features=5000, analyzer='char', ngram_range=(1, 3), sublinear_tf=True)153    embeddings = vectorizer.fit_transform(all_chunks).toarray().astype(np.float32)154 155    v_path = os.path.join(INDEX_DIR, "tfidf_vectorizer.pkl")156    with open(v_path, "wb") as f:157        pickle.dump(vectorizer, f)158 159    # 5. 保存FAISS160    print("\n[步骤5] 保存FAISS索引...")161    import faiss162    dimension = embeddings.shape[1]163    index = faiss.IndexFlatIP(dimension)164    faiss.normalize_L2(embeddings)165    index.add(embeddings.astype(np.float32))166    index_path = os.path.join(INDEX_DIR, "faiss_index.bin")167    faiss.write_index(index, index_path)168 169    summary = {"total_documents": len(all_documents), "total_chunks": len(all_chunks)}170    with open(os.path.join(PROCESSED_DIR, "summary.json"), "w", encoding="utf-8") as f:171        json.dump(summary, f, ensure_ascii=False, indent=2)172 173    print("\n✅ 奇迹发生!知识库构建彻底完成!")174    return True175 176if __name__ == "__main__":177    build_index()