| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- """
- Custom SendMessage Tool with Context Example
- Demonstrates how to configure agent-to-agent communication via `communication_flows`
- using a custom SendMessage tool (with an optional agency-level fallback).
- Run with: python examples/custom_send_message.py
- """
- import asyncio
- import logging
- import os
- import sys
- from pydantic import Field
- # Path setup so the example can be run standalone
- examples_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
- sys.path.insert(0, os.path.join(examples_root, "src"))
- sys.path.insert(0, examples_root)
- from agency_swarm import Agency, Agent, ModelSettings, function_tool # noqa: E402
- from agency_swarm.tools.send_message import Handoff, SendMessage # noqa: E402
- from examples.utils import print_highlighted_send_message_args # noqa: E402
- # Setup logging
- logging.basicConfig(level=logging.WARNING)
- logging.getLogger("agency_swarm").setLevel(
- logging.DEBUG if os.getenv("DEBUG_LOGS", "False").lower() == "true" else logging.WARNING
- )
- # Custom SendMessage tool that adds key moments and decisions to the message
- class SendMessageWithContext(SendMessage):
- """SendMessage with key moments and decisions tracking."""
- tool_name = "send_message_with_context"
- key_moments: str = Field(
- description=(
- "Document critical moments and decision points from the current conversation "
- "that the recipient agent needs to understand. Include context about what "
- "has been decided or prioritized that will guide the recipient's tool selection "
- "and task execution. For example: 'User decided to prioritize performance over cost', "
- "'Analysis focus shifted to Q4 optimization', etc."
- )
- )
- decisions: str = Field(
- description=(
- "Summarize the specific decisions made that will directly impact which tools "
- "or approaches the recipient agent should use. Be explicit about choices that "
- "narrow down the scope of work. For example: 'Prioritized performance analysis "
- "over cost reduction', 'Selected React over Vue for frontend', etc. This helps "
- "the recipient agent choose the most appropriate tools and approach."
- )
- )
- # Define tools for testing
- @function_tool
- def analyze_costs() -> str:
- """Analyze cost reduction and budget optimization opportunities."""
- return "Cost analysis complete. $45,000 annual savings identified."
- @function_tool
- def analyze_performance() -> str:
- """Analyze system performance and optimization opportunities."""
- return "Performance analysis complete. 23% efficiency gain possible."
- # Coordinator with custom SendMessage
- coordinator = Agent(
- name="Coordinator",
- description="Project coordinator who delegates analysis tasks",
- instructions=(
- "Your name is Coordinator agent."
- "You coordinate analysis work. When delegating tasks, make clear decisions about "
- "the focus area and approach needed. "
- "CRITICAL: When you receive responses from specialists, you MUST include their "
- "complete, word-for-word response text in your final output. Do not summarize, "
- "paraphrase, or omit any details from their responses."
- ),
- model_settings=ModelSettings(temperature=0.0),
- )
- # Specialist with both analysis tools
- specialist = Agent(
- name="Specialist",
- description="Specialist agent who performs performance or cost analysis",
- instructions=(
- "Your name is Specialist agent."
- "You perform analysis tasks using the appropriate tools. "
- "CRITICAL: After running any tool, you MUST copy the EXACT tool output "
- "into your response word-for-word, including any SECRET strings. "
- "Do not paraphrase, summarize, or rewrite the tool output. "
- "Include the complete raw result in your response."
- ),
- tools=[analyze_costs, analyze_performance],
- model_settings=ModelSettings(temperature=0.0),
- )
- agency = Agency(
- coordinator,
- specialist,
- communication_flows=[
- (coordinator > specialist, SendMessageWithContext),
- (specialist > coordinator, Handoff),
- ],
- shared_instructions="Use key decisions to guide analysis tool selection.",
- )
- # If you want a default communication tool for flows without explicit overrides, set
- # send_message_tool_class on the Agency.
- # agency = Agency(
- # coordinator,
- # specialist,
- # communication_flows=[coordinator > specialist, specialist > coordinator],
- # shared_instructions="Use key decisions to guide analysis tool selection.",
- # send_message_tool_class=SendMessageWithContext,
- # )
- # Helper function to visualize send message arguments
- def print_send_message_args(agency, agent_name: str) -> None:
- print_highlighted_send_message_args(agency, agent_name=agent_name)
- async def main():
- """Demonstrate key decisions being passed via custom SendMessage."""
- print("\nSendMessageWithContext Key Decisions Demo")
- # Turn 1: Initial discussion
- print("\n--- Turn 1: Send Message tool usage ---")
- initial_message = "Our Q4 operations need optimization. I want to focus on cost reduction."
- print(f"User: {initial_message}")
- response1 = await agency.get_response(message=initial_message)
- print(f"Coordinator: {response1.final_output}")
- print("\nSend Message arguments:")
- print_send_message_args(agency, "Coordinator")
- # Turn 2: Decision and delegation with random choice
- print("\n--- Turn 2: Handoff usage ---")
- delegate_message = "I've decided to prioritize performance analysis for Q4. Use the corresponding tool and transfer chat to the coordinator."
- print(f"Sending message to \033[32m{specialist.name}\033[0m: {delegate_message}")
- response2 = await agency.get_response(message=delegate_message, recipient_agent=specialist)
- print(f"\033[32m{response2.last_agent.name}\033[0m responded with: {response2.final_output}")
- print(
- "\n --- Key Takeaways: ---\n"
- "1. Coordinator agent's send message arguments include custom fields (SendMessageWithContext).\n"
- "2. The 2nd turn message is addressed to Specialist, but the final response is from Coordinator (handoff).\n"
- )
- if __name__ == "__main__":
- asyncio.run(main())
|