« Phase 23 README · Hitchhiker's · Deep Dive · Principal Deep Dive · Core Contributor · Staff Notes

Phase 23 Warmup — AutoGen & the Microsoft Agent Framework

Who this is for: someone comfortable with the agent loop (Phase 01), multi-agent coordination (Phase 07), and LangGraph's execution model (Phase 18), who now needs to speak fluently about the Microsoft agent-framework world — AutoGen, Semantic Kernel, and the Microsoft Agent Framework that succeeds both. By the end you will understand the conversational team model, the graph-Workflow model, the orchestration patterns, and the lineage story, from first principles and under the hood. Nothing here needs a GPU, an API key, or the real libraries; the labs rebuild the mechanisms in pure stdlib.

Table of Contents

  1. The lineage: three frameworks, one successor
  2. AutoGen's objects: AssistantAgent, UserProxyAgent, and messages
  3. The v0.2 → v0.4 rearchitecture: from a loop to an actor runtime
  4. Teams: the conversational control loop
  5. Speaker selection: round-robin vs selector
  6. Termination conditions and their algebra
  7. Semantic Kernel: the enterprise parent
  8. The convergence: why Microsoft merged them
  9. Agents vs Workflows: open-ended vs explicit
  10. MSAF Workflows: executors, typed edges, routing
  11. The super-step runtime and checkpointing
  12. Human-in-the-loop: the request/response node
  13. Middleware, threads/sessions, and MCP
  14. The orchestration patterns: sequential, concurrent, handoff, group-chat, magentic
  15. MSAF vs LangGraph vs OpenAI Agents SDK vs CrewAI
  16. Common misconceptions
  17. Lab walkthrough
  18. Success criteria
  19. Interview Q&A
  20. References

1. The lineage: three frameworks, one successor

Before any code, hold the map, because the single most common way to sound out of date in a Microsoft-stack interview is to talk about AutoGen and Semantic Kernel as if they were the current, separate destinations. They are not, as of late 2025. Three names, and how they relate:

  • AutoGen — born in Microsoft Research (~2023) as a framework for conversational multi-agent systems. Its mental model is a group chat: several agents share one message history and take turns writing to it until someone decides the task is done. It is excellent for open-ended, emergent collaboration and terrible at giving you predictable, auditable control flow.
  • Semantic Kernel (SK) — Microsoft's other agent line, born on the product side, aimed at enterprise integration: typed plugins/functions, planners, memory, and — critically — the production plumbing (session state, filters/middleware, telemetry, .NET-first support) that a bank actually needs. Strong on governance, less focused on emergent multi-agent chat.
  • The Microsoft Agent Framework (MSAF) — announced in late 2025 as the direct successor to both, built by the same teams. It is not "AutoGen 0.5" and not "SK 2.0"; it is the merge: AutoGen's simple agent abstractions plus SK's enterprise features plus a new graph-based Workflow engine that neither parent had. It ships for .NET, Python, and Go.
   AutoGen (MSR)                Semantic Kernel (product)
   conversational teams         enterprise plugins + state + telemetry
   emergent, open-ended         typed, governed, .NET-first
          \                              /
           \                            /
            ▼                          ▼
       ┌──────────────────────────────────────┐
       │       Microsoft Agent Framework       │
       │  agents (AutoGen)  + enterprise (SK)  │
       │      + graph Workflows (new)          │
       │        .NET · Python · Go             │
       └──────────────────────────────────────┘

Why you must know this cold: an interviewer who works in this ecosystem is testing whether you track the field. "I'd use AutoGen for the multi-agent part and Semantic Kernel for the enterprise integration" was a good 2024 answer and is now a stale one — the right answer is "I'd use the Microsoft Agent Framework, which is the successor to both; its Workflow engine gives me the typed, checkpointed orchestration, and its agent abstractions cover the conversational part." Same concepts, current framing. This warmup teaches the concepts through their AutoGen/SK origins (because that is where the mechanisms come from) and keeps pointing at where they land in MSAF.


2. AutoGen's objects: AssistantAgent, UserProxyAgent, and messages

Strip AutoGen to its nouns. The autogen-agentchat layer gives you two agent archetypes and a message type, and almost everything else is composition of those.

  • AssistantAgent — an agent backed by a model client (an LLM). You give it a name, a system prompt, optionally a set of tools (Python callables it can invoke), and it produces a reply from the conversation so far. This is the "thinking" agent.
  • UserProxyAgent — an agent that stands in for a human or an executor. It can solicit human input (a console prompt, a web callback) or execute code/tools the assistant proposes. It is the classic AutoGen pairing: an assistant that proposes and a user-proxy that executes — which is Phase 00's trust boundary wearing an AutoGen costume.
  • TextMessage (and siblings like MultiModalMessage, ToolCallSummaryMessage) — one turn on the shared conversation, carrying a source (who wrote it) and content. A team run is, at bottom, an ordered list of these.

The essential move, and the one the labs are built around: an agent's reply is a function of the conversation. Real AutoGen calls a model; for testing, you replace the model with a pure policy — a Callable[[list[TextMessage]], str]. That single substitution makes the whole team deterministic. It is not a toy: it is exactly how you unit-test a production AutoGen team (record the model's outputs, replay them), because the coordination logic must be correct independently of what the model happens to say.

