# Thinking 内容控制功能实现 ## 概述 本文档记录了在 API 返回结果中控制 `` 内容显示的功能实现。该功能允许通过配置参数 `DISPLAY_SUMMARY_THINKING` 来控制是否在 API 响应的 message 字段中显示 LLM 的思考过程。 ## 修改日期 2024年12月(具体日期根据实际情况) ## 问题背景 在使用 `/api/v0/ask` 接口时,当 LLM 无法生成有效 SQL 查询时,会返回包含 `` 标签的解释性文本。用户希望能够通过配置参数控制是否显示这些思考内容,与 `generate_summary` 方法中的行为保持一致。 ### 问题示例 修改前的 API 返回: ```json { "code": 400, "data": { "columns": [], "conversation_id": null, "rows": [], "session_id": null, "sql": null, "summary": null }, "message": "好的,用户现在问的是\"请找出50个vip用户的名称?\"。首先,我需要明确几个问题。表中是否有直接标识VIP用户的信息?根据提供的表结构,bank_churners表里并没有一个字段直接标明客户是VIP用户。现有的字段包括客户的基本信息、交易行为和流失概率等,但没有VIP相关的字段。\n\n接下来,用户可能认为某些信用卡类别或者收入等级较高的客户可以视为VIP。例如,信用卡类型中的Gold或Platinum卡通常被视为高价值客户。但根据提供的card_category字段,示例数据中只有Blue、Gold、Silver三种类型。可能Gold卡的客户被认为是VIP,但需要确认是否Gold卡就是VIP的标准。\n\n此外,用户可能想通过其他指标来定义VIP,比如高收入、高信用额度、高交易额等。比如,income_category中$120K+的客户可能属于高收入群体,或者credit_limit较高的客户。但问题在于,这些都需要业务定义,而当前的上下文并没有给出如何确定VIP的标准。\n\n另外,用户之前的查询历史显示他们关注过交易次数和平均利用率,但这次转向VIP用户,可能需要不同的标准。由于没有明确的VIP字段,可能需要创建一个基于现有字段的筛选条件,比如结合card_category(如Gold卡)、income_category(高收入)、credit_limit(高额度)等。\n\n但根据现有的表结构,无法直接获取VIP用户,因为没有相关字段。所以正确的做法应该是告知用户当前数据不足以回答这个问题,或者需要进一步明确VIP的定义标准。如果用户坚持要找50个VIP客户,可能需要假设某个字段作为VIP的标志,但这样存在风险,因为可能不符合实际业务定义。\n\n因此,根据提供的上下文,无法生成有效的SQL查询,因为没有足够的信息来确定如何识别VIP用户。需要用户提供更多关于VIP的定义,或者检查是否有遗漏的字段。\n\n根据提供的表结构,`bank_churners` 表中没有直接标识 VIP 用户的字段(如 VIP 标签、客户等级等)。要查询 VIP 用户需要额外的定义标准(例如:高收入人群、高信用额度、特定信用卡类别等),但当前上下文未提供相关字段或规则,因此无法直接生成 SQL 查询。\n\n**原因**:表中缺乏明确标识 VIP 用户的字段或筛选条件。", "success": false } ``` ## 解决方案 ### 1. 配置参数 在 `app_config.py` 中已存在配置参数: ```python # 是否在摘要中显示thinking过程 # True: 显示 内容 # False: 隐藏 内容,只显示最终答案 DISPLAY_SUMMARY_THINKING = False ``` ### 2. 核心函数实现 复用了 `base_llm_chat.py` 中的 `_remove_thinking_content` 函数: ```python def _remove_thinking_content(text: str) -> str: """ 移除文本中的 标签及其内容 复用自 base_llm_chat.py 中的同名方法 Args: text (str): 包含可能的 thinking 标签的文本 Returns: str: 移除 thinking 内容后的文本 """ if not text: return text # 移除 ... 标签及其内容(支持多行) # 使用 re.DOTALL 标志使 . 匹配包括换行符在内的任何字符 cleaned_text = re.sub(r'.*?\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 ``` ## 修改内容详情 ### 1. 文件修改:`citu_app.py` #### 1.1 导入必要模块 ```python from app_config import API_MAX_RETURN_ROWS, DISPLAY_SUMMARY_THINKING import re ``` #### 1.2 添加 thinking 内容处理函数 在文件中添加了 `_remove_thinking_content` 函数(复用自 `base_llm_chat.py`)。 #### 1.3 修改 `ask_full` 方法 在两个处理 `last_llm_explanation` 的位置添加了 thinking 内容控制逻辑: **位置1:正常流程处理** ```python # 关键:检查是否有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,因为请求本身成功处理了 ``` **位置2:异常处理** ```python # 即使发生异常,也检查是否有业务层面的解释 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 ``` #### 1.4 修改 `ask_cached` 方法 添加了对 `last_llm_explanation` 的处理逻辑(之前缺失): ```python # 检查是否有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 ``` ## 功能特性 ### 1. Thinking 内容控制 - **当 `DISPLAY_SUMMARY_THINKING = True`**:返回完整的 LLM 解释,包括 `` 内容 - **当 `DISPLAY_SUMMARY_THINKING = False`**:自动移除 `` 标签及其内容,只返回最终的解释文本 ### 2. 用户友好提示 在无法生成 SQL 的场景下,会在解释性文本末尾自动添加 "请尝试用其它方式提问。" 提示语。 ### 3. 适用场景 该功能仅在以下特定场景下生效: - 当 LLM 无法生成有效的 SQL 查询时 - 当 LLM 返回解释性文本而不是 SQL 时 - 例如:询问不存在的字段、表结构不支持的查询等 ### 4. 不适用场景 以下场景不会应用此功能: - 正常生成 SQL 并返回数据时 - 技术错误(如数据库连接失败)时 - 其他通用错误消息时 ## 测试验证 ### 测试用例 创建了测试脚本验证 `_remove_thinking_content` 函数的正确性: ```python # 测试结果 原始文本长度: 997 字符 处理后文本长度: 171 字符 # 成功移除了所有 内容 ``` ### 预期效果 修改后的 API 返回示例: ```json { "code": 400, "data": { "columns": [], "conversation_id": null, "rows": [], "session_id": null, "sql": null, "summary": null }, "message": "bank_churners表中未包含客户住址字段,无法提供该信息。请尝试用其它方式提问。", "success": false } ``` ## 配置说明 在 `app_config.py` 中的相关配置: ```python # 是否在摘要中显示thinking过程 # True: 显示 内容 # False: 隐藏 内容,只显示最终答案 DISPLAY_SUMMARY_THINKING = False ``` ## 影响的 API 接口 1. `/api/v0/ask` - 主要的问答接口 2. `/api/v0/ask_cached` - 带缓存的问答接口 ## 兼容性 - 该修改向后兼容,不会影响现有功能 - 与 `generate_summary` 方法中的 thinking 控制逻辑保持一致 - 默认配置 `DISPLAY_SUMMARY_THINKING = False` 确保用户体验的一致性 ## 总结 本次修改成功实现了在 API 返回结果中控制 thinking 内容显示的功能,提升了用户体验的一致性。通过复用现有的代码逻辑,确保了功能的稳定性和可维护性。同时,添加的用户友好提示语进一步改善了用户交互体验。