maodd commited on
Commit
2a57317
·
verified ·
1 Parent(s): 31039a7

Switch from CodeAgent to ToolCallingAgent

Browse files

15% 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.

Files changed (1) hide show
  1. app.py +19 -12
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: CodeAgent) -> None:
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
- CodeAgent re-sends the *entire* step history on every call, so input
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: CodeAgent) -> None:
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
- self.agent = CodeAgent(
 
 
 
 
 
 
 
 
 
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
- # add_base_tools=True would add a duplicate DuckDuckGo search tool -
127
- # every tool's schema is baked into the system prompt on every call,
128
- # keeping that overhead small helps stay well under the TPM budget.
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=[