diff --git a/ga.py b/ga.py index de031055..465190e2 100644 --- a/ga.py +++ b/ga.py @@ -448,16 +448,6 @@ class GenericAgentHandler(BaseHandler): def export_history(self, fn): with open(fn, 'w', encoding='utf-8') as f: json.dump(self.parent.llmclient.backend.history, f, ensure_ascii=False) def enter_project_mode(self, name): self.parent._ga_project_mode_name = name - def _in_plan_mode(self): return self.working.get('in_plan_mode') - def _exit_plan_mode(self): self.working.pop('in_plan_mode', None) - def enter_plan_mode(self, plan_path): - self.working['in_plan_mode'] = plan_path; self.max_turns = 100 - self.print(f"[Info] Entered plan mode with plan file: {plan_path}") - return plan_path - def _check_plan_completion(self): - if not os.path.isfile(p:=self._in_plan_mode() or ''): return None - try: return len(re.findall(r'\[ \]', open(p, encoding='utf-8', errors='replace').read())) - except: return None def do_update_working_checkpoint(self, args, response): '''为整个任务设定后续需要临时记忆的重点。''' @@ -491,11 +481,6 @@ class GenericAgentHandler(BaseHandler): if 'max_tokens !!!]' in content[-100:]: return self._retry_or_exit("[ERROR] max_tokens limit reached. Use multi small steps to do it.") - if self._in_plan_mode() and any(kw in content for kw in ['任务完成', '全部完成', '已完成所有', '🏁']): - if 'VERDICT' not in content and '[VERIFY]' not in content and '验证subagent' not in content: - yield "[Warn] Plan模式完成声明拦截。\n" - return StepOutcome({}, next_prompt="⛔ [验证拦截] 检测到你在plan模式下声称完成,但未执行[VERIFY]验证步骤。请先按plan_sop §四启动验证subagent,获得VERDICT后才能声称完成。") - # 2. 检测"包含较大代码块但未调用工具"的情况 # 关键特征:恰好1个大代码块 + 代码块直接结尾(后面只有空白) code_block_pattern = r"```[a-zA-Z0-9_]*\n[\s\S]{50,}?```" @@ -518,12 +503,6 @@ class GenericAgentHandler(BaseHandler): "并明确是否还需要额外的实际操作。" ) return StepOutcome({}, next_prompt=next_prompt) - - if self._in_plan_mode(): - remaining = self._check_plan_completion() - if remaining == 0: - self._exit_plan_mode(); yield "[Info] Plan完成:plan.md中0个[ ]残留,退出plan模式。\n" - #yield "[Info] Final response to user.\n" return StepOutcome(response, next_prompt=None) @@ -537,6 +516,7 @@ class GenericAgentHandler(BaseHandler): path = './memory/memory_management_sop.md' if os.path.exists(path): result = 'This is L0:\n' + file_read(path, show_linenos=False) else: result = "Memory Management SOP not found. Do not update memory." + if (l1 := load_L1()) and len(l1) > 6000: result += 'L1 (insight or index) is too large, follow L0 and cleanup sop to fix it!' if self.current_turn < 10: result, prompt = 'start_long_term_update is only used after completing a long turn task!', '\n' return StepOutcome(result, next_prompt=prompt) @@ -575,19 +555,14 @@ class GenericAgentHandler(BaseHandler): self.history_info.append('[Agent] ' + summary) if not rsumm and tool_calls and tool_calls[0]['tool_name'] != 'no_tool': next_prompt += "\n\n\n[TIPS] 必须在回复文本中包含!\n\n" - _plan = self._in_plan_mode() - if turn % 175 == 0 and (not _plan): + if turn % 175 == 0: next_prompt += f"\n\n[DANGER] Turn {turn}. Must call ask_user to summarize progress and get direction. No more blind retries." - elif turn % 13 == 0: + elif turn % 17 == 0: next_prompt += f"\n\n[DANGER] Turn {turn}. Call update_working_checkpoint to save key context. Stop ineffective retries; if no progress, switch strategy: 1) Probe physical boundaries 2) **Re-read relevant SOPs**" - elif turn % 31 == 0: + elif turn % 41 == 0: next_prompt += f"\n\n[DANGER] Turn {turn}. Write checkpoints/key findings/tried approaches to a **file** for future reference (not only working_checkpoint!). Avoid losing critical info." - elif turn % 10 == 0: next_prompt += f'{get_global_memory()}' - - if _plan and turn >= 10 and turn % 5 == 0: - next_prompt = f"[Plan Hint] 正在计划模式。必须 file_read({_plan}) 确认当前步骤,回复开头引用:📌 当前步骤:...\n\n" + next_prompt - if _plan and turn >= 190: next_prompt += f"\n\n[DANGER] Plan模式已运行 {turn} 轮,已达上限。必须 ask_user 汇报进度并确认是否继续。" + elif turn % 15 == 0: next_prompt += f'{get_global_memory()}' injkeyinfo = self.parent.extrakeyinfo or consume_file(self.parent.task_dir, '_keyinfo') injprompt = self.parent.intervene or consume_file(self.parent.task_dir, '_intervene') @@ -597,11 +572,17 @@ class GenericAgentHandler(BaseHandler): for hook in list(getattr(self.parent, '_turn_end_hooks', {}).values()): hook(locals()) # current readonly return next_prompt +def load_L1(): + try: + with open(os.path.join(script_dir, 'memory/global_mem_insight.txt'), 'r', encoding='utf-8', errors='replace') as f: content = f.read() + return content + except FileNotFoundError: return '' + def get_global_memory(): prompt = "\n" try: suffix = '_en' if os.environ.get('GA_LANG', '') == 'en' else '' - with open(os.path.join(script_dir, 'memory/global_mem_insight.txt'), 'r', encoding='utf-8', errors='replace') as f: insight = f.read() + insight = load_L1() with open(os.path.join(script_dir, f'assets/insight_fixed_structure{suffix}.txt'), 'r', encoding='utf-8') as f: structure = f.read() prompt += f'cwd = {os.path.join(script_dir, "temp")} (./)\n' prompt += f"\n[Memory] (../memory)\n" diff --git a/memory/memory_cleanup_sop.md b/memory/memory_cleanup_sop.md index 335c9e30..08a41084 100644 --- a/memory/memory_cleanup_sop.md +++ b/memory/memory_cleanup_sop.md @@ -18,6 +18,7 @@ ROI = (不放这几个词的犯错概率 × 代价) / 每轮词数成本 - 内容描述:`opencli_sop(66站点CLI,复用Chrome session)` → 实现细节属于SOP内部,不是触发场景 - 直觉能力:不提醒也能想到 → 0收益,白交每轮成本 - 冗余:L3已覆盖的规则 / L1其他行已含的片段 +- 简单判断:99% sop没有必要加括号触发词,特别是特定场景sop,仅用名字即可关联上场景 ## 压缩四原则 1. **命名自解释 > 加描述**:SOP名能说清的,L1不加注释;改名的ROI常高于改L1 @@ -48,12 +49,6 @@ ROI = (不放这几个词的犯错概率 × 代价) / 每轮词数成本 3. 每节独立闭环:迁移→压 L2→同步 L1,再进下一节,限制失败半径。 4. 验证:核对 6 项(每个新 SOP 文件存在 & 已入 L1)、L2 无遗留脏字符、总行数下降。 -## 坑:L2 历史脏字符导致 file_patch 匹配失败 -- 现象:`file_patch` 连续报"未找到匹配旧文本块",但肉眼看 old_content 与文件一致。 -- 根因:老记录行首可能混入真实的多余 `|`(或全角/箭头字节差异),复制时看不出。 -- 排查:`for i,l in enumerate(lines): print(i+1, repr(l[:20]))` 用 repr 看真实字节。 -- 解法:**改用 Python 按行号切片替换**:`lines=open(p,encoding='utf-8').read().split('\n'); assert lines[a].startswith(...); newlines=lines[:a]+repl+lines[b:]; open(p,'w',encoding='utf-8').write('\n'.join(newlines))`。前后加 `assert startswith` 双锚点防错位,改完 repr 复核。此法顺带清脏字符。 - ## L3 内容审计(清"误导"而非"占地") SOP 越写越让人不敢动手时,优先删这几类: 1. 无触发条件、不计成本的"必须全覆盖"要求。 @@ -61,4 +56,5 @@ SOP 越写越让人不敢动手时,优先删这几类: 3. 绑定过时人际局面而非事实的礼仪规则。 4. 散落各处的重复免责声明 → 合并为一条总则。 5. 从未证明有用的防御仪式。判据:说不出它哪次真抓到过问题,就删。 -写经验只记"坑在哪、怎么现形",不添加未验证有效的防御动作;带实测数字与反直觉机制的条目不动。 +先审准入:项目事实、实验流水、临时状态和未执行计划不入长期记忆,不能因带实测数字而豁免。 +只留已验证、跨会话仍重要且难以低成本重建的关键前置与典型坑,保留适用条件,不添加未验证有效的防御动作。 diff --git a/memory/memory_management_sop.md b/memory/memory_management_sop.md index c9782d70..528c44af 100644 --- a/memory/memory_management_sop.md +++ b/memory/memory_management_sop.md @@ -53,6 +53,7 @@ L4: ../memory/L4_raw_sessions/ (历史会话层 - scheduler反射自动收集, - 只记录:**跨会话**仍重要、且难以通过少量 file_read / web_scan / 简单脚本快速重建的要点。 - 优先写:该任务特有的隐藏前置条件、典型易踩坑点,一旦遗忘会导致高成本重试的信息。 - 不记录:普通操作步骤、可在几步探测中重新获得的路径或状态信息。不记录项目特定事实(不符合跨会话要求,应当放入项目目录中) +- L3 SOP更新:应当越更新越精华越短,而非追加 形式: - SOP(*_sop.md):为单一任务或小类任务保留极简的「关键前置 + 典型坑」清单,避免长篇教程。 - 工具脚本(*.py):仅封装高复用、逻辑相对复杂且不希望每次都重新推理的处理流程。 @@ -68,6 +69,8 @@ L4: ../memory/L4_raw_sessions/ (历史会话层 - scheduler反射自动收集, > **同步红线**:L1 只写关键词/名称,禁搬细节。括号内只写反直觉的场景触发词(2-4字),禁写机制/方法/步骤。需要评估L1中的token数和索引效用。 > 反例:❌ sop_name(场景A:方法1+方法2+方法3) → ✅ sop_name(场景A) > 反例:名字已自解释时 ❌ discord_slate_sop(Slate输入框) → ✅ discord_slate_sop +> **99% sop仅通过自解释名字索引,不需要触发词!** +> 大量同类sop应当合并并概括性索引,无需列举! --- ## 信息分类快速决策树