From 0440e8c817215479a6f3a06cf6dd9ec7bdf6c527 Mon Sep 17 00:00:00 2001 From: HuangHai <10402852@qq.com> Date: Tue, 19 Aug 2025 14:02:48 +0800 Subject: [PATCH] 'commit' --- dsLightRag/Config/Config.py | 13 +- .../Config/__pycache__/Config.cpython-310.pyc | Bin 1902 -> 2087 bytes dsLightRag/ElasticSearch/T1_RebuildMapping.py | 32 + dsLightRag/ElasticSearch/T2_Vector.py | 38 + dsLightRag/ElasticSearch/T3_InsertData.py | 28 + dsLightRag/ElasticSearch/T4_SelectAllData.py | 28 + .../ElasticSearch/T5_SelectByKeyWord.py | 28 + dsLightRag/ElasticSearch/T6_SelectByVector.py | 29 + .../ElasticSearch/T7_XiangLiangQuery.py | 66 ++ .../Utils/ElasticsearchCollectionManager.py | 110 +++ .../Utils/ElasticsearchConnectionPool.py | 65 ++ .../ElasticSearch/Utils/EsSearchUtil.py | 664 ++++++++++++++++++ .../ElasticSearch/Utils/VectorDBUtil.py | 125 ++++ dsLightRag/ElasticSearch/Utils/__init__.py | 0 ...ticsearchCollectionManager.cpython-310.pyc | Bin 0 -> 3488 bytes ...lasticsearchConnectionPool.cpython-310.pyc | Bin 0 -> 2754 bytes .../__pycache__/EsSearchUtil.cpython-310.pyc | Bin 0 -> 16161 bytes .../__pycache__/VectorDBUtil.cpython-310.pyc | Bin 0 -> 4136 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 158 bytes dsLightRag/ElasticSearch/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 144 bytes dsLightRag/Routes/QA.py | 203 ++++++ .../TeachingModel/api/LoginController.py | 9 +- .../__pycache__/DmController.cpython-310.pyc | Bin 0 -> 1802 bytes .../DocumentController.cpython-310.pyc | Bin 0 -> 5098 bytes .../LoginController.cpython-310.pyc | Bin 0 -> 5041 bytes .../TeachingModelController.cpython-310.pyc | Bin 0 -> 9091 bytes .../ThemeController.cpython-310.pyc | Bin 0 -> 4733 bytes .../UserController.cpython-310.pyc | Bin 0 -> 2030 bytes .../__pycache__/dependencies.cpython-310.pyc | Bin 0 -> 994 bytes .../BackgroundTasks.cpython-310.pyc | Bin 0 -> 3872 bytes .../__pycache__/Knowledge.cpython-310.pyc | Bin 3522 -> 3506 bytes .../Routes/__pycache__/Rag.cpython-310.pyc | Bin 7287 -> 7275 bytes dsLightRag/Start.py | 6 +- .../__pycache__/CommonUtil.cpython-310.pyc | Bin 0 -> 532 bytes .../__pycache__/CookieUtil.cpython-310.pyc | Bin 0 -> 2530 bytes .../Util/__pycache__/Database.cpython-310.pyc | Bin 0 -> 7037 bytes .../Util/__pycache__/DocxUtil.cpython-310.pyc | Bin 3125 -> 3503 bytes .../Util/__pycache__/JwtUtil.cpython-310.pyc | Bin 0 -> 855 bytes .../Util/__pycache__/PageUtil.cpython-310.pyc | Bin 0 -> 1773 bytes .../__pycache__/ParseRequest.cpython-310.pyc | Bin 0 -> 2346 bytes .../__pycache__/TranslateUtil.cpython-310.pyc | Bin 0 -> 1501 bytes dsSchoolBuddy/.idea/vcs.xml | 1 - dsSchoolBuddy/TestStart.py | 4 +- 44 files changed, 1439 insertions(+), 10 deletions(-) create mode 100644 dsLightRag/ElasticSearch/T1_RebuildMapping.py create mode 100644 dsLightRag/ElasticSearch/T2_Vector.py create mode 100644 dsLightRag/ElasticSearch/T3_InsertData.py create mode 100644 dsLightRag/ElasticSearch/T4_SelectAllData.py create mode 100644 dsLightRag/ElasticSearch/T5_SelectByKeyWord.py create mode 100644 dsLightRag/ElasticSearch/T6_SelectByVector.py create mode 100644 dsLightRag/ElasticSearch/T7_XiangLiangQuery.py create mode 100644 dsLightRag/ElasticSearch/Utils/ElasticsearchCollectionManager.py create mode 100644 dsLightRag/ElasticSearch/Utils/ElasticsearchConnectionPool.py create mode 100644 dsLightRag/ElasticSearch/Utils/EsSearchUtil.py create mode 100644 dsLightRag/ElasticSearch/Utils/VectorDBUtil.py create mode 100644 dsLightRag/ElasticSearch/Utils/__init__.py create mode 100644 dsLightRag/ElasticSearch/Utils/__pycache__/ElasticsearchCollectionManager.cpython-310.pyc create mode 100644 dsLightRag/ElasticSearch/Utils/__pycache__/ElasticsearchConnectionPool.cpython-310.pyc create mode 100644 dsLightRag/ElasticSearch/Utils/__pycache__/EsSearchUtil.cpython-310.pyc create mode 100644 dsLightRag/ElasticSearch/Utils/__pycache__/VectorDBUtil.cpython-310.pyc create mode 100644 dsLightRag/ElasticSearch/Utils/__pycache__/__init__.cpython-310.pyc create mode 100644 dsLightRag/ElasticSearch/__init__.py create mode 100644 dsLightRag/ElasticSearch/__pycache__/__init__.cpython-310.pyc create mode 100644 dsLightRag/Routes/QA.py create mode 100644 dsLightRag/Routes/TeachingModel/api/__pycache__/DmController.cpython-310.pyc create mode 100644 dsLightRag/Routes/TeachingModel/api/__pycache__/DocumentController.cpython-310.pyc create mode 100644 dsLightRag/Routes/TeachingModel/api/__pycache__/LoginController.cpython-310.pyc create mode 100644 dsLightRag/Routes/TeachingModel/api/__pycache__/TeachingModelController.cpython-310.pyc create mode 100644 dsLightRag/Routes/TeachingModel/api/__pycache__/ThemeController.cpython-310.pyc create mode 100644 dsLightRag/Routes/TeachingModel/api/__pycache__/UserController.cpython-310.pyc create mode 100644 dsLightRag/Routes/TeachingModel/auth/__pycache__/dependencies.cpython-310.pyc create mode 100644 dsLightRag/Routes/TeachingModel/tasks/__pycache__/BackgroundTasks.cpython-310.pyc create mode 100644 dsLightRag/Util/__pycache__/CommonUtil.cpython-310.pyc create mode 100644 dsLightRag/Util/__pycache__/CookieUtil.cpython-310.pyc create mode 100644 dsLightRag/Util/__pycache__/Database.cpython-310.pyc create mode 100644 dsLightRag/Util/__pycache__/JwtUtil.cpython-310.pyc create mode 100644 dsLightRag/Util/__pycache__/PageUtil.cpython-310.pyc create mode 100644 dsLightRag/Util/__pycache__/ParseRequest.cpython-310.pyc create mode 100644 dsLightRag/Util/__pycache__/TranslateUtil.cpython-310.pyc diff --git a/dsLightRag/Config/Config.py b/dsLightRag/Config/Config.py index ff0f753b..7825e09d 100644 --- a/dsLightRag/Config/Config.py +++ b/dsLightRag/Config/Config.py @@ -1,3 +1,12 @@ +# Elasticsearch配置 +ES_CONFIG = { + 'hosts': ['https://localhost:9200'], + 'basic_auth': ('elastic', 'jv9h8uwRrRxmDi1dq6u8'), + 'verify_certs': False, + 'index_name': 'ds_db', # 默认索引名称 + 'student_info_index': 'student_info' # 添加student_info索引名称配置 +} + # 阿里云的配置信息【绘智科技】 ALY_AK = 'LTAI5tE4tgpGcKWhbZg6C4bh' ALY_SK = 'oizcTOZ8izbGUouboC00RcmGE8vBQ1' @@ -45,8 +54,8 @@ GLM_MODEL_NAME = "THUDM/GLM-4.1V-9B-Thinking" # 阿里云API信息【YLT】 ALY_LLM_API_KEY = "sk-f6da0c787eff4b0389e4ad03a35a911f" ALY_LLM_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1" -ALY_LLM_MODEL_NAME = "deepseek-r1" -# ALY_LLM_MODEL_NAME = "qwen-plus" +#ALY_LLM_MODEL_NAME = "deepseek-r1" +ALY_LLM_MODEL_NAME = "qwen-plus" # ALY_LLM_MODEL_NAME = "deepseek-v3" # 华为云云存储 diff --git a/dsLightRag/Config/__pycache__/Config.cpython-310.pyc b/dsLightRag/Config/__pycache__/Config.cpython-310.pyc index 892a7740b3a50eb565d9dbd729de4058975388fe..68346443072d3d46785061b5462cfadc8505a8d4 100644 GIT binary patch delta 582 zcmYjPOKTHh6wOR#CduT{y!%Su&$LQg7t$64OJ5emgc%=jHe;GIX-3i{%}mMX&bjw~hwp>?JMl5uYsKSwK*Vk8L;bN}zXoDB zG$9^}biav%fisB`Wd^BGeWxNo4H83;8Agy1DAb`c3XL&@7)O{1L^uMSNko}KjA_J~ zL4sL~Fo#}JG*ZkX%~51HhOC{jvo#qxjw8i!wrXqvCo_71>vpJN51EmC-S-&kvj}Ufl0)ABn%Y7Ecj#y)NyhXF1JUtEl?w zz{7=pT5+AKRq1+l#4hcQQ+sVy?T+XAy3?e6Z8a+irB-o9Drx6kA8+(6shTPmWHGZ=h^Rdw#rJGM^qBqWnmZAz3aN z!}6JVX!xoB!{t5Gd{HtE{Qh_KtAzer?L-@_K}YZZ(Du_W1yy8O`U~w=iS!Gh2RT_$ N6zLy=$^cSW`U9|)oNfRB delta 406 zcmYjNOH0FG5X_gPN%~0ouC`kH(rN{T{s2KyQBN(2BFse!6I<}ml6ujL9z@TQf8fQd zP|#l@f>-aJJ^B?yI>Rh8%PuVYoceZdhPEA3BpyDV22NB_zNTRostnsw;}=<|K%s!3 z3Y8i(>d+ZMga!HP_NHdKLGsybAjGsMMkz*El z=1^cBb5ipxpvXCta3t44_IauE3Bc(I#$`h8kf=H zil?-ilRNP$(y&Iu@nt{o$Iyaa|9UfgRLkK&yHCXF`=hb%Uu*>J$&aQ9u|6>)ZLtHp z+wqPM+T=4(^ diff --git a/dsLightRag/ElasticSearch/T1_RebuildMapping.py b/dsLightRag/ElasticSearch/T1_RebuildMapping.py new file mode 100644 index 00000000..f0eb0d62 --- /dev/null +++ b/dsLightRag/ElasticSearch/T1_RebuildMapping.py @@ -0,0 +1,32 @@ +from Config import Config +from ElasticSearch.Utils.EsSearchUtil import EsSearchUtil + +# 创建EsSearchUtil实例 +search_util = EsSearchUtil(Config.ES_CONFIG) + +def rebuild_index(index_name): + """ + 重建指定的索引 + + 参数: + index_name: 要重建的索引名称 + + 返回: + bool: 操作是否成功 + """ + print(f"开始重建索引: {index_name}") + if search_util.rebuild_mapping(index_name): + print(f"重建索引 '{index_name}' 操作成功") + return True + else: + print(f"重建索引 '{index_name}' 操作失败") + return False + +if __name__ == "__main__": + # 重建ds_db索引 + rebuild_index(Config.ES_CONFIG['index_name']) + + # 重建student_info索引 + rebuild_index(Config.ES_CONFIG['student_info_index']) + + print("所有索引重建操作完成") \ No newline at end of file diff --git a/dsLightRag/ElasticSearch/T2_Vector.py b/dsLightRag/ElasticSearch/T2_Vector.py new file mode 100644 index 00000000..c805b7c3 --- /dev/null +++ b/dsLightRag/ElasticSearch/T2_Vector.py @@ -0,0 +1,38 @@ +# pip install pydantic requests +from ElasticSearch.Utils.VectorDBUtil import VectorDBUtil + + +def main(): + # 模拟长字符串文档内容 + long_text = """混凝土是一种广泛使用的建筑材料,由水泥、砂、石子和水混合而成。它具有高强度、耐久性和良好的可塑性,被广泛应用于建筑、桥梁、道路等土木工程领域。 + +混凝土的历史可以追溯到古罗马时期,当时人们使用火山灰、石灰和碎石混合制成类似混凝土的材料。现代混凝土技术始于19世纪,随着波特兰水泥的发明而得到快速发展。 + +混凝土的性能取决于其配合比,包括水灰比、砂率等参数。水灰比是影响混凝土强度的关键因素,较小的水灰比通常会产生更高强度的混凝土。 + +为了改善混凝土的性能,常常会添加各种外加剂,如减水剂、早强剂、缓凝剂等。此外,还可以使用纤维增强、聚合物改性等技术来提高混凝土的韧性和耐久性。 + +在施工过程中,混凝土需要适当的养护,以确保其强度正常发展。养护措施包括浇水、覆盖保湿、蒸汽养护等。 + +随着建筑技术的发展,高性能混凝土、自密实混凝土、再生骨料混凝土等新型混凝土不断涌现,为土木工程领域提供了更多的选择。""" + + # 创建工具实例 + vector_util = VectorDBUtil() + + # 调用文本入库功能 + vector_util.text_to_vector_db(long_text) + + # 调用文本查询功能 + query = "混凝土" + reranked_results = vector_util.query_vector_db(query, k=4) + + # 打印所有查询结果及其可信度 + print("最终查询结果:") + for i, (result, score) in enumerate(reranked_results): + print(f"结果 {i+1} (可信度: {score:.4f}):") + print(result.page_content) + print("---") + + +if __name__ == "__main__": + main() diff --git a/dsLightRag/ElasticSearch/T3_InsertData.py b/dsLightRag/ElasticSearch/T3_InsertData.py new file mode 100644 index 00000000..a5c0e76d --- /dev/null +++ b/dsLightRag/ElasticSearch/T3_InsertData.py @@ -0,0 +1,28 @@ +from Config import Config +from ElasticSearch.Utils.EsSearchUtil import EsSearchUtil + + +def main(): + # 示例1:插入单个长文本 + long_text = """混凝土是一种广泛使用的建筑材料,由水泥、砂、石子和水混合而成。它具有高强度、耐久性和良好的可塑性,被广泛应用于建筑、桥梁、道路等土木工程领域。 + +混凝土的历史可以追溯到古罗马时期,当时人们使用火山灰、石灰和碎石混合制成类似混凝土的材料。现代混凝土技术始于19世纪,随着波特兰水泥的发明而得到快速发展。 + +混凝土的性能取决于其配合比,包括水灰比、砂率等参数。水灰比是影响混凝土强度的关键因素,较小的水灰比通常会产生更高强度的混凝土。 + +为了改善混凝土的性能,常常会添加各种外加剂,如减水剂、早强剂、缓凝剂等。此外,还可以使用纤维增强、聚合物改性等技术来提高混凝土的韧性和耐久性。 + +在施工过程中,混凝土需要适当的养护,以确保其强度正常发展。养护措施包括浇水、覆盖保湿、蒸汽养护等。 + +随着建筑技术的发展,高性能混凝土、自密实混凝土、再生骨料混凝土等新型混凝土不断涌现,为土木工程领域提供了更多的选择。""" + + # 打标签 + tags = ["student_110"] + + # 创建EsSearchUtil实例 + search_util = EsSearchUtil(Config.ES_CONFIG) + search_util.insert_long_text_to_es(long_text, tags) + + +if __name__ == "__main__": + main() diff --git a/dsLightRag/ElasticSearch/T4_SelectAllData.py b/dsLightRag/ElasticSearch/T4_SelectAllData.py new file mode 100644 index 00000000..80562113 --- /dev/null +++ b/dsLightRag/ElasticSearch/T4_SelectAllData.py @@ -0,0 +1,28 @@ +from Config import Config +from ElasticSearch.Utils.EsSearchUtil import EsSearchUtil + +# 创建EsSearchUtil实例 +search_util = EsSearchUtil(Config.ES_CONFIG) + +# 查询所有数据 +def select_all_data(index_name): + try: + # 调用EsSearchUtil中的select_all_data方法 + response = search_util.select_all_data() + hits = response['hits']['hits'] + + if not hits: + print(f"索引 {index_name} 中没有数据") + else: + print(f"索引 {index_name} 中共有 {len(hits)} 条数据:") + for i, hit in enumerate(hits, 1): + print(f"{i}. ID: {hit['_id']}") + print(f" 内容: {hit['_source'].get('user_input', '')}") + print(f" 标签: {hit['_source'].get('tags', '')}") + print(f" 时间戳: {hit['_source'].get('timestamp', '')}") + print("-" * 50) + except Exception as e: + print(f"查询出错: {e}") + +if __name__ == "__main__": + select_all_data(Config.ES_CONFIG['index_name']) \ No newline at end of file diff --git a/dsLightRag/ElasticSearch/T5_SelectByKeyWord.py b/dsLightRag/ElasticSearch/T5_SelectByKeyWord.py new file mode 100644 index 00000000..af353c63 --- /dev/null +++ b/dsLightRag/ElasticSearch/T5_SelectByKeyWord.py @@ -0,0 +1,28 @@ +from Config import Config +from ElasticSearch.Utils.EsSearchUtil import EsSearchUtil + + +# 1. 创建EsSearchUtil实例(已封装连接池) +search_util = EsSearchUtil(Config.ES_CONFIG) + +# 2. 直接在代码中指定要查询的关键字 +query_keyword = "混凝土" + +# 3. 执行查询并处理结果 +try: + # 使用连接池进行查询 + results = search_util.text_search(query_keyword, size=1000) + print(f"查询关键字 '{query_keyword}' 结果:") + if results['hits']['hits']: + for i, hit in enumerate(results['hits']['hits'], 1): + doc = hit['_source'] + print(f"{i}. ID: {hit['_id']}") + print(f" 标签: {doc['tags']['tags'] if 'tags' in doc and 'tags' in doc['tags'] else '无'}") + print(f" 用户问题: {doc['user_input'] if 'user_input' in doc else '无'}") + print(f" 时间: {doc['timestamp'] if 'timestamp' in doc else '无'}") + print("-" * 50) + else: + print(f"未找到包含 '{query_keyword}' 的数据。") +except Exception as e: + print(f"查询失败: {e}") + print(f"查询关键字: {query_keyword}") \ No newline at end of file diff --git a/dsLightRag/ElasticSearch/T6_SelectByVector.py b/dsLightRag/ElasticSearch/T6_SelectByVector.py new file mode 100644 index 00000000..9a047397 --- /dev/null +++ b/dsLightRag/ElasticSearch/T6_SelectByVector.py @@ -0,0 +1,29 @@ +from Config import Config +from ElasticSearch.Utils.EsSearchUtil import EsSearchUtil + + +def main(): + # 初始化搜索工具 + search_util = EsSearchUtil(Config.ES_CONFIG) + + # 输入查询文本 + query = "混凝土" + print(f"查询文本: {query}") + + # 获取查询向量 + query_embedding = search_util.get_query_embedding(query) + print(f"查询向量维度: {len(query_embedding)}") + + # 向量搜索 + search_results = search_util.search_by_vector(query_embedding, k=10) + print(f"向量搜索结果数量: {len(search_results)}") + + # 结果重排 + reranked_results = search_util.rerank_results(query, search_results) + + # 显示结果 + search_util.display_results(reranked_results) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dsLightRag/ElasticSearch/T7_XiangLiangQuery.py b/dsLightRag/ElasticSearch/T7_XiangLiangQuery.py new file mode 100644 index 00000000..f91115bb --- /dev/null +++ b/dsLightRag/ElasticSearch/T7_XiangLiangQuery.py @@ -0,0 +1,66 @@ +import logging + +from Config.Config import ES_CONFIG +from ElasticSearch.Utils.EsSearchUtil import EsSearchUtil + +# 初始化日志 +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + + +if __name__ == "__main__": + # 初始化EsSearchUtil + search_util = EsSearchUtil(ES_CONFIG) + + # 获取用户输入 + user_query = input("请输入查询语句(例如:高性能的混凝土): ") + if not user_query: + user_query = "高性能的混凝土" + print(f"未输入查询语句,使用默认值: {user_query}") + + query_tags = [] # 可以根据需要添加标签过滤 + + print(f"\n=== 开始执行查询 ===") + print(f"原始查询文本: {user_query}") + + try: + # 1. 向量搜索 + print("\n=== 向量搜索阶段 ===") + print("1. 文本向量化处理中...") + query_embedding = search_util.get_query_embedding(user_query) + print(f"2. 生成的查询向量维度: {len(query_embedding)}") + print(f"3. 前3维向量值: {query_embedding[:3]}") + + print("4. 正在执行Elasticsearch向量搜索...") + vector_hits = search_util.search_by_vector(query_embedding, k=5) + print(f"5. 向量搜索结果数量: {len(vector_hits)}") + + # 向量结果重排 + print("6. 正在进行向量结果重排...") + reranked_vector_results = search_util.rerank_results(user_query, vector_hits) + print(f"7. 重排后向量结果数量: {len(reranked_vector_results)}") + + # 2. 关键字搜索 + print("\n=== 关键字搜索阶段 ===") + print("1. 正在执行Elasticsearch关键字搜索...") + keyword_results = search_util.text_search(user_query, size=5) + keyword_hits = keyword_results['hits']['hits'] + print(f"2. 关键字搜索结果数量: {len(keyword_hits)}") + + # 3. 合并结果 + print("\n=== 合并搜索结果 ===") + # 为关键字结果添加分数 + keyword_results_with_scores = [(doc, doc['_score']) for doc in keyword_hits] + merged_results = search_util.merge_results(keyword_results_with_scores, reranked_vector_results) + print(f"合并后唯一结果数量: {len(merged_results)}") + + # 4. 打印最终结果 + print("\n=== 最终搜索结果 ===") + for i, (doc, score, source) in enumerate(merged_results, 1): + print(f"{i}. 文档ID: {doc['_id']}, 分数: {score:.2f}, 来源: {source}") + print(f" 内容: {doc['_source']['user_input']}") + print(" --- ") + + except Exception as e: + logger.error(f"搜索过程中发生错误: {str(e)}") + print(f"搜索失败: {str(e)}") diff --git a/dsLightRag/ElasticSearch/Utils/ElasticsearchCollectionManager.py b/dsLightRag/ElasticSearch/Utils/ElasticsearchCollectionManager.py new file mode 100644 index 00000000..a2e8cf29 --- /dev/null +++ b/dsLightRag/ElasticSearch/Utils/ElasticsearchCollectionManager.py @@ -0,0 +1,110 @@ +from elasticsearch.exceptions import NotFoundError +import logging + +logger = logging.getLogger(__name__) + + +class ElasticsearchCollectionManager: + def __init__(self, index_name): + """ + 初始化Elasticsearch索引管理器 + :param index_name: Elasticsearch索引名称 + """ + self.index_name = index_name + + def load_collection(self, es_connection): + """ + 加载索引,如果不存在则创建 + :param es_connection: Elasticsearch连接 + """ + try: + if not es_connection.indices.exists(index=self.index_name): + logger.warning(f"Index {self.index_name} does not exist, creating new index") + self._create_index(es_connection) + except Exception as e: + logger.error(f"Failed to load collection: {str(e)}") + raise + + def _create_index(self, es_connection): + """创建新的Elasticsearch索引""" + mapping = { + "mappings": { + "properties": { + "user_input": {"type": "text"}, + "tags": { + "type": "object", + "properties": { + "tags": {"type": "keyword"}, + "full_content": {"type": "text"} + } + }, + "timestamp": {"type": "date"}, + "embedding": {"type": "dense_vector", "dims": 200} + } + } + } + es_connection.indices.create(index=self.index_name, body=mapping) + + def search(self, es_connection, query_embedding, search_params, expr=None, limit=5): + """ + 执行混合搜索(向量+关键字) + :param es_connection: Elasticsearch连接 + :param query_embedding: 查询向量 + :param search_params: 搜索参数 + :param expr: 过滤表达式 + :param limit: 返回结果数量 + :return: 搜索结果 + """ + try: + # 构建查询 + query = { + "query": { + "script_score": { + "query": { + "bool": { + "must": [] + } + }, + "script": { + "source": "cosineSimilarity(params.query_vector, 'embedding') + 1.0", + "params": {"query_vector": query_embedding} + } + } + }, + "size": limit + } + + # 添加标签过滤条件 + if expr: + query["query"]["script_score"]["query"]["bool"]["must"].append( + {"nested": { + "path": "tags", + "query": { + "terms": {"tags.tags": expr.split(" OR ")} + } + }} + ) + + logger.info(f"Executing search with query: {query}") + response = es_connection.search(index=self.index_name, body=query) + return response["hits"]["hits"] + except Exception as e: + logger.error(f"Search failed: {str(e)}") + raise + + def query_by_id(self, es_connection, doc_id): + """ + 根据ID查询文档 + :param es_connection: Elasticsearch连接 + :param doc_id: 文档ID + :return: 文档内容 + """ + try: + response = es_connection.get(index=self.index_name, id=doc_id) + return response["_source"] + except NotFoundError: + logger.warning(f"Document with id {doc_id} not found") + return None + except Exception as e: + logger.error(f"Failed to query document by id: {str(e)}") + raise \ No newline at end of file diff --git a/dsLightRag/ElasticSearch/Utils/ElasticsearchConnectionPool.py b/dsLightRag/ElasticSearch/Utils/ElasticsearchConnectionPool.py new file mode 100644 index 00000000..88a877fc --- /dev/null +++ b/dsLightRag/ElasticSearch/Utils/ElasticsearchConnectionPool.py @@ -0,0 +1,65 @@ +from elasticsearch import Elasticsearch +import threading +import logging + +logger = logging.getLogger(__name__) + + +class ElasticsearchConnectionPool: + def __init__(self, hosts, basic_auth, verify_certs=False, max_connections=50): + """ + 初始化Elasticsearch连接池 + :param hosts: Elasticsearch服务器地址 + :param basic_auth: 认证信息(username, password) + :param verify_certs: 是否验证SSL证书 + :param max_connections: 最大连接数 + """ + self.hosts = hosts + self.basic_auth = basic_auth + self.verify_certs = verify_certs + self.max_connections = max_connections + self._connections = [] + self._lock = threading.Lock() + self._initialize_pool() + + def _initialize_pool(self): + """初始化连接池""" + for _ in range(self.max_connections): + self._connections.append(self._create_connection()) + + def _create_connection(self): + """创建新的Elasticsearch连接""" + return Elasticsearch( + hosts=self.hosts, + basic_auth=self.basic_auth, + verify_certs=self.verify_certs + ) + + def get_connection(self): + """从连接池获取一个连接""" + with self._lock: + if not self._connections: + logger.warning("Connection pool exhausted, creating new connection") + return self._create_connection() + return self._connections.pop() + + def release_connection(self, connection): + """释放连接回连接池""" + with self._lock: + if len(self._connections) < self.max_connections: + self._connections.append(connection) + else: + try: + connection.close() + except Exception as e: + logger.warning(f"Failed to close connection: {str(e)}") + + def close(self): + """关闭所有连接""" + with self._lock: + for conn in self._connections: + try: + conn.close() + except Exception as e: + logger.warning(f"Failed to close connection: {str(e)}") + self._connections.clear() \ No newline at end of file diff --git a/dsLightRag/ElasticSearch/Utils/EsSearchUtil.py b/dsLightRag/ElasticSearch/Utils/EsSearchUtil.py new file mode 100644 index 00000000..17aa2bec --- /dev/null +++ b/dsLightRag/ElasticSearch/Utils/EsSearchUtil.py @@ -0,0 +1,664 @@ +import json +import logging +import warnings +import hashlib +import time + +import jieba +import requests + +from ElasticSearch.Utils.ElasticsearchConnectionPool import ElasticsearchConnectionPool +from langchain_core.documents import Document +from langchain_text_splitters import RecursiveCharacterTextSplitter +from langchain_openai import OpenAIEmbeddings +from pydantic import SecretStr +from Config import Config +from typing import List, Tuple, Dict +# 初始化日志 +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + + +class EsSearchUtil: + # 存储对话历史的字典,键为会话ID,值为对话历史列表 + conversation_history = {} + + # 存储学生信息的字典,键为用户ID,值为学生信息 + student_info = {} + + # 年级关键词词典 + GRADE_KEYWORDS = { + '一年级': ['一年级', '初一'], + '二年级': ['二年级', '初二'], + '三年级': ['三年级', '初三'], + '四年级': ['四年级'], + '五年级': ['五年级'], + '六年级': ['六年级'], + '七年级': ['七年级', '初一'], + '八年级': ['八年级', '初二'], + '九年级': ['九年级', '初三'], + '高一': ['高一'], + '高二': ['高二'], + '高三': ['高三'] + } + + # 最大对话历史轮数 + MAX_HISTORY_ROUNDS = 10 + + # 初始化停用词表 + STOPWORDS = set( + ['的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '一个', '上', '也', '很', '到', '说', + '要', '去', '你', '会', '着', '没有', '看', '好', '自己', '这']) + + def __init__(self, es_config): + """ + 初始化Elasticsearch搜索工具 + :param es_config: Elasticsearch配置字典,包含hosts, username, password, index_name等 + """ + # 抑制HTTPS相关警告 + warnings.filterwarnings('ignore', message='Connecting to .* using TLS with verify_certs=False is insecure') + warnings.filterwarnings('ignore', message='Unverified HTTPS request is being made to host') + + self.es_config = es_config + + # 初始化连接池 + self.es_pool = ElasticsearchConnectionPool( + hosts=es_config['hosts'], + basic_auth=es_config['basic_auth'], + verify_certs=es_config.get('verify_certs', False), + max_connections=50 + ) + + self.index_name = es_config['index_name'] + logger.info(f"EsSearchUtil初始化成功,索引名称: {self.index_name}") + + def rebuild_mapping(self, index_name=None): + """ + 重建Elasticsearch索引和mapping结构 + + 参数: + index_name: 可选,指定要重建的索引名称,默认使用初始化时的索引名称 + + 返回: + bool: 操作是否成功 + """ + try: + # 从连接池获取连接 + conn = self.es_pool.get_connection() + + # 使用指定的索引名称或默认索引名称 + target_index = index_name if index_name else self.index_name + logger.info(f"开始重建索引: {target_index}") + + # 定义mapping结构 + if target_index == 'student_info': + mapping = { + "mappings": { + "properties": { + "user_id": {"type": "keyword"}, + "grade": {"type": "keyword"}, + "recent_questions": {"type": "text"}, + "learned_knowledge": {"type": "text"}, + "updated_at": {"type": "date"} + } + } + } + else: + mapping = { + "mappings": { + "properties": { + "embedding": { + "type": "dense_vector", + "dims": Config.EMBED_DIM, + "index": True, + "similarity": "l2_norm" + }, + "user_input": {"type": "text"}, + "tags": { + "type": "object", + "properties": { + "tags": {"type": "keyword"}, + "full_content": {"type": "text"} + } + } + } + } + } + + # 检查索引是否存在,存在则删除 + if conn.indices.exists(index=target_index): + conn.indices.delete(index=target_index) + logger.info(f"删除已存在的索引 '{target_index}'") + print(f"删除已存在的索引 '{target_index}'") + + # 创建索引和mapping + conn.indices.create(index=target_index, body=mapping) + logger.info(f"索引 '{target_index}' 创建成功,mapping结构已设置") + print(f"索引 '{target_index}' 创建成功,mapping结构已设置。") + + return True + except Exception as e: + logger.error(f"重建索引 '{target_index}' 失败: {str(e)}") + print(f"重建索引 '{target_index}' 失败: {e}") + + # 提供认证错误的具体提示 + if 'AuthenticationException' in str(e): + print("认证失败提示: 请检查Config.py中的ES_CONFIG配置,确保用户名和密码正确。") + logger.error("认证失败: 请检查Config.py中的ES_CONFIG配置,确保用户名和密码正确。") + + return False + finally: + # 释放连接回连接池 + self.es_pool.release_connection(conn) + + def text_search(self, query, size=10): + # 从连接池获取连接 + conn = self.es_pool.get_connection() + try: + # 使用连接执行搜索 + result = conn.search( + index=self.es_config['index_name'], + query={"match": {"user_input": query}}, + size=size + ) + return result + except Exception as e: + logger.error(f"文本搜索失败: {str(e)}") + raise + finally: + # 释放连接回连接池 + self.es_pool.release_connection(conn) + + def select_all_data(self, size=1000): + """ + 查询索引中的所有数据 + + 参数: + size: 返回的最大结果数量,默认1000 + + 返回: + dict: 查询结果 + """ + # 从连接池获取连接 + conn = self.es_pool.get_connection() + try: + # 构建查询条件 - 匹配所有文档 + query = { + "query": { + "match_all": {} + }, + "size": size + } + + # 执行查询 + response = conn.search(index=self.es_config['index_name'], body=query) + return response + except Exception as e: + logger.error(f"查询所有数据失败: {str(e)}") + raise + finally: + # 释放连接回连接池 + self.es_pool.release_connection(conn) + + def split_text_into_chunks(self,text: str, chunk_size: int = 200, chunk_overlap: int = 0) -> list: + """ + 将文本切割成块 + + 参数: + text: 要切割的文本 + chunk_size: 每个块的大小 + chunk_overlap: 块之间的重叠大小 + + 返回: + list: 文本块列表 + """ + # 创建文档对象 + docs = [Document(page_content=text, metadata={"source": "simulated_document"})] + + # 切割文档 + text_splitter = RecursiveCharacterTextSplitter( + chunk_size=chunk_size, chunk_overlap=chunk_overlap, add_start_index=True + ) + all_splits = text_splitter.split_documents(docs) + print(f"切割后的文档块数量:{len(all_splits)}") + + return [split.page_content for split in all_splits] + + def insert_long_text_to_es(self,long_text: str, tags: list = None) -> bool: + """ + 将长文本切割后向量化并插入到Elasticsearch,基于文本内容哈希实现去重 + + 参数: + long_text: 要插入的长文本 + tags: 可选的标签列表 + + 返回: + bool: 插入是否成功 + """ + try: + # 1. 创建EsSearchUtil实例以使用连接池 + search_util = EsSearchUtil(Config.ES_CONFIG) + + # 2. 从连接池获取连接 + conn = search_util.es_pool.get_connection() + + # # 3. 检查索引是否存在,不存在则创建 + index_name = Config.ES_CONFIG['index_name'] + # if not conn.indices.exists(index=index_name): + # # 定义mapping结构 + # mapping = { + # "mappings": { + # "properties": { + # "embedding": { + # "type": "dense_vector", + # "dims": Config.EMBED_DIM, # 根据实际embedding维度调整 + # "index": True, + # "similarity": "l2_norm" + # }, + # "user_input": {"type": "text"}, + # "tags": { + # "type": "object", + # "properties": { + # "tags": {"type": "keyword"}, + # "full_content": {"type": "text"} + # } + # }, + # "timestamp": {"type": "date"} + # } + # } + # } + # conn.indices.create(index=index_name, body=mapping) + # print(f"索引 '{index_name}' 创建成功") + + # 4. 切割文本 + text_chunks = self.split_text_into_chunks(long_text) + + # 5. 准备标签 + if tags is None: + tags = ["general_text"] + + # 6. 获取当前时间 + timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + + # 7. 创建嵌入模型 + embeddings = OpenAIEmbeddings( + model=Config.EMBED_MODEL_NAME, + base_url=Config.EMBED_BASE_URL, + api_key=SecretStr(Config.EMBED_API_KEY) + ) + + # 8. 为每个文本块生成向量并插入 + for i, chunk in enumerate(text_chunks): + # 生成文本块的哈希值作为文档ID + doc_id = hashlib.md5(chunk.encode('utf-8')).hexdigest() + + # 检查文档是否已存在 + if conn.exists(index=index_name, id=doc_id): + print(f"文档块 {i+1} 已存在,跳过插入: {doc_id}") + continue + + # 生成文本块的嵌入向量 + embedding = embeddings.embed_documents([chunk])[0] + + # 准备文档数据 + doc = { + 'tags': {"tags": tags, "full_content": long_text}, + 'user_input': chunk, + 'timestamp': timestamp, + 'embedding': embedding + } + + # 插入数据到Elasticsearch + conn.index(index=index_name, id=doc_id, document=doc) + print(f"文档块 {i+1} 插入成功: {doc_id}") + + return True + except Exception as e: + print(f"插入数据失败: {e}") + return False + finally: + # 确保释放连接回连接池 + if 'conn' in locals() and 'search_util' in locals(): + search_util.es_pool.release_connection(conn) + + def get_query_embedding(self, query: str) -> list: + """ + 将查询文本转换为向量 + + 参数: + query: 查询文本 + + 返回: + list: 向量表示 + """ + # 创建嵌入模型 + embeddings = OpenAIEmbeddings( + model=Config.EMBED_MODEL_NAME, + base_url=Config.EMBED_BASE_URL, + api_key=SecretStr(Config.EMBED_API_KEY) + ) + + # 生成查询向量 + query_embedding = embeddings.embed_query(query) + return query_embedding + + def rerank_results(self, query: str, results: list) -> list: + """ + 使用重排模型对搜索结果进行重排 + + 参数: + query: 查询文本 + results: 搜索结果列表 + + 返回: + list: 重排后的结果列表,每个元素是(文档对象, 分数)的元组 + """ + if not results: + print("警告: 没有搜索结果可供重排") + return [] + + try: + # 准备重排请求数据 + # 确保doc是字典并包含'_source'和'user_input'字段 + documents = [] + valid_results = [] + for i, doc in enumerate(results): + if isinstance(doc, dict) and '_source' in doc and 'user_input' in doc['_source']: + documents.append(doc['_source']['user_input']) + valid_results.append(doc) + else: + print(f"警告: 结果项 {i} 格式不正确,跳过该结果") + print(f"结果项内容: {doc}") + + if not documents: + print("警告: 没有有效的文档可供重排") + # 返回原始结果,但转换为(结果, 分数)的元组格式 + return [(doc, doc.get('_score', 0.0)) for doc in results] + + rerank_data = { + "model": Config.RERANK_MODEL, + "query": query, + "documents": documents, + "top_n": len(documents) + } + + # 调用重排API + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {Config.RERANK_BINDING_API_KEY}" + } + + response = requests.post(Config.RERANK_BASE_URL, headers=headers, data=json.dumps(rerank_data)) + response.raise_for_status() # 检查请求是否成功 + rerank_result = response.json() + + # 处理重排结果 + reranked_results = [] + if "results" in rerank_result: + for item in rerank_result["results"]: + doc_idx = item.get("index") + score = item.get("relevance_score", 0.0) + if 0 <= doc_idx < len(valid_results): + result = valid_results[doc_idx] + reranked_results.append((result, score)) + else: + print("警告: 无法识别重排API响应格式") + # 返回原始结果,但转换为(结果, 分数)的元组格式 + reranked_results = [(doc, doc.get('_score', 0.0)) for doc in valid_results] + + print(f"重排后结果数量:{len(reranked_results)}") + return reranked_results + + except Exception as e: + print(f"重排失败: {e}") + print("将使用原始搜索结果") + # 返回原始结果,但转换为(结果, 分数)的元组格式 + return [(doc, doc.get('_score', 0.0)) for doc in results] + + def search_by_vector(self, query_embedding: list, k: int = 10) -> list: + """ + 根据向量进行相似性搜索 + + 参数: + query_embedding: 查询向量 + k: 返回的结果数量 + + 返回: + list: 搜索结果列表 + """ + try: + # 从连接池获取连接 + conn = self.es_pool.get_connection() + index_name = Config.ES_CONFIG['index_name'] + + # 执行向量搜索 + response = conn.search( + index=index_name, + body={ + "query": { + "script_score": { + "query": {"match_all": {}}, + "script": { + "source": "cosineSimilarity(params.query_vector, 'embedding') + 1.0", + "params": { + "query_vector": query_embedding + } + } + } + }, + "size": k + } + ) + + # 提取结果 + # 确保我们提取的是 hits.hits 部分 + if 'hits' in response and 'hits' in response['hits']: + results = response['hits']['hits'] + print(f"向量搜索结果数量: {len(results)}") + return results + else: + print("警告: 向量搜索响应格式不正确") + print(f"响应内容: {response}") + return [] + + except Exception as e: + print(f"向量搜索失败: {e}") + return [] + finally: + # 释放连接回连接池 + self.es_pool.release_connection(conn) + + def display_results(self, results: list, show_score: bool = True) -> None: + """ + 展示搜索结果 + + 参数: + results: 搜索结果列表 + show_score: 是否显示分数 + """ + if not results: + print("没有找到匹配的结果。") + return + + print(f"找到 {len(results)} 条结果:\n") + for i, item in enumerate(results, 1): + print(f"结果 {i}:") + try: + # 检查item是否为元组格式 (result, score) + if isinstance(item, tuple): + if len(item) >= 2: + result, score = item[0], item[1] + else: + result, score = item[0], 0.0 + else: + # 如果不是元组,假设item就是result + result = item + score = result.get('_score', 0.0) + + # 确保result是字典类型 + if not isinstance(result, dict): + print(f"警告: 结果项 {i} 不是字典类型,跳过显示") + print(f"结果项内容: {result}") + print("---") + continue + + # 尝试获取user_input内容 + if '_source' in result and 'user_input' in result['_source']: + content = result['_source']['user_input'] + print(f"内容: {content}") + elif 'user_input' in result: + content = result['user_input'] + print(f"内容: {content}") + else: + print(f"警告: 结果项 {i} 缺少'user_input'字段") + print(f"结果项内容: {result}") + print("---") + continue + + # 显示分数 + if show_score: + print(f"分数: {score:.4f}") + + # 如果有标签信息,也显示出来 + if '_source' in result and 'tags' in result['_source']: + tags = result['_source']['tags'] + if isinstance(tags, dict) and 'tags' in tags: + print(f"标签: {tags['tags']}") + + except Exception as e: + print(f"处理结果项 {i} 时出错: {str(e)}") + print(f"结果项内容: {item}") + print("---") + + def merge_results(self, keyword_results: List[Tuple[Dict, float]], vector_results: List[Tuple[Dict, float]]) -> List[Tuple[Dict, float, str]]: + """ + 合并关键字搜索和向量搜索结果 + + 参数: + keyword_results: 关键字搜索结果列表,每个元素是(文档, 分数)元组 + vector_results: 向量搜索结果列表,每个元素是(文档, 分数)元组 + + 返回: + list: 合并后的结果列表,每个元素是(文档, 分数, 来源)元组 + """ + # 标记结果来源并合并 + all_results = [] + for doc, score in keyword_results: + all_results.append((doc, score, "关键字搜索")) + for doc, score in vector_results: + all_results.append((doc, score, "向量搜索")) + + # 去重并按分数排序 + unique_results = {} + for doc, score, source in all_results: + doc_id = doc['_id'] + if doc_id not in unique_results or score > unique_results[doc_id][1]: + unique_results[doc_id] = (doc, score, source) + + # 按分数降序排序 + sorted_results = sorted(unique_results.values(), key=lambda x: x[1], reverse=True) + return sorted_results + + # 添加函数:保存学生信息到ES + def save_student_info_to_es(self,user_id, info): + """将学生信息保存到Elasticsearch""" + try: + # 使用用户ID作为文档ID + doc_id = f"student_{user_id}" + # 准备文档内容 + doc = { + "user_id": user_id, + "info": info, + "update_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + } + # 从连接池获取连接 + es_conn = self.es_pool.get_connection() + try: + # 确保索引存在,如果不存在则创建 + es_conn.index(index="student_info", id=doc_id, document=doc) + logger.info(f"学生 {user_id} 的信息已保存到ES: {info}") + finally: + # 释放连接回连接池 + self.es_pool.release_connection(es_conn) + except Exception as e: + logger.error(f"保存学生信息到ES失败: {str(e)}", exc_info=True) + + # 添加函数:从ES获取学生信息 + def get_student_info_from_es(self,user_id): + """从Elasticsearch获取学生信息""" + try: + doc_id = f"student_{user_id}" + # 从连接池获取连接 + es_conn = self.es_pool.get_connection() + try: + # 确保索引存在 + if es_conn.indices.exists(index=Config.ES_CONFIG.get("student_info_index")): + result = es_conn.get(index=Config.ES_CONFIG.get("student_info_index"), id=doc_id) + if result and '_source' in result: + logger.info(f"从ES获取到学生 {user_id} 的信息: {result['_source']['info']}") + return result['_source']['info'] + else: + logger.info(f"ES中没有找到学生 {user_id} 的信息") + else: + logger.info("student_info索引不存在") + finally: + # 释放连接回连接池 + self.es_pool.release_connection(es_conn) + except Exception as e: + # 如果文档不存在,返回空字典 + if "not_found" in str(e).lower(): + logger.info(f"学生 {user_id} 的信息在ES中不存在") + return {} + logger.error(f"从ES获取学生信息失败: {str(e)}", exc_info=True) + return {} + + def extract_student_info(self,text, user_id): + """使用jieba分词提取学生信息""" + try: + # 提取年级信息 + seg_list = jieba.cut(text, cut_all=False) # 精确模式 + seg_set = set(seg_list) + + # 检查是否已有学生信息,如果没有则从ES加载 + if user_id not in self.student_info: + # 从ES加载学生信息 + info_from_es = self.get_student_info_from_es(user_id) + if info_from_es: + self.student_info[user_id] = info_from_es + logger.info(f"从ES加载用户 {user_id} 的信息: {info_from_es}") + else: + self.student_info[user_id] = {} + + # 提取并更新年级信息 + grade_found = False + for grade, keywords in self.GRADE_KEYWORDS.items(): + for keyword in keywords: + if keyword in seg_set: + if 'grade' not in self.student_info[user_id] or self.student_info[user_id]['grade'] != grade: + self.student_info[user_id]['grade'] = grade + logger.info(f"提取到用户 {user_id} 的年级信息: {grade}") + # 保存到ES + self.save_student_info_to_es(user_id, self.student_info[user_id]) + grade_found = True + break + if grade_found: + break + + # 如果文本中明确提到年级,但没有匹配到关键词,尝试直接提取数字 + if not grade_found: + import re + # 匹配"我是X年级"格式 + match = re.search(r'我是(\d+)年级', text) + if match: + grade_num = match.group(1) + grade = f"{grade_num}年级" + if 'grade' not in self.student_info[user_id] or self.student_info[user_id]['grade'] != grade: + self.student_info[user_id]['grade'] = grade + logger.info(f"通过正则提取到用户 {user_id} 的年级信息: {grade}") + # 保存到ES + self.save_student_info_to_es(user_id, self.student_info[user_id]) + except Exception as e: + logger.error(f"提取学生信息失败: {str(e)}", exc_info=True) + + + diff --git a/dsLightRag/ElasticSearch/Utils/VectorDBUtil.py b/dsLightRag/ElasticSearch/Utils/VectorDBUtil.py new file mode 100644 index 00000000..2cb6ccd9 --- /dev/null +++ b/dsLightRag/ElasticSearch/Utils/VectorDBUtil.py @@ -0,0 +1,125 @@ +# pip install pydantic requests +from langchain_core.documents import Document +from langchain_core.vectorstores import InMemoryVectorStore +from langchain_openai import OpenAIEmbeddings +from langchain_text_splitters import RecursiveCharacterTextSplitter +from pydantic import SecretStr +import requests +import json +from Config.Config import ( + EMBED_MODEL_NAME, EMBED_BASE_URL, EMBED_API_KEY, + RERANK_MODEL, RERANK_BASE_URL, RERANK_BINDING_API_KEY +) + + +class VectorDBUtil: + """向量数据库工具类,提供文本向量化存储和查询功能""" + + def __init__(self): + """初始化向量数据库工具""" + # 初始化嵌入模型 + self.embeddings = OpenAIEmbeddings( + model=EMBED_MODEL_NAME, + base_url=EMBED_BASE_URL, + api_key=SecretStr(EMBED_API_KEY) # 包装成 SecretStr 类型 + ) + # 初始化向量存储 + self.vector_store = None + + def text_to_vector_db(self, text: str, chunk_size: int = 200, chunk_overlap: int = 0) -> tuple: + """ + 将文本存入向量数据库 + + 参数: + text: 要入库的文本 + chunk_size: 文本分割块大小 + chunk_overlap: 文本块重叠大小 + + 返回: + tuple: (向量存储对象, 文档数量, 分割后的文档块数量) + """ + # 创建文档对象 + docs = [Document(page_content=text, metadata={"source": "simulated_document"})] + doc_count = len(docs) + print(f"文档数量:{doc_count} 个") + + # 切割文档 + text_splitter = RecursiveCharacterTextSplitter( + chunk_size=chunk_size, chunk_overlap=chunk_overlap, add_start_index=True + ) + all_splits = text_splitter.split_documents(docs) + split_count = len(all_splits) + print(f"切割后的文档块数量:{split_count}") + + # 向量存储 + self.vector_store = InMemoryVectorStore(self.embeddings) + ids = self.vector_store.add_documents(documents=all_splits) + + return self.vector_store, doc_count, split_count + + def query_vector_db(self, query: str, k: int = 4) -> list: + """ + 从向量数据库查询文本 + + 参数: + query: 查询字符串 + k: 要返回的结果数量 + + 返回: + list: 重排后的结果列表,每个元素是(文档对象, 可信度分数)的元组 + """ + if not self.vector_store: + print("错误: 向量数据库未初始化,请先调用text_to_vector_db方法") + return [] + + # 向量查询 - 获取更多结果用于重排 + results = self.vector_store.similarity_search(query, k=k) + print(f"向量搜索结果数量:{len(results)}") + + # 存储重排后的文档和分数 + reranked_docs_with_scores = [] + + # 调用重排模型 + if len(results) > 1: + # 准备重排请求数据 + rerank_data = { + "model": RERANK_MODEL, + "query": query, + "documents": [doc.page_content for doc in results], + "top_n": len(results) + } + + # 调用SiliconFlow API进行重排 + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {RERANK_BINDING_API_KEY}" + } + + try: + response = requests.post(RERANK_BASE_URL, headers=headers, data=json.dumps(rerank_data)) + response.raise_for_status() # 检查请求是否成功 + rerank_result = response.json() + + # 处理重排结果,提取relevance_score + if "results" in rerank_result: + for item in rerank_result["results"]: + doc_idx = item.get("index") + score = item.get("relevance_score", 0.0) + if 0 <= doc_idx < len(results): + reranked_docs_with_scores.append((results[doc_idx], score)) + else: + print("警告: 无法识别重排API响应格式") + reranked_docs_with_scores = [(doc, 0.0) for doc in results] + + print(f"重排后结果数量:{len(reranked_docs_with_scores)}") + except Exception as e: + print(f"重排模型调用失败: {e}") + print("将使用原始搜索结果") + reranked_docs_with_scores = [(doc, 0.0) for doc in results] + else: + # 只有一个结果,无需重排 + reranked_docs_with_scores = [(doc, 1.0) for doc in results] # 单个结果可信度设为1.0 + + return reranked_docs_with_scores + + diff --git a/dsLightRag/ElasticSearch/Utils/__init__.py b/dsLightRag/ElasticSearch/Utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dsLightRag/ElasticSearch/Utils/__pycache__/ElasticsearchCollectionManager.cpython-310.pyc b/dsLightRag/ElasticSearch/Utils/__pycache__/ElasticsearchCollectionManager.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..059711511b9f6f86065ee1d816861205a065ccb9 GIT binary patch literal 3488 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 literal 0 HcmV?d00001 diff --git a/dsLightRag/ElasticSearch/Utils/__pycache__/VectorDBUtil.cpython-310.pyc b/dsLightRag/ElasticSearch/Utils/__pycache__/VectorDBUtil.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..33f9531bb716529a650554ebca6d1cfbf90a3bae GIT binary patch literal 4136 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 Y3My}L*yQG?l;)(`fs8I@0un3?0N)5E#sB~S literal 0 HcmV?d00001 diff --git a/dsLightRag/ElasticSearch/__init__.py b/dsLightRag/ElasticSearch/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dsLightRag/ElasticSearch/__pycache__/__init__.cpython-310.pyc b/dsLightRag/ElasticSearch/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..94ae6ce2baf4d4f283db992edbc385bb28d4e8c7 GIT binary patch literal 144 zcmd1j<>g`kf+(K23=sVoL?8o3AjbiSi&=m~3PUi1CZpdSknE3e2yv&mLc)fzkTO2mI`6;D2sdga4 KikW}}3j+WHkssOs literal 0 HcmV?d00001 diff --git a/dsLightRag/Routes/QA.py b/dsLightRag/Routes/QA.py new file mode 100644 index 00000000..b79d1af4 --- /dev/null +++ b/dsLightRag/Routes/QA.py @@ -0,0 +1,203 @@ +import json +import logging +import time +import uuid + +import fastapi +import jieba +from fastapi import APIRouter +from fastapi import HTTPException +from openai import AsyncOpenAI +from sse_starlette.sse import EventSourceResponse + +from Config import Config +from ElasticSearch.Utils.EsSearchUtil import EsSearchUtil + +# 创建路由路由器 +router = APIRouter(prefix="/qa", tags=["答疑"]) + +# 配置日志 +logger = logging.getLogger(__name__) + +# 初始化异步 OpenAI 客户端 +client = AsyncOpenAI( + api_key=Config.ALY_LLM_API_KEY, + base_url=Config.ALY_LLM_BASE_URL +) +# 初始化 ElasticSearch 工具 +search_util = EsSearchUtil(Config.ES_CONFIG) +@router.post("/chat") +async def chat(request: fastapi.Request): + """ + 根据用户输入的语句,查询相关历史对话 + 然后调用大模型进行回答 + """ + try: + data = await request.json() + user_id = data.get('user_id', 'anonymous') + query = data.get('query', '') + session_id = data.get('session_id', str(uuid.uuid4())) # 获取或生成会话ID + include_history = data.get('include_history', True) + + if not query: + raise HTTPException(status_code=400, detail="查询内容不能为空") + + # 1. 初始化会话历史和学生信息 + if session_id not in search_util.conversation_history: + search_util.conversation_history[session_id] = [] + + # 检查是否已有学生信息,如果没有则从ES加载 + if user_id not in search_util.student_info: + # 从ES加载学生信息 + info_from_es = search_util.get_student_info_from_es(user_id) + if info_from_es: + search_util.student_info[user_id] = info_from_es + logger.info(f"从ES加载用户 {user_id} 的信息: {info_from_es}") + else: + search_util.student_info[user_id] = {} + + # 2. 使用jieba分词提取学生信息 + search_util.extract_student_info(query, user_id) + + # 输出调试信息 + logger.info(f"当前学生信息: {search_util.student_info.get(user_id, {})}") + + # 为用户查询生成标签并存储到ES + current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + tags = [user_id, f"time:{current_time.split()[0]}", f"session:{session_id}"] + + # 提取查询中的关键词作为额外标签 - 使用jieba分词 + try: + seg_list = jieba.cut(query, cut_all=False) # 精确模式 + keywords = [kw for kw in seg_list if kw.strip() and kw not in search_util.STOPWORDS and len(kw) > 1] + keywords = keywords[:5] + tags.extend([f"keyword:{kw}" for kw in keywords]) + logger.info(f"使用jieba分词提取的关键词: {keywords}") + except Exception as e: + logger.error(f"分词失败: {str(e)}") + keywords = query.split()[:5] + tags.extend([f"keyword:{kw}" for kw in keywords if kw.strip()]) + + # 存储查询到ES + try: + search_util.insert_long_text_to_es(query, tags) + logger.info(f"用户 {user_id} 的查询已存储到ES,标签: {tags}") + except Exception as e: + logger.error(f"存储用户查询到ES失败: {str(e)}") + + # 3. 构建对话历史上下文 + history_context = "" + if include_history and session_id in search_util.conversation_history: + # 获取最近的几次对话历史 + recent_history = search_util.conversation_history[session_id][-search_util.MAX_HISTORY_ROUNDS:] + if recent_history: + history_context = "\n\n以下是最近的对话历史,可供参考:\n" + for i, (user_msg, ai_msg) in enumerate(recent_history, 1): + history_context += f"[对话 {i}] 用户: {user_msg}\n" + history_context += f"[对话 {i}] 老师: {ai_msg}\n" + + # 4. 构建学生信息上下文 + student_context = "" + if user_id in search_util.student_info and search_util.student_info[user_id]: + student_context = "\n\n学生基础信息:\n" + for key, value in search_util.student_info[user_id].items(): + if key == 'grade': + student_context += f"- 年级: {value}\n" + else: + student_context += f"- {key}: {value}\n" + + # 5. 构建提示词 + system_prompt = """ + 你是一位平易近人且教学方法灵活的教师,通过引导学生自主学习来帮助他们掌握知识。 + + 严格遵循以下教学规则: + 1. 基于学生情况调整教学:如果已了解学生的年级水平和知识背景,应基于此调整教学内容和难度。 + 2. 基于现有知识构建:将新思想与学生已有的知识联系起来。 + 3. 引导而非灌输:使用问题、提示和小步骤,让学生自己发现答案。 + 4. 检查和强化:在讲解难点后,确认学生能够重述或应用这些概念。 + 5. 变化节奏:混合讲解、提问和互动活动,让教学像对话而非讲座。 + + 最重要的是:不要直接给出答案,而是通过合作和基于学生已有知识的引导,帮助学生自己找到答案。 + """ + + # 添加学生信息到系统提示词 + if user_id in search_util.student_info and search_util.student_info[user_id]: + student_info_str = "\n\n学生基础信息:\n" + for key, value in search_util.student_info[user_id].items(): + if key == 'grade': + student_info_str += f"- 年级: {value}\n" + else: + student_info_str += f"- {key}: {value}\n" + system_prompt += student_info_str + + # 6. 流式调用大模型生成回答 + async def generate_response_stream(): + try: + # 构建消息列表 + messages = [{'role': 'system', 'content': system_prompt.strip()}] + + # 添加学生信息(如果有) + if student_context: + messages.append({'role': 'user', 'content': student_context.strip()}) + + # 添加历史对话(如果有) + if history_context: + messages.append({'role': 'user', 'content': history_context.strip()}) + + # 添加当前问题 + messages.append({'role': 'user', 'content': query}) + + stream = await client.chat.completions.create( + model=Config.ALY_LLM_MODEL_NAME, + messages=messages, + max_tokens=8000, + stream=True + ) + + # 收集完整回答用于保存 + full_answer = [] + async for chunk in stream: + if chunk.choices[0].delta.content: + full_answer.append(chunk.choices[0].delta.content) + yield f"data: {json.dumps({'reply': chunk.choices[0].delta.content}, ensure_ascii=False)}\n\n" + + # 保存回答到ES和对话历史 + if full_answer: + answer_text = ''.join(full_answer) + search_util.extract_student_info(answer_text, user_id) + try: + # 为回答添加标签 + answer_tags = [f"{user_id}_answer", f"time:{current_time.split()[0]}", f"session:{session_id}"] + try: + seg_list = jieba.cut(answer_text, cut_all=False) + answer_keywords = [kw for kw in seg_list if kw.strip() and kw not in search_util.STOPWORDS and len(kw) > 1] + answer_keywords = answer_keywords[:5] + answer_tags.extend([f"keyword:{kw}" for kw in answer_keywords]) + except Exception as e: + logger.error(f"回答分词失败: {str(e)}") + + search_util.insert_long_text_to_es(answer_text, answer_tags) + logger.info(f"用户 {user_id} 的回答已存储到ES") + + # 更新对话历史 + search_util.conversation_history[session_id].append((query, answer_text)) + # 保持历史记录不超过最大轮数 + if len(search_util.conversation_history[session_id]) > search_util.MAX_HISTORY_ROUNDS: + search_util.conversation_history[session_id].pop(0) + + except Exception as e: + logger.error(f"存储回答到ES失败: {str(e)}") + + except Exception as e: + logger.error(f"大模型调用失败: {str(e)}") + yield f"data: {json.dumps({'error': f'生成回答失败: {str(e)}'})}\n\n" + + return EventSourceResponse(generate_response_stream()) + + except HTTPException as e: + logger.error(f"聊天接口错误: {str(e.detail)}") + raise e + except Exception as e: + logger.error(f"聊天接口异常: {str(e)}") + raise HTTPException(status_code=500, detail=f"处理请求失败: {str(e)}") + diff --git a/dsLightRag/Routes/TeachingModel/api/LoginController.py b/dsLightRag/Routes/TeachingModel/api/LoginController.py index a9fb5f9e..76ec3328 100644 --- a/dsLightRag/Routes/TeachingModel/api/LoginController.py +++ b/dsLightRag/Routes/TeachingModel/api/LoginController.py @@ -1,19 +1,20 @@ +# pip install captcha # routes/LoginController.py import base64 -import os import json +import os import random import string -import jwt +import jwt from captcha.image import ImageCaptcha -from fastapi import APIRouter, Request, Response, status, HTTPException +from fastapi import APIRouter + from Util.CommonUtil import * from Util.CookieUtil import * from Util.Database import * from Util.JwtUtil import * from Util.ParseRequest import * -from Config.Config import * # 创建一个路由实例 router = APIRouter() diff --git a/dsLightRag/Routes/TeachingModel/api/__pycache__/DmController.cpython-310.pyc b/dsLightRag/Routes/TeachingModel/api/__pycache__/DmController.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aef1d04f8c4996c9002f06e6337843dce76510c5 GIT binary patch literal 1802 zcma)6&5I*N6z{6;q&r`eo{ycG^@~335E&xkO;=G2=)mqU%tVwX5<00$(%PNQys97* zGO$Ay6hZJPsH`q{*pvT&H~$Esui~O7PhRHatLn~7)>W{Zs($sW*RSfm`u!@Y)yf7w zpZ@Z26gr0Ss~W6677pITCceUs8H_|mOneekpAt2ukr`XQrEN1Rv=wGW#n|?3jTIs% zF8L*G7o&1q@himOgi~%W$dEAm3*uLK^}8B#z9+22%J+>0@z*p`L8QuR8mVh!4Usyx z29&vM{l0~sF55UU8k>LM?u-UaOK%+=94AxBA+=k4!pBTBNNOMR(m~F$ zQBEh*-bcJI?}S43)uxtD0S(oD#wI?%?t(CKZb)N}MO@GWEZn=sIhm7|@!{?{Wu_!^ z%B-7Mjva#)c8qfqea#hLYi^-O@wRbVV>WZBiD2PY2y#4VQ(=vb%hz z^=gNS_Y?RSKSz+L14ipkI2_62VA#=&6P-2>`lE0>yqho{b%IIQX~l0RV+lzVacEA? z((P+5t>#(Bm4u5Oy=R#&amW^RNtskB+(d9zyCT<9tfDxtB&u0;3yptg6*HN|3Ud+? zR@5A^4?*c(=h*&Bt^fGJ55NET@T%6Zt)|sPgTSWxP`IHWjbD`pb`eY0uG3PLeI7A^ zmr0(EOW;`?%<4Lgtja2p)XwGBCpf))ltyf9I->y^7es4>Fp96SC@htN`XCFEk5Dgj z@-<#PbBaA?ihc208#P1c=7JDIny4N7N@o_va#(3@VMWTiB9%j^;%e>L-v6TdzBj9T zfml)4-t3jVYvi9w`!6{_4HwO*_KdPL^K{uQP-OsB$Fwkk5P77}p#aY*Ty56R>8uum z9fdwg-0XDpZMRA{#S@`C>Ah{bMs}$~TzugLgbo|)sXYi#8j}zdyTHT_MIr-;xRkt^ zADRHgyR`P645MZXHP{OTPj6*XXg(o@W-yhb=IVV3MfLBx{`V*V!Sf4Rr|fTNybZxv zM1kaLl$D_f+0||$-?TI|vGl&L)tXtFm(J}tVbh4ep$H~aeO+254mS0=rel_gQ^V$L Gk-q`KwCN52 literal 0 HcmV?d00001 diff --git a/dsLightRag/Routes/TeachingModel/api/__pycache__/DocumentController.cpython-310.pyc b/dsLightRag/Routes/TeachingModel/api/__pycache__/DocumentController.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c191df58a2e26ba8fb427ddc303e6a77430974f GIT binary patch literal 5098 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 literal 0 HcmV?d00001 diff --git a/dsLightRag/Routes/TeachingModel/api/__pycache__/TeachingModelController.cpython-310.pyc b/dsLightRag/Routes/TeachingModel/api/__pycache__/TeachingModelController.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a46e782fcf94ea4e1ba96a9c90a3bcdb63f4308 GIT binary patch literal 9091 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 ZI0_XlC_#Tao>n{&JUw^@@oemr{}-yV9b^Ci literal 0 HcmV?d00001 diff --git a/dsLightRag/Routes/TeachingModel/api/__pycache__/ThemeController.cpython-310.pyc b/dsLightRag/Routes/TeachingModel/api/__pycache__/ThemeController.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ccc318cf69d97cb0a548c70bee2d1746fc3ac6e GIT binary patch literal 4733 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 literal 0 HcmV?d00001 diff --git a/dsLightRag/Routes/TeachingModel/tasks/__pycache__/BackgroundTasks.cpython-310.pyc b/dsLightRag/Routes/TeachingModel/tasks/__pycache__/BackgroundTasks.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c95abd0c52bcf425b47ebbb74e3a69d67bacdb31 GIT binary patch literal 3872 zcmb_fO>7&-6`tAU|DQ}x7HWw#XGE^O+?98S zl|(FC8FZU8O$?@o)^#M5Owzaw;26yzFkquO^xSiSJ=P&5os0rK<{rASInbP!5G z^YiA-oAz^MMo*qT$A)oaA{4jYH{;e7`1QDzu+3T^4-MJtOZZFCr z^n&Iq`elDHAP0&;Ifyy0sD+AQIqZ&oTBI12qZm=49wYt+mg6*DPf+1g%^5nQI^%nm6dg#gnoxC>sfwnSY1UNoPDoSp1qP$?3rEHk zE63C#%~;ZN(vfjp=?pdcGphB>i`24|JTT_Cc00cz7;vor4*%AFM1-zkh8Vt#>p}x? ztqK@1vF;;61D^)eP9coKi1;dqA>y~u2Z#hV#3~?QqZ&5<&HVKM3A%`cDwqXrOu|*c z+gTAFe0&G#CdFi#79v@WKohJeYQxF zqo^vj<@;cs8bu^Mj9}D1iiT0ePX=rs8Fa@1pbo+AAoRn)7r!Ea&S5gb!a(T*NpMmn z#A0@+t2Ov`6xMy6^)Rf5d)Hl?$80}famdq{93J7wd}jk938V6C`AHVH1JyQyl?Y4N z!78pq?a1XmJ8Jv$nE#XSzSp({FbAZ_6I|woi`PY@kNFTwuC_3$#6V^OI8sEiV_l9n z!9SJQ&ttmZ>zDB|x`^yZJ>5V%zSBdy=gWwEZfOk&konWa+rMqrZZsdPHE;iB>&x0!+SWmtxv8s zKfb#g6t)oOh7aP9qVUX`WpYJ^bFtUp4|H5pSJFP*g3HIw_kdvc}D?zIV)Y5 zIWu!|UV2uVojdos#IlN-WmL%(R6U<98iZ=RoR*4EN~MdZX69xjsF$jqUDTAkr0UZ6 zI3O6)48MkV?}t7f${Kl@5VAtm`q~s;(pm-eX#bcX0_n?wq=#|uq%iHzs zKYZK?@C=p%;Ccv(^58Vp=;P)c(6cZ0o?rf{1+sIj$N3Y0Vo7J?QyEFI+H{xVp}KDv zEf=UsrJZQzk#j;x5SRnMu0SotC~FWIN79DN#i!QE4br#-D<|Tg3)EeyuzzI zQB5>;|lWsyIfgY6>WVA|QYa|{cQ}PRQ27Fpr3-j#@@pXP5E-=NqVlBL;8Bi%RWb(_`OaZ8||O$W12(e6xmGR2YLa3wMVRg^)J4Ye3hN-e}xCWmR_iZd53+;g7O_i0+9)CBqT*7e@)9ZjJAi2&zV>xpsP z#uIUg@8Xb5jy-Y8Li@z)aO=kAW)l&PTAcE51-v&Ve@>H7LGM+(kkM?5g}B9>23dzS z2!l_n@@(KB)F{(^hdXs-gALM-hlWYUmHu7euw`wM00ZI!$2xMtph3h^grnQ;L>>^k z;#4c+fcVf7woM#Np?*fbyn-0gbGqBl_QkT^bAPQy^-5Z`A8XV%xz@LeRU?D>_JI+{ zf9?P|GDu1{!z3^w>zd{UMrUQ6H>VI&m=~5UQh7B_E6gZNDC88T#G}1q+arH5jNOZ&H(y{Lq#RXmX2IdJ|QUCw| delta 447 zcmYk1Pb-8`6o=36&X~C~-h1aA#TbNSqlnV@pHa55u@I4kTNH)Yi)OkbvbDh7TClSn zv#^n3!#7Y?7UDBlaqonj#dA8HbL#nBH$OCs4Iz?b3m<2zcV&?Qk1?V{6yHrZmQXOq zcx9_F>#V=)UJDgjW5J`Yz3_DM5}>WJ7CO31!K<=ei!ALTcs5V0t*5Lax>m_^`#noD z6s1s{puj*S2`Wm(L#gEvRLWgcWTc+8wp)=E(zNI;uo6lvB#k3zrpVoxe2qqX<+kPq zm^Wd3vy*?yL^R@q!-ogXJj*(;1Ic`}fFH20V9;>h$2(BXv|}dtTmH`{Q8=+S@v4m< zCUyMRW|m?!N6PN@<1;^f4|~*=o(^)Ar9=b%sg|IjC~H*}vZPow&S`QU{)mkej5?$p z#!M=fF?F79%DHjKVc4P9q0eE!TzSi$Y?QzHlO`jk(s{QVw0MbbZfu9aqzT3NH*X_j AFaQ7m diff --git a/dsLightRag/Routes/__pycache__/Rag.cpython-310.pyc b/dsLightRag/Routes/__pycache__/Rag.cpython-310.pyc index 8016b2363b97dfabb5ccf40ae292d24a76c7ecf3..adfb106f2254658242515c597c28b755080f1cb2 100644 GIT binary patch delta 889 zcmZvaPizuF6vkaZb_G@mN{QG+4N|g1pwvH*Xiti5Y>SD9me!=r$}X_7u$u??2V=ab zO=IF^jwU^M;bNkky&2EOi^;~L9vZ!Psi!8+wVUspt&oRJdc{ zp&X1yfRP1ihOLA*;^!dG0~hzxt5A9NF`V$PaoG&u``qOCty-<4cO4&D_d1T)v3gY1JCwm8V|B8h6N~;W z9_$}tbFqZbV9i*L*ZDHGC!Ez_Hh?>DNXgYqvXx0{@I4kb{rNWbcpXPOigi z0|bnngU=p|#OEt_As+w_fkzzwbVN^(dJ3Q!dIoHAtVt?$jjH*-PTt0suD;+N#Y1l4 zI`kQG-?(U6^}5lnkM7%j1M}GP28R>sk!c#Yv$jedoo=!#CterR$W{QnFTy2`UcA?6 ScL@U{oHf>>+iadGljYx-n#$+^ delta 855 zcmZXSO=uHQ5Xar5?S@TOmuM1AD}K~`nWh`RnrQK28?0I@h;3sPlWnpli<@kiBr3U8 zMGrj)zPliZ(u*Luc(F&pn>S(c*h}zU@gVsA8>o@);kWbVKQnLUz4tTwD=S;FEbZZ6 z@%^i+|D&7`g%y?&)0|at+k2cVw!5OYD9Q}a(LkE7X%d(KR3Oe?dtySo`^BTUdD?gH z)-j~f84g=o(#*D=BeIAh@D2e{_QUtqdk*FlkYwv}%|zH1zb5t3m$Bjwo}|3T#z)`KH`^-oDf z?(F!SUWWHy6a=k1a{xcK7RdiM|5o5$xP%I2jt!SRRIUCJjAO;4QOhWqdXsid^DcaScAdv0T;w#q3i|}%c5TtLDixz%>EEPtyE*Qs yd5FF?nWnKi+0dw|(+!px{g91AT?FuYs0cXk*=^KYghM0jH85iTQL@8h3x5HO0J;$X diff --git a/dsLightRag/Start.py b/dsLightRag/Start.py index 281e544b..59dbafbb 100644 --- a/dsLightRag/Start.py +++ b/dsLightRag/Start.py @@ -18,6 +18,7 @@ from Routes.TeachingModel.api.DmController import router as dm_router from Routes.TeachingModel.api.ThemeController import router as theme_router from Routes.TeachingModel.api.DocumentController import router as document_router from Routes.TeachingModel.api.TeachingModelController import router as teaching_model_router +from Routes.QA import router as qa_router from Util.LightRagUtil import * from contextlib import asynccontextmanager @@ -53,8 +54,8 @@ app.include_router(ggb_router) # Geogebra路由 app.include_router(rag_router) # LightRAG路由 app.include_router(knowledge_router) # 知识图谱路由 app.include_router(oss_router) # 阿里云OSS路由 - app.include_router(llm_router) # 大模型路由 +app.include_router(qa_router) # 答疑路由 # Teaching Model 相关路由 # 登录相关(不用登录) @@ -69,6 +70,9 @@ app.include_router(theme_router, prefix="/api/theme", tags=["theme"]) app.include_router(document_router, prefix="/api/document", tags=["document"]) # 问题相关(大模型应用) app.include_router(teaching_model_router, prefix="/api/teaching/model", tags=["teacher_model"]) +# 教学答疑 +app.include_router(teaching_model_router, prefix="/api/teaching/model", tags=["teacher_model"]) + if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8100) diff --git a/dsLightRag/Util/__pycache__/CommonUtil.cpython-310.pyc b/dsLightRag/Util/__pycache__/CommonUtil.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7cc4d57e99d5324a2e089e23b0b7a4c93a569c7d GIT binary patch literal 532 zcmYjP%}OLO5KhuRlX0BINARFv9~Abm%d#xX!YqOyqbRb_3^Z-0Cf#m#XGk)O%6Jnz z`3^JQd?F!NPriY8vN9`Th5D+#)DM*+s^4EBC^rvx7xtxlWmzvaU{D(1x!4zgUN%|I_W@RYNB*NxVp_vbzgOeG04JQT1uJwlcm?p!jA8%) literal 0 HcmV?d00001 diff --git a/dsLightRag/Util/__pycache__/CookieUtil.cpython-310.pyc b/dsLightRag/Util/__pycache__/CookieUtil.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0685e8018fd42ba1196fb08617a2cf1d7e16cee1 GIT binary patch literal 2530 zcmb_e-D?|15Wn5KlTN1(%lU2_T9Jp6gOU$g`XHK;lHz_)Oh{Y`B5+)_Ye}}GllSgI z9aIR3OB`A!rAeDFCpaY}q_lPdrTMVy{27IPttCrOdCx=B**obZt9fbZiXF}E&dkov z{ATX#WKx%4ocZI&;>nmK{V5*$hYAk|VLF$A5Q(6&R6#j}1dq$OBIjhEm&-~{`9LBH zQD-Egp2j(qMo455vyUkvQKHSET$HL48jX?|(Mfy`^r%b{Bw0WtHIK;7I^|Kv9nFi) z{*i`dF45o;;1to}_&AM@r-Z{Z>S>hI!1)xo zDkO)fIvycuGCZTejt)E9LMxyRdz;8+Un@?w9F>giTt8&nlO_78TPnBHDM@;U_Ku8k znNf$!lXRM^$E~tQd9-4EVOj;sm73)idBmZ4k5R4_T^DXyp3X<%0^K2a4dx-3&ORV@ zX+o-_I<80RYF(}?1kFW|AG@Lgi-;&RPQd5Bt6p^t;N;#-xu?~ zr+T=Wty#>fWWd#IX2f#b_YQxwes!iB_r}tr*8Q7T*CNKoD?Td?p0{(UTL>ebo z21Pod=o4zrpKX3W+r0E!`_ivZS7z7meD`$a?9-KR+RK+)i?{YS?wyA{*m(2pefxsV zL7O6~eFj?(mKv)wp@p@FkDCjRg2mmt_U;yJ>u(h1Sx8=KWKx#V74ci=mk@=Jl)?5hn7@pl&6W6eJ;t zC}Wv__5{tlz*0cUdMoS z*;@wTFe7Wr=YoK*J^ZD0?m^@H48PifutBcJvj=2C-2?Vd9m|M5ac@ zz&F7d39gxD)v8d_s|FN!yK-mbb74%aQuEt)=1bLnZ@V*ww4*1)}H#TC(04eTSe1AgIT zPyfI_)w$wM*WhJaq!yvffrC{iZ`a@(!Z&OT@!@fJ(OI?99(e2dpBIiFo?N=O?l%A@ zh>c%pA_QzF5UzE#{P6K8v@-Es;)jr{{;jxLV-V#apu2_U3!=)kcY@5l3vX%35fzn? c5=cWSS$j*zX&K89XpEohf2B0Bnv#|F--RdxHUIzs literal 0 HcmV?d00001 diff --git a/dsLightRag/Util/__pycache__/Database.cpython-310.pyc b/dsLightRag/Util/__pycache__/Database.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5593ac0b7b5f8ff2edc397f59f369a3da296857e GIT binary patch literal 7037 zcmb7}eT*Ds9mnUHncbb8eYw4B=^Is6MC<~V7K@q+wbfqRgJY?^mWH)t-R?7Y-Ll=i zJ~K;uo7tvtB|;P^2r87yl|Td&ZHfFrh%XWTlf=JFH1mi5$enHc!x&P7r&zzgXJ&SH zt}i+FGSAF2&-*;T=kt5HW+r1Q_kn47>}ROOL4w}8$5Y}l@fR|c?wSkPxH(PrIh4ZK7hN)b9@l@ z6d&SCa8L84{5sq-Xt9hhM~f_HV@hG=1&po~3(Om;HEK?IvS~XL!m{zutb3Y$xbDR# zE2479uBucs_$6?s<-L!~-pmx`QRXPq$_N&)L+PmP@3z0~sx$12vLSf)(!Cwc)efqz zGQnK-D%+(z9oE)4{z@J^Y2I45xfoN_w1EN3rb&!mL5@#C{<1&I;Om-VBd6Jt2zbMQ`fHZ;=-yn1s5?ikjeb*TQ~0G_THv=7`L6G zdC;mlcp9xu9B_73Cid-dYW00vD^6v9#kST?PI=~C^_uNen};Sh4ba#$k)=xehVfd1 zm-kPVArWiusyC~Zy1fYxVcm<%QE%>rb8}DqeD>Y%&%N^c`LnNHc>cxnXU}fT7ZQSc z@e-Bl;Z{vpUToZQst3GSwcfNXFFAa)YE3$|X2VO=n-deY#)KEMglGyh)X-4Shwu`U z6TVRD)n=nXOSM~dr#pvwBMxNotkqn>W3%zk)ygP1aLo9uX%EdA?-d3eKg})pgx(DI+SSB5Wg9?d$ zN1ISOI<6S5xXWhLGs;%wq5Jl!okYiQ4JpV)M{^BFcVgq}v??~Z`b^wSkbt=mIMNDb-5PKgzMe93ueM& zvi?a~KNi+^{gzjlImeek|8L0p@kP{6%p_fm-br$LLW_>c4Ocm(@T8kWYPu%U)agVg z<(ORrrQ6T9_qyqsR3CztcQS6~VA@RqL9qyevX`Tyk?Nx1Z~bU+vnQE|yIG!IiQ|p~ zpQe4pyBJ2xM3LlVX9jqN))=XIeY9qXA^<0Ml1*&~at45$Ps`R>cOcwznICXdk$ldL zh4}zU1IGp=l5h`@6wZ*oN8b#?al0!RkCsa;4HoCwO`cxdGdk*-HM`8My5(405UWb5 zs<0}KRd#BJEHBlX#w=ZL^SnV9V&9;W0dC3;A8c zyZ7uWjuf}&Yur<7ymbRQoqOiF*~ecGNb=fS=ih#PV}9=CpPv8C^RuUZJhD}!Q0F26 zaqh{VoImruzrJ*0?3<(K&;D%o8C1MzpkVfy$Iu+u8T1UAk(`~@XddxmHOD$+(>iJm z+o?3FmZ#Mk4tP{=Rva&0nVhs5+|%k-1FFvvo_?@dYe;yBG^{H)5v6FPDm#(&iuo{m4#S+zKy>+Nzd+|pq^_FFq z(v$T{)jH6ub4%Es_OLbO#bgm0%BtlCy0%M$ef9iTJ|~B0DoZG#4%e9 zOE}k~$aaya$4#J+{&K3pvdqNaGMSoeDMP7hGQbjLRI-#A$oJn(%>+i%z~~TL#nxPk zQ@Kg7G(XmvnIM?v=5s8^Bu+n3H4`|^`Z!&W2~=r<;V(kd6H%HbRv2Bkf`Nlz;Ei1d(ny&;22wuT$3V&txQRtG@Qy3bsLQ}D(Q?VaTyb8{ z-<`kd0zq#+fk(ojSVg&kKI%~&K__cIv1jMjExU*F^RfKc@a{a??A3;H#5LX^AWWI zpo_B=pBNb*Q7T(DZ)Bv!_famXbz0R;LxhB=BndtS`GXq9oSfha*fb|`Y{QB_-P*!@Bv(%CsyK) zIy#p|AswYcve?d!4UZ0gV|V_R{I*^9-_~q^X(W3==xR$6NX0h zZS>hE&V8#z_Vq&_zVmL26bLlx+{;hSz3_xDjeq~;qf*&lBn9ZJ)(_u#YWDFr3Nfkw zycoCkwZJuL$qY^O8h;uUZ8Ea4hinL{WJuGY zUUX7BP%&aFiXuBf`o3#^KZo-vcj7(l#CyR`ym#%L=;Bjv|FFFeMk&0?uFtxiywMq* zcI!;IS8+EkidV&+E>+Pe`ib?B$#wXexUhRr2-n1>-@d+}eH`s^b+PFzz~(K$W@73! zKreS zYy1obv$r9UG7Td6Bm#5bc`gHqh(6}W{UpjpNMwLSzW-_BGzjV{5{seZ)I9^|ky#Ln zDYtJ!yrDl9v;1nY7;;n=#!&hkp|uO-?ncO+HuV7HPKVqrb0GCNZF*PED6bL+BXXu6 z)1{o@9>|%`ObfG&_nn$h&iqn4S}GN@R_yI^5M+>cx*)_!o4n6NCrt+`NemJ2L~kRn zWGJTsp`d`ruTb44#To_<$kNr1XVt)}{V#$9v2c^bc2tbSa%f1p-pBP39fe03XB5eS zLSS@E9HrGh+#einKc%OuMCI`ogg2efed z{_1h~1n?Ae=~IY%@an2xO6;KVvF07Ynww%GvS5l>mGQFDsI@B;J_?ph^{%mABkV_v z&;VK~?l zXQmexS`RIRuu$L>#1}X-($OI?lFp3b_|D8CTXcI4of&dkWMBx+jMnGOQ2djencnEW zGee{6G&)=}=#(JH6QN*+)bl5qefG04pTak z&t4X{9y~TE8H{_t;BY?sqVpl}NNpdMKO8NW93Ci&J0R@hisn4X378W-t=(`Cr$Cyz%ZUX1L2x#vjx|O2;^%8X`v2D z^3KH3Cv!KoA$LY>q9p3ZlyY4^DD-o_hFYb81+RUj)6+L(1(!0F@o@y> zEndb1?JEXrt_!BNzk+aiIqdY$Xl*-47UuxzS`H4-ZYa_dI!18{gn#14ZdU&@ zr(eYxDq`RQ+g2Fx_lP1k-V*vR!rGnwzm5=&qpWXVk^AYzoYrLBk{*JfkdN3*3EA?} zn2`!o>H^77K_m1j7U?a>SiW)Bp(bzDtxd4=6?+8|11n3bL%KO+ZZhsPmKdvztdY!` F{{z*oS~36t literal 0 HcmV?d00001 diff --git a/dsLightRag/Util/__pycache__/DocxUtil.cpython-310.pyc b/dsLightRag/Util/__pycache__/DocxUtil.cpython-310.pyc index ba110f85cb3b5163bbd0030e20ac2a918547b091..39feec3ab0f53fb7d98a47cfe6141979681ab277 100644 GIT binary patch delta 1116 zcmZuw&2Jk;6rVS{_O53?>?9^h!AeOAB(_6P)rum%4iE_yRaFEBV3o=^p2cxuuhZSN zl16KCXeus5ZM7#-q1s5CP@6)j(nCc52QG+}dO>wRxFB)tCA?WDQV#6sy?GzMH*ep} zd-q0nUNW18!3cie{PDx$cf;4s9NfNMJhQ>yC z3miTr`GIGIOcHWF4^iT5qg=I77+$dbRoiwP+w)2l+wt9Uz2;xCe*SA$0xR5>KY&8` zP@aQqIFon>rtdB#J_fCTb-6Jsh7Xb>Z@z`Prz6u92DyDzW|Ator_oRWWQSiG1%dhy z$y=kLuG~mHXe)s-OD_Bx-cgp}-!P|6Zm1oTk9DD~2SPfIk`d@Uu}r-?fpM_wfw3&R z_xuch`7&ObQw%i3Fjnq$U@II8d6 zIO@g(6M=Svd{0H1AjxxQNIMw-2AvooD8t1$)KrlW-|7_(U3%yV04=Wyi$HxwIFWH( zmhg8|C&@MNGOKu|SYIIdERgwYe2{-Mv?X0N#JzL|g81qGC-n8Bp>d)~exge;;W)Mw z#pj+1|5hLJ=P(r|J-_VNDy<^k`_tW>-fx?a?%#TJf2;THmEN_Vrixz|`+z6go4ws% zp4|NLac8f$cl9~Uw-Oj~@zg(i+YzvJS}3fm^DB#G$8R z7pt~k;?2t1A)vHyskBmd&~#3;(U;*f_TC1g3`T)LmdY>zIckC`nJ|hw{c~(TQ_x{- zKP$VRU__p}N!(mT$itCRZ&s@{r|OEvbZ3!8a;sJ2ku-PqoJ&!+rW~A+rn|g0Q?6H= zSlMF3n^~x>lwBXIoWZF{9gK}Dg$>P_pB6{GA($9}IO~zJ`g4PhNPV}_z8Dg3 Z0+~uw5KE_mn53he!McznbzGJ7?0-R_9S#5h delta 729 zcmYjO&1(};5P!3~$-eBqZ1YjDVn|yRG>ri(hJs&*T9B%gfPxw-C1jSgH6OZ3h{Tj0 z?6qil;Kj?X;ME?AcoqK$4-0zh!IO7A)Oj0Hp=z~PGyq5Ntr zLQ(3m3S{NO*mWo!yp7$3$3-glBDh3Rn5oyA^?tL~bJws_f4SDe@&fT)K_E;Ea!1^- zbgp}4BI-bJe4+|^+$QgaAg`G36W*3NGMCuXhWu_mgO75NU)YXgZh>vZM=-K6e~M8I zFDmrnz8x@s0;drI?Km?6VrCShsva?92H9&JSewtY?zaJHsq6`P&l(vxiy6%B>Ti;2 zvY#47s-En-7&ZPp*%%Eui}^sKVdvB^67szKl=v|nf;H6U=7xOr`FgG0>$E}`3^mL8 z1FMC!SC+r6YfzGN_B>*PM2q@crrAboknA$-SCvWp3*in`uzOI-ljbnEVF@E}TjL diff --git a/dsLightRag/Util/__pycache__/JwtUtil.cpython-310.pyc b/dsLightRag/Util/__pycache__/JwtUtil.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd5b6230ecdaaa0dafa605df79b0219466762d09 GIT binary patch literal 855 zcmY*X&2G~`5Z=GUcAA#ZLXqGJ%pnheDnu0w{Y|SlRjF0Uvb@{Wshy41TSAa?L*mqT zXu0u3NPFePD{z5Xr$S||nejIs&(F6bS65pI&etE`F5eM^ep>MGPyla0${kP~ah#(9 zMX10L#uk&D6f~j*i&%|m-WVdzMDv3125)|0k;5I~0&=+r$m6XL`76I58}f;9d7_2R z3Sm5}xyW^5-Fv6QKq;vV9nZ9n4Ss1DgeEDrKi(e(wgJ)uDYrmX=oa0f1=jeM+#yXh zqyGFK<#u=*UsK+Nm(OjD#<)V7Xgb0`H(lTgpP~>kq}hBE zBm11es)T3;H*QI8{Uf#X8~4O;VTn;4NU0uMWiQt!Y7>lcrpY{)39n~fswdXp{wP#- zF<*}H2s~vQ6oE0CxD7)7sORA5`>p!c>f4IgSeBwrzbw7llaq^V)Lj}_vnLps>SYjoZD3;N^Avj6}9 literal 0 HcmV?d00001 diff --git a/dsLightRag/Util/__pycache__/PageUtil.cpython-310.pyc b/dsLightRag/Util/__pycache__/PageUtil.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a9d05fee087032983f8eef07fde3612c648ea99 GIT binary patch literal 1773 zcmZ8hOK%)S5boE^&aT&vfsG$Z0%L?xCI`510xKY5AuKs2a)OXnMys)V)?Pfjv+3y- ziq$J6awIPN1P(SQ_#ONRq`7j+U&x8Cdi>aUMqOQ9-PMoptEystzQyqU_V1rZyU_ll z!R!}e@C{n|Ere#8=d4S;$oVs-rB<(5SLi^8=%sFCN=I3sn_18gbxY5^R^3py^*m-9 znm=X9!W-GMfs^oY3l2Y~cdmv#E5_ zEIXHD<`?Fq$z$i^j0t4e!ltTo-ThX%rUwrruYRHg|M9e z8^5SJxJK{h2}D%62q9i7-sX$=6>p)LHjD>!+~r4071|@TvJU}R`LQ&&EIZ>4BLFT| z(BEO_e7p!6qyrhp+$?JWO1P5>TpcsB<~Zc~NN_e1P6Bk{cn6*4SFttZj(9es)B%z65t>(sfXAjWtXQpcUK{+_ds#g76GE`yF@0Xdaf_yL> z*lQro9PMzM!10bCt@Z-k1dK=LV;%vM6|}Z*<~C+bg9NQH??Jo+7NrLP=Hv1&C_XL` zb$B2<15jF?iz)NELgYcW0P&PxL}}mxKqt&64&Z2jcgS@3NAQ$c;R3Rq$~`reQ#B2z z4aaoDHC)&iONjbuWTlHvIcHY6NJm$Z-*ip0O}-x7X1(9d{gY-5_gnr|#ZJQbyWJli z$LM{?C&Be|tL7C<=6yV{Qu&~?C!KhE>+#mJcwNUe;G}u9kpc~qjq~&%^TFjYtb(x_ zjI5zNPFgn*H7l_C%{|TL0~&T9y7Qi@%T(&tl+*AguRiFn7uOki2V0aMLQv2XrER_- zBZPiOEK)BNC4w+_vE-d9{wFVhn_&)N!7{OP8Z*}F1s4!m>|2o`Y&jau4HOG)BjUBU!J0Srw zQx>D%KqE0~b(9V>v+O7LCnifLe)2-4$wXGNw5D3}g=@x(iWoMk?K2LwJqau2vr7$7 z2F*R3U{D#EV<^l08^eBcw5J4-Xh%>9uDl4+8RkW(S$SDz)v}T#$!oJFj zws3@dl@uM^6`piOMtUMEy;)E6i5%R>Y;%$q{cw^^?hi2 zFIXmjW_o&J>~dMwbr9B!J1LhMGSbG0w67bHaaFoerpVE48JHdDzd(=nz{xt*Bqj^A zMFWzoSc__F!D{mk?%NBFpkLcf3TwB?VkX!kQ^$ewnpSMJGaYh@TD630tZ7-(qk5$-4Ot!W>-9=d_Axw1FOM!S zFOSZKsyfEZ7}y@aaq68J5uFd!B1{u1 zT##iAtB-@Zc|GaR&79RiWo81k>?XvK`idD!)$XZ*qR}DITQIOSk1^`O=TPS_$Mcv& z2jTMuyg!Cu?oTPt`?I^^xhEb|F9T~CP3<)f3+x|2k6wi#CTa&jk|ZgTgA`$yG5|5U zA5!83S#ks<2lfIRPoqWEK42_x!_mX)1t8Qg4kI|A{ps)$uJ*%#iwJ8MPc-FEI8FAG zK>5{D%?F%%wXi0ub-e<(RYA3kTJ{>c0qLY5tid8J2rb%8LLPHL8bs~XRI>$3P{EQ! z;cXt%4uvzSMzkGw=@zX~VKwd8QlqgAX`jq(2a?^6nL3&5!Q7PwGWHT-LrR7OHtv0X|IUrgTi-tD+*bW?uAokn$MmztPM9yi*~Yc? zhqv$CzjOD&&AY$fTQ9gth!Qj^w9xFrf?0a=*s;?4@0}`5j(vJ|Y-(B^z#}Wt%GfXj z^@5WiSG@xJs(=H=vY6Y7o$bP5c0)RpdbdDSnmNaSIU0t6t@Q)M80FBNC&N4kR4$>j zKy$8+!lg1MO|M~SkWNDLAuQ6+#588H)#BX%p<8!rs9dq#V(A`KTw746xCdiCMgDt+ zU_;*(Sn{NCLD%E8Ms<6`O#$e{liKj71&lF3(eSE`hP3E;$QMpDTRB7te%jJ}0gx6GKoLhYAwHK0 z+;^V2|Mv6m1MUnx^5`iv^%}gN;%ejPYmYiV0jL0o8@Ij!TqWQuxKQzenlSckCG@q) zp2IRWrc|i@1)O>v#2726X>fG{r|q&B0dfv{bQlH%38UF9z+g|A^DM29qmWZWFd28& zhtgjU(pN3mO0b?n9}9#U#cTXx7*>*Sr|w4)TgnMOt+13B??g2ejfy-8f1EUW9@)kE OE$`5Qz5{#r4EzlkqDO@Q literal 0 HcmV?d00001 diff --git a/dsLightRag/Util/__pycache__/TranslateUtil.cpython-310.pyc b/dsLightRag/Util/__pycache__/TranslateUtil.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..16f201113b73d9affa4640ba9557d6eccd746011 GIT binary patch literal 1501 zcmah|OKTKC5bmDG?(8cY^H3um#+xf4Amk)bJmg>?EMgR4h>Ww_lg-50nN4>ul4TYW z67WZ8FbWD0^yV+{M+kEjBc5~fWYs*D-5}V|HPtoURo!2GUs$V^35-|Y-)~OUYu>XD%m(N*%S zI#eT-sS6}hBn3}fAcu5??A9X%>gmlvL%6=%mgX&Ui-%oP+N^5}>2zH4<+jUR(`?y& z;hJ~N+ovQhCx7g~>AJCz`W`z~I3f_MLh^1;lcp1=8+JAv>W>Eddsw-?tsuFR@J zRs`yN3XHO^(P^qtgI)&9F~o~t+7wJa@BugxR8 z3&0|z8-(cqXQ4S*&*03#Su|4*A^R`FrW3GizsCUb6cW5A$Da4AV9wrr`t|n1&(9yv zzI^2h1R84c9F8326*MCtc@38F>;x`IDvx0%fkxRc13Ct+QcdC05NN|3jd8YdTp(kv z{fjaE7B(p<9alxVN_b+G^OeWAfhn|jCMQI;4@hFoUx;NispO}TD8+1X@rCu|g;_G# zI0t2T9SwSMx#w~b2Jmb#(R9Q#*OGV+ivwu+3F!A79YXt0XoMc<3W>nfV|A!VY`!+q znF>17KvhVGrASTmrbjx{L2sH=!7F5XyAf$XkYe)>T^y`s0FJJ;XlCv>OT?bA{jlQ& z>Ay^lqcf%#m^TOYM#7nqM$-mV#?r;5(Gfsi7d_vTv9=R>L84k-f_FCxd>%g5)M%^i zdqP^5vYupNY}4FiDC<&=FgB@>?!MTgDeO^}2)12^dIGzH1S|vnQi*0F#lOvl5!VuJ zuR79cIl@gZi~^2TFOUgclMW}3;CN`PYNCQf0LDr;WPRVghe{HtlPWRPDZ?;kjK6H3 Bgr)!h literal 0 HcmV?d00001 diff --git a/dsSchoolBuddy/.idea/vcs.xml b/dsSchoolBuddy/.idea/vcs.xml index 61aaccdb..2e3f6920 100644 --- a/dsSchoolBuddy/.idea/vcs.xml +++ b/dsSchoolBuddy/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/dsSchoolBuddy/TestStart.py b/dsSchoolBuddy/TestStart.py index ec1ad94c..b4b3a4e0 100644 --- a/dsSchoolBuddy/TestStart.py +++ b/dsSchoolBuddy/TestStart.py @@ -10,8 +10,8 @@ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %( logger = logging.getLogger(__name__) # 服务器地址 -BASE_URL = "http://localhost:8000" -CHAT_ENDPOINT = f"{BASE_URL}/api/teaching_chat" +BASE_URL = "http://localhost:8100" +CHAT_ENDPOINT = f"{BASE_URL}/qa/chat" # 用户ID(固定一个以便模拟多轮对话) USER_ID = "test_user_123"