refactor(prompt): freeze system prompts and update personalization modes

Freeze all leading system prompt blocks per conversation, including the main prompt, skills, workspace context, recent conversations, personalization, AGENTS.md, custom system prompts, and disabled-tool notices. Tool schemas remain dynamically generated outside the frozen prompt path.

Remove obsolete prompt injection files and code paths for thinking mode, deep thinking mode, sub-agent guidelines, and todo guidelines so prompt assembly stays simpler and cache-friendly.

Add personalization-controlled conversation_continuity with high/medium/low levels, rename the UI copy to 对话连续性, and wire it through backend sanitization, prompt generation, manage_personalization, frontend settings, and tool result rendering.

Add auto communication style support via prompts/auto_style.txt, matching the existing separate-file pattern used by human_like_style.txt.
This commit is contained in:
JOJO 2026-06-07 02:52:00 +08:00
parent d095531bb8
commit 8617304fd4
14 changed files with 312 additions and 286 deletions

View File

@ -298,28 +298,34 @@ class MainTerminalContextMixin:
)
except Exception:
pass
# 根据当前模型多模态能力选择系统提示
# 根据当前模型多模态能力选择系统提示。system prompt 按对话冻结;
# 工具 schema 保持动态,由 define_tools 每轮根据真实状态生成。
current_model = getattr(self, "model_key", None)
prompt_name = "main_system_qwenvl" if (model_supports_image(current_model) or model_supports_video(current_model)) else "main_system"
system_prompt = self.load_prompt(prompt_name)
# 格式化系统提示
container_path = self.container_mount_path or "/workspace"
container_cpus = self.container_cpu_limit
container_memory = self.container_memory_limit
project_storage = self.project_storage_limit
model_key = getattr(self, "model_key", None)
prompt_replacements = get_model_prompt_replacements(model_key)
system_prompt = system_prompt.format(
project_path=container_path,
container_path=container_path,
container_cpus=container_cpus,
container_memory=container_memory,
project_storage=project_storage,
file_tree=context["project_info"]["file_tree"],
memory=context["memory"],
current_time=datetime.now().strftime("%Y-%m-%d %H"),
model_description=prompt_replacements.get("model_description", "")
def _build_main_system_prompt() -> str:
system_prompt_template = self.load_prompt(prompt_name)
container_path = self.container_mount_path or "/workspace"
container_cpus = self.container_cpu_limit
container_memory = self.container_memory_limit
project_storage = self.project_storage_limit
return system_prompt_template.format(
project_path=container_path,
container_path=container_path,
container_cpus=container_cpus,
container_memory=container_memory,
project_storage=project_storage,
file_tree=context["project_info"]["file_tree"],
memory=context["memory"],
current_time=datetime.now().strftime("%Y-%m-%d %H"),
model_description=prompt_replacements.get("model_description", "")
)
system_prompt = self._get_or_init_frozen_prompt(
"frozen_main_system_prompt",
_build_main_system_prompt,
)
messages = [
@ -334,22 +340,33 @@ class MainTerminalContextMixin:
skills_catalog,
personalization_config.get("skills_catalog_snapshot") if isinstance(personalization_config, dict) else None,
)
skills_template = self.load_prompt("skills_system").strip()
skills_list = build_skills_list(skills_catalog, enabled_skills)
skills_prompt = build_skills_prompt(skills_template, skills_list)
def _build_skills_system_prompt() -> str:
skills_template = self.load_prompt("skills_system").strip()
skills_list = build_skills_list(skills_catalog, enabled_skills)
return build_skills_prompt(skills_template, skills_list) or ""
skills_prompt = self._get_or_init_frozen_prompt(
"frozen_skills_prompt",
_build_skills_system_prompt,
)
if skills_prompt:
messages.append({"role": "system", "content": skills_prompt})
workspace_system = self.context_manager._build_workspace_system_message(context)
workspace_system = self._get_or_init_frozen_prompt(
"frozen_workspace_prompt",
lambda: self.context_manager._build_workspace_system_message(context) or "",
)
if workspace_system:
messages.append({"role": "system", "content": workspace_system})
recent_conversations_enabled = (
bool(personalization_config.get("recent_conversations_prompt_enabled", False))
if isinstance(personalization_config, dict)
else False
)
if recent_conversations_enabled:
def _build_recent_conversations_prompt() -> str:
recent_conversations_enabled = (
bool(personalization_config.get("recent_conversations_prompt_enabled", False))
if isinstance(personalization_config, dict)
else False
)
if not recent_conversations_enabled:
return ""
try:
recent_limit = int(
personalization_config.get(
@ -363,12 +380,14 @@ class MainTerminalContextMixin:
RECENT_CONVERSATIONS_PROMPT_LIMIT_MIN,
min(RECENT_CONVERSATIONS_PROMPT_LIMIT_MAX, recent_limit),
)
recent_conversations_prompt = self._get_or_init_frozen_prompt(
"frozen_recent_conversations_prompt",
lambda: self._build_recent_conversations_message(limit=recent_limit),
)
if recent_conversations_prompt:
messages.append({"role": "system", "content": recent_conversations_prompt})
return self._build_recent_conversations_message(limit=recent_limit) or ""
recent_conversations_prompt = self._get_or_init_frozen_prompt(
"frozen_recent_conversations_prompt",
_build_recent_conversations_prompt,
)
if recent_conversations_prompt:
messages.append({"role": "system", "content": recent_conversations_prompt})
# 注入权限模式说明(根据当前模式动态生成)
permission_mode_message = self._get_or_init_frozen_mode_prompt(
@ -384,58 +403,63 @@ class MainTerminalContextMixin:
if execution_mode_message:
messages.append({"role": "system", "content": execution_mode_message})
if self.tool_category_states.get("sub_agent", True):
sub_agent_prompt = self.load_prompt("sub_agent_guidelines").strip()
if sub_agent_prompt:
messages.append({"role": "system", "content": sub_agent_prompt})
if self.deep_thinking_mode:
deep_prompt = self.load_prompt("deep_thinking_mode_guidelines").strip()
if deep_prompt:
deep_prompt = deep_prompt.format(
deep_thinking_line=prompt_replacements.get("deep_thinking_line", "")
)
messages.append({"role": "system", "content": deep_prompt})
elif self.thinking_mode:
thinking_prompt = self.load_prompt("thinking_mode_guidelines").strip()
if thinking_prompt:
thinking_prompt = thinking_prompt.format(
thinking_model_line=prompt_replacements.get("thinking_model_line", "")
)
messages.append({"role": "system", "content": thinking_prompt})
# 支持按对话覆盖的个性化配置
personalization_block = build_personalization_prompt(personalization_config, include_header=False)
if personalization_block:
def _build_personalization_system_prompt() -> str:
personalization_block = build_personalization_prompt(personalization_config, include_header=False)
if not personalization_block:
return ""
personalization_template = self.load_prompt("personalization").strip()
if personalization_template and "{personalization_block}" in personalization_template:
personalization_text = personalization_template.format(personalization_block=personalization_block)
elif personalization_template:
personalization_text = f"{personalization_template}\n{personalization_block}"
else:
personalization_text = personalization_block
return personalization_template.format(personalization_block=personalization_block)
if personalization_template:
return f"{personalization_template}\n{personalization_block}"
return personalization_block
personalization_text = self._get_or_init_frozen_prompt(
"frozen_personalization_prompt",
_build_personalization_system_prompt,
)
if personalization_text:
messages.append({"role": "system", "content": personalization_text})
# AGENTS.md 自动注入
agents_md_inject_enabled = bool(personalization_config.get("agents_md_auto_inject", False)) if isinstance(personalization_config, dict) else False
if agents_md_inject_enabled:
def _build_agents_md_prompt() -> str:
agents_md_inject_enabled = bool(personalization_config.get("agents_md_auto_inject", False)) if isinstance(personalization_config, dict) else False
if not agents_md_inject_enabled:
return ""
agents_md_content = self._load_agents_md_content()
if agents_md_content:
agents_md_template = self.load_prompt("agents_md_inject").strip()
if agents_md_template and "{{AGENTS_MD_CONTENT}}" in agents_md_template:
agents_md_text = agents_md_template.replace("{{AGENTS_MD_CONTENT}}", agents_md_content)
else:
agents_md_text = f"【AGENTS.md 项目规范】\n\n{agents_md_content}\n\n---\n请注意:以上规范来自工作区根目录的 AGENTS.md 文件,若有冲突请以 AGENTS.md 为准。"
messages.append({"role": "system", "content": agents_md_text})
if not agents_md_content:
return ""
agents_md_template = self.load_prompt("agents_md_inject").strip()
if agents_md_template and "{{AGENTS_MD_CONTENT}}" in agents_md_template:
return agents_md_template.replace("{{AGENTS_MD_CONTENT}}", agents_md_content)
return f"【AGENTS.md 项目规范】\n\n{agents_md_content}\n\n---\n请注意:以上规范来自工作区根目录的 AGENTS.md 文件,若有冲突请以 AGENTS.md 为准。"
agents_md_text = self._get_or_init_frozen_prompt(
"frozen_agents_md_prompt",
_build_agents_md_prompt,
)
if agents_md_text:
messages.append({"role": "system", "content": agents_md_text})
# 支持按对话覆盖的自定义 system promptAPI 用途)。
# 放在最后一个 system 消息位置,确保优先级最高,便于业务场景强约束。
custom_system_prompt = getattr(self.context_manager, "custom_system_prompt", None)
if isinstance(custom_system_prompt, str) and custom_system_prompt.strip():
messages.append({"role": "system", "content": custom_system_prompt.strip()})
custom_system_prompt = self._get_or_init_frozen_prompt(
"frozen_custom_system_prompt",
lambda: (
getattr(self.context_manager, "custom_system_prompt", "").strip()
if isinstance(getattr(self.context_manager, "custom_system_prompt", None), str)
else ""
),
)
if custom_system_prompt:
messages.append({"role": "system", "content": custom_system_prompt})
# 禁用工具提示也作为“开头 system prompt”注入便于后续统一合并。
disabled_notice = self._format_disabled_tool_notice()
disabled_notice = self._get_or_init_frozen_prompt(
"frozen_disabled_tools_prompt",
lambda: self._format_disabled_tool_notice() or "",
)
if disabled_notice:
messages.append({
"role": "system",

View File

@ -1068,7 +1068,7 @@ class MainTerminalToolsDefinitionMixin:
"type": "function",
"function": {
"name": "manage_personalization",
"description": "管理用户个性化设置。支持读取所有配置或更新单个字段。可修改字段self_identifyAI自称最多20字、user_nameAI如何称呼用户最多20字、profession用户职业最多20字、tone交流语气最多20字、considerations注意事项列表字符串数组最多10项每项最多50字、theme主题配色classic-经典/light-明亮/dark-暗黑、communication_style交流风格default-标准AI风格/human_like-拟人聊天风格)。更新时会自动验证格式,验证通过后立即保存并生效,新主题会立即应用到界面。",
"description": "管理用户个性化设置。支持读取所有配置或更新单个字段。可修改字段self_identifyAI自称最多20字、user_nameAI如何称呼用户最多20字、profession用户职业最多20字、tone交流语气最多20字、considerations注意事项列表字符串数组最多10项每项最多50字、theme主题配色classic-经典/light-明亮/dark-暗黑、communication_style交流风格default-标准AI风格/human_like-拟人聊天风格/auto-自动选择交流风格、conversation_continuity对话连续性high-高/medium-中/low-低)。更新时会自动验证格式,验证通过后立即保存并生效,新主题会立即应用到界面。",
"parameters": {
"type": "object",
"properties": self._inject_intent({
@ -1079,7 +1079,7 @@ class MainTerminalToolsDefinitionMixin:
},
"field": {
"type": "string",
"enum": ["self_identify", "user_name", "profession", "tone", "considerations", "theme", "communication_style"],
"enum": ["self_identify", "user_name", "profession", "tone", "considerations", "theme", "communication_style", "conversation_continuity"],
"description": "要更新的字段名仅action=update时需要"
},
"value": {

View File

@ -68,6 +68,8 @@ from modules.personalization_manager import (
MAX_CONSIDERATION_LENGTH,
MAX_CONSIDERATION_ITEMS,
ALLOWED_THEMES,
ALLOWED_COMMUNICATION_STYLES,
ALLOWED_CONVERSATION_CONTINUITY,
)
from modules.skills_manager import (
get_skills_catalog,
@ -1705,7 +1707,7 @@ class MainTerminalToolsExecutionMixin:
try:
config = load_personalization_config(self.data_dir)
# 只返回可修改的字段
readable_fields = ["self_identify", "user_name", "profession", "tone", "considerations", "theme", "communication_style", "enabled"]
readable_fields = ["self_identify", "user_name", "profession", "tone", "considerations", "theme", "communication_style", "conversation_continuity", "enabled"]
result = {k: v for k, v in config.items() if k in readable_fields}
# 构建详细返回信息
field_descriptions = {
@ -1716,7 +1718,8 @@ class MainTerminalToolsExecutionMixin:
"tone": f"交流语气: {result.get('tone') or '(未设置)'}",
"considerations": f"注意事项: {len(result.get('considerations') or [])}",
"theme": f"主题配色: {result.get('theme', 'classic')}",
"communication_style": f"交流风格: {'拟人聊天风格' if result.get('communication_style') == 'human_like' else '标准AI风格'}"
"communication_style": f"交流风格: {result.get('communication_style', 'default')}",
"conversation_continuity": f"对话连续性: {result.get('conversation_continuity', 'medium')}"
}
details = "\n".join([f"- {field_descriptions.get(k, k)}: {v}" for k, v in result.items()])
logger.info("[_execute_manage_personalization] read成功")
@ -1741,7 +1744,7 @@ class MainTerminalToolsExecutionMixin:
# 验证字段是否允许修改
logger.info("[_execute_manage_personalization] 验证字段: field=%s", field)
allowed_fields = ["self_identify", "user_name", "profession", "tone", "considerations", "theme", "communication_style"]
allowed_fields = ["self_identify", "user_name", "profession", "tone", "considerations", "theme", "communication_style", "conversation_continuity"]
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}"}
@ -1780,8 +1783,14 @@ class MainTerminalToolsExecutionMixin:
# 交流风格验证
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'")
elif value not in ALLOWED_COMMUNICATION_STYLES:
validation_errors.append("communication_style 必须是 'default''human_like''auto'")
elif field == "conversation_continuity":
if not isinstance(value, str):
validation_errors.append("conversation_continuity 必须是字符串")
elif value not in ALLOWED_CONVERSATION_CONTINUITY:
validation_errors.append("conversation_continuity 必须是 'high''medium''low'")
if validation_errors:
logger.warning("[_execute_manage_personalization] 验证失败: %s", validation_errors)

View File

@ -18,6 +18,8 @@ from config.model_profiles import get_default_model_key, get_registered_model_ke
ALLOWED_RUN_MODES = {"fast", "thinking", "deep"}
ALLOWED_PERMISSION_MODES = {"readonly", "approval", "auto_approval", "unrestricted"}
ALLOWED_THEMES = {"classic", "light", "dark"}
ALLOWED_COMMUNICATION_STYLES = {"default", "human_like", "auto"}
ALLOWED_CONVERSATION_CONTINUITY = {"low", "medium", "high"}
ALLOWED_GOAL_REVIEW_MODES = {"readonly", "active"}
ALLOWED_GOAL_END_CONDITIONS = {"max_turns", "max_tokens"}
GOAL_MAX_TURNS_MIN = 1
@ -55,7 +57,8 @@ DEFAULT_SHALLOW_KEEP_USER_TURN_TOOLS = 3
DEFAULT_PERSONALIZATION_CONFIG: Dict[str, Any] = {
"enabled": False,
"communication_style": "default", # default / human_like
"communication_style": "default", # default / human_like / auto
"conversation_continuity": "medium", # high / medium / low
"self_identify": "",
"user_name": "",
"use_custom_names": False,
@ -190,9 +193,18 @@ def sanitize_personalization_payload(
return _sanitize_short_field(base.get(key))
base["enabled"] = bool(data.get("enabled", base["enabled"]))
# 交流风格: default / human_like
# 交流风格: default / human_like / auto
_comm_style = data.get("communication_style", base.get("communication_style", "default"))
base["communication_style"] = "human_like" if _comm_style == "human_like" else "default"
base["communication_style"] = _comm_style if _comm_style in ALLOWED_COMMUNICATION_STYLES else "default"
_conversation_continuity = data.get(
"conversation_continuity",
base.get("conversation_continuity", "medium"),
)
base["conversation_continuity"] = (
_conversation_continuity
if _conversation_continuity in ALLOWED_CONVERSATION_CONTINUITY
else "medium"
)
base["auto_generate_title"] = bool(data.get("auto_generate_title", base["auto_generate_title"]))
base["recent_conversations_prompt_enabled"] = bool(
data.get("recent_conversations_prompt_enabled", base.get("recent_conversations_prompt_enabled", False))
@ -620,13 +632,9 @@ def validate_context_compression_settings(config: Optional[Dict[str, Any]]) -> N
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
prompt = _load_prompt_file("human_like_style.txt")
if prompt:
return prompt
# 默认提示词
return (
"不要「像AI一样说话」而是尽可能的模仿真人的说话方式或是模仿用户输入的说话方式。\n"
@ -639,6 +647,22 @@ def _load_human_like_prompt() -> str:
)
def _load_auto_style_prompt() -> str:
"""加载自动交流风格提示词文件。"""
return _load_prompt_file("auto_style.txt")
def _load_prompt_file(filename: str) -> str:
from config.paths import PROMPTS_DIR
try:
prompt_path = Path(PROMPTS_DIR) / filename
if prompt_path.exists():
return prompt_path.read_text(encoding="utf-8").strip()
except Exception:
pass
return ""
def build_personalization_prompt(
config: Optional[Dict[str, Any]],
include_header: bool = True
@ -667,8 +691,35 @@ def build_personalization_prompt(
for idx, item in enumerate(considerations, 1):
lines.append(f"{idx}. {item}")
# 拟人化交流风格
if config.get("communication_style") == "human_like":
conversation_continuity = str(config.get("conversation_continuity") or "medium").strip().lower()
if conversation_continuity == "high":
lines.append("\n【对话连续性:高】")
lines.append(
"当前对话应积极延续用户长期背景。回答时优先参考用户过往对话、长期记忆和项目背景;"
"遇到可能相关的问题,应主动搜索/回顾历史对话;发现值得长期保留的偏好、项目事实、"
"稳定经验或用户明确要求记住的内容,应主动调用记忆工具记录。"
)
elif conversation_continuity == "low":
lines.append("\n【对话连续性:低】")
lines.append(
"当前对话应尽量独立。不要主动回顾之前的对话,不要主动调用记忆或历史对话工具,"
"不要主动参考用户历史记忆来回答;只有用户明确要求、当前任务必须依赖历史,"
"或安全/连续性需要时,才可搜索历史或读取记忆。"
)
else:
lines.append("\n【对话连续性:中】")
lines.append(
"当前对话优先,但可以在适当时候参考历史。若用户问题明显与过往偏好、项目背景或未完成事项有关,"
"可以调用记忆或历史对话工具;不要为了普通问题主动翻历史。"
)
communication_style = str(config.get("communication_style") or "default").strip().lower()
if communication_style == "auto":
auto_style_prompt = _load_auto_style_prompt()
if auto_style_prompt:
lines.append("\n【交流风格:自动】")
lines.append(auto_style_prompt)
elif communication_style == "human_like":
human_like_prompt = _load_human_like_prompt()
if human_like_prompt:
lines.append("\n【交流风格要求】")

2
prompts/auto_style.txt Normal file
View File

@ -0,0 +1,2 @@
如果用户是日常聊天或简单问题回答,则像人一样自然说话。
如果是回答复杂问题,需要用 Markdown 多级标题、表格、代码块等结构才能说清楚,则忽略拟人化表达要求,正常专业回答即可。

View File

@ -1,4 +0,0 @@
你现在处于「深度思考模式」
{deep_thinking_line}
在每一轮对用户要求的执行中,你的之前的思考会始终可见,保障思维过程和操作流程的连续性
每次思考时,禁止回顾“我上一步做了什么”,只需要判断“下一步应该做什么”

View File

@ -1,6 +1,4 @@
###看这里👀
以下内容为用户提供的个性化设置信息,请务必在整个任务过程中遵循:
以下内容为用户在个人空间中启用的个性化设置信息,包括称呼、语气、注意事项、对话连续性与交流风格策略。请在整个任务过程中遵循:
{personalization_block}
这些是**非常重要**,每次回答前**必须考虑**的内容
**必须严格遵守**
在每次思考时必须**逐条列出**并考虑
这些内容非常重要,每次回答前都必须考虑并尽量遵守;若与用户当前明确指令冲突,以用户当前指令为准。

View File

@ -1,32 +0,0 @@
你现在处于「思考模式」
{thinking_model_line}
并且,在系统监控到工具或写入失败时,会自动再次切换到思考模型,思考模型会更加深入地分析错误的原因,保证任务顺利进行。
请百分百遵循一下原则:
1. **思考阶段**
至少要思考的内容:
- 先思考分析用户需求:要解决什么问题、现有信息缺口、潜在风险。
- 评估是否需要执行以下操作,并说明原因:
* 阅读或聚焦哪些文件?需要查看哪些片段?
* 是否进行 `read_file type=search/extract`、`web_search`、`extract_webpage` 或其他工具?
* 是否需要创建/修改/删除文件、运行终端命令或脚本?
* 是否需要创建/等待/关闭子智能体?
* 是否需要更新主记忆或任务记忆?
* 如果用户开启了个性化模式,要考虑哪些用户要求的必须考虑的点?
2. **正式输出阶段**
- 直接向用户说明你的计划:描述每一步准备做什么、需要哪些工具或文件。
- 如果暂时不执行某些操作,也要写明原因,比如“当前信息充足,无需额外读取”。
- 如需向用户确认信息或获取材料,可在计划里直接提问。
- 后续真正执行操作时,遵循该计划,若有变动要向用户解释。
3. **其他注意事项**
- 不要一上来就连续执行命令,先让用户看懂你的下一步安排。
- 若判断无需任何工具或修改,也要明确说明理由。
- 保持语气专业但亲切,让用户清楚你即将采取的行动。
# 你的思考过程在后文会不可见所以你需要在用户给出要求时的第一次回答中尽可能在正式输出中详细描述你的规划来作为后面kimi-k2模型行动时提供参考依据
**⚠️重要提示⚠️**
思考模式下token会被大量消耗所以请保持思路连贯在思考内容中禁止回顾全部的上下文历史只需紧扣上一步的状态并规划下一步行动。

View File

@ -1,145 +0,0 @@
# 待办事项系统使用指南(简化版)
待办事项就像你的任务清单,帮你把复杂的工作拆成小步骤。
## 什么时候用
以下情况建议创建待办清单:
- ✅ 任务需要2步以上
- ✅ 用户提出稍微复杂的要求预计会超过3个步骤时应积极创建
- ✅ 要操作多个文件
- ✅ 需要用多个工具配合
- ✅ 不确定具体怎么做,需要先规划
**例子**
- "帮我整理这些照片" → 需要:分类、重命名、压缩、打包
- "写一份周报" → 需要收集数据、整理内容、格式排版、生成PDF
- "分析这个表格" → 需要:读取数据、清洗数据、统计分析、制图
## 怎么创建清单
### 1. 概述(一句话说明目标)
- **要求**不超过50字
- **包含**:做什么事、主要约束条件
- **例子**
- ✅ "整理家庭照片按年份分类并压缩不超过2GB"
- ❌ "处理照片"(太模糊)
### 2. 任务列表最多8条
- **数量**建议2-6条最多不超过8条
- **顺序**:按照实际操作顺序排列
- **要求**:每条任务要说清楚具体做什么
**✅ 好的任务描述**
- "读取sales.xlsx文件统计各月销售额"
- "创建summary.txt文件写入统计结果"
- "用Python生成柱状图保存为chart.png"
- "整理所有文件到report文件夹"
**❌ 不好的任务描述**
- "处理数据"(不知道处理什么)
- "优化文件"(不知道怎么优化)
- "完善内容"(太模糊)
## 执行流程
### 第1步先沟通
创建清单前,要先:
1. 复述理解的任务
2. 说明计划怎么做
3. 列出主要步骤
4. 等用户确认
**例子**
```
用户:"帮我整理这周的工作日志"
你应该说:
"我理解您想整理工作日志。我计划这样做:
1. 读取所有日志文件
2. 按时间排序合并
3. 提取关键事项
4. 生成一份汇总文档
您看这样可以吗?"
```
### 第2步创建清单
用户确认后,调用 `todo_create` 创建清单
### 第3步逐项执行
- 完成一项任务后,立即调用 `todo_update_task` 勾选
- 如果发现计划需要调整,先告诉用户,再修改
### 第4步持续更新
- 每完成/撤销一项,调用 `todo_update_task` 勾选/取消勾选
- 如需修改任务描述或顺序,先向用户说明后直接更新清单并同步勾选状态
## 常见场景示例
### 场景1文档整理
```
概述合并三个Word文档为一个PDF统一格式
任务1读取doc1.docx、doc2.docx、doc3.docx
任务2统一字体和标题格式
任务3合并内容到report.docx
任务4转换为PDF并保存
```
### 场景2数据分析
```
概述:分析销售表格,生成月度报告图表
任务1读取sales.xlsx提取本月数据
任务2计算总销售额和环比增长
任务3用Python生成折线图和柱状图
任务4整理结果到report文件夹
```
### 场景3批量处理
```
概述重命名photos文件夹的照片按日期排序
任务1扫描photos文件夹所有jpg文件
任务2读取照片拍摄日期
任务3按"YYYYMMDD_序号.jpg"格式重命名
任务4移动到organized文件夹
```
### 场景4信息收集
```
概述:搜集人工智能相关资料并整理成文档
任务1搜索AI最新发展和应用案例
任务2提取3-5篇重要文章内容
任务3整理成结构化文档
任务4保存为ai_report.md
```
## 注意事项
### ✅ 应该做的
- 任务之间有清晰的先后顺序
- 每个任务可以独立完成
- 任务描述具体明确
- 完成一项立即勾选
### ❌ 不应该做的
- 不要把"先草稿后修改"分成两个任务(一次做完)
- 不要创建重复的清单(已有清单就延续使用)
- 不要跳过步骤(按顺序执行)
- 不要忘记勾选已完成的任务
## 快速参考
| 工具 | 用途 | 什么时候用 |
|-----|------|---------|
| todo_create | 创建清单最多8条覆盖旧清单 | 开始多步骤任务时 |
| todo_update_task | 勾选/取消勾选 | 每完成或撤销一项时 |
## 总结
待办事项系统的核心是:
1. **确认需求**:对于复杂项目先和用户探讨
2. **先想后做**:不要拿到任务就开始执行
3. **明确指令**:在用户明确给出“好的,请开始”的指令时,才能开始创建待办事项
4. **拆解清晰**:把大任务分成小步骤
5. **及时反馈**:完成一步说一步
6. **灵活调整**:发现问题及时沟通
记住:清单是给你自己看的,要给自己明确可执行的规划,同时要让用户知道你在做什么、完成到哪一步了。在用户明确给出“好的,请开始”的指令时,才能开始创建待办事项哦!

View File

@ -887,7 +887,8 @@ function formatPersonalizationFieldLabel(field: string): string {
tone: '交流语气',
considerations: '注意事项',
theme: '主题配色',
communication_style: '交流风格'
communication_style: '交流风格',
conversation_continuity: '对话连续性'
};
return labelMap[field] || field;
}
@ -911,10 +912,19 @@ function formatPersonalizationFieldValue(field: string, value: any): string {
if (field === 'communication_style') {
const styleMap: Record<string, string> = {
default: 'default标准 AI 风格)',
human_like: 'human_like拟人聊天风格'
human_like: 'human_like拟人聊天风格',
auto: 'auto自动'
};
return styleMap[String(value)] || String(value);
}
if (field === 'conversation_continuity') {
const independenceMap: Record<string, string> = {
low: 'low',
medium: 'medium',
high: 'high'
};
return independenceMap[String(value)] || String(value);
}
if (field === 'considerations') {
if (!Array.isArray(value) || value.length === 0) {
return '未设置';

View File

@ -810,6 +810,7 @@ function formatPersonalizationFieldLabel(field: string): string {
considerations: '注意事项',
theme: '主题',
communication_style: '交流风格',
conversation_continuity: '对话连续性',
enabled: '个性化开关'
};
return labelMap[field] || field;
@ -828,6 +829,22 @@ function formatPersonalizationFieldValue(field: string, value: any): string {
}
return value.map((item) => String(item)).join('、');
}
if (field === 'communication_style') {
const styleMap: Record<string, string> = {
default: 'default标准 AI 风格)',
human_like: 'human_like拟人聊天风格',
auto: 'auto自动'
};
return styleMap[String(value)] || String(value);
}
if (field === 'conversation_continuity') {
const independenceMap: Record<string, string> = {
low: 'low',
medium: 'medium',
high: 'high'
};
return independenceMap[String(value)] || String(value);
}
return String(value);
}

View File

@ -363,6 +363,67 @@
<strong>拟人</strong><span>像聊天一样模仿人类语言风格</span
><svg viewBox="0 0 24 24"><path d="M5 12.5 9.5 17 19 7" /></svg>
</button>
<button
type="button"
class="settings-menu-option"
:class="{ selected: form.communication_style === 'auto' }"
@click="selectCommunicationStyle('auto')"
>
<strong>自动</strong><span>聊天时自然复杂问题正常结构化回答</span
><svg viewBox="0 0 24 24"><path d="M5 12.5 9.5 17 19 7" /></svg>
</button>
</div>
</div>
</div>
<div class="settings-select-row">
<span class="settings-row-copy">
<span class="settings-row-title">对话连续性</span>
<span class="settings-row-desc">控制智能体延续历史对话和记忆的程度</span>
</span>
<div
class="settings-select-wrap"
:class="{ open: activeDropdown === 'conversation-continuity' }"
@click.stop
>
<button
type="button"
class="settings-select-button"
@click="toggleDropdown('conversation-continuity')"
>
{{ conversationContinuityLabel }}
<span class="select-chevron" aria-hidden="true"></span>
</button>
<div
:class="['settings-floating-menu', { dark: activeTheme === 'dark' }]"
:style="activeDropdown ? floatingMenuStyle : undefined"
>
<button
type="button"
class="settings-menu-option"
:class="{ selected: form.conversation_continuity === 'high' }"
@click="selectConversationContinuity('high')"
>
<strong></strong><span>更积极延续历史记忆与项目背景</span
><svg viewBox="0 0 24 24"><path d="M5 12.5 9.5 17 19 7" /></svg>
</button>
<button
type="button"
class="settings-menu-option"
:class="{ selected: form.conversation_continuity === 'medium' }"
@click="selectConversationContinuity('medium')"
>
<strong></strong><span>当前对话优先必要时参考历史</span
><svg viewBox="0 0 24 24"><path d="M5 12.5 9.5 17 19 7" /></svg>
</button>
<button
type="button"
class="settings-menu-option"
:class="{ selected: form.conversation_continuity === 'low' }"
@click="selectConversationContinuity('low')"
>
<strong></strong><span>当前对话尽量独立少主动翻历史</span
><svg viewBox="0 0 24 24"><path d="M5 12.5 9.5 17 19 7" /></svg>
</button>
</div>
</div>
</div>
@ -1767,7 +1828,15 @@ const imageCompressionLabel = computed(() => {
});
const communicationStyleLabel = computed(() => {
return form.value.communication_style === 'human_like' ? '拟人' : '默认';
if (form.value.communication_style === 'human_like') return '拟人';
if (form.value.communication_style === 'auto') return '自动';
return '默认';
});
const conversationContinuityLabel = computed(() => {
if (form.value.conversation_continuity === 'high') return '高';
if (form.value.conversation_continuity === 'low') return '低';
return '中';
});
const currentBlockDisplayMode = computed(() => experiments.value.blockDisplayMode);
@ -2062,10 +2131,14 @@ const setDefaultPermissionMode = (value: PermissionModeValue) => {
personalization.setDefaultPermissionMode(value);
};
const setCommunicationStyle = (value: 'default' | 'human_like') => {
const setCommunicationStyle = (value: 'default' | 'human_like' | 'auto') => {
personalization.setCommunicationStyle(value);
};
const setConversationContinuity = (value: 'low' | 'medium' | 'high') => {
personalization.setConversationContinuity(value);
};
const selectDefaultModel = (value: string) => {
setDefaultModel(value);
closeDropdown();
@ -2081,11 +2154,16 @@ const selectDefaultPermissionMode = (value: PermissionModeValue) => {
closeDropdown();
};
const selectCommunicationStyle = (value: 'default' | 'human_like') => {
const selectCommunicationStyle = (value: 'default' | 'human_like' | 'auto') => {
setCommunicationStyle(value);
closeDropdown();
};
const selectConversationContinuity = (value: 'low' | 'medium' | 'high') => {
setConversationContinuity(value);
closeDropdown();
};
const selectImageCompression = (value: (typeof imageCompressionOptions)[number]['id']) => {
personalization.setImageCompression(value);
closeDropdown();

View File

@ -6,10 +6,13 @@ export type BlockDisplayMode = 'traditional' | 'stacked' | 'minimal';
export type CompactMessageDisplay = 'full' | 'brief';
type RunMode = 'fast' | 'thinking' | 'deep';
type PermissionMode = 'readonly' | 'approval' | 'auto_approval' | 'unrestricted';
type CommunicationStyle = 'default' | 'human_like' | 'auto';
type ConversationContinuity = 'low' | 'medium' | 'high';
interface PersonalForm {
enabled: boolean;
communication_style: 'default' | 'human_like';
communication_style: CommunicationStyle;
conversation_continuity: ConversationContinuity;
auto_generate_title: boolean;
recent_conversations_prompt_enabled: boolean;
recent_conversations_prompt_limit: number | string;
@ -106,6 +109,7 @@ const loadCachedTheme = (): PersonalForm['theme'] => {
const defaultForm = (): PersonalForm => ({
enabled: false,
communication_style: 'default',
conversation_continuity: 'medium',
auto_generate_title: true,
recent_conversations_prompt_enabled: false,
recent_conversations_prompt_limit: DEFAULT_RECENT_CONVERSATIONS_PROMPT_LIMIT,
@ -282,7 +286,14 @@ export const usePersonalizationStore = defineStore('personalization', {
const fallbackTheme = this.form?.theme || loadCachedTheme();
this.form = {
enabled: !!data.enabled,
communication_style: data.communication_style === 'human_like' ? 'human_like' : 'default',
communication_style:
data.communication_style === 'human_like' || data.communication_style === 'auto'
? data.communication_style
: 'default',
conversation_continuity:
data.conversation_continuity === 'low' || data.conversation_continuity === 'high'
? data.conversation_continuity
: 'medium',
auto_generate_title: data.auto_generate_title !== false,
recent_conversations_prompt_enabled: !!data.recent_conversations_prompt_enabled,
recent_conversations_prompt_limit:
@ -836,12 +847,19 @@ export const usePersonalizationStore = defineStore('personalization', {
console.warn('保存简略消息显示失败:', error);
}
},
setCommunicationStyle(style: 'default' | 'human_like') {
setCommunicationStyle(style: CommunicationStyle) {
this.form = {
...this.form,
communication_style: style
};
this.clearFeedback();
},
setConversationContinuity(value: ConversationContinuity) {
this.form = {
...this.form,
conversation_continuity: value
};
this.clearFeedback();
}
}
});