Procházet zdrojové kódy

当无法生成SQL时,拦截错误,返回解释信息。

wangxq před 2 týdny
rodič
revize
776d9af128
5 změnil soubory, kde provedl 703 přidání a 10 odebrání
  1. 1 1
      app_config.py
  2. 142 5
      citu_app.py
  3. 144 4
      customllm/base_llm_chat.py
  4. 281 0
      docs/app_config参数说明.md
  5. 135 0
      list.txt

+ 1 - 1
app_config.py

@@ -37,7 +37,7 @@ API_QIANWEN_CONFIG = {
     "api_key": os.getenv("QWEN_API_KEY"),  # 从环境变量读取API密钥
     "model": "qwen3-235b-a22b",
     "allow_llm_to_see_data": True,
-    "temperature": 0.7,
+    "temperature": 0.6,
     "n_results": 6,
     "language": "Chinese",
     "stream": True,  # 是否使用流式模式

+ 142 - 5
citu_app.py

@@ -6,7 +6,8 @@ import pandas as pd
 import common.result as result
 from datetime import datetime, timedelta
 from common.session_aware_cache import WebSessionAwareMemoryCache
-from app_config import API_MAX_RETURN_ROWS
+from app_config import API_MAX_RETURN_ROWS, DISPLAY_SUMMARY_THINKING
+import re
 
 # 设置默认的最大返回行数
 DEFAULT_MAX_RETURN_ROWS = 200
@@ -31,6 +32,34 @@ app = VannaFlaskApp(
     debug=True
 )
 
+
+def _remove_thinking_content(text: str) -> str:
+    """
+    移除文本中的 <think></think> 标签及其内容
+    复用自 base_llm_chat.py 中的同名方法
+    
+    Args:
+        text (str): 包含可能的 thinking 标签的文本
+        
+    Returns:
+        str: 移除 thinking 内容后的文本
+    """
+    if not text:
+        return text
+    
+    # 移除 <think>...</think> 标签及其内容(支持多行)
+    # 使用 re.DOTALL 标志使 . 匹配包括换行符在内的任何字符
+    cleaned_text = re.sub(r'<think>.*?</think>\s*', '', text, flags=re.DOTALL | re.IGNORECASE)
+    
+    # 移除可能的多余空行
+    cleaned_text = re.sub(r'\n\s*\n\s*\n', '\n\n', cleaned_text)
+    
+    # 去除开头和结尾的空白字符
+    cleaned_text = cleaned_text.strip()
+    
+    return cleaned_text
+
+
 # 修改ask接口,支持前端传递session_id
 @app.flask_app.route('/api/v0/ask', methods=['POST'])
 def ask_full():
@@ -58,6 +87,47 @@ def ask_full():
             allow_llm_to_see_data=True
         )
 
+        # 关键:检查是否有LLM解释性文本(无法生成SQL的情况)
+        if sql is None and hasattr(vn, 'last_llm_explanation') and vn.last_llm_explanation:
+            # 根据 DISPLAY_SUMMARY_THINKING 参数决定是否移除 thinking 内容
+            explanation_message = vn.last_llm_explanation
+            if not DISPLAY_SUMMARY_THINKING:
+                explanation_message = _remove_thinking_content(explanation_message)
+                print(f"[DEBUG] 隐藏thinking内容 - 原始长度: {len(vn.last_llm_explanation)}, 处理后长度: {len(explanation_message)}")
+            
+            # 在解释性文本末尾添加提示语
+            explanation_message = explanation_message + "请尝试提问其它问题。"
+            
+            # 使用 result.failed 返回,success为false,但在message中包含LLM友好的解释
+            return jsonify(result.failed(
+                message=explanation_message,  # 处理后的解释性文本
+                code=400,  # 业务逻辑错误,使用400
+                data={
+                    "sql": None,
+                    "rows": [],
+                    "columns": [],
+                    "summary": None,
+                    "conversation_id": conversation_id if 'conversation_id' in locals() else None,
+                    "session_id": browser_session_id
+                }
+            )), 200  # HTTP状态码仍为200,因为请求本身成功处理了
+
+        # 如果sql为None但没有解释性文本,返回通用错误
+        if sql is None:
+            return jsonify(result.failed(
+                message="无法生成SQL查询,请检查问题描述或数据表结构",
+                code=400,
+                data={
+                    "sql": None,
+                    "rows": [],
+                    "columns": [],
+                    "summary": None,
+                    "conversation_id": conversation_id if 'conversation_id' in locals() else None,
+                    "session_id": browser_session_id
+                }
+            )), 200
+
+        # 正常SQL流程
         rows, columns = [], []
         summary = None
         
