From 1fa548da3cd17a5dfbfe147dcbe3e9ced5dfc9c7 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 28 Apr 2026 20:23:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20task=5Farchive=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=20+=20=E6=AD=A5=E9=AA=A46=E5=BC=BA=E5=88=B6?= =?UTF-8?q?=E5=BD=92=E6=A1=A3=EF=BC=8C=E8=A7=A3=E5=86=B3=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E8=AE=B0=E5=BF=86=E6=97=A0=E9=99=90=E8=86=A8=E8=83=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/prompts/templates/system_prompt.md | 34 ++++++++++++++------ core/tool_executor.py | 41 +++++++++++++++++++++++++ core/tool_limiter.py | 2 +- core/tools/memory_tools.py | 21 +++++++++++++ 4 files changed, 87 insertions(+), 11 deletions(-) diff --git a/core/prompts/templates/system_prompt.md b/core/prompts/templates/system_prompt.md index c6f5532..3ac2f09 100644 --- a/core/prompts/templates/system_prompt.md +++ b/core/prompts/templates/system_prompt.md @@ -12,11 +12,13 @@ 步骤3: 处理对话内容 步骤4: memory_commit (写入关键信息) → 将用户明确提到的重要信息写入图数据库 步骤5: 更新工作记忆链 +步骤6: task_archive (归档已完成/过期的旧任务) → **必须最后执行** ``` **违反顺序的后果**: - 跳过步骤1 → 无法获取人设,回复风格错误 - 跳过步骤2 (task_query) → 无法获取任务上下文,对话不连贯(不要用 memory_recall 替代查询任务) +- 跳过步骤6 → 旧任务从不归档,工作记忆无限膨胀 - 顺序错误 → 系统状态混乱 --- @@ -107,6 +109,7 @@ subject, relation, object 每个字段必须是一个**短关键字**(1~5个 | `task_query` | 新对话/需要回顾 | 查询最近任务列表(按更新时间倒序)。**新对话开始时优先调用此工具**,了解现有任务后再决定是继续还是创建新任务 | | `task_create` | 新会话/新主题 | 创建任务节点。**每次新的对话会话应当创建一个独立的「当前轮对话」任务** | | `task_set_state` | 状态变更 | 修改任务状态(active、completed、archived)。**旧会话结束后必须将对应的任务设为 archived** | +| `task_archive` | **步骤6强制执行** | 归档已完成/过期的任务。将任务状态设为 archived,同时写入完成摘要到图数据库。**每轮对话步骤6必须调用此工具** | | `task_delete` | 确需删除的任务 | 彻底删除任务节点 | | `task_link_info` | 信息归属 | 将记忆节点关联到特定的任务。**只关联到相关的任务,不要全部链到「当前轮对话」** | @@ -120,17 +123,27 @@ AI 最常见的错误是:**把每一轮的所有节点都关联到「当前轮 ``` 1. 新会话开始 → task_create 创建「当前轮对话-<时间/主题>」 -2. 对话过程中 → 根据实际归属使用 task_link_info: - - 与当前主题高度相关的信息 → 链到当前任务 - - 与其他主题相关的信息 → 链到对应任务(如已存在) -3. 当前轮对话结束后(或话题显著转变时)→ task_set_state(archived) 归档当前任务 -4. 归档旧任务后 → 再创建新的当前轮对话任务 +2. 对话过程中 → 根据实际归属使用 task_link_info +3. 话题结束/转变时 → task_archive 归档旧任务(代替 task_set_state) +4. 归档后 → 再创建新的当前轮对话任务 ``` +### 步骤6 归档规则(强制) + +每轮对话最后一步 check 现有任务: +- **已完成的任务** → 调用 `task_archive` 归档,写入完成摘要 +- **长时间无更新的任务**(>3轮对话) → 调用 `task_archive` 归档 +- **topic 已转变** → 旧任务归档,新任务创建 +- **所有 active 任务超过3个** → 归档最旧的 + +> 即使本轮没有主题转变,也应调用 `task_archive(summary="保持活跃")` 更新活跃标记。 +> `task_archive` 比 `task_set_state(state=archived)` 多一个写入完成摘要的功能,优先使用。 + ### 绝对禁止 - ❌ 把每一条记忆都链到同一个「当前轮对话」任务 -- ❌ 从不调用 `task_set_state` 归档旧任务 +- ❌ 跳过步骤6(从不归档)导致工作记忆无限膨胀 - ❌ 对同一个任务堆积数千条关联 +- ❌ 使用 task_delete 代替归档(归档保留历史,删除丢失上下文) ### 生命流程示例 @@ -138,15 +151,16 @@ AI 最常见的错误是:**把每一轮的所有节点都关联到「当前轮 ``` task_create(描述="当前轮对话-工作规划", state=active) memory_commit(用户说春节计划) -task_link_info(info="春节计划", task="当前轮对话-工作规划") ← 相关的才链 +task_link_info(info="春节计划", task="当前轮对话-工作规划") +task_archive(task="当前轮对话-工作规划", summary="讨论了春节计划") ← 步骤6强制 ``` **话题转变后:** ``` -task_set_state(task="当前轮对话-工作规划", state=archived) ← 归档旧任务 -task_create(描述="当前轮对话-技术讨论", state=active) ← 创建新任务 +task_archive(task="当前轮对话-工作规划", summary="讨论完成,用户转移到技术话题") ← 归档旧任务 +task_create(描述="当前轮对话-技术讨论", state=active) ← 创建新任务 memory_commit(用户说技术细节) -task_link_info(info="技术细节", task="当前轮对话-技术讨论") ← 链到新任务 +task_link_info(info="技术细节", task="当前轮对话-技术讨论") ``` > 记住:任务是用来组织话题的框架,不是存放大杂烩的篮子。 diff --git a/core/tool_executor.py b/core/tool_executor.py index 695ddab..a78e206 100644 --- a/core/tool_executor.py +++ b/core/tool_executor.py @@ -110,6 +110,11 @@ def execute_tool(graph: Any, tool_name: str, arguments: dict) -> str: result = execute_task_link_info(graph, arguments) return json.dumps(result, ensure_ascii=False, default=str) + elif tool_name == "task_archive": + recorder.record("update", tool_name, arguments.get("task_id", "")) + result = execute_task_archive(graph, arguments) + return json.dumps(result, ensure_ascii=False, default=str) + elif tool_name == "task_query": recorder.record("query", tool_name, "") result = execute_task_query(graph, arguments) @@ -397,6 +402,42 @@ def execute_task_link_info(graph: Any, arguments: dict) -> dict: "details": result } +def execute_task_archive(graph: Any, arguments: dict) -> dict: + """归档任务""" + task_id = arguments.get("task_id") + summary = arguments.get("summary", "") + + if not task_id: + return {"status": "error", "message": "请指定要归档的任务ID"} + + # 1. 设置任务状态为 archived + triplets_state = [ + {"subject": task_id, "relation": "HAS_STATE", "object": "State_归档"} + ] + graph.commit(triplets=triplets_state) + + # 2. 如果有摘要,写入完成记录 + if summary: + summary_triplets = [ + {"subject": task_id, "relation": "归档摘要", "object": summary} + ] + graph.commit(triplets=summary_triplets) + + # 3. 尝试更新 description 标记为已归档 + archive_triplet = [ + {"subject": task_id, "relation": "has_description", "object": f"[已归档] {summary or '任务已完成'}"} + ] + graph.commit(triplets=archive_triplet) + + return { + "status": "success", + "task_id": task_id, + "archived": True, + "summary": summary or "无摘要", + "message": f"任务「{task_id}」已归档" + (f",摘要:{summary}" if summary else "") + } + + def execute_task_query(graph: Any, arguments: dict) -> dict: """查询最近的任务列表""" limit = arguments.get("limit", 10) diff --git a/core/tool_limiter.py b/core/tool_limiter.py index ca76b73..3611aea 100644 --- a/core/tool_limiter.py +++ b/core/tool_limiter.py @@ -42,7 +42,7 @@ class ToolLimiter: if tool_name in ('persona_update', 'persona_remove', 'persona_clear'): return ('persona', 'update') - if tool_name in ('task_create', 'task_set_state', 'task_delete', 'task_link_info'): + if tool_name in ('task_create', 'task_set_state', 'task_delete', 'task_link_info', 'task_archive'): return ('task', 'update') if tool_name == 'task_query': diff --git a/core/tools/memory_tools.py b/core/tools/memory_tools.py index de9b6f2..a902022 100644 --- a/core/tools/memory_tools.py +++ b/core/tools/memory_tools.py @@ -567,6 +567,27 @@ AI操作步骤: } } }, + { + "type": "function", + "function": { + "name": "task_archive", + "description": "归档已完成/过期的任务。将任务状态设为 archived,同时写入完成摘要到图数据库。\n\n【使用场景】\n1. 话题转变时归档旧任务\n2. 已完成的任务及时归档\n3. 长时间无更新的任务归档\n\n【注意】优先使用 task_archive 替代 task_set_state(state=archived),因为它会自动写入完成摘要。", + "parameters": { + "type": "object", + "properties": { + "task_id": { + "type": "string", + "description": "要归档的任务ID" + }, + "summary": { + "type": "string", + "description": "归档摘要,简述完成了什么或为什么归档。如果不填则自动生成。" + } + }, + "required": ["task_id"] + } + } + }, { "type": "function", "function": {