Skip to content

Add a Strands Agents travel planner example and multi-turn sessions - #471

Open
ps48 wants to merge 5 commits into
opensearch-project:mainfrom
ps48:feat/strands-travel-planner
Open

ps48 wants to merge 5 commits into
opensearch-project:mainfrom
ps48:feat/strands-travel-planner

Conversation

@ps48

@ps48 ps48 commented Oct 1, 2026

Copy link
Copy Markdown
Member

Description

Adds a Strands Agents version of the multi-agent travel planner (examples/strands/travel-planner), plus multi-turn sessions in the existing plain-agents planner. Both run side by side, so the stack's APM topology, Services, Agent Traces and Sessions views can be demoed on a real agent framework.

Strands travel planner (strands-travel-planner, strands-weather-agent, strands-events-agent; ports 8010 to 8012)

  • Agents: planner, weather and events agents are Strands Agents with @tool functions behind FastAPI. They use the same MCP server, fault panel and API shape (POST /plan) as the plain-agents planner.
  • Tracing: FastAPI and httpx instrumentation make each turn one trace across services and give the service map its edges. Strands emits the GenAI spans (invoke_agent, chat, execute_tool, event loop) with the latest GenAI conventions, as span attributes.
  • Sessions: requests sharing a conversation_id continue one conversation (the planner keeps its history). trace_attributes put gen_ai.conversation.id, session.id and user.id on every span Strands emits, including execute_tool spans, which OTel GenAI semconv allows since semantic-conventions-genai#518. No id is invented when the caller sends none.
  • Root span: the HTTP root span is described as the agent invocation (operation, agent, session, input/output messages), because agent views list traces by their root.
  • Models: Bedrock when the fault panel's real-LLM toggle is on and AWS credentials are set. Otherwise, or when a Bedrock call fails, a scripted fallback model drives the same Strands loop, so chat and execute_tool spans still appear without credentials (gen_ai.request.model = scripted-fallback).
  • Faults: the same shape as the plain-agents planner. A failing tool fails its span, the sub-agent response, the planner's tool span and the request root.
  • Canary: example-strands-canary runs the existing canary (normal, shallow, deep and session shapes) against these services.

Plain-agents planner

  • Multi-turn sessions: PlanRequest takes conversation_id and message, and the id is forwarded to the weather and events agents. The canary adds a session shape (SESSION_PROBABILITY, default 0.3) with 2 to 4 turns. The weather agent no longer invents a conv_ id.
  • Tool spans: execute_tool spans carry the conversation id, so filtering sessions by tool attributes (for example failing tools) finds them.

Issues Resolved

Testing

Run locally against the full stack (OpenSearch, Data Prepper, OpenSearch Dashboards 3.9):

  • Directly against OpenSearch and the APIs:
    • All services healthy. A Strands turn is one trace across strands-travel-planner, strands-weather-agent, strands-events-agent and mcp-server, with one root (POST /plan).
    • Every Strands execute_tool span in a session turn carries gen_ai.conversation.id. chat spans have gen_ai.input.messages and token usage.
    • With fault.weather.type = error: the response is partial; error spans (weather tool, weather request, planner tool, planner root) carry the session id.
    • The service-map index has edges planner -> weather, planner -> events, and each service -> mcp-server.
    • Plain-agents planner: a session turn's execute_tool spans carry the conversation id.
  • In the browser:
    • Agent Traces lists Strands traces with input/output.
    • Sessions shows canary sessions, and | where `status.code` = 2 finds the session with the failing tool (with its error indicator).
    • APM Services lists the three strands-* services.

Follow-up (not in this PR): publish the Strands image in publish-images.yml for the Helm and CDK demo paths.

Check List

  • Commits are signed per the DCO using --signoff

ps48 added 3 commits October 1, 2026 02:20
- Orchestrator and events agent accept a caller-provided conversation_id
  and record it as gen_ai.conversation.id
