Instrument AI Agents

Learn how to manually instrument your code to use Sentry's Agents module.

As a prerequisite to setting up AI Agents, you’ll need to first set up tracing. Once this is done, the Python SDK will automatically instrument AI agents created with the openai-agents library. If that doesn't fit your use case, you can use custom instrumentation described below.

For your AI agents data to show up in the Sentry AI Agents Insights, a couple of spans must be created and have well-defined names and data attributes. See below.

Describes AI agent invocation.

  • The spans op MUST be "gen_ai.invoke_agent".
  • The span name SHOULD be "invoke_agent {gen_ai.agent.name}".
  • The gen_ai.operation.name attribute MUST be "invoke_agent".
  • The gen_ai.agent.name attribute SHOULD be set to the agents name. (e.g. "Weather Agent")
  • All Common Span Attributes SHOULD be set (all required common attributes MUST be set).

Additional attributes on the span:

Data AttributeTypeRequirement LevelDescriptionExample
gen_ai.request.available_toolsstringoptionalList of objects describing the available tools. [0]"[{\"name\": \"random_number\", \"description\": \"...\"}, {\"name\": \"query_db\", \"description\": \"...\"}]"
gen_ai.request.frequency_penaltyfloatoptionalModel configuration parameter.0.5
gen_ai.request.max_tokensintoptionalModel configuration parameter.500
gen_ai.request.messagesstringoptionalList of objects describing the messages (prompts) sent to the LLM [0], [1]"[{\"role\": \"system\", \"content\": [{...}]}, {\"role\": \"system\", \"content\": [{...}]}]"
gen_ai.request.presence_penaltyfloatoptionalModel configuration parameter.0.5
gen_ai.request.temperaturefloatoptionalModel configuration parameter.0.1
gen_ai.request.top_pfloatoptionalModel configuration parameter.0.7
gen_ai.response.tool_callsstringoptionalThe tool calls in the model’s response. [0]"[{\"name\": \"random_number\", \"type\": \"function_call\", \"arguments\": \"...\"}]"
gen_ai.response.textstringoptionalThe text representation of the model’s responses. [0]"[\"The weather in Paris is rainy\", \"The weather in London is sunny\"]"
gen_ai.usage.input_tokens.cachedintoptionalThe number of cached tokens used in the AI input (prompt)50
gen_ai.usage.input_tokensintoptionalThe number of tokens used in the AI input (prompt).10
gen_ai.usage.output_tokens.reasoningintoptionalThe number of tokens used for reasoning.30
gen_ai.usage.output_tokensintoptionalThe number of tokens used in the AI response.100
gen_ai.usage.total_tokensintoptionalThe total number of tokens used to process the prompt. (input and output)190
  • [0]: Span attributes only allow primitive data types (like int, float, boolean, string). This means you need to use a stringified version of a list of dictionaries. Do NOT set [{"foo": "bar"}] but rather the string "[{\"foo\": \"bar\"}]".
  • [1]: Each message item uses the format {role:"", content:""}. The role can be "user", "assistant", or "system". The content can be either a string or a list of dictionaries.

Copied
import sentry_sdk

sentry_sdk.init(...)

# some example agent implementation for demonstration
my_agent = MyAgent(
    name="Weather Agent",
    model_provider="openai",
    model="o3-mini",
)

with sentry_sdk.start_span(
    op="gen_ai.invoke_agent",
    name=f"invoke_agent {my_agent.name}",
) as span:
    # set data about LLM and agent
    span.set_data("gen_ai.operation.name", "invoke_agent")
    span.set_data("gen_ai.system", my_agent.model_provider)
    span.set_data("gen_ai.request.model", my_agent.model)
    span.set_data("gen_ai.agent.name", my_agent.name)

    # run the agent
    result = my_agent.run()

    # set agent response
    # we assume result.output is a dictionary
    # type of `gen_ai.response.text` needs to be a string
    span.set_data("gen_ai.response.text", json.dumps([result.output]))

    # set token usage
    # we assume the result includes the tokens used
    span.set_data("gen_ai.usage.input_tokens", result.usage.input_tokens)
    span.set_data("gen_ai.usage.output.tokens", result.usage.output_tokens)

This span represents a request to an AI model or service that generates a response or requests a tool call based on the input prompt.

  • The span op MUST be "gen_ai.{gen_ai.operation.name}". (e.g. "gen_ai.chat")
  • The span name SHOULD be {gen_ai.operation.name} {gen_ai.request.model}". (e.g. "chat o3-mini")
  • All Common Span Attributes SHOULD be set (all required common attributes MUST be set).

