diff --git a/core/main_terminal_parts/tools_definition/context_tools.py b/core/main_terminal_parts/tools_definition/context_tools.py index db6327d..19b188c 100644 --- a/core/main_terminal_parts/tools_definition/context_tools.py +++ b/core/main_terminal_parts/tools_definition/context_tools.py @@ -252,7 +252,7 @@ class ToolsDefinitionContextToolsMixin: "type": "function", "function": { "name": "manage_personalization", - "description": "管理用户个性化设置。支持读取所有配置,或更新单个字段。可修改字段:self_identify(AI自称,最多20字)、user_name(AI如何称呼用户,最多20字)、profession(用户职业,最多20字)、tone(交流语气,最多20字)、considerations(注意事项列表,字符串数组,最多10项每项最多50字)、theme(主题配色:classic-经典/light-明亮/dark-暗黑)、communication_style(交流风格:default-标准AI风格/human_like-拟人聊天风格/auto-自动选择交流风格)、conversation_continuity(对话连续性:high-高/medium-中/low-低)。更新时会自动验证格式,验证通过后立即保存并生效,新主题会立即应用到界面。", + "description": "管理用户个性化设置。支持读取所有配置,或更新单个字段。可修改字段:self_identify(AI自称,最多20字)、user_name(AI如何称呼用户,最多20字)、profession(用户职业,最多20字)、tone(交流语气,最多20字)、considerations(回答时必须考虑的信息,字符串,最多2000字)、theme(主题配色:classic-经典/light-明亮/dark-暗黑)、communication_style(交流风格:default-标准AI风格/human_like-拟人聊天风格/auto-自动选择交流风格)、conversation_continuity(对话连续性:high-高/medium-中/low-低)。更新时会自动验证格式,验证通过后立即保存并生效,新主题会立即应用到界面。", "parameters": { "type": "object", "properties": self._inject_intent({ @@ -267,7 +267,7 @@ class ToolsDefinitionContextToolsMixin: "description": "要更新的字段名(仅action=update时需要)" }, "value": { - "description": "新值(仅action=update时需要)。注意事项需提供字符串数组,其他字段提供字符串" + "description": "新值(仅action=update时需要)。注意事项提供字符串,其他字段提供字符串" } }), "required": ["action"] diff --git a/core/main_terminal_parts/tools_execution.py b/core/main_terminal_parts/tools_execution.py index 5826ac7..f80e16e 100644 --- a/core/main_terminal_parts/tools_execution.py +++ b/core/main_terminal_parts/tools_execution.py @@ -72,8 +72,7 @@ from modules.personalization_manager import ( save_personalization_config, build_personalization_prompt, MAX_SHORT_FIELD_LENGTH, - MAX_CONSIDERATION_LENGTH, - MAX_CONSIDERATION_ITEMS, + MAX_CONSIDERATION_TEXT_LENGTH, ALLOWED_THEMES, ALLOWED_COMMUNICATION_STYLES, ALLOWED_CONVERSATION_CONTINUITY, @@ -1830,7 +1829,7 @@ class MainTerminalToolsExecutionMixin: "user_name": f"AI如何称呼用户: {result.get('user_name') or '(未设置)'}", "profession": f"用户职业: {result.get('profession') or '(未设置)'}", "tone": f"交流语气: {result.get('tone') or '(未设置)'}", - "considerations": f"注意事项: {len(result.get('considerations') or [])} 条", + "considerations": f"注意事项: {'已设置' if (result.get('considerations') or '').strip() else '未设置'}", "theme": f"主题配色: {result.get('theme', 'classic')}", "communication_style": f"交流风格: {result.get('communication_style', 'default')}", "conversation_continuity": f"对话连续性: {result.get('conversation_continuity', 'medium')}" @@ -1874,17 +1873,11 @@ class MainTerminalToolsExecutionMixin: validation_errors.append(f"{field} 不能超过 {MAX_SHORT_FIELD_LENGTH} 个字符") elif field == "considerations": - # 注意事项数组验证 - if not isinstance(value, list): - validation_errors.append("considerations 必须是字符串数组") - else: - if len(value) > MAX_CONSIDERATION_ITEMS: - validation_errors.append(f"considerations 不能超过 {MAX_CONSIDERATION_ITEMS} 项") - for i, item in enumerate(value): - if not isinstance(item, str): - validation_errors.append(f"considerations[{i}] 必须是字符串") - elif len(item) > MAX_CONSIDERATION_LENGTH: - validation_errors.append(f"considerations[{i}] 不能超过 {MAX_CONSIDERATION_LENGTH} 个字符") + # 注意事项文本验证 + if not isinstance(value, str): + validation_errors.append("considerations 必须是字符串") + elif len(value) > MAX_CONSIDERATION_TEXT_LENGTH: + validation_errors.append(f"considerations 不能超过 {MAX_CONSIDERATION_TEXT_LENGTH} 个字符") elif field == "theme": # 主题验证 diff --git a/modules/personalization_manager.py b/modules/personalization_manager.py index 520db11..0603180 100644 --- a/modules/personalization_manager.py +++ b/modules/personalization_manager.py @@ -5,7 +5,7 @@ from __future__ import annotations import json from copy import deepcopy from pathlib import Path -from typing import Any, Dict, Iterable, Optional, Union +from typing import Any, Dict, Optional, Union try: from config.limits import THINKING_FAST_INTERVAL @@ -30,7 +30,7 @@ GOAL_MAX_TOKENS_MAX = 100_000_000 PERSONALIZATION_FILENAME = "personalization.json" MAX_SHORT_FIELD_LENGTH = 20 -MAX_CONSIDERATION_LENGTH = 50 +MAX_CONSIDERATION_TEXT_LENGTH = 2000 MAX_CONSIDERATION_ITEMS = 10 TONE_PRESETS = ["健谈", "幽默", "直言不讳", "鼓励性", "诗意", "企业商务", "打破常规", "同理心"] THINKING_INTERVAL_MIN = 1 @@ -705,12 +705,10 @@ def build_personalization_prompt( if config.get("tone"): lines.append(f"用户希望你使用 {config['tone']} 的语气与TA交流") - considerations: Iterable[str] = config.get("considerations") or [] - considerations = [item for item in considerations if item] + considerations: str = config.get("considerations") or "" if considerations: lines.append("用户希望你在回答问题时必须考虑的信息是:") - for idx, item in enumerate(considerations, 1): - lines.append(f"{idx}. {item}") + lines.append(considerations) conversation_continuity = str(config.get("conversation_continuity") or "medium").strip().lower() if conversation_continuity == "high": @@ -761,20 +759,25 @@ def _sanitize_short_field(value: Optional[str]) -> str: return text[:MAX_SHORT_FIELD_LENGTH] -def _sanitize_considerations(value: Any) -> list: - if not isinstance(value, list): - return [] - cleaned = [] - for item in value: - if not isinstance(item, str): - continue - text = item.strip() - if not text: - continue - cleaned.append(text[:MAX_CONSIDERATION_LENGTH]) - if len(cleaned) >= MAX_CONSIDERATION_ITEMS: - break - return cleaned +def _sanitize_considerations(value: Any) -> str: + """Sanitize considerations text. Legacy array data is joined with newlines.""" + if value is None: + return "" + if isinstance(value, str): + return value[:MAX_CONSIDERATION_TEXT_LENGTH] + if isinstance(value, list): + cleaned = [] + for item in value: + if not isinstance(item, str): + continue + text = item.strip() + if not text: + continue + cleaned.append(text) + if len(cleaned) >= MAX_CONSIDERATION_ITEMS: + break + return "\n".join(cleaned)[:MAX_CONSIDERATION_TEXT_LENGTH] + return "" def _sanitize_thinking_interval(value: Any) -> Optional[int]: diff --git a/static/src/components/chat/actions/ToolAction.vue b/static/src/components/chat/actions/ToolAction.vue index d5003ea..e95ff89 100644 --- a/static/src/components/chat/actions/ToolAction.vue +++ b/static/src/components/chat/actions/ToolAction.vue @@ -375,8 +375,14 @@ function renderEditFile(result: any, args: any): string { if (details.length > 0) { html += '
${escapeHtml(match.snippet || match.content || '')}`;
html += '