24. OpenCLaw Implementation Project
Chapter 24 of 24 · 25 min
This chapter synthesizes the preceding material into a complete OpenCLaw implementation. The project builds incrementally, demonstrating how components integrate into a functional personal AI agent.
Project Structure
openclaw/
├── src/
│ ├── __init__.py
│ ├── core.py # Main agent implementation
│ ├── llm_client.py # LLM interface
│ ├── storage.py # Persistence layer
│ ├── context.py # Context management
│ ├── feedback.py # Learning system
│ ├── preferences.py # User preferences
│ ├── notifications.py # Notification system
│ ├── security.py # Authentication/authorization
│ ├── privacy.py # Privacy controls
│ ├── web.py # Web interface
│ ├── plugins/ # Plugin system
│ │ ├── __init__.py
│ │ ├── manager.py
│ │ └── sdk.py
│ └── utils/
│ ├── profiler.py
│ └── power.py
├── tests/
├── config/
├── plugins/
├── web_static/
└── main.py
Core Implementation
The main entry point initializes all components and starts the agent.
# main.py
import asyncio
from src.core import OpenCLawCore
from src.config import load_config
from src.storage import create_storage
from src.llm_client import create_llm_client
from src.web import WebInterface
from src.plugins.manager import PluginManager
async def main():
config = load_config('config/openclaw.yaml')
storage = create_storage(config.storage)
llm_client = create_llm_client(config.llm)
core = OpenCLawCore(
config=config.core,
storage=storage,
llm_client=llm_client
)
plugin_manager = PluginManager(
config.plugins.directory,
core.api
)
plugin_manager.load_all()
web_interface = WebInterface(core, config.web)
await core.start()
await web_interface.start()
print(f"OpenCLaw running on {config.web.host}:{config.web.port}")
try:
await asyncio.Event().wait()
except KeyboardInterrupt:
print("\nShutting down...")
await core.stop()
await web_interface.stop()
if __name__ == '__main__':
asyncio.run(main())
Complete Agent Class
The core agent class orchestrates all components.
# src/core.py
from dataclasses import dataclass
from typing import Optional, Dict, Any
from datetime import datetime
import asyncio
from .llm_client import LLMClient
from .storage import StorageBackend
from .context import ContextManager
from .feedback import FeedbackCapture
from .preferences import PreferenceLearner, PreferenceMixer
from .notifications import NotificationManager
from .security import AuthService, AuthzService
from .privacy import PrivacyManager
@dataclass
class CoreConfig:
max_context_tokens: int = 8192
feedback_threshold: int = 10
retention_days: int = 90
class OpenCLawCore:
def __init__(
self,
config: CoreConfig,
storage: StorageBackend,
llm_client: LLMClient
):
self.config = config
self.storage = storage
self.llm = llm_client
self.context_manager = ContextManager(config.max_context_tokens)
self.feedback_capture = FeedbackCapture(storage)
self.preference_learner = PreferenceLearner(storage)
self.notification_manager = NotificationManager()
self.privacy_manager = PrivacyManager(storage)
self.auth = AuthService()
self.authz = AuthzService(self.auth)
self.running = False
@property
def api(self):
return CoreAPI(self)
async def start(self):
self.running = True
await self.privacy_manager.apply_retention_policies()
async def stop(self):
self.running = False
async def process_message(
self,
message: str,
user_id: str = 'default',
context: Optional[Dict] = None
) -> Response:
user_context = self.context_manager.get_context(user_id)
preference_mixer = self.preference_learner.get_mixer(user_id)
response_config = preference_mixer.mix_response_config(
ResponseConfig(),
task_complexity=0.5
)
prompt = self.build_prompt(
message,
user_context,
response_config
)
llm_response = await self.llm.generate(
prompt,
max_tokens=response_config.max_tokens,
temperature=response_config.temperature
)
self.context_manager.add_message(
user_id,
Message(role="user", content=message, timestamp=datetime.utcnow())
)
self.context_manager.add_message(
user_id,
Message(role="assistant", content=llm_response.content, timestamp=datetime.utcnow())
)
self.preference_learner.observe_interaction(
user_id,
Interaction(message, llm_response.content)
)
return Response(
content=llm_response.content,
message_id=llm_response.message_id,
confidence=llm_response.confidence
)
def build_prompt(self, message: str, context: Any, config: ResponseConfig) -> str:
system_prompt = self.get_system_prompt(config)
context_text = context.to_prompt_string() if context else ""
return f"{system_prompt}\n\n{context_text}\nUser: {message}\nAssistant:"
class ResponseConfig:
verbosity: float = 0.5
format_hint: str = "auto"
max_tokens: int = 1000
temperature: float = 0.7
class Response:
content: str
message_id: str
confidence: float
Running the Project
# Installation
pip install -e .
# Configuration
cp config/openclaw.example.yaml config/openclaw.yaml
# Edit config/openclaw.yaml with your settings
# Run tests
pytest tests/ -v
# Start the agent
python main.py
Exercise Project: Build a Custom Plugin
Create a weather plugin that enhances OpenCLaw with weather information:
- Create
plugins/weather/__init__.pyandplugins/weather/weather.py - Implement
PluginInterfacewith metadata - Register a
message_processorcapability - Add weather query handling to extract location and date from user messages
- Fetch weather data from an external API
- Format weather response according to user preferences
- Test the plugin integration
# plugins/weather/weather.py
from src.plugins.sdk import PluginInterface, PluginMetadata, PluginContext, Capability, CapabilityType
class WeatherPlugin(PluginInterface):
def get_metadata(self) -> PluginMetadata:
return PluginMetadata(
name="weather",
version="1.0.0",
author="OpenCLaw",
description="Provides weather information for user queries"
)
def initialize(self, context: PluginContext) -> bool:
self.context = context
self.api_key = context.config.get('weather_api_key')
self.capability = Capability(
name="weather_query",
ctype=CapabilityType.MESSAGE_PROCESSOR,
priority=50,
handler=self.process_weather_query
)
self.context.api.register_capability("weather", self.capability)
return True
def shutdown(self):
pass
def process_weather_query(self, message: str, state: Dict) -> Optional[Dict]:
location = self.extract_location(message)
if not location:
return None
weather_data = self.fetch_weather(location)
return {
'enhanced': True,
'weather': weather_data,
'response_modifier': lambda r: f"{r}\n\nWeather in {location}: {weather_data['description']}, {weather_data['temp']}°"
}
def extract_location(self, text: str) -> Optional[str]:
# Implementation
pass
def fetch_weather(self, location: str) -> Dict:
# Implementation
pass
EXERCISE
Extend the OpenCLaw implementation with a memory persistence feature that survives restarts. Implement conversation state serialization and deserialization. Add a migration system for handling schema updates across versions.