Quellcode durchsuchen

Initial commit: enterprise knowledge base RAG

zengli-jenny vor 1 Monat
Commit
3e683e1096
100 geänderte Dateien mit 6326 neuen und 0 gelöschten Zeilen
  1. 24 0
      .env.example
  2. 55 0
      .gitignore
  3. 40 0
      2.1_Ollama大模型高效管理工具/01_ollama_chat_generate.py
  4. 36 0
      2.1_Ollama大模型高效管理工具/02_ollama_remote_client.py
  5. 33 0
      2.1_Ollama大模型高效管理工具/03_ollama_stream_chat.py
  6. 25 0
      2.1_Ollama大模型高效管理工具/04_langchain_ollama.py
  7. 44 0
      2.1_Ollama大模型高效管理工具/05_requests_ollama_chat.py
  8. 17 0
      2.1_Ollama大模型高效管理工具/README.md
  9. 21 0
      2.2_LangChain基础知识/01_models_openai_chat.py
  10. 23 0
      2.2_LangChain基础知识/02_models_ollama_llm.py
  11. 33 0
      2.2_LangChain基础知识/03_models_chat_messages.py
  12. 25 0
      2.2_LangChain基础知识/04_models_embeddings.py
  13. 34 0
      2.2_LangChain基础知识/05_prompt_template.py
  14. 52 0
      2.2_LangChain基础知识/06_few_shot_prompt.py
  15. 33 0
      2.2_LangChain基础知识/07_chat_prompt_template.py
  16. 37 0
      2.2_LangChain基础知识/08_chat_prompt_messages.py
  17. 40 0
      2.2_LangChain基础知识/09_chat_prompt_history_placeholder.py
  18. 32 0
      2.2_LangChain基础知识/10_chain_simple.py
  19. 46 0
      2.2_LangChain基础知识/11_chain_sequential.py
  20. 37 0
      2.2_LangChain基础知识/12_output_parser_str.py
  21. 41 0
      2.2_LangChain基础知识/13_output_parser_list.py
  22. 41 0
      2.2_LangChain基础知识/14_output_parser_json.py
  23. 51 0
      2.2_LangChain基础知识/15_output_parser_pydantic.py
  24. 61 0
      2.2_LangChain基础知识/16_output_parser_custom.py
  25. 36 0
      2.2_LangChain基础知识/17_memory_chat_history.py
  26. 58 0
      2.2_LangChain基础知识/18_memory_agent_short_term.py
  27. 42 0
      2.2_LangChain基础知识/19_indexes_document_loader.py
  28. 33 0
      2.2_LangChain基础知识/20_indexes_character_splitter.py
  29. 35 0
      2.2_LangChain基础知识/21_indexes_recursive_splitter.py
  30. 41 0
      2.2_LangChain基础知识/22_indexes_semantic_chunker.py
  31. 30 0
      2.2_LangChain基础知识/23_indexes_markdown_header_splitter.py
  32. 46 0
      2.2_LangChain基础知识/24_indexes_chroma_vector_store.py
  33. 38 0
      2.2_LangChain基础知识/25_indexes_retriever_options.py
  34. 47 0
      2.2_LangChain基础知识/26_indexes_retriever_query.py
  35. 30 0
      2.2_LangChain基础知识/27_retriever_tfidf.py
  36. 30 0
      2.2_LangChain基础知识/28_retriever_bm25.py
  37. 22 0
      2.2_LangChain基础知识/29_agents_tool_names.py
  38. 40 0
      2.2_LangChain基础知识/30_agents_openai_tools.py
  39. 13 0
      2.2_LangChain基础知识/README.md
  40. 5 0
      2.2_LangChain基础知识/data/pku.txt
  41. 4 0
      2.2_LangChain基础知识/data/衣服属性.txt
  42. 23 0
      2.3_Milvus向量数据库/01_operate_db.py
  43. 64 0
      2.3_Milvus向量数据库/02_collection_operations.py
  44. 114 0
      2.3_Milvus向量数据库/03_entity_insert_update_delete.py
  45. 90 0
      2.3_Milvus向量数据库/04_entity_query.py
  46. 91 0
      2.3_Milvus向量数据库/05_hybrid_search_rerank.py
  47. 19 0
      2.3_Milvus向量数据库/06_load_and_drop_collection.py
  48. 38 0
      2.3_Milvus向量数据库/README.md
  49. 139 0
      3.1 logging日志/log.py
  50. 2 0
      3.2 redis_lesson/base/__init__.py
  51. 89 0
      3.2 redis_lesson/base/config.py
  52. 65 0
      3.2 redis_lesson/base/logger.py
  53. 8 0
      3.2 redis_lesson/config.example.ini
  54. 32 0
      3.2 redis_lesson/main.py
  55. 46 0
      3.2 redis_lesson/redis_client.py
  56. 4 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/base/__init__.py
  57. 62 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/base/config.py
  58. 43 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/base/logger.py
  59. 16 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/config.example.ini
  60. 1 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/__init__.py
  61. 1 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/cache/__init__.py
  62. 56 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/cache/redis_client.py
  63. 31 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/data/JP企业知识库问答.csv
  64. 1 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/db/__init__.py
  65. 118 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/db/mysql_client.py
  66. 62 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/main.py
  67. 1 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/retrieval/__init__.py
  68. 152 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/retrieval/bm25_search.py
  69. 1 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/utils/__init__.py
  70. 16 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/utils/preprocess.py
  71. 6 0
      3.3 基于Mysql库的问答系统/integrated_qa_system/requirements.txt
  72. 27 0
      3.4 bm25_lesson/main.py
  73. 2 0
      3.4 bm25_lesson/requirements.txt
  74. 39 0
      3.4 bm25_lesson/retrieval/bm25_search.py
  75. 99 0
      4.基于Milvus库的问答系统/integrated_qa_system/base/config.py
  76. 45 0
      4.基于Milvus库的问答系统/integrated_qa_system/base/logger.py
  77. 33 0
      4.基于Milvus库的问答系统/integrated_qa_system/config.example.ini
  78. 220 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/chatgpt_generate_200.jsonl
  79. 220 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/chatgpt_generate_200_0423.jsonl
  80. 220 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/chatgpt_generate_200_query.jsonl
  81. 13 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/handler.py
  82. 220 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/model_generate_0623.jsonl
  83. 220 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/model_generate_262.json
  84. 220 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/model_generic_5000.json
  85. 220 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/qwen3-max_generate_200.json
  86. 220 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/training_data_1000.json
  87. 13 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/微调脚本生成.md
  88. 37 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/提示词模块.txt
  89. 5 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/数据生成与效果评估.md
  90. 112 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/document_processor.py
  91. 96 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/prompts.py
  92. 262 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/query_classifier.py
  93. 191 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/rag_system.py
  94. 90 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/strategy_selector.py
  95. 256 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/vector_store.py
  96. 284 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/父块与子块切分说明.md
  97. 17 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/data/enterprise_data/IT服务台知识库.md
  98. 13 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/data/enterprise_data/产品与售后知识库.md
  99. 25 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/data/enterprise_data/企业知识库员工服务手册.md
  100. 85 0
      4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/data/enterprise_data/父子块切分演示知识库.md

+ 24 - 0
.env.example

@@ -0,0 +1,24 @@
+# OpenAI-compatible API
+base_url=https://dashscope.aliyuncs.com/compatible-mode/v1
+api_key=your_api_key_here
+model=qwen-plus
+
+# Local Ollama service
+OLLAMA_BASE_URL=http://127.0.0.1:11434
+OLLAMA_MODEL=qwen2.5:7b
+OLLAMA_EMBEDDING_MODEL=mxbai-embed-large
+
+# openai or ollama
+MODEL_PROVIDER=openai
+
+# hash or ollama
+EMBEDDING_PROVIDER=hash
+
+# Docker demo credentials. Replace them in your local .env.
+MYSQL_ROOT_PASSWORD=change_me_root_password
+MYSQL_DATABASE=enterprise_kb
+MYSQL_USER=demo_user
+MYSQL_PASSWORD=change_me_mysql_password
+REDIS_PASSWORD=change_me_redis_password
+MINIO_ACCESS_KEY=minioadmin
+MINIO_SECRET_KEY=change_me_minio_secret

+ 55 - 0
.gitignore

@@ -0,0 +1,55 @@
+# Local secrets and machine-specific configuration
+.env
+.env.*
+!.env.example
+**/config.ini
+!**/config.example.ini
+
+# Downloaded model weights and training outputs
+**/models/*
+!**/models/download_models.py
+**/query_cls/
+**/bert_query_classifier/
+**/bert_results/
+**/notebooks/tokenizer.json
+**/notebooks/tokenizer_config.json
+**/notebooks/special_tokens_map.json
+**/notebooks/vocab.txt
+*.safetensors
+*.onnx
+*.pt
+*.pth
+*.bin
+*.model
+
+# Runtime databases and service data
+**/mysql_data/
+**/volumes/
+**/milvus_demo.db/
+**/docker_logs/
+**/logs/
+*.log
+*.db
+*.sqlite
+*.sqlite3
+*.pem
+
+# Python and tooling caches
+__pycache__/
+*.py[cod]
+.pytest_cache/
+.mypy_cache/
+.ruff_cache/
+.ipynb_checkpoints/
+.venv/
+venv/
+
+# IDE and operating-system files
+.idea/
+.vscode/
+.DS_Store
+
+# Frontend build output
+node_modules/
+dist/
+build/

+ 40 - 0
2.1_Ollama大模型高效管理工具/01_ollama_chat_generate.py

@@ -0,0 +1,40 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import env_value, print_dependency_hint
+
+
+try:
+    import ollama
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+MODEL = env_value("OLLAMA_MODEL", default="qwen2.5:7b")
+OLLAMA_HOST = env_value("OLLAMA_BASE_URL", default="http://127.0.0.1:11434")
+
+
+def main() -> None:
+    client = ollama.Client(host=OLLAMA_HOST)
+
+    response = client.chat(
+        model=MODEL,
+        messages=[{"role": "user", "content": "为什么天空是蓝色的?"}],
+    )
+    print(response)
+    print(response["message"]["content"])
+
+    # 生成式调用,和课件中的注释示例一致。
+    generate_response = client.generate(model=MODEL, prompt="为什么天空是蓝色的?")
+    print(generate_response["response"])
+
+
+if __name__ == "__main__":
+    try:
+        main()
+    except Exception as exc:
+        print(f"Ollama调用失败: {exc}")
+        print("请确认 Ollama 已启动,并且 代码/.env 中的 OLLAMA_BASE_URL、OLLAMA_MODEL 可用。")

+ 36 - 0
2.1_Ollama大模型高效管理工具/02_ollama_remote_client.py

@@ -0,0 +1,36 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import env_value, print_dependency_hint
+
+
+try:
+    from ollama import Client
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    # 远程调用时把 OLLAMA_BASE_URL 改成实际 Ollama 服务地址。
+    client = Client(host=env_value("OLLAMA_BASE_URL", default="http://127.0.0.1:11434"))
+
+    response = client.chat(
+        model=env_value("OLLAMA_MODEL", default="qwen2.5:7b"),
+        messages=[
+            {
+                "role": "user",
+                "content": "为什么天空是蓝色的?",
+            },
+        ],
+    )
+    print(response["message"]["content"])
+
+
+if __name__ == "__main__":
+    try:
+        main()
+    except Exception as exc:
+        print(f"Ollama远程Client调用失败: {exc}")

+ 33 - 0
2.1_Ollama大模型高效管理工具/03_ollama_stream_chat.py

@@ -0,0 +1,33 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import env_value, print_dependency_hint
+
+
+try:
+    import ollama
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    client = ollama.Client(host=env_value("OLLAMA_BASE_URL", default="http://127.0.0.1:11434"))
+    stream = client.chat(
+        model=env_value("OLLAMA_MODEL", default="qwen2.5:7b"),
+        messages=[{"role": "user", "content": "为什么天空是蓝色的?"}],
+        stream=True,
+    )
+
+    for chunk in stream:
+        print(chunk["message"]["content"], end="", flush=True)
+    print()
+
+
+if __name__ == "__main__":
+    try:
+        main()
+    except Exception as exc:
+        print(f"Ollama流式调用失败: {exc}")

+ 25 - 0
2.1_Ollama大模型高效管理工具/04_langchain_ollama.py

