| layout | default |
|---|---|
| title | Chapter 2: Agent Loop and Model-Driven Architecture |
| nav_order | 2 |
| parent | Strands Agents Tutorial |
Welcome to Chapter 2: Agent Loop and Model-Driven Architecture. In this part of Strands Agents Tutorial: Model-Driven Agent Systems with Native MCP Support, you will build an intuitive mental model first, then move into concrete implementation details and practical production tradeoffs.
This chapter explains why Strands is described as model-driven and how that affects design choices.
- understand the core agent loop model
- map model reasoning to tool invocation behavior
- identify extension points for custom logic
- preserve simplicity while adding capability
- the agent loop is intentionally lightweight
- models drive decision-making while tools execute capabilities
- APIs emphasize composable primitives instead of heavy framework layers
You now have the foundation to design Strands agents with clearer tradeoff awareness.
Next: Chapter 3: Tools and MCP Integration
The get_tracer function in src/strands/telemetry/tracer.py handles a key part of this chapter's functionality:
self.service_name = __name__
self.tracer_provider: trace_api.TracerProvider | None = None
self.tracer_provider = trace_api.get_tracer_provider()
self.tracer = self.tracer_provider.get_tracer(self.service_name)
ThreadingInstrumentor().instrument()
# Read OTEL_SEMCONV_STABILITY_OPT_IN environment variable
opt_in_values = self._parse_semconv_opt_in()
## To-do: should not set below attributes directly, use env var instead
self.use_latest_genai_conventions = "gen_ai_latest_experimental" in opt_in_values
self._include_tool_definitions = "gen_ai_tool_definitions" in opt_in_values
def _parse_semconv_opt_in(self) -> set[str]:
"""Parse the OTEL_SEMCONV_STABILITY_OPT_IN environment variable.
Returns:
A set of opt-in values from the environment variable.
"""
opt_in_env = os.getenv("OTEL_SEMCONV_STABILITY_OPT_IN", "")
return {value.strip() for value in opt_in_env.split(",")}
@property
def is_langfuse(self) -> bool:
"""Check if Langfuse is configured as the OTLP endpoint.
Returns:
True if Langfuse is the OTLP endpoint, False otherwise.
"""
return any(
"langfuse" in os.getenv(var, "")
for var in ("OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "LANGFUSE_BASE_URL")
)This function is important because it defines how Strands Agents Tutorial: Model-Driven Agent Systems with Native MCP Support implements the patterns covered in this chapter.
The serialize function in src/strands/telemetry/tracer.py handles a key part of this chapter's functionality:
"gen_ai.client.inference.operation.details",
{
"gen_ai.output.messages": serialize(
[
{
"role": message["role"],
"parts": self._map_content_blocks_to_otel_parts(message["content"]),
"finish_reason": str(stop_reason),
}
]
),
},
to_span_attributes=self.is_langfuse,
)
else:
self._add_event(
span,
"gen_ai.choice",
event_attributes={"finish_reason": str(stop_reason), "message": serialize(message["content"])},
)
span.set_attributes(attributes)
def start_tool_call_span(
self,
tool: ToolUse,
parent_span: Span | None = None,
custom_trace_attributes: Mapping[str, AttributeValue] | None = None,
**kwargs: Any,
) -> Span:
"""Start a new span for a tool call.This function is important because it defines how Strands Agents Tutorial: Model-Driven Agent Systems with Native MCP Support implements the patterns covered in this chapter.
The for interface in src/strands/telemetry/tracer.py handles a key part of this chapter's functionality:
# Handle datetime objects directly
if isinstance(value, (datetime, date)):
return value.isoformat()
# Handle dictionaries
elif isinstance(value, dict):
return {k: self._process_value(v) for k, v in value.items()}
# Handle lists
elif isinstance(value, list):
return [self._process_value(item) for item in value]
# Handle all other values
else:
try:
# Test if the value is JSON serializable
json.dumps(value)
return value
except (TypeError, OverflowError, ValueError):
return "<replaced>"
class Tracer:
"""Handles OpenTelemetry tracing.
This class provides a simple interface for creating and managing traces,
with support for sending to OTLP endpoints.
When the OTEL_EXPORTER_OTLP_ENDPOINT environment variable is set, traces
are sent to the OTLP endpoint.
Both attributes are controlled by including "gen_ai_latest_experimental" or "gen_ai_tool_definitions",This interface is important because it defines how Strands Agents Tutorial: Model-Driven Agent Systems with Native MCP Support implements the patterns covered in this chapter.
The of interface in src/strands/telemetry/tracer.py handles a key part of this chapter's functionality:
Returns:
JSON string representation of the object
"""
# Process the object to handle non-serializable values
processed_obj = self._process_value(obj)
# Use the parent class to encode the processed object
return super().encode(processed_obj)
def _process_value(self, value: Any) -> Any:
"""Process any value, handling containers recursively.
Args:
value: The value to process
Returns:
Processed value with unserializable parts replaced
"""
# Handle datetime objects directly
if isinstance(value, (datetime, date)):
return value.isoformat()
# Handle dictionaries
elif isinstance(value, dict):
return {k: self._process_value(v) for k, v in value.items()}
# Handle lists
elif isinstance(value, list):
return [self._process_value(item) for item in value]
# Handle all other values
else:This interface is important because it defines how Strands Agents Tutorial: Model-Driven Agent Systems with Native MCP Support implements the patterns covered in this chapter.
flowchart TD
A[get_tracer]
B[serialize]
C[for]
D[of]
E[methods]
A --> B
B --> C
C --> D
D --> E