# Lab 01's Agent: a name + an injected reply policy (the LLM stand-in).
class Agent:
    def __init__(self, name, policy):        # policy(messages) -> str
        self.name, self.policy = name, policy
    def reply(self, messages):
        return TextMessage(source=self.name, content=self.policy(list(messages)))

An AssistantAgent with a real model is this with policy = lambda msgs: model.complete(msgs). A UserProxyAgent is this with policy = lambda msgs: input(...) or policy = lambda msgs: run_code(...). Same shape; the seam is the policy.


3. The v0.2 → v0.4 rearchitecture: from a loop to an actor runtime

You will get asked "what changed in AutoGen v0.4?" and the answer signals whether you actually follow the project. Early AutoGen (v0.2) was a monolithic conversation loop: agents, orchestration, and the chat manager were tightly coupled, single-process, largely synchronous, and hard to extend, distribute, or observe. It was great for a notebook demo and painful for a production service.

v0.4 (early 2025) was a ground-up rewrite around an actor model / event-driven runtime. The layering:

  • autogen-core — the foundation: an asynchronous, message-passing runtime where agents are actors that receive and emit events/messages. Actors are isolated (own state, no shared memory), communicate only by messages, and can in principle be distributed across processes/machines. This is the Erlang/Akka lineage applied to agents.
  • autogen-agentchat — the ergonomic, opinionated layer on top: AssistantAgent, RoundRobinGroupChat, SelectorGroupChat, termination conditions — the "batteries-included" team API most people touch.
  • autogen-ext — model clients, tools, and integrations (OpenAI, Azure, etc.).

Why this matters beyond trivia: the actor/event architecture is the same idea MSAF inherits and LangGraph independently usesa framework is a thin, friendly layer over a message-passing runtime. Once you see that agents are actors exchanging messages, the jump to MSAF's Workflow (executors exchanging typed messages over a super-step runtime) is small, and the jump to Phase 18's Pregel model is smaller still. Three frameworks, one substrate: message passing with a scheduler.

The reliability angle. An actor runtime gives you natural seams for the things Staff interviews probe: message delivery guarantees, backpressure, distributing agents across workers, and — because everything is a message — a complete, replayable event log. The v0.2 loop had none of these cleanly; the v0.4 runtime is built for them.


4. Teams: the conversational control loop

A single agent is a function; a team is where AutoGen earns its name. A team (a GroupChat in the API) is exactly three things:

  1. A shared conversation — the ordered list of messages every participant can see.
  2. A speaker-selection policy — given the conversation so far, who speaks next?
  3. A termination condition — given the conversation so far, should we stop?

team.run(task) is then a strikingly small loop:

def run(self, task):
    messages = [TextMessage("user", task)]      # seed with the task
    stop = self.termination(messages)           # maybe we're already done
    while stop is None:
        speaker = self._select(messages)        # (2) who talks next
        messages.append(speaker.reply(messages))# that agent replies to the WHOLE chat
        stop = self.termination(messages)       # (3) are we done?
    return TaskResult(messages, stop_reason=stop)

Read what this says about AutoGen's philosophy. There is no graph. Nobody declared "after the researcher, run the writer." The path emerges from the selection policy and what the agents say. That open-endedness is the whole point of AutoGen — it shines when you don't know the collaboration structure in advance and want it to emerge — and it is also exactly why you need a hard termination condition: without one, two agents can politely converse until your token budget is gone. Termination is the team-level step budget from Phase 00. There is deliberately no hidden step cap in the loop above; the termination condition is the guard, which is why AutoGen ships MaxMessageTermination as a default backstop.

The TaskResult carries a stop_reason — not just "we stopped" but why. When a team stops after two messages and you expected ten, the reason string ("MaxMessageTermination(2)" vs "TextMentionTermination('TERMINATE')") is the first thing you read. That is why the labs' conditions return a reason string rather than a bare boolean.


5. Speaker selection: round-robin vs selector

Selection is the knob that turns a shared conversation into a coordinated one. AutoGen ships two canonical policies, and Lab 01 builds both.

RoundRobinGroupChat — speakers take turns in participant order, cycling. Speaker i mod n on turn i. It consults no model to choose, so it is fully deterministic: the only nondeterminism (in the real thing) lives inside each agent's own reply. Use it when the roles are fixed and the order is natural — a writer and a reviewer alternating, a fixed panel each weighing in once per round. It is the multi-agent equivalent of a for loop, and its predictability is a feature: a round-robin team is the closest AutoGen gets to a workflow.

SelectorGroupChat — an injected selector decides the next speaker each turn. In real AutoGen the default selector is an LLM handed the conversation plus the roster ("here are the agents and what they do; who should speak next?"), returning a name — a manager/router agent that never itself does task work. You can also pass a custom selector_func (a plain Python function) to override it, and options like allow_repeated_speaker shape the choice. Lab 01 injects the selector as a pure Callable[[messages, participants], name], which is precisely the seam AutoGen exposes, and validates the returned name against the roster — a selector that names a non-participant is a routing bug that should fail loudly, not silently stall.

# Selector chooses the specialist by the task text — deterministic, testable routing.
def selector(messages, participants):
    return "mathematician" if any(c.isdigit() for c in messages[0].content) else "writer"

