Files
agent-framework/python/samples/03-workflows
T
Eduard van Valkenburg 67ce1baecf Python: fix reasoning model workflow handoff and history serialization (#4083)
* fix: strip function_call and text_reasoning from cross-agent workflow handoff

When a reasoning model (e.g. gpt-5-mini) runs as Agent 1 in a workflow, its
response includes text_reasoning items (with server-scoped IDs like rs_XXXX)
and function_call items. Forwarding these to Agent 2 in a fresh conversation
caused API errors because the reasoning/call IDs are scoped to the original
stored response context.

Changes:
- Strip 'function_call', 'text_reasoning', 'function_approval_request', and
  'function_approval_response' from handoff messages in _agent_executor.py
- Keep 'function_result' so the actual tool output content is preserved for
  the next agent's context
- Update unit tests to reflect that function_result messages survive handoff
  (messages grow from 2→3: user, tool(result), assistant(summary))
- Fix incorrect test assertions in test_function_invocation_stop_clears_*
  that assumed the client layer updates session.service_session_id
- Also fixed _extract_function_calls to search all messages with call_id
  deduplication, and the error-limit stop path to submit function_call_output
  items before halting (via tool_choice=none cleanup call)

Relates to: https://github.com/microsoft/agent-framework/issues/4047

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: reasoning model workflow handoff and history serialization

Fixes multiple related issues when using reasoning models (gpt-5-mini,
gpt-5.2) in multi-agent workflows that chain agents via from_response
or replay full conversation history via AgentExecutorRequest.

## Reasoning items always emitted on output_item.added

When a reasoning model produces encrypted or hidden reasoning (no
visible text), the Responses API still fires a reasoning output item
without any reasoning_text.delta events. Previously no text_reasoning
Content was emitted in that case, making it invisible to downstream
logic. Both the non-streaming (_parse_response_from_openai) and
streaming (output_item.added) paths now always emit at least one
text_reasoning Content — with empty text if no content is available —
so co-occurrence detection and serialization guards work reliably.

## Reasoning items only serialized when paired with a function_call

The Responses API only accepts reasoning items in input when they
directly preceded a function_call in the original response. Sending a
reasoning item that preceded a text response (no tool call) causes:
  "reasoning was provided without its required following item"
_prepare_message_for_openai now checks has_function_call per message
and skips text_reasoning serialization when there is no accompanying
function_call.

## summary field is an array, not an object

The reasoning item summary field sent to the Responses API must be an
array of objects ([{"type": "summary_text", "text": ...}]), not a
single object. Fixed _prepare_content_for_openai accordingly.

## service_session_id cleared when explicit history is provided

When a workflow coordinator replays a full conversation (including
function calls from a previous agent run) back to an executor via
AgentExecutorRequest or from_response, the executor's session still
held a service_session_id (previous_response_id) from the prior run.
The API then received the same function-call items twice — once from
previous_response_id (server-stored) and once from the explicit input —
causing: "Duplicate item found with id fc_...".

AgentExecutor.run (when should_respond=True) and from_response now
reset self._session.service_session_id = None before running so that
explicit input is the sole source of conversation context.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* small improvements in text reasoning

* refactor: add reset_service_session to AgentExecutorRequest for explicit history replay

Replace the implicit 'always clear service_session_id when should_respond=True'
with an explicit opt-in field on AgentExecutorRequest.

The old approach used should_respond=True as a proxy for 'full history replay',
but that conflates two distinct intents:
- Orchestrations group chat sends should_respond=True with an empty/single-message
  list (not a full replay) — unnecessarily clearing service_session_id.
- HITL / feedback coordinators send the full prior conversation and truly need
  a fresh service session ID to avoid duplicate-item API errors.

Changes:
- Add AgentExecutorRequest.reset_service_session: bool = False
- AgentExecutor.run only clears service_session_id when this flag is True
- AgentExecutor.from_response unchanged (always clears; always full conversation)
- Set reset_service_session=True in all full-history-replay call sites:
  agents_with_HITL.py, azure_chat_agents_tool_calls_with_feedback.py,
  autogen-migration round-robin coordinator, tau2 runner
- Update _FullHistoryReplayCoordinator test helper to pass the flag

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* comment update

* fixes from feedback

* fix test

* reverted changes to agent executor

* fix: remove reset_service_session from tau2 runner

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* two other reverts

* fix sample

---------

Co-authored-by: Giles Odigwe <79032838+giles17@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
67ce1baecf · 2026-02-19 21:02:20 +00:00
History
..

Workflows Getting Started Samples

Installation

Microsoft Agent Framework Workflows support ships with the core agent-framework or agent-framework-core package, so no extra installation step is required.

To install with visualization support:

pip install agent-framework[viz] --pre

To export visualization images you also need to install GraphViz.

Samples Overview

Foundational Concepts - Start Here

Begin with the _start-here folder in order. These three samples introduce the core ideas of executors, edges, agents in workflows, and streaming.

Sample File Concepts
Executors and Edges _start-here/step1_executors_and_edges.py Minimal workflow with basic executors and edges
Agents in a Workflow _start-here/step2_agents_in_a_workflow.py Introduces adding Agents as nodes; calling agents inside a workflow
Streaming (Basics) _start-here/step3_streaming.py Extends workflows with event streaming

Once comfortable with these, explore the rest of the samples below.


Samples Overview (by directory)

agents

Sample File Concepts
Azure Chat Agents (Streaming) agents/azure_chat_agents_streaming.py Add Azure Chat agents as edges and handle streaming events
Azure AI Agents (Streaming) agents/azure_ai_agents_streaming.py Add Azure AI agents as edges and handle streaming events
Azure AI Agents (Shared Thread) agents/azure_ai_agents_with_shared_session.py Share a common message session between multiple Azure AI agents in a workflow
Custom Agent Executors agents/custom_agent_executors.py Create executors to handle agent run methods
Workflow as Agent (Reflection Pattern) agents/workflow_as_agent_reflection_pattern.py Wrap a workflow so it can behave like an agent (reflection pattern)
Workflow as Agent + HITL agents/workflow_as_agent_human_in_the_loop.py Extend workflow-as-agent with human-in-the-loop capability
Workflow as Agent with Session agents/workflow_as_agent_with_session.py Use AgentSession to maintain conversation history across workflow-as-agent invocations
Workflow as Agent kwargs agents/workflow_as_agent_kwargs.py Pass custom context (data, user tokens) via kwargs through workflow.as_agent() to @ai_function tools

checkpoint

Sample File Concepts
Checkpoint & Resume checkpoint/checkpoint_with_resume.py Create checkpoints, inspect them, and resume execution
Checkpoint & HITL Resume checkpoint/checkpoint_with_human_in_the_loop.py Combine checkpointing with human approvals and resume pending HITL requests
Checkpointed Sub-Workflow checkpoint/sub_workflow_checkpoint.py Save and resume a sub-workflow that pauses for human approval
Handoff + Tool Approval Resume orchestrations/handoff_with_tool_approval_checkpoint_resume.py Handoff workflow that captures tool-call approvals in checkpoints and resumes with human decisions
Workflow as Agent Checkpoint checkpoint/workflow_as_agent_checkpoint.py Enable checkpointing when using workflow.as_agent() with checkpoint_storage parameter

composition

Sample File Concepts
Sub-Workflow (Basics) composition/sub_workflow_basics.py Wrap a workflow as an executor and orchestrate sub-workflows
Sub-Workflow: Request Interception composition/sub_workflow_request_interception.py Intercept and forward sub-workflow requests using @handler for SubWorkflowRequestMessage
Sub-Workflow: Parallel Requests composition/sub_workflow_parallel_requests.py Multiple specialized interceptors handling different request types from same sub-workflow
Sub-Workflow: kwargs Propagation composition/sub_workflow_kwargs.py Pass custom context (user tokens, config) from parent workflow through to sub-workflow agents

control-flow

Sample File Concepts
Sequential Executors control-flow/sequential_executors.py Sequential workflow with explicit executor setup
Sequential (Streaming) control-flow/sequential_streaming.py Stream events from a simple sequential run
Edge Condition control-flow/edge_condition.py Conditional routing based on agent classification
Switch-Case Edge Group control-flow/switch_case_edge_group.py Switch-case branching using classifier outputs
Multi-Selection Edge Group control-flow/multi_selection_edge_group.py Select one or many targets dynamically (subset fan-out)
Simple Loop control-flow/simple_loop.py Feedback loop where an agent judges ABOVE/BELOW/MATCHED
Workflow Cancellation control-flow/workflow_cancellation.py Cancel a running workflow using asyncio tasks

human-in-the-loop

Sample File Concepts
Human-In-The-Loop (Guessing Game) human-in-the-loop/guessing_game_with_human_input.py Interactive request/response prompts with a human via ctx.request_info()
Agents with Approval Requests in Workflows human-in-the-loop/agents_with_approval_requests.py Agents that create approval requests during workflow execution and wait for human approval to proceed
Agents with Declaration-Only Tools human-in-the-loop/agents_with_declaration_only_tools.py Workflow pauses when agent calls a client-side tool (func=None), caller supplies the result

Builder-oriented request-info samples are maintained in the orchestration sample set (sequential, concurrent, and group-chat builder variants).

tool-approval

Builder-based tool approval samples are maintained in the orchestration sample set.

observability

Sample File Concepts
Executor I/O Observation observability/executor_io_observation.py Observe executor input/output data via executor_invoked events (type='executor_invoked') and executor_completed events (type='executor_completed') without modifying executor code

For additional observability samples in Agent Framework, see the observability concept samples. The workflow observability sample demonstrates integrating observability into workflows.

orchestration

Orchestration-focused samples (Sequential, Concurrent, Handoff, GroupChat, Magentic), including builder-based workflow.as_agent(...) variants, are documented in the orchestrations directory.

parallelism

Sample File Concepts
Concurrent (Fan-out/Fan-in) parallelism/fan_out_fan_in_edges.py Dispatch to multiple executors and aggregate results
Aggregate Results of Different Types parallelism/aggregate_results_of_different_types.py Handle results of different types from multiple concurrent executors
Map-Reduce with Visualization parallelism/map_reduce_and_visualization.py Fan-out/fan-in pattern with diagram export

state-management

Sample File Concepts
State with Agents state-management/state_with_agents.py Store in state once and later reuse across agents
Workflow Kwargs (Custom Context) state-management/workflow_kwargs.py Pass custom context (data, user tokens) via kwargs to @tool tools

visualization

Sample File Concepts
Concurrent with Visualization visualization/concurrent_with_visualization.py Fan-out/fan-in workflow with diagram export

declarative

YAML-based declarative workflows allow you to define multi-agent orchestration patterns without writing Python code. See the declarative workflows README for more details on YAML workflow syntax and available actions.

Sample File Concepts
Conditional Workflow declarative/conditional_workflow/ Nested conditional branching based on user input
Customer Support declarative/customer_support/ Multi-agent customer support with routing
Deep Research declarative/deep_research/ Research workflow with planning, searching, and synthesis
Function Tools declarative/function_tools/ Invoking Python functions from declarative workflows
Human-in-Loop declarative/human_in_loop/ Interactive workflows that request user input
Marketing declarative/marketing/ Marketing content generation workflow
Simple Workflow declarative/simple_workflow/ Basic workflow with variable setting, conditionals, and loops
Student Teacher declarative/student_teacher/ Student-teacher interaction pattern

resources

Notes

  • Agent-based samples use provider SDKs (Azure/OpenAI, etc.). Ensure credentials are configured, or adapt agents accordingly.

Sequential orchestration uses a few small adapter nodes for plumbing:

  • "input-conversation" normalizes input to list[Message]
  • "to-conversation:" converts agent responses into the shared conversation
  • "complete" publishes the final output event (type='output') These may appear in event streams (executor_invoked/executor_completed). They're analogous to concurrents dispatcher and aggregator and can be ignored if you only care about agent activity.

Environment Variables

Workflow samples that use AzureOpenAIResponsesClient expect:

  • AZURE_AI_PROJECT_ENDPOINT (Azure AI Foundry Agent Service (V2) project endpoint)
  • AZURE_AI_MODEL_DEPLOYMENT_NAME (model deployment name)

These values are passed directly into the client constructor via os.getenv() in sample code.