@@ -84,10 +154,36 @@ def ask_full():
         
     except Exception as e:
         print(f"[ERROR] ask_full执行失败: {str(e)}")
-        return jsonify(result.failed(
-            message=f"查询处理失败: {str(e)}", 
-            code=500
-        )), 500
+        
+        # 即使发生异常,也检查是否有业务层面的解释
+        if hasattr(vn, 'last_llm_explanation') and vn.last_llm_explanation:
+            # 根据 DISPLAY_SUMMARY_THINKING 参数决定是否移除 thinking 内容
+            explanation_message = vn.last_llm_explanation
+            if not DISPLAY_SUMMARY_THINKING:
+                explanation_message = _remove_thinking_content(explanation_message)
+                print(f"[DEBUG] 异常处理中隐藏thinking内容 - 原始长度: {len(vn.last_llm_explanation)}, 处理后长度: {len(explanation_message)}")
+            
+            # 在解释性文本末尾添加提示语
+            explanation_message = explanation_message + "请尝试提问其它问题。"
+            
+            return jsonify(result.failed(
+                message=explanation_message,
+                code=400,
+                data={
+                    "sql": None,
+                    "rows": [],
+                    "columns": [],
+                    "summary": None,
+                    "conversation_id": conversation_id if 'conversation_id' in locals() else None,
+                    "session_id": browser_session_id
+                }
+            )), 200
+        else:
+            # 技术错误,使用500错误码
+            return jsonify(result.failed(
+                message=f"查询处理失败: {str(e)}", 
+                code=500
+            )), 500
 
 @app.flask_app.route('/api/v0/citu_run_sql', methods=['POST'])
 def citu_run_sql():
@@ -164,6 +260,47 @@ def ask_cached():
                 allow_llm_to_see_data=True
             )
             
+            # 检查是否有LLM解释性文本(无法生成SQL的情况)
+            if sql is None and hasattr(vn, 'last_llm_explanation') and vn.last_llm_explanation:
+                # 根据 DISPLAY_SUMMARY_THINKING 参数决定是否移除 thinking 内容
+                explanation_message = vn.last_llm_explanation
+                if not DISPLAY_SUMMARY_THINKING:
+                    explanation_message = _remove_thinking_content(explanation_message)
+                    print(f"[DEBUG] ask_cached中隐藏thinking内容 - 原始长度: {len(vn.last_llm_explanation)}, 处理后长度: {len(explanation_message)}")
+                
+                # 在解释性文本末尾添加提示语
+                explanation_message = explanation_message + "请尝试用其它方式提问。"
+                
+                return jsonify(result.failed(
+                    message=explanation_message,
+                    code=400,
+                    data={
+                        "sql": None,
+                        "rows": [],
+                        "columns": [],
+                        "summary": None,
+                        "conversation_id": conversation_id,
+                        "session_id": browser_session_id,
+                        "cached": False
+                    }
+                )), 200
+            
+            # 如果sql为None但没有解释性文本,返回通用错误
+            if sql is None:
+                return jsonify(result.failed(
+                    message="无法生成SQL查询,请检查问题描述或数据表结构",
+                    code=400,
+                    data={
+                        "sql": None,
+                        "rows": [],
+                        "columns": [],
+                        "summary": None,
+                        "conversation_id": conversation_id,
+                        "session_id": browser_session_id,
+                        "cached": False
+                    }
+                )), 200
+            
             # 缓存结果
             app.cache.set(id=conversation_id, field="question", value=question)
             app.cache.set(id=conversation_id, field="sql", value=sql)

+ 144 - 4
customllm/base_llm_chat.py

@@ -1,6 +1,8 @@
 import os
 from abc import ABC, abstractmethod
