step-by-step-guide.mdx 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. ---
  2. title: "Step-by-Step Guide"
  3. description: "Learn how to create custom tools in Agency Swarm framework."
  4. icon: "map"
  5. ---
  6. In Agency Swarm, tools enable agents to perform specific actions and interact with external systems. The framework supports two approaches for creating custom tools depending on the version you're using.
  7. Framework supports 2 methods of creating tools:
  8. 1. Using agency-swarm's `BaseTool` class.
  9. 2. Using openai's `@function_tool` decorator with async functions.
  10. Explore the sections below to find a method that suits your approach the best.
  11. <Tabs defaultValue="BaseTool">
  12. <Tab title="BaseTool">
  13. In Agency Swarm’s approach, tools are Python classes that inherit from `BaseTool`. They are defined using [Pydantic](https://docs.pydantic.dev/latest/), a data validation library. Each BaseTool must implement the `run` method, which is the main method that will be called when the tool is invoked by an agent.
  14. ## Step-by-step Guide
  15. To create a custom tool, typically you need to follow these steps:
  16. <Steps>
  17. <Step title="Add Import Statements">
  18. On top of your tool file, import the necessary modules and classes.
  19. ```python
  20. from agency_swarm.tools import BaseTool
  21. from pydantic import Field, model_validator
  22. # ... other imports
  23. ```
  24. </Step>
  25. <Step title="Define the Tool Class and Docstring">
  26. Create a new class that inherits from `BaseTool`. Write a clear docstring describing the tool's purpose. **This docstring is crucial as it helps agents understand how to use the tool.**
  27. ```python
  28. class Calculator(BaseTool):
  29. """
  30. A simple calculator tool that evaluates mathematical expressions.
  31. """
  32. ```
  33. </Step>
  34. <Step title="Define Input Fields">
  35. Use Pydantic fields to define the inputs your tool will accept.
  36. ```python
  37. expression: str = Field(..., description="The mathematical expression to evaluate.")
  38. ```
  39. <Accordion title="Custom Validation Logic (Optional)" icon="hammer">
  40. You can use [Pydantic's validators](https://docs.pydantic.dev/latest/concepts/validators/) to verify the inputs. This can be extremely effective to avoid hallucinations or other errors in production.
  41. ```python
  42. @model_validator(mode="after")
  43. def validate_expression(self):
  44. if self.expression.endswith("/0"):
  45. raise ValueError("Division by zero is not permitted")
  46. ```
  47. </Accordion>
  48. </Step>
  49. <Step title="Implement the run Method">
  50. Add the functionality that will be executed when the tool is called.
  51. ```python
  52. async def run(self):
  53. # Implement the tool's functionality
  54. result = eval(self.expression)
  55. return str(result)
  56. ```
  57. The `run` method should return a string, which is the tool's output that the agent will see and use in its response.
  58. </Step>
  59. <Step title="Test the Tool Independently">
  60. Test the tool independently to ensure it behaves as expected. We recommend adding a `if __name__ == "__main__":` block at the end of the tool file:
  61. ```python
  62. import asyncio
  63. if __name__ == "__main__":
  64. calc = Calculator(expression="2 + 2 * 3")
  65. print(asyncio.run(calc.run())) # Output should be '8'
  66. ```
  67. </Step>
  68. <Step title="Add the Tool to an Agent">
  69. After your tool works as expected, simply add it to an agent's list of `tools`.
  70. ```python
  71. from agency_swarm import Agent
  72. from .tools.calculator import Calculator
  73. agent = Agent(
  74. name="MathAgent",
  75. tools=[Calculator],
  76. # Other agent parameters
  77. )
  78. ```
  79. <Accordion title="Using tools folder" icon="folder">
  80. Alternatively, you can simply place the tool file in the `tools_folder` directory and it will be automatically added to the agent.
  81. ```python
  82. from agency_swarm import Agent
  83. agent = Agent(
  84. name="MathAgent",
  85. tools_folder="./tools",
  86. # Other agent parameters
  87. )
  88. ```
  89. <Note>
  90. Each file in the `tools_folder` should contain a class that is named exactly the same as the file name. For example, `Calculator.py` should contain a `Calculator` class.
  91. </Note>
  92. </Accordion>
  93. </Step>
  94. </Steps>
  95. ## Full Code Example
  96. Below is the full code example for a calculator tool above.
  97. ```python
  98. # calculator.py
  99. from agency_swarm.tools import BaseTool
  100. from pydantic import Field, model_validator
  101. class Calculator(BaseTool):
  102. """
  103. A simple calculator tool that evaluates mathematical expressions.
  104. """
  105. expression: str = Field(..., description="The mathematical expression to evaluate.")
  106. @model_validator(mode="after")
  107. def validate_expression(self):
  108. if self.expression.endswith("/0"):
  109. raise ValueError("Division by zero is not permitted")
  110. async def run(self):
  111. result = eval(self.expression)
  112. return str(result)
  113. if __name__ == "__main__":
  114. import asyncio
  115. calc = Calculator(expression="2 + 2 * 3")
  116. print(asyncio.run(calc.run())) # Output should be '8'
  117. ```
  118. </Tab>
  119. <Tab title="FunctionTool">
  120. ## Step-by-step Guide
  121. <Steps>
  122. <Step title="Add Import Statements">
  123. Import the necessary modules for the new function-based approach.
  124. ```python
  125. from agency_swarm import function_tool, RunContextWrapper
  126. from pydantic import BaseModel, Field, field_validator
  127. from agency_swarm import MasterContext
  128. ```
  129. </Step>
  130. <Step title="Define Args Schema">
  131. Create a Pydantic model to define your tool's input parameters.
  132. ```python
  133. class CalculatorArgs(BaseModel):
  134. expression: str = Field(..., description="The mathematical expression to evaluate")
  135. @field_validator("expression")
  136. @classmethod
  137. def validate_expression(cls, v):
  138. if "/0" in v:
  139. raise ValueError("Division by zero is not permitted")
  140. return v
  141. ```
  142. </Step>
  143. <Step title="Create the Tool Function">
  144. Define an async function with the `@function_tool` decorator.
  145. ```python
  146. @function_tool
  147. async def calculator_tool(ctx: RunContextWrapper[MasterContext], args: CalculatorArgs) -> str:
  148. """
  149. A calculator tool that evaluates mathematical expressions.
  150. Use this when you need to perform mathematical calculations.
  151. """
  152. # Access shared context if needed
  153. calculation_count = ctx.context.get("calculations", 0)
  154. ctx.context.set("calculations", calculation_count + 1)
  155. # Perform the calculation
  156. result = eval(args.expression)
  157. return f"Result: {result}"
  158. ```
  159. <Note>
  160. All parameters of the function tool, except for `ctx`, are treated as input parameters and included in the tool’s schema.
  161. For example, in the tool above, the input model `CalculatorArgs` is passed as the `args` parameter, so the input JSON would look like:
  162. ```
  163. {"args": {"expression": "input_expression"}}
  164. ```
  165. `args` is an example name, you can name your input parameters however you like, and you may define more than one input parameter if needed.
  166. </Note>
  167. <Note>
  168. This example mentions tool context, which can be used as a shared storage for all agents and tools.
  169. For more info on agent context, refer to [this page](/additional-features/agency-context)
  170. </Note>
  171. </Step>
  172. <Step title="Test the Tool">
  173. Test your tool function independently.
  174. ```python
  175. if __name__ == "__main__":
  176. import asyncio
  177. import json
  178. async def test_tool():
  179. ctx = MasterContext(user_context={"calculations": 1}, thread_manager=None, agents={})
  180. run_ctx = RunContextWrapper(context=ctx)
  181. args = CalculatorArgs(expression="2 + 2 * 3")
  182. # Wrap in args to comply with oai expected inputs
  183. args_json = {"args": args.model_dump()}
  184. result = await calculator_tool.on_invoke_tool(run_ctx, json.dumps(args_json))
  185. print(result)
  186. asyncio.run(test_tool())
  187. ```
  188. </Step>
  189. <Step title="Add Tool to Agent">
  190. Pass the function directly to the agent's tools list.
  191. ```python
  192. from agency_swarm import Agent
  193. agent = Agent(
  194. name="MathAgent",
  195. instructions="You are a helpful math assistant",
  196. tools=[calculator_tool], # Pass function directly
  197. model="gpt-5.4-mini"
  198. )
  199. ```
  200. </Step>
  201. </Steps>
  202. ## Full Code Example
  203. ```python
  204. # calculator_tool.py
  205. from agency_swarm import function_tool, RunContextWrapper
  206. from pydantic import BaseModel, Field, field_validator
  207. from agency_swarm import MasterContext
  208. class CalculatorArgs(BaseModel):
  209. expression: str = Field(..., description="The mathematical expression to evaluate")
  210. @field_validator("expression")
  211. @classmethod
  212. def validate_expression(cls, v):
  213. if "/0" in v:
  214. raise ValueError("Division by zero is not permitted")
  215. return v
  216. @function_tool
  217. async def calculator_tool(ctx: RunContextWrapper[MasterContext], args: CalculatorArgs) -> str:
  218. """
  219. A calculator tool that evaluates mathematical expressions.
  220. Use this when you need to perform mathematical calculations.
  221. """
  222. # Access shared context if needed
  223. calculation_count = ctx.context.get("calculations", 0)
  224. ctx.context.set("calculations", calculation_count + 1)
  225. # Perform the calculation
  226. result = eval(args.expression)
  227. return f"Result: {result} (Calculation #{calculation_count + 1})"
  228. if __name__ == "__main__":
  229. import asyncio
  230. import json
  231. async def test_tool():
  232. ctx = MasterContext(user_context={"calculations": 1}, thread_manager=None, agents={})
  233. run_ctx = RunContextWrapper(context=ctx)
  234. args = CalculatorArgs(expression="2 + 2 * 3")
  235. args_json = {"args": args.model_dump()}
  236. result = await calculator_tool.on_invoke_tool(run_ctx, json.dumps(args_json))
  237. print(result)
  238. asyncio.run(test_tool())
  239. ```
  240. </Tab>
  241. </Tabs>
  242. ## Next Steps
  243. - Checkout [Best Practices & Tips](/core-framework/tools/custom-tools/best-practices)
  244. - Learn why [PyDantic is all you need](/core-framework/tools/custom-tools/pydantic-is-all-you-need)
  245. - Learn how to [stream events from tools](/additional-features/streaming#streaming-from-tools)