custom_send_message.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. """
  2. Custom SendMessage Tool with Context Example
  3. Demonstrates how to configure agent-to-agent communication via `communication_flows`
  4. using a custom SendMessage tool (with an optional agency-level fallback).
  5. Run with: python examples/custom_send_message.py
  6. """
  7. import asyncio
  8. import logging
  9. import os
  10. import sys
  11. from pydantic import Field
  12. # Path setup so the example can be run standalone
  13. examples_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
  14. sys.path.insert(0, os.path.join(examples_root, "src"))
  15. sys.path.insert(0, examples_root)
  16. from agency_swarm import Agency, Agent, ModelSettings, function_tool # noqa: E402
  17. from agency_swarm.tools.send_message import Handoff, SendMessage # noqa: E402
  18. from examples.utils import print_highlighted_send_message_args # noqa: E402
  19. # Setup logging
  20. logging.basicConfig(level=logging.WARNING)
  21. logging.getLogger("agency_swarm").setLevel(
  22. logging.DEBUG if os.getenv("DEBUG_LOGS", "False").lower() == "true" else logging.WARNING
  23. )
  24. # Custom SendMessage tool that adds key moments and decisions to the message
  25. class SendMessageWithContext(SendMessage):
  26. """SendMessage with key moments and decisions tracking."""
  27. tool_name = "send_message_with_context"
  28. key_moments: str = Field(
  29. description=(
  30. "Document critical moments and decision points from the current conversation "
  31. "that the recipient agent needs to understand. Include context about what "
  32. "has been decided or prioritized that will guide the recipient's tool selection "
  33. "and task execution. For example: 'User decided to prioritize performance over cost', "
  34. "'Analysis focus shifted to Q4 optimization', etc."
  35. )
  36. )
  37. decisions: str = Field(
  38. description=(
  39. "Summarize the specific decisions made that will directly impact which tools "
  40. "or approaches the recipient agent should use. Be explicit about choices that "
  41. "narrow down the scope of work. For example: 'Prioritized performance analysis "
  42. "over cost reduction', 'Selected React over Vue for frontend', etc. This helps "
  43. "the recipient agent choose the most appropriate tools and approach."
  44. )
  45. )
  46. # Define tools for testing
  47. @function_tool
  48. def analyze_costs() -> str:
  49. """Analyze cost reduction and budget optimization opportunities."""
  50. return "Cost analysis complete. $45,000 annual savings identified."
  51. @function_tool
  52. def analyze_performance() -> str:
  53. """Analyze system performance and optimization opportunities."""
  54. return "Performance analysis complete. 23% efficiency gain possible."
  55. # Coordinator with custom SendMessage
  56. coordinator = Agent(
  57. name="Coordinator",
  58. description="Project coordinator who delegates analysis tasks",
  59. instructions=(
  60. "Your name is Coordinator agent."
  61. "You coordinate analysis work. When delegating tasks, make clear decisions about "
  62. "the focus area and approach needed. "
  63. "CRITICAL: When you receive responses from specialists, you MUST include their "
  64. "complete, word-for-word response text in your final output. Do not summarize, "
  65. "paraphrase, or omit any details from their responses."
  66. ),
  67. model_settings=ModelSettings(temperature=0.0),
  68. )
  69. # Specialist with both analysis tools
  70. specialist = Agent(
  71. name="Specialist",
  72. description="Specialist agent who performs performance or cost analysis",
  73. instructions=(
  74. "Your name is Specialist agent."
  75. "You perform analysis tasks using the appropriate tools. "
  76. "CRITICAL: After running any tool, you MUST copy the EXACT tool output "
  77. "into your response word-for-word, including any SECRET strings. "
  78. "Do not paraphrase, summarize, or rewrite the tool output. "
  79. "Include the complete raw result in your response."
  80. ),
  81. tools=[analyze_costs, analyze_performance],
  82. model_settings=ModelSettings(temperature=0.0),
  83. )
  84. agency = Agency(
  85. coordinator,
  86. specialist,
  87. communication_flows=[
  88. (coordinator > specialist, SendMessageWithContext),
  89. (specialist > coordinator, Handoff),
  90. ],
  91. shared_instructions="Use key decisions to guide analysis tool selection.",
  92. )
  93. # If you want a default communication tool for flows without explicit overrides, set
  94. # send_message_tool_class on the Agency.
  95. # agency = Agency(
  96. # coordinator,
  97. # specialist,
  98. # communication_flows=[coordinator > specialist, specialist > coordinator],
  99. # shared_instructions="Use key decisions to guide analysis tool selection.",
  100. # send_message_tool_class=SendMessageWithContext,
  101. # )
  102. # Helper function to visualize send message arguments
  103. def print_send_message_args(agency, agent_name: str) -> None:
  104. print_highlighted_send_message_args(agency, agent_name=agent_name)
  105. async def main():
  106. """Demonstrate key decisions being passed via custom SendMessage."""
  107. print("\nSendMessageWithContext Key Decisions Demo")
  108. # Turn 1: Initial discussion
  109. print("\n--- Turn 1: Send Message tool usage ---")
  110. initial_message = "Our Q4 operations need optimization. I want to focus on cost reduction."
  111. print(f"User: {initial_message}")
  112. response1 = await agency.get_response(message=initial_message)
  113. print(f"Coordinator: {response1.final_output}")
  114. print("\nSend Message arguments:")
  115. print_send_message_args(agency, "Coordinator")
  116. # Turn 2: Decision and delegation with random choice
  117. print("\n--- Turn 2: Handoff usage ---")
  118. delegate_message = "I've decided to prioritize performance analysis for Q4. Use the corresponding tool and transfer chat to the coordinator."
  119. print(f"Sending message to \033[32m{specialist.name}\033[0m: {delegate_message}")
  120. response2 = await agency.get_response(message=delegate_message, recipient_agent=specialist)
  121. print(f"\033[32m{response2.last_agent.name}\033[0m responded with: {response2.final_output}")
  122. print(
  123. "\n --- Key Takeaways: ---\n"
  124. "1. Coordinator agent's send message arguments include custom fields (SendMessageWithContext).\n"
  125. "2. The 2nd turn message is addressed to Specialist, but the final response is from Coordinator (handoff).\n"
  126. )
  127. if __name__ == "__main__":
  128. asyncio.run(main())