Spaces:
Sleeping
Sleeping
Switch from CodeAgent to ToolCallingAgent
Browse files15% run showed gpt-oss-120b frequently breaking CodeAgent's <code> tag parser, burning steps on parse failures instead of progress. ToolCallingAgent uses native structured tool calls instead, eliminating this failure mode. PythonInterpreterTool added explicitly to retain compute capability.
app.py
CHANGED
|
@@ -10,8 +10,9 @@ import spaces
|
|
| 10 |
|
| 11 |
from smolagents import (
|
| 12 |
ActionStep,
|
| 13 |
-
CodeAgent,
|
| 14 |
LiteLLMModel,
|
|
|
|
|
|
|
| 15 |
WebSearchTool,
|
| 16 |
VisitWebpageTool,
|
| 17 |
WikipediaSearchTool,
|
|
@@ -56,7 +57,7 @@ class TokenPacer:
|
|
| 56 |
self.tokens_per_minute_budget = tokens_per_minute_budget
|
| 57 |
self._usage_window: deque[tuple[float, int]] = deque()
|
| 58 |
|
| 59 |
-
def __call__(self, memory_step: ActionStep, agent:
|
| 60 |
now = time.monotonic()
|
| 61 |
usage = getattr(memory_step, "token_usage", None)
|
| 62 |
tokens = (usage.input_tokens + usage.output_tokens) if usage else 0
|
|
@@ -75,7 +76,7 @@ class TokenPacer:
|
|
| 75 |
class MemoryTrimmer:
|
| 76 |
"""
|
| 77 |
Step_callback that collapses old tool outputs in the agent's memory.
|
| 78 |
-
|
| 79 |
tokens grow every single step (2k -> 5k -> 8k -> ... -> 20k+ by step 6),
|
| 80 |
which both wrecks the TPM budget and slows every later step far more
|
| 81 |
than it needs to. Keeps the most recent `keep_recent` steps' tool
|
|
@@ -87,7 +88,7 @@ class MemoryTrimmer:
|
|
| 87 |
self.keep_recent = keep_recent
|
| 88 |
self.max_old_observation_chars = max_old_observation_chars
|
| 89 |
|
| 90 |
-
def __call__(self, memory_step: ActionStep, agent:
|
| 91 |
action_steps = [step for step in agent.memory.steps if isinstance(step, ActionStep)]
|
| 92 |
for step in action_steps[:-self.keep_recent]:
|
| 93 |
observations = getattr(step, "observations", None)
|
|
@@ -116,19 +117,25 @@ class BasicAgent:
|
|
| 116 |
requests_per_minute=float(os.getenv("RATE_LIMIT_RPM", "4.5")),
|
| 117 |
retry=False,
|
| 118 |
)
|
| 119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
model=self.model,
|
| 121 |
tools=[
|
| 122 |
WebSearchTool(),
|
| 123 |
VisitWebpageTool(max_output_length=3000), # default 40000 chars blows the TPM budget in one call
|
| 124 |
WikipediaSearchTool(content_type="summary"), # "text" (default) returns the full article
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
additional_authorized_imports=[
|
| 130 |
-
"pandas", "numpy", "math", "re", "json", "itertools",
|
| 131 |
-
"collections", "statistics", "datetime", "io", "openpyxl", "PIL",
|
| 132 |
],
|
| 133 |
max_steps=7, # keep runs short: growing history otherwise blows the TPM budget by step ~6
|
| 134 |
step_callbacks=[
|
|
|
|
| 10 |
|
| 11 |
from smolagents import (
|
| 12 |
ActionStep,
|
|
|
|
| 13 |
LiteLLMModel,
|
| 14 |
+
PythonInterpreterTool,
|
| 15 |
+
ToolCallingAgent,
|
| 16 |
WebSearchTool,
|
| 17 |
VisitWebpageTool,
|
| 18 |
WikipediaSearchTool,
|
|
|
|
| 57 |
self.tokens_per_minute_budget = tokens_per_minute_budget
|
| 58 |
self._usage_window: deque[tuple[float, int]] = deque()
|
| 59 |
|
| 60 |
+
def __call__(self, memory_step: ActionStep, agent: ToolCallingAgent) -> None:
|
| 61 |
now = time.monotonic()
|
| 62 |
usage = getattr(memory_step, "token_usage", None)
|
| 63 |
tokens = (usage.input_tokens + usage.output_tokens) if usage else 0
|
|
|
|
| 76 |
class MemoryTrimmer:
|
| 77 |
"""
|
| 78 |
Step_callback that collapses old tool outputs in the agent's memory.
|
| 79 |
+
The agent re-sends the *entire* step history on every call, so input
|
| 80 |
tokens grow every single step (2k -> 5k -> 8k -> ... -> 20k+ by step 6),
|
| 81 |
which both wrecks the TPM budget and slows every later step far more
|
| 82 |
than it needs to. Keeps the most recent `keep_recent` steps' tool
|
|
|
|
| 88 |
self.keep_recent = keep_recent
|
| 89 |
self.max_old_observation_chars = max_old_observation_chars
|
| 90 |
|
| 91 |
+
def __call__(self, memory_step: ActionStep, agent: ToolCallingAgent) -> None:
|
| 92 |
action_steps = [step for step in agent.memory.steps if isinstance(step, ActionStep)]
|
| 93 |
for step in action_steps[:-self.keep_recent]:
|
| 94 |
observations = getattr(step, "observations", None)
|
|
|
|
| 117 |
requests_per_minute=float(os.getenv("RATE_LIMIT_RPM", "4.5")),
|
| 118 |
retry=False,
|
| 119 |
)
|
| 120 |
+
# ToolCallingAgent (not CodeAgent): gpt-oss-120b kept mixing reasoning
|
| 121 |
+
# text into its code instead of cleanly wrapping it in <code> tags,
|
| 122 |
+
# so CodeAgent's regex-based code-block parser failed on a large
|
| 123 |
+
# fraction of steps - wasted steps that burned tokens/rate-limit
|
| 124 |
+
# budget without making progress, and led to hallucinated final
|
| 125 |
+
# answers. ToolCallingAgent uses the provider's native structured
|
| 126 |
+
# tool-calling instead of parsing free-form text, which sidesteps
|
| 127 |
+
# this failure mode entirely. PythonInterpreterTool replaces the
|
| 128 |
+
# code-execution capability CodeAgent had built in.
|
| 129 |
+
self.agent = ToolCallingAgent(
|
| 130 |
model=self.model,
|
| 131 |
tools=[
|
| 132 |
WebSearchTool(),
|
| 133 |
VisitWebpageTool(max_output_length=3000), # default 40000 chars blows the TPM budget in one call
|
| 134 |
WikipediaSearchTool(content_type="summary"), # "text" (default) returns the full article
|
| 135 |
+
PythonInterpreterTool(authorized_imports=[
|
| 136 |
+
"pandas", "numpy", "math", "re", "json", "itertools",
|
| 137 |
+
"collections", "statistics", "datetime", "io", "openpyxl", "PIL",
|
| 138 |
+
]),
|
|
|
|
|
|
|
|
|
|
| 139 |
],
|
| 140 |
max_steps=7, # keep runs short: growing history otherwise blows the TPM budget by step ~6
|
| 141 |
step_callbacks=[
|