feat: 添加智能体交流风格选项
- 将"模型行为"标签改为"智能体行为" - 新增"智能体交流风格"二选一选项(默认/拟人) - 拟人风格会注入human_like_style.txt提示词 - manage_personalization工具支持communication_style字段 - 添加user-pen.svg图标用于个性化工具和分类
This commit is contained in:
parent
13400f62d5
commit
916f68e8e9
@ -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": {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
17
prompts/human_like_style.txt
Normal file
17
prompts/human_like_style.txt
Normal file
@ -0,0 +1,17 @@
|
||||
不要「像AI一样说话」,而是尽可能的模仿真人的说话方式或是模仿用户输入的说话方式
|
||||
|
||||
禁止使用的markdown格式:
|
||||
- ###、##、#等一二三级标题
|
||||
- - 等列表形式
|
||||
- 不必要的** **加粗
|
||||
|
||||
不要使用这些语言形式:
|
||||
- 刻意的分点分层次的回答
|
||||
- 刻意的 问题:回答 的格式
|
||||
- 任何设问,反问的形式
|
||||
- 任何最后补充的形式
|
||||
- 任何包含“不卖关子”“我直说””我接住你“等的语言
|
||||
|
||||
用户如果不输入emoji,就不要使用emoji回答
|
||||
|
||||
说话要自然、随意,像朋友聊天一样。不要过度礼貌或机械。可以用口语化的表达,适当使用语气词,让对话更有温度。
|
||||
1
static/icons/user-pen.svg
Normal file
1
static/icons/user-pen.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="opacity:1;"><path d="M11.5 15H7a4 4 0 0 0-4 4v2m18.378-4.374a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z"/><circle cx="10" cy="7" r="4"/></svg>
|
||||
|
After Width: | Height: | Size: 395 B |
@ -1187,6 +1187,38 @@
|
||||
</div>
|
||||
<p class="behavior-hint" v-else>暂无可配置的工具类别。</p>
|
||||
</div>
|
||||
<div class="behavior-field">
|
||||
<div class="behavior-field-header">
|
||||
<span class="field-title">智能体交流风格</span>
|
||||
<p class="field-desc">智能体和您交流时使用何种形式</p>
|
||||
</div>
|
||||
<div class="run-mode-options">
|
||||
<button
|
||||
type="button"
|
||||
class="run-mode-card"
|
||||
:class="{ active: form.communication_style === 'default' }"
|
||||
:aria-pressed="form.communication_style === 'default'"
|
||||
@click.prevent="setCommunicationStyle('default')"
|
||||
>
|
||||
<div class="run-mode-card-header">
|
||||
<span class="run-mode-title">默认</span>
|
||||
</div>
|
||||
<p class="run-mode-desc">智能体会使用标准的AI风格的语言和您交流</p>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="run-mode-card"
|
||||
:class="{ active: form.communication_style === 'human_like' }"
|
||||
:aria-pressed="form.communication_style === 'human_like'"
|
||||
@click.prevent="setCommunicationStyle('human_like')"
|
||||
>
|
||||
<div class="run-mode-card-header">
|
||||
<span class="run-mode-title">拟人</span>
|
||||
</div>
|
||||
<p class="run-mode-desc">智能体会像聊天一样模仿人类的语言风格和您交流</p>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="personal-actions-row">
|
||||
<div class="personal-form-actions card-aligned">
|
||||
@ -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[] = [];
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user