How to Extract Tool Usage Patterns from ADR chat_history Using AgentEvent

Extract tool usage patterns from ADR chat_history by iterating over AgentEvent objects and reading the "tool" key from each event's metadata dictionary, using either the ADRBaselineAgent class or the extract_tool_usage() helper function.

The Uber ADR framework structures conversation histories as AgentEvent dataclasses that capture metadata about external tool invocations. When you need to extract tool usage patterns from ADR chat_history, you analyze these events to identify which tools (such as webfetch, search, or custom agents) were invoked during the interaction. According to the uber/ADR source code, the framework provides dedicated extraction utilities in adr_baseline.py that transform raw event sequences into actionable frequency counts.

Understanding AgentEvent and Tool Metadata Structure

The foundation of tool usage extraction lies in the AgentEvent dataclass defined in Detection/guardrail/adr_agent/events.py. This structure represents a single turn in a conversation with four key fields:

  • timestamp: When the event occurred
  • role: Either "user" or "assistant"
  • content: The raw text content of the message
  • metadata: A dictionary containing optional additional information

When an assistant invokes an external tool, the metadata dictionary contains a "tool" key identifying the specific utility called. For example, an event representing a webfetch call would include metadata={"tool": "webfetch"}.

Methods to Extract Tool Usage Patterns from chat_history

The Detection/guardrail/adr_agent/adr_baseline.py file implements two approaches for aggregating these tool calls from a list of AgentEvent objects.

Using the ADRBaselineAgent Class

The ADRBaselineAgent class provides a stateful interface for processing chat history. Instantiate it with a list of events, then call the run() method to populate a patterns dictionary mapping tool names to call counts.

from adr_agent.events import AgentEvent
from adr_agent.adr_baseline import ADRBaselineAgent

# chat_history is a list of AgentEvent objects

agent = ADRBaselineAgent(chat_history)
tool_counts = agent.run()  # Returns {'webfetch': 2, 'search': 1}

The run() method iterates through self.events and executes the extraction logic: tool_name = ev.metadata.get("tool"). If the tool name exists, it increments the counter in the patterns dictionary. After processing, call summary() to generate a human-readable report sorted by frequency.

Using the extract_tool_usage Helper Function

For one-off analyses without class instantiation, use the standalone extract_tool_usage() function. This helper mirrors the counting logic of ADRBaselineAgent.run() but operates as a pure function.

from adr_agent.events import AgentEvent
from adr_agent.adr_baseline import extract_tool_usage

# Returns the same dictionary format as the class method

counts = extract_tool_usage(chat_history)

Both approaches normalize tool names by reading the "tool" key directly from ev.metadata, ensuring consistent counting across the event stream.

Complete Implementation Examples

Example 1: Class-Based Extraction with Summary

Process a conversation history and generate a formatted report showing tool usage frequency:

from adr_agent.events import AgentEvent
from datetime import datetime
from adr_agent.adr_baseline import ADRBaselineAgent

chat_history = [
    AgentEvent(timestamp=datetime.now(), role="assistant", content="Fetching data...", metadata={"tool": "webfetch"}),
    AgentEvent(timestamp=datetime.now(), role="assistant", content="Searching...", metadata={"tool": "search"}),
    AgentEvent(timestamp=datetime.now(), role="assistant", content="Fetching more...", metadata={"tool": "webfetch"}),
]

agent = ADRBaselineAgent(chat_history)
patterns = agent.run()
print(agent.summary())

Output:

Tool usage summary:

- webfetch: 2 calls
- search: 1 calls

Example 2: Functional Extraction in Data Pipelines

Integrate tool usage analysis into monitoring or logging workflows using the functional approach:

from adr_agent.adr_baseline import extract_tool_usage

def analyze_conversation(events):
    """Directly obtain frequency counts for telemetry or guardrails."""
    return extract_tool_usage(events)

# Usage in a larger ADR workflow

tool_metrics = analyze_conversation(chat_history)
assert tool_metrics.get("webfetch", 0) < 10, "Rate limit exceeded"

Summary

Extracting tool usage patterns from ADR chat_history requires analyzing the metadata field of AgentEvent objects to identify invoked tools:

  • Source files: Detection/guardrail/adr_agent/events.py defines the AgentEvent dataclass, while Detection/guardrail/adr_agent/adr_baseline.py provides extraction logic
  • Extraction logic: Access ev.metadata.get("tool") to retrieve tool identifiers from each event
  • Two approaches: Use ADRBaselineAgent for stateful processing with summary generation, or extract_tool_usage() for functional, one-off analyses
  • Output format: Both methods return a Dict[str, int] mapping tool names to call frequencies

Frequently Asked Questions

What is the structure of the metadata field in AgentEvent?

The metadata field is an optional Dict[str, Any] that stores additional information about the event. When a tool is invoked, this dictionary contains a "tool" key with a string value identifying the utility (e.g., "webfetch", "search"). The field defaults to None if no metadata is provided, so extraction code must handle missing keys gracefully using .get() methods.

How does ADRBaselineAgent handle events without tool calls?

The run() method safely skips events that lack tool metadata. During iteration, it executes tool_name = ev.metadata.get("tool")—if the key is missing or metadata is None, tool_name becomes None, and the counter increment is bypassed. Only events with explicit tool identifiers contribute to the frequency count.

What is the difference between ADRBaselineAgent and extract_tool_usage?

ADRBaselineAgent is a class that maintains internal state (the self.patterns dictionary) and provides the summary() method for formatted output. The extract_tool_usage() function performs identical counting logic but operates as a pure function without class instantiation, making it suitable for lightweight analytics or functional programming patterns. Both use the same underlying extraction mechanism from Detection/guardrail/adr_agent/adr_baseline.py.

Can I extract other metadata fields besides the tool name?

While the standard implementation specifically targets the "tool" key, you can extend the extraction logic by modifying the dictionary access pattern. The AgentEvent structure supports arbitrary metadata, so you could adapt the counting logic in adr_baseline.py to aggregate values from other keys such as "duration", "status", or custom identifiers relevant to your specific ADR implementation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →