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",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
"name": "manage_personalization",
|
"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": {
|
"parameters": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": self._inject_intent({
|
"properties": self._inject_intent({
|
||||||
@ -803,7 +803,7 @@ class MainTerminalToolsDefinitionMixin:
|
|||||||
},
|
},
|
||||||
"field": {
|
"field": {
|
||||||
"type": "string",
|
"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时需要)"
|
"description": "要更新的字段名(仅action=update时需要)"
|
||||||
},
|
},
|
||||||
"value": {
|
"value": {
|
||||||
|
|||||||
@ -1311,7 +1311,7 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
try:
|
try:
|
||||||
config = load_personalization_config(self.data_dir)
|
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}
|
result = {k: v for k, v in config.items() if k in readable_fields}
|
||||||
# 构建详细返回信息
|
# 构建详细返回信息
|
||||||
field_descriptions = {
|
field_descriptions = {
|
||||||
@ -1321,7 +1321,8 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
"profession": f"用户职业: {result.get('profession') or '(未设置)'}",
|
"profession": f"用户职业: {result.get('profession') or '(未设置)'}",
|
||||||
"tone": f"交流语气: {result.get('tone') or '(未设置)'}",
|
"tone": f"交流语气: {result.get('tone') or '(未设置)'}",
|
||||||
"considerations": f"注意事项: {len(result.get('considerations') 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()])
|
details = "\n".join([f"- {field_descriptions.get(k, k)}: {v}" for k, v in result.items()])
|
||||||
logger.info("[_execute_manage_personalization] read成功")
|
logger.info("[_execute_manage_personalization] read成功")
|
||||||
@ -1346,7 +1347,7 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
|
|
||||||
# 验证字段是否允许修改
|
# 验证字段是否允许修改
|
||||||
logger.info("[_execute_manage_personalization] 验证字段: field=%s", field)
|
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:
|
if field not in allowed_fields:
|
||||||
logger.warning("[_execute_manage_personalization] 字段不允许修改: %s not in %s", field, allowed_fields)
|
logger.warning("[_execute_manage_personalization] 字段不允许修改: %s not in %s", field, allowed_fields)
|
||||||
return {"success": False, "error": f"字段 '{field}' 不允许修改,可修改字段: {allowed_fields}"}
|
return {"success": False, "error": f"字段 '{field}' 不允许修改,可修改字段: {allowed_fields}"}
|
||||||
@ -1381,6 +1382,13 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
elif value not in ALLOWED_THEMES:
|
elif value not in ALLOWED_THEMES:
|
||||||
validation_errors.append(f"theme 必须是以下之一: {list(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:
|
if validation_errors:
|
||||||
logger.warning("[_execute_manage_personalization] 验证失败: %s", validation_errors)
|
logger.warning("[_execute_manage_personalization] 验证失败: %s", validation_errors)
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -45,6 +45,7 @@ DEFAULT_SHALLOW_KEEP_USER_TURN_TOOLS = 3
|
|||||||
|
|
||||||
DEFAULT_PERSONALIZATION_CONFIG: Dict[str, Any] = {
|
DEFAULT_PERSONALIZATION_CONFIG: Dict[str, Any] = {
|
||||||
"enabled": False,
|
"enabled": False,
|
||||||
|
"communication_style": "default", # default / human_like
|
||||||
"self_identify": "",
|
"self_identify": "",
|
||||||
"user_name": "",
|
"user_name": "",
|
||||||
"use_custom_names": False,
|
"use_custom_names": False,
|
||||||
@ -164,6 +165,9 @@ def sanitize_personalization_payload(
|
|||||||
return _sanitize_short_field(base.get(key))
|
return _sanitize_short_field(base.get(key))
|
||||||
|
|
||||||
base["enabled"] = bool(data.get("enabled", base["enabled"]))
|
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["auto_generate_title"] = bool(data.get("auto_generate_title", base["auto_generate_title"]))
|
||||||
base["self_identify"] = _resolve_short_field("self_identify")
|
base["self_identify"] = _resolve_short_field("self_identify")
|
||||||
base["user_name"] = _resolve_short_field("user_name")
|
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("深压缩触发上下文必须大于浅压缩触发上下文")
|
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(
|
def build_personalization_prompt(
|
||||||
config: Optional[Dict[str, Any]],
|
config: Optional[Dict[str, Any]],
|
||||||
include_header: bool = True
|
include_header: bool = True
|
||||||
@ -525,6 +550,13 @@ def build_personalization_prompt(
|
|||||||
for idx, item in enumerate(considerations, 1):
|
for idx, item in enumerate(considerations, 1):
|
||||||
lines.append(f"{idx}. {item}")
|
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):
|
if len(lines) == (1 if include_header else 0):
|
||||||
# 没有任何有效内容时不注入
|
# 没有任何有效内容时不注入
|
||||||
return None
|
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>
|
</div>
|
||||||
<p class="behavior-hint" v-else>暂无可配置的工具类别。</p>
|
<p class="behavior-hint" v-else>暂无可配置的工具类别。</p>
|
||||||
</div>
|
</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>
|
||||||
<div class="personal-actions-row">
|
<div class="personal-actions-row">
|
||||||
<div class="personal-form-actions card-aligned">
|
<div class="personal-form-actions card-aligned">
|
||||||
@ -1471,7 +1503,7 @@ const baseTabs = [
|
|||||||
{ id: 'workspace', label: '工作区设置', description: '默认权限模式' },
|
{ id: 'workspace', label: '工作区设置', description: '默认权限模式' },
|
||||||
{ id: 'usage', label: '用量统计', description: '对话累计数据' },
|
{ id: 'usage', label: '用量统计', description: '对话累计数据' },
|
||||||
{ id: 'app-update', label: '软件更新', description: '检查并下载新版 APK' },
|
{ id: 'app-update', label: '软件更新', description: '检查并下载新版 APK' },
|
||||||
{ id: 'behavior', label: '模型行为', description: '工具提示与界面表现' },
|
{ id: 'behavior', label: '智能体行为', description: '交流风格与界面表现' },
|
||||||
{ id: 'skills', label: 'Skills', description: '可用技能开关' },
|
{ id: 'skills', label: 'Skills', description: '可用技能开关' },
|
||||||
{ id: 'image', label: '图片压缩', description: '发送图片的尺寸策略' },
|
{ id: 'image', label: '图片压缩', description: '发送图片的尺寸策略' },
|
||||||
{ id: 'theme', label: '主题切换', description: '经典 / 明亮 / 夜间' },
|
{ id: 'theme', label: '主题切换', description: '经典 / 明亮 / 夜间' },
|
||||||
@ -1850,6 +1882,10 @@ const setDefaultPermissionMode = (value: PermissionModeValue) => {
|
|||||||
personalization.setDefaultPermissionMode(value);
|
personalization.setDefaultPermissionMode(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setCommunicationStyle = (value: 'default' | 'human_like') => {
|
||||||
|
personalization.setCommunicationStyle(value);
|
||||||
|
};
|
||||||
|
|
||||||
const checkModeModelConflict = (mode: RunModeValue, model: string | null): boolean => {
|
const checkModeModelConflict = (mode: RunModeValue, model: string | null): boolean => {
|
||||||
const found = (filteredModelOptions.value || []).find((item: any) => item.value === model);
|
const found = (filteredModelOptions.value || []).find((item: any) => item.value === model);
|
||||||
const warnings: string[] = [];
|
const warnings: string[] = [];
|
||||||
|
|||||||
@ -7,6 +7,7 @@ type PermissionMode = 'readonly' | 'approval' | 'unrestricted';
|
|||||||
|
|
||||||
interface PersonalForm {
|
interface PersonalForm {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
communication_style: 'default' | 'human_like';
|
||||||
auto_generate_title: boolean;
|
auto_generate_title: boolean;
|
||||||
tool_intent_enabled: boolean;
|
tool_intent_enabled: boolean;
|
||||||
skill_hints_enabled: boolean;
|
skill_hints_enabled: boolean;
|
||||||
@ -77,6 +78,7 @@ const EXPERIMENT_STORAGE_KEY = 'agents_personalization_experiments';
|
|||||||
|
|
||||||
const defaultForm = (): PersonalForm => ({
|
const defaultForm = (): PersonalForm => ({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
communication_style: 'default',
|
||||||
auto_generate_title: true,
|
auto_generate_title: true,
|
||||||
tool_intent_enabled: true,
|
tool_intent_enabled: true,
|
||||||
skill_hints_enabled: false,
|
skill_hints_enabled: false,
|
||||||
@ -228,6 +230,7 @@ export const usePersonalizationStore = defineStore('personalization', {
|
|||||||
: null) || 'kimi-k2.5';
|
: null) || 'kimi-k2.5';
|
||||||
this.form = {
|
this.form = {
|
||||||
enabled: !!data.enabled,
|
enabled: !!data.enabled,
|
||||||
|
communication_style: data.communication_style === 'human_like' ? 'human_like' : 'default',
|
||||||
auto_generate_title: data.auto_generate_title !== false,
|
auto_generate_title: data.auto_generate_title !== false,
|
||||||
tool_intent_enabled: !!data.tool_intent_enabled,
|
tool_intent_enabled: !!data.tool_intent_enabled,
|
||||||
skill_hints_enabled: !!data.skill_hints_enabled,
|
skill_hints_enabled: !!data.skill_hints_enabled,
|
||||||
@ -672,6 +675,13 @@ export const usePersonalizationStore = defineStore('personalization', {
|
|||||||
blockDisplayMode: mode
|
blockDisplayMode: mode
|
||||||
};
|
};
|
||||||
this.persistExperiments();
|
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',
|
id: 'tab-behavior',
|
||||||
title: '模型行为',
|
title: '智能体行为',
|
||||||
description: '下一步自动切换到“模型行为”。',
|
description: '下一步自动切换到“智能体行为”。',
|
||||||
target: '[data-tutorial="personal-tab-behavior"]',
|
target: '[data-tutorial="personal-tab-behavior"]',
|
||||||
mode: 'info',
|
mode: 'info',
|
||||||
autoClick: true,
|
autoClick: true,
|
||||||
@ -507,7 +507,7 @@ const DEFAULT_STEPS: TutorialStep[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'page-behavior',
|
id: 'page-behavior',
|
||||||
title: '模型行为内容',
|
title: '智能体行为内容',
|
||||||
description: '可配置工具显示、压缩策略等高级选项。',
|
description: '可配置工具显示、压缩策略等高级选项。',
|
||||||
target: '[data-tutorial="personal-content-shell"]',
|
target: '[data-tutorial="personal-content-shell"]',
|
||||||
mode: 'info',
|
mode: 'info',
|
||||||
|
|||||||
@ -35,6 +35,7 @@ export const ICONS = Object.freeze({
|
|||||||
trash: '/static/icons/trash.svg',
|
trash: '/static/icons/trash.svg',
|
||||||
triangleAlert: '/static/icons/triangle-alert.svg',
|
triangleAlert: '/static/icons/triangle-alert.svg',
|
||||||
user: '/static/icons/user.svg',
|
user: '/static/icons/user.svg',
|
||||||
|
userPen: '/static/icons/user-pen.svg',
|
||||||
wrench: '/static/icons/wrench.svg',
|
wrench: '/static/icons/wrench.svg',
|
||||||
x: '/static/icons/x.svg',
|
x: '/static/icons/x.svg',
|
||||||
zap: '/static/icons/zap.svg'
|
zap: '/static/icons/zap.svg'
|
||||||
@ -43,6 +44,7 @@ export const ICONS = Object.freeze({
|
|||||||
export const TOOL_ICON_MAP = Object.freeze({
|
export const TOOL_ICON_MAP = Object.freeze({
|
||||||
close_sub_agent: 'bot',
|
close_sub_agent: 'bot',
|
||||||
create_file: 'file',
|
create_file: 'file',
|
||||||
|
manage_personalization: 'userPen',
|
||||||
create_folder: 'folder',
|
create_folder: 'folder',
|
||||||
create_sub_agent: 'bot',
|
create_sub_agent: 'bot',
|
||||||
delete_file: 'trash',
|
delete_file: 'trash',
|
||||||
@ -75,6 +77,7 @@ export const TOOL_ICON_MAP = Object.freeze({
|
|||||||
export const TOOL_CATEGORY_ICON_MAP = Object.freeze({
|
export const TOOL_CATEGORY_ICON_MAP = Object.freeze({
|
||||||
network: 'globe',
|
network: 'globe',
|
||||||
file_edit: 'pencil',
|
file_edit: 'pencil',
|
||||||
|
personalization: 'userPen',
|
||||||
read_focus: 'eye',
|
read_focus: 'eye',
|
||||||
terminal_realtime: 'monitor',
|
terminal_realtime: 'monitor',
|
||||||
terminal_command: 'terminal',
|
terminal_command: 'terminal',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user