Skip to main content

build-your-own-openclaw-agent-tutorial

Step-by-step guide to building AI agents from simple chat loops to autonomous multi-agent systems with tools, memory, and event-driven architecture

Ir para a instalação

Informações da origem

Repositório
reason-machines/hermes-skills
Última atividade na origem
23 de maio de 2026 às 00:34
Idioma detectado do SKILL.md
inglês
Estrelas
5
Forks
0

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
build-your-own-openclaw-agent-tutorial
description
Step-by-step guide to building AI agents from simple chat loops to autonomous multi-agent systems with tools, memory, and event-driven architecture
triggers
["how do I build an AI agent from scratch","teach me to create an autonomous agent","show me how to build an OpenClaw-style agent","guide me through building an agent with tools and memory","help me create a multi-agent system","how to build an event-driven AI agent","create an agent with scheduled tasks and persistence","build an agent that can use tools and skills"]
# Build Your Own OpenClaw Agent Tutorial > Skill by [ara.so](https://ara.so) — Hermes Skills collection. A comprehensive tutorial for building AI agents progressively, from a basic chat loop to a production-ready autonomous agent system. This project walks through 18 steps covering single-agent capabilities, event-driven architecture, multi-agent collaboration, and production features like memory and concurrency control. ## What This Tutorial Teaches The tutorial is organized into 4 phases: 1. **Phase 1 (Steps 0-6)**: Single agent with tools, skills, persistence, and web access 2. **Phase 2 (Steps 7-10)**: Event-driven architecture with multi-platform support 3. **Phase 3 (Steps 11-15)**: Autonomous agents with routing and collaboration 4. **Phase 4 (Steps 16-17)**: Production features like concurrency and long-term memory ## Initial Setup ### Clone the Repository ```bash git clone https://github.com/czl9707/build-your-own-openclaw.git cd build-your-own-openclaw ``` ### Configure API Keys ```bash # Copy example config cp default_workspace/config.example.yaml default_workspace/config.user.yaml ``` Edit `default_workspace/config.user.yaml`: ```yaml llm: model: "gpt-4" # or anthropic/claude-3-5-sonnet-20241022, etc. api_key: "${OPENAI_API_KEY}" # Use environment variable # See https://docs.litellm.ai/docs/providers for all providers # Optional: Add additional services web: search_api_key: "${SERPER_API_KEY}" ``` ### Install Dependencies (for each step) ```bash cd 00-chat-loop # or any step directory pip install -r requirements.txt ``` ## Phase 1: Building a Capable Single Agent ### Step 0: Basic Chat Loop The foundation - a simple conversation loop with an LLM. ```python # 00-chat-loop/main.py from litellm import completion def chat_loop(): messages = [] while True: user_input = input("You: ") if user_input.lower() in ['/exit', '/quit']: break messages.append({"role": "user", "content": user_input}) response = completion( model="gpt-4", messages=messages, api_key="${OPENAI_API_KEY}" ) assistant_message = response.choices[0].message.content messages.append({"role": "assistant", "content": assistant_message}) print(f"Assistant: {assistant_message}") if __name__ == "__main__": chat_loop() ``` Run it: ```bash cd 00-chat-loop python main.py ``` ### Step 1: Adding Tools Give your agent function-calling capabilities. ```python # 01-tools/tools.py def get_current_weather(location: str) -> dict: """Get the current weather for a location.""" # Tool implementation return {"location": location, "temperature": 72, "condition": "sunny"} # Tool schema for LLM weather_tool = { "type": "function", "function": { "name": "get_current_weather", "description": "Get the current weather in a given location", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "City name, e.g. San Francisco" } }, "required": ["location"] } } } ``` Using tools in the chat loop: ```python import json from litellm import completion response = completion( model="gpt-4", messages=messages, tools=[weather_tool], tool_choice="auto" ) # Handle tool calls if response.choices[0].message.tool_calls: for tool_call in response.choices[0].message.tool_calls: function_name = tool_call.function.name arguments = json.loads(tool_call.function.arguments) # Execute tool result = get_current_weather(**arguments) # Add tool result to messages messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(result) }) ``` ### Step 2: Skills with SKILL.md Extend agent capabilities through markdown skill files. ```markdown <!-- skills/web_search.md --> # Web Search Skill You can search the internet using the `search_web` tool. ## When to Use - User asks for current information - Need to verify facts - Looking for recent news ## Example User: "What's the latest news on AI?" You: Let me search for that. [calls search_web("latest AI news")] ``` Loading skills: ```python # 02-skills/skill_loader.py import os def load_skills(skills_dir="skills"): """Load all .md files from skills directory.""" skills_content = [] for filename in os.listdir(skills_dir): if filename.endswith(".md"): with open(os.path.join(skills_dir, filename), 'r') as f: skills_content.append(f.read()) return "\n\n".join(skills_content) # Add to system prompt system_prompt = f"""You are a helpful assistant. ## Your Skills {load_skills()} """ ``` ### Step 3: Conversation Persistence Save conversations to resume later. ```python # 03-persistence/session_manager.py import json from datetime import datetime from pathlib import Path class SessionManager: def __init__(self, sessions_dir="sessions"): self.sessions_dir = Path(sessions_dir) self.sessions_dir.mkdir(exist_ok=True) def save_session(self, session_id: str, messages: list): """Save conversation history.""" session_file = self.sessions_dir / f"{session_id}.json" data = { "session_id": session_id, "updated_at": datetime.now().isoformat(), "messages": messages } with open(session_file, 'w') as f: json.dump(data, f, indent=2) def load_session(self, session_id: str) -> list: """Load conversation history.""" session_file = self.sessions_dir / f"{session_id}.json" if not session_file.exists(): return [] with open(session_file, 'r') as f: data = json.load(f) return data.get("messages", []) def list_sessions(self) -> list: """List all available sessions.""" return [f.stem for f in self.sessions_dir.glob("*.json")] ``` Usage: ```python manager = SessionManager() # Load or create session session_id = "my-conversation" messages = manager.load_session(session_id) # After each exchange manager.save_session(session_id, messages) ``` ### Step 4: Slash Commands Direct user control over agent behavior. ```python # 04-slash-commands/commands.py class CommandHandler: def __init__(self, session_manager): self.session_manager = session_manager self.commands = { '/new': self.new_session, '/load': self.load_session, '/list': self.list_sessions, '/save': self.save_session, '/clear': self.clear_session, '/help': self.show_help } def handle(self, user_input: str, current_session: str, messages: list): """Handle slash commands.""" parts = user_input.split() command = parts[0] args = parts[1:] if len(parts) > 1 else [] if command in self.commands: return self.commands[command](args, current_session, messages) return None # Not a command def new_session(self, args, current_session, messages): new_id = args[0] if args else f"session_{int(time.time())}" return {"action": "new_session", "session_id": new_id} def load_session(self, args, current_session, messages): if not args: print("Usage: /load <session_id>") return {"action": "none"} loaded = self.session_manager.load_session(args[0]) return {"action": "load_session", "session_id": args[0], "messages": loaded} ``` ### Step 5: Context Compaction Manage token limits by summarizing old messages. ```python # 05-compaction/compactor.py from litellm import completion class MessageCompactor: def __init__(self, max_messages=20): self.max_messages = max_messages def compact_if_needed(self, messages: list) -> list: """Compact messages if they exceed threshold.""" if len(messages) <= self.max_messages: return messages # Keep system message and recent messages system_msgs = [m for m in messages if m["role"] == "system"] recent_msgs = messages[-(self.max_messages - 2):] # Summarize older messages old_msgs = messages[len(system_msgs):-len(recent_msgs)] summary = self._summarize_messages(old_msgs) return system_msgs + [ {"role": "system", "content": f"Previous conversation summary:\n{summary}"} ] + recent_msgs def _summarize_messages(self, messages: list) -> str: """Generate summary of message history.""" conversation = "\n".join([ f"{m['role']}: {m['content']}" for m in messages ]) response = completion( model="gpt-4", messages=[{ "role": "user", "content": f"Summarize this conversation concisely:\n\n{conversation}" }] ) return response.choices[0].message.content ``` ### Step 6: Web Tools Give your agent internet access. ```python # 06-web-tools/web_tools.py import requests import os def search_web(query: str, num_results: int = 5) -> list: """Search the web using Serper API.""" api_key = os.getenv("SERPER_API_KEY") response = requests.post( "https://google.serper.dev/search", headers={"X-API-KEY": api_key}, json={"q": query, "num": num_results} ) results = response.json() return [ { "title": r.get("title"), "link": r.get("link"), "snippet": r.get("snippet") } for r in results.get("organic", []) ] def fetch_webpage(url: str) -> str: """Fetch and extract text from a webpage.""" from bs4 import BeautifulSoup response = requests.get(url, timeout=10) soup = BeautifulSoup(response.content, 'html.parser') # Remove script and style elements for script in soup(["script", "style"]): script.decompose() return soup.get_text(separator="\n", strip=True) ``` ## Phase 2: Event-Driven Architecture ### Step 7: Event-Driven Refactor Decouple components with an event bus. ```python # 07-event-driven/event_bus.py from typing import Callable, Dict, List from dataclasses import dataclass from enum import Enum class EventType(Enum): MESSAGE_RECEIVED = "message_received" MESSAGE_SENT = "message_sent" TOOL_CALLED = "tool_called" SESSION_CREATED = "session_created" @dataclass class Event: type: EventType data: dict source: str class EventBus: def __init__(self): self.listeners: Dict[EventType, List[Callable]] = {} def subscribe(self, event_type: EventType, handler: Callable): """Subscribe to an event type.""" if event_type not in self.listeners: self.listeners[event_type] = [] self.listeners[event_type].append(handler) def publish(self, event: Event): """Publish an event to all subscribers.""" if event.type in self.listeners: for handler in self.listeners[event.type]: handler(event) # Usage
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub