Skip to content

langchain: an agent run is reported as invoke_workflow, with no invoke_agent span #387

Description

@lmolkova

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:

  1. _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.
  2. _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.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

Status
New issues

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions