diff --git a/agentmain.py b/agentmain.py index 08b78e09..676ecd0c 100644 --- a/agentmain.py +++ b/agentmain.py @@ -1,4 +1,4 @@ -import os, sys, threading, queue, time, json, re, random, locale +import os, sys, threading, queue, time, json, re, random, locale, glob os.environ.setdefault('GA_LANG', 'zh' if any(k in (locale.getlocale()[0] or '').lower() for k in ('zh', 'chinese')) else 'en') if sys.stdout is None: sys.stdout = open(os.devnull, "w") elif hasattr(sys.stdout, 'reconfigure'): sys.stdout.reconfigure(errors='replace') @@ -178,8 +178,6 @@ class GenericAgent: if self.inc_out and last_pos < len(full_resp): display_queue.put({'next': full_resp[last_pos:], 'source': source, 'turn': curr_turn, 'outputs': turn_resps[-2:]}) - #if '' in full_resp: full_resp = full_resp.replace('', '\n\n') - #if '' in full_resp: full_resp = re.sub(r'\s*(.*?)\s*', r'\n````\n\n\1\n\n````', full_resp, flags=re.DOTALL) display_queue.put({'done': full_resp, 'source': source, 'turn': curr_turn, 'outputs': turn_resps.copy()}) self.history = handler.history_info except Exception as e: @@ -198,6 +196,7 @@ if __name__ == '__main__': from datetime import datetime parser = argparse.ArgumentParser() parser.add_argument('--task', metavar='IODIR', help='一次性任务模式,先看subagent.md') + parser.add_argument('--func', metavar='PROMPT_FILE', help='纯函数模式:读prompt文件→结果写prompt.out.txt→退出') parser.add_argument('--reflect', metavar='SCRIPT', help='反射模式:加载监控脚本,check()触发时发任务') parser.add_argument('--input', help='prompt') parser.add_argument('--llm_no', type=int, default=0) @@ -206,14 +205,17 @@ if __name__ == '__main__': args, _unknown = parser.parse_known_args() _reflect_args = dict(zip([k.lstrip('-') for k in _unknown[::2]], _unknown[1::2])) if _unknown else {} - if args.task and not args.nobg: + if (args.func or args.task) and not args.nobg: import subprocess, platform cmd = [sys.executable, os.path.abspath(__file__)] + [a for a in sys.argv[1:]] + ['--nobg'] - d = os.path.join(script_dir, f'temp/{args.task}'); os.makedirs(d, exist_ok=True) + if args.task: + d = os.path.join(script_dir, f'temp/{args.task}'); os.makedirs(d, exist_ok=True) + out = open(os.path.join(d, 'stdout.log'), 'w', encoding='utf-8') + err = open(os.path.join(d, 'stderr.log'), 'w', encoding='utf-8') + else: out, err = subprocess.DEVNULL, subprocess.DEVNULL p = subprocess.Popen(cmd, cwd=script_dir, creationflags=0x08000000 if platform.system() == 'Windows' else 0, - stdout=open(os.path.join(d, 'stdout.log'), 'w', encoding='utf-8'), - stderr=open(os.path.join(d, 'stderr.log'), 'w', encoding='utf-8')) + stdout=out, stderr=err) print('PID:', p.pid); sys.exit(0) agent = GeneraticAgent() @@ -222,31 +224,35 @@ if __name__ == '__main__': threading.Thread(target=agent.run, daemon=True).start() if args.task: - agent.peer_hint = False - agent.force_non_stream = True agent.task_dir = d = os.path.join(script_dir, f'temp/{args.task}'); nround = '' - infile = os.path.join(d, 'input.txt') + infile = os.path.join(d, 'input.txt'); outfile = f'{d}/output{nround}.txt' if args.input: os.makedirs(d, exist_ok=True) - import glob; [os.remove(f) for f in glob.glob(os.path.join(d, 'output*.txt'))] + [os.remove(f) for f in glob.glob(os.path.join(d, 'output*.txt'))] with open(infile, 'w', encoding='utf-8') as f: f.write(args.input) if (fh := consume_file(d, '_history.json')): agent.llmclient.backend.history = json.loads(fh) + elif args.func: + infile = args.func; outfile = os.path.splitext(args.func)[0] + '.out.txt' + + if args.func or args.task: + agent.peer_hint = False with open(infile, encoding='utf-8') as f: raw = f.read() while True: - dq = agent.put_task(raw, source='task') + dq = agent.put_task(raw, source='func' if args.func else 'task') while 'done' not in (item := dq.get(timeout=1200)): - if 'next' in item and random.random() < 0.95: # 概率写一次中间结果 - with open(f'{d}/output{nround}.txt', 'w', encoding='utf-8') as f: f.write(item.get('next', '')) - with open(f'{d}/output{nround}.txt', 'w', encoding='utf-8') as f: f.write(item['done'] + '\n\n[ROUND END]\n') + if 'next' in item: + with open(outfile, 'w', encoding='utf-8') as f: f.write(item.get('next', '')) + with open(outfile, 'w', encoding='utf-8') as f: f.write(item['done'] + '\n\n[ROUND END]\n') + if not args.task: break consume_file(d, '_stop') # 已经成功停下来了,避免打断下次reply for _ in range(300): # 等reply.txt,10分钟超时 time.sleep(2) if (raw := consume_file(d, 'reply.txt')): break else: break nround = nround + 1 if isinstance(nround, int) else 1 + outfile = f'{d}/output{nround}.txt' elif args.reflect: agent.peer_hint = False - agent.force_non_stream = True import importlib.util spec = importlib.util.spec_from_file_location('reflect_script', args.reflect) mod = importlib.util.module_from_spec(spec); spec.loader.exec_module(mod) diff --git a/frontends/desktop/src-tauri/tauri.conf.json b/frontends/desktop/src-tauri/tauri.conf.json index 5681ffa1..e74082c1 100644 --- a/frontends/desktop/src-tauri/tauri.conf.json +++ b/frontends/desktop/src-tauri/tauri.conf.json @@ -17,7 +17,7 @@ "width": 1280, "height": 800, "resizable": true, - "maximized": true, + "maximized": false, "visible": false, "url": "loading.html" }, diff --git a/memory/computer_use.md b/memory/computer_use.md index 2e5340c4..a6efa700 100644 --- a/memory/computer_use.md +++ b/memory/computer_use.md @@ -12,7 +12,8 @@ | 3 vision(VLM) | 2仍不足时才用 | 仅语义理解、确认界面状态、辅助判断目标 | 不可信其坐标 | Windows 下窗口截图和操作使用 ljqCtrl:严禁 pyautogui;记得先 Activate 到前台; -坑1-DPI:一律物理坐标;坑2-遮盖/失焦:混乱先枚举窗口确认前台; +ui_detect附送OCR,不要单独使用OCR +坑1-DPI:必须先import ljqCtrl,之后一律使用物理坐标;坑2-遮盖/失焦:混乱先枚举窗口确认前台; ui_detect 的 bbox 是截图内坐标,点击前必须用 `ClientToScreen(hwnd,(0,0))/dpi_scale + bbox中心` 转屏幕物理坐标 坐标转换禁用 `GetWindowRect` 或 DWM 窗口矩形直接加截图坐标(含标题栏/边框/阴影会错位) ljqCtrl.Click 后会返回像素/前台变化,0% 或近 0% 变化立即停下诊断,禁止盲目重试。 diff --git a/memory/subagent.md b/memory/subagent.md index 702667e4..ae4b3b03 100644 --- a/memory/subagent.md +++ b/memory/subagent.md @@ -1,40 +1,38 @@ # Subagent 调用 SOP -## 文件IO协议 +## 两种模式 -- 目录:`temp/{task_name}/`(cwd在temp/时即`./{task_name}/`) -- 启动:`python agentmain.py --task {name} [--input "短文本"] [--llm_no N]`(cwd=代码根) +### --func 纯函数模式 +- `python agentmain.py --func prompt.txt [--llm_no N]`(cwd=代码根) +- 读prompt文件→执行→结果写`prompt.out.txt`→退出,主agent读完可删 +- 后台启动(print PID),加`--nobg`前台同步等结果 +- 适用:单次任务、并行map、不需要追问的场景 + +### --task 持续协作模式 +- `python agentmain.py --task {name} [--input "短文本"] [--llm_no N]`(cwd=代码根) - `--input`自动建目录+清旧output+写input.txt;长文本先手动写input.txt再启动(不带--input) -- 自动后台启动,print PID then exit -- 统一设定:所有agent的cwd都是temp,方便文件共享,不是task目录 -- input:目标+约束即可,subagent同等智能。**禁写步骤/过度描述**,大量数据给路径 -- 通信:output.txt(append,`[ROUND END]`=轮完成) → 写reply.txt继续 → 不写10min退出。reply后输出为output1/2/3.txt(同格式) +- **不要--nobg**(会卡在等reply循环),只能后台启动 +- 通信:output.txt(`[ROUND END]`=轮完成) → 写reply.txt继续 → 不写10min退出。reply后输出为output1/2/3.txt - 干预文件:`_stop`(当轮结束) | `_keyinfo`(注入working memory) | `_intervene`(追加指令) -- [[可选fork功能]](继承对话上下文): 事先code_run(inline_eval=True),将变量history(自动注入,str)写入task目录下_history.json -- [[可选监察者模式]]:**主agent空闲时应读output观察进度,必要时用干预文件纠偏,禁止无脑长时间sleep** - 若加`--verbose`,output将包含工具执行结果,主agent可直接审查原始数据而非仅信任摘要 +- [[可选fork]]:将变量history(str)写入task目录下`_history.json`继承对话上下文 +- [[可选监察者]]:主agent空闲时读output观察进度,必要时干预文件纠偏。加`--verbose`可审查原始数据 + +## 共通规则 +- 所有agent的cwd=temp,方便文件共享 +- input:目标+约束即可,subagent同等智能。**禁写步骤/过度描述**,大量数据给路径 ## 场景1:测试模式 - 行为验证 **用途**:观察agent真实行为,修正RULES/L2/L3/SOP -**流程**:创建test_path/写input.txt→启动subagent→轮询output.txt(2秒间隔)→验证→清理重复 -**测试原则**:只给目标,不提示位置/不诱导做法,观察自主选择 -**修正闭环**:发现问题→设计测试→定位根源(RULES/L2/L3/SOP)→patch修正→验证 -**技术要点**:Insight优先级>SOP;subagent的cwd=temp/ +**流程**:写prompt→启动subagent→轮询结果→验证→清理 +**原则**:只给目标,不提示位置/不诱导做法;Insight优先级>SOP;subagent的cwd=temp/ **两种测试**: -- 测SOP质量:input指定SOP名(如"用ezgmail_sop查看最近3封未读邮件"),排除导航干扰,失败即SOP问题 -- 测导航能力:input只写目标,验证subagent能自主从insight找到正确SOP。禁止内联SOP内容 +- 测SOP质量:input指定SOP名,排除导航干扰,失败即SOP问题 +- 测导航能力:input只写目标,验证能自主从insight找到正确SOP ## 场景2:Map模式 - 并行处理 -**用途**:将N个独立同构子任务分发给各自的subagent处理 -**核心优势**:独立上下文。避免处理文档A的长上下文污染处理文档B的质量 -**约束**: -- 文件系统共享是优点:不同agent处理不同输入文件,产生不同输出文件 -- 共享资源冲突:键鼠不可共享;浏览器避免操作同一tab -- 不满足map模式的任务 → 主agent顺序执行即可,别用subagent -**标准流程(map-reduce)**: -1. 主agent准备阶段:爬取/dump数据,存为多个独立输入文件 -2. 分发:对每个文件启动一个subagent处理(主agent自己也可以处理其中一个) -3. 收集:等所有subagent完成,主agent读取各输出文件,汇总结果 +**用途**:N个独立同构子任务分发,独立上下文避免交叉污染 +**约束**:文件系统共享(优点);键鼠不可共享;浏览器避免同tab +**流程**:准备独立输入文件→每个启动subagent(--func优先)→收集输出汇总 ## subagent内部plan_mode使用 **原则**:subagent本身是完整agent,接收多步骤任务时应在内部创建plan管理执行