From 3acd58b36e6958e5cece977f616bd628ae746bf4 Mon Sep 17 00:00:00 2001 From: Liang Jiaqing Date: Thu, 4 Jun 2026 11:09:54 +0800 Subject: [PATCH] core: per-instance print injection (no_print), EXIT sentinel for run loop, log name collision fix, checklist poll hard cap --- agentmain.py | 10 +++++++--- ga.py | 26 ++++++++++++++------------ reflect/checklist_master.py | 2 ++ 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/agentmain.py b/agentmain.py index 90ba952f..4e06285d 100644 --- a/agentmain.py +++ b/agentmain.py @@ -53,7 +53,8 @@ class GenericAgent: self.inc_out = False; self.verbose = True; self.show_mode = 'text' self.peer_hint = True self.force_non_stream = False - self.log_path = os.path.join(script_dir, f'temp/model_responses/model_responses_{int(time.time()*1e6)%1000000:06d}.txt') + logid = f'{(time.time_ns() + random.randrange(1_000_000)) % 1_000_000:06d}' + self.log_path = os.path.join(script_dir, f'temp/model_responses/model_responses_{logid}.txt') self.load_llm_sessions() def load_llm_sessions(self): @@ -129,6 +130,7 @@ class GenericAgent: def run(self): while True: task = self.task_queue.get() + if isinstance(task, str): break raw_query, source, display_queue = task["query"], task["source"], task["output"] raw_query = self._handle_slash_cmd(raw_query, display_queue) if raw_query is None: @@ -144,6 +146,7 @@ class GenericAgent: sys_prompt = get_system_prompt() + getattr(self.llmclient.backend, 'extra_sys_prompt', '') if self.peer_hint: sys_prompt += f"\n[Peer] 用户提及其他会话/后台任务状态时: temp/model_responses/ (只找近期修改的文件尾部)\n" handler = GenericAgentHandler(self, self.history, os.path.join(script_dir, 'temp')) + if getattr(self, 'no_print', False): handler.print = lambda *a, **k: None if self.handler and 'key_info' in self.handler.working: ki = re.sub(r'\n\[SYSTEM\] 此为.*?工作记忆[。\n]*', '', self.handler.working['key_info']) # 去旧 handler.working['key_info'] = ki @@ -168,8 +171,9 @@ class GenericAgent: display_queue.put({'next': full_resp[last_pos:] if self.inc_out else full_resp, 'source': source, 'turn': curr_turn, 'outputs': turn_resps[-2:]}) last_pos = len(full_resp) - 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 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()}) diff --git a/ga.py b/ga.py index 2f1f1442..3ae4f991 100644 --- a/ga.py +++ b/ga.py @@ -9,7 +9,11 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from agent_loop import BaseHandler, StepOutcome, json_default script_dir = os.path.dirname(os.path.abspath(__file__)) -def code_run(code, code_type="python", timeout=60, cwd=None, code_cwd=None, stop_signal=None, maxlen=10000): +def safe_print(*args, **kwargs): + try: print(*args, **kwargs) + except: pass + +def code_run(code, code_type="python", timeout=60, cwd=None, code_cwd=None, stop_signal=None, maxlen=10000, myprint=safe_print): """代码执行器 python: 运行复杂的 .py 脚本(文件模式) powershell/bash: 运行单行指令(命令模式) @@ -33,7 +37,7 @@ def code_run(code, code_type="python", timeout=60, cwd=None, code_cwd=None, stop else: cmd = ["bash", "-c", code] else: return {"status": "error", "msg": f"不支持的类型: {code_type}"} - print("code run output:") + myprint("code run output:") startupinfo = None if os.name == 'nt': startupinfo = subprocess.STARTUPINFO() @@ -47,8 +51,7 @@ def code_run(code, code_type="python", timeout=60, cwd=None, code_cwd=None, stop try: line = line_bytes.decode('utf-8') except UnicodeDecodeError: line = line_bytes.decode('gbk', errors='ignore') logs.append(line) - try: print(line, end="") - except: pass + myprint(line, end="") except: pass try: @@ -65,7 +68,7 @@ def code_run(code, code_type="python", timeout=60, cwd=None, code_cwd=None, stop istimeout = time.time() - start_t > timeout if istimeout or stop_signal: process.kill() - print("[Debug] Process killed due to timeout or stop signal.") + myprint("[Debug] Process killed due to timeout or stop signal.") if istimeout: full_stdout.append("\n[Timeout Error] 超时强制终止") else: full_stdout.append("\n[Stopped] 用户强制终止") break @@ -271,6 +274,7 @@ class GenericAgentHandler(BaseHandler): self.history_info = last_history if last_history else [] self.code_stop_signal = [] self._done_hooks = [] + self.print = safe_print def _get_abs_path(self, path): if not path: return "" @@ -304,7 +308,7 @@ class GenericAgentHandler(BaseHandler): except SyntaxError: exec(code, ns); result = ns.get('_r', 'OK') except Exception as e: result = f'Error: {e}' finally: os.chdir(old_cwd) - else: result = yield from code_run(code, code_type, timeout, cwd, code_cwd=code_cwd, stop_signal=self.code_stop_signal, maxlen=maxlen) + else: result = yield from code_run(code, code_type, timeout, cwd, code_cwd=code_cwd, stop_signal=self.code_stop_signal, maxlen=maxlen, myprint=self.print) next_prompt = self._get_anchor_prompt(skip=args.get('_index', 0) > 0) return StepOutcome(result, next_prompt=next_prompt) @@ -350,8 +354,7 @@ class GenericAgentHandler(BaseHandler): result["js_return"] += f"\n\n[已保存完整内容到 {abs_path}]" except: result['js_return'] += f"\n\n[保存失败,无法写入文件 {abs_path}]" show = smart_format(json.dumps(result, ensure_ascii=False, indent=2, default=json_default), max_str_len=300) - try: print("Web Execute JS Result:", show) - except: pass + self.print("Web Execute JS Result:", show) yield f"JS 执行结果:\n{show}\n" next_prompt = self._get_anchor_prompt(skip=args.get('_index', 0) > 0) result = json.dumps(result, ensure_ascii=False, default=json_default) @@ -429,7 +432,8 @@ class GenericAgentHandler(BaseHandler): 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 - print(f"[Info] Entered plan mode with plan file: {plan_path}"); return plan_path + 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())) @@ -542,9 +546,7 @@ class GenericAgentHandler(BaseHandler): prompt += f"\nCurrent turn: {self.current_turn}\n" if self.working.get('key_info'): prompt += f"\n{self.working.get('key_info')}" if self.working.get('related_sop'): prompt += f"\n有不清晰的地方请再次读取{self.working.get('related_sop')}" - if getattr(self.parent, 'verbose', False): - try: print(prompt) - except: pass + if getattr(self.parent, 'verbose', False): self.print(prompt) return prompt def turn_end_callback(self, response, tool_calls, tool_results, turn, next_prompt, exit_reason): diff --git a/reflect/checklist_master.py b/reflect/checklist_master.py index 34dadc1c..80cd98e2 100644 --- a/reflect/checklist_master.py +++ b/reflect/checklist_master.py @@ -33,6 +33,8 @@ def _poll_bbs(data): return [] def check(): + check.times = getattr(check, "times", 0) + 1 + if check.times > 1000: return '/exit' if not _folder: return '/exit' data = _load() if not data or data.get("closed"): return '/exit'