The choice between them is the same open-ended-vs-explicit tension that runs through this whole phase: round-robin is explicit (you fixed the order); selector-with-a-model is open-ended (the model routes). And note the failure modes differ: round-robin can waste turns letting an irrelevant agent speak; a model selector can loop by repeatedly picking the same stuck agent — which is why the termination condition is non-negotiable in either case.


6. Termination conditions and their algebra

A termination condition is a predicate over the conversation that says stop (with a reason) or keep going. AutoGen ships a family of them, and their composability is the elegant part.

The two you must know:

  • MaxMessageTermination(n) — stop once the conversation reaches n messages (including the seed task). The unconditional backstop; the team-level max_steps. Every production team has one, even when another condition is expected to fire first, because "the model forgot to say TERMINATE" is a real failure and an unbounded chat is a real bill.
  • TextMentionTermination(text) — stop once a keyword (classically "TERMINATE") appears in the latest message. This is model-driven termination: you prompt an agent to emit the keyword when it believes the task is complete. Open-ended, and therefore only as reliable as the model — hence you compose it with a MaxMessageTermination backstop.

Others in the real family: TokenUsageTermination (budget in tokens), TimeoutTermination (wall-clock), HandoffTermination (stop when control is handed to a particular target, e.g. back to a human), StopMessageTermination, SourceMatchTermination, ExternalTermination (a caller flips a switch). You do not need to memorize all of them; you need the pattern: a condition is a small object, and conditions compose with | (OR) and & (AND).

# "Stop when someone says TERMINATE, OR after 10 messages, whichever first."
termination = TextMentionTermination("TERMINATE") | MaxMessageTermination(10)
  • a | b fires as soon as either fires — the standard "the agents finish naturally or we hit the budget" pattern. The reason is the first sub-condition that fired.
  • a & b fires only once both have fired — e.g. "at least 3 messages and the reviewer approved," so a premature approval on message one doesn't end the run.

There is a subtlety worth stating because it distinguishes a careful engineer: the real AutoGen conditions are stateful and async — each is fed the delta of new messages per turn and remembers whether it has fired (so & can accumulate across turns), and each needs a reset() between runs. Lab 01 models them as pure functions of the whole transcript, which is a faithful miniature of the composition algebra and keeps the team deterministic, while being honest that the production objects carry state. Knowing both — the clean algebra and the stateful reality — is the principal-level read.


7. Semantic Kernel: the enterprise parent

You cannot explain why MSAF exists without one honest paragraph on Semantic Kernel, because MSAF inherits its DNA. SK is Microsoft's enterprise agent SDK, and its contributions to the merged framework are the parts a bank cares about:

  • Typed plugins / functions — capabilities are declared as strongly-typed functions (native code or prompt templates) with schemas, not stringly-typed tool blobs. Type safety at the boundary.
  • Session state / threads — a first-class notion of a conversation's persisted state, so an agent's memory is an object you can store, inspect, and restore, not an ad-hoc list.
  • Filters / middleware — interceptors that wrap function and agent invocations: logging, guardrails, PII redaction, authorization, retries. The cross-cutting-concerns seam.
  • Telemetry — OpenTelemetry-based tracing/metrics baked in, because "we can't observe it" fails an enterprise review.
  • .NET-first (with Python) — SK grew up in the .NET world, which is where a lot of enterprise agent work actually happens and where AutoGen (Python-first) was weak.

Where SK was comparatively weak was emergent multi-agent collaboration — the open-ended group-chat dynamics AutoGen nailed. So you had two Microsoft frameworks, each strong where the other was weak, which is a strange thing to ask enterprise customers to choose between. That tension is the setup for the merge.


8. The convergence: why Microsoft merged them

Put sections 1–7 together and the merge is over-determined:

  • AutoGen had the best agent and multi-agent abstractions (simple AssistantAgent, teams, the v0.4 actor runtime) but weak enterprise posture (Python-first, thinner state/telemetry/ governance story) and no explicit-orchestration answer — everything was open-ended chat.
  • Semantic Kernel had the best enterprise posture (typed functions, session state, middleware, telemetry, .NET) but a weaker multi-agent story.
  • Customers were being asked to pick between two overlapping Microsoft frameworks, or bolt them together. Two teams inside one company were solving adjacent problems twice.

The Microsoft Agent Framework resolves it by taking the best of each and adding the missing piece:

  1. AutoGen's agent abstractionsAIAgent/ChatAgent are the descendants of AssistantAgent; the actor/message-passing runtime carries over.
  2. Semantic Kernel's enterprise features — session-based state (threads), type safety, middleware, and telemetry are first-class.
  3. A new graph-based Workflow engine — the thing neither had: explicit, typed, checkpointed, human-in-the-loop orchestration (sections 10–12). This is the deliberate answer to "AutoGen's open-ended chat is untestable and un-auditable for the 90% of tasks that have a known structure."

The tagline to remember: MSAF = AutoGen's simplicity + SK's enterprise features + graph Workflows, across .NET, Python, and Go. AutoGen and SK are now lineage, still usable, but the place new investment and new features land is MSAF. In an interview, that is the sentence that says "I am current."


