1
0

2 کامیت‌ها 7f2b6c6b1b ... f7b988b35d

نویسنده SHA1 پیام تاریخ
  linruying f7b988b35d initial commit for rag commit 2 ماه پیش
  linruying 008f3e68bb initial for rag [project 2 ماه پیش

+ 73 - 0
02_rag_build/config.py

@@ -0,0 +1,73 @@
+"""
+统一配置模块:环境变量、数据库连接、LLM 实例。
+
+其他模块直接 `from config import llm, db, engine` 即可,
+无需重复写 load_dotenv 和 db_uri 构造逻辑。
+"""
+
+import os
+from dotenv import load_dotenv
+from langchain_openai import ChatOpenAI
+from langchain_community.utilities import SQLDatabase
+from sqlalchemy import create_engine
+from langchain_community.embeddings import DashScopeEmbeddings
+from langchain_community.chat_models import ChatTongyi
+
+
+# ============================================================
+# 1. 加载 .env(整个项目只调用一次)
+# ============================================================
+load_dotenv(os.path.join(os.path.dirname(__file__), "..", ".env"))
+
+# ============================================================
+# 2. 数据库配置
+# ============================================================
+DB_HOST = os.getenv("DB_HOST", "127.0.0.1")
+DB_PORT = os.getenv("DB_PORT", "3306")
+DB_USER = os.getenv("DB_USER", "root")
+DB_PASSWORD = os.getenv("DB_PASSWORD", "")
+DB_NAME = os.getenv("DB_NAME", "analytics_demo")
+
+DB_URI = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
+
+# SQLAlchemy 引擎(DataFrame 加载用)
+engine = create_engine(DB_URI)
+
+# LangChain SQLDatabase 实例(Agent 工具包用)
+db = SQLDatabase.from_uri(DB_URI)
+
+# ============================================================
+# 3. DEEPSEEK LLM 配置
+# ============================================================
+DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
+DEEPSEEK_BASE_URL = os.getenv("DEEPSEEK_BASE_URL")
+
+llm = ChatOpenAI(
+    model="deepseek-v4-flash",
+    api_key=DEEPSEEK_API_KEY,
+    base_url=DEEPSEEK_BASE_URL,
+    temperature=0,
+)
+
+print("✅ 配置加载完成(LLM + 数据库连接)")
+
+
+
+# ============================================================
+# 4. ALI LLM 配置
+# ============================================================
+# 初始化 Embedding 模型
+# 需要先在 https://dashscope.console.aliyun.com/ 获取 API Key
+DASHSCOPE_API_KEY=os.getenv("DASHSCOPE_API_KEY")
+embedding_model = DashScopeEmbeddings(
+    model="text-embedding-v3",        # 模型名称
+    dashscope_api_key=DASHSCOPE_API_KEY   # 从 .env 读取
+)
+
+# ============================================================
+# 5. ChatTongyi LLM 配置
+# ============================================================
+ChatTongyillm = ChatTongyi(
+    model="qwen-plus", 
+    dashscope_api_key=DASHSCOPE_API_KEY
+    )

BIN
02_rag_build/data/car_info.pdf


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 7 - 0
02_rag_build/data/result.json


+ 620 - 0
02_rag_build/data/test_question.json

@@ -0,0 +1,620 @@
+[
+    {
+        "question": "中国足球的队长是谁",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "新冠肺炎如何预防?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "交通事故如何处理?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "怎样加热座椅?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "自动模式下,中央显示屏是如何切换日间和夜间模式的?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何通过中央显示屏进行副驾驶员座椅设置?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "副仪表台按钮如何操作中央显示屏?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何从锁定状态唤醒中央显示器?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何正确使用颈椎保护系统?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "前方交叉路口预警系统(FCTA)的作用是什么?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在使用FCTA时需要注意哪些事项?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何打开车辆尾门?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在哪些情况下智能钥匙可能会受到干扰,导致功能异常?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "车辆尾门的防夹保护功能是如何工作的?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在操作电动后备厢时需要注意哪些事项?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何进入车辆功能界面?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在车辆功能界面有哪些操作选项?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何编辑快捷开关图标?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何减少车辆腐蚀风险?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何通过空调系统面板调节空调风量?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何创建新的Lynk&CoID?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "什么是车主账户?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何创建人脸识别?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何添加亲情账号?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何开启或关闭用车偏好自动同步?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何熄火我的车辆?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何通过遥控钥匙启动车辆?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如果遥控钥匙电池电量低,我应该如何启动车辆?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何调节外后视镜?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "外部反光境显示物体距离是否准确?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "什么是自动驻车系统?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在什么情况下会停用AutoHold并启用EPB功能?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "中央扶手箱的USB接口有几个?它们分别是什么类型?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "中央扶手箱所支持的U盘及数据传输格式有哪些?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何通过中央显示屏调节驾驶员侧座椅通风强度?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何关闭前排座行车通风功能?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何进入系统设置界面?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在系统界面可以进行哪些操作?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "什么是无钥匙进入系统?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何设置无钥匙解锁模式?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "设置无钥匙解锁中单门和全车的区别在于什么?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "驾驶车辆时应遵守哪些注意事项?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何启用或停用手套箱密码保护功能?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "驾驶员状态监测系统是如何工作的?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "什么情况下会影响到驾驶员状态监测系统的工作?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何启用后排儿童锁功能?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "安全气囊是什么?它的作用是什么?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如果未使用或未正确使用安全带,会对安全气囊有何影响? ",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在使用车辆时,有哪些安全气囊的注意事项?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何开启动力电池电量保持功能?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在开启动力电池的情况下,选择经济性优先和充电速度优先有什么区别?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "后方碰撞预警系统在什么情况下会启动?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何调整方向盘的位置?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "什么情况下不能调节车辆的方向盘?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何通过手机APP启动车辆?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "什么是陡坡缓降系统(HDC什么是陡坡缓降系统(HDC)?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "当坡度过大时,如何操作才能使车辆保持匀速地行驶?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在什么情况下HDC会激活?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在什么情况下无法激活或自动退出HDC功能?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "开启陡坡缓降系统后,组合仪表显示什么颜色的指示灯?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "激活陡坡缓降系统时,组合仪表显示什么颜色的指示灯?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "当陡坡缓降系统出现故障时,组合仪表会显示怎样的提示?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "坡道辅助系统的主要功能是什么?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "使用坡道辅助系统时需要注意哪些警告?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在有路缘石的上坡和下坡驻车时,应如何操作?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "新车在最初的2000km行驶期间应遵守哪些事项?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "当刹车片将达到最小安全厚度时会有什么表现?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如果听到持续的高频尖锐噪声应该怎么做?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "风挡摄像头和前雷达在什么情况下会弹出警示消息?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何通过蓝牙钥匙启动车辆?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在什么情况下无法通过蓝牙钥匙启动车辆?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何通过网络远程启动发动机?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "变道辅助系统由哪三部分组成?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何检查制动液液位?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何调节车辆的背光亮度?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在白天和夜晚,开启和未开启整车背光联动时,转动调光旋钮有什么不同?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "当车辆发生故障时,应该如何处理?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "为什么不建议自行修理车辆故障?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "什么是能量回收系统?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何知道车辆正在进行能量回收?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "影响能量回收多少的因素有哪些?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "什么是智能远近光控制系统?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在什么情况下可以开启智能远近光控制系统?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "哪些因素可能导致智能远近光控制系统无法正常工作?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何启用前除霜/除雾功能?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "当我开启前挡风玻璃的去冰去雾模式时,车辆会有什么反应?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "为什么驾驶之前需要确保挡风玻璃无冰渣、积雪或冷凝水?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "什么情况下车辆需要进行报废处理?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "什么是遥控泊车(RPA)?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "使用Lynk&CoApp的RPA功能需要注意什么?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何开启方向盘助力与驾驶模式联动?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "有哪些可选的方向盘转向助力模式?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "主动式座舱清洁系统的作用是什么?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "全景天窗是由几部分组成的?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何使用位置记忆功能?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何更换后挡风玻璃雨刮片?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "在更换雨刮片时需要注意什么?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "如何添加香氛精油?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "我应该在哪里添加香氛精油?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "什么是转向助力系统?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "涉水行驶前应注意什么?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "涉水驾驶后需要进行哪些检查?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    },
+    {
+        "question": "什么时候应该为车辆打蜡?",
+        "answer_1": "",
+        "answer_2": "",
+        "answer_3": ""
+    }
+]

BIN
02_rag_build/knowledge_db/bcdc4df2-cfad-4a73-ba4c-df7709c6dd98/data_level0.bin


BIN
02_rag_build/knowledge_db/bcdc4df2-cfad-4a73-ba4c-df7709c6dd98/header.bin


BIN
02_rag_build/knowledge_db/bcdc4df2-cfad-4a73-ba4c-df7709c6dd98/index_metadata.pickle


BIN
02_rag_build/knowledge_db/bcdc4df2-cfad-4a73-ba4c-df7709c6dd98/length.bin


BIN
02_rag_build/knowledge_db/bcdc4df2-cfad-4a73-ba4c-df7709c6dd98/link_lists.bin


BIN
02_rag_build/knowledge_db/chroma.sqlite3


+ 90 - 0
02_rag_build/main.py

@@ -0,0 +1,90 @@
+import os
+from langchain_community.document_loaders import PyMuPDFLoader
+from langchain_text_splitters import RecursiveCharacterTextSplitter
+from utils.dataclean import clean_pdf_text
+from langchain_community.vectorstores import Chroma
+from langchain_core.prompts import ChatPromptTemplate
+from langchain_core.output_parsers import StrOutputParser
+from config import embedding_model, ChatTongyillm
+
+
+# ========== 第一步:加载文档 ==========
+# 获取当前脚本所在目录,构建 PDF 文件的绝对路径
+script_dir = os.path.dirname(os.path.abspath(__file__))
+pdf_path = os.path.join(script_dir, "data", "car_info.pdf")
+
+# 创建加载器实例,传入 PDF 文件路径
+pdf_loader = PyMuPDFLoader(pdf_path)
+
+# 调用 load() 方法,返回一个 Document 列表(每页一个 Document)
+pdf_pages = pdf_loader.load()
+print("加载资料完成")
+
+
+# ========== 第二步:清洗数据(可选,根据文档质量决定)==========
+# 清洗每个 Document 的文本内容
+for page in pdf_pages:
+    page.page_content = clean_pdf_text(page.page_content)
+print("清晰资料完成")    
+
+
+# ========== 第三步:创建递归字符分割器 ==========
+text_splitter = RecursiveCharacterTextSplitter(
+    # 分隔符优先级:段落 → 换行 → 句号 → 空格 → 硬切
+    separators=["\n\n", "\n", "。", "!", "?", " ", ""],
+    
+    # 每个块最大 50 字符
+    chunk_size=50,
+    
+    # 相邻块重叠 10 字符(chunk_size 的 20%)
+    chunk_overlap=10,
+    
+    # 长度计算函数
+    length_function=len
+)
+
+# 对整个文档切分
+split_docs = text_splitter.split_documents(pdf_pages)
+print("切割资料完成") 
+
+# ========== 第四步:向量化 + 存入向量库 ==========
+vectorstore = Chroma.from_documents(
+    documents=split_docs,
+    embedding=embedding_model,
+    persist_directory="./knowledge_db"
+)
+print("存储向量资料完成") 
+
+# ========== 第五步:创建检索器 ==========
+retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
+
+# ========== 第六步:提问 ==========
+query = "什么汽车比较经济实惠"
+relevant_docs = retriever.invoke(query)
+
+# ========== 第七步:生成回答 ==========
+context = "\n\n---\n\n".join([d.page_content for d in relevant_docs])
+
+prompt = ChatPromptTemplate.from_template("""
+你是一个专业的知识库助手。请根据以下上下文回答问题。
+
+**规则:**
+- 只基于提供的上下文回答,不要编造
+- 如果上下文中没有相关信息,直接说「根据现有资料,我找不到这个问题的答案」
+- 回答要简洁直接,引用原文时用引号
+
+**上下文:**
+{context}
+
+**问题:**
+{question}
+""")
+
+
+chain = prompt | ChatTongyillm | StrOutputParser()
+
+
+if __name__ == "__main__":
+
+    answer = chain.invoke({"context": context, "question": query})
+    print(answer)

+ 15 - 0
02_rag_build/utils/dataclean.py

@@ -0,0 +1,15 @@
+def clean_pdf_text(text: str) -> str:
+    """清洗 PDF 解析出的文本,去除常见噪声"""
+    import re
+    
+    # 删除非中文字符之间的换行符
+    text = re.sub(r'[^一](\n)[^一]', 
+                  lambda m: m.group(0).replace('\n', ''), text)
+    
+    # 删除项目符号和多余空格
+    text = text.replace('•', '').replace('  ', ' ')
+    
+    # 删除连续的换行符(保留一个)
+    text = re.sub(r'\n{2,}', '\n', text)
+    
+    return text.strip()

+ 48 - 0
add_git_bash_to_menu.reg

@@ -0,0 +1,48 @@
+Windows Registry Editor Version 5.00
+
+; 文件夹右键 → "Git Bash Here"
+[HKEY_CLASSES_ROOT\Directory\shell\gitbash]
+@="Git Bash Here"
+"Icon"="D:\\Git\\git-bash.exe"
+
+[HKEY_CLASSES_ROOT\Directory\shell\gitbash\command]
+@="D:\\Git\\git-bash.exe"
+
+; 文件夹空白处右键 → "Git Bash Here"
+[HKEY_CLASSES_ROOT\Directory\Background\shell\gitbash]
+@="Git Bash Here"
+"Icon"="D:\\Git\\git-bash.exe"
+
+[HKEY_CLASSES_ROOT\Directory\Background\shell\gitbash\command]
+@="D:\\Git\\git-bash.exe"
+
+; 右键 .git 文件夹也可以打开
+[HKEY_CLASSES_ROOT\LibraryFolder\background\shell\gitbash]
+@="Git Bash Here"
+"Icon"="D:\\Git\\git-bash.exe"
+
+[HKEY_CLASSES_ROOT\LibraryFolder\background\shell\gitbash\command]
+@="D:\\Git\\git-bash.exe"
+
+; ===== Git GUI Here =====
+
+[HKEY_CLASSES_ROOT\Directory\shell\gitgui]
+@="Git GUI Here"
+"Icon"="D:\\Git\\cmd\\git-gui.exe"
+
+[HKEY_CLASSES_ROOT\Directory\shell\gitgui\command]
+@="D:\\Git\\cmd\\git-gui.exe"
+
+[HKEY_CLASSES_ROOT\Directory\Background\shell\gitgui]
+@="Git GUI Here"
+"Icon"="D:\\Git\\cmd\\git-gui.exe"
+
+[HKEY_CLASSES_ROOT\Directory\Background\shell\gitgui\command]
+@="D:\\Git\\cmd\\git-gui.exe"
+
+[HKEY_CLASSES_ROOT\LibraryFolder\background\shell\gitgui]
+@="Git GUI Here"
+"Icon"="D:\\Git\\cmd\\git-gui.exe"
+
+[HKEY_CLASSES_ROOT\LibraryFolder\background\shell\gitgui\command]
+@="D:\\Git\\cmd\\git-gui.exe"

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است