From 916f68e8e95203a2652bf84cb44d064adb682645 Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Tue, 14 Apr 2026 01:47:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E4=BD=93=E4=BA=A4=E6=B5=81=E9=A3=8E=E6=A0=BC=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将"模型行为"标签改为"智能体行为" - 新增"智能体交流风格"二选一选项(默认/拟人) - 拟人风格会注入human_like_style.txt提示词 - manage_personalization工具支持communication_style字段 - 添加user-pen.svg图标用于个性化工具和分类 --- core/main_terminal_parts/tools_definition.py | 4 +- core/main_terminal_parts/tools_execution.py | 14 +++++-- modules/personalization_manager.py | 32 ++++++++++++++++ prompts/human_like_style.txt | 17 +++++++++ static/icons/user-pen.svg | 1 + .../personalization/PersonalizationDrawer.vue | 38 ++++++++++++++++++- static/src/stores/personalization.ts | 10 +++++ static/src/stores/tutorial.ts | 6 +-- static/src/utils/icons.ts | 3 ++ 9 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 prompts/human_like_style.txt create mode 100644 static/icons/user-pen.svg diff --git a/core/main_terminal_parts/tools_definition.py b/core/main_terminal_parts/tools_definition.py index e36c81c..35c07eb 100644 --- a/core/main_terminal_parts/tools_definition.py +++ b/core/main_terminal_parts/tools_definition.py @@ -792,7 +792,7 @@ class MainTerminalToolsDefinitionMixin: "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-暗黑)。更新时会自动验证格式,验证通过后立即保存并生效,新主题会立即应用到界面。", + "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-拟人聊天风格)。更新时会自动验证格式,验证通过后立即保存并生效,新主题会立即应用到界面。", "parameters": { "type": "object", "properties": self._inject_intent({ @@ -803,7 +803,7 @@ class MainTerminalToolsDefinitionMixin: }, "field": { "type": "string", - "enum": ["self_identify", "user_name", "profession", "tone", "considerations", "theme"], + "enum": ["self_identify", "user_name", "profession", "tone", "considerations", "theme", "communication_style"], "description": "要更新的字段名(仅action=update时需要)" }, "value": { diff --git a/core/main_terminal_parts/tools_execution.py b/core/main_terminal_parts/tools_execution.py index 1c58b82..556180e 100644 --- a/core/main_terminal_parts/tools_execution.py +++ b/core/main_terminal_parts/tools_execution.py @@ -1311,7 +1311,7 @@ class MainTerminalToolsExecutionMixin: try: config = load_personalization_config(self.data_dir) # 只返回可修改的字段 - readable_fields = ["self_identify", "user_name", "profession", "tone", "considerations", "theme", "enabled"] + readable_fields = ["self_identify", "user_name", "profession", "tone", "considerations", "theme", "communication_style", "enabled"] result = {k: v for k, v in config.items() if k in readable_fields} # 构建详细返回信息 field_descriptions = { @@ -1321,7 +1321,8 @@ class MainTerminalToolsExecutionMixin: "profession": f"用户职业: {result.get('profession') or '(未设置)'}", "tone": f"交流语气: {result.get('tone') or '(未设置)'}", "considerations": f"注意事项: {len(result.get('considerations') or [])} 条", - "theme": f"主题配色: {result.get('theme', 'classic')}" + "theme": f"主题配色: {result.get('theme', 'classic')}", + "communication_style": f"交流风格: {'拟人聊天风格' if result.get('communication_style') == 'human_like' else '标准AI风格'}" } details = "\n".join([f"- {field_descriptions.get(k, k)}: {v}" for k, v in result.items()]) logger.info("[_execute_manage_personalization] read成功") @@ -1346,7 +1347,7 @@ class MainTerminalToolsExecutionMixin: # 验证字段是否允许修改 logger.info("[_execute_manage_personalization] 验证字段: field=%s", field) - allowed_fields = ["self_identify", "user_name", "profession", "tone", "considerations", "theme"] + allowed_fields = ["self_identify", "user_name", "profession", "tone", "considerations", "theme", "communication_style"] if field not in allowed_fields: logger.warning("[_execute_manage_personalization] 字段不允许修改: %s not in %s", field, allowed_fields) return {"success": False, "error": f"字段 '{field}' 不允许修改,可修改字段: {allowed_fields}"} @@ -1381,6 +1382,13 @@ class MainTerminalToolsExecutionMixin: elif value not in ALLOWED_THEMES: validation_errors.append(f"theme 必须是以下之一: {list(ALLOWED_THEMES)}") + elif field == "communication_style": + # 交流风格验证 + if not isinstance(value, str): + validation_errors.append("communication_style 必须是字符串") + elif value not in ["default", "human_like"]: + validation_errors.append("communication_style 必须是 'default' 或 'human_like'") + if validation_errors: logger.warning("[_execute_manage_personalization] 验证失败: %s", validation_errors) return { diff --git a/modules/personalization_manager.py b/modules/personalization_manager.py index 4ab4c96..da3bf40 100644 --- a/modules/personalization_manager.py +++ b/modules/personalization_manager.py @@ -45,6 +45,7 @@ DEFAULT_SHALLOW_KEEP_USER_TURN_TOOLS = 3 DEFAULT_PERSONALIZATION_CONFIG: Dict[str, Any] = { "enabled": False, + "communication_style": "default", # default / human_like "self_identify": "", "user_name": "", "use_custom_names": False, @@ -164,6 +165,9 @@ def sanitize_personalization_payload( return _sanitize_short_field(base.get(key)) base["enabled"] = bool(data.get("enabled", base["enabled"])) + # 交流风格: default / human_like + _comm_style = data.get("communication_style", base.get("communication_style", "default")) + base["communication_style"] = "human_like" if _comm_style == "human_like" else "default" base["auto_generate_title"] = bool(data.get("auto_generate_title", base["auto_generate_title"])) base["self_identify"] = _resolve_short_field("self_identify") base["user_name"] = _resolve_short_field("user_name") @@ -497,6 +501,27 @@ def validate_context_compression_settings(config: Optional[Dict[str, Any]]) -> N raise ValueError("深压缩触发上下文必须大于浅压缩触发上下文") +def _load_human_like_prompt() -> str: + """加载拟人化风格提示词文件。""" + from config.paths import PROMPTS_DIR + try: + prompt_path = Path(PROMPTS_DIR) / "human_like_style.txt" + if prompt_path.exists(): + return prompt_path.read_text(encoding="utf-8").strip() + except Exception: + pass + # 默认提示词 + return ( + "不要「像AI一样说话」,而是尽可能的模仿真人的说话方式或是模仿用户输入的说话方式。\n" + "尽可能不要用markdown格式,尤其是:\n" + "- ###、##、#等一二三级标题\n" + "- 等列表形式\n" + "- 不必要的** **加粗\n" + "- 刻意的分点分层次的回答\n" + "用户如果不输入emoji,就不要使用emoji回答。" + ) + + def build_personalization_prompt( config: Optional[Dict[str, Any]], include_header: bool = True @@ -525,6 +550,13 @@ def build_personalization_prompt( for idx, item in enumerate(considerations, 1): lines.append(f"{idx}. {item}") + # 拟人化交流风格 + if config.get("communication_style") == "human_like": + human_like_prompt = _load_human_like_prompt() + if human_like_prompt: + lines.append("\n【交流风格要求】") + lines.append(human_like_prompt) + if len(lines) == (1 if include_header else 0): # 没有任何有效内容时不注入 return None diff --git a/prompts/human_like_style.txt b/prompts/human_like_style.txt new file mode 100644 index 0000000..972db48 --- /dev/null +++ b/prompts/human_like_style.txt @@ -0,0 +1,17 @@ +不要「像AI一样说话」,而是尽可能的模仿真人的说话方式或是模仿用户输入的说话方式 + +禁止使用的markdown格式: +- ###、##、#等一二三级标题 +- - 等列表形式 +- 不必要的** **加粗 + +不要使用这些语言形式: +- 刻意的分点分层次的回答 +- 刻意的 问题:回答 的格式 +- 任何设问,反问的形式 +- 任何最后补充的形式 +- 任何包含“不卖关子”“我直说””我接住你“等的语言 + +用户如果不输入emoji,就不要使用emoji回答 + +说话要自然、随意,像朋友聊天一样。不要过度礼貌或机械。可以用口语化的表达,适当使用语气词,让对话更有温度。 \ No newline at end of file diff --git a/static/icons/user-pen.svg b/static/icons/user-pen.svg new file mode 100644 index 0000000..7018394 --- /dev/null +++ b/static/icons/user-pen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/src/components/personalization/PersonalizationDrawer.vue b/static/src/components/personalization/PersonalizationDrawer.vue index 3784785..917e7ca 100644 --- a/static/src/components/personalization/PersonalizationDrawer.vue +++ b/static/src/components/personalization/PersonalizationDrawer.vue @@ -1187,6 +1187,38 @@

暂无可配置的工具类别。

+
+
+ 智能体交流风格 +

智能体和您交流时使用何种形式

+
+
+ + +
+
@@ -1471,7 +1503,7 @@ const baseTabs = [ { id: 'workspace', label: '工作区设置', description: '默认权限模式' }, { id: 'usage', label: '用量统计', description: '对话累计数据' }, { id: 'app-update', label: '软件更新', description: '检查并下载新版 APK' }, - { id: 'behavior', label: '模型行为', description: '工具提示与界面表现' }, + { id: 'behavior', label: '智能体行为', description: '交流风格与界面表现' }, { id: 'skills', label: 'Skills', description: '可用技能开关' }, { id: 'image', label: '图片压缩', description: '发送图片的尺寸策略' }, { id: 'theme', label: '主题切换', description: '经典 / 明亮 / 夜间' }, @@ -1850,6 +1882,10 @@ const setDefaultPermissionMode = (value: PermissionModeValue) => { personalization.setDefaultPermissionMode(value); }; +const setCommunicationStyle = (value: 'default' | 'human_like') => { + personalization.setCommunicationStyle(value); +}; + const checkModeModelConflict = (mode: RunModeValue, model: string | null): boolean => { const found = (filteredModelOptions.value || []).find((item: any) => item.value === model); const warnings: string[] = []; diff --git a/static/src/stores/personalization.ts b/static/src/stores/personalization.ts index 605877f..96edac6 100644 --- a/static/src/stores/personalization.ts +++ b/static/src/stores/personalization.ts @@ -7,6 +7,7 @@ type PermissionMode = 'readonly' | 'approval' | 'unrestricted'; interface PersonalForm { enabled: boolean; + communication_style: 'default' | 'human_like'; auto_generate_title: boolean; tool_intent_enabled: boolean; skill_hints_enabled: boolean; @@ -77,6 +78,7 @@ const EXPERIMENT_STORAGE_KEY = 'agents_personalization_experiments'; const defaultForm = (): PersonalForm => ({ enabled: false, + communication_style: 'default', auto_generate_title: true, tool_intent_enabled: true, skill_hints_enabled: false, @@ -228,6 +230,7 @@ export const usePersonalizationStore = defineStore('personalization', { : null) || 'kimi-k2.5'; this.form = { enabled: !!data.enabled, + communication_style: data.communication_style === 'human_like' ? 'human_like' : 'default', auto_generate_title: data.auto_generate_title !== false, tool_intent_enabled: !!data.tool_intent_enabled, skill_hints_enabled: !!data.skill_hints_enabled, @@ -672,6 +675,13 @@ export const usePersonalizationStore = defineStore('personalization', { blockDisplayMode: mode }; this.persistExperiments(); + }, + setCommunicationStyle(style: 'default' | 'human_like') { + this.form = { + ...this.form, + communication_style: style + }; + this.clearFeedback(); } } }); diff --git a/static/src/stores/tutorial.ts b/static/src/stores/tutorial.ts index 581213e..35bfad4 100644 --- a/static/src/stores/tutorial.ts +++ b/static/src/stores/tutorial.ts @@ -498,8 +498,8 @@ const DEFAULT_STEPS: TutorialStep[] = [ }, { id: 'tab-behavior', - title: '模型行为', - description: '下一步自动切换到“模型行为”。', + title: '智能体行为', + description: '下一步自动切换到“智能体行为”。', target: '[data-tutorial="personal-tab-behavior"]', mode: 'info', autoClick: true, @@ -507,7 +507,7 @@ const DEFAULT_STEPS: TutorialStep[] = [ }, { id: 'page-behavior', - title: '模型行为内容', + title: '智能体行为内容', description: '可配置工具显示、压缩策略等高级选项。', target: '[data-tutorial="personal-content-shell"]', mode: 'info', diff --git a/static/src/utils/icons.ts b/static/src/utils/icons.ts index c877de9..8e5419e 100644 --- a/static/src/utils/icons.ts +++ b/static/src/utils/icons.ts @@ -35,6 +35,7 @@ export const ICONS = Object.freeze({ trash: '/static/icons/trash.svg', triangleAlert: '/static/icons/triangle-alert.svg', user: '/static/icons/user.svg', + userPen: '/static/icons/user-pen.svg', wrench: '/static/icons/wrench.svg', x: '/static/icons/x.svg', zap: '/static/icons/zap.svg' @@ -43,6 +44,7 @@ export const ICONS = Object.freeze({ export const TOOL_ICON_MAP = Object.freeze({ close_sub_agent: 'bot', create_file: 'file', + manage_personalization: 'userPen', create_folder: 'folder', create_sub_agent: 'bot', delete_file: 'trash', @@ -75,6 +77,7 @@ export const TOOL_ICON_MAP = Object.freeze({ export const TOOL_CATEGORY_ICON_MAP = Object.freeze({ network: 'globe', file_edit: 'pencil', + personalization: 'userPen', read_focus: 'eye', terminal_realtime: 'monitor', terminal_command: 'terminal',