Additional attributes on the span:

Data AttributeTypeRequirement LevelDescriptionExample
gen_ai.request.available_toolsstringoptionalList of objects describing the available tools. [0]"[{\"name\": \"random_number\", \"description\": \"...\"}, {\"name\": \"query_db\", \"description\": \"...\"}]"
gen_ai.request.frequency_penaltyfloatoptionalModel configuration parameter.0.5
gen_ai.request.max_tokensintoptionalModel configuration parameter.500
gen_ai.request.messagesstringoptionalList of objects describing the messages (prompts) sent to the LLM [0], [1]"[{\"role\": \"system\", \"content\": [{...}]}, {\"role\": \"system\", \"content\": [{...}]}]"
gen_ai.request.presence_penaltyfloatoptionalModel configuration parameter.0.5
gen_ai.request.temperaturefloatoptionalModel configuration parameter.0.1
gen_ai.request.top_pfloatoptionalModel configuration parameter.0.7
gen_ai.response.tool_callsstringoptionalThe tool calls in the model’s response. [0]"[{\"name\": \"random_number\", \"type\": \"function_call\", \"arguments\": \"...\"}]"
gen_ai.response.textstringoptionalThe text representation of the model’s responses. [0]"[\"The weather in Paris is rainy\", \"The weather in London is sunny\"]"
gen_ai.usage.input_tokens.cachedintoptionalThe number of cached tokens used in the AI input (prompt)50
gen_ai.usage.input_tokensintoptionalThe number of tokens used in the AI input (prompt).10
gen_ai.usage.output_tokens.reasoningintoptionalThe number of tokens used for reasoning.30
gen_ai.usage.output_tokensintoptionalThe number of tokens used in the AI response.100
gen_ai.usage.total_tokensintoptionalThe total number of tokens used to process the prompt. (input and output)190
  • [0]: Span attributes only allow primitive data types. This means you need to use a stringified version of a list of dictionaries. Do NOT set [{"foo": "bar"}] but rather the string "[{\"foo\": \"bar\"}]".
  • [1]: Each message item uses the format {role:"", content:""}. The role can be "user", "assistant", or "system". The content can be either a string or a list of dictionaries.

Copied
import sentry_sdk

sentry_sdk.init(...)

# some example implementation for demonstration
my_ai = MyAI(
    model_provider="openai",
    model="o3-mini",
    model_config={
        "temperature": 0.1,
        "presence_penalty": 0.5,
    }
)

with sentry_sdk.start_span(
    op="gen_ai.chat",
    name=f"chat {my_ai.model}",
) as span:
    # set data about LLM
    span.set_data("gen_ai.operation.name", "chat")
    span.set_data("gen_ai.system", my_ai.model_provider)
    span.set_data("gen_ai.request.model", my_ai.model)

    # set up messages for LLM
    max_tokens = 1024
    prompt = "Tell me a joke"
    messages = [
        {"role": "user", "content": prompt},
    ]

    # set chat request data
    span.set_data("gen_ai.request.message", json.dumbs(messages))
    span.set_data("gen_ai.request.max_tokens", max_tokens)
    span.set_data(
        "gen_ai.request.temperature",
        my_ai.model_config.get("temperature"),
    )
    span.set_data(
        "gen_ai.request.presence_penalty",
        my_ai.model_config.get("presence_penalty"),
    )

    # ask the LLM
    result = my_ai.messages.create(
        messages=messages,
        max_tokens=max_tokens,
    )

    # set response
    # we assume result.output is a dictionary
    # type of `gen_ai.response.text` needs to be a string
    span.set_data("gen_ai.response.text", json.dumps([result.output]))

    # set token usage
    # we assume the result includes the tokens used
    span.set_data("gen_ai.usage.input_tokens", result.usage.input_tokens)
    span.set_data("gen_ai.usage.output.tokens", result.usage.output_tokens)

Describes a tool execution.

  • The spans op MUST be "gen_ai.execute_tool".
  • The spans name SHOULD be "gen_ai.execute_tool {gen_ai.tool.name}". (e.g. "gen_ai.execute_tool query_database")
  • The gen_ai.tool.name attribute SHOULD be set to the name of the tool. (e.g. "query_database")
  • All Common Span Attributes SHOULD be set (all required common attributes MUST be set).