9. Agents vs Workflows: open-ended vs explicit

MSAF's most important design idea — and a genuinely senior framing you should adopt — is the explicit split between two ways to build:

  • Agents (open-ended). The model decides the control flow at runtime. A ChatAgent, or an AutoGen team, where what happens next depends on what the model just said. Maximum flexibility, minimum predictability. Use when the task genuinely needs runtime adaptation and you cannot enumerate the path in advance.
  • Workflows (explicit). You decide the control flow, as a graph of executors and typed edges. The model runs inside nodes, but the orchestration is code you wrote and can read, test, replay, and audit. Use when the structure is knowable — which, per Phase 00, is most of the time.

MSAF states the principle bluntly, and it is worth quoting because it is the whole least-agentic argument compressed: "if you can write a function, do that." In other words, do not reach for an open-ended agent to do something a deterministic function (or an explicit workflow of functions and the occasional agent) does more reliably and cheaply. Autonomy is a cost, not a badge (Phase 00).

This is the same conclusion Anthropic's Building Effective Agents reaches ("workflows vs agents," prefer the simplest thing that works) and the same conclusion LangGraph embodies by making the graph explicit. MSAF's contribution is to build both modes into one framework and make choosing between them a first-class, per-task decision rather than a framework choice. The rest of this warmup is the mechanism behind the Workflow (explicit) mode, because that is the part that is new and the part enterprises adopt MSAF for.


10. MSAF Workflows: executors, typed edges, routing