@@ -0,0 +1,25 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import get_ollama_llm, print_dependency_hint
+
+
+def main() -> None:
+    try:
+        llm = get_ollama_llm(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    result = llm.invoke("你是谁")
+    print(result)
+
+
+if __name__ == "__main__":
+    try:
+        main()
+    except Exception as exc:
+        print(f"LangChain调用Ollama失败: {exc}")
+        print("请确认 Ollama 服务、模型名称,以及 langchain-ollama 依赖都可用。")

+ 44 - 0
2.1_Ollama大模型高效管理工具/05_requests_ollama_chat.py

@@ -0,0 +1,44 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import env_value, print_dependency_hint
+
+
+try:
+    import requests
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    base_url = env_value("OLLAMA_BASE_URL", default="http://127.0.0.1:11434").rstrip("/")
+    url = f"{base_url}/api/chat"
+    model = env_value("OLLAMA_MODEL", default="qwen2.5:7b")
+
+    headers = {"Content-Type": "application/json"}
+    data = {
+        "model": model,
+        "options": {
+            "temperature": 0.0,
+        },
+        "stream": False,
+        "messages": [
+            {
+                "role": "system",
+                "content": "你是谁?",
+            }
+        ],
+    }
+    response = requests.post(url, json=data, headers=headers, timeout=60)
+    response.raise_for_status()
+    print(response.json())
+
+
+if __name__ == "__main__":
+    try:
+        main()
+    except Exception as exc:
+        print(f"requests调用Ollama失败: {exc}")

+ 17 - 0
2.1_Ollama大模型高效管理工具/README.md

@@ -0,0 +1,17 @@
+# 2.1 Ollama大模型高效管理工具
+
+本目录对应课件 `2.1 Ollama大模型高效管理工具`。
+
+- `01_ollama_chat_generate.py`: 对应“在 Python 中使用 Ollama API”的聊天式/生成式调用。
+- `02_ollama_remote_client.py`: 对应“可以远程调用 Ollama”。
+- `03_ollama_stream_chat.py`: 对应“流式输出”。
+- `04_langchain_ollama.py`: 对应“LangChain调用”。
+- `05_requests_ollama_chat.py`: 对应“requests调用”。
+
+运行前可先启动 Ollama 并拉取模型:
+
+```bash
+ollama pull qwen2.5:7b
+```
+
+如果本地端口或模型名不同,修改上级目录 `代码/.env` 中的 `OLLAMA_BASE_URL` 和 `OLLAMA_MODEL`。

+ 21 - 0
2.2_LangChain基础知识/01_models_openai_chat.py

@@ -0,0 +1,21 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import as_text, get_chat_model, print_dependency_hint
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    result = model.invoke("帮我讲个笑话吧")
+    print(as_text(result))
+
+
+if __name__ == "__main__":
+    main()

+ 23 - 0
2.2_LangChain基础知识/02_models_ollama_llm.py

@@ -0,0 +1,23 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import as_text, get_chat_model, print_dependency_hint
+
+
+def main() -> None:
+    # 课件原始版本使用 OllamaLLM(model="qwen2.5:7b")。
+    # 这里默认走 代码/.env 中的 OpenAI兼容模型,MODEL_PROVIDER=ollama 时可切到本地Ollama。
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    result = model.invoke("请给我讲个笑话吧")
+    print(as_text(result))
+
+
+if __name__ == "__main__":
+    main()

+ 33 - 0
2.2_LangChain基础知识/03_models_chat_messages.py

@@ -0,0 +1,33 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import as_text, get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain_core.messages import HumanMessage, SystemMessage
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    messages = [
+        SystemMessage(content="现在你是一个著名的诗人"),
+        HumanMessage(content="给我写一首唐诗"),
+    ]
+
+    result = model.invoke(messages)
+    print(as_text(result))
+
+
+if __name__ == "__main__":
+    main()

+ 25 - 0
2.2_LangChain基础知识/04_models_embeddings.py

@@ -0,0 +1,25 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import get_embeddings, print_dependency_hint
+
+
+def main() -> None:
+    try:
+        model = get_embeddings()
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    res1 = model.embed_query("这是第一个测试文档")
+    print(f"result1->{res1}")
+    print(f"result1的长度->{len(res1)}")
+
+    res2 = model.embed_documents(["这是第一个测试文档", "这是第二个测试文档"])
+    print(res2)
+
+
+if __name__ == "__main__":
+    main()

+ 34 - 0
2.2_LangChain基础知识/05_prompt_template.py

@@ -0,0 +1,34 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import as_text, get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain_core.prompts import PromptTemplate
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    template = "我的邻居姓{lastname},他生了个儿子,给他儿子起个名字"
+    prompt = PromptTemplate(input_variables=["lastname"], template=template)
+
+    prompt_text = prompt.format(lastname="王")
+    print(prompt_text)
+
+    result = model.invoke(prompt_text)
+    print(as_text(result))
+
+
+if __name__ == "__main__":
+    main()

+ 52 - 0
2.2_LangChain基础知识/06_few_shot_prompt.py

@@ -0,0 +1,52 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import as_text, get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    examples = [
+        {"word": "开心", "antonym": "难过"},
+        {"word": "高", "antonym": "矮"},
+    ]
+
+    example_template = """
+单词: {word}
+反义词: {antonym}\n
+"""
+    example_prompt = PromptTemplate(
+        input_variables=["word", "antonym"],
+        template=example_template,
+    )
+
+    few_shot_prompt = FewShotPromptTemplate(
+        examples=examples,
+        example_prompt=example_prompt,
+        prefix="给出每个单词的反义词",
+        suffix="单词: {input}\n反义词:",
+        input_variables=["input"],
+        example_separator="\n",
+    )
+
+    prompt_text = few_shot_prompt.format(input="粗")
+    print(prompt_text)
+    print(as_text(model.invoke(prompt_text)))
+
+
+if __name__ == "__main__":
+    main()

+ 33 - 0
2.2_LangChain基础知识/07_chat_prompt_template.py

@@ -0,0 +1,33 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import as_text, get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain_core.prompts import ChatPromptTemplate
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    template_str = "帮我讲个关于{name}笑话吧"
+    prompt_template = ChatPromptTemplate.from_template(template_str)
+    prompt = prompt_template.format_messages(name="气球")
+    print(f"prompt-->{prompt}")
+
+    result = model.invoke(prompt)
+    print(f"result-->{as_text(result)}")
+
+
+if __name__ == "__main__":
+    main()

+ 37 - 0
2.2_LangChain基础知识/08_chat_prompt_messages.py

@@ -0,0 +1,37 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import as_text, get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain_core.messages import SystemMessage
+    from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    system_prompt = SystemMessage(content="你是取名专家。")
+    human_str = "我的邻居姓{lastname},他生了个儿子,给他儿子起个名字。"
+    human_template = HumanMessagePromptTemplate.from_template(human_str)
+    chat_template = ChatPromptTemplate.from_messages([system_prompt, human_template])
+
+    prompt = chat_template.format_messages(lastname="王")
+    print(f"prompt-->{prompt}")
+
+    result = model.invoke(prompt)
+    print(f"result-->{as_text(result)}")
+
+
+if __name__ == "__main__":
+    main()

+ 40 - 0
2.2_LangChain基础知识/09_chat_prompt_history_placeholder.py

@@ -0,0 +1,40 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import as_text, get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    prompt_template = ChatPromptTemplate.from_messages(
+        [
+            ("system", "给出每个单词的反义词"),
+            MessagesPlaceholder("history"),
+            ("human", "{question}"),
+        ]
+    )
+
+    history = [("human", "开心"), ("ai", "难过"), ("human", "高"), ("ai", "矮")]
+    prompt = prompt_template.format_messages(history=history, question="富有")
+    print(f"prompt-->{prompt}")
+
+    result = model.invoke(prompt)
+    print(f"result-->{as_text(result)}")
+
+
+if __name__ == "__main__":
+    main()

+ 32 - 0
2.2_LangChain基础知识/10_chain_simple.py

@@ -0,0 +1,32 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import as_text, get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain_core.prompts import PromptTemplate
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    template = "我的邻居姓{lastname},他生了个儿子,给他儿子起个名字"
+    prompt = PromptTemplate(input_variables=["lastname"], template=template)
+
+    try:
+        llm = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    chain = prompt | llm
+    result = chain.invoke({"lastname": "王"})
+    print(as_text(result))
+
+
+if __name__ == "__main__":
+    main()

+ 46 - 0
2.2_LangChain基础知识/11_chain_sequential.py

@@ -0,0 +1,46 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import as_text, get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain_core.output_parsers import StrOutputParser
+    from langchain_core.prompts import PromptTemplate
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    first_prompt = PromptTemplate(
+        input_variables=["lastname"],
+        template="我的邻居姓{lastname},他生了个儿子,给他儿子起个名字",
+    )
+
+    try:
+        llm = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    first_chain = first_prompt | llm | StrOutputParser()
+
+    second_prompt = PromptTemplate(
+        input_variables=["child_name"],
+        template="邻居的儿子名字叫{child_name},给他起一个小名",
+    )
+    second_chain = second_prompt | llm
+
+    overall_chain = first_chain | second_chain
+    print(overall_chain)
+    print("*" * 80)
+
+    result = overall_chain.invoke({"lastname": "王"})
+    print(as_text(result))
+
+
+if __name__ == "__main__":
+    main()

+ 37 - 0
2.2_LangChain基础知识/12_output_parser_str.py

@@ -0,0 +1,37 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import as_text, get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain_core.output_parsers import StrOutputParser
+    from langchain_core.prompts import ChatPromptTemplate
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    prompt = ChatPromptTemplate.from_template("解释{topic}是什么?回答控制20字以内")
+
+    chain1 = prompt | model
+    result1 = chain1.invoke({"topic": "ai"})
+    print(f"result1-->{as_text(result1)}")
+
+    parser = StrOutputParser()
+    chain2 = prompt | model | parser
+    result2 = chain2.invoke({"topic": "ai"})
+    print(f"result2-->{result2}")
+
+
+if __name__ == "__main__":
+    main()

+ 41 - 0
2.2_LangChain基础知识/13_output_parser_list.py

@@ -0,0 +1,41 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain_core.output_parsers import CommaSeparatedListOutputParser
+    from langchain_core.prompts import ChatPromptTemplate
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    parser = CommaSeparatedListOutputParser()
+    format_instructions = parser.get_format_instructions()
+    prompt = ChatPromptTemplate.from_template(
+        "用中文列出{topic}的五个最重要特点。\n{format_instructions}"
+    )
+
+    chain = prompt | model | parser
+    result = chain.invoke(
+        {
+            "topic": "大模型",
+            "format_instructions": format_instructions,
+        }
+    )
+    print(result)
+
+
+if __name__ == "__main__":
+    main()

+ 41 - 0
2.2_LangChain基础知识/14_output_parser_json.py

@@ -0,0 +1,41 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain_core.output_parsers import JsonOutputParser
+    from langchain_core.prompts import ChatPromptTemplate
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    json_parser = JsonOutputParser()
+    json_format_instructions = json_parser.get_format_instructions()
+    json_prompt = ChatPromptTemplate.from_template(
+        "生成一个包含{person}基本信息的JSON。应包括姓名、职业、年龄和技能列表,不要包含任何注释或额外说明。\n{format_instructions}"
+    )
+
+    json_chain = json_prompt | model | json_parser
+    result = json_chain.invoke(
+        {
+            "person": "雷军",
+            "format_instructions": json_format_instructions,
+        }
+    )
+    print(result)
+
+
+if __name__ == "__main__":
+    main()

+ 51 - 0
2.2_LangChain基础知识/15_output_parser_pydantic.py

@@ -0,0 +1,51 @@
+from pathlib import Path
+from typing import List
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain_core.output_parsers import PydanticOutputParser
+    from langchain_core.prompts import ChatPromptTemplate
+    from pydantic import BaseModel, Field
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+class Movie(BaseModel):
+    title: str = Field(description="电影标题")
+    director: str = Field(description="导演姓名")
+    year: int = Field(description="上映年份")
+    genre: List[str] = Field(description="电影类型")
+    rating: float = Field(description="评分(1-10)")
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    pydantic_parser = PydanticOutputParser(pydantic_object=Movie)
+    format_instructions = pydantic_parser.get_format_instructions()
+    pydantic_prompt = ChatPromptTemplate.from_template(
+        "生成一部{genre}电影的信息。\n{format_instructions}"
+    )
+
+    pydantic_chain = pydantic_prompt | model | pydantic_parser
+    movie_data = pydantic_chain.invoke(
+        {
+            "genre": "科幻",
+            "format_instructions": format_instructions,
+        }
+    )
+    print(movie_data)
+
+
+if __name__ == "__main__":
+    main()

+ 61 - 0
2.2_LangChain基础知识/16_output_parser_custom.py

@@ -0,0 +1,61 @@
+from pathlib import Path
+from typing import Any, Dict
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain_core.output_parsers import BaseOutputParser
+    from langchain_core.prompts import ChatPromptTemplate
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+class CustomKeyValueParser(BaseOutputParser[Dict[str, Any]]):
+    """解析形如 'key: value' 的文本。"""
+
+    def parse(self, text: str) -> Dict[str, Any]:
+        result = {}
+        lines = text.strip().split("\n")
+        for line in lines:
+            if ":" in line:
+                key, value = line.split(":", 1)
+                result[key.strip()] = value.strip()
+        return result
+
+    def get_format_instructions(self) -> str:
+        return """请以'键: 值'的格式返回信息,每行一个键值对。
+例如:
+名称: 爱因斯坦
+职业: 物理学家
+贡献: 相对论"""
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    custom_parser = CustomKeyValueParser()
+    custom_prompt = ChatPromptTemplate.from_template(
+        "提供关于{person}的基本信息。\n{format_instructions}"
+    )
+
+    custom_chain = custom_prompt | model | custom_parser
+    result = custom_chain.invoke(
+        {
+            "person": "屠呦呦",
+            "format_instructions": custom_parser.get_format_instructions(),
+        }
+    )
+    print(result)
+
+
+if __name__ == "__main__":
+    main()

+ 36 - 0
2.2_LangChain基础知识/17_memory_chat_history.py

@@ -0,0 +1,36 @@
+from pathlib import Path
+import json
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import print_dependency_hint
+
+
+try:
+    from langchain_community.chat_message_histories import ChatMessageHistory
+    from langchain_core.messages import messages_from_dict, messages_to_dict
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    history = ChatMessageHistory()
+    history.add_user_message("在吗?")
+    history.add_ai_message("在")
+    print(f"history.messages-->{history.messages}")
+
+    dicts = messages_to_dict(history.messages)
+    print(f"dicts-->{dicts}")
+
+    history_file = Path(__file__).with_name("history.json")
+    history_file.write_text(json.dumps(dicts, indent=2, ensure_ascii=False), encoding="utf-8")
+
+    messages = json.loads(history_file.read_text(encoding="utf-8"))
+    chat_messages = messages_from_dict(messages)
+    print(f"chat_messages-->{chat_messages}")
+
+
+if __name__ == "__main__":
+    main()

+ 58 - 0
2.2_LangChain基础知识/18_memory_agent_short_term.py

@@ -0,0 +1,58 @@
+from pathlib import Path
+import sys
+import time
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain.agents import create_agent
+    from langchain_core.messages import HumanMessage
+    try:
+        from langgraph.checkpoint.memory import InMemorySaver
+    except ImportError:
+        from langgraph.checkpoint.memory import MemorySaver as InMemorySaver
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    checkpointer = InMemorySaver()
+    timestamp = int(time.time())
+    config = {"configurable": {"thread_id": f"{timestamp}"}}
+
+    agent = create_agent(
+        model=model,
+        tools=[],
+        checkpointer=checkpointer,
+        system_prompt="你是一个翻译官,擅长中英互译。",
+    )
+
+    input_1 = {"messages": [HumanMessage(content="你好,我叫小呆。")]}
+    response_a = agent.invoke(input=input_1, config=config)
+
+    input_2 = {"messages": [HumanMessage(content="你好,我叫什么?")]}
+    config_2 = {"configurable": {"thread_id": f"{timestamp}-new"}}
+
+    response_b = agent.invoke(input=input_2, config=config_2)
+    response_c = agent.invoke(input=input_2, config=config)
+
+    print("Response A (第一次对话):")
+    print(response_a)
+    print("\nResponse B (新线程,无记忆):")
+    print(response_b)
+    print("\nResponse C (回到原线程,有记忆):")
+    print(response_c)
+
+
+if __name__ == "__main__":
+    main()

+ 42 - 0
2.2_LangChain基础知识/19_indexes_document_loader.py

@@ -0,0 +1,42 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import print_dependency_hint
+
+
+DATA_FILE = Path(__file__).with_name("data") / "衣服属性.txt"
+
+
+def main() -> None:
+    try:
+        from langchain_community.document_loaders import TextLoader
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    try:
+        from langchain_unstructured import UnstructuredLoader
+    except ModuleNotFoundError:
+        UnstructuredLoader = None
+
+    if UnstructuredLoader is not None:
+        loader = UnstructuredLoader(str(DATA_FILE), encoding="utf8")
+        docs = loader.load()
+        print(f"unstructured docs-->{docs}")
+        print(f"len-->{len(docs)}")
+        print(f"第一行数据-->{docs[0].page_content}")
+        print("*" * 100)
+    else:
+        print("未安装 langchain-unstructured,跳过 UnstructuredLoader 示例。")
+
+    loader = TextLoader(str(DATA_FILE), encoding="utf8")
+    docs = loader.load()
+    print(f"TextLoader docs-->{docs}")
+    print(f"len-->{len(docs)}")
+    print("第一行数据-->{}".format(docs[0].page_content.split("\n")[0]))
+
+
+if __name__ == "__main__":
+    main()

+ 33 - 0
2.2_LangChain基础知识/20_indexes_character_splitter.py

@@ -0,0 +1,33 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import print_dependency_hint
+
+
+try:
+    from langchain_core.documents import Document
+    from langchain_text_splitters import CharacterTextSplitter
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    text_splitter = CharacterTextSplitter(separator=" ", chunk_size=5, chunk_overlap=1)
+
+    result1 = text_splitter.split_text("a b c d e f")
+    print(f"result1--->{result1}")
+
+    result2 = text_splitter.create_documents(["a b c d e f", "e f g h"])
+    print(f"result2--->{result2}")
+
+    result3 = text_splitter.split_documents(
+        [Document(page_content="a b c d e f", metadata={"id": "1"})]
+    )
+    print(f"result3--->{result3}")
+
+
+if __name__ == "__main__":
+    main()

+ 35 - 0
2.2_LangChain基础知识/21_indexes_recursive_splitter.py

@@ -0,0 +1,35 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import print_dependency_hint
+
+
+try:
+    from langchain_text_splitters import RecursiveCharacterTextSplitter
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    text_splitter = RecursiveCharacterTextSplitter(
+        chunk_size=20,
+        chunk_overlap=6,
+        length_function=len,
+        separators=["\n\n", "\n", " ", ""],
+    )
+
+    text = """
+人工智能正在快速发展,尤其是大语言模型的应用,正在改变人类的工作方式。
+它们可以帮助人们进行写作、代码生成、甚至是科研探索。
+相比之下,新能源的发展同样重要。
+电动车和太阳能正在逐渐替代传统能源,减少碳排放,对全球环境保护至关重要。
+"""
+    docs = text_splitter.split_text(text)
+    print(docs)
+
+
+if __name__ == "__main__":
+    main()

+ 41 - 0
2.2_LangChain基础知识/22_indexes_semantic_chunker.py

@@ -0,0 +1,41 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import get_embeddings, print_dependency_hint
+
+
+try:
+    from langchain_experimental.text_splitter import SemanticChunker
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    embed = get_embeddings()
+    text_splitter = SemanticChunker(
+        embeddings=embed,
+        breakpoint_threshold_type="percentile",
+        breakpoint_threshold_amount=70.0,
+        sentence_split_regex=r"(?<=[。!?.!?])\s*",
+        min_chunk_size=10,
+    )
+
+    text = """
+人工智能正在快速发展,尤其是大语言模型的应用,正在改变人类的工作方式。
+它们可以帮助人们进行写作、代码生成、甚至是科研探索。
+相比之下,新能源的发展同样重要。
+电动车和太阳能正在逐渐替代传统能源,减少碳排放,对全球环境保护至关重要。
+"""
+
+    docs = text_splitter.split_text(text)
+    for i, doc in enumerate(docs):
+        print(f"------ Chunk {i + 1} ------")
+        print(doc.strip())
+        print()
+
+
+if __name__ == "__main__":
+    main()

+ 30 - 0
2.2_LangChain基础知识/23_indexes_markdown_header_splitter.py

@@ -0,0 +1,30 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import print_dependency_hint
+
+
+try:
+    from langchain_text_splitters import MarkdownHeaderTextSplitter
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    headers_to_split_on = [
+        ("#", "Header 1"),
+        ("##", "Header 2"),
+        ("###", "Header 3"),
+    ]
+
+    markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
+    markdown_text = "# Header 1\nSome text\n## Header 2\nMore text\n### Header 3\nEven more text"
+    docs = markdown_splitter.split_text(markdown_text)
+    print(docs)
+
+
+if __name__ == "__main__":
+    main()

+ 46 - 0
2.2_LangChain基础知识/24_indexes_chroma_vector_store.py

@@ -0,0 +1,46 @@
+from pathlib import Path
+import shutil
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import get_embeddings, print_dependency_hint
+
+
+DATA_FILE = Path(__file__).with_name("data") / "pku.txt"
+PERSIST_DIR = Path(__file__).with_name("chroma_db")
+
+
+def main() -> None:
+    try:
+        from langchain_chroma import Chroma
+        from langchain_community.document_loaders import TextLoader
+        from langchain_text_splitters import CharacterTextSplitter
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    if PERSIST_DIR.exists():
+        shutil.rmtree(PERSIST_DIR)
+
+    loader = TextLoader(str(DATA_FILE), encoding="utf-8")
+    docs = loader.load()
+
+    text_splitter = CharacterTextSplitter(separator="\n\n", chunk_size=200, chunk_overlap=30)
+    split_docs = text_splitter.split_documents(docs)
+    print(f"split_docs-->{split_docs}")
+
+    embedding = get_embeddings()
+    chroma_db = Chroma.from_documents(
+        documents=split_docs,
+        embedding=embedding,
+        persist_directory=str(PERSIST_DIR),
+    )
+
+    query = "1937年北京大学发生了什么?"
+    result = chroma_db.similarity_search(query, k=2)
+    print(f"result-->{result}")
+
+
+if __name__ == "__main__":
+    main()

+ 38 - 0
2.2_LangChain基础知识/25_indexes_retriever_options.py

@@ -0,0 +1,38 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import get_embeddings, print_dependency_hint
+
+
+def main() -> None:
+    try:
+        from langchain_chroma import Chroma
+        from langchain_core.documents import Document
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    vector_store = Chroma.from_documents(
+        documents=[
+            Document(page_content="重要文档记录了北京大学在1937年南迁。", metadata={"source": "重要文档.pdf"}),
+            Document(page_content="普通文档记录了北京大学创办于1898年。", metadata={"source": "普通文档.pdf"}),
+            Document(page_content="清华大学和南开大学也参与组成西南联合大学。", metadata={"source": "重要文档.pdf"}),
+        ],
+        embedding=get_embeddings(),
+    )
+
+    retriever = vector_store.as_retriever(
+        search_type="similarity",
+        search_kwargs={
+            "k": 2,
+            "filter": {"source": "重要文档.pdf"},
+        },
+    )
+    result = retriever.invoke("1937年北京大学发生了什么?")
+    print(result)
+
+
+if __name__ == "__main__":
+    main()

+ 47 - 0
2.2_LangChain基础知识/26_indexes_retriever_query.py

@@ -0,0 +1,47 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import get_embeddings, print_dependency_hint
+
+
+DATA_FILE = Path(__file__).with_name("data") / "pku.txt"
+PERSIST_DIR = Path(__file__).with_name("chroma_db")
+
+
+def build_if_needed():
+    from langchain_chroma import Chroma
+    from langchain_community.document_loaders import TextLoader
+    from langchain_text_splitters import CharacterTextSplitter
+
+    embedding = get_embeddings()
+    if PERSIST_DIR.exists():
+        return Chroma(persist_directory=str(PERSIST_DIR), embedding_function=embedding)
+
+    loader = TextLoader(str(DATA_FILE), encoding="utf-8")
+    docs = loader.load()
+    text_splitter = CharacterTextSplitter(separator="\n\n", chunk_size=200, chunk_overlap=30)
+    split_docs = text_splitter.split_documents(docs)
+    return Chroma.from_documents(
+        documents=split_docs,
+        embedding=embedding,
+        persist_directory=str(PERSIST_DIR),
+    )
+
+
+def main() -> None:
+    try:
+        chroma_db = build_if_needed()
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    query = "1937年北京大学发生了什么?"
+    retriever = chroma_db.as_retriever(search_kwargs={"k": 2})
+    result = retriever.invoke(query)
+    print(f"result-->{result}")
+
+
+if __name__ == "__main__":
+    main()

+ 30 - 0
2.2_LangChain基础知识/27_retriever_tfidf.py

@@ -0,0 +1,30 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import print_dependency_hint
+
+
+try:
+    from langchain_community.retrievers import TFIDFRetriever
+    from langchain_core.documents import Document
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    docs = [
+        Document(page_content="量子计算是一种基于量子力学的计算范式。"),
+        Document(page_content="人工智能是模拟人类智能的技术。"),
+    ]
+
+    retriever = TFIDFRetriever.from_documents(docs)
+    retriever.k = 1
+    results = retriever.invoke("量子计算")
+    print(results)
+
+
+if __name__ == "__main__":
+    main()

+ 30 - 0
2.2_LangChain基础知识/28_retriever_bm25.py

@@ -0,0 +1,30 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import print_dependency_hint
+
+
+try:
+    from langchain_community.retrievers import BM25Retriever
+    from langchain_core.documents import Document
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    docs = [
+        Document(page_content="量子计算是一种基于量子力学的计算范式。"),
+        Document(page_content="人工智能是模拟人类智能的技术。"),
+    ]
+
+    retriever = BM25Retriever.from_documents(docs)
+    retriever.k = 1
+    results = retriever.invoke("量子计算")
+    print(results)
+
+
+if __name__ == "__main__":
+    main()

+ 22 - 0
2.2_LangChain基础知识/29_agents_tool_names.py

@@ -0,0 +1,22 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import print_dependency_hint
+
+
+try:
+    from langchain_community.agent_toolkits.load_tools import get_all_tool_names
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    results = get_all_tool_names()
+    print(results)
+
+
+if __name__ == "__main__":
+    main()

+ 40 - 0
2.2_LangChain基础知识/30_agents_openai_tools.py

@@ -0,0 +1,40 @@
+from pathlib import Path
+import sys
+
+sys.path.append(str(Path(__file__).resolve().parents[1]))
+
+from _chapter2_config import get_chat_model, print_dependency_hint
+
+
+try:
+    from langchain.agents import create_agent
+    from langchain_community.agent_toolkits.load_tools import load_tools
+    from langchain_core.messages import HumanMessage
+    from langgraph.checkpoint.memory import MemorySaver
+except ModuleNotFoundError as exc:
+    print_dependency_hint(exc)
+    raise SystemExit(1)
+
+
+def main() -> None:
+    try:
+        model = get_chat_model(temperature=0)
+    except ModuleNotFoundError as exc:
+        print_dependency_hint(exc)
+        raise SystemExit(1)
+
+    memory = MemorySaver()
+    tools = load_tools(["llm-math"], llm=model)
+    agent_executor = create_agent(model, tools, checkpointer=memory)
+
+    config = {"configurable": {"thread_id": "abc123"}}
+    for chunk in agent_executor.stream(
+        {"messages": [HumanMessage(content="计算一下300的25%是多少?")]},
+        config,
+    ):
+        print(chunk)
+        print("----")
+
+
+if __name__ == "__main__":
+    main()

+ 13 - 0
2.2_LangChain基础知识/README.md

@@ -0,0 +1,13 @@
+# 2.2 LangChain基础知识
+
+本目录对应课件 `2.2 LangChain基础知识.md`,文件按课件代码块顺序编号。
+
+- `01` 到 `04`: Models
+- `05` 到 `09`: Prompts
+- `10` 到 `11`: Chains
+- `12` 到 `16`: output_parsers
+- `17` 到 `18`: Memory
+- `19` 到 `28`: Indexes / Retrievers
+- `29` 到 `30`: Agents
+
+默认模型配置在上级目录 `代码/.env`。如需从 ModelVerse 切到本地 Ollama,将 `MODEL_PROVIDER=openai` 改为 `MODEL_PROVIDER=ollama`,并确认 Ollama 服务与模型已启动。

+ 5 - 0
2.2_LangChain基础知识/data/pku.txt

@@ -0,0 +1,5 @@
+北京大学创办于1898年,初名京师大学堂,是中国近现代第一所国立综合性大学。
+
+1937年卢沟桥事变后,北京大学、清华大学和南开大学南迁,先在长沙组成临时大学,后迁至昆明组成西南联合大学。
+
+西南联合大学在艰苦环境中坚持办学,培养了大批杰出人才,也成为中国高等教育史上的重要篇章。

+ 4 - 0
2.2_LangChain基础知识/data/衣服属性.txt

@@ -0,0 +1,4 @@
+白色T恤, 纯棉, 适合夏季通勤和日常穿搭
+黑色冲锋衣, 防风防水, 适合户外徒步
+蓝色牛仔裤, 直筒版型, 适合休闲场景
+灰色卫衣, 加绒保暖, 适合秋冬运动

+ 23 - 0
2.3_Milvus向量数据库/01_operate_db.py

@@ -0,0 +1,23 @@
+from pymilvus import MilvusClient
+
+
+# 1 数据库的操作
+def operate_db():
+    # 如果 uri 为数据库名称路径,代表本地操作数据库
+    client = MilvusClient(uri="http://localhost:19530")
+    # 如果 uri 为链接地址,代表 Milvus 属于单机服务,需要开启 Milvus 后台服务操作
+    # client = MilvusClient(uri="http://localhost:19530")
+    # # # 创建名称为 milvus_demo 的数据库
+    # #
+    # databases = client.list_databases()
+    # if "milvus_demo" not in databases:
+    #     client.create_database(db_name="milvus_demo")
+    # else:
+    #     client.using_database(db_name="milvus_demo")
+    return client
+
+
+if __name__ == '__main__':
+    client = operate_db()
+    print(client)
+    print(f'collections-->{client.list_collections()}')

+ 64 - 0
2.3_Milvus向量数据库/02_collection_operations.py

@@ -0,0 +1,64 @@
+from pymilvus import DataType, MilvusClient
+
+client = MilvusClient(uri="http://localhost:19530")
+
+
+# 2 collection集合的操作
+def operate_table():
+    # 脚本可重复运行:先清理上一次留下的 collection
+    if client.has_collection(collection_name='demo_v1'):
+        client.drop_collection(collection_name='demo_v1')
+
+    # 定义schema
+    ## 注意:在定义集合 Schema 时,enable_dynamic_field=True 使得您可以插入未定义的字段。一般动态字段以 JSON 格式存储,通常命名为 $meta。在插入数据时,所有未定义的字段及其值将被保存为键值对。
+    ## 在定义集合 Schema 时,auto_id=True 可以对主键自动增长id。
+    schema = client.create_schema(auto_id=False, enable_dynamic_field=True)
+    # # schema添加字段id, vector
+    schema.add_field(field_name='id', datatype=DataType.INT64, is_primary=True)
+    schema.add_field(field_name='vector', datatype=DataType.FLOAT_VECTOR, dim=5)
+    schema.add_field(field_name='scalar1', datatype=DataType.VARCHAR, max_length=256, description='标量字段')
+
+    # # 创建集合
+    client.create_collection(collection_name='demo_v1', schema=schema)
+    # # 设置索引
+    index_params = client.prepare_index_params()
+    # # 在向量字段vector上面添加一个索引;
+    # index_type='',  # 留空以使用自动索引 —— 当前 pymilvus/Milvus Lite 版本要求显式指定 index_type,这里用 'AUTOINDEX' 代替课件里的空字符串写法
+    # 对于向量字段,常见的默认索引类型包括IVF_FLAT或HNSW等,具体取决于数据的特性和查询需求。
+    # 对于标量字段,常见的默认索引可能是INVERTED等。
+    index_params.add_index(field_name='vector', metric_type="COSINE", index_type='AUTOINDEX', index_name="vector_index")
+    client.create_index(collection_name='demo_v1', index_params=index_params)
+    #
+    # # 查看索引信息
+    res = client.list_indexes(collection_name='demo_v1')
+    print(f'索引信息-->{res}')
+
+    # 当前 Milvus Lite 版本以字段名作为实际生效的 index_name(自定义的 index_name 不会生效),
+    # 所以通过 list_indexes(field_name=...) 拿到真实的 index_name 再查询详情。
+    vector_index_name = client.list_indexes(collection_name='demo_v1', field_name='vector')[0]
+    res = client.describe_index(collection_name='demo_v1', index_name=vector_index_name)
+    print(f'指定索引详细信息-->{res}')
+
+    # 查看索引状态
+    # client.load_collection(collection_name='demo_v1')
+    # print(client.get_load_state(collection_name='demo_v1'))
+    # 如果不需要索引,可以删除相关索引
+    # client.release_collection(collection_name='demo_v1')
+    # client.drop_index(collection_name='demo_v1', index_name='vector_index')
+
+    # # 检索标量字段
+    index_params1 = client.prepare_index_params()
+    index_params1.add_index(field_name='scalar1', index_type='INVERTED', index_name='default_index')
+    client.create_index(collection_name='demo_v1', index_params=index_params1)
+    # #
+    # # # 查看索引信息
+    res = client.list_indexes(collection_name='demo_v1')
+    print(f'索引信息-->{res}')
+    #
+    scalar_index_name = client.list_indexes(collection_name='demo_v1', field_name='scalar1')[0]
+    res = client.describe_index(collection_name='demo_v1', index_name=scalar_index_name)
+    print(f'指定索引详细信息-->{res}')
+
+
+if __name__ == '__main__':
+    operate_table()

+ 114 - 0
2.3_Milvus向量数据库/03_entity_insert_update_delete.py

@@ -0,0 +1,114 @@
+from pymilvus import MilvusClient
+
+client = MilvusClient(uri="http://localhost:19530")
+
+
+def operate_entity():
+    # # todo:1. 创建集合collection
+    # 这种方式: collection 只包括两个字段. id 作为主键, vector 作为向量字段,以及自动设置 enable_dynamic_field 为 True
+    # auto_id 启用此设置可确保主键自动递增。在数据插入期间无需手动提供主键。
+    # enable_dynamic_field 启用后,要插入的数据中除 id 和 vector 之外的所有字段都将被视为动态字段。
+    # # 这些附加字段作为键值对保存在名为 $meta 的特殊字段中。此功能允许在数据插入期间包含额外的字段。
+    # 脚本可重复运行:先清理上一次留下的 collection,再按课件方式快速创建(auto_id 默认为 False,可自行指定主键 id)
+    if client.has_collection(collection_name='demo_v2'):
+        client.drop_collection(collection_name='demo_v2')
+    client.create_collection(collection_name='demo_v2', dimension=5, metric_type='IP')
+    #
+    # # todo:2. 插入数据(也叫实体)
+    data = [
+        {"id": 0, "vector": [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354,
+                             0.9029438446296592], "color": "pink_8682"},
+        {"id": 1, "vector": [0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501,
+                             0.838729485096104], "color": "red_7025"},
+        {"id": 2, "vector": [0.43742130801983836, -0.5597502546264526, 0.6457887650909682, 0.7894058910881185,
+                             0.20785793220625592], "color": "orange_6781"},
+        {"id": 3, "vector": [0.3172005263489739, 0.9719044792798428, -0.36981146090600725, -0.4860894583077995,
+                             0.95791889146345], "color": "pink_9298"},
+        {"id": 4, "vector": [0.4452349528804562, -0.8757026943054742, 0.8220779437047674, 0.46406290649483184,
+                             0.30337481143159106], "color": "red_4794"},
+        {"id": 5, "vector": [0.985825131989184, -0.8144651566660419, 0.6299267002202009, 0.1206906911183383,
+                             -0.1446277761879955], "color": "yellow_4222"},
+        {"id": 6, "vector": [0.8371977790571115, -0.015764369584852833, -0.31062937026679327, -0.562666951622192,
+                             -0.8984947637863987], "color": "red_9392"},
+        {"id": 7, "vector": [-0.33445148015177995, -0.2567135004164067, 0.8987539745369246, 0.9402995886420709,
+                             0.5378064918413052], "color": "grey_8510"},
+        {"id": 8, "vector": [0.39524717779832685, 0.4000257286739164, -0.5890507376891594, -0.8650502298996872,
+                             -0.6140360785406336], "color": "white_9381"},
+        {"id": 9, "vector": [0.5718280481994695, 0.24070317428066512, -0.3737913482606834, -0.06726932177492717,
+                             -0.6980531615588608], "color": "purple_4976"}
+    ]
+    res = client.insert(collection_name='demo_v2', data=data)
+    # print(res)
+
+    ## todo:2.1 将数据插入到特定分区,可以在插入请求中指定分区名称,如下所示:
+    data = [
+        {"id": 10, "vector": [-0.5570353903748935, -0.8997887893201304, -0.7123782431855732, -0.6298990746450119,
+                              0.6699215060604258], "color": "red_1202"},
+        {"id": 11, "vector": [0.6319019033373907, 0.6821488267878275, 0.8552303045704168, 0.36929791364943054,
+                              -0.14152860714878068], "color": "blue_4150"},
+        {"id": 12, "vector": [0.9483947484855766, -0.32294203351925344, 0.9759290319978025, 0.8262982148666174,
+                              -0.8351194181285713], "color": "orange_4590"},
+        {"id": 13, "vector": [-0.5449109892498731, 0.043511240563786524, -0.25105249484790804, -0.012030655265886425,
+                              -0.0010987671273892108], "color": "pink_9619"},
+        {"id": 14, "vector": [0.6603339372951424, -0.10866551787442225, -0.9435597754324891, 0.8230244263466688,
+                              -0.7986720938400362], "color": "orange_4863"},
+        {"id": 15, "vector": [-0.8825129181091456, -0.9204557711667729, -0.935350065513425, 0.5484069690287079,
+                              0.24448151140671204], "color": "orange_7984"},
+        {"id": 16, "vector": [0.6285586391568163, 0.5389064528263487, -0.3163366239905099, 0.22036279378888013,
+                              0.15077052220816167], "color": "blue_9010"},
+        {"id": 17, "vector": [-0.20151825016059233, -0.905239387635804, 0.6749305353372479, -0.7324272081377843,
+                              -0.33007998971889263], "color": "blue_4521"},
+        {"id": 18, "vector": [0.2432286610792349, 0.01785636564206139, -0.651356982731391, -0.35848148851027895,
+                              -0.7387383128324057], "color": "orange_2529"},
+        {"id": 19, "vector": [0.055512329053363674, 0.7100266349039421, 0.4956956543575197, 0.24541352586717702,
+                              0.4209030729923515], "color": "red_9437"}
+    ]
+
+    # ##  todo:3. 创建分区
+    client.create_partition(collection_name='demo_v2', partition_name='partitionA')
+    #
+    # # # # todo: 3.1 分区中插入数据
+    res = client.insert(collection_name='demo_v2', data=data, partition_name='partitionA')
+    # print(res)
+    ## todo:4. 更新插入数据
+    # 在 Milvus 中,upsert 操作执行数据级操作,根据集合中是否已存在主键来插入或更新实体。具体来说:
+    # 如果集合中已存在该实体的主键,则现有实体将被覆盖。
+    # 如果集合中不存在主键,则将插入一个新实体。
+    data = [
+        {"id": 0, "vector": [-0.619954382375778, 0.4479436794798608, -0.17493894838751745, -0.4248030059917294,
+                             -0.8648452746018911], "color": "black_9898"},
+        {"id": 1, "vector": [0.4762662251462588, -0.6942502138717026, -0.4490002642657902, -0.628696575798281,
+                             0.9660395877041965], "color": "red_7319"},
+        {"id": 2, "vector": [-0.8864122635045097, 0.9260170474445351, 0.801326976181461, 0.6383943392381306,
+                             0.7563037341572827], "color": "white_6465"},
+        {"id": 3, "vector": [0.14594326235891586, -0.3775407299900644, -0.3765479013078812, 0.20612075380355122,
+                             0.4902678929632145], "color": "orange_7580"},
+        {"id": 4, "vector": [0.4548498669607359, -0.887610217681605, 0.5655081329910452, 0.19220509387904117,
+                             0.016513983433433577], "color": "red_3314"},
+        {"id": 5, "vector": [0.11755001847051827, -0.7295149788999611, 0.2608115847524266, -0.1719167007897875,
+                             0.7417611743754855], "color": "black_9955"},
+        {"id": 6, "vector": [0.9363032158314308, 0.030699901477745373, 0.8365910312319647, 0.7823840208444011,
+                             0.2625222076909237], "color": "yellow_2461"},
+        {"id": 7, "vector": [0.0754823906014721, -0.6390658668265143, 0.5610517334334937, -0.8986261118798251,
+                             0.9372056764266794], "color": "white_5015"},
+        {"id": 8, "vector": [-0.3038434006935904, 0.1279149203380523, 0.503958664270957, -0.2622661156746988,
+                             0.7407627307791929], "color": "purple_6414"},
+        {"id": 9, "vector": [-0.7125086947677588, -0.8050968321012257, -0.32608864121785786, 0.3255654958645424,
+                             0.26227968923834233], "color": "brown_7231"}
+    ]
+
+    res = client.upsert(collection_name='demo_v2', data=data)
+    # print(res)
+    # 注意如果分区中不存在更新数据的id,就不会受影响,但是会影响集合里已经存在的相同id的实体
+    # res = client.upsert(collection_name='demo_v2', data=data, partition_name="partitionA")
+    # todo:5. 删除实体(数据)
+    # 按照过滤器删除;如果不指定分区,默认情况下会在整个集合中进行删除
+    res = client.delete(collection_name='demo_v2', filter='id in [12, 5, 6]')
+    print(res)
+    # 按照id进行删除;指定分区删除数据
+    # res = client.delete(collection_name='demo_v2', ids=[1, 2, 3, 4], partition_name='partitionA')
+    print(res)
+
+
+if __name__ == '__main__':
+    operate_entity()

+ 90 - 0
2.3_Milvus向量数据库/04_entity_query.py

@@ -0,0 +1,90 @@
+import json
+
+from pymilvus import MilvusClient
+
+client = MilvusClient(uri="http://localhost:19530")
+
+
+# entity实体数据的操作:查询
+# 注意:本脚本依赖 demo_v2 集合中已有数据,请先运行 03_entity_insert_update_delete.py
+def query_operation():
+    # 每次新建 MilvusClient 连接本地库文件时,集合默认是 released 状态,
+    # 需要先 load 才能执行 search/query(同一进程内创建/插入数据后会自动处于 loaded 状态,无需重复 load)。
+    client.load_collection(collection_name='demo_v2')
+
+    # # todo: 1. 单向量搜索
+    res = client.search(collection_name='demo_v2',
+                        data=[[0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104]],
+                        limit=2,
+                        search_params={"metric_type": "IP"},
+                        output_fields=["id", 'vector']) # search_params是在查询时执行距离计算方式,如果定义索引的时候,已经制定了方式可以不写
+    print(res)
+    # todo: 2. 批量向量搜索
+    res = client.search(collection_name='demo_v2',
+                        data=[[0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104],
+                              [0.3172005263489739, 0.9719044792798428, -0.36981146090600725, -0.4860894583077995, 0.95791889146345]],
+                        limit=2,
+                        search_params={"metric_type": "IP"},
+                        output_fields=["id", 'vector']) # search_params是在查询时执行距离计算方式,如果定义索引的时候,已经制定了方式可以不写
+    print(res)
+    # todo: 3. 分区搜索
+    # 要进行分区搜索,只需在搜索请求的 partition_names 中包含目标分区的名称即可。这指定search操作仅考虑指定分区内的向量。
+    res = client.search(
+        collection_name="demo_v2",
+        data=[[0.02174828545444263, 0.058611125483182924, 0.6168633415965343, -0.7944160935612321, 0.5554828317581426]],
+        limit=5,
+        search_params={"metric_type": "IP", "params": {}},
+        partition_names=["partitionA"]  # 这里指定搜索的分区
+    )
+    print(res)
+    # todo: 4.使用输出字段进行搜索
+    # 使用输出字段进行搜索允许您指定搜索结果中应包含匹配向量的哪些属性或字段。
+    res = client.search(
+        collection_name="demo_v2",
+        data=[[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]],
+        limit=5,
+        search_params={"metric_type": "IP", "params": {}},
+        output_fields=['vector', "color"]  # 返回定义的字段
+    )
+    print(res)
+    # todo: 5.过滤搜索
+    # 过滤器搜索:筛选搜索将标量筛选器应用于矢量搜索,允许我们根据特定条件优化搜索结果。
+    # 例如,要根据字符串模式优化搜索结果,可以使用 like 运算符。此运算符通过考虑前缀、中缀和后缀来启用字符串匹配:
+    # 筛选颜色以红色为前缀的结果:
+    res = client.search(
+        collection_name="demo_v2",
+        data=[[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]],
+        limit=5,
+        search_params={"metric_type": "IP", "params": {}},
+        output_fields=["color"],
+        filter='color like "red%"'
+    )
+    print(res)
+    # todo: 6.范围搜索
+    # 范围搜索允许查找距查询向量指定距离范围内的向量。
+    # 范围搜索:radius:定义搜索空间的外边界。只有距查询向量在此距离内的向量才被视为潜在匹配。
+    # range_filter:虽然radius设置搜索的外部限制,但可以选择使用range_filter来定义内部边界,创建一个距离范围,在该范围内向量必须落下才被视为匹配。
+    search_params = {
+        "metric_type": "IP",
+        "params": {
+            "radius": 0.8,  # 搜索圆的半径
+            "range_filter": 1  # 范围过滤器,用于过滤出不在搜索圆内的向量。
+        }
+    }
+
+    res = client.search(
+        collection_name="demo_v2",
+        data=[[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]],
+        limit=3,  # 返回的搜索结果最大数量
+        search_params=search_params,
+        output_fields=["color"],
+    )
+    #
+    #
+    print(res)
+    result = json.dumps(res, indent=4)
+    print(result)
+
+
+if __name__ == '__main__':
+    query_operation()

+ 91 - 0
2.3_Milvus向量数据库/05_hybrid_search_rerank.py

@@ -0,0 +1,91 @@
+import random
+
+from pymilvus import AnnSearchRequest, DataType, MilvusClient, RRFRanker, WeightedRanker
+
+client = MilvusClient(uri="http://localhost:19530")
+
+
+def demo_rankers():
+    # 加权排名:为某些向量场分配更高的权重,从而更加强调这些向量场。
+    # 输入值的范围应为 [0,1],数值越接近 1 表示重要性越高。
+    rerank = WeightedRanker(0.8, 0.3)
+    print(f'WeightedRanker-->{rerank}')
+
+    # RRFRanker(倒数排序融合):没有特定重点时使用,k 的默认值为 60,用于控制随着排名增加分数的降低速度。
+    ranker = RRFRanker(100)
+    print(f'RRFRanker-->{ranker}')
+
+
+def complex_query():
+    # 脚本可重复运行:先清理上一次留下的 collection
+    if client.has_collection(collection_name='demo_v3'):
+        client.drop_collection(collection_name='demo_v3')
+
+    # # 定义schema
+    schema = client.create_schema(enable_dynamic_field=False)
+    schema.add_field(field_name='film_id', datatype=DataType.INT64, is_primary=True)
+    schema.add_field(field_name='filmVector', datatype=DataType.FLOAT_VECTOR, dim=5)  # 向量字段
+    schema.add_field(field_name="posterVector", datatype=DataType.FLOAT_VECTOR, dim=5)  # 向量字段
+    # #
+    # 定义索引
+    index_params = client.prepare_index_params()
+    index_params.add_index(field_name='filmVector', index_type="IVF_FLAT",
+                           metric_type="L2", params={"nlist": 128})
+    # index_type="",  # 留空以使用自动索引 —— 当前 pymilvus/Milvus Lite 版本要求显式指定 index_type,这里用 'AUTOINDEX' 代替课件里的空字符串写法
+    index_params.add_index(field_name='posterVector', index_type="AUTOINDEX",
+                           metric_type="COSINE")
+
+    # 创建集合
+    client.create_collection(collection_name='demo_v3', schema=schema, index_params=index_params)
+
+    # 向量库中插入实体
+    entities = []
+    for _ in range(1000):
+        # 构造实体
+        film_id = random.randint(1, 10000)
+        film_vector = [random.random() for _ in range(5)]
+        poster_vector = [random.random() for _ in range(5)]
+        entity = {"film_id": film_id, "filmVector": film_vector, "posterVector": poster_vector}
+        entities.append(entity)
+
+    client.insert(collection_name='demo_v3', data=entities)
+
+    # 多向量查询(注意和批量向量查询不同)
+    # 多向量搜索使用 hybrid_search() API 在一次调用中执行多个 ANN 搜索请求。每个 AnnSearchRequest 代表特定矢量场上的单个搜索请求。
+    # 示例创建两个 AnnSearchRequest 实例以对两个向量字段执行单独的相似性搜索。
+    # 创建多搜索请求 filmVector
+    query_filmVector = [[0.8896863042430693, 0.370613100114602, 0.23779315077113428, 0.38227915951132996, 0.5997064603128835]]
+    dense_search_params = {"data": query_filmVector,
+                           "anns_field": "filmVector",  # 该参数值必须与集合模式中使用的值相同。
+                           "param": {"metric_type": "L2", "nprobe": 10},  # nprobe代表访问簇的数量
+                           "limit": 2}
+    request_1 = AnnSearchRequest(**dense_search_params)
+
+    # 创建多搜索请求 posterVector
+    query_posterVector = [[0.02550758562349764, 0.006085637357292062, 0.5325251250159071, 0.7676432650114147, 0.5521074424751443]]
+    sparse_search_params = {"data": query_posterVector,
+                            "anns_field": "posterVector",
+                            # 该参数值必须与集合模式中使用的值相同。
+                            "param": {"metric_type": "COSINE"},
+                            "limit": 2
+                            }
+    request_2 = AnnSearchRequest(**sparse_search_params)
+
+    reqs = [request_1, request_2]
+    ranker = RRFRanker(100)
+
+    res = client.hybrid_search(
+        collection_name="demo_v3",
+        reqs=reqs,
+        ranker=ranker,
+        limit=2
+    )
+    for hits in res:
+        print("TopK results:")
+        for hit in hits:
+            print(hit)
+
+
+if __name__ == '__main__':
+    demo_rankers()
+    complex_query()

+ 19 - 0
2.3_Milvus向量数据库/06_load_and_drop_collection.py

@@ -0,0 +1,19 @@
+# 4 加载现有数据
+# 由于 Milvus Lite 的所有数据都存储在本地文件中,因此即使在程序终止后,你也可以通过创建一个带有现有文件的 MilvusClient,
+# 将所有数据加载到内存中。例如,这将恢复 "milvus_demo.db" 文件中的 Collections,并继续向其中写入数据。
+# 注意:本脚本依赖 demo_v2 集合已存在,请先运行 03_entity_insert_update_delete.py
+from pymilvus import MilvusClient
+
+client = MilvusClient("http://localhost:19530")
+
+print(f'当前collections-->{client.list_collections()}')
+print("加载已有数据:")
+# 新建的 MilvusClient 连接同一个库文件后,集合默认是 released 状态,需要先 load 才能查询
+client.load_collection(collection_name='demo_v2')
+print(client.query(collection_name='demo_v2', filter='id in [0, 1]', output_fields=["id", "color"]))
+
+# 5 删除 Collections
+# 如果想删除某个 Collections 中的所有数据,可以通过以下方法丢弃该 Collections
+print("删除collection:")
+client.drop_collection(collection_name="demo_v2")
+print(f'删除后collections-->{client.list_collections()}')

+ 38 - 0
2.3_Milvus向量数据库/README.md

@@ -0,0 +1,38 @@
+# 2.3 Milvus向量数据库
+
+本目录对应课件 `② 项目工具/2.4 Milvus向量数据库`,代码内容与课件保持一致(仅针对当前环境的 pymilvus 版本做了少量兼容性修正,见下方说明)。
+
+- `01_operate_db.py`: 设置向量数据库(`operate_db`)。
+- `02_collection_operations.py`: Collection 与索引操作(`operate_table`)。
+- `03_entity_insert_update_delete.py`: Entity 增、删、改(`operate_entity`)。
+- `04_entity_query.py`: 单向量、批量、分区、过滤、范围搜索(`query_operation`)。
+- `05_hybrid_search_rerank.py`: 多向量混合检索与 RRF/加权重排(`demo_rankers` + `complex_query`)。
+- `06_load_and_drop_collection.py`: 加载已有数据与删除 Collection。
+
+示例默认使用 Milvus Lite,本地数据库文件为当前目录下的 `milvus_demo.db`(首次运行会自动创建)。
+
+## 运行方式
+
+请使用 `EnterpriseKB-RAG` conda 环境,并**按顺序**依次运行 01 ~ 06(04 依赖 03 写入的数据,06 依赖 03/04 使用的 `demo_v2` 集合):
+
+```bash
+conda activate EnterpriseKB-RAG
+python 01_operate_db.py
+python 02_collection_operations.py
+python 03_entity_insert_update_delete.py
+python 04_entity_query.py
+python 05_hybrid_search_rerank.py
+python 06_load_and_drop_collection.py
+```
+
+每个脚本都会在开头清理自己创建的 collection(`has_collection` + `drop_collection`),所以可以重复运行整套流程。
+
+## 相对课件代码的兼容性修正
+
+当前环境安装的 `pymilvus==3.0.0`(对应 `milvus-lite==3.2.1`)比课件编写时用的版本更新,以下两处课件写法在新版本下会报错,已做调整:
+
+1. **索引类型不能留空字符串**:课件中 `index_type=''`(留空表示自动索引)在新版本会报 `missing required 'index_type' parameter`,已改为向量字段用 `'AUTOINDEX'`、标量字段用 `'INVERTED'`。
+2. **自定义 `index_name` 不生效**:新版本里实际生效的索引名固定为字段名(课件里自定义的 `vector_index`/`default_index` 只是入参,服务端仍以字段名命名索引),因此 `describe_index` 前先用 `list_indexes(field_name=...)` 取真实的索引名,避免 "多个索引,请指定 index_name" 的报错。
+3. **跨进程查询前需要 `load_collection`**:Milvus Lite 每次新建 `MilvusClient` 连接同一个库文件时,之前写入的 collection 默认是 `released` 状态(只有同一进程内创建/插入数据后才会自动处于加载状态)。因此 `04_entity_query.py`、`06_load_and_drop_collection.py` 在查询前显式调用了 `client.load_collection(...)`。
+
+除以上三点,其余代码逻辑、数据、注释均与课件保持一致。

+ 139 - 0
3.1 logging日志/log.py

@@ -0,0 +1,139 @@
+    # import logging
+
+    # # 配置基本的日志设置
+    # logging.basicConfig(level=logging.INFO)
+
+    # # 获取日志记录器
+    # logger = logging.getLogger("Example1")
+
+    # # 记录不同级别的日志
+    # logger.debug("这是调试信息,通常用于开发")
+    # logger.info("程序运行正常")
+    # logger.warning("注意,可能有小问题")
+    # logger.error("发生错误")
+    # logger.critical("严重错误,程序可能崩溃")
+    # import logging
+# import logging
+
+# # 配置日志格式
+# logging.basicConfig(
+#     level=logging.DEBUG,
+#     format='%(asctime)s - %(levelname)s - %(message)s'
+# )
+
+# # 获取日志记录器
+# logger = logging.getLogger("Example2")
+
+# # 记录日志
+# logger.debug("调试模式已开启")
+# logger.info("正在处理数据")
+# logger.error("数据处理失败")
+
+# import logging
+
+# # 配置日志,输出到文件
+# logging.basicConfig(
+#     level=logging.INFO,
+#     format='%(asctime)s - %(levelname)s - %(message)s',
+#     filename='/Users/Admin/Documents/AI教育/2-企业知识库RAG项目/代码/logs/app.log',  # 日志文件路径
+#     filemode='a'         # 'a'表示追加,'w'表示覆盖
+# )
+
+# # 获取日志记录器
+# logger = logging.getLogger("Example3")
+
+# # 记录日志
+# logger.info("程序启动")
+# logger.warning("内存使用率较高")
+# logger.error("无法连接数据库")
+
+import logging
+
+# 创建日志记录器
+logger = logging.getLogger("Example4")
+logger.setLevel(logging.DEBUG)  # 设置记录器级别
+
+# 创建控制台处理器
+console_handler = logging.StreamHandler()
+console_handler.setLevel(logging.INFO)  # 控制台显示INFO及以上级别
+
+# 创建文件处理器
+file_handler = logging.FileHandler('/Users/Admin/Documents/AI教育/2-企业知识库RAG项目/代码/app.log', mode='a')
+file_handler.setLevel(logging.DEBUG)  # 文件记录DEBUG及以上级别
+
+# 定义日志格式
+formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
+
+# 为处理器设置格式
+console_handler.setFormatter(formatter)
+file_handler.setFormatter(formatter)
+
+# 将处理器添加到记录器
+logger.addHandler(console_handler)
+logger.addHandler(file_handler)
+
+# 记录日志
+logger.debug("调试信息,仅写入文件")
+logger.info("程序运行正常")
+logger.error("发生错误")
+# # # 配置日志格式
+# # logging.basicConfig(
+# #     level=logging.DEBUG,
+# #     format='%(asctime)s - %(levelname)s - %(message)s'
+# # )
+
+# # # 获取日志记录器
+# # logger = logging.getLogger("Example2")
+
+# # # 记录日志
+# # logger.debug("调试模式已开启")
+# # logger.info("正在处理数据")
+# # logger.error("数据处理失败")
+
+# # import logging
+
+# # # 配置日志,输出到文件
+# # logging.basicConfig(
+# #     level=logging.INFO,
+# #     format='%(asctime)s - %(levelname)s - %(message)s',
+# #     filename='app.log',  # 日志文件路径
+# #     filemode='a'         # 'a'表示追加,'w'表示覆盖
+# # )
+
+# # # 获取日志记录器
+# # logger = logging.getLogger("Example3")
+
+# # # 记录日志
+# # logger.info("程序启动")
+# # logger.warning("内存使用率较高")
+# # logger.error("无法连接数据库")
+
+# import logging
+
+# # 创建日志记录器
+# logger = logging.getLogger("Example4")
+# logger.setLevel(logging.DEBUG)  # 设置记录器级别
+
+# # 创建控制台处理器
+# console_handler = logging.StreamHandler()
+# console_handler.setLevel(logging.INFO)  # 控制台显示INFO及以上级别
+
+# # 创建文件处理器
+# file_handler = logging.FileHandler('logs/app.log', mode='a')
+# file_handler.setLevel(logging.DEBUG)  # 文件记录DEBUG及以上级别
+
+# # 定义日志格式
+# formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
+
+# # 为处理器设置格式
+# console_handler.setFormatter(formatter)
+# file_handler.setFormatter(formatter)
+
+# # 将处理器添加到记录器
+# logger.addHandler(console_handler)
+# logger.addHandler(file_handler)
+
+# # 记录日志
+# logger.debug("调试信息,仅写入文件")
+# logger.info("程序运行正常")
+# logger.error("发生错误")

+ 2 - 0
3.2 redis_lesson/base/__init__.py

@@ -0,0 +1,2 @@
+from base.config import *
+from base.logger import *

+ 89 - 0
3.2 redis_lesson/base/config.py

@@ -0,0 +1,89 @@
+# 导入配置解析库
+import configparser
+# 导入路径操作库
+import os
+
+
+class Config:
+    # 初始化配置,加载 config.ini 文件
+    def __init__(self, config_file=None):
+        project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+        if config_file is None:
+            config_file = os.path.join(project_root, "config.ini")
+        # 创建配置解析器
+        self.config = configparser.ConfigParser()
+        # 读取配置文件
+        self.config.read(config_file)  # windows包gbk错误,增加encoding=utf-8
+
+        # MySQL 配置
+        # MySQL 主机地址
+        # 获取section 是mysql host变量失败返回localhost
+        self.MYSQL_HOST = "localhost"
+
+        # MySQL 用户名
+        self.MYSQL_USER = self.config.get('mysql', 'user', fallback='root')
+        # MySQL 密码
+        self.MYSQL_PASSWORD = self.config.get('mysql', 'password', fallback='')
+        # MySQL 数据库名
+        self.MYSQL_DATABASE = self.config.get('mysql', 'database', fallback='enterprise_kb')
+
+        # Redis 配置
+        # Redis 主机地址
+        self.REDIS_HOST = self.config.get('redis', 'host', fallback='localhost')
+        # Redis 端口
+        self.REDIS_PORT = self.config.getint('redis', 'port', fallback=6379)
+        # Redis 密码
+        self.REDIS_PASSWORD = self.config.get('redis', 'password', fallback='')
+        # Redis 数据库编号
+        self.REDIS_DB = self.config.getint('redis', 'db', fallback=0)
+
+        # Milvus 配置
+        # Milvus 主机地址
+        self.MILVUS_HOST = self.config.get('milvus', 'host', fallback='localhost')
+        # Milvus 端口
+        self.MILVUS_PORT = self.config.get('milvus', 'port', fallback='19530')
+        # Milvus 数据库名
+        self.MILVUS_DATABASE_NAME = self.config.get('milvus', 'database_name', fallback='enterprise_kb')
+        # Milvus 集合名
+        self.MILVUS_COLLECTION_NAME = self.config.get('milvus', 'collection_name', fallback='enterprise_kb_rag_final')
+
+        # LLM 配置
+        # LLM 模型名
+        self.LLM_MODEL = self.config.get('llm', 'model', fallback='qwen-plus')
+        # DashScope API 密钥
+        self.DASHSCOPE_API_KEY = os.getenv("API_KEY") or self.config.get('llm', 'dashscope_api_key', fallback=None)
+        # DashScope API 地址
+        self.DASHSCOPE_BASE_URL = self.config.get('llm', 'dashscope_base_url',
+                                                  fallback='https://dashscope.aliyuncs.com/compatible-mode/v1')
+
+        # 检索参数
+        # 父块大小
+        self.PARENT_CHUNK_SIZE = self.config.getint('retrieval', 'parent_chunk_size', fallback=1200)
+        # 子块大小
+        self.CHILD_CHUNK_SIZE = self.config.getint('retrieval', 'child_chunk_size', fallback=300)
+        # 块重叠大小
+        self.CHUNK_OVERLAP = self.config.getint('retrieval', 'chunk_overlap', fallback=50)
+        # 检索返回数量
+        self.RETRIEVAL_K = self.config.getint('retrieval', 'retrieval_k', fallback=5)
+        # 最终候选数量
+        self.CANDIDATE_M = self.config.getint('retrieval', 'candidate_m', fallback=2)
+
+        # 应用配置
+        # 有效来源列表
+        self.VALID_SOURCES = eval(
+            self.config.get('app', 'valid_sources', fallback='["hr", "it", "finance", "legal", "product", "ops", "security"]'))
+        # 客服电话
+        self.CUSTOMER_SERVICE_PHONE = self.config.get('app', 'customer_service_phone', fallback='12345678')
+        # 日志文件路径
+        self.LOG_FILE = self.config.get('logger', 'log_file', fallback='logs/app.log')
+
+        # model path
+        # self.bge_m3 = os.path.join(project_root, "rag_qa/models/bge-m3")
+        # self.bge_reranker = os.path.join(project_root, "rag_qa/models/bge-reranker-large")
+        self.nlp_bert_doc_seg = os.path.join(project_root, "rag_qa/models/nlp_bert_document-segmentation_chinese-base")
+        self.bert_intent_cls = os.path.join(project_root, "rag_qa/core/bert_query_classifier")
+
+
+if __name__ == '__main__':
+    conf = Config()
+    print(conf.LOG_FILE)

+ 65 - 0
3.2 redis_lesson/base/logger.py

@@ -0,0 +1,65 @@
+# 导入日志库
+import logging
+# 导入路径操作库
+import os
+# 导入配置类
+from base.config import Config
+
+
+def setup_logging(log_file=Config().LOG_FILE):
+    # 创建日志目录
+    os.makedirs(os.path.dirname(log_file), exist_ok=True)
+    # 获取日志器
+    logger = logging.getLogger("EnterpriseKB-RAG")
+    # 设置日志级别
+    logger.setLevel(logging.INFO)
+    # 避免重复添加处理器
+    if not logger.handlers:
+        # 创建文件处理器
+        file_handler = logging.FileHandler(log_file, encoding='utf-8')
+        # 设置文件处理器级别
+        file_handler.setLevel(logging.INFO)
+        # 创建控制台处理器
+        console_handler = logging.StreamHandler()
+        # 设置控制台处理器级别
+        console_handler.setLevel(logging.INFO)
+        # 设置日志格式
+        formatter = logging.Formatter(
+            '%(asctime)s - %(pathname)s[line:%(lineno)d] - %(name)s - %(levelname)s - %(message)s')
+        # 为文件处理器设置格式
+        file_handler.setFormatter(formatter)
+        # 为控制台处理器设置格式
+        console_handler.setFormatter(formatter)
+        # 添加文件处理器
+        logger.addHandler(file_handler)
+        # 添加控制台处理器
+        logger.addHandler(console_handler)
+    # 返回日志器
+    return logger
+
+
+# 初始化日志器
+logger = setup_logging()
+
+
+def process_data(data):
+    logger.debug(f"开始处理数据: {data}")
+    if not data:
+        logger.error("数据为空,无法处理")
+        return None
+    logger.info("数据处理完成")
+    return data.upper()
+
+
+def main():
+    logger.info("程序启动")
+    result = process_data("hello")
+    if result:
+        logger.info(f"处理结果: {result}")
+    else:
+        logger.warning("处理失败")
+    logger.info("程序结束")
+
+
+if __name__ == "__main__":
+    main()

+ 8 - 0
3.2 redis_lesson/config.example.ini

@@ -0,0 +1,8 @@
+[redis]
+host = localhost
+port = 6379
+password = change_me_redis_password
+db = 0
+
+[logger]
+log_file = logs/app.log

+ 32 - 0
3.2 redis_lesson/main.py

@@ -0,0 +1,32 @@
+from redis_client import RedisClient
+import logging
+
+# 配置日志
+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
+logger = logging.getLogger(__name__)
+
+def main():
+    # 初始化 Redis 客户端
+    redis_client = RedisClient()
+    # 示例数
+    key = "answer:user:1"
+    value = {"name": "Alice", "age": 25}
+    # 存储数据
+    # {"user:1":{"name": "Alice", "age": 25}}
+    redis_client.set_data(key, value)
+    # 获取数据
+    result = redis_client.get_data(key)
+    if result:
+        logger.info(f"查询结果: {result}")
+    else:
+        logger.info("未找到数据")
+    # 示例查询缓存
+    query = "user:1"
+    answer = redis_client.get_answer(query)
+    if answer:
+        logger.info(f"缓存答案: {answer}")
+    else:
+        logger.info("未找到缓存答案")
+
+if __name__ == "__main__":
+    main()

+ 46 - 0
3.2 redis_lesson/redis_client.py

@@ -0,0 +1,46 @@
+import redis
+import json
+from base import Config, logger
+
+class RedisClient:
+    def __init__(self):
+        self.logger = logger
+        try:
+            self.client = redis.StrictRedis(
+                host=Config().REDIS_HOST,
+                port=Config().REDIS_PORT,
+                password=Config().REDIS_PASSWORD,
+                db=Config().REDIS_DB,
+                decode_responses=True
+            )
+            self.logger.info("Redis 连接成功")
+        except redis.RedisError as e:
+            self.logger.error(f"Redis 连接失败: {e}")
+            raise
+
+    def set_data(self, key, value):
+        try:
+            self.client.set(key, json.dumps(value))
+            self.logger.info(f"存储数据到 Redis: {key}")
+        except redis.RedisError as e:
+            self.logger.error(f"Redis 存储失败: {e}")
+
+    def get_data(self, key):
+        try:
+            data = self.client.get(key)
+            return json.loads(data) if data else None
+        except redis.RedisError as e:
+            self.logger.error(f"Redis 获取失败: {e}")
+            return None
+
+    def get_answer(self, query):
+        try:
+            answer = self.client.get(f"answer:{query}")
+            if answer:
+                self.logger.info(f"从 Redis 获取答案: {query}")
+                return answer
+            return None
+        except redis.RedisError as e:
+            self.logger.error(f"Redis 查询失败: {e}")
+            return None
+

+ 4 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/base/__init__.py

@@ -0,0 +1,4 @@
+from base.config import Config
+from base.logger import logger, setup_logging
+
+__all__ = ["Config", "logger", "setup_logging"]

+ 62 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/base/config.py

@@ -0,0 +1,62 @@
+# 导入配置解析库
+import configparser
+# 导入环境变量和路径操作库
+import os
+from pathlib import Path
+
+
+class Config:
+    # 初始化配置,加载项目根目录下的 config.ini 文件
+    def __init__(self, config_file=None):
+        project_root = Path(__file__).resolve().parent.parent
+        config_path = Path(config_file) if config_file else project_root / "config.ini"
+
+        # 创建配置解析器
+        self.config = configparser.ConfigParser()
+        # 读取配置文件
+        self.config.read(config_path, encoding="utf-8")
+
+        # MySQL 配置
+        self.MYSQL_HOST = os.getenv(
+            "MYSQL_HOST", self.config.get("mysql", "host", fallback="localhost")
+        )
+        self.MYSQL_USER = os.getenv(
+            "MYSQL_USER", self.config.get("mysql", "user", fallback="root")
+        )
+        self.MYSQL_PASSWORD = os.getenv(
+            "MYSQL_PASSWORD", self.config.get("mysql", "password", fallback="")
+        )
+        self.MYSQL_DATABASE = os.getenv(
+            "MYSQL_DATABASE",
+            self.config.get("mysql", "database", fallback="subjects_kg"),
+        )
+
+        # Redis 配置
+        self.REDIS_HOST = os.getenv(
+            "REDIS_HOST", self.config.get("redis", "host", fallback="localhost")
+        )
+        self.REDIS_PORT = int(
+            os.getenv(
+                "REDIS_PORT", self.config.getint("redis", "port", fallback=6379)
+            )
+        )
+        redis_password = os.getenv(
+            "REDIS_PASSWORD", self.config.get("redis", "password", fallback="")
+        )
+        self.REDIS_PASSWORD = redis_password or None
+        self.REDIS_DB = int(
+            os.getenv("REDIS_DB", self.config.getint("redis", "db", fallback=0))
+        )
+
+        # 日志文件路径
+        log_file = Path(
+            self.config.get("logger", "log_file", fallback="logs/app.log")
+        )
+        if not log_file.is_absolute():
+            log_file = project_root / log_file
+        self.LOG_FILE = str(log_file)
+
+
+if __name__ == "__main__":
+    conf = Config()
+    print(conf.MYSQL_DATABASE)

+ 43 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/base/logger.py

@@ -0,0 +1,43 @@
+# 导入日志库
+import logging
+# 导入路径操作库
+import os
+# 导入配置类
+from base.config import Config
+
+
+def setup_logging(log_file=Config().LOG_FILE):
+    # 创建日志目录
+    os.makedirs(os.path.dirname(log_file), exist_ok=True)
+    # 获取日志器
+    logger = logging.getLogger("EnterpriseKB-RAG")
+    # 设置日志级别
+    logger.setLevel(logging.INFO)
+    # 避免重复添加处理器
+    if not logger.handlers:
+        # 创建文件处理器
+        file_handler = logging.FileHandler(log_file, encoding="utf-8")
+        # 设置文件处理器级别
+        file_handler.setLevel(logging.INFO)
+        # 创建控制台处理器
+        console_handler = logging.StreamHandler()
+        # 设置控制台处理器级别
+        console_handler.setLevel(logging.INFO)
+        # 设置日志格式
+        formatter = logging.Formatter(
+            "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+        )
+        # 为文件处理器设置格式
+        file_handler.setFormatter(formatter)
+        # 为控制台处理器设置格式
+        console_handler.setFormatter(formatter)
+        # 添加文件处理器
+        logger.addHandler(file_handler)
+        # 添加控制台处理器
+        logger.addHandler(console_handler)
+    # 返回日志器
+    return logger
+
+
+# 初始化日志器
+logger = setup_logging()

+ 16 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/config.example.ini

@@ -0,0 +1,16 @@
+# MySQL demo configuration
+[mysql]
+host = localhost
+user = demo_user
+password = change_me_mysql_password
+database = enterprise_kb
+
+# Redis demo configuration
+[redis]
+host = localhost
+port = 6379
+password = change_me_redis_password
+db = 0
+
+[logger]
+log_file = logs/app.log

+ 1 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/__init__.py

@@ -0,0 +1 @@
+"""MySQL FAQ 问答系统。"""

+ 1 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/cache/__init__.py

@@ -0,0 +1 @@
+"""Redis 缓存模块。"""

+ 56 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/cache/redis_client.py

@@ -0,0 +1,56 @@
+# 导入 Redis 客户端
+import redis
+# 导入 JSON 处理
+import json
+# 导入配置和日志
+from base import Config, logger
+
+
+class RedisClient:
+    def __init__(self):
+        # 初始化日志
+        self.logger = logger
+        try:
+            # 连接 Redis
+            self.client = redis.StrictRedis(
+                host=Config().REDIS_HOST,
+                port=Config().REDIS_PORT,
+                password=Config().REDIS_PASSWORD,
+                db=Config().REDIS_DB,
+                decode_responses=True,
+            )
+            # 记录连接成功
+            self.logger.info("Redis 连接成功")
+        except redis.RedisError as exc:
+            # 记录连接失败
+            self.logger.error(f"Redis 连接失败: {exc}")
+            raise
+
+    def set_data(self, key, value):
+        # 存储数据到 Redis
+        try:
+            self.client.set(key, json.dumps(value, ensure_ascii=False))
+            self.logger.info(f"存储数据到 Redis: {key}")
+        except redis.RedisError as exc:
+            self.logger.error(f"Redis 存储失败: {exc}")
+
+    def get_data(self, key):
+        # 从 Redis 获取数据
+        try:
+            data = self.client.get(key)
+            return json.loads(data) if data else None
+        except (redis.RedisError, json.JSONDecodeError) as exc:
+            self.logger.error(f"Redis 获取失败: {exc}")
+            return None
+
+    def get_answer(self, query):
+        # 获取查询的缓存答案
+        answer = self.get_data(f"answer:{query}")
+        if answer:
+            self.logger.info(f"从 Redis 获取答案: {query}")
+        return answer
+
+
+if __name__ == "__main__":
+    redis_client = RedisClient()
+    print(redis_client)

+ 31 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/data/JP企业知识库问答.csv

@@ -0,0 +1,31 @@
+知识域,问题,答案
+hr,年假如何申请?,员工可在OA系统提交年假申请,选择请假类型、起止时间和交接人;直属负责人审批通过后自动同步考勤。
+hr,试用期转正需要哪些材料?,需要提交转正申请、试用期工作总结、直属负责人评价和关键目标完成情况,HR在审批完成后更新员工状态。
+hr,员工福利包括哪些?,员工福利包含五险一金、补充商业保险、年度体检、节日福利、团建活动和带薪年假,具体标准以当年制度为准。
+hr,病假需要上传什么证明?,连续病假超过一天需上传医院诊断证明或病假单;短时病假可先提交申请,返岗后补充证明。
+hr,入职当天需要办理哪些事项?,入职当天需完成身份核验、合同签署、工牌领取、邮箱开通、系统权限申请和新人指引确认。
+finance,差旅报销需要哪些附件?,差旅报销需上传审批单、发票、行程单、酒店水单和费用明细;超标准费用需补充说明并由部门负责人确认。
+finance,发票抬头和税号在哪里查看?,发票抬头、税号和开票信息可在财务知识库的“开票信息”页面查看,报销前请确认发票信息完整。
+finance,采购付款流程是什么?,采购付款需先完成采购申请、合同归档、验收入库和发票校验,再由财务按付款计划处理。
+finance,餐补报销有什么限制?,餐补报销按城市和出差类型执行不同标准,超过标准部分默认不予报销,特殊情况需提前审批。
+finance,费用单被驳回怎么办?,费用单被驳回后,请根据驳回原因补充附件或修改金额,再重新提交;如对规则有疑问可联系财务支持。
+it,VPN无法连接怎么办?,请先确认网络可用、账号未过期并安装最新客户端;仍无法连接时提交IT工单,附上错误截图和设备信息。
+it,邮箱密码忘记如何重置?,可在统一身份认证平台发起密码重置;若绑定手机不可用,请提交账号恢复工单并进行身份验证。
+it,如何申请系统权限?,在权限中心选择目标系统和角色,填写申请原因、使用周期和数据范围,经直属负责人及系统负责人审批后生效。
+it,电脑蓝屏或无法开机怎么处理?,请先记录错误代码并避免反复强制重启;提交IT工单后由桌面支持排查硬件、驱动和系统日志。
+it,企业网盘文件误删能恢复吗?,网盘文件通常可在回收站保留30天,超过保留期需联系IT管理员确认是否存在备份。
+legal,合同审批需要哪些步骤?,合同审批需完成业务发起、法务审核、财务审核、负责人审批和盖章归档,涉及特殊条款时需补充风险说明。
+legal,NDA模板在哪里下载?,标准NDA模板可在法务知识库下载,使用前需确认主体信息、保密范围和有效期限。
+legal,客户要求修改责任条款怎么办?,涉及赔偿上限、违约责任、数据安全或知识产权的条款变更,必须提交法务复审。
+legal,合同归档有什么要求?,已签署合同需上传扫描件、审批记录和关键附件,归档名称应包含客户名称、合同编号和签署日期。
+ops,项目立项流程是什么?,项目立项需要提交业务目标、范围说明、预算、人力计划和风险评估,经项目委员会审批后生成项目编号。
+ops,跨部门协作如何发起?,跨部门协作可在协作平台创建任务,明确目标、负责人、截止时间和交付物,并同步相关部门负责人。
+ops,SOP变更如何通知相关人员?,SOP变更需在知识库提交版本更新,变更说明通过公告和订阅通知推送给受影响团队。
+ops,知识库文档过期如何处理?,文档负责人需在到期前完成复核;过期未复核的文档会被标记为待确认,问答系统会降低其引用优先级。
+product,产品参数表在哪里查看?,产品参数表存放在产品知识库,按产品线、版本号和发布日期归档,引用时应优先使用最新正式版本。
+product,客户常见故障如何定位?,先确认产品版本、故障现象、复现步骤和日志信息,再按故障排查SOP定位配置、网络、权限或服务状态问题。
+product,售后问题如何升级?,一级支持无法解决时,应补充客户影响范围、日志、截图和已尝试方案,并升级给对应产品线支持组。
+product,产品手册更新后如何同步?,产品手册更新后需在知识库发布新版本,维护变更记录,并通知销售、交付和售后团队。
+security,敏感数据可以上传知识库吗?,不得上传明文密码、密钥、身份证号、银行卡号等敏感信息;必要时应脱敏后再提交。
+security,发现账号异常登录怎么办?,请立即修改密码、退出所有会话,并提交安全事件工单,安全团队会检查登录日志和风险范围。
+security,外部分享文档有什么要求?,外部分享需设置访问期限、下载权限和水印,涉及客户或公司机密的内容必须经过负责人审批。

+ 1 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/db/__init__.py

@@ -0,0 +1 @@
+"""MySQL 数据访问模块。"""

+ 118 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/db/mysql_client.py

@@ -0,0 +1,118 @@
+# 导入 MySQL 连接库
+import pymysql
+# 导入 pandas
+import pandas as pd
+import sys
+from pathlib import Path
+
+# 直接运行本文件时,把项目根目录加入模块搜索路径。
+if __package__ in (None, ""):
+    project_root = Path(__file__).resolve().parents[2]
+    sys.path.insert(0, str(project_root))
+
+# 导入配置和日志
+from base import Config, logger
+
+
+class MySQLClient:
+    def __init__(self):
+        # 初始化日志
+        self.logger = logger
+        try:
+            # 连接 MySQL 数据库
+            self.connection = pymysql.connect(
+                host=Config().MYSQL_HOST,
+                user=Config().MYSQL_USER,
+                password=Config().MYSQL_PASSWORD,
+                database=Config().MYSQL_DATABASE,
+                charset="utf8mb4",
+            )
+            # 创建游标
+            self.cursor = self.connection.cursor()
+            # 记录连接成功
+            self.logger.info("MySQL 连接成功")
+        except pymysql.MySQLError as exc:
+            # 记录连接失败
+            self.logger.error(f"MySQL 连接失败: {exc}")
+            raise
+
+    def create_table(self):
+        create_table_query = """
+        CREATE TABLE IF NOT EXISTS jpkb (
+            id INT AUTO_INCREMENT PRIMARY KEY,
+            subject_name VARCHAR(20),
+            question VARCHAR(1000),
+            answer VARCHAR(1000)
+        )
+        """
+        try:
+            self.cursor.execute(create_table_query)
+            self.connection.commit()
+            self.logger.info("表创建成功")
+        except pymysql.MySQLError as exc:
+            self.logger.error(f"表创建失败: {exc}")
+            raise
+
+    def insert_data(self, csv_path):
+        try:
+            data = pd.read_csv(csv_path)
+            records = [
+                (row["知识域"], row["问题"], row["答案"])
+                for _, row in data.iterrows()
+            ]
+            insert_query = (
+                "INSERT INTO jpkb (subject_name, question, answer) "
+                "VALUES (%s, %s, %s)"
+            )
+
+            # 删除和重新插入处于同一事务中,失败时可恢复原有数据。
+            self.cursor.execute("DELETE FROM jpkb")
+            self.cursor.executemany(insert_query, records)
+            self.connection.commit()
+            self.logger.info(f"数据刷新成功,共插入 {len(records)} 条")
+        except Exception as exc:
+            self.logger.error(f"数据插入失败: {exc}")
+            self.connection.rollback()
+            raise
+
+    def fetch_questions(self):
+        # 获取所有问题
+        try:
+            self.cursor.execute("SELECT question FROM jpkb")
+            results = self.cursor.fetchall()
+            self.logger.info("成功获取问题")
+            return results
+        except pymysql.MySQLError as exc:
+            self.logger.error(f"查询失败: {exc}")
+            return []
+
+    def fetch_answer(self, question):
+        # 获取指定问题的答案
+        try:
+            self.cursor.execute(
+                "SELECT answer FROM jpkb WHERE question=%s", (question,)
+            )
+            result = self.cursor.fetchone()
+            return result[0] if result else None
+        except pymysql.MySQLError as exc:
+            self.logger.error(f"答案获取失败: {exc}")
+            return None
+
+    def close(self):
+        # 关闭数据库连接
+        try:
+            self.cursor.close()
+            self.connection.close()
+            self.logger.info("MySQL 连接已关闭")
+        except pymysql.MySQLError as exc:
+            self.logger.error(f"关闭连接失败: {exc}")
+
+
+if __name__ == "__main__":
+    mysql_client = MySQLClient()
+    try:
+        mysql_client.create_table()
+        data_file = Path(__file__).resolve().parents[1] / "data" / "JP企业知识库问答.csv"
+        mysql_client.insert_data(data_file)
+    finally:
+        mysql_client.close()

+ 62 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/main.py

@@ -0,0 +1,62 @@
+# 在项目根目录运行:python -m mysql_qa.main
+# 也支持直接运行:python mysql_qa/main.py
+import sys
+import time
+from pathlib import Path
+
+if __package__ in (None, ""):
+    sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
+
+# 导入 MySQL 客户端
+from mysql_qa.db.mysql_client import MySQLClient
+# 导入 Redis 客户端
+from mysql_qa.cache.redis_client import RedisClient
+# 导入 BM25 搜索
+from mysql_qa.retrieval.bm25_search import BM25Search
+# 导入日志
+from base import logger
+
+
+class MySQLQASystem:
+    def __init__(self):
+        self.logger = logger
+        self.mysql_client = MySQLClient()
+        self.redis_client = RedisClient()
+        self.bm25_search = BM25Search(self.redis_client, self.mysql_client)
+
+    def query(self, query):
+        start_time = time.time()
+        self.logger.info(f"处理查询: '{query}'")
+        answer, _ = self.bm25_search.search(query, threshold=0.85)
+        if answer:
+            self.logger.info(f"MySQL 答案: {answer}")
+        else:
+            self.logger.info("SQL 中未找到答案,需要调用 RAG 系统")
+            answer = "SQL未找到答案"
+        processing_time = time.time() - start_time
+        self.logger.info(f"查询处理耗时 {processing_time:.2f}秒")
+        return answer
+
+
+def main():
+    mysql_system = MySQLQASystem()
+    try:
+        print("\n欢迎使用 MySQL 问答系统!")
+        print("输入查询进行问答,输入 'exit' 退出。")
+        while True:
+            query = input("\n输入查询: ").strip()
+            if query.lower() == "exit":
+                logger.info("退出 MySQL 系统")
+                print("再见!")
+                break
+            answer = mysql_system.query(query)
+            print(f"\n答案: {answer}")
+    except Exception as exc:
+        logger.error(f"系统错误: {exc}")
+        print(f"发生错误: {exc}")
+    finally:
+        mysql_system.mysql_client.close()
+
+
+if __name__ == "__main__":
+    main()

+ 1 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/retrieval/__init__.py

@@ -0,0 +1 @@
+"""FAQ 检索模块。"""

+ 152 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/retrieval/bm25_search.py

@@ -0,0 +1,152 @@
+# 导入 BM25 算法
+from rank_bm25 import BM25Okapi
+# 导入数值计算库
+import numpy as np
+# 导入文本预处理
+from mysql_qa.utils.preprocess import preprocess_text
+# 导入日志
+from base import logger
+
+
+class BM25Search:
+    def __init__(self, redis_client, mysql_client):
+        # 初始化日志与数据客户端
+        self.logger = logger
+        self.redis_client = redis_client
+        self.mysql_client = mysql_client
+        self.bm25 = None
+        self.questions = None
+        self.original_questions = None
+        self._load_data()
+
+    def _load_data(self):
+        # Redis 键的数据结构:str
+        original_key = "qa_original_questions"
+        tokenized_key = "qa_tokenized_questions"
+
+        # Redis 中的值由 set_data() 使用 json.dumps() 序列化,因此实际存储的是 JSON 字符串:
+        # qa_original_questions -> '["如何修改密码?", "如何申请退款?"]'
+        # qa_tokenized_questions -> '[["如何", "修改密码", "?"], ["如何", "申请", "退款", "?"]]'
+        # get_data() 使用 json.loads() 反序列化后,恢复为下面对应的 Python 列表结构。
+
+        # 优先从 Redis 获取问题及其分词结果
+        # 数据结构:list[str] | None
+        # 示例:["如何修改密码?", "如何申请退款?"]
+        # {"qa_original_questions":""}
+        # {"qa_tokenized_questions":""}
+        self.original_questions = self.redis_client.get_data(original_key)
+
+        # 数据结构:list[list[str]] | None
+        # 示例:[["如何", "修改密码", "?"], ["如何", "申请", "退款", "?"]]
+        tokenized_questions = self.redis_client.get_data(tokenized_key)
+
+        # Redis 中没有数据时,从 MySQL 加载
+        if not self.original_questions or not tokenized_questions:
+            # 默认 PyMySQL Cursor.fetchall() 的数据结构:tuple[tuple[str], ...]
+            # 因为 SQL 只查询 question 一列,所以每一行都是仅包含一个问题字符串的元组
+            # 示例:rows = (("如何修改密码?",), ("如何申请退款?",), ("忘记密码怎么办?",))
+            rows = self.mysql_client.fetch_questions()
+            if not rows:
+                self.logger.warning("未加载到问题")
+                return
+
+            # 逐行取出第 1 列的字符串,将“元组的集合”转换成“问题字符串列表”
+            # row[0]:从 ("如何修改密码?",) 中取出 "如何修改密码?"
+            # isinstance 判断用于兼容 row 为 tuple、list 或已经是字符串的情况
+            self.original_questions = [
+                row[0] if isinstance(row, (tuple, list)) else row for row in rows
+            ]
+            # 转换后的数据结构:list[str]
+            # 示例:self.original_questions = ["如何修改密码?", "如何申请退款?", "忘记密码怎么办?"]
+
+            # 遍历 list[str] 类型的原始问题,每个问题经 preprocess_text() 转成 list[str]
+            # 最终数据结构:list[list[str]],外层每个元素对应一个问题的分词结果
+            # 示例(具体分词结果由 Jieba 词典决定):
+            # self.original_questions = ["如何修改密码?", "如何申请退款?"]
+            # tokenized_questions = [["如何", "修改密码", "?"], ["如何", "申请", "退款", "?"]]
+            tokenized_questions = [
+                preprocess_text(question) for question in self.original_questions
+            ]
+            self.redis_client.set_data(original_key, self.original_questions)
+            self.redis_client.set_data(tokenized_key, tokenized_questions)
+
+        # 数据结构:list[list[str]],外层列表对应所有问题,内层列表是单个问题的分词结果
+        # 示例:[["如何", "修改", "密码"], ["如何", "申请", "退款"], ["忘记", "密码", "怎么办"]]
+        self.questions = tokenized_questions
+        # 使用全部分词问题构建 BM25 检索模型,供后续计算查询与各问题的相关性分数
+        self.bm25 = BM25Okapi(self.questions)
+        self.logger.info("BM25 模型初始化完成")
+
+    def _softmax(self, scores):
+        # scores:numpy.ndarray,形状为 (问题总数,),保存每个问题的 BM25 原始分数
+        # 示例:scores = np.array([0.3, 1.2, 3.5])
+
+        # 先减去最大值,避免指数运算时数值过大:[-3.2, -2.3, 0.0]
+        # np.exp() 会对数组中的每个元素计算自然指数 e^x,其中 e ≈ 2.71828
+        # 公式:exp_scores[i] = e ** (scores[i] - max(scores))
+        # 示例:[e^-3.2, e^-2.3, e^0] ≈ [0.041, 0.100, 1.000]
+        # exp_scores 的数据结构仍为 numpy.ndarray,形状与 scores 相同
+        exp_scores = np.exp(scores - np.max(scores))
+
+        # 返回相同形状的 numpy.ndarray;各元素之和为 1
+        # 示例:[0.036, 0.088, 0.876]
+        return exp_scores / exp_scores.sum()
+
+    def search(self, query, threshold=0.85):
+        if not query or not isinstance(query, str):
+            self.logger.error("无效查询")
+            return None, False
+
+        cached_answer = self.redis_client.get_answer(query)
+        if cached_answer:
+            return cached_answer, False
+
+        if self.bm25 is None or not self.original_questions:
+            self.logger.warning("问题库为空,无法执行检索")
+            return None, True
+
+        try:
+            # 数据结构:list[str],表示查询语句的分词结果
+            # 示例:query = "忘记密码怎么办" -> query_tokens = ["忘记", "密码", "怎么办"]
+            query_tokens = preprocess_text(query)
+
+            # 数据结构:numpy.ndarray,形状为 (问题总数,)
+            # 每个元素是查询与对应问题的 BM25 原始相关性分数
+            # 示例:scores = np.array([0.3, 1.2, 3.5])
+            scores = self.bm25.get_scores(query_tokens)
+
+            # 数据结构:numpy.ndarray,形状不变;所有元素之和为 1
+            # 与上面的示例对应:softmax_scores ≈ np.array([0.036, 0.088, 0.876])
+            softmax_scores = self._softmax(scores)
+
+            # 数据结构:numpy 整数标量(通常为 numpy.int64)
+            # argmax() 返回最大分数的位置;示例中 best_idx = 2,表示第 3 个问题
+            best_idx = softmax_scores.argmax()
+
+            # 数据结构:numpy 浮点标量(通常为 numpy.float64)
+            # 根据最佳位置取出最高分;示例中 best_score ≈ 0.876
+            best_score = softmax_scores[best_idx]
+
+            if best_score >= threshold:
+                original_question = self.original_questions[best_idx]
+                answer = self.redis_client.get_answer(original_question)
+                if answer:
+                    self.logger.info(f"从 Redis 中获取到了问题的答案: {answer}")
+                else:
+                    answer = self.mysql_client.fetch_answer(original_question)
+                    self.logger.info(f"从 MySQL 中获取到了问题的答案: {answer}")
+
+                if answer:
+                    self.redis_client.set_data(f"answer:{query}", answer)
+                    self.logger.info(
+                        f"搜索成功,Softmax 相似度: {best_score:.3f}"
+                    )
+                    return answer, False
+
+            self.logger.info(
+                f"未找到可靠答案,最高 Softmax 相似度: {best_score:.3f}"
+            )
+            return None, True
+        except Exception as exc:
+            self.logger.error(f"搜索失败: {exc}")
+            return None, True

+ 1 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/utils/__init__.py

@@ -0,0 +1 @@
+"""文本处理工具。"""

+ 16 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/mysql_qa/utils/preprocess.py

@@ -0,0 +1,16 @@
+# 导入分词库
+import jieba
+# 导入日志
+from base import logger
+
+
+def preprocess_text(text):
+    # 预处理文本
+    logger.info("开始预处理文本")
+    try:
+        # 分词并转换为小写
+        return jieba.lcut(text.lower())
+    except AttributeError as exc:
+        # 记录预处理失败
+        logger.error(f"文本预处理失败: {exc}")
+        return []

+ 6 - 0
3.3 基于Mysql库的问答系统/integrated_qa_system/requirements.txt

@@ -0,0 +1,6 @@
+pymysql
+pandas
+redis
+jieba
+rank_bm25
+numpy

+ 27 - 0
3.4 bm25_lesson/main.py

@@ -0,0 +1,27 @@
+from retrieval.bm25_search import BM25Search
+import logging
+
+# 配置日志
+logging.basicConfig(
+    level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
+)
+logger = logging.getLogger(__name__)
+
+
+def main():
+    # 示例文档集合
+    documents = ["我喜欢编程", "编程很有趣"]
+    # 初始化 BM25 检索器
+    bm25_search = BM25Search(documents)
+    # 示例查询
+    query = "他喜欢编程"
+    # 执行检索
+    result, score = bm25_search.search(query)
+    if result:
+        logger.info(f"查询结果: {result}, 得分: {score}")
+    else:
+        logger.info("未找到匹配结果")
+
+
+if __name__ == "__main__":
+    main()

+ 2 - 0
3.4 bm25_lesson/requirements.txt

@@ -0,0 +1,2 @@
+jieba
+rank_bm25

+ 39 - 0
3.4 bm25_lesson/retrieval/bm25_search.py

@@ -0,0 +1,39 @@
+import jieba
+from rank_bm25 import BM25L
+import logging
+
+# 配置日志
+logging.basicConfig(
+    level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
+)
+logger = logging.getLogger(__name__)
+
+
+class BM25Search:
+    def __init__(self, documents):
+        # 初始化文档集合
+        self.documents = documents
+        # 分词后的文档
+        self.tokenized_docs = [jieba.lcut(doc) for doc in documents]
+        # 初始化 BM25 模型
+        self.bm25 = BM25L(self.tokenized_docs)
+        logger.info("BM25模型初始化完成")
+
+    def search(self, query):
+        # 分词查询
+        tokenized_query = jieba.lcut(query)
+        try:
+            # 计算每个文档的 BM25 得分
+            scores = self.bm25.get_scores(tokenized_query)
+            print(f"scores--》{scores}")
+            # 获取最高得分的文档索引
+            best_idx = scores.argmax()
+            best_score = scores[best_idx]
+            best_doc = self.documents[best_idx]
+            logger.info(
+                f"查询: {query}, 最佳匹配: {best_doc}, 得分: {best_score}"
+            )
+            return best_doc, best_score
+        except Exception as exc:
+            logger.error(f"检索失败: {exc}")
+            return None, 0.0

+ 99 - 0
4.基于Milvus库的问答系统/integrated_qa_system/base/config.py

@@ -0,0 +1,99 @@
+# base/config.py
+# 导入配置解析库
+import configparser
+# 导入路径操作库
+import os
+# 导入日志记录库
+import logging
+
+class Config:
+    # 初始化配置,加载 config.ini 文件
+    def __init__(self, config_file=None):
+        # 创建配置解析器,启用插值功能
+        self.config = configparser.ConfigParser(interpolation=configparser.ExtendedInterpolation())
+        # 如果没有提供配置文件路径,则使用默认路径
+
+        self.PROJECT_ROOT = os.path.dirname(os.path.dirname(__file__))
+
+        self.LOG_DIR = os.path.join(self.PROJECT_ROOT, 'logs')
+        self.DATA_DIR = os.path.join(self.PROJECT_ROOT, 'rag_qa/data')
+        self.MODELS_DIR = os.path.join(self.PROJECT_ROOT, 'rag_qa/models')
+        self.KB_DOCUMENT_LOADERS_DIR = os.path.join(self.PROJECT_ROOT, 'rag_qa/kb_document_loaders')
+
+        if config_file is None:
+            config_file = os.path.join(self.PROJECT_ROOT, 'config.ini')
+        # 读取配置文件
+        self.config.read(config_file)
+
+        # MySQL 配置
+        # MySQL 主机地址
+        self.MYSQL_HOST = os.getenv('MYSQL_HOST', self.config.get('mysql', 'host', fallback='localhost'))
+        # MySQL 用户名
+        self.MYSQL_USER = os.getenv('MYSQL_USER', self.config.get('mysql', 'user', fallback='enterprise_kb_rag'))
+        # MySQL 密码
+        self.MYSQL_PASSWORD = os.getenv('MYSQL_PASSWORD', self.config.get('mysql', 'password', fallback=''))
+        # MySQL 数据库名
+        self.MYSQL_DATABASE = os.getenv('MYSQL_DATABASE', self.config.get('mysql', 'database', fallback='subjects_kg'))
+
+        # Redis 配置
+        # Redis 主机地址
+        self.REDIS_HOST = os.getenv('REDIS_HOST', self.config.get('redis', 'host', fallback='localhost'))
+        # Redis 端口
+        self.REDIS_PORT = int(os.getenv('REDIS_PORT', self.config.get('redis', 'port', fallback=6379)))
+        # Redis 密码
+        self.REDIS_PASSWORD = os.getenv('REDIS_PASSWORD', self.config.get('redis', 'password', fallback=''))
+        # Redis 数据库编号
+        self.REDIS_DB = int(os.getenv('REDIS_DB', self.config.get('redis', 'db', fallback=0)))
+
+        # Milvus 配置
+        # Milvus 主机地址
+        self.MILVUS_HOST = os.getenv('MILVUS_HOST', self.config.get('milvus', 'host', fallback='localhost'))
+        # Milvus 端口
+        self.MILVUS_PORT = os.getenv('MILVUS_PORT', self.config.get('milvus', 'port', fallback='19530'))
+        # Milvus 数据库名
+        self.MILVUS_DATABASE_NAME = os.getenv('MILVUS_DATABASE_NAME', self.config.get('milvus', 'database_name', fallback='enterprise_kb'))
+        # Milvus 集合名
+        self.MILVUS_COLLECTION_NAME = os.getenv('MILVUS_COLLECTION_NAME', self.config.get('milvus', 'collection_name', fallback='enterprise_kb_rag_xian_1'))
+
+        # LLM 配置
+        # LLM 模型名
+        self.LLM_MODEL = self.config.get('llm', 'model', fallback='qwen-plus')
+        # DashScope API 密钥
+        self.DASHSCOPE_API_KEY = os.getenv(
+            'DASHSCOPE_API_KEY',
+            self.config.get('llm', 'dashscope_api_key', fallback='')
+        )
+        # DashScope API 地址
+        self.DASHSCOPE_BASE_URL = self.config.get('llm', 'dashscope_base_url',
+                                                  fallback='https://dashscope.aliyuncs.com/compatible-mode/v1')
+
+        # 检索参数
+        # 父块大小
+        self.PARENT_CHUNK_SIZE = self.config.getint('retrieval', 'parent_chunk_size', fallback=1200)
+        # 子块大小
+        self.CHILD_CHUNK_SIZE = self.config.getint('retrieval', 'child_chunk_size', fallback=300)
+        # 块重叠大小
+        self.CHUNK_OVERLAP = self.config.getint('retrieval', 'chunk_overlap', fallback=50)
+        # 检索返回数量
+        self.RETRIEVAL_K = self.config.getint('retrieval', 'retrieval_k', fallback=5)
+        # 最终候选数量
+        self.CANDIDATE_M = self.config.getint('retrieval', 'candidate_m', fallback=2)
+
+        # 应用配置
+        # 有效来源列表
+        self.VALID_SOURCES = eval(
+            self.config.get('app', 'valid_sources', fallback='["ai", "java", "test", "ops", "bigdata"]'))
+        # 客服电话
+        self.CUSTOMER_SERVICE_PHONE = self.config.get('app', 'customer_service_phone', fallback='12345678')
+
+        # 日志文件路径
+        self.LOG_FILE = os.path.join(self.LOG_DIR, 'app.log')
+
+        # 路径配置
+
+
+config = Config()
+
+if __name__ == '__main__':
+    conf = Config()
+    print(conf.MYSQL_USER)

+ 45 - 0
4.基于Milvus库的问答系统/integrated_qa_system/base/logger.py

@@ -0,0 +1,45 @@
+# 导入日志库
+import logging
+# 导入路径操作库
+import os
+import os.path
+import logging
+from base.config import config
+
+def setup_logger(logger_name='EnterpriseKB-RAG', logger_file=config.LOG_FILE):
+    # 1. 确保日志目录存在,不存在创建
+    # logger_file 是日志的文件名,所以我们先取到它所在的目录 os.path.pathj
+    dirname = os.path.dirname(logger_file)
+    if not os.path.exists(dirname):
+        os.makedirs(dirname)
+    # 2. 创建日志记录器: Logger
+    # 2.1 获取Logger对象
+    logger = logging.getLogger(logger_name)
+    # 2.2 设置日志级别为所有控制器最低的(设置全局的日志级别)
+    logger.setLevel(logging.DEBUG)
+
+    if not logger.handlers:
+        # 3. 创建控制台控制器:StreamHandler
+        # 3.1 创建控制台处理器对象
+        stream_handler = logging.StreamHandler()
+        # 3.2 设置日志级别为INFO
+        stream_handler.setLevel(logging.INFO)
+        # 4. 创建文件处理器:FileHandler,并指定目录
+        # 4.1 创建文件处理对象
+        file_handler = logging.FileHandler(logger_file, mode='a')
+        # 4.2 设置日志级别为DEBUG
+        file_handler.setLevel(logging.DEBUG)
+        # 5. 定义并设置日志格式:
+        # 5.1 定义日志格式:logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
+        formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(pathname)s - %(funcName)s - %(module)s - %(lineno)d - %(message)s')
+        # 5.2 设置处理器日志格式
+        stream_handler.setFormatter(formatter)
+        file_handler.setFormatter(formatter)
+        # 6. 把处理器添加到logger中
+        logger.addHandler(stream_handler)
+        logger.addHandler(file_handler)
+
+    return logger
+
+
+logger = setup_logger('EnterpriseKB-RAG')

+ 33 - 0
4.基于Milvus库的问答系统/integrated_qa_system/config.example.ini

@@ -0,0 +1,33 @@
+[mysql]
+host = localhost
+user = demo_user
+password = change_me_mysql_password
+database = enterprise_kb
+
+[redis]
+host = localhost
+port = 6379
+password = change_me_redis_password
+db = 0
+
+[milvus]
+host = localhost
+port = 19530
+database_name = enterprise_kb
+collection_name = enterprise_kb_rag_demo
+
+[llm]
+model = qwen-plus
+dashscope_api_key = your_dashscope_api_key_here
+dashscope_base_url = https://dashscope.aliyuncs.com/compatible-mode/v1
+
+[retrieval]
+parent_chunk_size = 1200
+child_chunk_size = 300
+chunk_overlap = 50
+retrieval_k = 5
+candidate_m = 2
+
+[app]
+valid_sources = ["demo", "docs"]
+customer_service_phone = 400-000-0000

+ 220 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/chatgpt_generate_200.jsonl

@@ -0,0 +1,220 @@
+{"query": "离职交接需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?", "label": "企业知识"}
+{"query": "年假如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请按步骤说明。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请给出办理入口。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请按步骤说明。", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?有注意事项吗?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请按步骤说明。", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是日志级别?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是召回率和准确率?", "label": "通用知识"}
+{"query": "入职当天需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?", "label": "企业知识"}
+{"query": "加班调休如何登记?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?", "label": "企业知识"}
+{"query": "什么是Prompt模板?", "label": "通用知识"}
+{"query": "产品手册更新后如何同步?请按步骤说明。", "label": "企业知识"}
+{"query": "如何用Python读取CSV文件?", "label": "通用知识"}
+{"query": "会议室预订规则是什么?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?请给出办理入口。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请按步骤说明。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请给出办理入口。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?需要哪些材料?", "label": "企业知识"}
+{"query": "如何在Linux中查看当前进程?", "label": "通用知识"}
+{"query": "跨部门协作如何发起?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?有注意事项吗?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?有注意事项吗?", "label": "企业知识"}
+{"query": "售后问题如何升级?请给出办理入口。", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是重排序模型?", "label": "通用知识"}
+{"query": "发票抬头和税号在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请给出办理入口。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请按步骤说明。", "label": "企业知识"}
+{"query": "餐补报销有什么标准?有注意事项吗?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?需要哪些材料?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请给出办理入口。", "label": "企业知识"}
+{"query": "项目立项流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是余弦相似度?", "label": "通用知识"}
+{"query": "NDA模板在哪里下载?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "加班调休如何登记?有注意事项吗?", "label": "企业知识"}
+{"query": "加班调休如何登记?请给出办理入口。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?", "label": "企业知识"}
+{"query": "如何理解缓存穿透?", "label": "通用知识"}
+{"query": "如何排查接口超时?", "label": "通用知识"}
+{"query": "餐补报销有什么标准?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请按步骤说明。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?需要哪些材料?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请给出办理入口。", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?", "label": "企业知识"}
+{"query": "售后问题如何升级?有注意事项吗?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?需要哪些材料?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请给出办理入口。", "label": "企业知识"}
+{"query": "售后问题如何升级?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?需要哪些材料?", "label": "企业知识"}
+{"query": "售后问题如何升级?需要哪些材料?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是RESTful API?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?有注意事项吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?有注意事项吗?", "label": "企业知识"}
+{"query": "如何用SQL进行表连接?", "label": "通用知识"}
+{"query": "如何申请CRM系统权限?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何理解父子分块?", "label": "通用知识"}
+{"query": "敏感数据可以上传知识库吗?请给出办理入口。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请给出办理入口。", "label": "企业知识"}
+{"query": "合同归档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是JWT认证?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?", "label": "企业知识"}
+{"query": "加班调休如何登记?请按步骤说明。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "合同归档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?有注意事项吗?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?", "label": "企业知识"}
+{"query": "项目立项流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?需要哪些材料?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?需要哪些材料?", "label": "企业知识"}
+{"query": "加班调休如何登记?需要哪些材料?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "项目立项流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "解释一下HTTP和HTTPS的区别。", "label": "通用知识"}
+{"query": "解释一下同步和异步的区别。", "label": "通用知识"}
+{"query": "客户要求修改责任条款怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是向量数据库?", "label": "通用知识"}
+{"query": "采购付款流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "请说明BM25算法的基本思想。", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?", "label": "企业知识"}
+{"query": "年假如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请给出办理入口。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是Docker镜像?", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?请按步骤说明。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是WebSocket?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?请按步骤说明。", "label": "企业知识"}
+{"query": "售后问题如何升级?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?有注意事项吗?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?有注意事项吗?", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?请按步骤说明。", "label": "企业知识"}
+{"query": "如何计算文本相似度?", "label": "通用知识"}
+{"query": "如何查看最新版本的交付SOP?有注意事项吗?", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?有注意事项吗?", "label": "企业知识"}
+{"query": "采购付款流程是什么?", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?有注意事项吗?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?有注意事项吗?", "label": "企业知识"}

+ 220 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/chatgpt_generate_200_0423.jsonl

@@ -0,0 +1,220 @@
+{"query": "离职交接需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?", "label": "企业知识"}
+{"query": "年假如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请按步骤说明。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请给出办理入口。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请按步骤说明。", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?有注意事项吗?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请按步骤说明。", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是日志级别?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是召回率和准确率?", "label": "通用知识"}
+{"query": "入职当天需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?", "label": "企业知识"}
+{"query": "加班调休如何登记?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?", "label": "企业知识"}
+{"query": "什么是Prompt模板?", "label": "通用知识"}
+{"query": "产品手册更新后如何同步?请按步骤说明。", "label": "企业知识"}
+{"query": "如何用Python读取CSV文件?", "label": "通用知识"}
+{"query": "会议室预订规则是什么?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?请给出办理入口。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请按步骤说明。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请给出办理入口。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?需要哪些材料?", "label": "企业知识"}
+{"query": "如何在Linux中查看当前进程?", "label": "通用知识"}
+{"query": "跨部门协作如何发起?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?有注意事项吗?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?有注意事项吗?", "label": "企业知识"}
+{"query": "售后问题如何升级?请给出办理入口。", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是重排序模型?", "label": "通用知识"}
+{"query": "发票抬头和税号在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请给出办理入口。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请按步骤说明。", "label": "企业知识"}
+{"query": "餐补报销有什么标准?有注意事项吗?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?需要哪些材料?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请给出办理入口。", "label": "企业知识"}
+{"query": "项目立项流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是余弦相似度?", "label": "通用知识"}
+{"query": "NDA模板在哪里下载?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "加班调休如何登记?有注意事项吗?", "label": "企业知识"}
+{"query": "加班调休如何登记?请给出办理入口。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?", "label": "企业知识"}
+{"query": "如何理解缓存穿透?", "label": "通用知识"}
+{"query": "如何排查接口超时?", "label": "通用知识"}
+{"query": "餐补报销有什么标准?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请按步骤说明。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?需要哪些材料?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请给出办理入口。", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?", "label": "企业知识"}
+{"query": "售后问题如何升级?有注意事项吗?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?需要哪些材料?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请给出办理入口。", "label": "企业知识"}
+{"query": "售后问题如何升级?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?需要哪些材料?", "label": "企业知识"}
+{"query": "售后问题如何升级?需要哪些材料?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是RESTful API?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?有注意事项吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?有注意事项吗?", "label": "企业知识"}
+{"query": "如何用SQL进行表连接?", "label": "通用知识"}
+{"query": "如何申请CRM系统权限?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何理解父子分块?", "label": "通用知识"}
+{"query": "敏感数据可以上传知识库吗?请给出办理入口。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请给出办理入口。", "label": "企业知识"}
+{"query": "合同归档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是JWT认证?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?", "label": "企业知识"}
+{"query": "加班调休如何登记?请按步骤说明。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "合同归档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?有注意事项吗?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?", "label": "企业知识"}
+{"query": "项目立项流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?需要哪些材料?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?需要哪些材料?", "label": "企业知识"}
+{"query": "加班调休如何登记?需要哪些材料?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "项目立项流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "解释一下HTTP和HTTPS的区别。", "label": "通用知识"}
+{"query": "解释一下同步和异步的区别。", "label": "通用知识"}
+{"query": "客户要求修改责任条款怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是向量数据库?", "label": "通用知识"}
+{"query": "采购付款流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "请说明BM25算法的基本思想。", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?", "label": "企业知识"}
+{"query": "年假如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请给出办理入口。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是Docker镜像?", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?请按步骤说明。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是WebSocket?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?请按步骤说明。", "label": "企业知识"}
+{"query": "售后问题如何升级?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?有注意事项吗?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?有注意事项吗?", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?请按步骤说明。", "label": "企业知识"}
+{"query": "如何计算文本相似度?", "label": "通用知识"}
+{"query": "如何查看最新版本的交付SOP?有注意事项吗?", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?有注意事项吗?", "label": "企业知识"}
+{"query": "采购付款流程是什么?", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?有注意事项吗?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?有注意事项吗?", "label": "企业知识"}

+ 220 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/chatgpt_generate_200_query.jsonl

@@ -0,0 +1,220 @@
+{"query": "离职交接需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?", "label": "企业知识"}
+{"query": "年假如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请按步骤说明。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请给出办理入口。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请按步骤说明。", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?有注意事项吗?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请按步骤说明。", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是日志级别?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是召回率和准确率?", "label": "通用知识"}
+{"query": "入职当天需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?", "label": "企业知识"}
+{"query": "加班调休如何登记?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?", "label": "企业知识"}
+{"query": "什么是Prompt模板?", "label": "通用知识"}
+{"query": "产品手册更新后如何同步?请按步骤说明。", "label": "企业知识"}
+{"query": "如何用Python读取CSV文件?", "label": "通用知识"}
+{"query": "会议室预订规则是什么?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?请给出办理入口。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请按步骤说明。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请给出办理入口。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?需要哪些材料?", "label": "企业知识"}
+{"query": "如何在Linux中查看当前进程?", "label": "通用知识"}
+{"query": "跨部门协作如何发起?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?有注意事项吗?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?有注意事项吗?", "label": "企业知识"}
+{"query": "售后问题如何升级?请给出办理入口。", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是重排序模型?", "label": "通用知识"}
+{"query": "发票抬头和税号在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请给出办理入口。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请按步骤说明。", "label": "企业知识"}
+{"query": "餐补报销有什么标准?有注意事项吗?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?需要哪些材料?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请给出办理入口。", "label": "企业知识"}
+{"query": "项目立项流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是余弦相似度?", "label": "通用知识"}
+{"query": "NDA模板在哪里下载?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "加班调休如何登记?有注意事项吗?", "label": "企业知识"}
+{"query": "加班调休如何登记?请给出办理入口。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?", "label": "企业知识"}
+{"query": "如何理解缓存穿透?", "label": "通用知识"}
+{"query": "如何排查接口超时?", "label": "通用知识"}
+{"query": "餐补报销有什么标准?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请按步骤说明。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?需要哪些材料?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请给出办理入口。", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?", "label": "企业知识"}
+{"query": "售后问题如何升级?有注意事项吗?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?需要哪些材料?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请给出办理入口。", "label": "企业知识"}
+{"query": "售后问题如何升级?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?需要哪些材料?", "label": "企业知识"}
+{"query": "售后问题如何升级?需要哪些材料?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是RESTful API?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?有注意事项吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?有注意事项吗?", "label": "企业知识"}
+{"query": "如何用SQL进行表连接?", "label": "通用知识"}
+{"query": "如何申请CRM系统权限?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何理解父子分块?", "label": "通用知识"}
+{"query": "敏感数据可以上传知识库吗?请给出办理入口。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请给出办理入口。", "label": "企业知识"}
+{"query": "合同归档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是JWT认证?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?", "label": "企业知识"}
+{"query": "加班调休如何登记?请按步骤说明。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "合同归档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?有注意事项吗?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?", "label": "企业知识"}
+{"query": "项目立项流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?需要哪些材料?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?需要哪些材料?", "label": "企业知识"}
+{"query": "加班调休如何登记?需要哪些材料?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "项目立项流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "解释一下HTTP和HTTPS的区别。", "label": "通用知识"}
+{"query": "解释一下同步和异步的区别。", "label": "通用知识"}
+{"query": "客户要求修改责任条款怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是向量数据库?", "label": "通用知识"}
+{"query": "采购付款流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "请说明BM25算法的基本思想。", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?", "label": "企业知识"}
+{"query": "年假如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请给出办理入口。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是Docker镜像?", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?请按步骤说明。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是WebSocket?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?请按步骤说明。", "label": "企业知识"}
+{"query": "售后问题如何升级?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?有注意事项吗?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?有注意事项吗?", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?请按步骤说明。", "label": "企业知识"}
+{"query": "如何计算文本相似度?", "label": "通用知识"}
+{"query": "如何查看最新版本的交付SOP?有注意事项吗?", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?有注意事项吗?", "label": "企业知识"}
+{"query": "采购付款流程是什么?", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?有注意事项吗?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?有注意事项吗?", "label": "企业知识"}

+ 13 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/handler.py

@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+@Author  : Vincent
+@Time    : 2025/11/12 21:23
+@File    : handler.py
+@Function :
+"""
+import pandas as pd
+
+data = pd.read_json("training_data_1000.json", lines=False)
+print(data.head())
+data.to_json("training_data_1000.json", orient="records", lines=True, index=None, force_ascii=False)

+ 220 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/model_generate_0623.jsonl

@@ -0,0 +1,220 @@
+{"query": "离职交接需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?", "label": "企业知识"}
+{"query": "年假如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请按步骤说明。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请给出办理入口。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请按步骤说明。", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?有注意事项吗?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请按步骤说明。", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是日志级别?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是召回率和准确率?", "label": "通用知识"}
+{"query": "入职当天需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?", "label": "企业知识"}
+{"query": "加班调休如何登记?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?", "label": "企业知识"}
+{"query": "什么是Prompt模板?", "label": "通用知识"}
+{"query": "产品手册更新后如何同步?请按步骤说明。", "label": "企业知识"}
+{"query": "如何用Python读取CSV文件?", "label": "通用知识"}
+{"query": "会议室预订规则是什么?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?请给出办理入口。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请按步骤说明。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请给出办理入口。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?需要哪些材料?", "label": "企业知识"}
+{"query": "如何在Linux中查看当前进程?", "label": "通用知识"}
+{"query": "跨部门协作如何发起?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?有注意事项吗?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?有注意事项吗?", "label": "企业知识"}
+{"query": "售后问题如何升级?请给出办理入口。", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是重排序模型?", "label": "通用知识"}
+{"query": "发票抬头和税号在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请给出办理入口。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请按步骤说明。", "label": "企业知识"}
+{"query": "餐补报销有什么标准?有注意事项吗?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?需要哪些材料?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请给出办理入口。", "label": "企业知识"}
+{"query": "项目立项流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是余弦相似度?", "label": "通用知识"}
+{"query": "NDA模板在哪里下载?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "加班调休如何登记?有注意事项吗?", "label": "企业知识"}
+{"query": "加班调休如何登记?请给出办理入口。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?", "label": "企业知识"}
+{"query": "如何理解缓存穿透?", "label": "通用知识"}
+{"query": "如何排查接口超时?", "label": "通用知识"}
+{"query": "餐补报销有什么标准?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请按步骤说明。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?需要哪些材料?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请给出办理入口。", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?", "label": "企业知识"}
+{"query": "售后问题如何升级?有注意事项吗?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?需要哪些材料?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请给出办理入口。", "label": "企业知识"}
+{"query": "售后问题如何升级?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?需要哪些材料?", "label": "企业知识"}
+{"query": "售后问题如何升级?需要哪些材料?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是RESTful API?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?有注意事项吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?有注意事项吗?", "label": "企业知识"}
+{"query": "如何用SQL进行表连接?", "label": "通用知识"}
+{"query": "如何申请CRM系统权限?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何理解父子分块?", "label": "通用知识"}
+{"query": "敏感数据可以上传知识库吗?请给出办理入口。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请给出办理入口。", "label": "企业知识"}
+{"query": "合同归档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是JWT认证?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?", "label": "企业知识"}
+{"query": "加班调休如何登记?请按步骤说明。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "合同归档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?有注意事项吗?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?", "label": "企业知识"}
+{"query": "项目立项流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?需要哪些材料?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?需要哪些材料?", "label": "企业知识"}
+{"query": "加班调休如何登记?需要哪些材料?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "项目立项流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "解释一下HTTP和HTTPS的区别。", "label": "通用知识"}
+{"query": "解释一下同步和异步的区别。", "label": "通用知识"}
+{"query": "客户要求修改责任条款怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是向量数据库?", "label": "通用知识"}
+{"query": "采购付款流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "请说明BM25算法的基本思想。", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?", "label": "企业知识"}
+{"query": "年假如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请给出办理入口。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是Docker镜像?", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?请按步骤说明。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是WebSocket?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?请按步骤说明。", "label": "企业知识"}
+{"query": "售后问题如何升级?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?有注意事项吗?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?有注意事项吗?", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?请按步骤说明。", "label": "企业知识"}
+{"query": "如何计算文本相似度?", "label": "通用知识"}
+{"query": "如何查看最新版本的交付SOP?有注意事项吗?", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?有注意事项吗?", "label": "企业知识"}
+{"query": "采购付款流程是什么?", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?有注意事项吗?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?有注意事项吗?", "label": "企业知识"}

+ 220 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/model_generate_262.json

@@ -0,0 +1,220 @@
+{"query": "离职交接需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?", "label": "企业知识"}
+{"query": "年假如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请按步骤说明。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请给出办理入口。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请按步骤说明。", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?有注意事项吗?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请按步骤说明。", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是日志级别?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是召回率和准确率?", "label": "通用知识"}
+{"query": "入职当天需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?", "label": "企业知识"}
+{"query": "加班调休如何登记?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?", "label": "企业知识"}
+{"query": "什么是Prompt模板?", "label": "通用知识"}
+{"query": "产品手册更新后如何同步?请按步骤说明。", "label": "企业知识"}
+{"query": "如何用Python读取CSV文件?", "label": "通用知识"}
+{"query": "会议室预订规则是什么?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?请给出办理入口。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请按步骤说明。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请给出办理入口。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?需要哪些材料?", "label": "企业知识"}
+{"query": "如何在Linux中查看当前进程?", "label": "通用知识"}
+{"query": "跨部门协作如何发起?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?有注意事项吗?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?有注意事项吗?", "label": "企业知识"}
+{"query": "售后问题如何升级?请给出办理入口。", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是重排序模型?", "label": "通用知识"}
+{"query": "发票抬头和税号在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请给出办理入口。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请按步骤说明。", "label": "企业知识"}
+{"query": "餐补报销有什么标准?有注意事项吗?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?需要哪些材料?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请给出办理入口。", "label": "企业知识"}
+{"query": "项目立项流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是余弦相似度?", "label": "通用知识"}
+{"query": "NDA模板在哪里下载?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "加班调休如何登记?有注意事项吗?", "label": "企业知识"}
+{"query": "加班调休如何登记?请给出办理入口。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?", "label": "企业知识"}
+{"query": "如何理解缓存穿透?", "label": "通用知识"}
+{"query": "如何排查接口超时?", "label": "通用知识"}
+{"query": "餐补报销有什么标准?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请按步骤说明。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?需要哪些材料?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请给出办理入口。", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?", "label": "企业知识"}
+{"query": "售后问题如何升级?有注意事项吗?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?需要哪些材料?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请给出办理入口。", "label": "企业知识"}
+{"query": "售后问题如何升级?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?需要哪些材料?", "label": "企业知识"}
+{"query": "售后问题如何升级?需要哪些材料?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是RESTful API?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?有注意事项吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?有注意事项吗?", "label": "企业知识"}
+{"query": "如何用SQL进行表连接?", "label": "通用知识"}
+{"query": "如何申请CRM系统权限?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何理解父子分块?", "label": "通用知识"}
+{"query": "敏感数据可以上传知识库吗?请给出办理入口。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请给出办理入口。", "label": "企业知识"}
+{"query": "合同归档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是JWT认证?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?", "label": "企业知识"}
+{"query": "加班调休如何登记?请按步骤说明。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "合同归档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?有注意事项吗?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?", "label": "企业知识"}
+{"query": "项目立项流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?需要哪些材料?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?需要哪些材料?", "label": "企业知识"}
+{"query": "加班调休如何登记?需要哪些材料?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "项目立项流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "解释一下HTTP和HTTPS的区别。", "label": "通用知识"}
+{"query": "解释一下同步和异步的区别。", "label": "通用知识"}
+{"query": "客户要求修改责任条款怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是向量数据库?", "label": "通用知识"}
+{"query": "采购付款流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "请说明BM25算法的基本思想。", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?", "label": "企业知识"}
+{"query": "年假如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请给出办理入口。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是Docker镜像?", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?请按步骤说明。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是WebSocket?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?请按步骤说明。", "label": "企业知识"}
+{"query": "售后问题如何升级?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?有注意事项吗?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?有注意事项吗?", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?请按步骤说明。", "label": "企业知识"}
+{"query": "如何计算文本相似度?", "label": "通用知识"}
+{"query": "如何查看最新版本的交付SOP?有注意事项吗?", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?有注意事项吗?", "label": "企业知识"}
+{"query": "采购付款流程是什么?", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?有注意事项吗?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?有注意事项吗?", "label": "企业知识"}

+ 220 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/model_generic_5000.json

@@ -0,0 +1,220 @@
+{"query": "离职交接需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?", "label": "企业知识"}
+{"query": "年假如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请按步骤说明。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请给出办理入口。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请按步骤说明。", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?有注意事项吗?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请按步骤说明。", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是日志级别?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是召回率和准确率?", "label": "通用知识"}
+{"query": "入职当天需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?", "label": "企业知识"}
+{"query": "加班调休如何登记?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?", "label": "企业知识"}
+{"query": "什么是Prompt模板?", "label": "通用知识"}
+{"query": "产品手册更新后如何同步?请按步骤说明。", "label": "企业知识"}
+{"query": "如何用Python读取CSV文件?", "label": "通用知识"}
+{"query": "会议室预订规则是什么?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?请给出办理入口。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请按步骤说明。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请给出办理入口。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?需要哪些材料?", "label": "企业知识"}
+{"query": "如何在Linux中查看当前进程?", "label": "通用知识"}
+{"query": "跨部门协作如何发起?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?有注意事项吗?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?有注意事项吗?", "label": "企业知识"}
+{"query": "售后问题如何升级?请给出办理入口。", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是重排序模型?", "label": "通用知识"}
+{"query": "发票抬头和税号在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请给出办理入口。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请按步骤说明。", "label": "企业知识"}
+{"query": "餐补报销有什么标准?有注意事项吗?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?需要哪些材料?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请给出办理入口。", "label": "企业知识"}
+{"query": "项目立项流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是余弦相似度?", "label": "通用知识"}
+{"query": "NDA模板在哪里下载?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "加班调休如何登记?有注意事项吗?", "label": "企业知识"}
+{"query": "加班调休如何登记?请给出办理入口。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?", "label": "企业知识"}
+{"query": "如何理解缓存穿透?", "label": "通用知识"}
+{"query": "如何排查接口超时?", "label": "通用知识"}
+{"query": "餐补报销有什么标准?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请按步骤说明。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?需要哪些材料?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请给出办理入口。", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?", "label": "企业知识"}
+{"query": "售后问题如何升级?有注意事项吗?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?需要哪些材料?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请给出办理入口。", "label": "企业知识"}
+{"query": "售后问题如何升级?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?需要哪些材料?", "label": "企业知识"}
+{"query": "售后问题如何升级?需要哪些材料?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是RESTful API?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?有注意事项吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?有注意事项吗?", "label": "企业知识"}
+{"query": "如何用SQL进行表连接?", "label": "通用知识"}
+{"query": "如何申请CRM系统权限?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何理解父子分块?", "label": "通用知识"}
+{"query": "敏感数据可以上传知识库吗?请给出办理入口。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请给出办理入口。", "label": "企业知识"}
+{"query": "合同归档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是JWT认证?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?", "label": "企业知识"}
+{"query": "加班调休如何登记?请按步骤说明。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "合同归档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?有注意事项吗?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?", "label": "企业知识"}
+{"query": "项目立项流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?需要哪些材料?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?需要哪些材料?", "label": "企业知识"}
+{"query": "加班调休如何登记?需要哪些材料?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "项目立项流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "解释一下HTTP和HTTPS的区别。", "label": "通用知识"}
+{"query": "解释一下同步和异步的区别。", "label": "通用知识"}
+{"query": "客户要求修改责任条款怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是向量数据库?", "label": "通用知识"}
+{"query": "采购付款流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "请说明BM25算法的基本思想。", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?", "label": "企业知识"}
+{"query": "年假如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请给出办理入口。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是Docker镜像?", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?请按步骤说明。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是WebSocket?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?请按步骤说明。", "label": "企业知识"}
+{"query": "售后问题如何升级?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?有注意事项吗?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?有注意事项吗?", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?请按步骤说明。", "label": "企业知识"}
+{"query": "如何计算文本相似度?", "label": "通用知识"}
+{"query": "如何查看最新版本的交付SOP?有注意事项吗?", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?有注意事项吗?", "label": "企业知识"}
+{"query": "采购付款流程是什么?", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?有注意事项吗?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?有注意事项吗?", "label": "企业知识"}

+ 220 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/qwen3-max_generate_200.json

@@ -0,0 +1,220 @@
+{"query": "离职交接需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?", "label": "企业知识"}
+{"query": "年假如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请按步骤说明。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请给出办理入口。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请按步骤说明。", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?有注意事项吗?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请按步骤说明。", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是日志级别?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是召回率和准确率?", "label": "通用知识"}
+{"query": "入职当天需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?", "label": "企业知识"}
+{"query": "加班调休如何登记?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?", "label": "企业知识"}
+{"query": "什么是Prompt模板?", "label": "通用知识"}
+{"query": "产品手册更新后如何同步?请按步骤说明。", "label": "企业知识"}
+{"query": "如何用Python读取CSV文件?", "label": "通用知识"}
+{"query": "会议室预订规则是什么?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?请给出办理入口。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请按步骤说明。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请给出办理入口。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?需要哪些材料?", "label": "企业知识"}
+{"query": "如何在Linux中查看当前进程?", "label": "通用知识"}
+{"query": "跨部门协作如何发起?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?有注意事项吗?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?有注意事项吗?", "label": "企业知识"}
+{"query": "售后问题如何升级?请给出办理入口。", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是重排序模型?", "label": "通用知识"}
+{"query": "发票抬头和税号在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请给出办理入口。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请按步骤说明。", "label": "企业知识"}
+{"query": "餐补报销有什么标准?有注意事项吗?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?需要哪些材料?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请给出办理入口。", "label": "企业知识"}
+{"query": "项目立项流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是余弦相似度?", "label": "通用知识"}
+{"query": "NDA模板在哪里下载?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "加班调休如何登记?有注意事项吗?", "label": "企业知识"}
+{"query": "加班调休如何登记?请给出办理入口。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?", "label": "企业知识"}
+{"query": "如何理解缓存穿透?", "label": "通用知识"}
+{"query": "如何排查接口超时?", "label": "通用知识"}
+{"query": "餐补报销有什么标准?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请按步骤说明。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?需要哪些材料?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请给出办理入口。", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?", "label": "企业知识"}
+{"query": "售后问题如何升级?有注意事项吗?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?需要哪些材料?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请给出办理入口。", "label": "企业知识"}
+{"query": "售后问题如何升级?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?需要哪些材料?", "label": "企业知识"}
+{"query": "售后问题如何升级?需要哪些材料?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是RESTful API?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?有注意事项吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?有注意事项吗?", "label": "企业知识"}
+{"query": "如何用SQL进行表连接?", "label": "通用知识"}
+{"query": "如何申请CRM系统权限?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何理解父子分块?", "label": "通用知识"}
+{"query": "敏感数据可以上传知识库吗?请给出办理入口。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请给出办理入口。", "label": "企业知识"}
+{"query": "合同归档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是JWT认证?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?", "label": "企业知识"}
+{"query": "加班调休如何登记?请按步骤说明。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "合同归档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?有注意事项吗?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?", "label": "企业知识"}
+{"query": "项目立项流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?需要哪些材料?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?需要哪些材料?", "label": "企业知识"}
+{"query": "加班调休如何登记?需要哪些材料?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "项目立项流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "解释一下HTTP和HTTPS的区别。", "label": "通用知识"}
+{"query": "解释一下同步和异步的区别。", "label": "通用知识"}
+{"query": "客户要求修改责任条款怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是向量数据库?", "label": "通用知识"}
+{"query": "采购付款流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "请说明BM25算法的基本思想。", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?", "label": "企业知识"}
+{"query": "年假如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请给出办理入口。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是Docker镜像?", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?请按步骤说明。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是WebSocket?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?请按步骤说明。", "label": "企业知识"}
+{"query": "售后问题如何升级?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?有注意事项吗?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?有注意事项吗?", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?请按步骤说明。", "label": "企业知识"}
+{"query": "如何计算文本相似度?", "label": "通用知识"}
+{"query": "如何查看最新版本的交付SOP?有注意事项吗?", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?有注意事项吗?", "label": "企业知识"}
+{"query": "采购付款流程是什么?", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?有注意事项吗?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?有注意事项吗?", "label": "企业知识"}

+ 220 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/training_data_1000.json

@@ -0,0 +1,220 @@
+{"query": "离职交接需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?", "label": "企业知识"}
+{"query": "年假如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请按步骤说明。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请给出办理入口。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?请按步骤说明。", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?有注意事项吗?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请按步骤说明。", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是日志级别?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是召回率和准确率?", "label": "通用知识"}
+{"query": "入职当天需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?", "label": "企业知识"}
+{"query": "加班调休如何登记?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?", "label": "企业知识"}
+{"query": "什么是Prompt模板?", "label": "通用知识"}
+{"query": "产品手册更新后如何同步?请按步骤说明。", "label": "企业知识"}
+{"query": "如何用Python读取CSV文件?", "label": "通用知识"}
+{"query": "会议室预订规则是什么?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?请给出办理入口。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请按步骤说明。", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请按步骤说明。", "label": "企业知识"}
+{"query": "试用期转正流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?请给出办理入口。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?请给出办理入口。", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?需要哪些材料?", "label": "企业知识"}
+{"query": "如何在Linux中查看当前进程?", "label": "通用知识"}
+{"query": "跨部门协作如何发起?需要哪些材料?", "label": "企业知识"}
+{"query": "年假如何申请?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?请给出办理入口。", "label": "企业知识"}
+{"query": "客户工单优先级怎么判定?需要哪些材料?", "label": "企业知识"}
+{"query": "采购付款流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?有注意事项吗?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?有注意事项吗?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?有注意事项吗?", "label": "企业知识"}
+{"query": "售后问题如何升级?请给出办理入口。", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是重排序模型?", "label": "通用知识"}
+{"query": "发票抬头和税号在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请给出办理入口。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?请按步骤说明。", "label": "企业知识"}
+{"query": "餐补报销有什么标准?有注意事项吗?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?需要哪些材料?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?请给出办理入口。", "label": "企业知识"}
+{"query": "项目立项流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "什么是余弦相似度?", "label": "通用知识"}
+{"query": "NDA模板在哪里下载?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "加班调休如何登记?有注意事项吗?", "label": "企业知识"}
+{"query": "加班调休如何登记?请给出办理入口。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请按步骤说明。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?", "label": "企业知识"}
+{"query": "如何理解缓存穿透?", "label": "通用知识"}
+{"query": "如何排查接口超时?", "label": "通用知识"}
+{"query": "餐补报销有什么标准?请给出办理入口。", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请按步骤说明。", "label": "企业知识"}
+{"query": "跨部门协作如何发起?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?需要哪些材料?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?请给出办理入口。", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?需要哪些材料?", "label": "企业知识"}
+{"query": "项目立项流程是什么?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?", "label": "企业知识"}
+{"query": "售后问题如何升级?有注意事项吗?", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?需要哪些材料?", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?有注意事项吗?", "label": "企业知识"}
+{"query": "产品手册更新后如何同步?", "label": "企业知识"}
+{"query": "客户常见故障如何定位?请给出办理入口。", "label": "企业知识"}
+{"query": "售后问题如何升级?", "label": "企业知识"}
+{"query": "如何查看最新版本的交付SOP?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?需要哪些材料?", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?需要哪些材料?", "label": "企业知识"}
+{"query": "售后问题如何升级?需要哪些材料?", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请按步骤说明。", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "跨部门协作如何发起?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是RESTful API?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?有注意事项吗?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "NDA模板在哪里下载?有注意事项吗?", "label": "企业知识"}
+{"query": "如何用SQL进行表连接?", "label": "通用知识"}
+{"query": "如何申请CRM系统权限?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请按步骤说明。", "label": "企业知识"}
+{"query": "如何理解父子分块?", "label": "通用知识"}
+{"query": "敏感数据可以上传知识库吗?请给出办理入口。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?有注意事项吗?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?有注意事项吗?", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请按步骤说明。", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?请给出办理入口。", "label": "企业知识"}
+{"query": "合同归档有什么要求?有注意事项吗?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "离职交接需要完成哪些事项?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是JWT认证?", "label": "通用知识"}
+{"query": "差旅报销需要哪些附件?", "label": "企业知识"}
+{"query": "加班调休如何登记?请按步骤说明。", "label": "企业知识"}
+{"query": "会议室预订规则是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?有注意事项吗?", "label": "企业知识"}
+{"query": "办公用品采购如何申请?需要哪些材料?", "label": "企业知识"}
+{"query": "员工福利在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "餐补报销有什么标准?请按步骤说明。", "label": "企业知识"}
+{"query": "外部分享文档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "合同归档有什么要求?需要哪些材料?", "label": "企业知识"}
+{"query": "远程办公需要申请哪些权限?有注意事项吗?", "label": "企业知识"}
+{"query": "合同审批需要哪些步骤?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?", "label": "企业知识"}
+{"query": "项目立项流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "发票抬头和税号在哪里查看?请给出办理入口。", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?需要哪些材料?", "label": "企业知识"}
+{"query": "产品参数表在哪里查看?需要哪些材料?", "label": "企业知识"}
+{"query": "试用期转正流程是什么?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请CRM系统权限?需要哪些材料?", "label": "企业知识"}
+{"query": "加班调休如何登记?需要哪些材料?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "合同归档有什么要求?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?请按步骤说明。", "label": "企业知识"}
+{"query": "项目立项流程是什么?需要哪些材料?", "label": "企业知识"}
+{"query": "解释一下HTTP和HTTPS的区别。", "label": "通用知识"}
+{"query": "解释一下同步和异步的区别。", "label": "通用知识"}
+{"query": "客户要求修改责任条款怎么办?请给出办理入口。", "label": "企业知识"}
+{"query": "什么是向量数据库?", "label": "通用知识"}
+{"query": "采购付款流程是什么?请按步骤说明。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?", "label": "企业知识"}
+{"query": "客户要求修改责任条款怎么办?需要哪些材料?", "label": "企业知识"}
+{"query": "请说明BM25算法的基本思想。", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?有注意事项吗?", "label": "企业知识"}
+{"query": "费用单被驳回后怎么处理?", "label": "企业知识"}
+{"query": "年假如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?请给出办理入口。", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?", "label": "企业知识"}
+{"query": "知识库文档过期如何处理?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请给出办理入口。", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?需要哪些材料?", "label": "企业知识"}
+{"query": "什么是Docker镜像?", "label": "通用知识"}
+{"query": "客户工单优先级怎么判定?请按步骤说明。", "label": "企业知识"}
+{"query": "办公用品采购如何申请?请给出办理入口。", "label": "企业知识"}
+{"query": "知识库答案不准确如何反馈?有注意事项吗?", "label": "企业知识"}
+{"query": "VPN无法连接怎么办?", "label": "企业知识"}
+{"query": "发现账号异常登录怎么办?有注意事项吗?", "label": "企业知识"}
+{"query": "什么是WebSocket?", "label": "通用知识"}
+{"query": "如何查询组织架构和部门负责人?请按步骤说明。", "label": "企业知识"}
+{"query": "售后问题如何升级?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?请按步骤说明。", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?有注意事项吗?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?需要哪些材料?", "label": "企业知识"}
+{"query": "病假需要上传什么证明?有注意事项吗?", "label": "企业知识"}
+{"query": "SOP变更后如何通知相关人员?有注意事项吗?", "label": "企业知识"}
+{"query": "如何查询组织架构和部门负责人?请给出办理入口。", "label": "企业知识"}
+{"query": "入职当天需要完成哪些事项?需要哪些材料?", "label": "企业知识"}
+{"query": "敏感数据可以上传知识库吗?请按步骤说明。", "label": "企业知识"}
+{"query": "如何计算文本相似度?", "label": "通用知识"}
+{"query": "如何查看最新版本的交付SOP?有注意事项吗?", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?请给出办理入口。", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?有注意事项吗?", "label": "企业知识"}
+{"query": "采购付款流程是什么?", "label": "企业知识"}
+{"query": "邮箱密码忘记如何重置?需要哪些材料?", "label": "企业知识"}
+{"query": "企业网盘文件误删能恢复吗?需要哪些材料?", "label": "企业知识"}
+{"query": "电脑无法开机应该提交什么信息?", "label": "企业知识"}
+{"query": "如何申请代码仓库访问权限?请按步骤说明。", "label": "企业知识"}
+{"query": "病假需要上传什么证明?请给出办理入口。", "label": "企业知识"}
+{"query": "差旅报销需要哪些附件?有注意事项吗?", "label": "企业知识"}
+{"query": "会议室预订规则是什么?有注意事项吗?", "label": "企业知识"}

+ 13 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/微调脚本生成.md

@@ -0,0 +1,13 @@
+## 任务描述
+
+需要你完成一个基于bert-base-chinese,微调一个文本分类的任务,分类类表为“通用问题”和“企业知识”两个类。
+
+* 使用transformers模块的加载预训练模型,训练工具使用Trainer, TrainingArguments
+* 数据格式为jsonl数据,包含两个字段{"query": "Python如何实现快速排序?", "label": "通用知识"}
+
+## 输出要求
+
+* 分类的功能封装成一个类,需要包含预测接口,评估模型的接口,输出分类报告和混淆矩阵
+* 模型训练完整以后,需要保存效果最好权重
+* 数据和模型路径,作为参数形式,我自己来配置
+* 支持自适应设备,包括CPU,GPU和苹果笔记本MPS等设备

+ 37 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/提示词模块.txt

@@ -0,0 +1,37 @@
+你是一个高效的文本分类系统,任务是判断用户的查询属于“通用知识”还是“企业知识”。
+
+## 任务说明
+- “通用知识”类主要包含数学计算、代码生成/纠错、概念与原理问题以及其他常识性问题。
+- “企业知识”类专指与IT知识库内部赋能相关的查询,如知识文档详情、专家支持介绍、项目内容、内部赋能周期、地点、时间和费用等。
+
+## 要求
+1. 请仔细阅读以下查询文本,判断其所属类别。
+2. 根据查询文本的内容和上下文,输出对应的分类标签:
+   - 如果内容涉及IT内部赋能(例如JAVA、AI、测试等知识文档介绍、内部赋能服务、专家支持力量、学习周期等),请标注为“企业知识”;
+   - 如果内容主要涉及计算、代码处理或基础概念问题,请标注为“通用知识”。
+3. 输出格式为JSON:包含"query"(原始查询文本)和"label"(分类结果)的键值对。
+
+## 示例
+输入示例:
+  “什么AI?”
+输出示例:
+{"query": "什么AI?","label": "通用知识"}
+
+输入示例:
+  “JAVA的知识文档目录是什么?”
+输出示例:
+{"query": "JAVA的知识文档目录是什么?","label": "企业知识"}
+
+## 注意事项
+- 请不要输出除结果之外的多余文字;
+- 如果存在歧义,请优先判断是否涉及具体的IT知识库内部赋能服务,如果是,则分类为“企业知识”;
+- 输出时请保持标签仅为“通用知识”或“企业知识”。
+
+## 输出要求
+1. 一共需要生成200条训练集,注意训练集之间一定要两个类别生成的数量是均衡的,而且要多样性。我需要你边生成边检查,保证数据的质量
+2. 输出格式必须是json格式输出例子为
+{"query": "xxx", "label": "xxx"}
+其中"xxx",需要按照要求生成样本数据
+3. 直接输出json文件提供下载,不要输出别的内容
+
+4. 不要通过写python脚本生成数据,一定要你逐条生成数据

+ 5 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/classify_data/数据生成与效果评估.md

@@ -0,0 +1,5 @@
+## 可以搭建coze或者dify智能体生成数据
+
+1. 数据生成节点:训练数据生成
+2. 质量过滤节点:过滤掉文本过段,低质量的训练数据
+3. 自动化评估工作流,评估模型效果

+ 112 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/document_processor.py

@@ -0,0 +1,112 @@
+# 这个脚本讲义的代码架构图没有体现,需要进行补充
+import os
+from langchain_community.document_loaders import TextLoader
+from langchain_community.document_loaders.markdown import UnstructuredMarkdownLoader  # NLTK
+from langchain_text_splitters import MarkdownTextSplitter
+from datetime import datetime
+from rag_qa.kb_document_loaders import OCRPDFLoader, OCRDOCLoader, OCRPPTLoader, OCRIMGLoader
+from rag_qa.kb_text_spliter import ChineseRecursiveTextSplitter
+from base import logger, Config
+
+conf = Config()
+# 定义支持的文件类型及其对应的加载器字典
+document_loaders = {
+    # 文本文件使用 TextLoader
+    ".txt": TextLoader,
+    # PDF 文件使用 OCRPDFLoader
+    ".pdf": OCRPDFLoader,
+    # Word 文件使用 OCRDOCLoader
+    ".docx": OCRDOCLoader,
+    # PPT 文件使用 OCRPPTLoader
+    ".ppt": OCRPPTLoader,
+    # PPTX 文件使用 OCRPPTLoader
+    ".pptx": OCRPPTLoader,
+    # JPG 文件使用 OCRIMGLoader
+    ".jpg": OCRIMGLoader,
+    # PNG 文件使用 OCRIMGLoader
+    ".png": OCRIMGLoader,
+    # Markdown 文件使用 UnstructuredMarkdownLoader
+    ".md": UnstructuredMarkdownLoader
+}
+
+
+# 定义函数,从指定文件夹加载多种类型文件并添加元数据
+def load_documents_from_directory(directory_path):
+    documents = []
+    supported_extensions = document_loaders.keys()
+    source = os.path.basename(directory_path).replace("_data", "")
+    for root, _, files in os.walk(directory_path):
+        for file in files:
+            file_path = os.path.join(root, file)
+            file_extension = os.path.splitext(file_path)[1].lower()
+            if file_extension in supported_extensions:
+                try:
+                    loader_class = document_loaders[file_extension]
+                    if file_extension == ".txt":
+                        loader = loader_class(file_path, encoding="utf-8")
+                    else:
+                        loader = loader_class(file_path)
+
+                    #writer.write()
+                    #写手 写作
+                    #loader.load()
+                    loaded_docs = loader.load()
+                    for doc in loaded_docs:
+                        doc.metadata["source"] = source
+                        doc.metadata["file_path"] = file_path
+                        doc.metadata["timestamp"] = datetime.now().isoformat()
+                    documents.extend(loaded_docs)
+                    logger.info(f"成功加载文件: {file_path}")
+                except Exception as e:
+                    logger.error(f"加载文件 {file_path} 失败: {str(e)}")
+            else:
+                logger.warning(f"不支持的文件类型: {file_path}")
+    return documents
+
+
+# 定义函数,处理文档并进行分层切分,返回子块结果
+def process_documents(directory_path, parent_chunk_size=conf.PARENT_CHUNK_SIZE,
+                      child_chunk_size=conf.CHILD_CHUNK_SIZE,
+                      chunk_overlap=conf.CHUNK_OVERLAP):
+    # 从指定目录加载所有文档
+    documents = load_documents_from_directory(directory_path)
+    # 记录加载的文档总数日志
+    logger.info(f"加载的文档数量: {len(documents)}")
+
+    # 初始化父块和子块分词器(通用) /n/n /n   
+    parent_splitter = ChineseRecursiveTextSplitter(chunk_size=parent_chunk_size, chunk_overlap=chunk_overlap)
+    child_splitter = ChineseRecursiveTextSplitter(chunk_size=child_chunk_size, chunk_overlap=chunk_overlap)
+    # 初始化 Markdown 专用分词器 # ##
+    markdown_parent_splitter = MarkdownTextSplitter(chunk_size=parent_chunk_size, chunk_overlap=chunk_overlap)
+    markdown_child_splitter = MarkdownTextSplitter(chunk_size=child_chunk_size, chunk_overlap=chunk_overlap)
+
+    # 初始化空列表,用于存储所有子块
+    child_chunks = []
+    for i, doc in enumerate(documents):
+        file_extension = os.path.splitext(doc.metadata.get("file_path", ''))[1].lower()
+        is_markdown = (file_extension == '.md')
+        parent_splitter_to_use = markdown_parent_splitter if is_markdown else parent_splitter
+        child_splitter_to_use = markdown_child_splitter if is_markdown else child_splitter
+        logger.info(
+            f"处理文档: {doc.metadata['file_path']}, 使用切分器: {'Markdown' if is_markdown else 'ChineseRecursive'}")
+        parent_docs = parent_splitter_to_use.split_documents([doc])
+        logger.info(f"父块数量: {len(parent_docs)}")
+        for j, parent_doc in enumerate(parent_docs):
+            parent_id = f"doc_{i}_parent_{j}"
+            sub_chunks = child_splitter_to_use.split_documents([parent_doc])
+            for k, sub_chunk in enumerate(sub_chunks):
+                sub_chunk.metadata["parent_id"] = parent_id
+                sub_chunk.metadata["parent_content"] = parent_doc.page_content
+                sub_chunk.metadata["id"] = f"{parent_id}_child_{k}"
+                child_chunks.append(sub_chunk)
+
+    logger.info(f"子块数量: {len(child_chunks)}")
+    return child_chunks
+
+
+if __name__ == '__main__':
+    directory_path = '/app/rag_qa/data/enterprise_data'
+    # documents = load_documents_from_directory(directory_path)
+    # print(documents)
+    child_chunks = process_documents(directory_path)
+    print(f'child_chunks--》{child_chunks[0]}')

+ 96 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/prompts.py

@@ -0,0 +1,96 @@
+# core/prompts.py
+# 导入 PromptTemplate 类,用于创建 Prompt 模板
+from langchain_core.prompts import PromptTemplate
+
+
+# 定义 RAGPrompts 类,用于管理所有 Prompt 模板
+class RAGPrompts:
+    # 定义 RAG 提示模板
+    @staticmethod
+    def rag_prompt():
+        # 创建并返回 PromptTemplate 对象
+        return PromptTemplate(
+            template="""  
+            你是一个智能助手,帮助用户回答问题。  
+            如果提供了上下文,请基于上下文回答;如果没有上下文,请直接根据你的知识回答。  
+            如果答案来源于检索到的文档,请在回答中说明。
+
+            上下文: {context}  
+            问题: {question}  
+
+            如果无法回答,请回复:“信息不足,无法回答,请联系人工客服,电话:{phone}。”  
+            回答:  
+            """,
+            #   定义输入变量
+            input_variables=["context", "question", "phone"],
+        )
+        # @staticmethod
+    # def rag_prompt():
+    #     return PromptTemplate(
+    #         template="""
+    #     你是一个智能助手,负责帮助用户回答问题。请按照以下步骤处理:
+    # 
+    #     1. **分析问题和上下文**:
+    #        - 基于提供的上下文(如果有)和你的知识回答问题。
+    #        - 如果答案来源于检索到的文档,请在回答中明确说明,例如:“根据提供的文档,……”。
+    # 
+    #     2. **评估对话历史**:
+    #        - 检查对话历史是否与当前问题相关(例如,是否涉及相同的话题、实体或问题背景)。
+    #        - 如果对话历史与问题相关,请结合历史信息生成更准确的回答。
+    #        - 如果对话历史无关(例如,仅包含问候或不相关的内容),忽略历史,仅基于上下文和问题回答。
+    # 
+    #     3. **生成回答**:
+    #        - 提供清晰、准确的回答,避免无关信息。
+    #        - 如果上下文和历史消息均不足以回答问题,请回复:“信息不足,无法回答,请联系人工客服,电话:{phone}。”
+    # 
+    #     **上下文**: {context}
+    #     **对话历史**:
+    #     {history}
+    #     **问题**: {question}
+    # 
+    #     **回答**:
+    #     """,
+    #         input_variables=["context", "history", "question", "phone"],
+    #     )
+
+    # 定义假设问题生成的 Prompt 模板
+    @staticmethod
+    def hyde_prompt():
+        #   创建并返回 PromptTemplate 对象
+        return PromptTemplate(
+            template="""  
+            假设你是用户,想了解以下问题,请生成一个简短的假设答案:  
+            问题: {query}  
+            假设答案:  
+            """,
+            #   定义输入变量
+            input_variables=["query"],
+        )
+
+    #   定义子查询生成的 Prompt 模板
+    @staticmethod
+    def subquery_prompt():
+        #   创建并返回 PromptTemplate 对象
+        return PromptTemplate(
+            template="""  
+            将以下复杂查询分解为多个简单子查询,每行一个子查询:  
+            查询: {query}  
+            子查询:  
+            """,
+            #   定义输入变量
+            input_variables=["query"],
+        )
+
+    #   定义回溯问题生成的 Prompt 模板
+    @staticmethod
+    def backtracking_prompt():
+        #   创建并返回 PromptTemplate 对象
+        return PromptTemplate(
+            template="""  
+            将以下复杂查询简化为一个更简单的问题:  
+            查询: {query}  
+            简化问题:  
+            """,
+            #   定义输入变量
+            input_variables=["query"],
+        )

+ 262 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/query_classifier.py

@@ -0,0 +1,262 @@
+# 导入标准库
+import json
+import os
+# 导入 PyTorch
+import torch
+# 导入日志
+import sys
+from base.logger import logger
+# 导入numpy
+import numpy as np
+# 导入 Transformers 库
+from transformers import BertTokenizer, BertForSequenceClassification
+from transformers import Trainer, TrainingArguments
+# 导入train_test_split
+from sklearn.model_selection import train_test_split
+from sklearn.metrics import classification_report, confusion_matrix
+
+current_dir = os.path.dirname(os.path.abspath(__file__))
+rag_qa_path = os.path.abspath(os.path.dirname(os.path.abspath(current_dir)))
+project_root = os.path.abspath(os.path.dirname(os.path.abspath(rag_qa_path)))
+sys.path.insert(0, project_root)
+
+class QueryClassifier:
+    def __init__(self, model_path='models/bert_query_classifier'):
+       # 加载bert
+        self.pre_trained_model_path = f'{rag_qa_path}/models/bert-base-chinese'
+        # 模型训练以后保存的位置
+        self.model_path = model_path
+        # 加载 BERT 分词器
+        self.tokenizer = BertTokenizer.from_pretrained(self.pre_trained_model_path)
+        # 初始化模型
+        self.model = None
+        # 确定设备(GPU 或 CPU)
+        self.device =  torch.device("cuda" if torch.cuda.is_available() else "mps" if torch.mps.is_available() else "cpu" )
+        # 记录设备信息
+        logger.info(f"使用设备: {self.device}")
+        # 定义标签映射
+        self.label_map = {"通用知识": 0, "企业知识": 1}
+        # 加载模型
+        self.load_model()
+
+    def load_model(self):
+        # 检查模型路径是否存在
+        if os.path.exists(self.model_path):
+            # 加载预训练模型
+            self.model = BertForSequenceClassification.from_pretrained(self.model_path)
+            # 将模型移到指定设备
+            self.model.to(self.device)
+            # 记录加载成功的日志
+            logger.info(f"加载模型: {self.model_path}")
+        else:
+            # 初始化新模型
+            self.model = BertForSequenceClassification.from_pretrained(self.pre_trained_model_path, num_labels=2)
+            # 将模型移到指定设备
+            self.model.to(self.device)
+            # 记录初始化模型的日志
+            logger.info("初始化新 BERT 模型")
+
+    def save_model(self):
+        """保存模型"""
+        self.model.save_pretrained(self.model_path)
+        self.tokenizer.save_pretrained(self.model_path)
+        logger.info(f"模型保存至: {self.model_path}")
+
+    def preprocess_data(self, texts, labels):
+        """预处理数据为 BERT 输入格式"""
+        encodings = self.tokenizer(
+            texts,
+            truncation=True,
+            padding=True,
+            max_length=128,
+            return_tensors="pt"
+        )
+        # encodings 是一个可以像字典一样使用的 BatchEncoding 对象。
+        # 因为 return_tensors="pt",其中每个字段的值都是 PyTorch 张量。
+        # 例如 texts = ["什么是人工智能?", "公司的报销流程是什么?"] 时,结构大致为:
+        # {
+        #     "input_ids": tensor([[101, ..., 102, 0, ...], [101, ..., 102]]),
+        #     "token_type_ids": tensor([[0, ..., 0], [0, ..., 0]]),
+        #     "attention_mask": tensor([[1, ..., 1, 0, ...], [1, ..., 1]])
+        # }
+        # input_ids:每个 token 在 BERT 词表中的数字编号。
+        # token_type_ids:标识 token 属于第几段文本;此处都是单句输入,所以通常全为 0。
+        # attention_mask:1 表示真实 token,0 表示为对齐长度而补入的 [PAD]。
+        # 上例中每个张量的形状都是 (2, L):2 是文本数量,L 是批次补齐后的长度,最大为 128。
+        # labels 同时会被转为数字标签:["通用知识", "企业知识"] -> [0, 1]。
+        return encodings, [self.label_map[label] for label in labels]
+
+    def create_dataset(self, encodings, labels):
+        """创建 PyTorch 数据集"""
+
+        class Dataset(torch.utils.data.Dataset):
+            def __init__(self, encodings, labels):
+                self.encodings = encodings
+                self.labels = labels
+
+            def __getitem__(self, idx):
+                item = {key: val[idx] for key, val in self.encodings.items()}
+                item["labels"] = torch.tensor(self.labels[idx])
+                return item
+
+            def __len__(self):
+                return len(self.labels)
+
+        return Dataset(encodings, labels)
+
+    def train_model(self, data_file="training_dataset_hybrid_5000.json"):
+        """训练 BERT 分类模型"""
+        # 加载数据集
+        if not os.path.exists(data_file):
+            logger.error(f"数据集文件 {data_file} 不存在")
+            raise FileNotFoundError(f"数据集文件 {data_file} 不存在")
+
+        with open(data_file, "r", encoding="utf-8") as f:
+            data = [json.loads(value) for value in f.readlines()]
+
+        texts = [item["query"] for item in data]
+        labels = [item["label"] for item in data]
+
+        # 将文本和对应标签同步划分为训练集和验证集
+        # test_size=0.2:20% 的数据用作验证集,其余 80% 用作训练集
+        # random_state=42:固定随机种子,使每次运行都得到相同的划分结果
+        # 返回值都是 Python 列表,文本与同一下标的标签一一对应,例如:
+        # train_texts = ["什么是人工智能?", "公司的报销流程是什么?"]
+        # train_labels = ["通用知识", "企业知识"]
+        # val_texts = ["地球上有多少个大洲?"]
+        # val_labels = ["通用知识"]
+        train_texts, val_texts, train_labels, val_labels = train_test_split(
+            texts, labels, test_size=0.2, random_state=42
+        )
+
+        # 预处理
+        train_encodings, train_labels = self.preprocess_data(train_texts, train_labels)
+        val_encodings, val_labels = self.preprocess_data(val_texts, val_labels)
+
+        # 创建数据集
+        train_dataset = self.create_dataset(train_encodings, train_labels)
+        # print(f'train_dataset--》{train_dataset[0]}')
+        val_dataset = self.create_dataset(val_encodings, val_labels)
+        #
+        # 设置训练参数
+        training_args = TrainingArguments(
+            # 设置模型和检查点保存的目录路径
+            output_dir="./bert_results",
+            # 设置训练的总轮数为3轮
+            num_train_epochs=3,
+            # 设置每个设备(GPU/CPU)上的训练批次大小为8
+            per_device_train_batch_size=8,
+            # 设置每个设备(GPU/CPU)上的评估批次大小为8
+            per_device_eval_batch_size=8,
+            # 设置学习率预热步数为500步,训练初期学习率从0逐渐增加到设定值
+            warmup_steps=500,
+            # 设置权重衰减系数为0.01,用于防止过拟合
+            weight_decay=0.01,
+            # 设置日志文件保存的目录路径
+            logging_dir="./bert_logs",
+            # 设置每10个训练步骤记录一次日志
+            logging_steps=10,
+            # 设置评估策略为每个epoch结束后进行评估
+            eval_strategy="epoch",
+            # 设置模型保存策略为每个epoch结束后保存
+            save_strategy="epoch",
+            # 设置训练结束后加载最佳模型而非最后一个模型
+            load_best_model_at_end=True,
+            # 设置最多保存1个检查点文件,超出时自动删除旧的
+            save_total_limit=1,
+            # 设置用于判断最佳模型的指标为评估损失
+            metric_for_best_model="eval_loss",
+            # 禁用FP16混合精度训练,使用FP32精度
+            fp16=False,
+        )
+
+        # 初始化 Trainer
+        trainer = Trainer(
+            # 传入要训练的模型实例
+            model=self.model,
+            # 传入上面定义的训练参数配置
+            args=training_args,
+            # 传入训练数据集
+            train_dataset=train_dataset,
+            # 传入验证数据集,用于训练过程中评估模型性能
+            eval_dataset=val_dataset,
+            # 传入计算评估指标的函数,用于在验证集上计算准确率等指标
+            compute_metrics=self.compute_metrics
+        )
+        # 训练模型
+        logger.info("开始训练 BERT 模型...")
+        trainer.train()
+        self.save_model()
+
+        # 评估模型
+        self.evaluate_model(val_texts, val_labels)
+
+    def compute_metrics(self, eval_pred):
+        """计算评估指标"""
+        logits, labels = eval_pred
+        predictions = np.argmax(logits, axis=-1)
+        accuracy = (predictions == labels).mean()
+        return {"accuracy": accuracy}
+
+    def evaluate_model(self, texts, labels):
+        """评估模型性能"""
+        # 仅对 texts 进行分词,labels 已为数字
+        encodings = self.tokenizer(
+            texts,
+            truncation=True,
+            padding=True,
+            max_length=128,
+            return_tensors="pt"
+        )
+        dataset = self.create_dataset(encodings, labels)
+
+        trainer = Trainer(model=self.model)
+        predictions = trainer.predict(dataset)
+        pred_labels = np.argmax(predictions.predictions, axis=-1)
+        true_labels = labels  # 直接使用数字标签
+
+        logger.info("分类报告:")
+        logger.info(classification_report(
+            true_labels,
+            pred_labels,
+            target_names=["通用知识", "企业知识"]
+        ))
+        logger.info("混淆矩阵:")
+        logger.info(confusion_matrix(true_labels, pred_labels))
+
+    def predict_category(self, query):
+        # 检查模型是否加载
+        if self.model is None:
+            # 模型未加载,记录错误
+            logger.error("模型未训练或加载")
+            # 默认返回通用知识
+            return "通用知识"
+        # 对查询进行编码
+        encoding = self.tokenizer(query, truncation=True, padding=True, max_length=128, return_tensors="pt")
+        # 将编码移到指定设备
+        encoding = {k: v.to(self.device) for k, v in encoding.items()}
+        # 不计算梯度,进行预测
+        with torch.no_grad():
+            # 获取模型输出
+            outputs = self.model(**encoding)
+            # 获取预测结果
+            prediction = torch.argmax(outputs.logits, dim=1).item()
+        # 根据预测结果返回类别
+        return "企业知识" if prediction == 1 else "通用知识"
+
+if __name__ == "__main__":
+    # 初始化分类器
+    classifier = QueryClassifier(model_path="bert_query_classifier")
+
+    # 训练模型
+    classifier.train_model(data_file='../classify_data/model_generic_5000.json')
+    # 示例预测
+    test_queries = [
+        "AI知识域的知识文档目录是什么",
+        "JAVA知识文档费用多少?",
+        "5*9等于多少?",
+        "AI内部赋能有哪些支持人员?"
+    ]
+    for query in test_queries:
+        category = classifier.predict_category(query)
+        print(f"查询: {query} -> 分类: {category}")

+ 191 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/rag_system.py

@@ -0,0 +1,191 @@
+# core/rag_system.py 源码
+from rag_qa.core.prompts import RAGPrompts
+#   导入 time 模块,用于计算时间
+import time
+from base.config import config
+from base.logger import logger
+from rag_qa.core.query_classifier import QueryClassifier  # 导入查询分类器
+from rag_qa.core.strategy_selector import StrategySelector  # 导入策略选择器
+
+
+#   定义 RAGSystem 类,封装 RAG 系统的核心逻辑
+class RAGSystem:
+    #   初始化方法,设置 RAG 系统的基本参数
+    def __init__(self, vector_store, llm):
+        #   设置向量数据库对象
+        self.vector_store = vector_store
+        #   设置大语言模型调用函数
+        self.llm = llm
+        #   获取 RAG 提示模板
+        self.rag_prompt = RAGPrompts.rag_prompt()
+        #   初始化查询分类器
+        self.query_classifier =QueryClassifier(model_path=f'{config.MODELS_DIR}/bert_query_classifier')
+        #   初始化策略选择器
+        self.strategy_selector = StrategySelector()
+
+    #   定义私有方法,使用假设文档进行检索(HyDE)
+    def _retrieve_with_hyde(self, query):
+        logger.info(f"使用 HyDE 策略进行检索 (查询: '{query}')")
+        #   获取假设问题生成的 Prompt 模板
+        hyde_prompt_template = RAGPrompts.hyde_prompt() # 使用 template 后缀区分
+        #   调用大语言模型生成假设答案
+        try:
+            hypo_answer = self.llm(hyde_prompt_template.format(query=query)).strip()
+            logger.info(f"HyDE 生成的假设答案: '{hypo_answer}'")
+            #   使用假设答案进行检索,并返回检索结果
+            #   注意:HyDE 通常只用于生成检索向量,不一定需要 rerank 这一步,但这里复用了
+            return self.vector_store.hybrid_search_with_rerank(
+                hypo_answer, k=conf.RETRIEVAL_K # 使用 K 而非 M
+            )
+        except Exception as e:
+            logger.error(f"HyDE 策略执行失败: {e}")
+            return []
+
+
+    #   定义私有方法,使用子查询进行检索
+    def _retrieve_with_subqueries(self, query):
+        logger.info(f"使用子查询策略进行检索 (查询: '{query}')")
+        #   获取子查询生成的 Prompt 模板
+        subquery_prompt_template = RAGPrompts.subquery_prompt() # 使用 template 后缀区分
+        try:
+            #   调用大语言模型生成子查询列表
+            subqueries_text = self.llm(subquery_prompt_template.format(query=query)).strip()
+            subqueries = [q.strip() for q in subqueries_text.split("\n") if q.strip()]
+            logger.info(f"生成的子查询: {subqueries}")
+            if not subqueries:
+                 logger.warning("未能生成有效的子查询")
+                 return []
+
+            #   初始化空列表,用于存储所有子查询的检索结果
+            all_docs = []
+            #   遍历每个子查询
+            for sub_q in subqueries:
+                #   使用子查询进行检索,并将结果添加到列表中
+                #   这里对每个子查询都执行了 hybrid search + rerank,开销可能较大
+                docs = self.vector_store.hybrid_search_with_rerank(
+                    sub_q, k=conf.RETRIEVAL_K # 使用 K
+                )
+                all_docs.extend(docs)
+                logger.info(f"子查询 '{sub_q}' 检索到 {len(docs)} 个文档")
+
+            #   对所有检索结果进行去重 (基于对象内存地址,如果 Document 内容相同但对象不同则无法去重)
+            #   更可靠的去重方式是基于文档内容或 ID
+            unique_docs_dict = {doc.page_content: doc for doc in all_docs} # 基于内容去重
+            unique_docs = list(unique_docs_dict.values())
+
+            logger.info(f"所有子查询共检索到 {len(all_docs)} 个文档, 去重后剩 {len(unique_docs)} 个")
+            #   返回去重后的文档,限制数量 (是否需要在此处限制? retrieve_and_merge 末尾会限制)
+            # return unique_docs[: Config.CANDIDATE_M]
+            return unique_docs # 返回所有唯一文档,让 retrieve_and_merge 处理数量
+
+        except Exception as e:
+            logger.error(f"子查询策略执行失败: {e}")
+            return []
+
+    #   定义私有方法,使用回溯问题进行检索
+    def _retrieve_with_backtracking(self, query):
+        logger.info(f"使用回溯问题策略进行检索 (查询: '{query}')")
+        #   获取回溯问题生成的 Prompt 模板
+        backtrack_prompt_template = RAGPrompts.backtracking_prompt() # 使用 template 后缀区分
+        try:
+            #   调用大语言模型生成回溯问题
+            simplified_query = self.llm(backtrack_prompt_template.format(query=query)).strip()
+            logger.info(f"生成的回溯问题: '{simplified_query}'")
+            #   使用回溯问题进行检索,并返回检索结果
+            return self.vector_store.hybrid_search_with_rerank(
+                simplified_query, k=conf.RETRIEVAL_K # 使用 K
+            )
+        except Exception as e:
+            logger.error(f"回溯问题策略执行失败: {e}")
+            return []
+
+    #   定义方法,检索并合并相关文档
+    def retrieve_and_merge(self, query, source_filter=None, strategy=None):  #   新增 strategy 参数
+        #   如果未指定检索策略,则使用策略选择器选择
+        if not strategy:
+            strategy = self.strategy_selector.select_strategy(query)
+
+        #   根据检索策略选择不同的检索方式
+        ranked_sub_chunks = [] # 初始化
+        if strategy == "回溯问题检索":
+            ranked_sub_chunks = self._retrieve_with_backtracking(query)
+        elif strategy == "子查询检索":
+            ranked_sub_chunks = self._retrieve_with_subqueries(query) # 返回的是唯一文档列表
+             # 注意:子查询返回的是已 rerank 过的父文档或子块列表,后续合并逻辑可能需要调整
+             # 当前实现中,子查询返回的是初步检索(可能已rerank)的块,再进行合并
+        elif strategy == "假设问题检索":
+            ranked_sub_chunks = self._retrieve_with_hyde(query)
+        else:  #   默认或“直接检索”
+            logger.info(f"使用直接检索策略 (查询: '{query}')")
+            ranked_sub_chunks = self.vector_store.hybrid_search_with_rerank(
+                query, k=conf.RETRIEVAL_K, source_filter=source_filter
+            ) # 注意 hybrid_search_with_rerank 返回的是 rerank 后的父文档
+
+        logger.info(f"策略 '{strategy}' 检索到 {len(ranked_sub_chunks)} 个候选文档 (可能已是父文档)")
+        final_context_docs = ranked_sub_chunks[:conf.CANDIDATE_M]
+        logger.info(f"最终选取 {len(final_context_docs)} 个文档作为上下文")
+        return final_context_docs
+
+    #   定义方法,生成答案
+    def generate_answer(self, query, source_filter=None):
+        #   记录查询开始时间
+        start_time = time.time()
+        logger.info(f"开始处理查询: '{query}', 知识域过滤: {source_filter}")
+
+        #   判断查询类型
+        query_category = self.query_classifier.predict_category(query)
+        logger.info(f"查询分类结果:{query_category} (查询: '{query}')")
+
+        #   如果查询属于“通用知识”类别,则直接使用 LLM 回答
+        if query_category == "通用知识":
+            logger.info("查询为通用知识,直接调用 LLM")
+             prompt_input = self.rag_prompt.format(
+                context="", question=query, history="", phone=config.CUSTOMER_SERVICE_PHONE
+            )  #   不使用上下文
+            try:
+                answer = self.llm(prompt_input)
+            except Exception as e:
+                logger.error(f"直接调用 LLM 失败: {e}")
+                answer = f"抱歉,处理您的通用知识问题时出错。请联系人工客服:{conf.CUSTOMER_SERVICE_PHONE}"
+            processing_time = time.time() - start_time
+            logger.info(
+                f"通用知识查询处理完成 (耗时: {processing_time:.2f}s, 查询: '{query}')"
+            )
+            return answer
+
+        #   否则,进行 RAG 检索并生成答案
+        logger.info("查询为企业知识,执行 RAG 流程")
+        #   选择检索策略
+        strategy = self.strategy_selector.select_strategy(query)
+
+        #   检索相关文档
+        context_docs = self.retrieve_and_merge(
+            query, source_filter=source_filter, strategy=strategy
+        )  #   传递 strategy
+
+        #   准备上下文
+        if context_docs:
+            context = "\n\n".join([doc.page_content for doc in context_docs]) # 使用换行符分隔文档
+            logger.info(f"构建上下文完成,包含 {len(context_docs)} 个文档块")
+            # logger.debug(f"上下文内容:\n{context[:500]}...") # Debug 日志可以打印部分上下文
+        else:
+            context = ""
+            logger.info("未检索到相关文档,上下文为空")
+
+        #   构造 Prompt,调用大语言模型生成答案
+        prompt_input = self.rag_prompt.format(
+            context=context, question=query, phone=conf.CUSTOMER_SERVICE_PHONE
+        )
+        # logger.debug(f"最终生成的 Prompt:\n{prompt_input}") # Debug 日志
+
+        try:
+            answer = self.llm(prompt_input)
+        except Exception as e:
+            logger.error(f"调用 LLM 生成最终答案失败: {e}")
+            answer = f"抱歉,处理您的企业知识问题时出错。请联系人工客服:{conf.CUSTOMER_SERVICE_PHONE}"
+
+
+        #   记录查询处理完成的日志
+        processing_time = time.time() - start_time
+        logger.info(f"查询处理完成 (耗时: {processing_time:.2f}s, 查询: '{query}')")
+        return answer

+ 90 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/strategy_selector.py

@@ -0,0 +1,90 @@
+# core/strategy_selector.py 源码
+# 导入 LangChain 提示模板
+from langchain_core.prompts import PromptTemplate
+# 导入日志和配置
+from base.config import config
+from base.logger import logger
+# 导入 OpenAI
+from openai import OpenAI
+
+
+class StrategySelector:
+    def __init__(self):
+        # 初始化 OpenAI 客户端
+        self.client = OpenAI(api_key=config.DASHSCOPE_API_KEY,
+                             base_url=config.DASHSCOPE_BASE_URL)
+        # 获取策略选择提示模板
+        self.strategy_prompt_template = self._get_strategy_prompt()
+
+    def call_dashscope(self, prompt):
+        # 调用 DashScope API
+        try:
+            # 创建聊天完成请求
+            completion = self.client.chat.completions.create(
+                model=Config().LLM_MODEL,
+                messages=[
+                    {"role": "system", "content": "你是一个有用的助手,能够根据用户输入的Prompt严格执行并返回可靠的结果"},
+                    {"role": "user", "content": prompt},
+                ],
+                temperature=0.1
+            )
+            # 返回完成结果
+            return completion.choices[0].message.content if completion.choices else "直接检索"
+        except Exception as e:
+            # 记录 API 调用失败
+            logger.error(f"DashScope API 调用失败: {e}")
+            # 默认返回直接检索
+            return "直接检索"
+
+
+    def _get_strategy_prompt(self):
+        #   定义私有方法,获取策略选择 Prompt 模板
+        return PromptTemplate(
+            template="""
+            你是一个智能助手,负责分析用户查询 {query},并从以下四种检索增强策略中选择一个最适合的策略,直接返回策略名称,不需要解释过程。
+
+            以下是几种检索增强策略及其适用场景:
+
+            1.  **直接检索:**
+                * 描述:对用户查询直接进行检索,不进行任何增强处理。
+                * 适用场景:适用于查询意图明确,需要从知识库中检索**特定信息**的问题,例如:
+                    * 示例:
+                        * 查询:AI 知识域报销标准是多少?
+                        * 策略:直接检索
+                    * 查询:JAVA的知识文档目录是什么?
+                        * 策略:直接检索
+            2.  **假设问题检索(HyDE):**
+                * 描述:使用 LLM 生成一个假设的答案,然后基于假设答案进行检索。
+                * 适用场景:适用于查询较为抽象,直接检索效果不佳的问题,例如:
+                    * 示例:
+                        * 查询:人工智能在企业知识管理场景的应用有哪些?
+                        * 策略:假设问题检索
+            3.  **子查询检索:**
+                * 描述:将复杂的用户查询拆分为多个简单的子查询,分别检索并合并结果。
+                * 适用场景:适用于查询涉及多个实体或方面,需要分别检索不同信息的问题,例如:
+                    * 示例:
+                        * 查询:比较 Milvus 和 Zilliz Cloud 的优缺点。
+                        * 策略:子查询检索
+            4.  **回溯问题检索:**
+                * 描述:将复杂的用户查询转化为更基础、更易于检索的问题,然后进行检索。
+                * 适用场景:适用于查询较为复杂,需要简化后才能有效检索的问题,例如:
+                    * 示例:
+                        * 查询:我有一个包含 100 亿条记录的数据集,想把它存储到 Milvus 中进行查询。可以吗?
+                        * 策略:回溯问题检索
+
+            根据用户查询 {query},直接返回最适合的策略名称,例如 "直接检索"。不要输出任何分析过程或其他内容。
+            """
+            ,
+            input_variables=["query"],
+        )
+
+    #   定义方法,选择检索策略
+    def select_strategy(self, query):
+        #   调用 LLM 获取检索策略
+        strategy = self.call_dashscope(self.strategy_prompt_template.format(query=query)).strip()
+        logger.info(f"为查询 '{query}' 选择的检索策略:{strategy}")
+        return strategy
+
+if __name__ == '__main__':
+    ss = StrategySelector()
+    ss.select_strategy('你好吗')

+ 256 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/vector_store.py

@@ -0,0 +1,256 @@
+# 导入 BGE-M3 嵌入函数,用于生成文档和查询的向量表示
+from milvus_model.hybrid import BGEM3EmbeddingFunction
+# 导入 Milvus 相关类,用于操作向量数据库
+from pymilvus import MilvusClient, DataType, AnnSearchRequest, WeightedRanker
+# 导入 Document 类,用于创建文档对象
+from langchain_core.documents import Document
+# 导入 CrossEncoder,用于重排序和 NLI 判断
+from sentence_transformers import CrossEncoder
+# 导入 hashlib 模块,用于生成唯一 ID 的哈希值
+import hashlib
+from base.config import config
+from base.logger import logger
+import sys
+import os
+# core/vector_store.py
+# 定义 VectorStore 类,封装向量存储和检索功能
+
+local_path = os.path.abspath(os.path.dirname(__file__))
+rag_qa_path = os.path.abspath(os.path.dirname(local_path))
+sys.path.insert(0, rag_qa_path)
+project_root = os.path.dirname(rag_qa_path)
+sys.path.insert(0, project_root)
+
+
+class VectorStore:
+    # 初始化方法,设置向量存储的基本参数
+    def __init__(self,
+                 collection_name=conf.MILVUS_COLLECTION_NAME,
+                 host=conf.MILVUS_HOST,
+                 port=conf.MILVUS_PORT,
+                 database=conf.MILVUS_DATABASE_NAME):
+        # 设置 Milvus 集合名称
+        self.collection_name = collection_name
+        # 设置 Milvus 主机地址
+        self.host = host
+        # 设置 Milvus 端口号
+        self.port = port
+        # 设置 Milvus 数据库名称
+        self.database = database
+        # 设置日志记录器
+        self.logger = logger
+        rerank_model_path = os.path.join(config.MODELS_DIR, 'bge-reranker-v2-m3')
+        # 初始化 BGE-Reranker 模型,用于重排序检索结果
+        # TODO device代表设备: mps:m1系列的mac/ cpu: cpu / cuda: nvidia的gpu。和操作系统无关
+        self.reranker = CrossEncoder(rerank_model_path, device='mps')
+        # 初始化 BGE-M3 嵌入函数,使用 CPU 设备,不启用 FP16
+        bge_m3_model_path = os.path.join(config.MODELS_DIR, 'bge-m3')
+        self.embedding_function = BGEM3EmbeddingFunction(
+            model_name_or_path=bge_m3_model_path
+            , use_f16=False,
+             device='mps'
+        )
+        # 获取稠密向量的维度
+        self.dense_dim = self.embedding_function.dim["dense"]
+        # 初始化 Milvus 客户端,连接到指定主机和数据库
+        self.client = MilvusClient(uri=f"http://{self.host}:{self.port}", db_name=self.database)
+        # 调用方法创建或加载 Milvus 集合
+        self._create_or_load_collection()
+
+    # 定义私有方法,创建或加载 Milvus 集合
+    def _create_or_load_collection(self):
+        # 检查指定集合是否已存在
+        if not self.client.has_collection(self.collection_name):
+            # 创建集合 Schema,禁用自动 ID,启用动态字段
+            schema = self.client.create_schema(auto_id=False, enable_dynamic_field=True)
+            # 添加 ID 字段,作为主键,VARCHAR 类型,最大长度 100
+            schema.add_field(field_name="id", datatype=DataType.VARCHAR, is_primary=True, max_length=100)
+            # 添加文本字段,VARCHAR 类型,最大长度 65535
+            schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=65535)
+            # 添加稠密向量字段,FLOAT_VECTOR 类型,维度由嵌入函数指定
+            schema.add_field(field_name="dense_vector", datatype=DataType.FLOAT_VECTOR, dim=self.dense_dim)
+            # 添加稀疏向量字段,SPARSE_FLOAT_VECTOR 类型
+            schema.add_field(field_name="sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR)
+            # 添加父块 ID 字段,VARCHAR 类型,最大长度 100
+            schema.add_field(field_name="parent_id", datatype=DataType.VARCHAR, max_length=100)
+            # 添加父块内容字段,VARCHAR 类型,最大长度 65535
+            schema.add_field(field_name="parent_content", datatype=DataType.VARCHAR, max_length=65535)
+            # 添加知识域字段,VARCHAR 类型,最大长度 50
+            schema.add_field(field_name="source", datatype=DataType.VARCHAR, max_length=50)
+            # 添加时间戳字段,VARCHAR 类型,最大长度 50
+            schema.add_field(field_name="timestamp", datatype=DataType.VARCHAR, max_length=50)
+
+            # 创建索引参数对象
+            index_params = self.client.prepare_index_params()
+            # 为稠密向量字段添加 IVF_FLAT 索引,度量类型为内积 (IP)
+            index_params.add_index(
+                field_name="dense_vector",
+                index_name="dense_index",
+                index_type="IVF_FLAT",
+                metric_type="IP",
+                params={"nlist": 128}
+            )
+            # 为稀疏向量字段添加 SPARSE_INVERTED_INDEX 索引,度量类型为内积 (IP)
+            index_params.add_index(
+                field_name="sparse_vector",
+                index_name="sparse_index",
+                index_type="SPARSE_INVERTED_INDEX",
+                metric_type="IP"
+            )
+
+            # 创建 Milvus 集合,应用定义的 Schema 和索引参数
+            self.client.create_collection(collection_name=self.collection_name, schema=schema,
+                                         index_params=index_params)
+            # 记录创建集合的日志
+            logger.info(f"已创建集合 {self.collection_name}")
+        # 如果集合已存在
+        else:
+            # 记录加载集合的日志
+            logger.info(f"已加载集合 {self.collection_name}")
+        # 将集合加载到内存,确保可立即查询 
+        self.client.load_collection(self.collection_name)
+
+    # 定义方法,向向量存储添加文档
+    def add_documents(self, documents):
+        # 提取所有文档的内容列表
+        texts = [doc.page_content for doc in documents]
+        # 使用 BGE-M3 嵌入函数生成文档的嵌入 [[300个字],[300个字]]
+        embeddings = self.embedding_function(texts) 
+        # 初始化空列表,用于存储插入的数据
+        data = []
+        # 遍历每个文档,带上索引 i
+        for i, doc in enumerate(documents):
+            # 生成文档内容的 MD5 哈希值,作为唯一 ID
+            text_hash = hashlib.md5(doc.page_content.encode('utf-8')).hexdigest()
+            # 初始化稀疏向量字典
+            sparse_vector = {}
+            # 获取第 i 行的稀疏向量数据
+            # embeddings['sparse'] = 
+            # [["1234":"0.01231","123":"0.03112",...],[...]]
+
+            row = embeddings['sparse'][[i],:] 
+            # 获取稀疏向量的非零值索引
+            indices = row.indices
+            # 获取稀疏向量的非零值
+            values = row.data
+            # 将索引和值配对,填充稀疏向量字典
+            for idx, value in zip(indices, values):
+                sparse_vector[idx] = value
+            # 创建数据字典,包含所有字段
+            data.append({
+                "id": text_hash,
+                "text": doc.page_content,
+                "dense_vector": embeddings["dense"][i],
+                "sparse_vector": sparse_vector,
+                "parent_id": doc.metadata["parent_id"],
+                "parent_content": doc.metadata["parent_content"],
+                "source": doc.metadata.get("source", "unknown"),
+                "timestamp": doc.metadata.get("timestamp", "unknown")
+            })
+        # 检查是否有数据需要插入
+        if data:
+            # 使用 upsert 操作插入数据,覆盖重复 ID
+            self.client.upsert(collection_name=self.collection_name, data=data)
+            # 记录插入或更新的文档数量日志
+            logger.info(f"已插入或更新 {len(data)} 个文档")
+
+    # 定义方法,执行混合检索并重排序
+    def hybrid_search_with_rerank(self, query, k=conf.RETRIEVAL_K, source_filter=None):
+        # 使用 BGE-M3 嵌入函数生成查询的嵌入
+        query_embeddings = self.embedding_function([str(query)])
+        # 获取查询的稠密向量
+        dense_query_vector = query_embeddings["dense"][0]
+        # 初始化查询的稀疏向量字典
+        sparse_query_vector = {}
+        # 获取查询稀疏向量的第 0 行数据
+        row = query_embeddings["sparse"][[0], :]
+        # 获取稀疏向量的非零值索引
+        indices = row.indices
+        # 获取稀疏向量的非零值
+        values = row.data
+        # 将索引和值配对,填充稀疏向量字典
+        for idx, value in zip(indices, values):
+            sparse_query_vector[idx] = value
+
+        # 初始化过滤表达式,默认不过滤
+        filter_expr = f"source == '{source_filter}'" if source_filter else ""
+        # 创建稠密向量搜索请求
+        dense_request = AnnSearchRequest(
+            data=[dense_query_vector],
+            anns_field="dense_vector",
+            param={"metric_type": "IP", "params": {"nprobe": 10}},
+            limit=k,
+            expr=filter_expr
+        )
+        # 创建稀疏向量搜索请求
+        sparse_request = AnnSearchRequest(
+            data=[sparse_query_vector],
+            anns_field="sparse_vector",
+            param={"metric_type": "IP", "params": {}},
+            limit=k,
+            expr=filter_expr
+        )
+
+        # 创建加权排序器,稀疏向量权重 0.7,稠密向量权重 1.0
+        ranker = WeightedRanker(0.7, 1.0)
+        # 执行混合搜索,返回 Top-K 结果
+        results = self.client.hybrid_search(
+            collection_name=self.collection_name,
+            reqs=[dense_request, sparse_request],
+            ranker=ranker,
+            limit=k,
+            output_fields=["text", "parent_id", "parent_content", "source", "timestamp"]
+        )[0]
+
+        # 将搜索结果转换为 Document 对象列表
+        sub_chunks = [self._doc_from_hit(hit["entity"]) for hit in results]
+        print(f'sub_chunks-->{len(sub_chunks)}')
+        # 从子块中提取去重的父文档
+        parent_docs = self._get_unique_parent_docs(sub_chunks)
+
+        # 如果有父文档,进行重排序
+        if parent_docs:
+            # 创建查询与文档内容的配对列表
+            pairs = [[query, doc.page_content] for doc in parent_docs]
+            # 使用 BGE-Reranker 计算每个配对的得分
+            scores = self.reranker.predict(pairs)
+            # 根据得分从高到低排序文档
+            ranked_parent_docs = [doc for _, doc in sorted(zip(scores, parent_docs), reverse=True)]
+        # 如果没有父文档,返回空列表
+        else:
+            ranked_parent_docs = []
+
+        # 返回前 k 个重排序后的文档
+        return ranked_parent_docs[:conf.CANDIDATE_M]
+
+    # 定义私有方法,从子块中提取去重的父文档
+    def _get_unique_parent_docs(self, sub_chunks):
+        # 初始化集合,用于存储已处理的父块内容(去重)
+        parent_contents = set()
+        # 初始化列表,用于存储唯一父文档
+        unique_docs = []
+        # 遍历所有子块
+        for chunk in sub_chunks:
+            # 获取子块的父块内容,默认为子块内容
+            parent_content = chunk.metadata.get("parent_content", chunk.page_content)
+            # 检查父块内容是否非空且未重复
+            if parent_content and parent_content not in parent_contents:
+                # 创建新的 Document 对象,包含父块内容和元数据
+                unique_docs.append(Document(page_content=parent_content, metadata=chunk.metadata))
+                # 将父块内容添加到去重集合
+                parent_contents.add(parent_content)
+        # 返回去重后的父文档列表
+        return unique_docs
+
+    # 定义私有方法,从 Milvus 查询结果创建 Document 对象
+    def _doc_from_hit(self, hit):
+        # 创建并返回 Document 对象,填充内容和元数据
+        return Document(
+            page_content=hit.get("text"),
+            metadata={
+                "parent_id": hit.get("parent_id"),
+                "parent_content": hit.get("parent_content"),
+                "source": hit.get("source"),
+                "timestamp": hit.get("timestamp")
+            }
+        )

+ 284 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/core/父块与子块切分说明.md

@@ -0,0 +1,284 @@
+# 父块与子块切分说明
+
+> 本文原来的“产品与售后知识库.md”只有约 243 个字符,适合说明短文档会得到 1 个父块和 1 个子块。要观察多个父块、多个子块以及父子块 ID 的对应关系,建议使用同一数据目录下的 `父子块切分演示知识库.md`。
+
+## 一、整体作用
+
+`process_documents()` 会先加载知识库文件,然后对每个文档进行两层切分:
+
+```text
+原始文档
+└── 父块 parent chunk
+    ├── 子块 child chunk
+    ├── 子块 child chunk
+    └── 子块 child chunk
+```
+
+这样做是为了同时兼顾检索准确性和上下文完整性:
+
+- 子块较短,适合生成向量并进行相似度检索。
+- 父块包含更多上下文,检索到子块后可以把对应的父块内容交给大模型回答。
+
+## 二、当前切分参数
+
+项目的 `config.ini` 中配置如下:
+
+```ini
+parent_chunk_size = 1200
+child_chunk_size = 300
+chunk_overlap = 50
+```
+
+含义是:
+
+- 父块最大长度约为 1200 个字符。
+- 子块最大长度约为 300 个字符。
+- 相邻块之间尽量保留约 50 个字符的重叠内容。
+
+实际切分时,分词器会优先按照 Markdown 标题、换行、句号等结构切分,所以块长度不一定刚好等于配置值。这里的长度通常由 Python 的 `len()` 计算。
+
+## 三、选择分词器
+
+代码先根据文件扩展名判断是否为 Markdown 文件:
+
+```python
+file_extension = os.path.splitext(doc.metadata.get("file_path", ""))[1].lower()
+is_markdown = (file_extension == ".md")
+```
+
+Markdown 文件使用 `MarkdownTextSplitter`:
+
+```python
+parent_splitter_to_use = markdown_parent_splitter if is_markdown else parent_splitter
+child_splitter_to_use = markdown_child_splitter if is_markdown else child_splitter
+```
+
+其他文件使用 `ChineseRecursiveTextSplitter`。
+
+## 四、以产品与售后知识库为例
+
+文件路径:
+
+```text
+rag_qa/data/enterprise_data/产品与售后知识库.md
+```
+
+文件内容如下:
+
+```markdown
+# 产品与售后知识库
+
+## 产品资料管理
+
+产品参数、版本说明、部署手册和常见问题应按产品线和版本号归档。对外回答客户问题时,应优先引用最新正式版本,不引用草稿或过期文档。
+
+## 故障排查流程
+
+处理客户故障时,应先收集产品版本、部署环境、故障现象、复现步骤、日志片段和影响范围。一级支持按SOP完成初步排查,无法解决时升级给产品线支持组。
+
+## 售后升级
+
+升级问题必须包含客户影响、紧急程度、已尝试方案和期望解决时间。紧急生产故障应同步值班负责人,并在问题关闭后沉淀复盘文档。
+```
+
+该文件大约只有 243 个字符,小于父块大小 1200,也小于子块大小 300。因此本文件会得到:
+
+```text
+1 个父块
+└── 1 个子块
+```
+
+## 五、原始文档对象
+
+文件加载后,会形成一个 LangChain `Document` 对象,大致如下:
+
+```python
+Document(
+    page_content="完整的产品与售后知识库内容",
+    metadata={
+        "source": "enterprise",
+        "file_path": ".../产品与售后知识库.md"
+    }
+)
+```
+
+其中:
+
+- `page_content`:文件正文。
+- `source`:知识域名称。目录名 `enterprise_data` 去掉 `_data` 后得到 `enterprise`。
+- `file_path`:文件的完整路径。
+
+## 六、父块的结果
+
+代码首先对原始文档进行父块切分:
+
+```python
+parent_docs = parent_splitter_to_use.split_documents([doc])
+```
+
+由于这个文件较短,结果是:
+
+```python
+len(parent_docs) == 1
+```
+
+唯一的父块内容基本就是整个文件:
+
+```python
+parent_doc.page_content = """
+# 产品与售后知识库
+
+## 产品资料管理
+
+产品参数、版本说明、部署手册和常见问题应按产品线和版本号归档。对外回答客户问题时,应优先引用最新正式版本,不引用草稿或过期文档。
+
+## 故障排查流程
+
+处理客户故障时,应先收集产品版本、部署环境、故障现象、复现步骤、日志片段和影响范围。一级支持按SOP完成初步排查,无法解决时升级给产品线支持组。
+
+## 售后升级
+
+升级问题必须包含客户影响、紧急程度、已尝试方案和期望解决时间。紧急生产故障应同步值班负责人,并在问题关闭后沉淀复盘文档。
+"""
+```
+
+然后生成父块 ID:
+
+```python
+parent_id = f"doc_{i}_parent_{j}"
+```
+
+如果该文件是当前加载文档中的第一个文档,且它是第一个父块,那么:
+
+```python
+parent_id == "doc_0_parent_0"
+```
+
+这里的 `i` 是原始文档索引,`j` 是当前文档中的父块索引。
+
+## 七、子块的结果
+
+父块会继续被切分成子块:
+
+```python
+sub_chunks = child_splitter_to_use.split_documents([parent_doc])
+```
+
+由于这个父块只有约 243 个字符,小于子块大小 300,所以:
+
+```python
+len(sub_chunks) == 1
+```
+
+这个子块的正文也基本等于完整文件内容。代码会为它添加以下元数据:
+
+```python
+sub_chunk.metadata["parent_id"] = parent_id
+sub_chunk.metadata["parent_content"] = parent_doc.page_content
+sub_chunk.metadata["id"] = f"{parent_id}_child_{k}"
+```
+
+最终保存到 `child_chunks` 中的结果大致如下:
+
+```python
+Document(
+    page_content="完整的产品与售后知识库内容",
+    metadata={
+        "source": "enterprise",
+        "file_path": ".../产品与售后知识库.md",
+        "parent_id": "doc_0_parent_0",
+        "parent_content": "完整的产品与售后知识库内容",
+        "id": "doc_0_parent_0_child_0"
+    }
+)
+```
+
+其中:
+
+- `parent_id`:说明该子块属于哪个父块。
+- `parent_content`:保存完整父块内容,便于检索后恢复上下文。
+- `id`:当前子块的唯一 ID。
+
+## 八、长文档的切分关系
+
+如果原始文档超过 1200 个字符,切分结果可能类似这样:
+
+```text
+原始文档
+├── doc_0_parent_0,约 1200 字
+│   ├── doc_0_parent_0_child_0,约 300 字
+│   ├── doc_0_parent_0_child_1,约 300 字
+│   └── doc_0_parent_0_child_2,约 300 字
+├── doc_0_parent_1,约 1200 字
+│   ├── doc_0_parent_1_child_0,约 300 字
+│   └── doc_0_parent_1_child_1,约 300 字
+└── doc_0_parent_2
+    └── doc_0_parent_2_child_0
+```
+
+注意,Markdown 分词器会尽量保留标题和段落结构,因此这里的“约 1200 字”和“约 300 字”不是绝对值。
+
+## 九、函数最终返回什么
+
+函数最后返回的是子块列表:
+
+```python
+return child_chunks
+```
+
+父块没有单独放入返回列表,而是通过子块的元数据保存:
+
+```python
+sub_chunk.metadata["parent_content"] = parent_doc.page_content
+```
+
+因此,向量数据库通常保存每个子块的向量和元数据。检索时先找到相关子块,再通过 `parent_id` 或 `parent_content` 获取更完整的父块上下文。
+
+## 十、使用长文档演示文件
+
+建议使用以下文件观察真正的父块和子块切分效果:
+
+```text
+rag_qa/data/enterprise_data/父子块切分演示知识库.md
+```
+
+这份文件约有 4357 个字符,包含多个一级、二级标题和较长段落。按照当前配置:
+
+```text
+父块大小:1200
+子块大小:300
+重叠大小:50
+```
+
+它会被切分成多个父块,每个父块还会继续切分成多个子块。与只有 243 个字符的“产品与售后知识库.md”相比,这个文件更容易观察以下关系:
+
+```text
+doc_0_parent_0
+├── doc_0_parent_0_child_0
+├── doc_0_parent_0_child_1
+└── doc_0_parent_0_child_2
+
+doc_0_parent_1
+├── doc_0_parent_1_child_0
+└── doc_0_parent_1_child_1
+```
+
+实际父块和子块数量会受到 Markdown 标题、段落长度和分词器规则影响,因此上面的数量只是结构示意。运行程序后,可以根据日志中的文件路径,观察该演示文件对应的父块数量和最终子块数量。
+
+## 十一、如何打印实际结果
+
+可以在调用 `process_documents()` 后查看每个子块:
+
+```python
+child_chunks = process_documents(directory_path)
+
+for chunk in child_chunks:
+    print("子块 ID:", chunk.metadata.get("id"))
+    print("父块 ID:", chunk.metadata.get("parent_id"))
+    print("文件路径:", chunk.metadata.get("file_path"))
+    print("子块内容:")
+    print(chunk.page_content)
+    print("父块内容:")
+    print(chunk.metadata.get("parent_content"))
+    print("-" * 50)
+```

+ 17 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/data/enterprise_data/IT服务台知识库.md

@@ -0,0 +1,17 @@
+# IT服务台知识库
+
+## VPN连接问题
+
+VPN无法连接时,先确认本地网络可用、账号未过期、客户端版本为最新版本。如果仍然失败,应提交IT工单并附上错误截图、设备系统版本、网络环境和失败时间。
+
+## 邮箱与账号
+
+邮箱密码忘记时,可在统一身份认证平台发起密码重置。账号被锁定通常由连续输错密码、异常登录或权限过期导致,需要完成身份验证后恢复。
+
+## 权限申请
+
+员工申请系统权限时,应在权限中心选择系统、角色、数据范围和使用期限。审批通过后权限自动下发,到期后自动回收。
+
+## 企业网盘
+
+企业网盘文件误删后通常可在回收站恢复。涉及部门共享目录的删除恢复,需要目录负责人确认。外部分享必须设置有效期、访问权限和水印。

+ 13 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/data/enterprise_data/产品与售后知识库.md

@@ -0,0 +1,13 @@
+# 产品与售后知识库
+
+## 产品资料管理
+
+产品参数、版本说明、部署手册和常见问题应按产品线和版本号归档。对外回答客户问题时,应优先引用最新正式版本,不引用草稿或过期文档。
+
+## 故障排查流程
+
+处理客户故障时,应先收集产品版本、部署环境、故障现象、复现步骤、日志片段和影响范围。一级支持按SOP完成初步排查,无法解决时升级给产品线支持组。
+
+## 售后升级
+
+升级问题必须包含客户影响、紧急程度、已尝试方案和期望解决时间。紧急生产故障应同步值班负责人,并在问题关闭后沉淀复盘文档。

+ 25 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/data/enterprise_data/企业知识库员工服务手册.md

@@ -0,0 +1,25 @@
+# 企业知识库员工服务手册
+
+## 1. 员工服务范围
+
+企业知识库智能问答系统面向公司内部员工,覆盖人事制度、财务报销、IT服务、法务合同、运营流程、产品资料和信息安全等常见问题。系统优先从公司制度文档、SOP、产品手册和历史高频问答中检索证据,再生成结构化回答。
+
+## 2. 人事制度
+
+员工请假需在OA系统提交申请。年假、调休和事假应提前提交;病假可先提交申请,返岗后补充证明。试用期员工转正需要提交工作总结、目标完成情况和直属负责人评价。
+
+## 3. 财务报销
+
+差旅报销需上传审批单、发票、行程单、酒店水单和费用明细。采购付款需完成采购申请、合同归档、验收入库和发票校验。费用单被驳回后,应根据原因补充附件或修改金额后重新提交。
+
+## 4. IT服务
+
+VPN、邮箱、企业网盘、权限申请和设备故障统一通过IT工单处理。申请系统权限时,需要填写使用场景、权限范围和有效期。涉及核心系统或敏感数据的权限申请需要系统负责人审批。
+
+## 5. 法务合同
+
+合同审批包括业务发起、法务审核、财务审核、负责人审批和盖章归档。涉及赔偿上限、数据安全、知识产权或客户特殊条款时,应提交法务复审。
+
+## 6. 知识库治理
+
+每篇知识文档必须有负责人、适用范围、版本号、发布日期和复核日期。过期文档会被标记为待确认,问答系统会降低其引用优先级。

+ 85 - 0
4.基于Milvus库的问答系统/integrated_qa_system/rag_qa/data/enterprise_data/父子块切分演示知识库.md

@@ -0,0 +1,85 @@
+# 企业知识库 RAG 系统操作与服务手册
+
+## 一、知识库建设目标
+
+企业知识库 RAG 系统用于集中管理公司内部的产品资料、员工服务流程、技术支持规范和常见问题。过去这些资料分散在网盘、邮件、项目群和个人电脑中,员工遇到问题时往往需要反复询问同事,或者打开多个文件进行查找。知识库系统将这些内容统一导入,经过文本切分、向量化和索引后,员工可以直接用自然语言提问。
+
+知识库回答问题时,不是简单地把整本手册发送给大模型,而是先从大量文档中检索与问题最相关的内容。为了让检索更加准确,系统会把较大的文档切分成适合保存上下文的父块,再把父块切分成更短的子块。子块用于相似度搜索,父块用于补充完整语境。这样既能避免每次检索返回过多无关内容,也能减少答案缺少上下文的问题。
+
+知识库中的每份文档都应该有明确的主题、稳定的标题层级和完整的段落。建议使用一级标题表示文档名称,二级标题表示业务模块,三级标题表示具体流程或问题。段落之间保留必要的空行,不要把多个完全无关的主题连续写在同一个段落中。
+
+## 二、文档导入与格式要求
+
+系统支持 Markdown、TXT、PDF、Word、PPT、PPTX、JPG 和 PNG 等文件类型。Markdown 和 TXT 文件适合保存结构清晰的制度、流程和产品说明;PDF 和 Office 文件适合保存已经排版完成的正式手册;图片文件通常用于识别截图、扫描件或现场设备标签。导入前应检查文件是否能够正常打开,以及文字是否存在乱码。
+
+Markdown 文档建议使用 UTF-8 编码保存。文件名应包含能够表达主题的名称,例如“售后服务流程.md”“员工账号管理.md”或“产品部署手册.md”。不要使用“最终版”“最终版2”“最新版本”这类容易失控的名称,建议在文档中写明版本号、负责人、生效日期和过期日期。
+
+导入程序会递归扫描指定目录下的文件。它先读取文件扩展名,再从文件类型映射表中选择对应的加载器。TXT 文件会显式按照 UTF-8 编码读取,Markdown 文件使用 Markdown 加载器,PDF、Word、PPT 和图片文件使用相应的解析或 OCR 加载器。加载成功后,系统会把来源名称和文件路径写入文档元数据,方便后续追踪答案来自哪个知识库文件。
+
+如果文件无法加载,程序会记录警告日志并继续处理其他文件。常见原因包括文件扩展名不在支持列表中、文件权限不足、文件编码错误、PDF 内容是不可识别的扫描图片,或者文件本身已经损坏。处理导入异常时,应先根据日志定位具体文件,不要直接删除整个知识库目录。
+
+## 三、员工账号与权限管理
+
+员工登录知识库后,可以根据权限访问不同的资料范围。普通员工通常可以查询员工服务、办公系统和通用产品资料;技术支持人员可以访问故障排查、部署手册和内部解决方案;产品经理可以维护产品规格、版本变更和发布说明;管理员负责配置数据源、检查导入任务和处理权限申请。
+
+账号权限应遵循最小权限原则。员工只应看到完成工作所必需的资料,涉及客户合同、内部报价、生产密钥和安全事件的文档不能默认开放给所有用户。权限发生变化时,应由部门负责人提交申请,管理员核对申请人身份、所属部门、访问范围和有效期限后再执行变更。
+
+员工离职、转岗或长期休假时,应及时回收或调整账号权限。对于临时项目成员,可以创建带有明确过期时间的临时权限,不建议直接复制长期员工账号的全部权限。每次权限变更都应保留操作记录,包括操作人、操作时间、变更前权限、变更后权限和变更原因。
+
+## 四、知识库检索流程
+
+用户提问后,系统首先对问题进行预处理,包括清理多余空格、识别问题中的关键词和判断可能涉及的业务领域。随后,系统使用向量检索查找语义相近的子块,也可以结合关键词检索、BM25 检索或其他排序方式提高召回效果。多个检索结果会按照相似度和业务规则进行排序。
+
+例如,用户提问“生产环境部署失败后应该收集哪些信息”,系统可能会召回“故障排查流程”中的部署环境、错误日志、复现步骤和影响范围,也可能召回“生产故障升级规范”中的客户影响和紧急程度要求。单个子块可能只包含其中一部分内容,因此系统需要根据子块携带的 `parent_id` 找到对应父块,再使用父块补充完整的流程上下文。
+
+检索结果不能只看相似度最高的一条。相似度最高的内容有时只是某个关键词的局部匹配,未必能够完整回答问题。系统可以保留多个候选子块,再根据来源文件、标题层级、版本号和发布日期进行过滤。对于同一主题的多个版本,应优先使用生效时间最新且状态为正式发布的文档。
+
+如果检索结果为空,系统不应编造答案。更合适的处理方式是提示用户没有找到足够依据,并建议用户补充产品名称、版本号、故障现象或所属部门。对于涉及生产故障、数据安全和权限变更的问题,还应提示用户联系对应负责人。
+
+## 五、产品发布与版本管理
+
+产品文档应按照产品名称、主要版本和发布日期进行归档。每次正式发布前,产品团队需要确认功能说明、配置要求、兼容性说明、升级步骤和回滚方案是否完整。对于已经停止维护的版本,应在文档中明确标记维护状态和最后支持日期,避免员工误把旧版本操作步骤用于新版本环境。
+
+版本变更说明至少需要包含变更原因、影响范围、配置变化、数据库变化、接口变化和验证结果。如果新版本修改了默认配置,应同时说明旧配置是否仍然兼容。对于需要人工执行的升级步骤,应给出执行顺序、预计耗时、风险点和失败后的恢复方式。
+
+产品问题关闭后,应将最终解决方案沉淀到知识库,而不是只留在聊天记录中。解决方案应说明问题表现、影响版本、根本原因、临时处理方法、长期修复方法和验证结果。如果同一问题在不同环境下表现不同,应分别记录操作系统、部署方式、依赖组件和相关日志特征。
+
+## 六、故障排查与升级规范
+
+收到客户或内部员工的故障反馈后,一级支持人员首先要确认产品版本、部署环境、故障发生时间、影响用户数量、错误提示、复现步骤和最近一次变更。信息越完整,后续定位越快。不要只记录“系统不能用”或“接口报错”,应尽量保留原始错误信息、请求参数、响应状态码和相关日志片段。
+
+初步排查可以按照“确认现象、缩小范围、检查依赖、验证修复”的顺序进行。确认现象时,需要判断问题是全部用户都能复现,还是只有某个账号、某个租户或某个网络区域受到影响。缩小范围时,可以比较正常环境和异常环境的版本、配置、资源使用率和最近变更记录。
+
+如果问题涉及数据库连接,应检查连接地址、端口、账号权限、连接池状态和数据库健康情况。如果问题涉及缓存,应检查缓存服务是否可访问、关键键是否存在以及是否出现大面积过期。如果问题涉及向量检索,应检查向量数据库连接、集合名称、索引状态、嵌入模型配置和查询结果数量。
+
+当一级支持无法在规定时间内解决时,应升级给对应产品线支持组。升级单必须包含问题标题、客户影响、紧急程度、产品版本、部署环境、故障现象、复现步骤、已经尝试的方案、相关日志和期望解决时间。紧急生产故障需要同步值班负责人,并持续更新处理进展。
+
+## 七、数据更新与索引刷新
+
+知识库文档更新后,需要重新执行导入和索引流程,才能让新的内容参与检索。更新前应确认文件名称、版本号和生效日期,避免把草稿文件与正式文件同时放在同一数据目录中。对于大批量更新,建议先在测试目录验证文件能够正常加载和切分,再执行正式刷新。
+
+刷新过程通常包括读取文件、切分文档、生成嵌入向量、写入向量数据库和更新相关缓存。切分阶段会先生成父块,再根据每个父块生成子块。每个子块会保留自己的内容、来源文件、父块 ID、父块内容和唯一 ID。向量数据库主要保存子块向量,但子块元数据中的父块信息用于检索后的上下文恢复。
+
+如果某个文件已经删除或替换,刷新流程应同步处理旧数据,不能只追加新向量,否则旧版本仍可能被检索出来。生产环境执行刷新前,应确认目标集合、数据来源和备份情况。刷新完成后,需要检查导入文件数量、父块数量、子块数量以及若干典型问题的检索结果。
+
+在刷新过程中出现异常时,应优先查看失败文件和错误日志。单个文件解析失败不一定意味着所有数据都不可用,但如果向量写入过程被中断,应确认事务或批量写入状态,避免出现只有部分数据更新成功的情况。正式环境的刷新操作应记录开始时间、结束时间、操作人、文件版本和最终统计结果。
+
+## 八、常见问题与处理建议
+
+问题一:为什么搜索结果与用户问题看起来相关,但回答仍然不完整?可能原因是子块太短,只包含了一个步骤或一句定义,没有携带完整上下文。可以检查子块对应的 `parent_id` 和 `parent_content` 是否正确,并确认回答阶段使用了父块内容,而不是只使用子块正文。
+
+问题二:为什么同一个问题召回了旧版本和新版本两份答案?可能是旧文档没有从索引中删除,或者文档元数据中缺少版本号和生效状态。处理时应清理旧版本数据,并在检索排序中优先选择正式且最新的版本。
+
+问题三:为什么中文 TXT 文件导入后出现乱码?通常是文件实际编码与读取时指定的 UTF-8 不一致。应确认文件保存编码,并在统一转换为 UTF-8 后重新导入。不要只修改文件扩展名,因为扩展名不会改变文件实际编码。
+
+问题四:为什么 Markdown 标题没有出现在检索结果中?需要检查 Markdown 文件是否使用规范的标题语法,例如使用 `#`、`##` 和 `###`,并确认加载器没有在解析阶段丢失标题。标题是重要的上下文信息,应该尽量保留在父块和子块内容中。
+
+问题五:为什么修改了知识库文件,问答结果却没有变化?修改本地文件并不会自动修改向量数据库中的旧数据。必须重新执行导入、切分、向量化和索引刷新流程,并确认缓存已经针对当前知识库完成失效处理。
+
+## 九、服务联系与记录要求
+
+日常使用问题可以先查看员工服务手册和系统操作指南。产品使用问题应联系对应产品线支持组,部署和故障问题应提交包含完整上下文的服务单,权限和数据访问问题应联系管理员。涉及生产环境的紧急问题,不要只在普通群聊中留言,应按照值班流程电话或即时通知负责人。
+
+所有服务单在关闭前都应填写最终处理结果。处理结果不能只写“已解决”,还应说明实际原因、执行过的操作、验证方式和后续预防措施。能够复用的解决方案应同步沉淀到知识库,并由文档负责人确认标题、版本、生效范围和适用环境。
+
+知识库的价值不只是让系统能够回答问题,更重要的是让组织中的经验可以被持续复用。高质量的文档、明确的版本管理、完整的故障记录和可追溯的检索来源,都会直接影响 RAG 系统最终回答的准确性和可信度。

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.