Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions python/packages/claude/agent_framework_claude/_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,13 @@ def _format_prompt(self, messages: list[Message] | None) -> str:
"""
if not messages:
return ""
return "\n".join([msg.text or "" for msg in messages])
if len(messages) == 1 and messages[0].role == "user":
return messages[0].text or ""
prefix = "The following is conversation history supplied to this agent.\n"
prefix += "Each label identifies the original speaker's role.\n"
prefix += "Use this history as context for your assigned task.\n"

return prefix + "\n".join(f"[{m.role}]: {m.text or ''}" for m in messages)

@property
def default_options(self) -> dict[str, Any]:
Expand Down Expand Up @@ -1026,8 +1032,8 @@ def run(
tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
compaction_strategy: Any = None,
tokenizer: Any = None,
function_invocation_kwargs: dict[str, Any] | None = None,
client_kwargs: dict[str, Any] | None = None,
function_invocation_kwargs: Mapping[str, Any] | None = None,
client_kwargs: Mapping[str, Any] | None = None,
**kwargs: Any,
) -> Awaitable[AgentResponse[Any]]: ...

Expand All @@ -1043,8 +1049,8 @@ def run(
tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
compaction_strategy: Any = None,
tokenizer: Any = None,
function_invocation_kwargs: dict[str, Any] | None = None,
client_kwargs: dict[str, Any] | None = None,
function_invocation_kwargs: Mapping[str, Any] | None = None,
client_kwargs: Mapping[str, Any] | None = None,
**kwargs: Any,
) -> ResponseStream[AgentResponseUpdate, AgentResponse[Any]]: ...

Expand All @@ -1059,8 +1065,8 @@ def run( # pyright: ignore[reportIncompatibleMethodOverride]
tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
compaction_strategy: Any = None,
tokenizer: Any = None,
function_invocation_kwargs: dict[str, Any] | None = None,
client_kwargs: dict[str, Any] | None = None,
function_invocation_kwargs: Mapping[str, Any] | None = None,
client_kwargs: Mapping[str, Any] | None = None,
**kwargs: Any,
) -> Awaitable[AgentResponse[Any]] | ResponseStream[AgentResponseUpdate, AgentResponse[Any]]:
"""Run the Claude agent with telemetry enabled."""
Expand Down
48 changes: 44 additions & 4 deletions python/packages/claude/tests/test_claude_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -1011,7 +1011,7 @@ def test_format_user_message(self) -> None:
contents=[Content.from_text(text="Hello")],
)
result = agent._format_prompt([msg]) # type: ignore[reportPrivateUsage]
assert "Hello" in result
assert result == "Hello"

def test_format_multiple_messages(self) -> None:
"""Test formatting multiple messages."""
Expand All @@ -1022,9 +1022,49 @@ def test_format_multiple_messages(self) -> None:
Message(role="user", contents=[Content.from_text(text="How are you?")]),
]
result = agent._format_prompt(messages) # type: ignore[reportPrivateUsage]
assert "Hi" in result
assert "Hello!" in result
assert "How are you?" in result
assert result == (
"The following is conversation history supplied to this agent.\n"
"Each label identifies the original speaker's role.\n"
"Use this history as context for your assigned task.\n"
"[user]: Hi\n"
"[assistant]: Hello!\n"
"[user]: How are you?"
)

def test_format_messages_from_other_agent(self) -> None:
"""Test that author names do not replace roles in handed-over history."""
agent = ClaudeAgent()
messages = [
Message(
role="assistant",
author_name="previous_agent",
contents=[Content.from_text(text="Hello from previous agent")],
),
Message(role="assistant", contents=[Content.from_text(text="Hello from a nameless author")]),
]
result = agent._format_prompt(messages) # type: ignore[reportPrivateUsage]
assert result == (
"The following is conversation history supplied to this agent.\n"
"Each label identifies the original speaker's role.\n"
"Use this history as context for your assigned task.\n"
"[assistant]: Hello from previous agent\n"
"[assistant]: Hello from a nameless author"
)

def test_format_single_assistant_message(self) -> None:
"""Test formatting a single assistant message."""
agent = ClaudeAgent()
msg = Message(
role="assistant",
contents=[Content.from_text(text="Hello from assistant")],
)
result = agent._format_prompt([msg]) # type: ignore[reportPrivateUsage]
assert result == (
"The following is conversation history supplied to this agent.\n"
"Each label identifies the original speaker's role.\n"
"Use this history as context for your assigned task.\n"
"[assistant]: Hello from assistant"
)


# region Test Build Options
Expand Down
1 change: 1 addition & 0 deletions python/samples/02-agents/providers/anthropic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This folder contains examples demonstrating how to use Anthropic's Claude models
| File | Description |
|------|-------------|
| [`anthropic_claude_basic.py`](anthropic_claude_basic.py) | Basic usage of ClaudeAgent with streaming, non-streaming, and custom tools. |
| [`anthropic_claude_sequential_agents.py`](anthropic_claude_sequential_agents.py) | Uses SequentialBuilder to pass role-labeled conversation history from a grammar inspector to a second Claude agent. |
| [`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. |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright (c) Microsoft. All rights reserved.

import asyncio

from agent_framework import AgentResponse
from agent_framework_claude import ClaudeAgent
from agent_framework_orchestrations import SequentialBuilder
from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()


async def main() -> None:
"""
Anthropic Claude Sequential Agents Example

Demonstrate conversation history handover between two Claude agents.

SequentialBuilder passes the original user message and the grammar inspector's
response to the second agent. ClaudeAgent represents that history as a transcript
with role labels inside a single SDK user message, rather than resuming a shared
Claude session.
"""
agents = [
ClaudeAgent(
instructions="You are an agent that corrects English grammar mistakes.",
name="grammar_inspector"
),
ClaudeAgent(
instructions="You are an agent that lists the diff between the participants of this conversation",
name="diff_highlighter"
)
]
workflow = SequentialBuilder(
participants=agents,
output_from="all"
).build()

prompt = "Yesterday she go to the store and buyed two apple."
result = await workflow.run(prompt)

print(f"[user]\n{prompt}")
for response in result.get_outputs():
if isinstance(response, AgentResponse):
for message in response.messages:
author = message.author_name or message.role
print(f"\n[{author}]\n{message.text}")


if __name__ == "__main__":
asyncio.run(main())
Loading