From bd9e45f7613c7bbe3ef00f4a354f5c27932506f7 Mon Sep 17 00:00:00 2001 From: Liang Jiaqing Date: Wed, 15 Apr 2026 20:58:29 +0800 Subject: [PATCH] feat(file_read): fuzzy file suggestion on not-found with history dirs; sync L1 template --- README.md | 4 ++-- assets/global_mem_insight_template.txt | 32 ++++++++++++-------------- ga.py | 25 ++++++++++++++++---- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 486ab1f..3b9eb44 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ After a few weeks, your agent instance will have a skill tree no one else in the - **2026-03-10:** [Released million-scale Skill Library](https://mp.weixin.qq.com/s/q2gQ7YvWoiAcwxzaiwpuiQ?scene=1&click_id=7) - **2026-03-08:** [Released "Dintal Claw" — a GenericAgent-powered government affairs bot](https://mp.weixin.qq.com/s/eiEhwo-j6S-WpLxgBnNxBg) - **2026-03-01:** [GenericAgent featured by Jiqizhixin (机器之心)](https://mp.weixin.qq.com/s/uVWpTTF5I1yzAENV_qm7yg) -- **2026-01-11:** GenericAgent V1.0 public release +- **2026-01-16:** GenericAgent V1.0 public release --- @@ -251,7 +251,7 @@ MIT License — see [LICENSE](LICENSE) - **2026-03-10:** [发布百万级 Skill 库](https://mp.weixin.qq.com/s/q2gQ7YvWoiAcwxzaiwpuiQ?scene=1&click_id=7) - **2026-03-08:** [发布以 GenericAgent 为核心的"政务龙虾" Dintal Claw](https://mp.weixin.qq.com/s/eiEhwo-j6S-WpLxgBnNxBg) - **2026-03-01:** [GenericAgent 被机器之心报道](https://mp.weixin.qq.com/s/uVWpTTF5I1yzAENV_qm7yg) -- **2026-01-11:** GenericAgent V1.0 公开版本发布 +- **2026-01-16:** GenericAgent V1.0 公开版本发布 --- diff --git a/assets/global_mem_insight_template.txt b/assets/global_mem_insight_template.txt index 3e956d0..62a3971 100644 --- a/assets/global_mem_insight_template.txt +++ b/assets/global_mem_insight_template.txt @@ -1,24 +1,22 @@ # [Global Memory Insight] -浏览器自动化: web_scan/web_execute_js直接调用 | 特殊:tmwebdriver_sop(文件上传/图搜/PDF blob/元素物理坐标/Cookie提取含HttpOnly/跨域iframe操控/CDP/跨tab/后台tab操作) -键鼠模拟: ljqCtrl_sop+.py(仅win,禁pyautogui/先activate窗口) -定时任务: scheduled_task_sop(报告→sche_tasks/done/) | 与自主任务完全独立 -自主探索任务: autonomous_operation_sop(报告→temp/autonomous_reports/history.txt,不在memory下) | 与定时任务完全独立 -手机操控: adb_ui.py +浏览器特殊操作: tmwebdriver_sop(文件上传/图搜/PDF blob/物理坐标/HttpOnly Cookie/autofill突破/跨域iframe/CDP/跨tab) +键鼠: ljqCtrl_sop(禁pyautogui/先activate) 截图/视觉: ocr/vision_sop | 禁全屏截图,优先窗口 +定时:scheduled_task_sop | 自主:autonomous_operation_sop | watchdog/反射:agentmain --reflect +手机:adb_ui.py 需要时read L2 或 ls ../memory/ 查L3 L0(META-SOP): memory_management_sop L2: 现空 -L3: web_setup_sop | autonomous_operation_sop | scheduled_task_sop | ljqCtrl_sop+.py | tmwebdriver_sop | subagent_sop | plan_sop | procmem_scanner.py | adb_ui.py -L4: ../memory/L4_raw_sessions/ +L3: memory_cleanup_sop(记忆整理) | skill_search | ui_detect.py | ocr_utils.py | subagent | web_setup_sop | plan_sop +| procmem_scanner | keychain | ljqCtrl_sop+.py | tmwebdriver_sop | autonomous_operation_sop | scheduled_task_sop | vision_sop | adb_ui.py +L4: L4_raw_sessions/ [RULES] -1. 搜索先行: 信息尽量用google(必须web), 项目内os.listdir, 禁猜路径 -2. 交叉验证: 禁信搜索摘要, 数值必进详情页核实 -3. 编码安全: 改前必读源码; import memory用sys.path.append -4. 闭环: 物理模拟后必确认; 3次失败请求干预; -5. 进程: 禁无条件杀python(会杀自己), 精确PID, 禁os.kill判活 -6. 窗口: GUI状态优先枚举窗口, 比OCR快 -7. 物理红线: cwd用./; cwd指定后代码内禁用../向上切换,改用绝对路径 -8. web JS: 一次写对,输入用原生setter+事件链,点击前检查disabled,注意引号转义; scan空再scan或innerText -9. SOP: 执行前读取缓存硬参数,禁凭印象,有utils必用; 复杂长程先读plan_sop -10. 用户提及或复杂长程需规划任务要读plan_sop进入规划模式 +1. 搜索先行: 搜文件名严禁不用es(禁PS递归/禁dir遍历), 搜索一定优先使用web工具的google(严禁duckduckgo等), 优先看cwd,禁猜路径 +2. 交叉验证: 禁信摘要, 数值进详情页核实 +3. 编码安全: 禁PS cat/type用file_read; 改前必读; memory模块直接import(已在PATH,禁加虚假前缀) +4. 闭环: 物理模拟后确认; 3次失败请求干预; Git完整闭环 +5. 进程: 禁无条件杀python(杀自己), 精确PID, 禁os.kill判活 +6. 窗口: GUI状态优先win32gui枚举标题 +7. web JS: 输入用原生setter+事件链, 点击前检disabled, 注意引号转义; scan空/不全先稍等再scan, 禁首扫定论 +8. SOP: 读SOP禁凭印象,有utils必用 | 复杂长程/用户提及规划→读plan_sop diff --git a/ga.py b/ga.py index d20ae63..9cd9fbe 100644 --- a/ga.py +++ b/ga.py @@ -1,7 +1,7 @@ import sys, os, re, json, time, threading, importlib from datetime import datetime from pathlib import Path -import tempfile, traceback, subprocess, itertools, collections +import tempfile, traceback, subprocess, itertools, collections, difflib if sys.stdout is None: sys.stdout = open(os.devnull, "w") if sys.stderr is None: sys.stderr = open(os.devnull, "w") sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) @@ -221,6 +221,13 @@ def file_patch(path: str, old_content: str, new_content: str): except Exception as e: return {"status": "error", "msg": str(e)} +_read_dirs = set() +def _scan_files(base, depth=2): + try: + for e in os.scandir(base): + if e.is_file(): yield (e.name, e.path) + elif depth > 0 and e.is_dir(follow_symlinks=False): yield from _scan_files(e.path, depth - 1) + except (PermissionError, OSError): pass def file_read(path, start=1, keyword=None, count=200, show_linenos=True): try: with open(path, 'r', encoding='utf-8', errors='replace') as f: @@ -243,7 +250,19 @@ def file_read(path, start=1, keyword=None, count=200, show_linenos=True): res = [(i, l if len(l) <= L_MAX else l[:L_MAX] + TAG) for i, l in res] result = "\n".join(f"{i}|{l}" if show_linenos else l for i, l in res) if show_linenos: result = total_tag + result + _read_dirs.add(os.path.dirname(os.path.abspath(path))) return result + except FileNotFoundError: + msg = f"Error: File not found: {path}" + try: + tgt = os.path.basename(path); scan = os.path.dirname(os.path.dirname(os.path.abspath(path))) + roots = [scan] + [d for d in _read_dirs if not d.startswith(scan)] + cands = list(itertools.islice((c for base in roots for c in _scan_files(base)), 2000)) + top = sorted([(difflib.SequenceMatcher(None, tgt.lower(), c[0].lower()).ratio(), c) for c in cands[:2000]], key=lambda x: -x[0])[:5] + top = [(s, c) for s, c in top if s > 0.3] + if top: msg += "\n\nDid you mean:\n" + "\n".join(f" {c[1]} ({s:.0%})" for s, c in top) + except Exception: pass + return msg except Exception as e: return f"Error: {str(e)}" def smart_format(data, max_str_len=100, omit_str=' ... '): @@ -404,9 +423,7 @@ class GenericAgentHandler(BaseHandler): show_linenos = args.get("show_linenos", True) result = file_read(path, start=start, keyword=keyword, count=count, show_linenos=show_linenos) - if show_linenos: - tips = '由于设置了show_linenos,以下返回信息为:(行号|)内容 。\n' - result = tips + result + if show_linenos and not result.startswith("Error:"): result = '由于设置了show_linenos,以下返回信息为:(行号|)内容 。\n' + result if ' ... [TRUNCATED]' in result: result += '\n\n(某些行被截断,如需完整内容可改用 code_run 读取)' result = smart_format(result, max_str_len=20000, omit_str='\n\n[omitted long content]\n\n') next_prompt = self._get_anchor_prompt(skip=args.get('_index', 0) > 0)