handoffs.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. """
  2. Handoffs Example
  3. This example demonstrates a realistic handoff workflow using Handoff.
  4. - DevLead implements features and hands off security-sensitive work to SecurityEngineer (default reminder)
  5. - SecurityEngineer performs audits and hands off to ComplianceOfficer (reminder disabled)
  6. - ComplianceOfficer completes compliance checks and hands back to DevLead, triggering a custom reminder for DevLead
  7. Run with: python examples/handoffs.py
  8. Try: "Implement user authentication with 2FA and ensure it passes security and compliance."
  9. """
  10. import asyncio
  11. import logging
  12. import os
  13. import sys
  14. from utils import print_history
  15. # Path setup so the example can be run standalone
  16. sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src")))
  17. from agency_swarm import Agency, Agent, ModelSettings, function_tool
  18. from agency_swarm.tools.send_message import Handoff
  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. @function_tool
  25. def implement_feature(feature_name: str) -> str:
  26. """Implement a feature with basic scaffolding and docs."""
  27. return (
  28. f"Feature '{feature_name}' implemented with auth middleware, input validation, and error handling. "
  29. "Added developer docs and unit tests."
  30. )
  31. @function_tool
  32. def security_audit(component: str) -> str:
  33. """Perform a targeted security audit of the given component."""
  34. return f"Security audit for {component}: No critical findings. 1 medium (sanitize inputs), 1 low (log rotation)."
  35. @function_tool
  36. def vulnerability_scan(target: str) -> str:
  37. """Scan the target for known vulnerabilities and CVEs."""
  38. return f"Scan for {target}: 0 critical CVEs, 1 medium (dependency), 2 low (headers)."
  39. @function_tool
  40. def policy_check(area: str) -> str:
  41. """Check policy adherence for a given domain (e.g., auth, data retention)."""
  42. return f"Policy check for {area}: Meets GDPR data minimization; add DPA reference in docs."
  43. @function_tool
  44. def generate_compliance_report(scope: str) -> str:
  45. """Generate a compliance report for audit trail and sign-off."""
  46. return f"Compliance report generated for {scope}: Ready for sign-off; attach to release notes."
  47. # By default, to reduce hallucinations, when using Handoff, a reminder system message is added to the history.
  48. # You can adjust the reminder message per receiving agent via `Agent.handoff_reminder` or disable it entirely by subclassing.
  49. class NoReminderHandoff(Handoff):
  50. """Handoff with no reminder."""
  51. add_reminder = False # True by default
  52. dev_lead = Agent(
  53. name="DevLead",
  54. description="Leads development, implements features, and delegates security and compliance tasks.",
  55. instructions=(
  56. "You are the DevLead. Implement requested features using tools and provide technical context. "
  57. "When the task involves security concerns, hand off to SecurityEngineer using the transfer tool. "
  58. "Include all relevant details and acceptance criteria in the handoff message. "
  59. "Only use transfer tool once."
  60. ),
  61. tools=[implement_feature],
  62. # Reminder shown when DevLead receives a handoff (e.g., from ComplianceOfficer).
  63. # Default format: "Transfer completed. You are {recipient_agent_name}. Please continue the task."
  64. handoff_reminder="Compliance review is complete. Confirm deployment steps and attach audit artifacts.",
  65. model_settings=ModelSettings(temperature=0.0),
  66. )
  67. security_engineer = Agent(
  68. name="SecurityEngineer",
  69. description="Performs security audits and vulnerability assessments prior to release.",
  70. instructions=(
  71. "You are the SecurityEngineer. Perform audits and scans using your tools. "
  72. "Include full tool outputs verbatim. When compliance review is required, hand off to ComplianceOfficer "
  73. "without adding a reminder (disabled)."
  74. ),
  75. tools=[security_audit, vulnerability_scan],
  76. model_settings=ModelSettings(temperature=0.0),
  77. )
  78. compliance_officer = Agent(
  79. name="ComplianceOfficer",
  80. description="Verifies regulatory and policy compliance and issues sign-off.",
  81. instructions=(
  82. "You are the ComplianceOfficer. Run policy checks and generate compliance reports. "
  83. "When handing back to DevLead, confirm sign-off steps and artifacts—they receive a custom reminder to double-check."
  84. ),
  85. tools=[policy_check, generate_compliance_report],
  86. model_settings=ModelSettings(temperature=0.0),
  87. )
  88. agency = Agency(
  89. dev_lead,
  90. communication_flows=[
  91. (dev_lead > security_engineer, Handoff),
  92. (security_engineer > compliance_officer, NoReminderHandoff),
  93. (compliance_officer > dev_lead, Handoff),
  94. ],
  95. shared_instructions=(
  96. "Deliver secure, compliant features. Preserve exact tool outputs in responses and include sign-off details."
  97. ),
  98. )
  99. async def main(input_message):
  100. async for _ in agency.get_response_stream(input_message):
  101. pass
  102. return
  103. if __name__ == "__main__":
  104. input_message = (
  105. "Implement user authentication with 2FA for our web app. "
  106. "After implementation, hand off to SecurityEngineer to run security_audit on the auth module and vulnerability_scan on the web app. "
  107. "Then hand off to ComplianceOfficer to run policy_check on authentication and generate_compliance_report for release v1.2. "
  108. "Finally, hand back to DevLead to confirm deployment sign-off steps and attach the compliance report."
  109. )
  110. asyncio.run(main(input_message))
  111. print_history(agency.thread_manager, roles=("assistant", "system", "function_call", "function_call_output"))