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 only metadata["otel_agent_span"], metadata["agent_name"] and metadata["agent_type"]. LangChain sets none of these, so the agent branch is never taken.
_looks_like_workflow is reached next. serialized is empty here, so it falls through to its final return True and 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
open-telemetry/semantic-conventions-conformance#33.
The scenarios there deliberately stay idiomatic and record the missing agent
span as a gap.
opentelemetry-instrumentation-genai-langchain1.0b0 emits noinvoke_agentspan for an idiomatic LangChain agent run. The agent invocation is reported asinvoke_workflowinstead, so an agent application produces no agent telemetry at all.Repro
with
from langchain_core.messages import HumanMessage, run underopentelemetry-instrument. (Passing the message as a plain{"role": ..., "content": ...}dict instead raises inside the callback andloses the span entirely — that is a separate bug, filed alongside this one.)
Expected: an
invoke_agent weather_assistantspan, per the GenAI semantic conventions.Actual:
invoke_workflow weather_assistant, with thechatspan 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:
which emits
invoke_agent weather_assistantas expected.Why it happens
LangChain already tells the callback that this is an agent, and what it is called. Dumping every
on_chain_startfor the run above gives:So the root chain carries the agent name twice: as
kwargs["name"]and asmetadata["lc_agent_name"].classify_chain_runinoperation_mapping.pydoes 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_namewould already return"weather_assistant"fromkwargs["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 carrylc_agent_nametoo, so the root needs distinguishing —parent_run_id is None, or the absence oflanggraph_node, both separate it from themodelnode in the trace above.Context
Found while adding LangChain conformance scenarios in
open-telemetry/semantic-conventions-conformance#33.
The scenarios there deliberately stay idiomatic and record the missing agent
span as a gap.