-from typing import List, Dict, Any, Optional
+from typing import List, Dict, Any, Optional, Union, Tuple
+import pandas as pd
+import plotly.graph_objs
 from vanna.base import VannaBase
 # 导入配置参数
 from app_config import REWRITE_QUESTION_ENABLED, DISPLAY_SUMMARY_THINKING
@@ -11,6 +13,9 @@ class BaseLLMChat(VannaBase, ABC):
     
     def __init__(self, config=None):
         VannaBase.__init__(self, config=config)
+
+        # 存储LLM解释性文本
+        self.last_llm_explanation = None
         
         print("传入的 config 参数如下:")
         for key, value in self.config.items():
@@ -266,15 +271,19 @@ class BaseLLMChat(VannaBase, ABC):
 
     def generate_sql(self, question: str, **kwargs) -> str:
         """
-        重写父类的 generate_sql 方法,增加异常处理
+        重写父类的 generate_sql 方法,增加异常处理和解释性文本保存
         """
         try:
+            # 清空上次的解释性文本
+            self.last_llm_explanation = None
+            
             print(f"[DEBUG] 尝试为问题生成SQL: {question}")
             # 调用父类的 generate_sql
             sql = super().generate_sql(question, **kwargs)
             
             if not sql or sql.strip() == "":
                 print(f"[WARNING] 生成的SQL为空")
+                self.last_llm_explanation = "无法生成SQL查询,可能是问题描述不够清晰或缺少必要的数据表信息。"
                 return None
             
             # 替换 "\_" 为 "_",解决特殊字符转义问题
@@ -293,15 +302,21 @@ class BaseLLMChat(VannaBase, ABC):
             for indicator in error_indicators:
                 if indicator in sql_lower:
                     print(f"[WARNING] LLM返回错误信息而非SQL: {sql}")
+                    # 保存LLM的解释性文本
+                    self.last_llm_explanation = sql
                     return None
             
             # 简单检查是否像SQL语句(至少包含一些SQL关键词)
             sql_keywords = ["select", "insert", "update", "delete", "with", "from", "where"]
             if not any(keyword in sql_lower for keyword in sql_keywords):
                 print(f"[WARNING] 返回内容不像有效SQL: {sql}")
+                # 保存LLM的解释性文本
+                self.last_llm_explanation = sql
                 return None
                 
             print(f"[SUCCESS] 成功生成SQL:\n {sql}")
+            # 清空解释性文本
+            self.last_llm_explanation = None
             return sql
             
         except Exception as e:
@@ -310,7 +325,7 @@ class BaseLLMChat(VannaBase, ABC):
             # 导入traceback以获取详细错误信息
             import traceback
             print(f"[ERROR] 详细错误信息: {traceback.format_exc()}")
-            # 返回 None 而不是抛出异常
+            self.last_llm_explanation = f"SQL生成过程中出现异常: {str(e)}"
             return None
 
     def generate_question(self, sql: str, **kwargs) -> str:
@@ -473,6 +488,130 @@ class BaseLLMChat(VannaBase, ABC):
         cleaned_text = cleaned_text.strip()
         
         return cleaned_text