- Weather agent no longer fabricates a conversation id (semconv SHOULD NOT)
- Canary sends 2-4 turn sessions (SESSION_PROBABILITY, default 0.3)

Signed-off-by: Shenoy Pratik Gurudatt <4348487+ps48@users.noreply.github.com>
The planner's MCP tool calls, the weather agent's tools and the events agent's
MCP call now carry the turn's conversation id on their execute_tool spans, as
OTel GenAI semconv allows since semantic-conventions-genai#518. Filtering
sessions by tool attributes (for example failing tools) then finds them.
Nothing is set when the caller sends no conversation id.

Signed-off-by: Shenoy Pratik Gurudatt <4348487+ps48@users.noreply.github.com>
examples/strands/travel-planner runs the travel planner on the Strands Agents
SDK next to the plain-agents version (separate services and ports, same MCP
server, fault panel and canary shapes), for demos of the APM topology,
Services, Agent Traces and Sessions views on a real agent framework.

- Planner, weather and events agents are Strands Agents with @tool functions
  behind FastAPI; FastAPI and httpx instrumentation make each turn one trace
  across services and give the service map its edges.
- Sessions: requests sharing a conversation_id continue one conversation.
  trace_attributes put gen_ai.conversation.id (and session.id, user.id) on
  every span Strands emits, tool spans included. No id is invented when the
  caller has none.
- The HTTP root span is described as the agent invocation (operation, agent,
  session, input/output messages), since agent views list traces by root.
- Bedrock when the fault panel's real-LLM toggle is on and AWS credentials are
  set; otherwise a scripted fallback model drives the same Strands loop, so
  chat and execute_tool spans still appear (model id scripted-fallback).
- Faults use the plain-agents shape; a failing tool fails its span, the
  sub-agent's response, the planner's tool span and the request root.
- example-strands-canary runs the existing canary against these services.

Signed-off-by: Shenoy Pratik Gurudatt <4348487+ps48@users.noreply.github.com>
ps48 added 2 commits October 1, 2026 15:07
…lation

Only the plain-agents weather agent exported logs, so traces from the
travel planners had few or no correlated logs. Now every agent does:

- Plain agents (travel-planner orchestrator, events-agent): Python
  logging over OTLP gRPC, as the weather agent does. Each step logs
  inside its span: plan requested, sub-agent fan-out, MCP tool calls,
  failed sub-tasks (warning), tool errors, completion.
- Strands agents (planner, weather, events): the same over OTLP gRPC
  (OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; spans and metrics stay on HTTP).
  Shared helpers log MCP calls and errors, injected faults, the model
  used and the answer; the planner logs sub-agent calls and failures.
  The SDK's own warnings are not exported, so export retries cannot
  feed back into the log stream.

Every log carries the active trace and span ids, so Agent Traces'
Related logs and Explore traces' logs tab list them under the trace.

Tested: one plain and two Strands plans (one with a weather error
fault); logs-otel-v1-* holds each step once per service under the
trace id, with the fault as WARN/ERROR logs.

Signed-off-by: Shenoy Pratik Gurudatt <4348487+ps48@users.noreply.github.com>
FastAPI 0.142 (picked up by unpinned rebuilds) adds its own OpenTelemetry
spans, including a fastapi.endpoint span around every handler. The
plain agents' enrich() and get_current_span() then reached that span
instead of the request span, so the trace root lost gen_ai.operation.name,
the conversation id and the messages. Agent views read the root, so
sessions showed no messages and traces lost their agent attributes.

The example apps instrument themselves (ASGI middleware, FastAPI and
httpx instrumentation), so they pass FastAPI(telemetry={...}) with
tracing, metrics, logs and operation spans off. Older FastAPI versions
put unknown arguments in **extra and ignore them.

Tested: rebuilt the plain and Strands agents on FastAPI 0.142; the
POST /plan root again carries invoke_agent, the conversation id and
input messages, with no fastapi.* spans, and logs still correlate.

Signed-off-by: Shenoy Pratik Gurudatt <4348487+ps48@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant