From 820ec69354088dc6cb0539bb3ca2db89bd6dec69 Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Wed, 10 Jun 2026 15:14:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(memory):=20=E6=96=B0=E5=A2=9E=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E8=AE=B0=E5=BF=86=E7=B3=BB=E7=BB=9F=EF=BC=8C=E6=95=B4?= =?UTF-8?q?=E5=90=88=E6=80=BB=E4=BD=93+=E9=A1=B9=E7=9B=AE=E8=AE=B0?= =?UTF-8?q?=E5=BF=86=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新建 .agents/memory/ 项目记忆目录,支持 YAML frontmatter + Markdown - 新增 update_project_memory / recall_project_memory 工具(复用 write_file/read_file) - 新建 memory_system.txt prompt,总体长期记忆 + 项目记忆统一注入 - 记忆从 workspace_system.txt 中移出,独立为 memory_prompt 注入 - 重排 system prompt 注入顺序:主→权限→环境→对话→个性化→工作区→AGENTS.md→skills→记忆→禁用 - 压缩后选择性 invalidate 4 个 frozen keys(skills/workspace/personalization/memory) - 前端:notebook/notebook-pen 图标、动画、状态文案、展开内容渲染 - 优化三个记忆工具的 description 和 memory_system prompt,引导模型主动调用 --- core/main_terminal_parts/context.py | 172 +++++++++++++----- core/main_terminal_parts/tools_definition.py | 44 ++++- core/main_terminal_parts/tools_execution.py | 51 ++++++ core/main_terminal_parts/tools_read.py | 15 ++ prompts/memory_system.txt | 14 ++ prompts/workspace_system.txt | 2 - server/deep_compression.py | 13 +- static/icons/notebook-pen.svg | 1 + static/icons/notebook.svg | 1 + .../components/chat/actions/toolRenderers.ts | 52 ++++++ static/src/stores/monitor.ts | 2 + static/src/utils/chatDisplay.ts | 6 + static/src/utils/icons.ts | 4 + utils/context_manager.py | 1 - 14 files changed, 332 insertions(+), 46 deletions(-) create mode 100644 prompts/memory_system.txt create mode 100644 static/icons/notebook-pen.svg create mode 100644 static/icons/notebook.svg diff --git a/core/main_terminal_parts/context.py b/core/main_terminal_parts/context.py index 8cd0e17..e07f5cf 100644 --- a/core/main_terminal_parts/context.py +++ b/core/main_terminal_parts/context.py @@ -225,6 +225,79 @@ class MainTerminalContextMixin: # 构建上下文 return self.context_manager.build_main_context(memory) + def _scan_project_memories(self): + """扫描 .agents/memory/*.md 并解析 frontmatter 中的 name 和 description""" + try: + memory_dir = Path(self.project_path) / ".agents" / "memory" + except Exception: + return [] + if not memory_dir.exists() or not memory_dir.is_dir(): + return [] + results = [] + for md_file in sorted(memory_dir.glob("*.md")): + try: + text = md_file.read_text(encoding="utf-8") + name = None + description = None + if text.startswith("---"): + end = text.find("---", 3) + if end > 0: + frontmatter = text[3:end] + for line in frontmatter.strip().split("\n"): + line_stripped = line.strip() + if line_stripped.startswith("name:"): + name = line_stripped.split(":", 1)[1].strip() + elif line_stripped.startswith("description:"): + description = line_stripped.split(":", 1)[1].strip() + results.append({ + "file": md_file.name, + "name": name or md_file.stem, + "description": description or "", + }) + except Exception: + pass + return results + + def _build_memory_system_content(self) -> str: + """构建记忆系统的 prompt 内容""" + template = self.load_prompt("memory_system").strip() + if not template: + return "" + + try: + global_memory = self.memory_manager.read_main_memory() + global_memory_text = global_memory.strip() if global_memory else "" + except Exception: + global_memory_text = "" + + project_memories = self._scan_project_memories() + if project_memories: + lines = [] + for m in project_memories: + desc = m.get("description", "") + if desc: + lines.append(f".agents/memory/{m['file']}:{desc}") + else: + lines.append(f".agents/memory/{m['file']}") + project_memory_list = "\n".join(lines) + else: + project_memory_list = "" + + result = template + if global_memory_text: + result = result.replace("{global_memory}", global_memory_text) + result = result.replace("[global_memory_empty]", "").replace("[/global_memory_empty]", "") + else: + result = result.replace("{global_memory}", "") + + if project_memory_list: + result = result.replace("{project_memory_list}", project_memory_list) + result = result.replace("[project_memory_empty]", "").replace("[/project_memory_empty]", "") + else: + result = result.replace("{project_memory_list}", "") + + return result.strip() + def _build_recent_conversations_message(self, limit: int = 10) -> Optional[str]: """构建最近对话提示(仅当前工作区)。""" try: @@ -318,7 +391,6 @@ class MainTerminalContextMixin: 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", "") ) @@ -334,31 +406,26 @@ class MainTerminalContextMixin: personalization_config = getattr(self.context_manager, "custom_personalization_config", None) or load_personalization_config(self.data_dir) shallow_replace_enabled = bool(personalization_config.get("auto_shallow_compress_enabled", False)) if isinstance(personalization_config, dict) else False - skills_catalog = get_skills_catalog(private_dir=infer_private_skills_dir(self.data_dir)) - enabled_skills = merge_enabled_skills( - personalization_config.get("enabled_skills") if isinstance(personalization_config, dict) else None, - skills_catalog, - personalization_config.get("skills_catalog_snapshot") if isinstance(personalization_config, dict) else None, - ) - 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}) + # 顺序:主prompt → 权限模式 → 执行环境 → 最近对话 → 个性化配置 → 工作区信息 → AGENTS.md → skills → 记忆 → 自定义 → 禁用提示 - workspace_system = self._get_or_init_frozen_prompt( - "frozen_workspace_prompt", - lambda: self.context_manager._build_workspace_system_message(context) or "", + # 权限模式 + permission_mode_message = self._get_or_init_frozen_mode_prompt( + "frozen_permission_prompt", + self._build_permission_mode_message, ) - if workspace_system: - messages.append({"role": "system", "content": workspace_system}) + if permission_mode_message: + messages.append({"role": "system", "content": permission_mode_message}) + # 执行环境 + execution_mode_message = self._get_or_init_frozen_mode_prompt( + "frozen_execution_prompt", + self._build_execution_mode_message, + ) + if execution_mode_message: + messages.append({"role": "system", "content": execution_mode_message}) + + # 最近对话 def _build_recent_conversations_prompt() -> str: recent_conversations_enabled = ( bool(personalization_config.get("recent_conversations_prompt_enabled", False)) @@ -389,21 +456,7 @@ class MainTerminalContextMixin: if recent_conversations_prompt: messages.append({"role": "system", "content": recent_conversations_prompt}) - # 注入权限模式说明(根据当前模式动态生成) - permission_mode_message = self._get_or_init_frozen_mode_prompt( - "frozen_permission_prompt", - self._build_permission_mode_message, - ) - if permission_mode_message: - messages.append({"role": "system", "content": permission_mode_message}) - execution_mode_message = self._get_or_init_frozen_mode_prompt( - "frozen_execution_prompt", - self._build_execution_mode_message, - ) - if execution_mode_message: - messages.append({"role": "system", "content": execution_mode_message}) - - # 支持按对话覆盖的个性化配置 + # 个性化配置 def _build_personalization_system_prompt() -> str: personalization_block = build_personalization_prompt(personalization_config, include_header=False) if not personalization_block: @@ -422,7 +475,15 @@ class MainTerminalContextMixin: if personalization_text: messages.append({"role": "system", "content": personalization_text}) - # AGENTS.md 自动注入 + # 工作区信息 + 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}) + + # AGENTS.md 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: @@ -442,8 +503,37 @@ class MainTerminalContextMixin: if agents_md_text: messages.append({"role": "system", "content": agents_md_text}) - # 支持按对话覆盖的自定义 system prompt(API 用途)。 - # 放在最后一个 system 消息位置,确保优先级最高,便于业务场景强约束。 + # skills 列表 + skills_catalog = get_skills_catalog(private_dir=infer_private_skills_dir(self.data_dir)) + enabled_skills = merge_enabled_skills( + personalization_config.get("enabled_skills") if isinstance(personalization_config, dict) else None, + skills_catalog, + personalization_config.get("skills_catalog_snapshot") if isinstance(personalization_config, dict) else None, + ) + 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}) + + # 记忆系统(总体长期记忆 + 项目记忆) + def _build_memory_system_prompt() -> str: + return self._build_memory_system_content() + + memory_prompt = self._get_or_init_frozen_prompt( + "frozen_memory_prompt", + _build_memory_system_prompt, + ) + if memory_prompt: + messages.append({"role": "system", "content": memory_prompt}) + + # API 自定义 system prompt custom_system_prompt = self._get_or_init_frozen_prompt( "frozen_custom_system_prompt", lambda: ( @@ -455,7 +545,7 @@ class MainTerminalContextMixin: if custom_system_prompt: messages.append({"role": "system", "content": custom_system_prompt}) - # 禁用工具提示也作为“开头 system prompt”注入,便于后续统一合并。 + # 禁用工具提示 disabled_notice = self._get_or_init_frozen_prompt( "frozen_disabled_tools_prompt", lambda: self._format_disabled_tool_notice() or "", diff --git a/core/main_terminal_parts/tools_definition.py b/core/main_terminal_parts/tools_definition.py index 3941e2b..ac95bb6 100644 --- a/core/main_terminal_parts/tools_definition.py +++ b/core/main_terminal_parts/tools_definition.py @@ -835,7 +835,7 @@ class MainTerminalToolsDefinitionMixin: "type": "function", "function": { "name": "update_memory", - "description": "按条目更新记忆列表(自动编号)。append 追加新条目;replace 用序号替换;delete 用序号删除。应积极记录长期偏好、项目事实、已验证经验、用户明确要求记住的内容;不要记录密钥、隐私或未确认猜测。", + "description": "按条目管理总体长期记忆(自动编号,跨项目通用)。append/replace/delete。当用户提到个人信息、偏好、跨项目习惯,或用户主动要求「记住xxx」时,应积极主动地调用本工具。不记录密钥、隐私、未确认猜测。与特定项目绑定的技术约定用 update_project_memory。", "parameters": { "type": "object", "properties": self._inject_intent({ @@ -847,6 +847,48 @@ class MainTerminalToolsDefinitionMixin: } } }, + { + "type": "function", + "function": { + "name": "recall_project_memory", + "description": "读取指定的项目记忆文件,返回完整内容(含 frontmatter)。项目记忆存储在 .agents/memory/ 目录下。", + "parameters": { + "type": "object", + "properties": self._inject_intent({ + "name": { + "type": "string", + "description": "记忆名称,对应 .agents/memory/{name}.md 文件名" + } + }), + "required": ["name"] + } + } + }, + { + "type": "function", + "function": { + "name": "update_project_memory", + "description": "创建或覆盖项目记忆文件。当你发现有关当前项目的重要约定/决策/坑、用户表现出对项目的偏好,或用户主动要求「在当前项目里,下次要xxx/记住xxx」时,应积极主动地调用本工具。记忆名称用英文下划线,描述格式为'当xxxx时,应该索引本记忆'。不记录可从代码直接推断的或一次性信息。", + "parameters": { + "type": "object", + "properties": self._inject_intent({ + "name": { + "type": "string", + "description": "记忆名称,同时作为文件名 .agents/memory/{name}.md。建议英文+下划线,如 docker_compose" + }, + "description": { + "type": "string", + "description": "记忆简述,格式为'当xxxx时,应该索引本记忆',用于在 prompt 中作为索引展示(≤100字)" + }, + "content": { + "type": "string", + "description": "记忆正文(Markdown),记录具体内容" + } + }), + "required": ["name", "description", "content"] + } + } + }, { "type": "function", "function": { diff --git a/core/main_terminal_parts/tools_execution.py b/core/main_terminal_parts/tools_execution.py index d805272..219fdb8 100644 --- a/core/main_terminal_parts/tools_execution.py +++ b/core/main_terminal_parts/tools_execution.py @@ -185,6 +185,8 @@ class MainTerminalToolsExecutionMixin: "vlm_analyze", "ocr_image", "update_memory", + "recall_project_memory", + "update_project_memory", "conversation_search", "conversation_review", "todo_create", @@ -535,6 +537,35 @@ class MainTerminalToolsExecutionMixin: result.pop("target_dir", None) return result + def _handle_update_project_memory(self, name: str, description: str, content: str) -> Dict[str, Any]: + """处理 update_project_memory:写入 .agents/memory/{name}.md""" + safe_name = str(name).strip() + if not safe_name or "/" in safe_name or "\\" in safe_name: + return {"success": False, "error": f"记忆名称不合法: {name}"} + + # 确保 .agents/memory/ 目录存在 + memory_dir = Path(self.project_path) / ".agents" / "memory" + try: + memory_dir.mkdir(parents=True, exist_ok=True) + except Exception as exc: + return {"success": False, "error": f"创建记忆目录失败: {exc}"} + + # 拼接完整文件内容(YAML frontmatter + markdown) + full_content = f"---\nname: {safe_name}\ndescription: {description}\n---\n\n{content}\n" + file_path = memory_dir / f"{safe_name}.md" + + try: + file_path.write_text(full_content, encoding="utf-8") + except Exception as exc: + return {"success": False, "error": f"写入记忆文件失败: {exc}"} + + return { + "success": True, + "memory_name": safe_name, + "path": str(file_path), + "summary": f"已{'更新' if file_path.exists() else '创建'}项目记忆: {safe_name}", + } + def _mark_skill_read_from_result(self, tool_name: str, arguments: Dict[str, Any], result: Dict[str, Any]) -> None: if tool_name not in {"read_file", "read_skill"}: return @@ -1504,6 +1535,26 @@ class MainTerminalToolsExecutionMixin: index=index ) + elif tool_name == "recall_project_memory": + name = str(arguments.get("name", "")).strip() + if not name: + result = {"success": False, "error": "recall_project_memory 需要 name 参数"} + else: + result = self._handle_recall_project_memory(name) + + elif tool_name == "update_project_memory": + name = str(arguments.get("name", "")).strip() + description = str(arguments.get("description", "")).strip() + content_text = str(arguments.get("content", "")).strip() + if not name: + result = {"success": False, "error": "update_project_memory 需要 name 参数"} + elif not description: + result = {"success": False, "error": "update_project_memory 需要 description 参数"} + elif not content_text: + result = {"success": False, "error": "update_project_memory 需要 content 参数"} + else: + result = self._handle_update_project_memory(name, description, content_text) + elif tool_name == "conversation_search": query = str(arguments.get("query") or "").strip() raw_keywords = arguments.get("keywords") diff --git a/core/main_terminal_parts/tools_read.py b/core/main_terminal_parts/tools_read.py index 57333a4..5068848 100644 --- a/core/main_terminal_parts/tools_read.py +++ b/core/main_terminal_parts/tools_read.py @@ -158,6 +158,21 @@ class MainTerminalToolsReadMixin: result["skill_name"] = skill_name return result + def _handle_recall_project_memory(self, name: str) -> Dict: + """处理 recall_project_memory:读取 .agents/memory/{name}.md""" + safe_name = str(name).strip() + if not safe_name or "/" in safe_name or "\\" in safe_name: + return {"success": False, "error": f"记忆名称不合法: {name}"} + file_path = f".agents/memory/{safe_name}.md" + read_args = { + "path": file_path, + "type": "read", + } + result = self._handle_read_tool(read_args) + if result.get("success"): + result["memory_name"] = safe_name + return result + @staticmethod def _clamp_int(value, default, min_value=None, max_value=None): """将输入转换为整数并限制范围。""" diff --git a/prompts/memory_system.txt b/prompts/memory_system.txt new file mode 100644 index 0000000..d0a7351 --- /dev/null +++ b/prompts/memory_system.txt @@ -0,0 +1,14 @@ +项目记忆系统已启用。总体长期记忆来自用户的全局记录,项目记忆以文件形式存放在 .agents/memory/ 下。 + +- 开始涉及项目约定、历史决策或已知问题的任务前,先检查项目记忆索引,如有相关记忆则用 recall_project_memory 读取 +- 当你发现有关当前项目的重要约定/决策/调试坑、用户表现出对项目的偏好,或用户说「在这个项目里下次/记住」时,用 update_project_memory 主动记录 +- 当用户提到个人信息、偏好,或说「记住xxx」时,用 update_memory 记录为总体长期记忆 +- 记忆内容为提示而非事实,执行前应与实际代码验证 + +### 总体长期记忆 +{global_memory} +[global_memory_empty]暂无总体长期记忆[/global_memory_empty] + +### 项目记忆(.agents/memory/) +{project_memory_list} +[project_memory_empty]暂无项目记忆[/project_memory_empty] diff --git a/prompts/workspace_system.txt b/prompts/workspace_system.txt index 86662fa..0e5d0b1 100644 --- a/prompts/workspace_system.txt +++ b/prompts/workspace_system.txt @@ -9,5 +9,3 @@ ``` {file_tree} ``` - -- **长期记忆**:{memory} diff --git a/server/deep_compression.py b/server/deep_compression.py index b263f93..0779eb0 100644 --- a/server/deep_compression.py +++ b/server/deep_compression.py @@ -457,6 +457,13 @@ async def run_deep_compression( ) # 更新对话 metadata:压缩记录 + 清理压缩状态标记(同一对话,无切换)。 + # 同时清除需要重建的 frozen prompt 缓存,使压缩后下一次请求自动重新加载动态内容。 + REBUILD_FROZEN_KEYS = ( + "frozen_skills_prompt", + "frozen_workspace_prompt", + "frozen_personalization_prompt", + "frozen_memory_prompt", + ) meta_updates = { "compression_count": target_count, "deep_compression_records": all_records, @@ -469,10 +476,14 @@ async def run_deep_compression( "compression_resume_payload": None, "is_ultra_long_conversation": False, } + for frozen_key in REBUILD_FROZEN_KEYS: + meta_updates[frozen_key] = None cm.conversation_manager.update_conversation_metadata(conversation_id, meta_updates) - # 同步内存中的 metadata,保证后续在同一对话内的判断读到最新值。 + # 同步内存中的 metadata,清除 frozen 缓存 try: if getattr(cm, "current_conversation_id", None) == conversation_id and isinstance(cm.conversation_metadata, dict): + for frozen_key in REBUILD_FROZEN_KEYS: + cm.conversation_metadata.pop(frozen_key, None) cm.conversation_metadata.update(meta_updates) except Exception: pass diff --git a/static/icons/notebook-pen.svg b/static/icons/notebook-pen.svg new file mode 100644 index 0000000..6892d7c --- /dev/null +++ b/static/icons/notebook-pen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/icons/notebook.svg b/static/icons/notebook.svg new file mode 100644 index 0000000..b50cf38 --- /dev/null +++ b/static/icons/notebook.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/src/components/chat/actions/toolRenderers.ts b/static/src/components/chat/actions/toolRenderers.ts index 92d4664..685d426 100644 --- a/static/src/components/chat/actions/toolRenderers.ts +++ b/static/src/components/chat/actions/toolRenderers.ts @@ -100,6 +100,10 @@ export function renderEnhancedToolResult( // 记忆类 else if (name === 'update_memory') { return renderUpdateMemory(result, args); + } else if (name === 'recall_project_memory') { + return renderRecallProjectMemory(result, args); + } else if (name === 'update_project_memory') { + return renderUpdateProjectMemory(result, args); } else if (name === 'conversation_search') { return renderConversationSearch(result, args); } else if (name === 'conversation_review') { @@ -704,6 +708,54 @@ function renderUpdateMemory(result: any, args: any): string { return html; } +function renderRecallProjectMemory(result: any, args: any): string { + const name = args.name || result.memory_name || ''; + const status = formatToolStatusLabel(result, '✓ 已读取'); + const content = result.content || result.text || ''; + + let html = '
'; + html += `
状态:${status}
`; + html += `
记忆:${escapeHtml(name)}
`; + html += '
'; + + if (result.success && content) { + html += '
'; + html += `
${escapeHtml(content)}
`; + html += '
'; + } else if (!result.success && result.error) { + html += `
${escapeHtml(String(result.error))}
`; + } + + return html; +} + +function renderUpdateProjectMemory(result: any, args: any): string { + const name = args.name || result.memory_name || ''; + const description = args.description || ''; + const content = args.content || ''; + const status = formatToolStatusLabel(result, '✓ 已更新'); + + let html = '
'; + html += `
状态:${status}
`; + html += `
记忆:${escapeHtml(name)}
`; + if (description) { + html += `
描述:${escapeHtml(description)}
`; + } + if (!result.success && result.error) { + html += `
错误:${escapeHtml(String(result.error))}
`; + } + html += '
'; + + if (result.success && content) { + html += '
'; + html += '
记忆内容:
'; + html += `
${escapeHtml(content)}
`; + html += '
'; + } + + return html; +} + function renderConversationSearch(result: any, args: any): string { const results = Array.isArray(result?.results) ? result.results : []; const keywords = Array.isArray(result?.keywords) diff --git a/static/src/stores/monitor.ts b/static/src/stores/monitor.ts index a5a2e4f..e15d9af 100644 --- a/static/src/stores/monitor.ts +++ b/static/src/stores/monitor.ts @@ -113,6 +113,8 @@ const TOOL_SCENE_MAP: Record = { terminal_snapshot: 'terminalSnapshot', sleep: 'terminalSleep', update_memory: 'memoryUpdate', + recall_project_memory: 'reader', + update_project_memory: 'memoryUpdate', todo_create: 'todoCreate', todo_update_task: 'todoUpdate', todo_delete_task: 'todoDelete', diff --git a/static/src/utils/chatDisplay.ts b/static/src/utils/chatDisplay.ts index 96ef310..0f71783 100644 --- a/static/src/utils/chatDisplay.ts +++ b/static/src/utils/chatDisplay.ts @@ -18,6 +18,8 @@ const RUNNING_ANIMATIONS: Record = { vlm_analyze: 'file-animation', run_command: 'terminal-animation', update_memory: 'memory-animation', + recall_project_memory: 'read-animation', + update_project_memory: 'memory-animation', sleep: 'wait-animation', terminal_session: 'terminal-animation', terminal_input: 'terminal-animation', @@ -41,6 +43,8 @@ const RUNNING_STATUS_TEXTS: Record = { save_webpage: '正在保存网页...', run_command: '调用 run_command', update_memory: '正在更新记忆...', + recall_project_memory: '正在回顾项目记忆...', + update_project_memory: '正在更新项目记忆...', terminal_session: '正在管理终端会话...', terminal_input: '调用 terminal_input', terminal_snapshot: '正在获取终端快照...', @@ -63,6 +67,8 @@ const COMPLETED_STATUS_TEXTS: Record = { vlm_analyze: '图片解析完成', run_command: '命令执行完成', update_memory: '记忆更新成功', + recall_project_memory: '项目记忆已读取', + update_project_memory: '项目记忆已更新', terminal_session: '终端操作完成', terminal_input: '终端输入完成', terminal_snapshot: '终端快照已返回', diff --git a/static/src/utils/icons.ts b/static/src/utils/icons.ts index 6d5e1bc..c01b157 100644 --- a/static/src/utils/icons.ts +++ b/static/src/utils/icons.ts @@ -29,6 +29,8 @@ export const ICONS = Object.freeze({ mcpLogo: '/static/icons/mcp-logo.svg', monitor: '/static/icons/monitor.svg', navigation: '/static/icons/navigation.svg', + notebook: '/static/icons/notebook.svg', + notebookPen: '/static/icons/notebook-pen.svg', octagon: '/static/icons/octagon.svg', pencil: '/static/icons/pencil.svg', python: '/static/icons/python.svg', @@ -77,6 +79,8 @@ export const TOOL_ICON_MAP = Object.freeze({ terminal_snapshot: 'clipboard', unfocus_file: 'eye', update_memory: 'brain', + recall_project_memory: 'notebook', + update_project_memory: 'notebookPen', conversation_search: 'search', conversation_review: 'book', get_sub_agent_status: 'bot', diff --git a/utils/context_manager.py b/utils/context_manager.py index 220352c..8ab8ee0 100644 --- a/utils/context_manager.py +++ b/utils/context_manager.py @@ -2331,7 +2331,6 @@ class ContextManager: if context["project_info"].get("file_tree") else "" ), - memory=context["memory"], ) return content