+    
+
+    def ask(
+        self,
+        question: Union[str, None] = None,
+        print_results: bool = True,
+        auto_train: bool = True,
+        visualize: bool = True,
+        allow_llm_to_see_data: bool = False,
+    ) -> Union[
+        Tuple[
+            Union[str, None],
+            Union[pd.DataFrame, None],
+            Union[plotly.graph_objs.Figure, None],
+        ],
+        None,
+    ]:
+        """
+        重载父类的ask方法,处理LLM解释性文本
+        当generate_sql无法生成SQL时,保存解释性文本供API层使用
+        """
+        if question is None:
+            question = input("Enter a question: ")
+
+        # 清空上次的解释性文本
+        self.last_llm_explanation = None
+
+        try:
+            sql = self.generate_sql(question=question, allow_llm_to_see_data=allow_llm_to_see_data)
+        except Exception as e:
+            print(e)
+            self.last_llm_explanation = str(e)
+            if print_results:
+                return None
+            else:
+                return None, None, None
+
+        # 如果SQL为空,说明有解释性文本,按照正常流程返回None
+        # API层会检查 last_llm_explanation 来获取解释
+        if sql is None:
+            print(f"[INFO] 无法生成SQL,解释: {self.last_llm_explanation}")
+            if print_results:
+                return None
+            else:
+                return None, None, None
+
+        # 以下是正常的SQL执行流程(保持VannaBase原有逻辑)
+        if print_results:
+            print(sql)
+
+        if self.run_sql_is_set is False:
+            print("If you want to run the SQL query, connect to a database first.")
+            if print_results:
+                return None
+            else:
+                return sql, None, None
+
+        try:
+            df = self.run_sql(sql)
+            
+            if df is None:
+                print("The SQL query returned no results.")
+                if print_results:
+                    return None
+                else:
+                    return sql, None, None
+
+            if print_results:
+                # 显示结果表格
+                if len(df) > 10:
+                    print(df.head(10).to_string())
+                    print(f"... ({len(df)} rows)")
+                else:
+                    print(df.to_string())
+
+            # 如果启用了自动训练,添加问题-SQL对到训练集
+            if auto_train:
+                try:
+                    self.add_question_sql(question=question, sql=sql)
+                except Exception as e:
+                    print(f"Could not add question and sql to training data: {e}")
+
+            if visualize:
+                try:
+                    # 检查是否应该生成图表
+                    if self.should_generate_chart(df):
+                        plotly_code = self.generate_plotly_code(
+                            question=question, 
+                            sql=sql, 
+                            df=df,
+                            chart_instructions=""
+                        )
+                        if plotly_code is not None and plotly_code.strip() != "":
+                            fig = self.get_plotly_figure(
+                                plotly_code=plotly_code, 
+                                df=df, 
+                                dark_mode=False
+                            )
+                            if fig is not None:
+                                if print_results:
+                                    print("Chart generated (use fig.show() to display)")
+                                return sql, df, fig
+                            else:
+                                print("Could not generate chart")
+                                return sql, df, None
+                        else:
+                            print("No chart generated")
+                            return sql, df, None
+                    else:
+                        print("Not generating chart for this data")
+                        return sql, df, None
+                except Exception as e:
+                    print(f"Couldn't generate chart: {e}")
+                    return sql, df, None
+            else:
+                return sql, df, None
+
+        except Exception as e:
+            print("Couldn't run sql: ", e)
+            if print_results:
+                return None
+            else:
+                return sql, None, None
+
 
     @abstractmethod
     def submit_prompt(self, prompt, **kwargs) -> str:
