mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Add BaseAgent implementation for Claude Agent SDK (#3509)
* Added ClaudeAgent implementation * Updated streaming logic * Small updates * Small update * Fixes * Small fix * Naming improvements * Updated imports * Addressed comments * Updated package versions
This commit is contained in:
committed by
GitHub
Unverified
parent
0fcf075ea7
commit
8b475afe17
@@ -2,7 +2,7 @@
|
||||
|
||||
This folder contains examples demonstrating how to use Anthropic's Claude models with the Agent Framework.
|
||||
|
||||
## Examples
|
||||
## Anthropic Client Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
@@ -11,14 +11,36 @@ This folder contains examples demonstrating how to use Anthropic's Claude models
|
||||
| [`anthropic_skills.py`](anthropic_skills.py) | Illustrates how to use Anthropic-managed Skills with an agent, including the Code Interpreter tool and file generation and saving. |
|
||||
| [`anthropic_foundry.py`](anthropic_foundry.py) | Example of using Foundry's Anthropic integration with the Agent Framework. |
|
||||
|
||||
## Claude Agent Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`anthropic_claude_basic.py`](anthropic_claude_basic.py) | Basic usage of ClaudeAgent with streaming, non-streaming, and custom tools. |
|
||||
| [`anthropic_claude_with_tools.py`](anthropic_claude_with_tools.py) | Using built-in tools (Read, Glob, Grep, etc.). |
|
||||
| [`anthropic_claude_with_shell.py`](anthropic_claude_with_shell.py) | Shell command execution with interactive permission handling. |
|
||||
| [`anthropic_claude_with_multiple_permissions.py`](anthropic_claude_with_multiple_permissions.py) | Combining multiple tools (Bash, Read, Write) with permission prompts. |
|
||||
| [`anthropic_claude_with_url.py`](anthropic_claude_with_url.py) | Fetching and processing web content with WebFetch. |
|
||||
| [`anthropic_claude_with_mcp.py`](anthropic_claude_with_mcp.py) | Local (stdio) and remote (HTTP) MCP server configuration. |
|
||||
| [`anthropic_claude_with_session.py`](anthropic_claude_with_session.py) | Session management, persistence, and resumption. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Set the following environment variables before running the examples:
|
||||
### Anthropic Client
|
||||
|
||||
- `ANTHROPIC_API_KEY`: Your Anthropic API key (get one from [Anthropic Console](https://console.anthropic.com/))
|
||||
- `ANTHROPIC_CHAT_MODEL_ID`: The Claude model to use (e.g., `claude-haiku-4-5`, `claude-sonnet-4-5-20250929`)
|
||||
|
||||
Or, for Foundry:
|
||||
### Foundry
|
||||
|
||||
- `ANTHROPIC_FOUNDRY_API_KEY`: Your Foundry Anthropic API key
|
||||
- `ANTHROPIC_FOUNDRY_ENDPOINT`: The endpoint URL for your Foundry Anthropic resource
|
||||
- `ANTHROPIC_CHAT_MODEL_ID`: The Claude model to use in Foundry (e.g., `claude-haiku-4-5`)
|
||||
|
||||
### Claude Agent
|
||||
|
||||
- `CLAUDE_AGENT_CLI_PATH`: Path to the Claude Code CLI executable
|
||||
- `CLAUDE_AGENT_MODEL`: Model to use (sonnet, opus, haiku)
|
||||
- `CLAUDE_AGENT_CWD`: Working directory for Claude CLI
|
||||
- `CLAUDE_AGENT_PERMISSION_MODE`: Permission mode (default, acceptEdits, plan, bypassPermissions)
|
||||
- `CLAUDE_AGENT_MAX_TURNS`: Maximum number of conversation turns
|
||||
- `CLAUDE_AGENT_MAX_BUDGET_USD`: Maximum budget in USD
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent Basic Example
|
||||
|
||||
This sample demonstrates using ClaudeAgent for basic interactions
|
||||
with Claude Agent SDK.
|
||||
|
||||
Prerequisites:
|
||||
- Claude Code CLI must be installed and configured
|
||||
- pip install agent-framework-claude
|
||||
|
||||
Environment variables:
|
||||
- CLAUDE_AGENT_MODEL: Model to use (sonnet, opus, haiku)
|
||||
- CLAUDE_AGENT_PERMISSION_MODE: Permission mode (default, acceptEdits, bypassPermissions)
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import tool
|
||||
from agent_framework_claude import ClaudeAgent
|
||||
|
||||
|
||||
@tool
|
||||
def get_weather(location: Annotated[str, "The city name"]) -> str:
|
||||
"""Get the current weather for a location."""
|
||||
return f"The weather in {location} is sunny with a high of 25C."
|
||||
|
||||
|
||||
async def non_streaming_example() -> None:
|
||||
"""Example of non-streaming response."""
|
||||
print("=== Non-streaming Example ===")
|
||||
|
||||
agent = ClaudeAgent(
|
||||
name="BasicAgent",
|
||||
instructions="You are a helpful assistant. Keep responses concise.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "What's the weather in Seattle?"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result.text}\n")
|
||||
|
||||
|
||||
async def streaming_example() -> None:
|
||||
"""Example of streaming response."""
|
||||
print("=== Streaming Example ===")
|
||||
|
||||
agent = ClaudeAgent(
|
||||
name="StreamingAgent",
|
||||
instructions="You are a helpful assistant.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "What's the weather in Paris?"
|
||||
print(f"User: {query}")
|
||||
print("Agent: ", end="", flush=True)
|
||||
async for chunk in agent.run_stream(query):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="", flush=True)
|
||||
print("\n")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude Agent Basic Example ===\n")
|
||||
|
||||
await non_streaming_example()
|
||||
await streaming_example()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,81 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent with MCP Servers
|
||||
|
||||
This sample demonstrates how to configure MCP (Model Context Protocol) servers
|
||||
with ClaudeAgent. It shows both local (stdio) and remote (HTTP) server
|
||||
configurations, giving the agent access to external tools and data sources.
|
||||
|
||||
Supported MCP server types:
|
||||
- "stdio": Local process-based server
|
||||
- "http": Remote HTTP server
|
||||
- "sse": Remote SSE (Server-Sent Events) server
|
||||
|
||||
SECURITY NOTE: MCP servers can expose powerful capabilities. Only configure
|
||||
servers you trust. Use permission handlers to control what actions are allowed.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from agent_framework_claude import ClaudeAgent
|
||||
from claude_agent_sdk import PermissionResultAllow, PermissionResultDeny
|
||||
|
||||
|
||||
async def prompt_permission(
|
||||
tool_name: str,
|
||||
tool_input: dict[str, Any],
|
||||
context: object,
|
||||
) -> PermissionResultAllow | PermissionResultDeny:
|
||||
"""Permission handler that prompts the user for approval."""
|
||||
print(f"\n[Permission Request: {tool_name}]")
|
||||
|
||||
response = input("Approve? (y/n): ").strip().lower()
|
||||
if response in ("y", "yes"):
|
||||
return PermissionResultAllow()
|
||||
return PermissionResultDeny(message="Denied by user")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude Agent with MCP Servers ===\n")
|
||||
|
||||
# Configure both local and remote MCP servers
|
||||
mcp_servers: dict[str, Any] = {
|
||||
# Local stdio server: provides filesystem access tools
|
||||
"filesystem": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."],
|
||||
},
|
||||
# Remote HTTP server: Microsoft Learn documentation
|
||||
"microsoft-learn": {
|
||||
"type": "http",
|
||||
"url": "https://learn.microsoft.com/api/mcp",
|
||||
},
|
||||
}
|
||||
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful assistant with access to the local filesystem and Microsoft Learn.",
|
||||
default_options={
|
||||
"can_use_tool": prompt_permission,
|
||||
"mcp_servers": mcp_servers,
|
||||
},
|
||||
)
|
||||
|
||||
async with agent:
|
||||
# Query that exercises the local filesystem MCP server
|
||||
query1 = "List the first three files in the current directory"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent.run(query1)
|
||||
print(f"Agent: {result1.text}\n")
|
||||
|
||||
# Query that exercises the remote Microsoft Learn MCP server
|
||||
query2 = "Search Microsoft Learn for 'Azure Functions Python' and summarize the top result"
|
||||
print(f"User: {query2}")
|
||||
result2 = await agent.run(query2)
|
||||
print(f"Agent: {result2.text}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent with Multiple Permissions
|
||||
|
||||
This sample demonstrates how to enable multiple permission types with ClaudeAgent.
|
||||
By combining different tools and using a permission handler, the agent can perform
|
||||
complex tasks that require multiple capabilities.
|
||||
|
||||
Available built-in tools:
|
||||
- "Bash": Execute shell commands
|
||||
- "Read": Read files from the filesystem
|
||||
- "Write": Write files to the filesystem
|
||||
- "Edit": Edit existing files
|
||||
- "Glob": Search for files by pattern
|
||||
- "Grep": Search file contents
|
||||
|
||||
SECURITY NOTE: Only enable permissions that are necessary for your use case.
|
||||
More permissions mean more potential for unintended actions.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from agent_framework_claude import ClaudeAgent
|
||||
from claude_agent_sdk import PermissionResultAllow, PermissionResultDeny
|
||||
|
||||
|
||||
async def prompt_permission(
|
||||
tool_name: str,
|
||||
tool_input: dict[str, Any],
|
||||
context: object,
|
||||
) -> PermissionResultAllow | PermissionResultDeny:
|
||||
"""Permission handler that prompts the user for approval."""
|
||||
print(f"\n[Permission Request: {tool_name}]")
|
||||
|
||||
if "command" in tool_input:
|
||||
print(f" Command: {tool_input.get('command')}")
|
||||
if "file_path" in tool_input:
|
||||
print(f" Path: {tool_input.get('file_path')}")
|
||||
if "pattern" in tool_input:
|
||||
print(f" Pattern: {tool_input.get('pattern')}")
|
||||
|
||||
response = input("Approve? (y/n): ").strip().lower()
|
||||
if response in ("y", "yes"):
|
||||
return PermissionResultAllow()
|
||||
return PermissionResultDeny(message="Denied by user")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude Agent with Multiple Permissions ===\n")
|
||||
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful development assistant that can read, write files and run commands.",
|
||||
tools=["Bash", "Read", "Write", "Glob"],
|
||||
default_options={
|
||||
"can_use_tool": prompt_permission,
|
||||
},
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "List the first 3 Python files, then read the first one and create a summary in summary.txt"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result.text}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,145 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent with Session Management
|
||||
|
||||
This sample demonstrates session management with ClaudeAgent, showing
|
||||
persistent conversation capabilities. Sessions are automatically persisted
|
||||
by the Claude Code CLI.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import tool
|
||||
from agent_framework_claude import ClaudeAgent
|
||||
from pydantic import Field
|
||||
|
||||
|
||||
@tool
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="The location to get the weather for.")],
|
||||
) -> str:
|
||||
"""Get the weather for a given location."""
|
||||
conditions = ["sunny", "cloudy", "rainy", "stormy"]
|
||||
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
|
||||
|
||||
|
||||
async def example_with_automatic_session_creation() -> None:
|
||||
"""Each agent instance creates a new session."""
|
||||
print("=== Automatic Session Creation Example ===")
|
||||
|
||||
# First agent - first session
|
||||
agent1 = ClaudeAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent1:
|
||||
query1 = "What's the weather like in Seattle?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent1.run(query1)
|
||||
print(f"Agent: {result1.text}")
|
||||
|
||||
# Second agent - new session, no memory of previous conversation
|
||||
agent2 = ClaudeAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent2:
|
||||
query2 = "What was the last city I asked about?"
|
||||
print(f"\nUser: {query2}")
|
||||
result2 = await agent2.run(query2)
|
||||
print(f"Agent: {result2.text}")
|
||||
print("Note: Each agent instance creates a separate session, so the agent doesn't remember previous context.\n")
|
||||
|
||||
|
||||
async def example_with_session_persistence() -> None:
|
||||
"""Reuse session via thread object for multi-turn conversations."""
|
||||
print("=== Session Persistence Example ===")
|
||||
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent:
|
||||
# Create a thread to maintain conversation context
|
||||
thread = agent.get_new_thread()
|
||||
|
||||
# First query
|
||||
query1 = "What's the weather like in Tokyo?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent.run(query1, thread=thread)
|
||||
print(f"Agent: {result1.text}")
|
||||
|
||||
# Second query - using same thread maintains context
|
||||
query2 = "How about London?"
|
||||
print(f"\nUser: {query2}")
|
||||
result2 = await agent.run(query2, thread=thread)
|
||||
print(f"Agent: {result2.text}")
|
||||
|
||||
# Third query - agent should remember both previous cities
|
||||
query3 = "Which of the cities I asked about has better weather?"
|
||||
print(f"\nUser: {query3}")
|
||||
result3 = await agent.run(query3, thread=thread)
|
||||
print(f"Agent: {result3.text}")
|
||||
print("Note: The agent remembers context from previous messages in the same session.\n")
|
||||
|
||||
|
||||
async def example_with_existing_session_id() -> None:
|
||||
"""Resume session in new agent instance using service_thread_id."""
|
||||
print("=== Existing Session ID Example ===")
|
||||
|
||||
existing_session_id = None
|
||||
|
||||
# First agent instance - start a conversation
|
||||
agent1 = ClaudeAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent1:
|
||||
thread = agent1.get_new_thread()
|
||||
|
||||
query1 = "What's the weather in Paris?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent1.run(query1, thread=thread)
|
||||
print(f"Agent: {result1.text}")
|
||||
|
||||
# Capture the session ID for later use
|
||||
existing_session_id = thread.service_thread_id
|
||||
print(f"Session ID: {existing_session_id}")
|
||||
|
||||
if existing_session_id:
|
||||
print("\n--- Continuing with the same session ID in a new agent instance ---")
|
||||
|
||||
# Second agent instance - resume the conversation
|
||||
agent2 = ClaudeAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent2:
|
||||
# Create thread with existing session ID
|
||||
thread = agent2.get_new_thread(service_thread_id=existing_session_id)
|
||||
|
||||
query2 = "What was the last city I asked about?"
|
||||
print(f"User: {query2}")
|
||||
result2 = await agent2.run(query2, thread=thread)
|
||||
print(f"Agent: {result2.text}")
|
||||
print("Note: The agent continues the conversation using the session ID.\n")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude Agent Session Management Examples ===\n")
|
||||
|
||||
await example_with_automatic_session_creation()
|
||||
await example_with_session_persistence()
|
||||
await example_with_existing_session_id()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,57 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent with Shell Permissions
|
||||
|
||||
This sample demonstrates how to enable shell command execution with ClaudeAgent.
|
||||
By providing a permission handler via `can_use_tool`, the agent can execute
|
||||
shell commands to perform tasks like listing files, running scripts, or executing system commands.
|
||||
|
||||
SECURITY NOTE: Only enable shell permissions when you trust the agent's actions.
|
||||
Shell commands have full access to your system within the permissions of the running process.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from agent_framework_claude import ClaudeAgent
|
||||
from claude_agent_sdk import PermissionResultAllow, PermissionResultDeny
|
||||
|
||||
|
||||
async def prompt_permission(
|
||||
tool_name: str,
|
||||
tool_input: dict[str, Any],
|
||||
context: object,
|
||||
) -> PermissionResultAllow | PermissionResultDeny:
|
||||
"""Permission handler that prompts the user for approval."""
|
||||
print(f"\n[Permission Request: {tool_name}]")
|
||||
|
||||
if "command" in tool_input:
|
||||
print(f" Command: {tool_input.get('command')}")
|
||||
|
||||
response = input("Approve? (y/n): ").strip().lower()
|
||||
if response in ("y", "yes"):
|
||||
return PermissionResultAllow()
|
||||
return PermissionResultDeny(message="Denied by user")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude Agent with Shell Permissions ===\n")
|
||||
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful assistant that can execute shell commands.",
|
||||
tools=["Bash"],
|
||||
default_options={
|
||||
"can_use_tool": prompt_permission,
|
||||
},
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "List the first 3 Python files in the current directory"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result.text}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,40 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent with Built-in Tools
|
||||
|
||||
This sample demonstrates using ClaudeAgent with built-in tools for file operations.
|
||||
Built-in tools are specified as strings in the tools parameter.
|
||||
|
||||
Available built-in tools:
|
||||
- "Bash": Execute shell commands
|
||||
- "Read": Read files from the filesystem
|
||||
- "Write": Write files to the filesystem
|
||||
- "Edit": Edit existing files
|
||||
- "Glob": Search for files by pattern
|
||||
- "Grep": Search file contents
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework_claude import ClaudeAgent
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude Agent with Built-in Tools ===\n")
|
||||
|
||||
# Built-in tools can be specified as strings in the tools parameter
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful assistant that can read files.",
|
||||
tools=["Read", "Glob"],
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "List the first 3 Python files in the current directory"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result.text}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,38 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent with URL Fetching
|
||||
|
||||
This sample demonstrates how to enable URL fetching with ClaudeAgent.
|
||||
By enabling the WebFetch tool, the agent can fetch and process content from web URLs.
|
||||
|
||||
Available web tools:
|
||||
- "WebFetch": Fetch content from URLs
|
||||
- "WebSearch": Search the web
|
||||
|
||||
SECURITY NOTE: Only enable URL permissions when you trust the agent's actions.
|
||||
URL fetching allows the agent to access any URL accessible from your network.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework_claude import ClaudeAgent
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude Agent with URL Fetching ===\n")
|
||||
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful assistant that can fetch and summarize web content.",
|
||||
tools=["WebFetch"],
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "Fetch https://learn.microsoft.com/agent-framework/tutorials/quick-start and summarize its contents"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result.text}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user