Skip to content

feat: add agent span support in langchain instrumentation#3788

Closed
yiyuan-he wants to merge 23 commits into
open-telemetry:mainfrom
yiyuan-he:langchain-agent-spans
Closed

feat: add agent span support in langchain instrumentation#3788
yiyuan-he wants to merge 23 commits into
open-telemetry:mainfrom
yiyuan-he:langchain-agent-spans

Conversation

@yiyuan-he
Copy link
Copy Markdown

@yiyuan-he yiyuan-he commented Sep 25, 2025

Description

This PR adds agent span support to the LangChain instrumentation following the OpenTelemetry Semantic Conventions for GenAI agent spans. It enables tracing of LangChain agent invocations, chain executions, and agent actions/decisions.

  • invoke_agent operation for agent invocations
  • Proper span naming: invoke_agent {agent_name} and chain {chain_name}
  • Agent-specific attributes: gen_ai.agent.name, gen_ai.operation.name

A follow-up PR will add tool execution spans (execute_tool operation).

Sidenote: Previous PR from our team can be closed.

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration

  • Added unit tests for agent span creation and lifecycle
  • Added unit tests for chain span creation with proper parent-child
    relationships
  • Added unit tests for agent action and finish callbacks
  • All existing tests continue to pass
  • Tests verify proper span attributes according to semantic conventions

Does This PR Require a Core Repo Change?

  • Yes. - Link to PR:
  • No.

Checklist:

See contributing.md for styleguide, changelog guidelines, and more.

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added
  • [] Documentation has been updated (README update may be needed in follow-up)

@yiyuan-he yiyuan-he requested a review from a team as a code owner September 25, 2025 16:05
)
span.set_attribute(
GenAI.GEN_AI_OPERATION_NAME,
"invoke_agent",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible that the operation name might be changed in the future, if so, declaring it a constant for it might be better.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point - I'm not sure if this semantic convention will change but agree that this should be moved to a constant since it's currently hardcoded in a few places.

GenAI.GEN_AI_OPERATION_NAME,
"invoke_agent",
)
if agent_name:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If agent_name is None, in that case, should a default value be passed?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah makes sense, i'll default it to "unknown" for now. Open to suggestions on the naming though.

span = spans[0]
assert span.name == "chain TestAgent"
assert span.kind == SpanKind.INTERNAL
assert span.attributes.get(GenAI.GEN_AI_AGENT_NAME) == "TestAgent"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the above comment you mentioned that chain spans are internal operations and will not have an operation name but there you are adding assert statements for it, did I miss something?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your review! Let me try to clarify.

Regular chains don't have gen_ai.operation.name since they're just internal operations. However, Agent chains are different. In LangChain's architecture, agents are implemented as chains so we:

  1. create them using create_agent_span() (which creates an internal span)
  2. detect if it's actually an agent by checking for agent_name in metadata
  3. if it is an agent, we add the agent-specific attributes:
  • gen_ai.agent.name
  • gen_ai.operation.name = "invoke_agent"

I'll update the docstrings to make this distinction clearer.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome. Thanks for the clarification.

__all__ = ["_SpanManager", "_OPERATION_INVOKE_AGENT"]

# Operation name constants
_OPERATION_INVOKE_AGENT = "invoke_agent"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if span:
tool = getattr(action, "tool", None) # type: ignore[arg-type]
if tool:
span.set_attribute("langchain.agent.action.tool", tool)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tool_input = getattr(action, "tool_input", None) # type: ignore[arg-type]
if tool_input:
span.set_attribute(
"langchain.agent.action.tool_input", str(tool_input)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be gen_ai.tool.call.arguments ?

return_values = getattr(finish, "return_values", None) # type: ignore[arg-type]
if return_values and "output" in return_values:
span.set_attribute(
"langchain.agent.finish.output",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be either [gen_ai.tool.call.result](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/registry/attributes/gen-ai.md) or gen_ai.output.messages ?

if agent_name:
span.set_attribute(GenAI.GEN_AI_AGENT_NAME, agent_name)

return span
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invoke agent span must have gen_ai.provider.name attribute with a name of genai system used under (e.g. openai, vertex_ai, etc). Is it possible to populate them here?

there are more recommended/opt-in attributes documented in https://github.com/open-telemetry/semantic-conventions/blob/main/docs/gen-ai/gen-ai-agent-spans.md#invoke-agent-span

Copy link
Copy Markdown
Member

@lmolkova lmolkova left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments around following semconv more closely and reusing constants from the semconv lib.

@lmolkova
Copy link
Copy Markdown
Member

lmolkova commented Dec 4, 2025

@zhirafovod @wrisa could you please review?

if span:
tool = getattr(action, "tool", None) # type: ignore[arg-type]
if tool:
span.set_attribute("langchain.agent.action.tool", tool)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess your intention is to use gen_ai.tool.name, gen_ai.tool.call.result and gen_ai.tool.call.arguments which are on execute_tool. Please use these gen-ai attributes. Also another PR updating invoke_agent with the above attributes would also be required.

@github-actions
Copy link
Copy Markdown

This PR has been automatically marked as stale because it has not had any activity for 14 days. It will be closed if no further activity occurs within 14 days of this comment.
If you're still working on this, please add a comment or push new commits.

@github-actions github-actions Bot added the Stale label Mar 11, 2026
@github-actions
Copy link
Copy Markdown

This PR has been closed due to inactivity. Please reopen if you would like to continue working on it.

@github-actions github-actions Bot closed this Mar 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants