drupal ai agents plugin guide
drupal/ai_agents Plugin Development Guide
Overview
The drupal/ai_agents module is a comprehensive framework for creating intelligent agents within Drupal that can perform text-to-action operations. It enables developers to build "agents of all sorts, including text-to-action agents that can manipulate your actual Drupal configurations or content based on textual or multimodal instructions."
The module leverages Drupal's plugin system and integrates with the drupal/ai module's provider abstraction layer, allowing agents to communicate with 48+ AI platforms including Anthropic, OpenAI, Google Gemini, AWS Bedrock, and more.
Architecture
High-Level Architecture
The drupal/ai_agents module follows a layered architecture:
‚ User Interface Layer ‚
‚ (Chatbot, Admin UI, Forms) ‚
˜
‚
¼
‚ Agent Framework Layer ‚
‚ - Agent Plugins ‚
‚ - Tool Management ‚
‚ - Agent Lifecycle ‚
˜
‚
¼
‚ Tool Execution Layer ‚
‚ - Tool Plugins ‚
‚ - Natural Language Processing ‚
‚ - Action Execution ‚
˜
‚
¼
‚ AI Provider Integration Layer ‚
‚ (drupal/ai module) ‚
‚ - Provider Abstraction ‚
‚ - Model Management ‚
‚ - Token Handling ‚
˜
‚
¼
‚ External AI Services ‚
‚ (OpenAI, Anthropic, Google, AWS, etc.) ‚
˜
Plugin System
The drupal/ai_agents module uses Drupal's plugin system with two primary plugin types:
- Agent Plugins: Represent AI agents that can perform specific tasks
- Tool Plugins: Represent specific actions/tools that agents can execute
Plugin Discovery
Plugins are discovered using:
- Attributes (modern approach, Drupal 10.2+): PHP attributes that register plugins
- Annotations (legacy approach, pre-Drupal 10.2): Annotation classes for plugin registration
Plugin Manager
The plugin manager orchestrates plugin discovery and instantiation:
/** * Service container provides the plugin manager */ \Drupal::service('plugin.manager.ai_agents.agent') \Drupal::service('plugin.manager.ai_agents.tool')
Configuration System
The module uses Drupal's Configuration API with YAML schema definitions for:
- Agent definitions
- Tool configurations
- Provider settings
- Context and constraints
Built-in Agents
The drupal/ai_agents module comes with three pre-installed agents:
1. Field Type Agent
Purpose: Creates, edits, and answers questions about fields on fieldable entities
Capabilities:
- Discover existing fields on entities
- Create new fields based on natural language descriptions
- Modify field properties and settings
- Answer questions about field configurations
Configuration:
- Entity type selection
- Bundle (content type, taxonomy vocabulary) selection
- Field operation scope
Example Usage:
"Create a text field called 'Product Description' on Article content type"
"What fields does the Product node type have?"
"Add a price field to the Product content type"
2. Content Type Agent
Purpose: Manages node type (content type) creation, editing, and inquiries
Capabilities:
- Create new content types with specified properties
- Edit existing content type settings
- Answer questions about content type structure
- Manage content type permissions
Configuration:
- Content type naming conventions
- Field association policies
- Permission templates
Example Usage:
"Create a new content type called Blog Post"
"What content types exist on this site?"
"Add workflow states to the Article content type"
3. Taxonomy Agent
Purpose: Handles vocabulary and taxonomy term operations
Capabilities:
- Create and manage taxonomies (vocabularies)
- Add taxonomy terms
- Organize taxonomy hierarchies
- Query taxonomy information
Configuration:
- Vocabulary namespacing
- Term weight management
- Hierarchy depth constraints
Example Usage:
"Create a Tags vocabulary"
"Add a 'Featured' term to the Categories taxonomy"
"What are all the tags on this site?"
Creating Custom Agents
Step 1: Create a New Module
First, create a custom module to house your agent. Follow standard Drupal module structure:
my_custom_agent/
src/
‚ Plugin/
‚ ‚ AiAgent/
‚ ‚ MyCustomAgent.php
‚ Service/
‚ MyCustomAgentService.php
config/
‚ install/
‚ my_custom_agent.settings.yml
my_custom_agent.info.yml
my_custom_agent.module
my_custom_agent.services.yml
Step 2: Create Module Info File
Create my_custom_agent.info.yml:
name: 'My Custom Agent' description: 'Custom AI agent for specific domain tasks' type: module core_version_requirement: '>=10.0' dependencies: - drupal:core - ai_agents:ai_agents - ai:ai version: 1.0.0
Step 3: Define Services
Create my_custom_agent.services.yml:
services: my_custom_agent.agent_service: class: Drupal\my_custom_agent\Service\MyCustomAgentService arguments: - '@plugin.manager.ai_agents.agent' - '@plugin.manager.ai_agents.tool' - '@config.factory' - '@logger.channel.my_custom_agent'
Step 4: Create the Agent Plugin
Create src/Plugin/AiAgent/MyCustomAgent.php:
<?php namespace Drupal\my_custom_agent\Plugin\AiAgent; use Drupal\ai_agents\Attribute\AiAgent; use Drupal\ai_agents\Plugin\AiAgentBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; /** * Custom AI Agent for domain-specific tasks. */ #[AiAgent( id: 'my_custom_agent', label: 'My Custom Agent', description: 'An AI agent that performs custom tasks', provider: 'anthropic', // Default provider )] class MyCustomAgent extends AiAgentBase { use StringTranslationTrait; /** * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { $form = parent::buildConfigurationForm($form, $form_state); // Add custom configuration options $form['custom_setting'] = [ '#type' => 'textfield', '#title' => $this->t('Custom Setting'), '#default_value' => $this->configuration['custom_setting'] ?? '', '#required' => TRUE, ]; return $form; } /** * {@inheritdoc} */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { parent::submitConfigurationForm($form, $form_state); $this->configuration['custom_setting'] = $form_state->getValue('custom_setting'); } /** * {@inheritdoc} */ public function getAvailableTools() { $tools = []; // Define which tools this agent can use $tools[] = 'my_custom_agent_tool_1'; $tools[] = 'my_custom_agent_tool_2'; return $tools; } /** * {@inheritdoc} */ public function getAgentPrompt() { return $this->t("You are a helpful AI assistant specializing in custom domain tasks. You can help users by executing available tools to accomplish their goals. Always provide clear explanations of what you're doing and confirm actions with users."); } /** * {@inheritdoc} */ public function validateConfiguration() { $errors = []; // Add custom validation logic if (empty($this->configuration['custom_setting'])) { $errors[] = $this->t('Custom setting is required'); } return $errors; } }
Step 5: Create Tool Plugins
Tools are the actual capabilities that agents can execute. Create src/Plugin/AiAgentTool/MyCustomTool.php:
<?php namespace Drupal\my_custom_agent\Plugin\AiAgentTool; use Drupal\ai_agents\Attribute\AiAgentTool; use Drupal\ai_agents\Plugin\AiAgentToolBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; /** * Custom tool for AI agents. */ #[AiAgentTool( id: 'my_custom_agent_tool_1', label: 'Custom Tool', description: 'Performs a specific custom action', agent_types: ['my_custom_agent'], )] class MyCustomTool extends AiAgentToolBase { use StringTranslationTrait; /** * {@inheritdoc} */ public function getToolDefinition() { return [ 'name' => 'custom_tool', 'description' => 'Performs a custom action based on natural language input', 'parameters' => [ 'type' => 'object', 'properties' => [ 'action' => [ 'type' => 'string', 'description' => 'The specific action to perform', ], 'context' => [ 'type' => 'string', 'description' => 'Additional context for the action', ], ], 'required' => ['action'], ], ]; } /** * {@inheritdoc} */ public function execute(array $parameters = []) { $action = $parameters['action'] ?? ''; $context = $parameters['context'] ?? ''; // Implement the actual tool logic try { $result = $this->performCustomAction($action, $context); return [ 'success' => TRUE, 'data' => $result, 'message' => $this->t('Action completed successfully'), ]; } catch (\Exception $e) { return [ 'success' => FALSE, 'message' => $this->t('Error executing tool: @error', ['@error' => $e->getMessage()]), ]; } } /** * {@inheritdoc} */ public function validateParameters(array $parameters) { $errors = []; if (empty($parameters['action'])) { $errors[] = $this->t('Action parameter is required'); } return $errors; } /** * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { $form = parent::buildConfigurationForm($form, $form_state); $form['tool_option'] = [ '#type' => 'select', '#title' => $this->t('Tool Option'), '#options' => [ 'option1' => $this->t('Option 1'), 'option2' => $this->t('Option 2'), ], '#default_value' => $this->configuration['tool_option'] ?? 'option1', ]; return $form; } /** * Performs the custom action. * * @param string $action * The action to perform. * @param string $context * Additional context. * * @return array * The action result. */ private function performCustomAction($action, $context) { // Implementation details return [ 'action_performed' => $action, 'result' => 'Success', ]; } }
Tool-Calling Patterns
Understanding Tool Calls
Tool calling is the mechanism by which agents execute specific actions. The flow works as follows:
1. User Input (Natural Language)
†
2. Agent receives input with available tools
†
3. AI Provider processes request and suggests tool call
†
4. Tool Execution Layer parses tool call
†
5. Tool Plugin executes the action
†
6. Result returned to Agent
†
7. Agent incorporates result into response
†
8. Response returned to User
Tool Definition Schema
Tools must define their interface for the AI provider:
public function getToolDefinition() { return [ 'name' => 'tool_name', 'description' => 'What this tool does', 'parameters' => [ 'type' => 'object', 'properties' => [ 'param1' => [ 'type' => 'string', 'description' => 'Description of param1', ], 'param2' => [ 'type' => 'number', 'description' => 'Description of param2', ], ], 'required' => ['param1'], // At least one parameter typically required ], ]; }
Parameter Types
Common parameter types for tools:
| Type | Description | Example |
|---|---|---|
string | Text parameter | Entity name, query |
number | Numeric parameter | Count, ID, weight |
boolean | True/false option | Enable/disable flag |
array | List of values | Tags, IDs, options |
object | Complex nested data | Configuration object |
Tool Execution Best Practices
-
Always Validate Input:
public function execute(array $parameters = []) { $errors = $this->validateParameters($parameters); if (!empty($errors)) { return ['success' => FALSE, 'errors' => $errors]; } // Execute... } -
Handle Errors Gracefully:
try { // Perform action } catch (\Exception $e) { return [ 'success' => FALSE, 'message' => 'Human-readable error message', 'error_code' => get_class($e), ]; } -
Return Structured Results:
return [ 'success' => TRUE|FALSE, 'data' => [...], // The actual result 'message' => 'User-facing message', 'metadata' => [...], // Additional context ]; -
Respect Permissions:
if (!$user->hasPermission('required_permission')) { return [ 'success' => FALSE, 'message' => 'You do not have permission to perform this action', ]; }
Integration with drupal/ai
AI Provider Integration
The drupal/ai_agents module integrates with drupal/ai's provider abstraction layer:
// Get AI provider service $ai_provider = \Drupal::service('ai.provider'); // Get available providers $providers = $ai_provider->getProviders(); // Use a specific provider $response = $ai_provider->sendMessage( $prompt, $provider_id, // e.g., 'anthropic', 'openai' $options // Configuration options );
Supported Providers
The drupal/ai module supports 48+ AI platforms:
- Large Language Models: Anthropic Claude, OpenAI GPT, Google Gemini, AWS Bedrock, Azure OpenAI, Hugging Face, llama2
- Vector Databases: Milvus, Pinecone, Postgres pgvector, Azure AI Search, SQLite
- Emerging Platforms: Amazee.io custom models
Provider Configuration
Configure providers in Drupal settings:
// In settings.php or through UI $config['ai.settings']['default_provider'] = 'anthropic'; $config['ai.provider.anthropic'] = [ 'api_key' => 'your-api-key', 'model' => 'claude-3-sonnet', ];
Using Providers in Agents
class MyAgent extends AiAgentBase { protected function callAiProvider($prompt, array $tools = []) { $ai_provider = \Drupal::service('ai.provider'); $response = $ai_provider->sendMessage( $prompt, $this->configuration['provider'], [ 'tools' => $tools, 'temperature' => 0.7, 'max_tokens' => 2000, ] ); return $response; } }
Agent Lifecycle Management
Agent State Management
Agents maintain state during conversations:
/** * Get chat history for the agent. */ public function getChatHistory() { return \Drupal::service('ai_agents.chat_history') ->getHistory($this->getPluginId()); } /** * Set chat history for context preservation. */ public function setChatHistory(array $history) { \Drupal::service('ai_agents.chat_history') ->setHistory($this->getPluginId(), $history); }
Agent Context
Define context that the agent should consider:
#[AiAgent( id: 'my_agent', label: 'My Agent', description: 'Custom agent', context_definitions: [ 'user' => 'current_user', 'language' => 'current_language', 'site' => 'site_configuration', ], )] class MyAgent extends AiAgentBase { public function getContextualInformation() { $context = []; // Include current user info $context['user'] = \Drupal::currentUser()->getEmail(); // Include language $context['language'] = \Drupal::languageManager() ->getCurrentLanguage() ->getId(); // Include site name $context['site_name'] = \Drupal::config('system.site')->get('name'); return $context; } }
Tool Availability Management
Dynamically control which tools are available:
public function getAvailableTools() { $user = \Drupal::currentUser(); $tools = []; // Base tools available to everyone $tools[] = 'search_tool'; // Permission-based tool availability if ($user->hasPermission('create content')) { $tools[] = 'content_creation_tool'; } if ($user->hasPermission('manage users')) { $tools[] = 'user_management_tool'; } return $tools; } public function getToolRestrictions() { return [ 'content_creation_tool' => [ 'require_permission' => 'create content', 'max_daily_uses' => 100, ], 'user_management_tool' => [ 'require_permission' => 'administer users', 'require_confirmation' => TRUE, ], ]; }
Natural Language Processing & Action Execution
Text-to-Action Flow
The drupal/ai_agents module converts natural language to actions:
User: "Create a new Article with the title 'Hello World'"
†
[NLP Parser]
†
Identified Actions:
- Entity: node
- Bundle: article
- Operation: create
- Fields: {title: "Hello World"}
†
[Permission Check]
†
[Action Execution]
†
Result: "Article created with ID 123"
Command Parsing
Tools help the AI understand what actions are available:
public function getToolDefinition() { return [ 'name' => 'create_node', 'description' => 'Create a new node (content)', 'parameters' => [ 'type' => 'object', 'properties' => [ 'type' => [ 'type' => 'string', 'description' => 'The node type (e.g., article, page)', ], 'title' => [ 'type' => 'string', 'description' => 'The node title', ], 'body' => [ 'type' => 'string', 'description' => 'The node body content', ], 'status' => [ 'type' => 'boolean', 'description' => 'Whether to publish the node immediately', ], ], 'required' => ['type', 'title'], ], ]; }
Action Execution
Execute the parsed action:
public function execute(array $parameters = []) { // Create the node $node = \Drupal::entityTypeManager() ->getStorage('node') ->create([ 'type' => $parameters['type'], 'title' => $parameters['title'], 'body' => $parameters['body'] ?? '', 'status' => $parameters['status'] ?? TRUE, ]); try { $node->save(); return [ 'success' => TRUE, 'data' => [ 'node_id' => $node->id(), 'url' => $node->toUrl()->toString(), ], 'message' => "Node created successfully (ID: {$node->id()})", ]; } catch (\Exception $e) { return [ 'success' => FALSE, 'message' => "Failed to create node: {$e->getMessage()}", ]; } }
Configuration Schema
Config File Structure
Define configuration schema in config/schema/my_custom_agent.schema.yml:
my_custom_agent.settings: type: config_object label: 'My Custom Agent settings' mapping: agent_config: type: mapping label: 'Agent configuration' mapping: default_provider: type: string label: 'Default AI provider' model: type: string label: 'AI model to use' temperature: type: float label: 'Temperature setting' max_tokens: type: integer label: 'Maximum tokens' tool_config: type: sequence label: 'Tool configurations' sequence: - type: mapping mapping: tool_id: type: string label: 'Tool ID' enabled: type: boolean label: 'Tool enabled' max_uses_per_day: type: integer label: 'Maximum uses per day'
Accessing Configuration
// Get configuration $config = \Drupal::config('my_custom_agent.settings'); $provider = $config->get('agent_config.default_provider'); // Set configuration $config->set('agent_config.default_provider', 'openai') ->save(); // Use in agent class MyAgent extends AiAgentBase { public function getAgentProvider() { return \Drupal::config('my_custom_agent.settings') ->get('agent_config.default_provider'); } }
Best Practices
1. Security and Permissions
Always check permissions before executing tools:
public function execute(array $parameters = []) { if (!$this->currentUser->hasPermission('required_permission')) { return [ 'success' => FALSE, 'message' => 'You do not have permission for this action', ]; } // Execute action }
2. Error Handling and Logging
Implement robust error handling:
try { // Execute action } catch (\Drupal\Core\Entity\EntityStorageException $e) { $this->logger->error('Entity storage error: @message', [ '@message' => $e->getMessage(), ]); return ['success' => FALSE]; } catch (\Exception $e) { $this->logger->error('Unexpected error: @message', [ '@message' => $e->getMessage(), ]); }
3. Rate Limiting and Throttling
Prevent abuse by implementing rate limits:
private function checkRateLimit($tool_id) { $key = "ai_agents:$tool_id:" . \Drupal::currentUser()->id(); $uses = \Drupal::service('tempstore.private') ->get('ai_agents') ->get($key); if ($uses >= $max_daily_uses) { return FALSE; } return TRUE; }
4. Validation and Sanitization
Always validate and sanitize input:
public function validateParameters(array $parameters) { $errors = []; if (empty($parameters['title'])) { $errors[] = 'Title is required'; } if (strlen($parameters['title']) > 255) { $errors[] = 'Title must be less than 255 characters'; } // Sanitize input $parameters['title'] = \Drupal\Component\Utility\Html::escape( $parameters['title'] ); return $errors; }
5. Testing Agents and Tools
Create tests for custom agents:
<?php namespace Drupal\Tests\my_custom_agent\Unit\Plugin\AiAgent; use Drupal\Tests\UnitTestCase; use Drupal\my_custom_agent\Plugin\AiAgent\MyCustomAgent; class MyCustomAgentTest extends UnitTestCase { public function testAgentInstantiation() { $agent = new MyCustomAgent([], 'my_agent', []); $this->assertInstanceOf(MyCustomAgent::class, $agent); } public function testToolDefinition() { // Test tool definition schema } public function testToolExecution() { // Test tool execution with various inputs } }
6. Documentation
Document your agent and tools:
/** * Custom agent for managing blog posts. * * This agent can: * - Create new blog posts * - Edit existing posts * - Publish/unpublish posts * - Query posts by various criteria * * @usage * "Create a new blog post titled 'My Blog Post'" * "Edit the blog post with ID 123" * "List all published blog posts" */ #[AiAgent( id: 'blog_post_agent', label: 'Blog Post Agent', description: 'Manages blog posts through natural language commands', )] class BlogPostAgent extends AiAgentBase { // ... }
7. Performance Optimization
Optimize for performance:
// Cache tool definitions $cache_key = 'ai_agents:tools:' . $this->getPluginId(); $cached = \Drupal::cache() ->get($cache_key); if ($cached) { return $cached->data; } $tools = $this->getAvailableTools(); \Drupal::cache() ->set($cache_key, $tools, time() + 3600);
8. AI Agents & Evaluations Recipe
The module can be distributed through recipes combined with the AI Agents & Evaluations Recipe for structured evaluation:
# recipe.yml name: 'My Custom Agent Recipe' description: 'Complete setup for custom agent with evaluation' modules: - ai_agents - my_custom_agent config: - ai_agents_recipes:agent_setup
Common Patterns from Issue Queue
Pattern 1: Dynamic Tool Overriding
Version 1.1.0+ allows overriding available tools:
public function overrideTools(array $tools) { // Filter or modify tools based on context return array_filter($tools, function($tool) { return $this->isToolAllowed($tool); }); }
Pattern 2: Tool Usage Restrictions
Implement restrictions on tool usage:
public function getToolRestrictions() { return [ 'dangerous_tool' => [ 'require_confirmation' => TRUE, 'require_admin_approval' => TRUE, 'log_usage' => TRUE, ], ]; }
Pattern 3: Context-Aware Execution
Use context to modify execution:
public function execute(array $parameters = []) { $context = $this->getContextualInformation(); if ($context['language'] === 'es') { $parameters['language'] = 'es'; } // Execute with context }
Pattern 4: Multi-Step Tool Chains
Allow agents to chain multiple tool calls:
public function executeChain(array $tool_calls = []) { $results = []; foreach ($tool_calls as $call) { $result = $this->executeTool( $call['tool_id'], $call['parameters'] ); $results[] = $result; // Stop if a step fails if (!$result['success']) { break; } } return $results; }
Example: Complete Blog Post Agent
Full Working Example
<?php namespace Drupal\blog_post_agent\Plugin\AiAgent; use Drupal\ai_agents\Attribute\AiAgent; use Drupal\ai_agents\Plugin\AiAgentBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\node\Entity\Node; /** * AI Agent for managing blog posts. * * This agent enables natural language management of blog posts including: * - Creating new articles * - Editing existing articles * - Publishing/unpublishing content * - Querying articles with filters */ #[AiAgent( id: 'blog_post_agent', label: 'Blog Post Manager', description: 'Create, edit, and manage blog posts through natural language', provider: 'anthropic', )] class BlogPostAgent extends AiAgentBase { use StringTranslationTrait; /** * {@inheritdoc} */ public function getAvailableTools() { return [ 'blog_create_post', 'blog_edit_post', 'blog_publish_post', 'blog_unpublish_post', 'blog_list_posts', 'blog_delete_post', ]; } /** * {@inheritdoc} */ public function getAgentPrompt() { return $this->t("You are a helpful blog post management assistant. You can help users create, edit, publish, and manage blog posts. Always confirm significant actions with users before executing them. Be clear about what you're doing and provide useful feedback."); } /** * {@inheritdoc} */ public function getToolRestrictions() { return [ 'blog_delete_post' => [ 'require_confirmation' => TRUE, 'require_permission' => 'delete any article content', ], 'blog_publish_post' => [ 'require_permission' => 'access content overview', ], ]; } /** * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { $form = parent::buildConfigurationForm($form, $form_state); $form['blog_category'] = [ '#type' => 'textfield', '#title' => $this->t('Default Blog Category'), '#description' => $this->t('Default taxonomy term for new blog posts'), '#default_value' => $this->configuration['blog_category'] ?? '', ]; $form['default_author'] = [ '#type' => 'checkbox', '#title' => $this->t('Assign to Current User'), '#description' => $this->t('Automatically assign created posts to current user'), '#default_value' => $this->configuration['default_author'] ?? TRUE, ]; return $form; } /** * {@inheritdoc} */ public function validateConfiguration() { $errors = []; // Add validation as needed return $errors; } }
Tool implementation:
<?php namespace Drupal\blog_post_agent\Plugin\AiAgentTool; use Drupal\ai_agents\Attribute\AiAgentTool; use Drupal\ai_agents\Plugin\AiAgentToolBase; use Drupal\node\Entity\Node; /** * Tool for creating blog posts. */ #[AiAgentTool( id: 'blog_create_post', label: 'Create Blog Post', description: 'Create a new blog post', agent_types: ['blog_post_agent'], )] class CreateBlogPostTool extends AiAgentToolBase { /** * {@inheritdoc} */ public function getToolDefinition() { return [ 'name' => 'create_blog_post', 'description' => 'Create a new blog post article', 'parameters' => [ 'type' => 'object', 'properties' => [ 'title' => [ 'type' => 'string', 'description' => 'The blog post title (required)', ], 'body' => [ 'type' => 'string', 'description' => 'The blog post content', ], 'tags' => [ 'type' => 'array', 'description' => 'Tags for the post', 'items' => ['type' => 'string'], ], 'published' => [ 'type' => 'boolean', 'description' => 'Whether to publish immediately', ], ], 'required' => ['title', 'body'], ], ]; } /** * {@inheritdoc} */ public function execute(array $parameters = []) { $errors = $this->validateParameters($parameters); if (!empty($errors)) { return ['success' => FALSE, 'message' => implode(', ', $errors)]; } try { $node = Node::create([ 'type' => 'article', 'title' => $parameters['title'], 'body' => [ 'value' => $parameters['body'], 'format' => 'full_html', ], 'status' => $parameters['published'] ?? TRUE, 'uid' => \Drupal::currentUser()->id(), ]); $node->save(); return [ 'success' => TRUE, 'data' => [ 'node_id' => $node->id(), 'title' => $node->getTitle(), 'url' => $node->toUrl()->toString(), ], 'message' => "Blog post created successfully: {$node->getTitle()}", ]; } catch (\Exception $e) { \Drupal::logger('blog_post_agent') ->error('Failed to create blog post: @error', [ '@error' => $e->getMessage(), ]); return [ 'success' => FALSE, 'message' => "Failed to create blog post: {$e->getMessage()}", ]; } } /** * {@inheritdoc} */ public function validateParameters(array $parameters) { $errors = []; if (empty($parameters['title'])) { $errors[] = 'Title is required'; } if (strlen($parameters['title'] ?? '') > 255) { $errors[] = 'Title must be less than 255 characters'; } if (empty($parameters['body'])) { $errors[] = 'Body content is required'; } if (!$this->currentUser->hasPermission('create article content')) { $errors[] = 'You do not have permission to create blog posts'; } return $errors; } }
References and Resources
- Drupal AI Agents Module: https://www.drupal.org/project/ai_agents
- Drupal AI Module: https://www.drupal.org/project/ai
- Plugin API Documentation: https://api.drupal.org/api/drupal/core%21core.api.php/group/plugin_api
- Configuration API: https://api.drupal.org/api/drupal
- Form API: https://api.drupal.org/api/drupal/core%21core.api.php/group/form_api
- Module Development: https://www.drupal.org/docs/creating-custom-modules
Version Notes
This guide covers drupal/ai_agents versions 1.0.0 and later, with specific features noted for version 1.1.0+ improvements:
- v1.0.0: Initial stable release
- v1.1.0: Added
getChatHistory/setChatHistory, tool overriding, and configurable contexts - v1.2.0: Bug fixes and stability improvements
- 1.3.x-dev: Current development branch with new features
Troubleshooting
Plugin Not Discovered
If your agent plugin isn't discovered:
- Clear Drupal caches:
drush cache:rebuild - Verify module is enabled:
drush en my_custom_agent - Check annotation syntax
- Verify plugin ID is unique
Tool Execution Failures
Check:
- Required permissions are granted
- Input parameters match schema
- Error logs:
/admin/reports/dblog - Drupal logs:
logs/drupal.log
Configuration Not Loading
Ensure:
- Schema file is in
config/schema/ - Config file naming matches pattern
- Run
drush cache:rebuild
Document Version: 1.0 Last Updated: January 8, 2026 Maintainer: Agent 7 - drupal/ai_agents Module Deep Researcher