* Python: Enhance Azure AI Search citations with document URLs in Foundry V2 (Responses API) Override _parse_response_from_openai and _parse_chunk_from_openai in RawAzureAIClient to extract get_urls from azure_ai_search_call_output items and enrich url_citation annotations with document-specific URLs. - Non-streaming: first pass collects get_urls, post-processes annotations - Streaming: captures search output state, enriches url_citation events (also handles url_citation annotation type not handled by base class) - Updated V2 sample to demonstrate citation URL extraction - Added 14 unit tests covering extraction, enrichment, and edge cases Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: rework search citation enrichment to override _inner_get_response - Remove all direct openai/pydantic imports from _client.py - Override _inner_get_response instead of _parse_response_from_openai/_parse_chunk_from_openai - Use closure-local state for streaming instead of instance-level _streaming_search_get_urls - Add _build_url_citation_content helper for streaming url_citation handling - Fix mypy errors by using str(value or '') for Annotation TypedDict fields - Fix docstring to say 'citation' instead of 'url_citation' - Update tests to match new approach Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: handle streaming search citations from output_item.done events The azure_ai_search_call_output item only has populated output data (including get_urls) in the response.output_item.done event, not in the response.output_item.added event. Also removed the search_get_urls guard on url_citation handling so annotations are always produced even if get_urls haven't been captured yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * addressed comments * refactor: address PR review - eliminate type: ignore[assignment] pattern Call super()._inner_get_response() independently in each branch instead of once at the top with union type reassignment. Non-streaming uses two-arg super() in the closure; streaming uses cast() for type narrowing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: remove defensive patterns per PR review - Replace all getattr() with direct attribute access - Remove cast() for streaming branch, use type: ignore[assignment] - Simplify _build_url_citation_content to use dict access directly - Simplify _extract_azure_search_urls to use item.type/item.output - Handle empty list output from streaming 'added' events - Update tests to match actual runtime types (objects, not dicts) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * mypy fix * small fixes --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Python Samples
This directory contains samples demonstrating the capabilities of Microsoft Agent Framework for Python.
Structure
| Folder | Description |
|---|---|
01-get-started/ |
Progressive tutorial: hello agent → hosting |
02-agents/ |
Deep-dive by concept: tools, middleware, providers, orchestrations |
03-workflows/ |
Workflow patterns: sequential, concurrent, state, declarative |
04-hosting/ |
Deployment: Azure Functions, Durable Tasks, A2A |
05-end-to-end/ |
Full applications, evaluation, demos |
Getting Started
Start with 01-get-started/ and work through the numbered files:
- 01_hello_agent.py — Create and run your first agent
- 02_add_tools.py — Add function tools with
@tool - 03_multi_turn.py — Multi-turn conversations with
AgentThread - 04_memory.py — Agent memory with
ContextProvider - 05_first_workflow.py — Build a workflow with executors and edges
- 06_host_your_agent.py — Host your agent via A2A
Prerequisites
pip install agent-framework --pre
Environment Variables
Samples call load_dotenv() to automatically load environment variables from a .env file in the python/ directory. This is a convenience for local development and testing.
For local development, set up your environment using any of these methods:
Option 1: Using a .env file (recommended for local development):
- Copy
.env.exampleto.envin thepython/directory:cp .env.example .env - Edit
.envand set your values (API keys, endpoints, etc.)
Option 2: Export environment variables directly:
export AZURE_AI_PROJECT_ENDPOINT="your-foundry-project-endpoint"
export AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME="gpt-4o"
Option 3: Using env_file_path parameter (for per-client configuration):
All client classes (e.g., OpenAIChatClient, AzureOpenAIResponsesClient) support an env_file_path parameter to load environment variables from a specific file:
from agent_framework.openai import OpenAIChatClient
# Load from a custom .env file
client = OpenAIChatClient(env_file_path="path/to/custom.env")
This allows different clients to use different configuration files if needed.
For the getting-started samples, you'll need at minimum:
AZURE_AI_PROJECT_ENDPOINT="your-foundry-project-endpoint"
AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME="gpt-4o"
Note for production: In production environments, set environment variables through your deployment platform (e.g., Azure App Settings, Kubernetes ConfigMaps/Secrets) rather than using .env files. The load_dotenv() call in samples will have no effect when a .env file is not present, allowing environment variables to be loaded from the system.
For Azure authentication, run az login before running samples.
Note on XML tags
Some sample files include XML-style snippet tags (for example <snippet_name> and </snippet_name>). These are used by our documentation tooling and can be ignored or removed when you use the samples outside this repository.
Additional Resources
- Agent Framework Documentation
- AGENTS.md — Structure documentation for maintainers
- SAMPLE_GUIDELINES.md — Coding conventions for samples