/ docs / docs / documentation / developing / create-agents.md
create-agents.md
  1  ---
  2  title: Creating Agents
  3  sidebar_position: 420
  4  ---
  5  
  6  :::tip
  7  For a more comprehensive tutorial example, see the [Build Your Own Agent](tutorials/custom-agent.md) guide.
  8  This page walks through the fundamental concepts for creating agents in Agent Mesh.
  9  :::
 10  
 11  Agent Mesh is a powerful platform that enables you to create intelligent agents that can communicate with each other and perform complex tasks. At its core, Agent Mesh uses a tool-based architecture where LLM-powered agents are equipped with specific capabilities (tools) that they can use to accomplish user requests.
 12  
 13  Before continuing with this tutorial, make sure you are familiar with the basic [agent concept](../components/agents.md).
 14  
 15  This tutorial guides you through creating your first Agent Mesh agent from scratch. You will learn how to define tools as Python functions, configure an agent using YAML, and set up agent lifecycle functions. By the end of this tutorial, you should have a working "Hello World" agent that demonstrates the fundamental concepts of Agent Mesh agent development.
 16  
 17  ## Understanding the Architecture
 18  
 19  Before diving into implementation, you need to understand how the different components of an Agent Mesh agent work together. This architectural overview will help you see the big picture before you start building.
 20  
 21  ```mermaid
 22  graph TD
 23      subgraph Agent Configuration
 24          direction LR
 25          A[config.yaml] -->|Defines| B(Agent Properties);
 26          A -->|Lists & Configures| C(Tools);
 27      end
 28  
 29      subgraph Agent Host
 30          direction TB
 31          D[Agent Mesh Host] -->|Loads| A;
 32          D -->|Instantiates| E[Agent];
 33          E -->|Initializes with| F[Lifecycle Functions];
 34      end
 35  
 36      subgraph Tool Implementation
 37          direction LR
 38          G[Python Module tools.py] -->|Contains| H[Tool Functions];
 39      end
 40  
 41      subgraph Execution Flow
 42          direction TB
 43          I[User Request] --> J[LLM Orchestrator];
 44          J -->|Selects Tool| K{ADKToolWrapper};
 45          K -->|Calls| H;
 46          H -->|Accesses| L[ToolContext];
 47          H -->|Uses| M[tool_config];
 48          H -->|Returns Result| J;
 49      end
 50  
 51      C -->|Wrapped by| K;
 52  
 53      style A fill:#b60000,stroke:#faa,stroke-width:2px
 54      style H fill:#b60000,stroke:#faa,stroke-width:2px
 55      style F fill:#007000,stroke:#faa,stroke-width:2px
 56  ```
 57  
 58  When a user sends a request to your agent, the LLM orchestrator analyzes the request and decides which tool to use. The framework wraps your tool functions and provides them with context and configuration. Your tool executes its logic and returns results to the LLM, which then formulates a response to the user.
 59  
 60  ## Core Concepts
 61  
 62  Understanding these fundamental concepts will help you build effective agents.
 63  
 64  ### Tools: The Building Blocks
 65  
 66  Tools are the fundamental building blocks of Agent Mesh agents. Each tool is implemented as a Python function that performs a specific task. The LLM orchestrating your agent decides which tools to use based on the user's request and the tool descriptions you provide.
 67  
 68  Tools can process text and data, interact with external APIs, create and manipulate files, communicate with other agents, and access databases and services. You write tools as standard Python functions, and the framework handles the integration with the LLM.
 69  
 70  :::tip
 71  Agent Mesh provides a set of [built-in tools](../components/builtin-tools/builtin-tools.md) plus support for [model context protocol (MCP)](tutorials/mcp-integration.md) servers, which can be configured in the tools list of your agent configuration.
 72  :::
 73  
 74  ### Configuration File: The Blueprint
 75  
 76  The `config.yaml` (for plugin template) or `agent-name.yaml` (for agent instances) file is the blueprint of your agent. This YAML file defines your agent's identity (name, description, and capabilities), model configuration (which LLM to use), tools list (which tools the agent can access and how they're configured), lifecycle functions (setup and cleanup procedures), framework services (session management, artifact storage, and so on), and [agent card](../components/agents.md#agent-card) (metadata describing the agent capabilities, skills and its visibility in the system).
 77  
 78  The configuration file connects all the pieces together. It tells the framework where to find your tool functions, how to configure them, and what instructions to give the LLM about your agent's purpose.
 79  
 80  ### Tool Configuration: Customizing Behavior
 81  
 82  Within the `tools` list in your YAML config, each tool can have its own `tool_config` section. This allows you to configure the same tool function for different purposes, pass specific parameters to tool instances, and customize tool behavior per agent.
 83  
 84  This design pattern enables code reuse. You can write a single generic tool function and configure it multiple times with different parameters to serve different purposes within the same agent.
 85  
 86  :::info
 87  For tools of type "python", you can also use the `tool_name` and `tool_description` to overwrite the function name and description in the tool docstring.
 88  
 89  This is useful when using a generic tool function for multiple purposes, allowing you to provide a more descriptive name and description for each instance.
 90  :::
 91  
 92  ### ToolContext: Accessing Framework Services
 93  
 94  The `ToolContext` object (passed as one of the arguments to your tool function) provides your tools with access to Agent Mesh core services. Through this context object, your tools can access structured logging for debugging and monitoring, the artifact service for file storage and retrieval, session information about the current user and session context, and agent state for sharing data between tool calls.
 95  
 96  The framework automatically provides this context to your tool functions. You don't need to create or manage it yourself.
 97  
 98  ### Lifecycle Functions: Managing Resources
 99  
100  Lifecycle functions allow you to manage resources that should persist for the agent's lifetime. The `agent_init_function` runs when the agent starts (for example, to establish database connections), and the `agent_cleanup_function` runs when the agent shuts down (for example, to close connections gracefully).
101  
102  These functions are optional but recommended for managing resources effectively. They ensure that your agent properly initializes any required resources and cleans them up when shutting down.
103  
104  :::note
105  Lifecycle functions are optional but recommended for managing resources effectively.
106  :::
107  
108  ## Creating Your First Agent: Step-by-Step
109  
110  Now that you understand the core concepts, you can create a simple agent that demonstrates how these pieces work together. You will build a "Hello World" agent that can greet users and say goodbye.
111  
112  ### Step 1: Initialize Your Agent Plugin
113  
114  You can create an agent either by using the `sam add agent` command or by creating a new plugin of type agent with `sam plugin create my-hello-agent --type agent`.
115  
116  :::tip
117  For information and recommendations about these options, see [`Agent or Plugin: Which To use?`](../components/plugins.md#agent-or-plugin-which-to-use).
118  :::
119  
120  In this tutorial, you create a new agent by creating a new plugin of type agent. For an example of custom agents, see [Build Your Own Agent](tutorials/custom-agent.md) guide.
121  
122  Although the directory structure for plugins is slightly different than the one for agents, both require a YAML configuration file, and a python module with the tools and lifecycle functions you want.
123  
124  To create a new agent plugin, run the following command:
125  
126  ```bash
127  sam plugin create my-hello-agent --type agent
128  ```
129  
130  Follow the prompts to set up your agent. The prompts create a new directory structure for your agent:
131  
132  ```
133  my-hello-agent/
134  ├── src/
135  │   └── my_hello_agent/
136  │       ├── __init__.py
137  │       ├── tools.py
138  │       └── lifecycle.py # This file won't be automatically created
139  ├── config.yaml
140  ├── pyproject.toml
141  └── README.md
142  ```
143  
144  ```mermaid
145  graph TD
146      A[my-hello-agent/] --> B[src/]
147      A --> C[config.yaml]
148      A --> D[pyproject.toml]
149      A --> E[README.md]
150      
151      B --> F[my_hello_agent/]
152      F --> G[__init__.py]
153      F --> H[tools.py]
154      F --> I[lifecycle.py]
155      
156      style C fill:#b60000,stroke:#333,stroke-width:2px
157      style H fill:#b60000,stroke:#333,stroke-width:2px
158      style I fill:#007000,stroke:#333,stroke-width:2px
159  ```
160  
161  The `config.yaml` file will contain your agent configuration, `tools.py` will contain your tool functions, and `lifecycle.py` (which you'll create manually) will contain your lifecycle functions. The `pyproject.toml` file manages your plugin's dependencies and metadata.
162  
163  ### Step 2: Create Your Tool Functions
164  
165  Tools are where your agent's actual capabilities live. You will create two simple tools: one to greet users and one to say goodbye.
166  
167  Create your tool functions in the `src/my_hello_agent/tools.py` file. For a complete guide on creating python tools, see our [Creating Python Tools](./creating-python-tools.md) documentation.
168  
169  ```python
170  # src/my_hello_agent/tools.py
171  """
172  Tools for the Hello World agent.
173  """
174  
175  from typing import Any, Dict, Optional
176  from google.adk.tools import ToolContext
177  from solace_ai_connector.common.log import log
178  
179  
180  async def hello_tool(
181      name: str,
182      tool_context: Optional[ToolContext] = None,
183      tool_config: Optional[Dict[str, Any]] = None
184  ) -> Dict[str, Any]:
185      """
186      Greets a user with a personalized message.
187      
188      Args:
189          name: The name of the person to greet
190      
191      Returns:
192          A dictionary with the greeting message
193      """
194      log_identifier = "[HelloTool]"
195      log.info(f"{log_identifier} Greeting user: {name}")
196      
197      # Get configuration from tool_config
198      greeting_prefix = "Hello"
199      if tool_config:
200          greeting_prefix = tool_config.get("greeting_prefix", "Hello")
201      
202      # Create the greeting message
203      greeting_message = f"{greeting_prefix}, {name}! Welcome to Agent Mesh!"
204      
205      log.info(f"{log_identifier} Generated greeting: {greeting_message}")
206      
207      return {
208          "status": "success",
209          "message": greeting_message,
210          "greeted_name": name
211      }
212  
213  
214  async def farewell_tool(
215      name: str,
216      tool_context: Optional[ToolContext] = None,
217      tool_config: Optional[Dict[str, Any]] = None
218  ) -> Dict[str, Any]:
219      """
220      Says goodbye to a user.
221      
222      Args:
223          name: The name of the person to say goodbye to
224      
225      Returns:
226          A dictionary with the farewell message
227      """
228      log_identifier = "[FarewellTool]"
229      log.info(f"{log_identifier} Saying goodbye to user: {name}")
230      
231      # Get configuration from tool_config
232      farewell_prefix = "Goodbye"
233      if tool_config:
234          farewell_prefix = tool_config.get("farewell_prefix", "Goodbye")
235      
236      # Create the farewell message
237      farewell_message = f"{farewell_prefix}, {name}! Thanks for using Agent Mesh!"
238      
239      log.info(f"{log_identifier} Generated farewell: {farewell_message}")
240      
241      return {
242          "status": "success",
243          "message": farewell_message,
244          "farewell_name": name
245      }
246  ```
247  
248  Let's examine what makes these tool functions work. All tool functions must be asynchronous (defined with `async def`) because the framework uses asynchronous execution. The framework automatically provides two special parameters: `tool_context` gives you access to framework services like logging and artifact storage, and `tool_config` contains any custom configuration you define in your YAML file.
249  
250  The function signature includes regular parameters (like `name`) that the LLM will provide when calling the tool. The docstring is important because the LLM uses it to understand what the tool does and when to use it. You should always write clear, descriptive docstrings.
251  
252  The tool retrieves its configuration from the `tool_config` dictionary. This allows you to customize the tool's behavior without changing the code. In this example, you can configure different greeting prefixes for different use cases.
253  
254  The tool returns a dictionary with at least a `status` field. This structured format makes it easy for the LLM to understand the result. You can include any additional data that might be useful for the LLM or for debugging.
255  
256  The logging statements help you track what your tool is doing. The framework provides a logger that you should use for consistent logging across your agent.
257  
258  ### Step 3: Configure Your Agent
259  
260  Now you need to tell the framework about your agent and its tools. Create the main configuration file for your agent in `config.yaml`:
261  
262  ```yaml
263  # File: config.yaml (at root of project directory)
264  # ... (additional services and configurations)
265  
266  apps:
267    - name: my-hello-agent
268      app_module: solace_agent_mesh.agent.sac.app 
269      broker:
270        # Can be found in configs/shared_config.yaml after running sam init
271        <<: *broker_connection
272      
273      # Agent-specific configuration
274      app_config:
275        # Basic agent identity
276        namespace: ${NAMESPACE} 
277        supports_streaming: true 
278        agent_name: "HelloAgent"
279        display_name: "Hello World Agent"
280        
281        # LLM model configuration
282        model: *general_model
283        model_provider: 
284          - "general" 
285        
286        # Agent instructions (system prompt)
287        instruction: |
288          You are a friendly Hello World agent. Your purpose is to greet users and 
289          demonstrate the capabilities of Agent Mesh. You can:
290          
291          1. Greet users with personalized messages using the hello_tool
292          2. Say goodbye to users using the farewell_tool
293          
294          Always be polite and helpful. When greeting someone, ask for their name 
295          if they haven't provided it.
296        
297        # Lifecycle functions
298        agent_init_function:
299          module: "my_hello_agent.lifecycle" # This should point to your lifecycle python module
300          name: "initialize_hello_agent"
301          base_path: .
302          config:
303            startup_message: "Hello Agent is starting up!"
304            log_level: "INFO"
305        
306        agent_cleanup_function:
307          module: "my_hello_agent.lifecycle"
308          base_path: .
309          name: "cleanup_hello_agent"
310        
311        # Tools configuration
312        tools:
313          # Hello tool with custom greeting
314          - tool_type: python
315            component_module: "my_hello_agent.tools"
316            component_base_path: .
317            function_name: "hello_tool"
318            tool_name: "greet_user" # Renaming the tool, must use this name in the agent card
319            tool_config:
320              greeting_prefix: "Hello there"
321          
322          # Farewell tool with custom farewell
323          - tool_type: python
324            component_module: "my_hello_agent.tools"
325            component_base_path: .
326            function_name: "farewell_tool"
327            tool_name: "say_goodbye"
328            tool_config:
329              farewell_prefix: "See you later"
330          
331          # Built-in artifact tools for file operations
332          - tool_type: builtin-group
333            group_name: "artifact_management"
334        
335        # Agent card (describes the agent's capabilities)
336        agent_card:
337          description: "A friendly Hello World agent that demonstrates Agent Mesh capabilities"
338          defaultInputModes: ["text"]
339          defaultOutputModes: ["text"]
340          skills:
341            - id: "greet_user"
342              name: "Greet User"
343              description: "Greets users with personalized messages"
344            - id: "say_goodbye"
345              name: "Say Goodbye"
346              description: "Says goodbye to users"
347            - id: "file_operations"
348              name: "File Operations"
349              description: "Create, read, and manage files and artifacts"
350        
351        # Session and artifact services
352        session_service: *default_session_service
353        artifact_service: *default_artifact_service
354  # ... (additional services and configurations)
355  ```
356  
357  This configuration file connects all the pieces of your agent. Let's examine each section to understand its purpose.
358  
359  The `namespace` uniquely identifies your agent in the mesh. This allows multiple agents to coexist and communicate. The `agent_name` and `display_name` provide human-readable identifiers for your agent.
360  
361  The `model` section specifies which LLM to use. The `*general_model` reference points to a model configuration defined elsewhere in your configuration files (typically in `shared_config.yaml`). Alternatively, you can use the `model_provider` field to reference a model configured through the Agent Mesh UI, which is the recommended approach for most deployments. For details on both approaches, see [Model Configurations](../installing-and-configuring/model_configurations.md#using-model-configurations-in-agents).
362  
363  The `instruction` field contains the system prompt that defines your agent's personality and behavior. This text tells the LLM what role it should play and what capabilities it has. You
364  should write clear, specific instructions that help the LLM understand its role.
365  
366  The `tools` section lists all the tools your agent can use. Each tool entry specifies the `tool_type` (python for custom functions, builtin-group for built-in tools), the `component_module` that contains the function, the `function_name` to call, and optionally a `tool_name` to rename the tool for the LLM. The `tool_config` section passes custom configuration to each tool instance. This is where you provide the `greeting_prefix` and `farewell_prefix` values that your tool functions read.
367  
368  Notice that you can configure the same function multiple times with different names and configurations. The `hello_tool` function is configured as `greet_user` with one greeting prefix, but you could add another configuration with a different prefix for formal greetings.
369  
370  The `agent_card` section describes your agent's capabilities to other parts of the system. The `skills` list should match the tools you've configured. Each skill has an `id` that corresponds to a tool name, making it discoverable by other agents and the user interface.
371  
372  The `session_service` and `artifact_service` references connect your agent to framework services for managing user sessions and storing files. These services are typically defined in your shared configuration.
373  
374  ### Step 4: Create Lifecycle Functions
375  
376  Lifecycle functions manage resources that should persist for your agent's lifetime. Although these functions are optional, they demonstrate how to properly initialize and clean up resources.
377  
378  The lifecycle file is not automatically created, so you need to create it manually:
379  
380  ```bash
381  touch src/my_hello_agent/lifecycle.py
382  ```
383  
384  Now add your lifecycle functions:
385  
386  ```python
387  # src/my_hello_agent/lifecycle.py
388  """
389  Lifecycle functions for the Hello World agent.
390  """
391  
392  from typing import Any, Dict
393  from pydantic import BaseModel, Field
394  from solace_ai_connector.common.log import log
395  
396  
397  class HelloAgentInitConfig(BaseModel):
398      """
399      Configuration model for the Hello Agent initialization.
400      """
401      startup_message: str = Field(description="Message to log on startup")
402      log_level: str = Field(default="INFO", description="Logging level for the agent")
403  
404  
405  def initialize_hello_agent(host_component: Any, init_config: HelloAgentInitConfig):
406      """
407      Initializes the Hello World agent.
408      
409      Args:
410          host_component: The agent host component
411          init_config: Validated initialization configuration
412      """
413      log_identifier = f"[{host_component.agent_name}:init]"
414      log.info(f"{log_identifier} Starting Hello Agent initialization...")
415      
416      # Log the startup message from config
417      log.info(f"{log_identifier} {init_config.startup_message}")
418      
419      # You could initialize shared resources here, such as:
420      # - Database connections
421      # - API clients
422      # - Caches or shared data structures
423      
424      # Store any shared state in the agent
425      host_component.set_agent_specific_state("initialized_at", "2024-01-01T00:00:00Z")
426      host_component.set_agent_specific_state("greeting_count", 0)
427      
428      log.info(f"{log_identifier} Hello Agent initialization completed successfully")
429  
430  
431  def cleanup_hello_agent(host_component: Any):
432      """
433      Cleans up resources when the Hello World agent shuts down.
434      
435      Args:
436          host_component: The agent host component
437      """
438      log_identifier = f"[{host_component.agent_name}:cleanup]"
439      log.info(f"{log_identifier} Starting Hello Agent cleanup...")
440      
441      # Retrieve any shared state
442      greeting_count = host_component.get_agent_specific_state("greeting_count", 0)
443      log.info(f"{log_identifier} Agent processed {greeting_count} greetings during its lifetime")
444      
445      # Clean up resources here, such as:
446      # - Closing database connections
447      # - Shutting down background tasks
448      # - Saving final state
449      
450      log.info(f"{log_identifier} Hello Agent cleanup completed")
451  ```
452  
453  The lifecycle functions follow a specific pattern. The `initialize_hello_agent` function receives two parameters: `host_component` provides access to the agent instance and its methods, and `init_config` contains the validated configuration from your YAML file's `agent_init_function.config` section.
454  
455  Using a Pydantic model for `init_config` provides automatic validation. The framework validates your configuration against this model when the agent starts, catching configuration errors early. This is better than manually checking configuration values in your code.
456  
457  The `host_component` object provides methods for managing agent state. The `set_agent_specific_state` method stores data that persists across tool calls within the same agent instance. This is useful for tracking statistics, caching data, or maintaining connections. The state is specific to this agent instance and is not shared with other agents.
458  
459  The `cleanup_hello_agent` function runs when the agent shuts down. This is your opportunity to gracefully close connections, save final state, or perform any other cleanup tasks. The function receives only the `host_component` parameter because cleanup typically doesn't need additional configuration.
460  
461  In this example, you retrieve the greeting count from the agent state and log it. In a real application, you might close database connections, flush caches to disk, or notify other services that the agent is shutting down.
462  
463  ## Running Your Agent
464  
465  Now that you have created all the necessary components, you can run your agent. The process involves building your plugin and adding it to your Agent Mesh project.
466  
467  ### Building and Installing the Plugin
468  
469  To properly instantiate your plugin agent, first build the plugin. The following command will produce a python wheel file under `dist` directory:
470  
471  ```bash
472  sam plugin build
473  ```
474  
475  This command packages your agent code, configuration, and dependencies into a distributable wheel file. The wheel file is a standard Python package format that can be installed into any Python environment.
476  
477  Check into [your Agent Mesh project directory](../getting-started/try-agent-mesh.md), and add the plugin wheel with a given name:
478  
479  ```bash
480  sam plugin add my-first-weather-agent --plugin PATH/TO/weather-agent/dist/weather-agent.whl
481  ```
482  
483  :::note
484  Using the `sam plugin add` command installs your plugin as a python dependency into your python environment.
485  This also means changing the source code without reinstalling the plugin will not reflect the changes.
486  :::
487  
488  The `sam plugin add` command does several things. It installs your plugin package into your Python environment, making your tool functions and lifecycle functions importable. It also creates a configuration file in your project's `configs/agents/` directory that references your plugin. This configuration file is what the framework loads when you run your agent.
489  
490  Now, you can run the complete Agent Mesh application along with your newly added agent:
491  
492  ```bash
493  sam run
494  ```
495  
496  Alternatively, only run the newly added agent using `sam run configs/agents/my-first-weather-agent.yaml`
497  
498  ### Quick Debug Mode
499  
500  :::tip[Quick Debug]
501  
502  For debugging or isolated development testing, you can run your agent from the `src` directory directly using the Agent Mesh CLI.
503  
504  ```bash
505  cd src
506  sam run ../config.yaml
507  ```
508  
509  Changing to the src directory allows the module path to be set correctly so that Agent Mesh can find your functions without your having to install them in your python environment as a plugin package.
510  :::
511  
512  This quick debug mode is useful during development because you can make changes to your code and immediately test them without rebuilding and reinstalling the plugin. However, you should always test with the full plugin installation process before deploying to production.
513  
514  ## Advanced Concepts
515  
516  Once you understand the basics, you can explore more advanced patterns for building sophisticated agents.
517  
518  ### Working with Artifacts
519  
520  The artifact service allows your tools to create, store, and retrieve files. You can enhance your hello tool to save greetings to a file using the artifact service:
521  
522  ```python
523  
524  from datetime import datetime
525  from solace_agent_mesh.agent.utils.artifact_helpers import save_artifact_with_metadata
526  
527  async def hello_tool_with_artifact(
528      name: str,
529      save_to_file: bool = False,
530      tool_context: Optional[ToolContext] = None,
531      tool_config: Optional[Dict[str, Any]] = None
532  ) -> Dict[str, Any]:
533      """
534      Greets a user and optionally saves the greeting to a file.
535      """
536      log_identifier = "[HelloToolWithArtifact]"
537      
538      # Generate greeting (same as before)
539      greeting_prefix = tool_config.get("greeting_prefix", "Hello") if tool_config else "Hello"
540      greeting_message = f"{greeting_prefix}, {name}! Welcome to Agent Mesh!"
541      
542      result = {
543          "status": "success",
544          "message": greeting_message,
545          "greeted_name": name
546      }
547      
548      # Save to artifact if requested
549      if save_to_file and tool_context:
550          try:
551              # Prepare content
552              timestamp = datetime.now(timezone.utc)
553              filename = f"greeting_{name}_{timestamp}.txt"
554              content = f"Greeting: {greeting_message}\nTimestamp: {timestamp}\n"
555              
556              # Save artifact
557              artifact_service = tool_context._invocation_context.artifact_service
558              await save_artifact_with_metadata(
559                  artifact_service=artifact_service,
560                  app_name=tool_context._invocation_context.app_name,
561                  user_id=tool_context._invocation_context.user_id,
562                  session_id=tool_context._invocation_context.session.id,
563                  filename=filename,
564                  content_bytes=content.encode('utf-8'),
565                  mime_type="application/json",
566                  metadata_dict={
567                      "description": "Greeting message",
568                      "source": "Greeting Agent",
569                  },
570                  timestamp=timestamp
571              )
572              
573              result["artifact_saved"] = filename
574              log.info(f"{log_identifier} Saved greeting to artifact: {filename}")
575          
576          except Exception as e:
577              log.error(f"{log_identifier} Failed to save artifact: {e}")
578              result["artifact_error"] = str(e)
579      
580      return result
581  ```
582  
583  This enhanced tool demonstrates several important concepts. The `save_to_file` parameter allows the LLM to decide whether to save the greeting based on the user's request. This gives your agent flexibility in how it uses the tool.
584  
585  The `tool_context` object provides access to the artifact service through its `_invocation_context` property. The invocation context contains information about the current execution environment, including the user ID, session ID, and app name. These identifiers are necessary for properly organizing and retrieving artifacts.
586  
587  The `save_artifact_with_metadata` helper function handles the details of saving files to the artifact service. You provide the content as bytes, specify a MIME type, and include metadata that describes the artifact. The metadata makes it easier to search for and manage artifacts later.
588  
589  Error handling is important when working with external services. The try-except block ensures that if artifact saving fails, your tool can still return a successful greeting. The error is logged and included in the result, allowing the LLM to inform the user about the issue.
590  
591  ### Using Multiple Tool Configurations
592  
593  You can configure the same tool function multiple times with different settings. This pattern is useful when you want to provide the LLM with multiple variations of the same capability:
594  
595  ```yaml
596  tools:
597    # Formal greeting
598    - tool_type: python
599      component_module: "my_hello_agent.tools"
600      function_name: "hello_tool"
601      tool_name: "formal_greeting"
602      tool_config:
603        greeting_prefix: "Good day"
604    
605    # Casual greeting
606    - tool_type: python
607      component_module: "my_hello_agent.tools"
608      function_name: "hello_tool"
609      tool_name: "casual_greeting"
610      tool_config:
611        greeting_prefix: "Hey there"
612    
613    # Enthusiastic greeting
614    - tool_type: python
615      component_module: "my_hello_agent.tools"
616      function_name: "hello_tool"
617      tool_name: "enthusiastic_greeting"
618      tool_config:
619        greeting_prefix: "Hello and welcome"
620  ```
621  
622  This configuration creates three different tools from the same function. The LLM sees these as distinct capabilities and can choose the appropriate greeting style based on the context of the conversation. For example, it might use the formal greeting for business contexts and the casual greeting for friendly conversations.
623  
624  Each tool configuration should have a unique `tool_name` and should be listed in your agent card's skills section. This makes each variation discoverable and allows you to provide specific descriptions for each greeting style.
625  
626  ## Quick Start: Using the CLI
627  
628  If you want to get started quickly without manually creating all the files, you can use the Agent Mesh CLI to generate the basic structure:
629  
630  ```bash
631  sam add agent my-first-agent
632  ```
633  
634  This command launches an interactive setup (or use `--gui` for browser-based configuration) that generates the necessary files and configuration, and sets up the basic agent structure.
635  
636  Note that creating an agent as a plugin is preferred over creating an agent directly because plugins are more portable and easier to share.
637  
638  ### CLI Options
639  
640  You can customize the agent creation with provided CLI options. For a complete list of options, run:
641  
642  ```bash
643  sam add agent --help
644  ```
645  
646  The CLI tool is useful for quickly scaffolding new agents, but understanding the manual process helps you customize and troubleshoot your agents more effectively.
647  
648  ## Best Practices
649  
650  Following these best practices will help you create robust, maintainable agents.
651  
652  ### Tool Design
653  
654  Each tool should do one thing well. This single responsibility principle makes your tools easier to test, debug, and reuse. Instead of creating one large tool that handles multiple tasks, create several focused tools that each handle a specific task.
655  
656  Write detailed docstrings for your tools. The LLM uses these docstrings to understand what each tool does and when to use it. Include descriptions of all parameters, return values, and any important behavior or limitations.
657  
658  Always return structured error responses. When something goes wrong, your tool should return a dictionary with a status field indicating failure and a message explaining what went wrong. This allows the LLM to understand the error and potentially retry with different parameters or inform the user about the issue.
659  
660  Use consistent logging for debugging and monitoring. Log important events, parameter values, and results. This makes it much easier to troubleshoot issues when your agent is running in production.
661  
662  ### Configuration
663  
664  Use environment variables for sensitive data like API keys, database passwords, and other credentials. Never hardcode sensitive information in your configuration files or source code.
665  
666  Use Pydantic models for configuration validation. This catches configuration errors early and provides clear error messages when something is wrong. It also serves as documentation for what configuration options are available.
667  
668  Comment your configuration files thoroughly. YAML files can become complex, and clear comments help other developers (and your future self) understand what each section does and why it's configured that way.
669  
670  ### Testing
671  
672  Write unit tests for your tool functions independently. Test them with various inputs, including edge cases and error conditions. Mock the `tool_context` and `tool_config` parameters to isolate your tool logic from the framework.
673  
674  Write integration tests that test your agent with real Agent Mesh infrastructure. These tests verify that your configuration is correct and that your tools work properly when called by the LLM.
675  
676  Mock external dependencies for reliable testing. If your tools call external APIs or databases, create mock versions for testing. This makes your tests faster and more reliable because they don't depend on external services being available.