core: per-instance print injection (no_print), EXIT sentinel for run loop, log name collision fix, checklist poll hard cap

This commit is contained in:
Liang Jiaqing committed 2026-06-04 11:09:54 +08:00
1 parent 3c4adeb26c
commit 5d122e20ea
3 files changed
+23 -15

No files matched your search

+7 -3
View File
@@ -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 '</summary>' in full_resp: full_resp = full_resp.replace('</summary>', '</summary>\n\n')
#if '</file_content>' in full_resp: full_resp = re.sub(r'<file_content>\s*(.*?)\s*</file_content>', r'\n````\n<file_content>\n\1\n</file_content>\n````', full_resp, flags=re.DOTALL)
display_queue.put({'done': full_resp, 'source': source, 'turn': curr_turn, 'outputs': turn_resps.copy()})
+14 -12
View File
@@ -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<key_info>{self.working.get('key_info')}</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):
+2
View File
@@ -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'