Skip to content

Research spike: source of from_* tool calls in the main loop #480

Description

@hoblin

Problem to solve

Aoide's main loop occasionally surfaces tool calls named after sister/sub-agent message channels — e.g. from_melete_goal, from_zero-width-sleuth, from_whitespace-scout. These names are not registered as tools; they are the prefixes the system uses when delivering messages to the agent. Calling them produces:

{"error":"Tools::UnknownToolError: Unknown tool: from_<name>"}

…polluting the conversation and wasting tokens. Today this is a known papercut that requires the human to nudge the agent ("these are not tools").

We do not currently know whether the offending tool calls originate from the LLM itself or from somewhere inside Anima.

Two hypotheses

  1. LLM hallucination. The model sees prior from_* blocks in the conversation context and pattern-matches them as callable tools. Anthropic's endpoint returns them in tool_use blocks because the model decided to invoke them.
  2. Internal leak. Some part of Anima's pipeline (event subscriber, tool-execution dispatcher, registry assembly) generates these calls when it shouldn't — they never come from the LLM at all.

The fix differs significantly between the two:

  • If hallucination: filter from_* tool_use blocks out of the dispatch path (lib/events/subscribers/llm_response_handler.rb#normalize_tool_uses) and pretend the LLM never called them. No error round-trip.
  • If internal leak: find the source and stop emitting them.

Research approach

Wire up debug logging for Aoide's main loop the same way the muses are instrumented today (lib/melete/runner.rb:132-133 logs system prompt and user message at debug level). For Aoide we additionally need:

  • Raw response payload from the Anthropic endpoint, including content blocks (text + tool_use)
  • The list of tool_use blocks as returned by the API, before any normalization
  • The tool name and ID of every dispatched tool execution, so we can correlate "what came in" vs "what got called"

Enable this in dev environment only.

Acceptance criteria

  • After a session that exhibits the bug, the logs contain enough information to determine whether the from_* tool_use block was present in the API response or was synthesized later.
  • Decision recorded in this issue (or a follow-up): hallucination vs internal leak.
  • Follow-up implementation issue opened with the appropriate fix.

Suspected choke points (starting hints, not prescribed)

  • lib/events/subscribers/llm_response_handler.rbnormalize_tool_uses is where the API response gets turned into dispatchable tool calls. Logging the raw response just before this method runs would give the cleanest signal.
  • The decorators in app/decorators/pending_from_*.rb produce the from_* named blocks that appear in the agent's view. Worth checking whether the same names ever leak into the outgoing tool registry (they shouldn't, but if they do, the model sees them as legitimate tools).

Why this matters

Every spurious from_* call costs context (the failed tool result is persisted), wastes tokens, and erodes trust in the agent's apparent competence. It's one of the first weird behaviors a new agent encounters, and the human currently has to coach around it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    anima-wantsFeature requested by the agent during real work

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions