From 3083b5d9d7b4cf8043c2b6ffe3addc284017e093 Mon Sep 17 00:00:00 2001 From: ansible Date: Tue, 7 Apr 2026 20:53:51 +0000 Subject: [PATCH] Add large output handling and iteration budget to agent - Tool outputs >2K chars saved to workspace/tool_outputs/ with preview - Agent gets first 1500 chars + file path to read the rest - Iteration budget bumped to 10 rounds (configurable per template) - Warning injected when 2 rounds remaining to help model wrap up --- agent/agent.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/agent/agent.py b/agent/agent.py index 8616e9c..11aa5b4 100644 --- a/agent/agent.py +++ b/agent/agent.py @@ -32,7 +32,7 @@ OLLAMA_URL = CONFIG.get("ollama_url", "http://172.16.0.1:11434") CONTEXT_SIZE = CONFIG.get("context_size", 20) MAX_RESPONSE_LINES = CONFIG.get("max_response_lines", 50) TOOLS_ENABLED = CONFIG.get("tools", True) -MAX_TOOL_ROUNDS = CONFIG.get("max_tool_rounds", 5) +MAX_TOOL_ROUNDS = CONFIG.get("max_tool_rounds", 10) WORKSPACE = "/workspace" SKILL_DIRS = ["/opt/skills", f"{WORKSPACE}/skills"] @@ -169,9 +169,15 @@ def discover_skills(): return tools, scripts +LARGE_OUTPUT_THRESHOLD = 2000 +LARGE_OUTPUT_DIR = f"{WORKSPACE}/tool_outputs" +_output_counter = 0 + + def execute_skill(script_path, args): - """Execute a skill script with args as JSON on stdin.""" - # Pass config extras via env + """Execute a skill script with args as JSON on stdin. + Large outputs are saved to a file with a preview returned.""" + global _output_counter env = os.environ.copy() env["WORKSPACE"] = WORKSPACE env["SEARX_URL"] = CONFIG.get("searx_url", "https://searx.mymx.me") @@ -188,7 +194,19 @@ def execute_skill(script_path, args): output = result.stdout if result.stderr: output += f"\n[stderr] {result.stderr}" - return output.strip() or "[no output]" + output = output.strip() or "[no output]" + + # Large output handling — save to file, return preview + if len(output) > LARGE_OUTPUT_THRESHOLD: + os.makedirs(LARGE_OUTPUT_DIR, exist_ok=True) + _output_counter += 1 + filepath = f"{LARGE_OUTPUT_DIR}/output_{_output_counter}.txt" + with open(filepath, "w") as f: + f.write(output) + preview = output[:1500] + return f"{preview}\n\n[output truncated — full result ({len(output)} chars) saved to {filepath}. Use run_command to read it: cat {filepath}]" + + return output except subprocess.TimeoutExpired: return "[skill timed out after 120s]" except Exception as e: @@ -323,6 +341,7 @@ def query_ollama(messages): payload["tools"] = TOOLS for round_num in range(MAX_TOOL_ROUNDS): + remaining = MAX_TOOL_ROUNDS - round_num try: data = ollama_request(payload) except (urllib.error.URLError, TimeoutError) as e: @@ -341,6 +360,9 @@ def query_ollama(messages): fn.get("arguments", {}), round_num + 1, ) + # Warn when budget is running low + if remaining <= 2: + result += f"\n[warning: {remaining - 1} tool rounds remaining — wrap up]" messages.append({"role": "tool", "content": result}) payload["messages"] = messages continue @@ -353,6 +375,8 @@ def query_ollama(messages): if fn_name in SKILL_SCRIPTS: messages.append({"role": "assistant", "content": content}) result = dispatch_tool(fn_name, fn_args, round_num + 1) + if remaining <= 2: + result += f"\n[warning: {remaining - 1} tool rounds remaining — wrap up]" messages.append({ "role": "user", "content": f"Tool result:\n{result}\n\nNow respond to the user based on this result.",