open-telemetry / open-telemetry/opentelemetry-python-genai
langchain: an agent run is reported as invoke_workflow, with no invoke_agent span
@AgentGymLeader is already working on this.
Since Aug 14, 2026.
- Dominant language
- Python
- Stars
- 39
- Forks
- 64
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 175
Description
opentelemetry-instrumentation-genai-langchain 1.0b0 emits no invoke_agent span for an idiomatic LangChain agent run. The agent invocation is reported as invoke_workflow instead, so an agent application produces no agent telemetry at all.
Repro
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
agent = create_agent(
model=ChatOpenAI(model="gpt-4o-mini", max_tokens=100),
tools=[],
system_prompt="You are a helpful assistant.",
name="weather_assistant",
)
agent.invoke({"messages": [HumanMessage(content="hi")]})
with from langchain_core.messages import HumanMessage, run under
opentelemetry-instrument. (Passing the message as a plain
{"role": ..., "content": ...} dict instead raises inside the callback and
loses the span entirely — that is a separate bug, filed alongside this one.)
Expected: an invoke_agent weather_assistant span, per the GenAI semantic conventions.
Actual: invoke_workflow weather_assistant, with the chat span beneath it. The agent name resolves correctly — it is only the operation that is wrong.
The span does appear if the caller passes an OpenTelemetry-specific metadata key:
agent.invoke(
{"messages": [HumanMessage(content="hi")]},
config={"metadata": {"agent_name": "weather_assistant"}},
)
which emits invoke_agent weather_assistant as expected.
Why it happens
LangChain already tells the callback that this is an agent, and what it is called. Dumping every on_chain_start for the run above gives:
root=True serialized.name=None kwargs.name='weather_assistant'
metadata_keys=['lc_agent_name', 'ls_integration']
root=False serialized.name=None kwargs.name='model'
metadata_keys=[..., 'langgraph_node', 'lc_agent_name', ...]
So the root chain carries the agent name twice: as kwargs["name"] and as metadata["lc_agent_name"].
classify_chain_run in operation_mapping.py does not look at either:
_has_agent_signals(metadata)accepts onlymetadata["otel_agent_span"],metadata["agent_name"]andmetadata["agent_type"]. LangChain sets none of these, so the agent branch is never taken._looks_like_workflowis reached next.serializedis empty here, so it falls through to its finalreturn Trueand the run is classified as a workflow.
resolve_agent_name would already return "weather_assistant" from kwargs["name"], but it is only consulted for suppression and inside the agent branch that is never entered.
Requiring metadata["agent_name"] means the instrumentation reports agents only for applications modified to describe themselves to it, which defeats zero-code instrumentation.
Suggested fix
Treat metadata["lc_agent_name"] as an agent signal in _has_agent_signals. Note that nested LangGraph nodes carry lc_agent_name too, so the root needs distinguishing — parent_run_id is None, or the absence of langgraph_node, both separate it from the model node in the trace above.
Context
Found while adding LangChain conformance scenarios in
https://github.com/open-telemetry/semantic-conventions-conformance/pull/33.
The scenarios there deliberately stay idiomatic and record the missing agent
span as a gap.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.