mcpuniverse / docs /custom-agent-guide.md
haochengsama's picture
Add files using upload-large-folder tool
86acc45 verified
|
Raw
History Blame Contribute Delete
9.98 kB
# Custom Agent Implementation Guide
This guide explains how to implement custom agents in the MCPUniverse framework, building upon the existing agent architecture to create specialized AI agents.
## Overview
MCPUniverse provides a flexible agent framework that allows you to create custom agents with different reasoning patterns, tool usage, and behaviors. The framework is built around the Model Control Protocol (MCP) and supports various LLM providers.
## Architecture
### Core Components
1. **BaseAgent**: Abstract base class that all agents inherit from
2. **BaseAgentConfig**: Configuration class for agent parameters
3. **AgentResponse**: Standardized response format
4. **MCPManager**: Manages connections to MCP servers
5. **Tracer**: Handles execution tracing and debugging
### Agent Types
The framework includes several built-in agent types:
- **BasicAgent**: Simple LLM interaction agent
- **ReAct**: Reasoning and Acting agent implementation
- **FunctionCallAgent**: Uses LLM native tool calling APIs
- **ReflectionAgent**: Self-reflective agent with memory
## Creating a Custom Agent
### Step 1: Define Your Agent Configuration
Create a configuration class that extends `BaseAgentConfig`:
```python
from dataclasses import dataclass
from mcpuniverse.agent.base import BaseAgentConfig
@dataclass
class MyCustomAgentConfig(BaseAgentConfig):
"""Configuration for your custom agent."""
# Add custom configuration parameters
max_retries: int = 3
temperature: float = 0.7
enable_memory: bool = True
custom_prompt_path: str = "custom_prompt.j2"
# You can override default values
system_prompt: str = "path/to/your/custom_system_prompt.j2"
max_iterations: int = 10
```
### Step 2: Implement Your Custom Agent Class
Create your agent class by inheriting from `BaseAgent`:
```python
from typing import Optional, Union, Dict, List
from mcpuniverse.agent.base import BaseAgent
from mcpuniverse.agent.types import AgentResponse
from mcpuniverse.mcp.manager import MCPManager
from mcpuniverse.llm.base import BaseLLM
from mcpuniverse.tracer import Tracer
from mcpuniverse.agent.utils import build_system_prompt
class MyCustomAgent(BaseAgent):
"""A custom agent implementation."""
# Required class attributes
config_class = MyCustomAgentConfig
alias = ["custom", "my-agent"] # Alternative names for agent registration
def __init__(
self,
mcp_manager: Optional[MCPManager] = None,
llm: BaseLLM = None,
config: Optional[Union[Dict, str]] = None,
**kwargs
):
"""Initialize your custom agent."""
super().__init__(mcp_manager=mcp_manager, llm=llm, config=config)
# Initialize any custom attributes
self._custom_memory = []
self._retry_count = 0
async def _initialize(self):
"""Optional: Initialize custom resources."""
# This method is called after MCP clients are set up
# Add any custom initialization logic here
pass
async def _execute(
self,
message: Union[str, List[str]],
**kwargs
) -> AgentResponse:
"""Main execution method - implement your agent logic here."""
# Get tracer for debugging
tracer = kwargs.get("tracer", Tracer())
callbacks = kwargs.get("callbacks", [])
# Build system prompt with tools
params = {"INSTRUCTION": self._config.instruction}
params.update(self._config.template_vars)
# Build system prompt using available tools
system_prompt = build_system_prompt(
system_prompt_template=self._config.system_prompt,
tool_prompt_template=self._config.tools_prompt,
tools=self._tools,
**params
)
# Process input message
if isinstance(message, (list, tuple)):
message = "\n".join(message)
# Implement your custom agent logic here
response = await self._custom_reasoning_loop(
system_prompt, message, tracer, callbacks
)
return AgentResponse(
name=self._name,
class_name=self.__class__.__name__,
response=response,
trace_id=tracer.trace_id
)
async def _custom_reasoning_loop(
self,
system_prompt: str,
user_message: str,
tracer: Tracer,
callbacks: List
) -> str:
"""Implement your custom reasoning logic."""
# Example: Multi-step reasoning with tool calls
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_message}
]
for iteration in range(self._config.max_iterations):
# Generate LLM response
llm_response = await self._llm.generate_async(
messages=messages,
tracer=tracer,
callbacks=callbacks,
remote_mcp=self.get_remote_mcp_list()
)
# Check if tool calling is needed
if self._should_call_tool(llm_response):
try:
tool_result = await self.call_tool(
llm_response, tracer=tracer, callbacks=callbacks
)
# Add tool result to conversation
messages.append({"role": "assistant", "content": llm_response})
messages.append({"role": "user", "content": f"Tool result: {tool_result}"})
except Exception as e:
# Handle tool call errors
error_msg = f"Tool call failed: {str(e)}"
messages.append({"role": "user", "content": error_msg})
else:
# Return final response
return llm_response
return "Maximum iterations reached"
def _should_call_tool(self, response: str) -> bool:
"""Determine if the response contains a tool call."""
# Implement your logic to detect tool calls
# This is a simple example - you might want more sophisticated parsing
try:
import json
parsed = json.loads(response.strip())
return "server" in parsed and "tool" in parsed and "arguments" in parsed
except:
return False
async def _cleanup(self):
"""Optional: Cleanup custom resources."""
# Clean up any resources your agent created
self._custom_memory.clear()
```
### Step 3: Create Custom Prompt Templates
Create Jinja2 templates for your agent's prompts:
**custom_system_prompt.j2:**
```jinja2
You are a specialized AI agent designed for {{INSTRUCTION}}.
{% if TOOLS_PROMPT is defined and TOOLS_PROMPT|length %}
{{TOOLS_PROMPT}}
When you need to use tools, respond with this JSON format:
{
"server": "server-name",
"tool": "tool-name",
"arguments": {"key": "value"}
}
{% endif %}
Follow these guidelines:
1. Be thorough in your analysis
2. Use tools when additional information is needed
3. Provide clear, actionable responses
4. If uncertain, ask clarifying questions
```
### Step 4: Register Your Agent
Create an `__init__.py` file or add to existing agent module:
```python
from .my_custom_agent import MyCustomAgent
# The agent will be automatically registered due to the metaclass
__all__ = [..., "MyCustomAgent"]
```
## Testing Your Custom Agent
Create tests for your agent:
```python
import pytest
from mcpuniverse.agent.my_custom_agent import MyCustomAgent
from mcpuniverse.llm.manager import ModelManager
from mcpuniverse.mcp.manager import MCPManager
@pytest.mark.asyncio
async def test_custom_agent():
# Setup
agent = MyCustomAgent(
mcp_manager=MCPManager(),
llm=ModelManager().build_model(name="openai"),
config={"name": "test-agent", "instruction": "Test agent"}
)
# Test initialization
await agent.initialize()
# Test execution
response = await agent.execute(message="Hello, world!")
assert response.name == "test-agent"
assert isinstance(response.response, str)
# Cleanup
await agent.cleanup()
```
## Best Practices
1. **Configuration Management**: Use YAML files for configuration and support environment variable substitution
2. **Error Handling**: Implement comprehensive error handling with meaningful error messages
3. **Logging**: Use the framework's logging system for debugging and monitoring
4. **Resource Cleanup**: Always implement proper cleanup in the `_cleanup` method
6. **Memory Management**: Consider memory usage for long-running agents
7. **Testing**: Write comprehensive tests for your agent's functionality
8. **Documentation**: Document your agent's capabilities, configuration options, and usage examples
## Troubleshooting
### Common Issues
1. **Agent Not Registered**: Ensure your agent class has the correct metaclass and is imported
2. **Tool Not Found**: Check that MCP servers are properly configured and tools are available
3. **Configuration Errors**: Validate YAML configuration files and required environment variables
### Debugging
Use the built-in tracing system:
```python
from mcpuniverse.tracer import Tracer
tracer = Tracer()
response = await agent.execute("test message", tracer=tracer)
# Examine trace data
trace_data = tracer.get_trace()
print(json.dumps(trace_data, indent=2))
```
## Conclusion
The MCPUniverse framework provides a powerful foundation for building custom AI agents. By following this guide, you can create sophisticated agents that leverage MCP tools, implement custom reasoning patterns, and integrate seamlessly with the broader MCPUniverse ecosystem.
For more examples and advanced patterns, refer to the existing agent implementations in the `mcpuniverse/agent/` directory.