refactor: agent loop done_hooks, inline_eval, scheduler logging & health_check
- agent_loop: for→while, _done_hooks callback mechanism, max_turns=40 - agentmain: pass self to handler, abort clears task queue - stapp: heartbeat yield for Streamlit StopException detection - ga: _inline_eval param for in-process eval, no_tool thresholds tuned - scheduler: logging, max_delay_hours, weekday repeat, health_check()
This commit is contained in:
28
ga.py
28
ga.py
@@ -299,7 +299,12 @@ class GenericAgentHandler(BaseHandler):
|
||||
raw_path = os.path.join(self.cwd, args.get("cwd", './'))
|
||||
cwd = os.path.normpath(os.path.abspath(raw_path))
|
||||
code_cwd = os.path.normpath(self.cwd)
|
||||
result = yield from code_run(code, code_type, timeout, cwd, code_cwd=code_cwd, stop_signal=self.code_stop_signal)
|
||||
if args.get("_inline_eval"):
|
||||
ns = {'handler': self, 'parent': self.parent}
|
||||
try: result = repr(eval(code, ns))
|
||||
except SyntaxError: exec(code, ns); result = ns.get('_r', 'OK')
|
||||
except Exception as e: result = f'Error: {e}'
|
||||
else: result = yield from code_run(code, code_type, timeout, cwd, code_cwd=code_cwd, stop_signal=self.code_stop_signal)
|
||||
next_prompt = self._get_anchor_prompt()
|
||||
return StepOutcome(result, next_prompt=next_prompt)
|
||||
|
||||
@@ -394,8 +399,7 @@ class GenericAgentHandler(BaseHandler):
|
||||
with open(path, 'a' if mode == "append" else 'w', encoding="utf-8") as f: f.write(new_content)
|
||||
yield f"[Status] ✅ {mode.capitalize()} 成功 ({len(new_content)} bytes)\n"
|
||||
next_prompt = self._get_anchor_prompt()
|
||||
return StepOutcome({"status": "success", 'writed_bytes': len(new_content)},
|
||||
next_prompt=next_prompt)
|
||||
return StepOutcome({"status": "success", 'writed_bytes': len(new_content)}, next_prompt=next_prompt)
|
||||
except Exception as e:
|
||||
yield f"[Status] ❌ 写入异常: {str(e)}\n"
|
||||
return StepOutcome({"status": "error", "msg": str(e)}, next_prompt="\n")
|
||||
@@ -437,31 +441,29 @@ class GenericAgentHandler(BaseHandler):
|
||||
def do_no_tool(self, args, response):
|
||||
'''这是一个特殊工具,由引擎自主调用,不要包含在TOOLS_SCHEMA里。
|
||||
当模型在一轮中未显式调用任何工具时,由引擎自动触发。
|
||||
二次确认仅在回复几乎只包含<thinking>/<summary>和一段大代码块时触发。
|
||||
'''
|
||||
二次确认仅在回复几乎只包含<thinking>/<summary>和一段大代码块时触发。'''
|
||||
content = getattr(response, 'content', '') or ""
|
||||
if not response or not content.strip():
|
||||
yield "[Warn] LLM returned an empty response. Retrying...\n"
|
||||
return StepOutcome({}, next_prompt="[System] Blank response, regenerate and tooluse", should_exit=False)
|
||||
return StepOutcome({}, next_prompt="[System] Blank response, regenerate and tooluse")
|
||||
if '流异常中断,未收到完整响应 !!!]' in content:
|
||||
return StepOutcome({}, next_prompt="[System] Incomplete response. Regenerate and tooluse.", should_exit=False)
|
||||
return StepOutcome({}, next_prompt="[System] Incomplete response. Regenerate and tooluse.")
|
||||
if 'max_tokens !!!]' in content:
|
||||
return StepOutcome({}, next_prompt="[System] max_tokens limit reached. Use multi small steps to do it.", should_exit=False)
|
||||
return StepOutcome({}, next_prompt="[System] max_tokens limit reached. Use multi small steps to do it.")
|
||||
# 2. 检测“包含较大代码块但未调用工具”的情况
|
||||
# 这里通过三引号代码块 + 最少字符数的方式粗略判断“大段代码”
|
||||
code_block_pattern = r"```[a-zA-Z0-9_]*\n[\s\S]{100,}?```"
|
||||
code_block_pattern = r"```[a-zA-Z0-9_]*\n[\s\S]{300,}?```"
|
||||
m = re.search(code_block_pattern, content)
|
||||
if m:
|
||||
# 仅当 content 由 <thinking> / <summary> 和该代码块构成时才触发二次确认
|
||||
residual = content
|
||||
# 去掉代码块本身
|
||||
residual = residual.replace(m.group(0), "")
|
||||
# 去掉<thinking>和<summary>块(大小写不敏感)
|
||||
residual = re.sub(r"<thinking>[\s\S]*?</thinking>", "", residual, flags=re.IGNORECASE)
|
||||
residual = re.sub(r"<summary>[\s\S]*?</summary>", "", residual, flags=re.IGNORECASE)
|
||||
# 如果去除上述结构后的非空白字符很少,说明没有额外自然语言说明
|
||||
clean_residual = re.sub(r"\s+", "", residual)
|
||||
if len(clean_residual) <= 50:
|
||||
if len(clean_residual) <= 20:
|
||||
yield "[Info] Detected large code block without tool call and no extra natural language. Requesting clarification.\n"
|
||||
next_prompt = (
|
||||
"[System] 检测到你在上一轮回复中主要内容是较大代码块(仅配有<thinking>/<summary>),且本轮未调用任何工具。\n"
|
||||
@@ -470,10 +472,10 @@ class GenericAgentHandler(BaseHandler):
|
||||
"如果只是向用户展示或讲解代码片段,请在回复中补充自然语言说明,"
|
||||
"并明确是否还需要额外的实际操作。"
|
||||
)
|
||||
return StepOutcome({}, next_prompt=next_prompt, should_exit=False)
|
||||
return StepOutcome({}, next_prompt=next_prompt)
|
||||
# 3. 正常情况:直接将回复返回给用户并结束循环
|
||||
yield "[Info] Final response to user.\n"
|
||||
return StepOutcome(response, next_prompt=None, should_exit=True)
|
||||
return StepOutcome(response, next_prompt=None)
|
||||
|
||||
def do_start_long_term_update(self, args, response):
|
||||
'''Agent觉得当前任务完成后有重要信息需要记忆时调用此工具。'''
|
||||
|
||||
Reference in New Issue
Block a user