A Workflow is a directed graph. Two nouns and one verb:

  • Executor — a node. It has an id and a handler; the handler takes an input message and produces an output message. An executor can wrap an agent (its handler calls the model), a plain function (deterministic transformation), or a special capability (the human-input node, §12). In the lab: Executor(id, handler) with handler(payload, ctx) -> output.
  • Edge — a directed connection source -> target. Two properties matter, and keeping them distinct is the crux of the lab:
    • message_type — a type CONTRACT. MSAF edges are type-safe: an edge declares the type of message it carries, and when the edge fires, a payload of the wrong type is a WorkflowTypeError, not a value that silently flows to a handler expecting something else. This is Semantic Kernel's type-safety DNA applied to orchestration. It catches the classic multi-agent bug — one agent emits a shape the next didn't expect — at the wiring, before it corrupts the run.
    • condition — the ROUTER. An edge can carry a predicate; it fires only if condition(payload) holds. This is how a workflow branches: two edges out of a classifier node with complementary conditions, exactly one fires. (Contrast Lab 01's SelectorGroupChat, where a model routes; here a typed predicate routes — explicit, not emergent.)

The distinction is deliberate and worth saying out loud in an interview: the type is a correctness guarantee that errors on violation; the condition is control flow that selects a branch. If you conflate them (use the type as the router), a type mismatch becomes "that edge just didn't fire," which silently drops messages — the opposite of what you want. The lab enforces the split.

b = WorkflowBuilder()
b.add_executor(Executor("classify", lambda n, ctx: n))
b.add_executor(Executor("even", lambda n, ctx: f"{n} is even"))
b.add_executor(Executor("odd",  lambda n, ctx: f"{n} is odd"))
b.add_edge("classify", "even", condition=lambda n: n % 2 == 0)   # router
b.add_edge("classify", "odd",  condition=lambda n: n % 2 == 1)
b.set_start("classify")
wf = b.build()                                                    # validates the graph
wf.run(4).outputs   # -> ["4 is even"]

A node with no firing outgoing edge is a leaf — its output is collected as a workflow output (MSAF executors yield_output; the terminal node produces the run's result). The shared WorkflowContext carries session state every executor can read and write — Semantic Kernel's session/thread idea, threaded through the run and (crucially) captured by the checkpoint.


11. The super-step runtime and checkpointing

Underneath, MSAF's Workflow runtime is Pregel / Bulk-Synchronous-Parallel — the same super-step message-passing model Phase 18's Lab 01 built for LangGraph. That is not a coincidence: graph-of-actors orchestration converges on this design because it makes concurrency and checkpointing clean.

The run advances in discrete super-steps:

  1. There is a queue of pending messages — pairs of (target_executor, payload). Initially it holds (start, input).
  2. A super-step delivers every pending message: run each target executor against its payload, collect each output, and route each output along its outgoing edges (type-checking, applying conditions) into the next super-step's queue. Leaf outputs are collected as results.
  3. Repeat until the queue is empty (completed), guarded by a max_supersteps bound so a cyclic graph cannot loop forever (the graph-level recursion_limit).

Why this design earns its keep: a checkpoint is just the queue + shared state captured at a super-step boundary. Because a super-step is an atomic "deliver all pending, produce the next batch" unit, freezing between super-steps captures a consistent snapshot with no half-run executors. Restore that snapshot and the run continues byte-identically. In the lab:

@dataclass
class Checkpoint:
    shared: dict          # the session state
    queue: list           # the pending (executor, payload) messages
    pending: list         # any deferred human-input requests (§12)
    outputs: list         # results collected so far
    superstep: int        # where we froze

wf = build_pipeline()
paused  = wf.run(0, pause_after=1)          # simulate a crash after super-step 1
resumed = wf.resume(paused.checkpoint)       # continue; identical to an uninterrupted run

In production MSAF the checkpoint is serialized to a checkpoint store (a database, blob storage) so a process can die and a different process can resume the workflow hours later. This is the durable-execution idea (Phase 08) at the orchestration layer, and it is a headline enterprise feature: a multi-step agent workflow that survives a deploy, a crash, or a long human-approval wait without re-running completed work (re-running is not just wasteful — if a completed step sent an email or moved money, re-running is dangerous; idempotency and checkpointing are two sides of one coin, Phase 08). The lab's in-memory Checkpoint is the same shape; the only thing missing is the serialization to a store, which the README's Extensions section points at.


12. Human-in-the-loop: the request/response node

Some steps must pause for a human — approve a refund, pick between two drafts, confirm a destructive action. MSAF makes this a first-class executor, the RequestInfoExecutor. Reaching it does not block a thread waiting on input(); it suspends the workflow:

  1. The workflow emits a RequestInfoEvent carrying the node id and a prompt.
  2. It checkpoints — capturing exactly where it paused (the pending request) plus the state and any other queued work.
  3. workflow.run(...) returns with status="paused" and the request(s). Your application shows the human the prompt, gets an answer out of band (a web form, a Slack approval, an email reply — possibly hours later), and calls workflow.resume(checkpoint, {node_id: response}).
  4. On resume, the human's response becomes that node's output and is routed downstream like any other executor output; the workflow continues.
b.add_executor(Executor("draft",   lambda topic, ctx: f"draft:{topic}"))
b.add_executor(RequestInfoExecutor("approve", prompt="approve? (yes/no)"))
b.add_executor(Executor("publish", lambda decision, ctx: f"published({decision})"))
b.add_edge("draft", "approve"); b.add_edge("approve", "publish"); b.set_start("draft")

paused = wf.run("MSAF")                       # status="paused", requests=[approve]
done   = wf.resume(paused.checkpoint, {"approve": "yes"})   # -> ["published(yes)"]

Two design points that separate a real implementation from a demo, both enforced by the lab:

  • HITL is checkpointing. Pausing for a human is the same mechanism as pausing for a crash — a checkpoint at a super-step boundary. That is why they live in one code path (_drive returns a checkpoint whether it paused for a request or for pause_after). Realizing HITL and durability are the same feature is the insight.
  • Resume must be given the response, or fail loudly. resume(checkpoint, {}) with a pending request raises — a workflow that silently continues past a missing human decision is a bug that ships a refund nobody approved. The lab raises WorkflowError on a missing response.

This is the exact HITL story LangGraph tells with interrupt() and a checkpointer (Phase 18 Lab 03); MSAF's version is the RequestInfoExecutor plus its checkpoint store. Both are "pause the graph, persist, resume with injected input."


13. Middleware, threads/sessions, and MCP

Three more MSAF surfaces that come straight from the Semantic Kernel side and that an enterprise interviewer will probe:

  • Middleware. MSAF lets you wrap agent calls and function/tool calls with interceptors — the middleware pattern (SK's "filters"). A middleware sees the invocation, can inspect/modify the input, run the next handler (or short-circuit it), and inspect/modify the output. This is where the cross-cutting concerns live: logging and tracing, guardrails and PII redaction (Phase 10), authorization checks (Phase 13), caching, retries, rate limiting. It is the same "wrap the boundary" idea as web middleware, applied to agent and tool invocations, and it is the answer to "how do you add governance without rewriting every agent."
  • Threads / sessions. An agent conversation's state is a thread (a session) — a first-class, persistable object holding the message history and any associated state. You can run an agent, get a thread back, store it, and later resume the conversation with full context. This is SK's session state, and it is what makes multi-turn, resumable agents clean instead of a pile of ad-hoc lists. (The Workflow's WorkflowContext.shared in the lab is the workflow-scoped cousin of this.)
  • MCP (Model Context Protocol). MSAF is an MCP client: an agent can consume tools/resources exposed by any MCP server (Phase 03), so tools are not hard-coded into the agent but discovered from standardized servers. This is the interop story — the same MCP you built a server for in Phase 03, consumed here as a first-class tool source. It also means an agent can be exposed as a tool to other systems, which is how MSAF agents compose across process boundaries.

Together these are the "enterprise" in "enterprise agent framework": you can observe it (telemetry), govern it (middleware), persist it (threads/checkpoints), and integrate it (MCP). None of that is glamorous, and all of it is what actually gets an agent through a bank's architecture review.


14. The orchestration patterns: sequential, concurrent, handoff, group-chat, magentic

Above the raw Workflow/team primitives, both AutoGen and MSAF ship a small library of named orchestration patterns, because ~90% of multi-agent systems are one of five shapes. Lab 03 builds four of them (group-chat is Lab 01). Learn them as a vocabulary — naming the pattern is the senior move in a design review.

  • Sequential (chain). Agents run in a fixed pipeline; each one's output is the next one's input. The cheapest, most reliable, most auditable shape — it is really a workflow (Phase 00). MSAF's SequentialBuilder. Use for "outline → draft → polish," "extract → transform → load."
  • Concurrent (fan-out / fan-in). The same task goes to N agents independently; an aggregator merges their outputs. Independence is the point — it is what lets the runtime run them in parallel (the lab runs them in list order for determinism, since the outputs don't depend on each other, so order can't change the result). MSAF's ConcurrentBuilder. Use for ensembles, multi-reviewer gates, "ask three models and vote." The aggregator is where the interesting logic lives (join, majority vote, "pass only if all approve").
  • Handoff (dynamic transfer). An agent can transfer control to a named other agent at runtime — a triage agent routes to billing or tech; a generalist hands a math sub-problem to a specialist. The transfer is a structured, in-band signal (in real frameworks, a transfer_to_<agent> tool call — OpenAI Agents SDK handoffs, AutoGen's Swarm). Two invariants the lab enforces: the target is validated to exist (an unknown handoff is a routing bug, not a no-op), and the chain is bounded (max_steps) so two agents cannot hand off to each other forever. This is the open-ended pattern of the four — the path is decided at runtime by the agents.
  • Group-chat (AutoGen team). Sections 4–6 — a shared conversation with speaker selection and termination. The most emergent, least predictable pattern; reach for it when the collaboration structure genuinely can't be pre-drawn.
  • Magentic (manager + progress ledger). The general-purpose orchestrator, named after Microsoft Research's Magentic-One. A manager/orchestrator agent maintains a progress ledger (a task ledger of facts/plan plus a record of what's been done), and each round it: reads the ledger, assigns a subtask to the best specialist, observes the result (appended to the ledger), and repeats — until it decides the task is complete and synthesizes a final answer. Bounded by max_rounds. It subsumes the other patterns (a fixed plan is sequential; parallel assignments are concurrent; a reassignment is a handoff), which is why MSAF ships it as the flagship pattern for open-ended, multi-specialist tasks.
# Magentic: the manager drives from the ledger; specialists execute; the manager synthesizes.
def manager_policy(ledger_text):
    if "researcher" not in ledger_text: return "ASSIGN: researcher: gather sources"
    if "writer"     not in ledger_text: return "ASSIGN: writer: draft from the sources"
    return "FINAL: report complete"
mag = magentic(Agent("manager", manager_policy), [researcher, writer], "produce a cited report")
# mag.ledger records every (agent, subtask, result); mag.rounds is bounded; mag.output is the synthesis

The progress-ledger idea is the reliability lever from Phase 00 applied to orchestration: the manager observes progress explicitly (rather than hoping the chat converges), which lets it detect being stuck and re-plan — Magentic-One tracks exactly this to decide when to reassign or reset. It is the difference between "a group chat that might wander" and "a manager that drives to done."


15. MSAF vs LangGraph vs OpenAI Agents SDK vs CrewAI

The compare-and-contrast you will be asked. Hold the axes: explicit graph vs open-ended, checkpointing/HITL, type safety, ecosystem/language, who it's for.

  • MSAF vs LangGraph — the closest comparison, and the most likely interview question. Both are graph-workflow frameworks with checkpointing and human-in-the-loop, both run on a super-step runtime, both let you mix explicit orchestration with LLM nodes. Differences: MSAF adds type-safe edges and a first-class agent/thread/session model and lives in the .NET + Python + Go / Azure world with SK's enterprise plumbing; LangGraph is Python/JS-first with arguably the richest checkpointer/store ecosystem (Postgres, Redis, SQLite savers) and the deepest LangChain tool integration. Pick MSAF in a Microsoft/Azure/.NET shop or an SK migration; pick LangGraph in a Python-native shop already on LangChain. The concepts — nodes, typed state, super-steps, checkpoints, interrupts — map almost one-to-one, which is why Phase 18 transfers directly.
  • MSAF vs OpenAI Agents SDK — the OpenAI Agents SDK is lighter and more open-ended: Agent + Runner (a built-in agent loop) + handoffs + guardrails, Python-first, minimal orchestration scaffolding. It has handoffs (the pattern in Lab 03) but no built-in graph-Workflow engine, typed edges, or durable checkpointing the way MSAF does — you'd add durability yourself. Pick the Agents SDK for a fast, model-centric, OpenAI-stack build; pick MSAF when you need the explicit, typed, checkpointed, governed workflow layer.
  • MSAF vs CrewAI — CrewAI is role-and-task-centric and opinionated: you declare a crew of agents with roles and a set of tasks, and it orchestrates (sequential or hierarchical) with a friendly, high-level API. Fast to start, less control, thinner on typed orchestration and durable execution. Pick CrewAI for quick role-based prototypes; pick MSAF when you need enterprise control, type safety, and resumability.

The through-line for a senior answer: name the axis that matters for the task (does it need durable, auditable, typed control flow? → MSAF/LangGraph; is it a light model-centric loop? → Agents SDK; is it a quick role-based crew? → CrewAI) and name the ecosystem you're in (Azure/.NET → MSAF; Python/LangChain → LangGraph; OpenAI → Agents SDK). "It depends" is only a good answer when you say on what.


16. Common misconceptions

  • "AutoGen and Semantic Kernel are the current, separate choices." As of late 2025 they are the lineage of the Microsoft Agent Framework, which supersedes both. Talking about them as the destination dates you.
  • "AutoGen is a graph framework like LangGraph." No — AutoGen's team model is a conversation with speaker selection, not a declared graph. The graph is the MSAF Workflow, which is the new part MSAF adds. Conflating them misses the whole open-ended-vs-explicit split.
  • "A group chat will terminate on its own." Only if a termination condition fires. Without a MaxMessageTermination backstop, a polite team can converse until your budget is gone. Termination is the team-level step budget; it is not optional.
  • "Type-safe edges are just documentation." They raise on a mismatch. The type is a runtime contract that catches the "one agent emits a shape the next didn't expect" bug at the wiring — the most common multi-agent failure — instead of letting it corrupt the run downstream.
  • "Checkpointing and human-in-the-loop are separate features." They are the same mechanism: a checkpoint at a super-step boundary. HITL is "pause for a human," durability is "pause for a crash"; one code path serves both.
  • "Concurrent means nondeterministic." Concurrent means the agents are independent (no shared context), which is what permits parallelism — but the result is order-independent, so you can run them deterministically (as the lab does) and still call it concurrent. Independence, not wall-clock parallelism, is the defining property.
  • "Magentic is just a fancier group chat." The difference is the progress ledger: the manager observes progress explicitly and can detect being stuck and re-plan, rather than hoping a conversation converges. Explicit progress tracking is the reliability lever.
  • "More agents = smarter." More agents = more coordination cost, more tokens, more latency, and more ways to loop (Phase 00/07). Multi-agent is separation of concerns plus verification, bounded by a step budget — not intelligence by headcount.

17. Lab walkthrough

Three labs, each a faithful miniature with the model injected as a pure policy.

Lab 01 — AutoGen GroupChat. Build TextMessage; the termination family (MaxMessageTermination, TextMentionTermination, and the _Or/_And composition behind |/&); the Agent with an injected reply policy; BaseGroupChat.run (the seed → check → select → reply → check loop); and the two selection policies (RoundRobinGroupChat cycles, SelectorGroupChat calls an injected selector and validates the name). The tests assert speaker order, per-condition and composed termination with the right stop-reason, selector routing, and determinism.

Lab 02 — MSAF Workflows. Build Edge.fires (router) and Edge.check_type (contract) — keep them distinct; the WorkflowBuilder validation; Workflow._emit (route to firing edges with a type check, or collect a leaf output); and the super-step driver _drive (deliver the queue in deterministic order, run/route normal nodes, defer request nodes and suspend with a checkpoint, honor pause_after, bound with max_supersteps), plus run/resume. The tests assert linear typed flow, even/odd routing, a WorkflowTypeError on a wrong type, shared-state threading, checkpoint/resume equivalence, and HITL pause/resume (including the missing-response error).

Lab 03 — Orchestration Patterns. Build the four patterns: sequential (thread each output into the next), concurrent (fan out the same task, merge via an aggregator), parse_handoff + handoff (follow validated transfers, bounded), and parse_directive + magentic (the manager loop over a ProgressLedger, bounded, synthesizing on timeout). The tests assert chaining, fan-in merges, handoff routing/validation/bounding, magentic assignment + ledger + synthesis + bound, and determinism.

Run LAB_MODULE=solution pytest test_lab.py -v in each to see green, then make your lab.py match. Finish by reading each solution.py's main() output — the whole warmup, worked.


18. Success criteria

  • You can explain, in three sentences, that MSAF is the successor to both AutoGen and Semantic Kernel, and what each parent contributed.
  • You can describe AutoGen's team loop (shared conversation + selection + termination) and why it is open-ended, and contrast round-robin vs selector selection.
  • You can compose termination conditions with |/& and say why a MaxMessage backstop is mandatory — and note that the real conditions are stateful/async.
  • You can explain an MSAF Workflow: executors, the type-contract vs router split on edges, the super-step runtime, and why that runtime makes checkpointing clean.
  • You can explain why HITL and durable checkpointing are the same mechanism.
  • You can name the five orchestration patterns, each one's tradeoff and bound, and what the magentic progress ledger buys you.
  • You can compare MSAF to LangGraph, the OpenAI Agents SDK, and CrewAI along the right axes and say when you'd pick each.
  • All 70 lab tests (22 + 23 + 25) pass under both lab and solution.

19. Interview Q&A

Q: What is the Microsoft Agent Framework, and how does it relate to AutoGen and Semantic Kernel? A: It is the successor to both, announced late 2025 by the same teams. AutoGen brought the simple conversational agent and multi-agent-team abstractions plus its v0.4 actor runtime; Semantic Kernel brought the enterprise features — typed functions, session/thread state, middleware, telemetry, and .NET support. MSAF merges them and adds what neither had: a graph-based Workflow engine with type-safe routing, checkpointing, and human-in-the-loop. AutoGen and SK are now lineage; MSAF is where new work lands, across .NET, Python, and Go.

Q: When would you use an AutoGen-style team vs an MSAF Workflow? A: A team (open-ended group chat with speaker selection + termination) when the collaboration structure is genuinely emergent and you can't pre-draw the path — exploratory, creative, or under-specified tasks. A Workflow (explicit graph of executors + typed edges) when the structure is knowable, which per Phase 00 is most of the time: you get determinism, type safety, checkpointing, HITL, and auditability. MSAF's own guidance is "if you can write a function, do that" — prefer the explicit workflow; reserve the open-ended agent for the sub-tasks that truly need runtime flexibility.

Q: What changed in AutoGen v0.4? A: A ground-up rewrite from a monolithic conversation loop to an actor/event-driven runtime. autogen-core is the asynchronous message-passing foundation where agents are isolated actors communicating by events (distributable, observable, replayable); autogen-agentchat is the ergonomic team layer (AssistantAgent, RoundRobinGroupChat, SelectorGroupChat, termination conditions) on top; autogen-ext holds model clients and integrations. The point is that a framework is a thin layer over a message-passing runtime — the same substrate MSAF inherits and LangGraph independently uses.

Q: Explain type-safe edges. Why not just check types inside the handler? A: An MSAF edge declares the message type it carries; when the edge fires with a mismatched payload it raises a WorkflowTypeError at the wiring, before the wrong-shaped message reaches a handler that assumed something else — the most common multi-agent bug, caught at the boundary. Checking inside every handler is defensive and scattered; the edge contract is declarative and central, and it also documents the graph. Crucially, the type is a contract that errors, separate from the edge's condition, which is control-flow routing — conflating them would turn a type bug into a silently dropped message.

Q: How do checkpointing and human-in-the-loop relate in MSAF? A: They are the same mechanism. The Workflow runs in super-steps; a checkpoint is the message queue plus shared state captured at a super-step boundary. A RequestInfoExecutor (HITL node) pauses the workflow by emitting a request and checkpointing; the run returns paused, the app gets the human's answer out of band (possibly hours later), and resume(checkpoint, response) injects the answer as that node's output and continues. "Pause for a human" and "pause for a crash" are one code path — which is why resumability is a headline enterprise feature and why re-running is both wasteful and dangerous (a completed step may have already sent an email or moved money).

Q: Walk me through the orchestration patterns. A: Sequential — a fixed pipeline, each output feeds the next; cheapest and most auditable. Concurrent — the same task fans out to N independent agents, an aggregator fans the results back in; independence permits parallelism. Handoff — an agent transfers control to a named other agent at runtime; validated target, bounded chain. Group-chat — an AutoGen team; emergent, least predictable. Magentic — a manager keeps a progress ledger, assigns subtasks to specialists, observes, and synthesizes, bounded by rounds; it subsumes the others and is the flagship for open-ended multi-specialist work. Each is bounded (no infinite ping-pong) and, where it routes, validates its targets.

Q: MSAF or LangGraph? A: They're close cousins — both graph workflows on a super-step runtime with checkpointing and HITL, and the concepts map almost one-to-one. MSAF adds type-safe edges and a first-class agent/thread/session model and lives in the .NET + Python + Go / Azure world with Semantic Kernel's enterprise plumbing; LangGraph is Python/JS-first with the richest checkpointer/store ecosystem and deepest LangChain integration. In an Azure/.NET shop or an SK migration, MSAF; in a Python-native LangChain shop, LangGraph. I'd pick on ecosystem and the need for typed/governed control flow, not on raw capability, because the capability overlaps heavily.

Q: What is the magentic pattern's progress ledger, and why does it matter? A: It's the manager's explicit working memory (from Magentic-One): a task ledger of facts and plan plus a record of every completed (agent, subtask, result). The manager reasons over it each round to decide the next assignment or to synthesize. It matters because it makes progress observable — the manager can detect that it's stuck (no progress across rounds) and re-plan or reassign, rather than hoping a group chat converges. Explicit progress tracking is the reliability lever from Phase 00 applied to orchestration.


20. References

  • Microsoft Agent Framework — official docs. https://learn.microsoft.com/agent-framework/
  • Microsoft Agent Framework — GitHub. https://github.com/microsoft/agent-framework
  • AutoGen — docs (v0.4+, autogen-agentchat / autogen-core). https://microsoft.github.io/autogen/
  • AutoGen — GitHub. https://github.com/microsoft/autogen
  • AutoGen v0.4 announcement / rearchitecture blog (actor/event-driven runtime). https://microsoft.github.io/autogen/stable/user-guide/core-user-guide/index.html
  • Semantic Kernel — docs. https://learn.microsoft.com/semantic-kernel/
  • Magentic-One (Microsoft Research) — a generalist multi-agent system with an orchestrator + ledger. https://www.microsoft.com/research/article/magentic-one-a-generalist-multi-agent-system-for-solving-complex-tasks/
  • Magentic-One paper. https://arxiv.org/abs/2411.04468
  • Anthropic, Building Effective Agents — workflows vs agents, "least-agentic that works." https://www.anthropic.com/research/building-effective-agents
  • OpenAI Agents SDK — Agent + Runner + handoffs + guardrails. https://openai.github.io/openai-agents-python/
  • LangGraph — graph workflows with checkpointing + interrupts (Phase 18). https://langchain-ai.github.io/langgraph/
  • CrewAI — role/task-based multi-agent orchestration. https://docs.crewai.com/
  • Model Context Protocol — the tool/resource interop standard (Phase 03). https://modelcontextprotocol.io/
  • Pregel: A System for Large-Scale Graph Processing (Malewicz et al., 2010) — the super-step / BSP model underneath both MSAF Workflows and LangGraph. https://research.google/pubs/pub37252/