Additional attributes on the span:

Data AttributeTypeRequirement LevelDescriptionExample
gen_ai.tool.descriptionstringoptionalDescription of the tool executed."Tool returning a random number"
gen_ai.tool.inputstringoptionalInput that was given to the executed tool as string."{\"max\":10}"
gen_ai.tool.namestringoptionalName of the tool executed."random_number"
gen_ai.tool.outputstringoptionalThe output from the tool."7"
gen_ai.tool.typestringoptionalThe type of the tools."function"; "extension"; "datastore"

Copied
import sentry_sdk

sentry_sdk.init(...)

# some example implementation for demonstration
my_ai = MyAI(
    model_provider="openai",
    model="o3-mini",
)

with sentry_sdk.start_span(op="gen_ai.chat", ...):
    #  .. some code ..
    result = my_ai.messages.create(
        messages=messages,
        max_tokens=1024,
    )
    # .. some code ..

# we assume the llm tells us to call a tool in the result
if my_should_call_tool(result):
    # we parse the result to know which tool to call
    tool = my_get_tool_to_call(result)

    with sentry_sdk.start_span(
        op="gen_ai.execute_tool",
        name=f"gen_ai.execute_tool {tool.name}"
    ) as span:
        # set data about LLM and tool
        span.set_data("gen_ai.system", my_ai.model_provider)
        span.set_data("gen_ai.request.model", my_ai.model)
        span.set_data("gen_ai.tool.type", "function")
        span.set_data("gen_ai.tool.name", tool.name)
        span.set_data("gen_ai.tool.description", tool.description)

        # run tool
        tool_result = my_run_tool(tool)

        # set tool result
        span.set_data("gen_ai.tool.output", json.dumps(tool_result))

A span that describes the handoff from one agent to another agent.

  • The spans op MUST be "gen_ai.handoff".
  • The spans name SHOULD be "handoff from {from_agent} to {to_agent}".
  • All Common Span Attributes SHOULD be set.

Copied
import sentry_sdk

sentry_sdk.init(...)

# some example agent implementation for demonstration
my_agent = MyAgent(
    name="Weather Agent",
    model_provider="openai",
    model="o3-mini",
)

with sentry_sdk.start_span(op="gen_ai.invoke_agent", ...):
    #  .. some code ..
    result = my_agent.run()
    #  .. some code ..


# we assume the llm tells us to handoff to another agent
if my_should_handoff(result):
    # we parse the result to know which agent to handoff to
    other_agent = my_get_agent(result)

    with sentry_sdk.start_span(
        op="gen_ai.handoff",
        name=f"handoff from {my_agent.name} to {other_agent.name}",
    ):
        # the handoff span just marks the handoff
        pass

    with sentry_sdk.start_span(op="gen_ai.invoke_agent", ...):
        #  .. some code ..
        other_agent.run()
        #  .. some code ..

Some attributes are common to all AI Agents spans:

Data AttributeTypeRequirement LevelDescriptionExample
gen_ai.systemstringrequiredThe Generative AI product as identified by the client or server instrumentation. [0]"openai"
gen_ai.request.modelstringrequiredThe name of the AI model a request is being made to."o3-mini"
gen_ai.operation.namestringoptionalThe name of the operation being performed. [1]"chat"
gen_ai.agent.namestringoptionalThe name of the agent this span belongs to."Weather Agent"

[0] Well defined values for data attribute gen_ai.system:

ValueDescription
"anthropic"Anthropic
"aws.bedrock"AWS Bedrock
"az.ai.inference"Azure AI Inference
"az.ai.openai"Azure OpenAI
"cohere"Cohere
"deepseek"DeepSeek
"gcp.gemini"Gemini
"gcp.gen_ai"Any Google generative AI endpoint
"gcp.vertex_ai"Vertex AI
"groq"Groq
"ibm.watsonx.ai"IBM Watsonx AI
"mistral_ai"Mistral AI
"openai"OpenAI
"perplexity"Perplexity
"xai"xAI

[1] Well defined values for data attribute gen_ai.operation.name:

ValueDescription
"chat"Chat completion operation such as OpenAI Chat API
"create_agent"Create GenAI agent
"embeddings"Embeddings operation such as OpenAI Create embeddings API
"execute_tool"Execute a tool
"generate_content"Multimodal content generation operation such as Gemini Generate Content
"invoke_agent"Invoke GenAI agent
Was this helpful?
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").