How ADK's config loader turns a declarative YAML file into a fully-built agent — and why treating an agent as data changes who gets to edit it.
Defining an agent declaratively in YAML and loading it via from_config — the loader reads, resolves, and validates the config into a built agent, so config-as-data works without writing code.
How caching a large, stable prompt prefix cuts latency and cost — and the ADK config that decides when it pays off.
Context caching cuts latency and cost by caching large, stable context — system prompt, reference docs, tool definitions — so repeated calls don't re-send and re-process the same tokens.
The reference capstone — each term in the series, defined in plain English and grouped by what it does.
The capstone of the series: every LangGraph concept defined in one place — the graph model, state and reducers, persistence, human-in-the-loop, agents and tools, parallelism, streaming, and the surrounding ecosystem.
Post 23 of 26 in "Google ADK, Concept by Concept" — how a planner turns one-shot answers into inspectable plan-then-act reasoning.
Structuring an agent's reasoning: planners that make the model plan-then-act (ReAct-style), the built-in thinking feature, and how a planner improves multi-step tool use over naive prompting.
The higher-level building blocks LangGraph stacks on top of the graph engine — pausing for a human, running an agent loop, calling tools, and fanning out dynamically.
The building blocks on top of the core graph: interrupt() to pause for human input, create_react_agent and ToolNode for tool-using agents, and the Send API for dynamic parallel fan-out with a reducer fan-in.
How ADK closes the write-code, run-it, read-the-output loop — and why "unsafe" is a warning, not a typo.
Letting an agent write and run code: built-in and container-based code executors, safe sandboxed execution, how results flow back into the conversation, and the security tradeoffs.
How a checkpointer turns a graph run into something you can stop, reload, and replay from any point in its history.
A checkpointer saves state at every superstep boundary, so a run can pause, resume on a thread_id, and even fork from an earlier checkpoint (time-travel). This is the foundation human-in-the-loop is built on.
How ADK skills bundle instructions, tools, and resources into folders an agent can browse and load on demand.
Skills package reusable capabilities — instructions, tools, resources, including file-based skills — so they can be discovered and attached to agents, promoting reuse across projects.
Watching a LangGraph run happen — the three things `.stream()` can show you, and why they fall out of the superstep model for free.
stream() exposes a run in three modes: values (full state after each node), updates (what each node changed), and debug (the raw event stream). Streaming falls out naturally from the superstep model.
cross-cutting concerns registered once on the Runner instead of copied onto every agent
Plugins are cross-cutting hooks that apply globally across every agent, tool, and runner — logging, policy, metrics, caching — as opposed to per-agent callbacks. When a plugin beats a callback.
How a single `Command` object folds a state update and a routing decision together — and the tiny lowering that makes `goto` just another guarded edge.
Command lets a node return a state update and a goto in one object, moving the routing decision inside the node. It is the cleanest way to express supervisor handoffs and dynamic control flow.
How ADK's model abstraction lets you swap Gemini for Claude, GPT, or Ollama without touching a line of agent code
ADK is model-agnostic: use Gemini natively or plug in Claude, GPT, or Ollama via LiteLLM and a model registry, swapping the model without changing agent code.
The single most important pattern in LangGraph — a branch plus a back-edge, and the `recursion_limit` that keeps it from running forever.
Branching plus a back-edge is a cycle, and that cycle IS the agent loop: model proposes tool calls, tools run, control returns to the model, repeat until done. Plus recursion_limit, the guardrail that stops a runaway loop.
Post 18 of 26 in "Google ADK, Concept by Concept" — retrieval tools, grounding metadata, rendering citations, and the retrieve→augment→generate loop.
Grounding answers in real data: retrieval tools, grounding metadata returned with responses, rendering citations from that metadata, and the retrieve-augment-generate RAG pattern in ADK.
How one router function plus a `path_map` dict lowers to exactly one edge firing per step.
A conditional edge is a router function plus a path map: the router reads state and returns a key, the path map turns that key into the next node. This is how branching (and, next post, loops) are expressed.
Two open protocols that let an agent reach outside its own process — one to borrow tools, one to call other agents as peers.
Two interoperability protocols: MCP lets an agent consume tools from external servers, and A2A — HTTP for agents — lets one agent discover and call another remote agent as a peer over HTTP.
How the four smallest pieces of the LangGraph API turn a bag of nodes into a program you can run.
Edges, START, END, compile() and invoke() are the four smallest pieces that turn a bag of nodes into a runnable program. Here is the full lifecycle of a tiny two-node graph.
Stack the guardrails — callbacks, model filters, restricted tools, and clean-room sandboxing — so that if one layer misses, the next one catches
Layered defense-in-depth for agents: input/output guardrails via callbacks, Gemini safety settings, restricting tools, and sandboxing untrusted actions.
A LangGraph node is just a function — it reads the whole state and returns only the channels it changed.
A node is just a function: it receives the whole current state and returns only the channels it changed. Understand the partial-update contract and the immutable-snapshot guarantee that makes supersteps safe.
How OpenTelemetry traces, structured logs, and token metrics turn an agent's event stream into something you can debug in production.
Seeing inside a running agent: OpenTelemetry tracing with spans for agent, model, and tool steps, structured logging, and exporting traces to debug latency and tool-call trajectories.
The one idea that makes everything else in LangGraph click: nodes don't pass messages, they update a shared state — and reducers decide how.
State is a typed dict of channels; each channel has an optional reducer. No reducer overwrites; a reducer (like add_messages or operator.add) combines. This is the single idea the rest of LangGraph is built on.
How `adk deploy` builds, pushes, and ships an agent in a single step — and the ack-after-invocation rule that keeps event-driven agents reliable.
Deploying an agent: adk deploy with its cloud_run and agent_engine subcommands, containerizing the app, and reliability rules like ack-after-invocation so failures are redelivered, not dropped.
The foundational mental model — why "the graph" is a Pregel program, and how shared state differs from message passing.
LangGraph is shared-state, not message-passing, and both models descend from Google's Pregel/BSP: work advances in supersteps that end at a synchronization barrier. Get this mental model first and the whole API stops being magic.
How ADK turns "did the agent behave correctly?" into a number you can gate a merge on.
Measuring agent quality: eval sets, scoring both the trajectory (right tools, right order) and the final response, criteria configs, and the adk eval CLI — a Python-first workflow today.
token streaming, the accumulate-and-reconcile consumer pattern, and full-duplex live streaming for voice
Consuming output as it is produced: partial events and token streaming, the accumulate-and-reconcile consumer pattern, and bidi/live streaming for voice and interactive UIs.
How an agent actually runs — a Runner drives an invocation and hands you back a stream of events, not a single answer.
How ADK runs an agent: the Runner drives an invocation that yields a stream of Event objects — content, tool calls, state deltas, control signals. The event loop explains streaming, callbacks, and state.
before/after the agent, model, and tool steps — and the single short-circuit rule that turns them into guardrails
Callbacks are lifecycle hooks around the agent, model, and tool steps — before/after each — used for guardrails (short-circuit by returning a response), logging, and mutating requests and responses.
ReadonlyContext, CallbackContext, ToolContext, InvocationContext — and why the read-only vs mutable distinction is a feature, not a limitation.
The context objects ADK passes into tools and callbacks — InvocationContext, ToolContext, CallbackContext, ReadonlyContext — what each exposes and why the read-only vs mutable split matters.
Turning agents into a service you can run and expose, then a full DocQA app that ties the whole series together.
Host MAF agents with DevUI, A2A, MCP, and AG-UI, then build DocQA — a grounded, cited multi-agent app that ties the whole Python series together.
Session state is for small text and JSON. When your agent produces a PNG, a PDF, or a WAV, it belongs in the artifact store — binary-native, versioned, and out of the session record.
Artifacts are binary/file data agents produce or consume: ArtifactService saves and versions named artifacts, loaded and saved via context, keeping large blobs out of session state.
Durable workflows in Python: checkpoint and resume, pause for a human with request_info, and package a workflow as an agent.
Durable MAF workflows in Python: checkpoint and resume every superstep, suspend on request_info for a human decision, and package a workflow as an agent.
State remembers things inside one chat; Memory is the searchable archive that lets an agent recall what you told it weeks ago.
Memory is long-term recall across sessions, distinct from per-session state: MemoryService stores and retrieves prior context, surfaced via a recall tool so an agent remembers a user over time.
Five prebuilt multi-agent shapes — Sequential, Concurrent, Group Chat, Handoff, Magentic — and when each beats hand-wiring a graph.
Sequential, Concurrent, Group Chat, Handoff, Magentic — the five prebuilt MAF orchestrations in Python and when each beats hand-wiring a graph.
A `Session` is the conversation; `state` is the key-value bag agents and tools read and write — and the prefix on a key decides how long it lives.
A Session holds a conversation; state is a scoped key-value store (session/user/app/temp) read and written by tools, injected into instructions via {state} templating, and persisted by SessionService.
Agents as graph nodes: switch-case routing, fan-out/fan-in, and mixing plain functions with agent steps in one workflow.
Agents are just workflow executors: switch-case routing, fan-out/fan-in concurrency, and mixing plain function nodes with agent nodes in one graph.
How a plain function becomes a callable tool, how ToolContext reaches session state, and how long-running tools pause a run for a human.
Tools give agents capabilities: a plain function becomes a tool with its signature as the schema, plus ToolContext, built-in tools, and long-running/human-in-the-loop tools across Python and Go.
The graph model underneath every multi-agent app: executors as nodes, edges as data flow, and typed events streaming out as it runs.
The MAF workflow model in Python: executors as nodes, edges as data flow, switch-case routing, and typed streaming events - learned model-free.
How one agent routes work to specialists — and why the description field is the most important string you write.
Agent hierarchies and LLM-driven delegation: sub_agents, how the description field drives auto-transfer, and coordinator/dispatcher patterns — contrasted with deterministic workflow agents.
Turn agent runs into OpenTelemetry spans, block prompt injection with information-flow control, and swap model providers behind one Agent API.
Turn MAF agent runs into OpenTelemetry spans, block prompt injection with information-flow control, and swap model providers behind one Agent API.
When you want fixed control flow, don't ask the model — wire it yourself.
Sequential, Parallel, and Loop agents compose sub-agents in fixed patterns — deterministic orchestration where you, not the model, decide control flow, with state flowing between steps.
Wrapping an agent run with async seams that log, time, guard, and short-circuit — without touching the agent's logic.
Wrapping an MAF agent run in Python with async middleware seams — timing, logging, and a guardrail that short-circuits a tool call before it runs.
description, instruction, generation params, and structured output — the dials on almost every agent you'll build
The four knobs on almost every ADK agent: description (for delegation), instruction with {state} templating, generation params, and structured output — Python Pydantic model vs Go genai.Schema.
Typed results from `response_format`, consuming a stream event by event, and sending an image alongside text.
Three dials on one run() call: typed results via response_format, consuming a stream event by event, and sending an image alongside text.
An `LlmAgent`, a `Runner`, a `Session`, and a CLI that runs it all — the four pieces the other 25 concepts sit on top of.
The smallest ADK agent and the machinery around it: an LlmAgent, the Runner, a Session, and the adk CLI (adk web / adk run) that runs your agent with a dev UI or REPL — no server code.
How a stateless agent remembers: sessions carry one conversation, context providers carry knowledge across all of them.
MAF agents are stateless. Sessions carry one conversation; context providers carry memory across all of them. Here is the mental model in real code.
Turn a plain Python function into something the model can call, and watch the tool-call loop close itself.
Turn a plain Python function into a tool the model can call. The @tool decorator, the tool-call loop, multiple tools, and what the model actually sees.
The minimal loop: a Foundry chat client, an Agent with instructions, run non-streaming and streaming — and what actually comes back.
The minimal MAF loop in Python: a FoundryChatClient, an Agent whose instructions are its whole personality, run non-streaming and streaming.
Why I learned the whole framework by writing one runnable lesson per concept, against Azure AI Foundry, instead of reading the docs top to bottom.
I learned the whole Microsoft Agent Framework in Python by building one runnable lesson per concept against Azure AI Foundry. Here is the 12-track map.
Single-turn evals check one decision. Multi-turn evals check the whole trajectory. A Python harness with three evaluators, an offline test suite, and the judge prompt that actually works.
AgentSession is short-term memory. MemoryContextProvider + MemoryFileStore is long-term memory. Mem0 is long-term memory for serious workloads. The boundary that matters and how to implement each.
The Microsoft Agent Framework deliberately does not ship an agent registry. Here is why that is the right call, and what to build as a project-local convention when you need one.
Exposing an agent to any HTTP front-end over AG-UI, an SSE protocol, with one FastAPI helper.
AG-UI exposes an agent over HTTP + SSE: one add_agent_framework_fastapi_endpoint call registers a POST route that streams RUN_STARTED through RUN_FINISHED events.
Rendering the OpenTelemetry spans an agent already emits as a timeline with serve(tracing_enabled=True).
DevUI tracing renders the OTel GenAI spans the framework already emits: pass tracing_enabled=True to serve() to see LLM and tool calls as a timeline.
Sequential, Concurrent, Handoff, and Custom WorkflowBuilder. Four shapes the Microsoft Agent Framework ships out of the box, when to pick each, and the gotchas that cost me a day.
Serving a whole directory of agents at once by exporting a module-level agent variable.
DevUI directory discovery serves a folder of agents at once: each entity dir exports a module-level agent variable, then devui ./entities finds them all.
Microsoft published a 12-chapter reference architecture for multi-agent systems and a separate framework — the Microsoft Agent Framework — to build them. Here is what the 102 Python files actually contain and how they map to the chapters.
Packaging an agent as a container on Foundry Agent Service with ResponsesHostServer.
A Foundry hosted agent runs as a managed container: wrap the agent in ResponsesHostServer to serve POST /responses, and set store=False so the host owns history.
Speaking the OpenAI Chat Completions and Responses wire protocols on both sides of an agent.
Agent Framework speaks the OpenAI Chat Completions and stateful Responses protocols; the Python pivot consumes any base_url endpoint while a Foundry agent stays the backend brain.
Making agent threads and orchestrations crash-proof with AgentFunctionApp on Durable Task infrastructure.
The Durable Extension checkpoints agent threads on Durable Task infra: wrap agents in AgentFunctionApp and yield durable-wrapper calls inside orchestration triggers.
Turning an agent into a network service other agents can call with the A2A protocol.
A2A turns an agent into a network service: consume a remote one with A2AAgent(url=...) or expose your local Foundry agent by wrapping it in A2AExecutor.
Giving any agent a local chat window plus a live call inspector with agent_framework.devui.serve.
DevUI wraps any Foundry agent in a local chat window plus a live inspector via serve(entities=[agent]) from the separate agent-framework-devui package.
For open-ended tasks with no known solution path: a manager agent plans, keeps a shared ledger, and round-by-round picks which specialist acts next. Ordering is dynamic, not a fixed graph.
Magentic orchestration puts a manager agent in charge: it plans, keeps a shared ledger, and picks which specialist acts next round by round until it synthesizes an answer.
A collaborative conversation among agents, coordinated by an orchestrator that decides who speaks next. Agents share full history and refine each other's work over rounds — a star topology.
Group chat coordinates several agents in a star topology: an orchestrator decides who speaks next while agents share full history and refine each other over rounds.
A mesh of specialists where any agent can transfer the whole conversation to a better-suited peer — no central orchestrator. Control passes agent-to-agent via an auto-injected handoff tool call.
Handoff orchestration lets any agent transfer the whole conversation to a better-suited peer via an auto-injected tool call. Run unattended with autonomous mode.
Run several agents on the same prompt in parallel, then fan their answers back in. `ConcurrentBuilder` wires the fan-out/fan-in graph — latency is max(agent), not sum.
Concurrent orchestration fans one prompt out to several agents in parallel, then fans their answers back in with a built-in aggregator. Latency is max(agent), not sum.
Wire agents into a pipeline where each runs in turn and feeds the next. Hand a list to `SequentialBuilder`, call `.build()`, run it once — draft then review, extract then summarize.
Sequential orchestration chains agents so each runs in turn and feeds the next. Hand a list to SequentialBuilder, build, and run once — draft then review.
A whole workflow run as a single executor inside a parent. Wrap the inner graph in `WorkflowExecutor`, drop it into the parent like any node, and compose big systems from small, testable pieces.
Run a whole workflow as one executor inside a parent: wrap the inner graph in WorkflowExecutor, line up its I/O types, and drop it into the parent like any node.
Executor state leaks when you reuse one workflow across independent runs. `.NET` has `IResettableExecutor`; Python's answer is simpler — never share state, build fresh instances from a factory.
Reusing one workflow across runs leaks executor state. .NET has IResettableExecutor; Python's answer is a factory that builds fresh workflow and executor instances per run.
The same `workflow.run(...)` graph, consumed two ways — await it to completion, or stream its events live. The `.NET` OffThread/Lockstep modes don't apply to Python.
One workflow.run graph, two modes: await it to completion or iterate stream=True live events. The .NET OffThread/Lockstep modes do not apply to Python.
Wrapping agents as AgentExecutors explicitly — controlling the id, the context mode, and chaining a writer to a translator that sees only the previous agent's reply.
Wrap agents explicitly as AgentExecutors to set the id and context_mode. last_agent mode feeds a downstream agent only the previous reply, ideal for translate and refine.
WorkflowViz renders a built workflow's graph as a Mermaid diagram, a Graphviz DOT string, or an exported image — so you can eyeball fan-out/fan-in structure without running the agents.
Render a built workflow with WorkflowViz as Mermaid or Graphviz DOT, or export an image. Text formats need no deps; visualization reads graph shape without calling agents.
Turn a workflow run into a tree of OpenTelemetry spans — workflow.build, workflow.run, executor.process, edge_group.process, message.send — with one setup call.
Trace a workflow with configure_otel_providers and a root span. The framework emits workflow.build, workflow.run, executor.process, edge_group.process, message.send spans.
Declare the orchestration in YAML instead of Python — ordered actions load at runtime via WorkflowFactory, with agents resolved by name and PowerFx expressions for logic.
Declare a workflow in YAML and load it with WorkflowFactory. Ordered actions call agents resolved by name, with PowerFx expressions for prompts and variables.
Shared workflow state: any executor writes a keyed value, any later executor reads it back — so big payloads never have to be threaded through every message on the edge.
Share workflow state with ctx.set_state and ctx.get_state so big payloads skip the edge. Writers see updates immediately; other nodes see them the next superstep.
An agent can be a graph node directly — no custom Executor subclass. Writer drafts, a direct edge hands its output to Reviewer, and streamed tokens group by author.
Use an agent as a workflow executor node directly, no subclass needed. Wire Writer to Reviewer with an edge and group streamed AgentResponseUpdate tokens by author.
Wiring executors and edges into a graph with WorkflowBuilder — pick a start node, add edges, build, then run an input through the chain.
Build a graph workflow with WorkflowBuilder: set a start executor, add edges, build, and run. Handlers are async and type-annotated; output_from names the terminal node.
Streaming a workflow run so you can watch progress live — built-in lifecycle events plus your own custom ones — all discriminated by a single `.type` string.
Stream a Microsoft Agent Framework workflow with run(stream=True). Every event is one WorkflowEvent keyed by a .type string, plus custom events via ctx.add_event.
A workflow and an agent both expose .run() — so package a whole workflow as an agent and drop it anywhere an agent is expected.
A workflow and an agent both expose run(); workflow.as_agent() packages a workflow as an agent whose start executor accepts a list of Message.
Some steps need a person — request_info suspends the workflow and hands control back to your code, then you resume by re-running with the human's answer keyed by request_id.
request_info suspends the workflow and returns a pending request; you resume by re-running with the human's answer keyed by the same request_id.
Long or human-gated workflows can't live only in memory — hand the builder a CheckpointStorage and it snapshots state after each superstep, so you can restore and resume.
Hand the builder a CheckpointStorage and it snapshots state after each superstep, so you can list saved checkpoints and resume from an id.
We built a small Go + Python service that parses a project's INFORMATION_SCHEMA, asks Gemini to classify each top-spending query against a catalog of anti-patterns, and recommends a rewrite. It is not a magic box; it is a pipeline that cuts the human review time per query from 20 minutes to 90 seconds.
The reason to reach for a graph over a chain: fan out work to run concurrently, then fan in to merge — wall-clock is the slowest worker, not the sum.
Fan-out broadcasts the same message to concurrent workers; fan-in gives a joiner a list of all outputs and fires once every source completes.
A workflow isn't always a straight line — route the next executor by the data with a switch-case edge group, no model required.
A workflow isn't always a straight line: a switch-case edge group routes a message to the first Case whose predicate is true, else to the Default.
Point an agent at a remote MCP server and let Foundry do the calling — you describe the server once, the service discovers and invokes its tools for you.
A hosted MCP tool aims the agent at a remote MCP server and lets Foundry connect and call it; you describe the server once and set an approval mode.
Let an agent reach live search results for anything past its training cutoff — Foundry runs the search and grounding server-side, and returns URL citations.
The hosted web search tool lets an agent reach live results past its training cutoff; Foundry searches and grounds server-side and returns URL citations.
Ground an agent's answers in documents you upload — the provider indexes them into a vector store and searches server-side, no retrieval code of your own.
File search is a hosted tool: upload files, index them into a vector store, and bind the agent to the store id so Foundry runs retrieval server-side.
A provider-hosted sandbox that writes and runs Python for exact answers.
The hosted code interpreter runs the model's Python server-side. Attach it via FoundryChatClient.get_code_interpreter_tool and read the code and output as content parts.
Pausing the agent for a human yes/no before a sensitive tool runs.
Mark a tool approval_mode always_require and the run pauses with user_input_requests. Show the pending call, collect a decision, feed the approval back into a fresh run.
Growing and shrinking the tool set from inside a running turn.
From inside a tool you can call ctx.add_tools and ctx.remove_tools to reveal or hide tools on the next loop iteration, plus tool_choice to force a mandatory first call.
How a plain Python function becomes a JSON schema the model can call.
The framework turns a function's signature and docstring into a schema. Annotated Field descriptions document each parameter, and @tool overrides the name and description.
Writing your own agent by subclassing BaseAgent — a drop-in for the built-in one.
Subclass BaseAgent and implement one run() method to satisfy SupportsAgentRun. A deterministic EchoAgent then behaves like any framework agent, no chat client required.
The two ways to reach a model in an Azure AI Foundry project.
Foundry offers two doors to a deployed model: FoundryChatClient where your app owns the loop, and FoundryAgent where the portal definition wins. This runs the direct path end to end.
Passing per-request values to tools without leaking them into the model's schema.
Pass per-run values via function_invocation_kwargs and read them from FunctionInvocationContext inside tools and middleware, with session.state for state that persists across runs.
Passing data between middleware by putting them on one object.
Function middleware has no built-in shared bag, so put both middleware on one object and let them read and write instance attributes. That instance is the shared state.
Catching tool failures in middleware and turning them into graceful replies.
Wrap call_next() in try/except inside FunctionInvocationContext middleware to catch tool exceptions like TimeoutError and set context.result to a friendly fallback.
Rewriting a run's output after the model finishes, without touching instructions.
Result-override middleware rewrites context.result after call_next(); chat middleware overrides each ChatResponse and agent middleware wraps the whole AgentResponse.
Building guardrails by raising MiddlewareTermination to stop a run.
Raise MiddlewareTermination to stop a run: block disallowed input before the model, or cap responses using state carried on the middleware instance across runs.
Where you attach middleware decides when it fires — every run, or just one.
Agent-scope middleware fires on every run for cross-cutting policy; run-scope fires only for its call. The same middleware= keyword nests both around the agent.
Wrapping every model call to inspect, mutate, or short-circuit it.
Chat middleware wraps each model call via ChatContext and call_next; mutate context.messages in place, or set context.result and raise MiddlewareTermination to skip the model.
Keeping a growing transcript under a token budget with layered compaction strategies.
CompactionProvider rewrites in-memory history before each model call; a TokenBudgetComposedStrategy layers tool-result collapse, summarization and sliding window gentlest-first.
Where conversation history actually lives — local session state vs service-managed.
Local session state via InMemoryHistoryProvider re-sends the transcript each run; service-managed keeps only a remote id. Persist the whole AgentSession to survive restarts.
Carrying conversation state across separate agent.run() calls with AgentSession.
AgentSession carries history across separate agent.run() calls; pass the same session every time and serialize it with to_dict()/from_dict() to resume.
Letting the agent write and run one Python program instead of looping tool-by-tool.
CodeAct adds a single execute_code tool so the agent writes one sandboxed Python program calling tools via call_tool, wired through the Hyperlight provider with a fallback to direct tools.
The batteries-included autonomous agent pipeline from create_harness_agent.
Assemble a batteries-included autonomous agent with create_harness_agent: a function-calling loop, todo list, plan/execute modes, and compaction, each individually switchable via disable flags.
Packaging reusable agent capabilities that load lazily via progressive disclosure.
Package reusable agent capabilities as skills: define an InlineSkill with frontmatter, resources, and scripts, attach via SkillsProvider, and let the agent load them lazily on demand.
Scoring Microsoft Agent Framework agent outputs offline with a LocalEvaluator.
Score Microsoft Agent Framework agent outputs with evaluate_agent and a LocalEvaluator: run offline keyword, tool-call, and custom evaluator checks, then gate CI via raise_for_status.
Defining a Microsoft Agent Framework agent from a YAML spec instead of code.
Define a Microsoft Agent Framework agent from a YAML spec with AgentFactory create_agent_from_yaml, resolving connection values from env and building the Foundry client from the spec.
Grounding a Microsoft Agent Framework agent in your own docs with a search tool.
Ground a Microsoft Agent Framework agent with RAG-as-a-tool: expose a search function over your docs, instruct the agent to retrieve then answer and cite, and decline when nothing matches.
Starting long agent work in the background and polling with a continuation token.
Start long agent work with options background True, poll by re-running with the continuation_token until it returns None, and resume interrupted streams from the last token seen.
Making a Microsoft Agent Framework agent return typed data instead of prose.
Use response_format in agent.run options to make a Microsoft Agent Framework agent return a typed Pydantic model or parsed JSON on response.value, including streaming.
Building a Message of mixed content parts so an agent can reason about an image.
Sending images to a Microsoft Agent Framework agent by building a Message of Content parts with Content.from_uri and Content.from_data, targeting a vision-capable Foundry model.
An Agent is a layered pipeline — knowing the layers tells you exactly where to plug in.
The layered execution model of a Microsoft Agent Framework agent: agent middleware, RawAgent, context providers, and the separate swappable ChatClient pipeline.
The four ways you actually call run(): non-streaming, streaming, sessions, and per-run options.
The four ways to call run() in Microsoft Agent Framework: AgentResponse, stream=True with a ResponseStream, sessions for state, and per-run options merged over defaults.
Defending against prompt injection with information-flow control over trusted and untrusted content.
Defending a Microsoft Agent Framework agent from prompt injection with SecureAgentConfig, information-flow labeling of untrusted content, and block_on_violation enforcement.
Turning an agent run into OpenTelemetry spans you can read in the console or ship to a collector.
Wiring OpenTelemetry into a Microsoft Agent Framework agent with configure_otel_providers, console vs OTLP export, and the enable_sensitive_data content trade-off.
Context providers with two hooks — and file-backed history that survives restarts.
ContextProvider before_run/after_run hooks in Microsoft Agent Framework, plus FileHistoryProvider for on-disk transcripts and a per-session state dict for counters.
Borrowing tools from an external MCP server instead of writing them yourself.
Connecting a Microsoft Agent Framework agent to an external Model Context Protocol server via MCPStdioTool, entered with async with, so its remote tools merge into tools=[...].
Wrapping an agent run to observe, guard, or short-circuit it — without touching the agent's logic.
Agent, chat, and function middleware in Microsoft Agent Framework: one middleware=[...] list, call_next() with no args, and short-circuiting via MiddlewareTermination.
Everything so far ran once and exited. To use an agent like a product, put a server in front of it — DevUI stands up a local web chat around any agent with one call.
serve() from agent_framework.devui wraps any agent in a local web chat; it blocks with its own event loop and ships as a separate install.
The functional API hid the graph. Here it's explicit: executors are nodes, edges join them, and a message flows from the start executor along the edges.
Build a two-node graph with WorkflowBuilder: executors send_message or yield_output, and the type hint on WorkflowContext carries the routing intent.
A workflow is not an LLM thing — it's a way to compose steps with checkpointing and events. This one has no agent and no model call, so it runs with zero credentials.
A credential-free workflow of two @step functions composed by a @workflow, isolating checkpointing, steps and outputs from any model call.
Compose several agents into a pipeline with the gentlest workflow API: decorate an async function with @workflow and call agents inside it with plain await.
Decorate an async function with @workflow and call agents inside it with plain await: a writer drafts, an editor tightens, one line of glue each.
A session remembers within one conversation. A ContextProvider remembers across all of them — durable facts injected into every run.
A ContextProvider holds facts that outlive any session and injects them into every run via before_run and context.extend_instructions.
An Agent is stateless — two runs are strangers. What carries history from one turn to the next is an AgentSession.
An Agent is stateless; an AgentSession carries history between turns. Thread create_session() through each run so turn two can see turn one.
A tool is just a Python function the model may call: decorate it with @tool, annotate the arguments, and the framework wires the schema, the call, and the result back into the conversation.
Turn a Python function into a tool with the @tool decorator and Annotated Field descriptions, then let the model pick the right one from the question wording.
The smallest possible Python agent: a Foundry chat client plus instructions, run once collected and once streamed.
The smallest MAF Python agent: wire a FoundryChatClient plus instructions, then run it once collected and once streamed with stream=True.