@@ -486,4 +625,5 @@ class BaseLLMChat(VannaBase, ABC):
         Returns:
             str: LLM的响应
         """
-        pass 
+        pass 
+

+ 281 - 0
docs/app_config参数说明.md

@@ -0,0 +1,281 @@
+# app_config.py 参数配置说明
+
+## 一、核心架构配置
+
+### 1. 模型提供商选择
+
+#### LLM模型提供商
+- **`LLM_MODEL_TYPE`**: 选择大语言模型的提供商类型
+  - 可选值:`"api"` 或 `"ollama"`
+  - 默认值:`"api"`
+  - 样例值:`"api"`
+  - 依赖关系:
+    - 当选择 `"api"` 时,必须设置 `API_LLM_MODEL`
+    - 当选择 `"ollama"` 时,将使用 `OLLAMA_LLM_CONFIG` 配置
+
+#### Embedding模型提供商
+- **`EMBEDDING_MODEL_TYPE`**: 选择嵌入模型的提供商类型
+  - 可选值:`"api"` 或 `"ollama"`
+  - 默认值:`"ollama"`
+  - 样例值:`"ollama"`
+  - 依赖关系:
+    - 当选择 `"api"` 时,使用 `API_EMBEDDING_CONFIG`
+    - 当选择 `"ollama"` 时,使用 `OLLAMA_EMBEDDING_CONFIG`
+
+#### API模型选择
+- **`API_LLM_MODEL`**: 当 `LLM_MODEL_TYPE="api"` 时使用的模型
+  - 可选值:`"qianwen"` 或 `"deepseek"`
+  - 默认值:`"deepseek"`
+  - 样例值:`"deepseek"`
+  - 依赖关系:
+    - 当选择 `"qianwen"` 时,使用 `API_QIANWEN_CONFIG`
+    - 当选择 `"deepseek"` 时,使用 `API_DEEPSEEK_CONFIG`
+
+#### 向量数据库选择
+- **`VECTOR_DB_TYPE`**: 选择向量数据库类型
+  - 可选值:`"chromadb"` 或 `"pgvector"`
+  - 默认值:`"pgvector"`
+  - 样例值:`"pgvector"`
+  - 依赖关系:
+    - 当选择 `"chromadb"` 时,使用本地文件存储
+    - 当选择 `"pgvector"` 时,使用 `PGVECTOR_CONFIG` 配置
+
+## 二、模型配置
+
+### 1. API模型配置
+
+#### DeepSeek模型配置 (`API_DEEPSEEK_CONFIG`)
+- **`api_key`**: DeepSeek API密钥(从环境变量 `DEEPSEEK_API_KEY` 读取)
+  - 样例值:`"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"`
+- **`model`**: 模型版本
+  - 可选值:`"deepseek-reasoner"` 或 `"deepseek-chat"`
+  - 默认值:`"deepseek-reasoner"`
+  - 样例值:`"deepseek-reasoner"`
+- **`allow_llm_to_see_data`**: 是否允许模型查看数据
+  - 可选值:`True` 或 `False`
+  - 默认值:`True`
+  - 样例值:`True`
+- **`temperature`**: 温度参数,控制创造性
+  - 取值范围:`0.0` 到 `1.0`
+  - 默认值:`0.6`
+  - 样例值:`0.6`
+- **`n_results`**: 返回结果数量
+  - 默认值:`6`
+  - 样例值:`6`
+- **`language`**: 语言设置
+  - 默认值:`"Chinese"`
+  - 样例值:`"Chinese"`
+- **`stream`**: 是否使用流式输出
+  - 可选值:`True` 或 `False`
+  - 默认值:`True`
+  - 样例值:`True`
+- **`enable_thinking`**: 是否启用思考功能(需要 `stream=True`)
+  - 可选值:`True` 或 `False`
+  - 默认值:`True`
+  - 样例值:`True`
+
+#### 千问模型配置 (`API_QIANWEN_CONFIG`)
+- **`api_key`**: 千问API密钥(从环境变量 `QWEN_API_KEY` 读取)
+  - 样例值:`"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"`
+- **`model`**: 模型版本
+  - 可选值:`"qwen3-235b-a22b"`, `"qwen3-30b-a3b"`, `"qwen-plus-latest"`, `"qwen-plus"`
+  - 默认值:`"qwen3-235b-a22b"`
+  - 样例值:`"qwen3-235b-a22b"`
+- **`allow_llm_to_see_data`**: 是否允许模型查看数据
+  - 可选值:`True` 或 `False`
+  - 默认值:`True`
+  - 样例值:`True`
+- **`temperature`**: 温度参数
+  - 取值范围:`0.0` 到 `1.0`
+  - 默认值:`0.7`
+  - 样例值:`0.7`
+- **`n_results`**: 返回结果数量
+  - 默认值:`6`
+  - 样例值:`6`
+- **`language`**: 语言设置
+  - 默认值:`"Chinese"`
+  - 样例值:`"Chinese"`
+- **`stream`**: 是否使用流式输出
+  - 可选值:`True` 或 `False`
+  - 默认值:`True`
+  - 样例值:`True`
+- **`enable_thinking`**: 是否启用思考功能(需要 `stream=True`)
+  - 可选值:`True` 或 `False`
+  - 默认值:`True`
+  - 样例值:`True`
+
+### 2. Ollama模型配置
+
+#### Ollama LLM配置 (`OLLAMA_LLM_CONFIG`)
+- **`base_url`**: Ollama服务地址
+  - 默认值:`"http://192.168.3.204:11434"`
+  - 样例值:`"http://localhost:11434"`
+- **`model`**: Ollama模型名称
+  - 示例:`"qwen3:32b"`, `"deepseek-r1:32b"`
+  - 样例值:`"qwen3:32b"`
+- **`allow_llm_to_see_data`**: 是否允许模型查看数据
+  - 可选值:`True` 或 `False`
+  - 默认值:`True`
+  - 样例值:`True`
+- **`temperature`**: 温度参数
+  - 取值范围:`0.0` 到 `1.0`
+  - 默认值:`0.7`
+  - 样例值:`0.7`
+- **`n_results`**: 返回结果数量
+  - 默认值:`6`
+  - 样例值:`6`
+- **`language`**: 语言设置
+  - 默认值:`"Chinese"`
+  - 样例值:`"Chinese"`
+- **`timeout`**: 超时时间(秒)
+  - 默认值:`60`
+  - 样例值:`60`
+- **`stream`**: 是否使用流式输出
+  - 可选值:`True` 或 `False`
+  - 默认值:`True`
+  - 样例值:`True`
+- **`enable_thinking`**: 是否启用思考功能
+  - 可选值:`True` 或 `False`
+  - 默认值:`True`
+  - 样例值:`True`
+- **`num_ctx`**: 上下文长度(可选)
+  - 默认值:`4096`
+  - 样例值:`4096`
+- **`num_predict`**: 预测token数量(可选)
+  - 默认值:`-1`(无限制)
+  - 样例值:`-1`
+- **`repeat_penalty`**: 重复惩罚(可选)
+  - 默认值:`1.1`
+  - 样例值:`1.1`
+- **`auto_check_connection`**: 是否自动检查连接(可选)
+  - 可选值:`True` 或 `False`
+  - 默认值:`True`
+  - 样例值:`True`
+
+#### Ollama Embedding配置 (`OLLAMA_EMBEDDING_CONFIG`)
+- **`base_url`**: Ollama服务地址
+  - 默认值:`"http://192.168.3.204:11434"`
+  - 样例值:`"http://localhost:11434"`
+- **`model_name`**: Embedding模型名称
+  - 默认值:`"bge-m3:567m"`
+  - 样例值:`"bge-m3:567m"`
+- **`embedding_dimension`**: 向量维度
+  - 默认值:`1024`
+  - 样例值:`1024`
+
+### 3. API Embedding配置 (`API_EMBEDDING_CONFIG`)
+- **`model_name`**: 模型名称
+  - 可选值:`"BAAI/bge-m3"`, `"text-embedding-v4"`
+  - 样例值:`"BAAI/bge-m3"`
+- **`api_key`**: API密钥(从环境变量 `EMBEDDING_API_KEY` 读取)
+  - 样例值:`"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"`
+- **`base_url`**: API基础URL(从环境变量 `EMBEDDING_BASE_URL` 读取)
+  - 样例值:`"https://api.example.com/v1"`
+- **`embedding_dimension`**: 向量维度
+  - 默认值:`1024`
+  - 样例值:`1024`
+
+## 三、数据库配置
+
+### 1. 业务数据库配置 (`APP_DB_CONFIG`)
+- **`host`**: 数据库主机地址
+  - 样例值:`"192.168.67.1"`
+- **`port`**: 数据库端口
+  - 样例值:`5432`
+- **`dbname`**: 数据库名称
+  - 样例值:`"bank_db"`
+- **`user`**: 用户名(从环境变量 `APP_DB_USER` 读取)
+  - 样例值:`"postgres"`
+- **`password`**: 密码(从环境变量 `APP_DB_PASSWORD` 读取)
+  - 样例值:`"your_password"`
+
+### 2. 向量数据库配置
+
+#### PgVector配置 (`PGVECTOR_CONFIG`)
+- **`host`**: 数据库主机地址
+  - 样例值:`"192.168.67.1"`
+- **`port`**: 数据库端口
+  - 样例值:`5432`
+- **`dbname`**: 数据库名称
+  - 样例值:`"pgvector_db"`
+- **`user`**: 用户名(从环境变量 `PGVECTOR_DB_USER` 读取)
+  - 样例值:`"postgres"`
+- **`password`**: 密码(从环境变量 `PGVECTOR_DB_PASSWORD` 读取)
+  - 样例值:`"your_password"`
+
+## 四、训练配置
+
+### 1. 批处理配置
+- **`TRAINING_BATCH_PROCESSING_ENABLED`**: 是否启用训练数据批处理
+  - 可选值:`True` 或 `False`
+  - 默认值:`True`
+  - 样例值:`True`
+- **`TRAINING_BATCH_SIZE`**: 每批处理的训练项目数量
+  - 默认值:`10`
+  - 样例值:`10`
+- **`TRAINING_MAX_WORKERS`**: 训练批处理的最大工作线程数
+  - 默认值:`4`
+  - 样例值:`4`
+
+### 2. 训练数据路径
+- **`TRAINING_DATA_PATH`**: 训练数据存放路径
+  - 默认值:`"./training/data"`
+  - 样例值:`"./training/data"`
+  - 支持格式:
+    - 相对路径(以 `.` 开头):`"./training/data"`, `"../data"`
+    - 绝对路径:`"/home/user/data"`, `"C:/data"`, `"D:\\training\\data"`
+    - 相对路径(不以 `.` 开头):`"training/data"`, `"my_data"`
+
+## 五、功能开关
+
+### 1. 问题重写
+- **`REWRITE_QUESTION_ENABLED`**: 是否启用问题重写功能
+  - 可选值:`True` 或 `False`
+  - 默认值:`False`
+  - 样例值:`False`
+  - 功能:将上下文问题合并优化
+
+### 2. 思考过程显示
+- **`DISPLAY_SUMMARY_THINKING`**: 是否在摘要中显示思考过程
+  - 可选值:`True` 或 `False`
+  - 默认值:`False`
+  - 样例值:`False`
+  - 功能:控制是否显示 `<think></think>` 标签内容
+
+### 3. SQL错误修正
+- **`ENABLE_ERROR_SQL_PROMPT`**: 是否启用SQL错误修正提示
+  - 可选值:`True` 或 `False`
+  - 默认值:`True`
+  - 样例值:`True`
+
+## 六、向量查询配置
+
+### 1. 得分阈值过滤
+- **`ENABLE_RESULT_VECTOR_SCORE_THRESHOLD`**: 是否启用向量查询结果得分阈值过滤
+  - 可选值:`True` 或 `False`
+  - 默认值:`True`
+  - 样例值:`True`
+  - 功能:根据相似度得分过滤查询结果
+
+### 2. 阈值设置
+- **`RESULT_VECTOR_SQL_SCORE_THRESHOLD`**: SQL相关查询阈值
+  - 取值范围:`0.0` 到 `1.0`
+  - 默认值:`0.65`
+  - 样例值:`0.65`
+- **`RESULT_VECTOR_DDL_SCORE_THRESHOLD`**: 数据定义语言阈值
+  - 取值范围:`0.0` 到 `1.0`
+  - 默认值:`0.5`
+  - 样例值:`0.5`
+- **`RESULT_VECTOR_DOC_SCORE_THRESHOLD`**: 文档查询阈值
+  - 取值范围:`0.0` 到 `1.0`
+  - 默认值:`0.5`
+  - 样例值:`0.5`
+- **`RESULT_VECTOR_ERROR_SQL_SCORE_THRESHOLD`**: 错误SQL修正阈值
+  - 取值范围:`0.0` 到 `1.0`
+  - 默认值:`0.8`
+  - 样例值:`0.8`
+
+### 3. 返回结果限制
+- **`API_MAX_RETURN_ROWS`**: 接口返回查询记录的最大行数
+  - 默认值:`1000`
+  - 样例值:`1000`

+ 135 - 0
list.txt

@@ -0,0 +1,135 @@
+aiofiles==24.1.0
+aiohappyeyeballs==2.6.1
+aiohttp==3.11.18
+aiosignal==1.3.2
+annotated-types==0.7.0
+anthropic==0.52.0
+anyio==4.9.0
+asgiref==3.8.1
+asyncer==0.0.7
+asyncpg==0.30.0
+colorama==0.4.6
+coloredlogs==15.0.1
+dataclasses-json==0.6.7
+db-dtypes==1.4.3
+Deprecated==1.2.18
+distro==1.9.0
+durationpy==0.10
+fastapi==0.115.12
+filelock==3.18.0
+filetype==1.2.0
+flasgger==0.9.7.1
+Flask==3.1.1
+flask-sock==0.7.0
+flatbuffers==25.2.10
+importlib_resources==6.5.2
+inflection==0.5.1
+itsdangerous==2.2.0
+Jinja2==3.1.6
+jiter==0.10.0
+jsonpatch==1.33
+jsonpointer==3.0.0
+jsonschema==4.23.0
+jsonschema-specifications==2025.4.1
+kaleido==0.2.1
+kubernetes==32.0.1
+langchain-core==0.3.64
+langchain-postgres==0.0.14
+langsmith==0.3.45
+Lazify==0.4.0
+literalai==0.1.201
+markdown-it-py==3.0.0
+MarkupSafe==3.0.2
+marshmallow==3.26.1
+mcp==1.9.1
+mdurl==0.1.2
+opentelemetry-exporter-otlp==1.31.1
+opentelemetry-exporter-otlp-proto-common==1.31.1
+opentelemetry-exporter-otlp-proto-grpc==1.31.1
+opentelemetry-exporter-otlp-proto-http==1.31.1
+opentelemetry-instrumentation==0.52b1
+opentelemetry-instrumentation-alephalpha==0.40.7
+opentelemetry-instrumentation-anthropic==0.40.7
+opentelemetry-instrumentation-asgi==0.52b1
+opentelemetry-instrumentation-bedrock==0.40.7
+opentelemetry-instrumentation-chromadb==0.40.7
+opentelemetry-instrumentation-cohere==0.40.7
+opentelemetry-instrumentation-crewai==0.40.7
+opentelemetry-instrumentation-fastapi==0.52b1
+opentelemetry-instrumentation-google-generativeai==0.40.7
+opentelemetry-instrumentation-groq==0.40.7
+opentelemetry-instrumentation-pinecone==0.40.7
+opentelemetry-instrumentation-qdrant==0.40.7
+opentelemetry-instrumentation-replicate==0.40.7
+opentelemetry-instrumentation-requests==0.52b1
+opentelemetry-instrumentation-sagemaker==0.40.7
+opentelemetry-instrumentation-sqlalchemy==0.52b1
+opentelemetry-instrumentation-threading==0.52b1
+opentelemetry-instrumentation-together==0.40.7
+opentelemetry-instrumentation-transformers==0.40.7
+opentelemetry-instrumentation-urllib3==0.52b1
+opentelemetry-instrumentation-vertexai==0.40.7
+opentelemetry-instrumentation-watsonx==0.40.7
+opentelemetry-instrumentation-weaviate==0.40.7
+opentelemetry-proto==1.31.1
+opentelemetry-sdk==1.31.1
+opentelemetry-semantic-conventions==0.52b1
+opentelemetry-semantic-conventions-ai==0.4.9
+opentelemetry-util-http==0.52b1
+orjson==3.10.18
+overrides==7.7.0
+packaging==24.2
+pandas==2.2.3
+pgvector==0.3.6
+pip==24.2
+plotly==5.22.0
+posthog==3.25.0
+propcache==0.3.1
+protobuf==5.29.4
+psycopg==3.2.9
+psycopg-pool==3.2.6
+psycopg2-binary==2.9.10
+pyarrow==20.0.0
+pyasn1==0.6.1
+pyasn1_modules==0.4.2
+pydantic==2.11.5
+pydantic_core==2.33.2
+pydantic-settings==2.9.1
+Pygments==2.19.1
+PyJWT==2.10.1
+referencing==0.36.2
+regex==2024.11.6
+requests==2.32.3
+requests-oauthlib==2.0.0
+requests-toolbelt==1.0.0
+rich==14.0.0
+rpds-py==0.25.1
+rsa==4.9.1
+shellingham==1.5.4
+simple-websocket==1.1.0
+six==1.17.0
+tabulate==0.9.0
+tenacity==9.1.2
+tiktoken==0.9.0
+tokenizers==0.21.1
+tomli==2.2.1
+tqdm==4.67.1
+traceloop-sdk==0.40.7
+typer==0.15.4
+typing_extensions==4.13.2
+typing-inspect==0.9.0
+typing-inspection==0.4.1
+tzdata==2025.2
+uptrace==1.31.0
+urllib3==2.4.0
+uvicorn==0.34.2
+vanna==0.7.9
+watchfiles==0.20.0
+websocket-client==1.8.0
+websockets==15.0.1
+Werkzeug==3.1.3
+wrapt==1.17.2
+wsproto==1.2.0
+yarl==1.20.0
+zipp==3.21.0
+zstandard==0.23.0