Simplify memory maintenance and remove legacy plan mode
This commit is contained in:
1 parent
9b3aff4358
commit
5205f4080f
3 files changed
+18
-38
No files matched your search
@@ -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] 必须在回复文本中包含<summary>!\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'<global_memory>{get_global_memory()}</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'<global_memory>{get_global_memory()}</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"
|
||||
|
||||
@@ -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. 从未证明有用的防御仪式。判据:说不出它哪次真抓到过问题,就删。
|
||||
写经验只记"坑在哪、怎么现形",不添加未验证有效的防御动作;带实测数字与反直觉机制的条目不动。
|
||||
先审准入:项目事实、实验流水、临时状态和未执行计划不入长期记忆,不能因带实测数字而豁免。
|
||||
只留已验证、跨会话仍重要且难以低成本重建的关键前置与典型坑,保留适用条件,不添加未验证有效的防御动作。
|
||||
@@ -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应当合并并概括性索引,无需列举!
|
||||
|
||||
---
|
||||
## 信息分类快速决策树
|
||||
|
||||
Reference in new issue
Block a user