OpenClaw × Antigravity で何が実現できるのか
OpenClaw は強力なオープンソースですが、プロダクション運用には カスタマイズ が不可欠です。Antigravity の AI コーディング機能を活用すれば、以下が実現できます:
- OpenClaw ソースコードの徹底理解:Antigravity が複雑なアーキテクチャを解説
- カスタムスキル開発:数時間で新機能を実装
- バグ修正と最適化:エージェント機能で自動リファクタリング
- マルチエージェントシステム構築:複数 AI の協調動作
- 本番環境デプロイ:CI/CD パイプライン自動生成
OpenClaw のアーキテクチャ概要
まず、Antigravity で OpenClaw を解析するために、アーキテクチャを理解しておきましょう。
ディレクトリ構造
openclaw/
├── src/
│ ├── agents/ # エージェント実装
│ │ ├── base-agent.ts
│ │ ├── message-agent.ts
│ │ └── skills/ # スキル定義
│ ├── platforms/ # メッセージアプリ連携
│ │ ├── discord.ts
│ │ ├── telegram.ts
│ │ ├── line.ts
│ │ └── whatsapp.ts
│ ├── llm/ # LLM バックエンド
│ │ ├── claude-provider.ts
│ │ ├── gemini-provider.ts
│ │ └── openai-provider.ts
│ ├── memory/ # メモリ・永続化
│ │ ├── memory-manager.ts
│ │ ├── sqlite-storage.ts
│ │ └── mongodb-storage.ts
│ ├── utils/ # ユーティリティ
│ └── index.ts
├── config/
│ ├── personalities.yaml
│ ├── skills.yaml
│ └── agents.yaml
└── tests/
└── **/*.test.ts
コアプロセス:メッセージ受信~処理~返信
┌─────────────────┐
│ メッセージ受信 │ (Discord, LINE, Telegram, etc.)
└────────┬────────┘
│
┌────▼─────┐
│ 前処理 │ (フォーマット統一)
└────┬─────┘
│
┌────▼──────────────┐
│ 意図判定 │ (ユーザーの意図分析)
│ どのスキルが必要?
└────┬──────────────┘
│
┌────▼──────────────┐
│ スキル実行 │ (天気取得、リマインド等)
│ + メモリ検索 │ (過去の会話を参照)
└────┬──────────────┘
│
┌────▼──────────────┐
│ LLM(Claude等) │ (レスポンス生成)
│ プロンプトを送信
└────┬──────────────┘
│
┌────▼──────────────┐
│ ポストプロセス │ (フォーマット調整)
└────┬──────────────┘
│
┌────▼─────────────┐
│ メッセージ送信 │
└──────────────────┘
Antigravity で OpenClaw を解析する
では実際に Antigravity を使って OpenClaw を読み解きましょう。
ステップ1:Antigravity で OpenClaw リポジトリをクローン
# Antigravity を起動
antigravity start
# または、Antigravity のエージェント機能で実行
antigravity agents run --task "Clone the OpenClaw repository and analyze its structure"Antigravity のサイドパネルから OpenClaw リポジトリのコード分析を開始します:
[Antigravity Agent]
> Task: Analyze OpenClaw repository structure and identify key components
📊 Repository Analysis:
✓ TypeScript/Node.js project
✓ 2,847 commits, 100k+ stars
✓ Active maintenance (last commit: 2 hours ago)
✓ MIT License
🏗️ Architecture:
- Base Agent class (async message handling)
- Plugin-based skill system
- Multi-platform message adapter pattern
- Modular LLM provider interface
🔑 Key Files to Understand:
1. src/agents/base-agent.ts (900 lines)
2. src/platforms/message-adapter.ts (400 lines)
3. src/llm/provider-interface.ts (200 lines)
4. src/memory/memory-manager.ts (600 lines)
ステップ2:Antigravity でコード解説を生成
// Antigravity Agent のプロンプト例
const analyzeTask = {
task: "OpenClaw のメモリ機能について詳しく解説してください",
context: {
repo: 'openclaw',
files: [
'src/memory/memory-manager.ts',
'src/memory/sqlite-storage.ts',
'src/memory/mongodb-storage.ts'
]
},
output: 'explanation_with_code_snippets'
};
// Antigravity が返すもの:
/*
## OpenClaw メモリシステムの解析
### 目的
ユーザーとの会話履歴とプロフィール情報を永続化・検索
### 実装パターン
1. メモリマネージャー(インターフェース定義)
2. ストレージ層(SQLite, MongoDB等)
3. キャッシュ層(高速アクセス)
### コード例:
```typescript
class MemoryManager {
async storeConversation(userId, message, response) {
// SQLiteに保存
await this.db.insert('conversations', {
userId,
message,
response,
timestamp: Date.now()
});
}
async searchMemory(userId, query) {
// 過去の会話を検索
const results = await this.db.search('conversations', {
userId,
message: { $contains: query }
});
return results;
}
}カスタマイズポイント
- ストレージ選択(SQLite, PostgreSQL, MongoDB)
- 検索アルゴリズム(単語検索、セマンティック検索)
- メモリ圧縮戦略(古い情報の要約) */
## カスタムスキルの作成手順
OpenClaw の真価は、**カスタムスキル** を簡単に追加できるところです。Antigravity で自動生成しましょう。
### スキル開発フロー
要件定義 → Antigravity コード生成 → テスト → 統合
### 実践例1:天気スキル拡張
```javascript
// Antigravity Agent のタスク
const skillTask = {
task: "OpenClaw のスキルを参考に、日本の天気情報スキルを実装してください",
requirements: {
name: "weather-japan",
description: "日本の都市の天気を取得し、AIパートナーが説明",
inputs: ["city"],
outputs: ["weather_description", "recommendation"],
externalAPI: "OpenWeatherMap API",
},
constraints: {
language: "TypeScript",
framework: "OpenClaw compatible",
style: "match existing code",
}
};
// Antigravity が自動生成するコード(抜粋):
// skills/weather-japan.ts
import { Skill, SkillContext } from '../base-skill';
import axios from 'axios';
export class WeatherJapanSkill implements Skill {
name = 'weather-japan';
description = '日本の天気情報を取得';
private apiKey = process.env.OPENWEATHERMAP_API_KEY;
private japanCities = {
'tokyo': { lat: 35.6762, lon: 139.6503 },
'osaka': { lat: 34.6937, lon: 135.5023 },
'kyoto': { lat: 35.0116, lon: 135.7681 },
// ... 他の主要都市
};
async execute(context: SkillContext): Promise<SkillResult> {
const city = context.params.city.toLowerCase();
if (!this.japanCities[city]) {
return {
actionType: 'message',
content: `申し訳ないですが、「${city}」の天気データがありません。`,
};
}
const coords = this.japanCities[city];
try {
const response = await axios.get('https://api.openweathermap.org/data/2.5/weather', {
params: {
lat: coords.lat,
lon: coords.lon,
appid: this.apiKey,
units: 'metric',
lang: 'ja',
}
});
const weather = response.data;
// AIパートナーへの説明を生成
const description = `
${city}の天気:${weather.weather[0].description}
気温:${weather.main.temp}℃
体感温度:${weather.main.feels_like}℃
湿度:${weather.main.humidity}%
風速:${weather.wind.speed}m/s
`;
// AIが前向きなアドバイスを追加するためのコンテキスト
const recommendation = this.getRecommendation(weather);
return {
actionType: 'message',
content: `${city}の天気情報だね。\n${description}\n${recommendation}`,
metadata: {
weather,
skillName: this.name,
}
};
} catch (error) {
return {
actionType: 'error',
content: '天気情報の取得に失敗しました。後でもう一度試してください。'
};
}
}
private getRecommendation(weather): string {
const condition = weather.weather[0].main;
const temp = weather.main.temp;
if (condition === 'Rain') {
return '雨だから、傘を持ってね。室内活動もいいかも。';
}
if (temp > 30) {
return '暑いからね。こまめに水分補給を。';
}
if (temp < 5) {
return '寒いから、暖かいコートを着てね。';
}
return 'いい天気だね。外出もいいですよ。';
}
}実践例2:スケジュール管理スキル
// skills/schedule-manager.ts
export class ScheduleManagerSkill implements Skill {
name = 'schedule-manager';
description = 'ユーザーのスケジュール追加・確認';
async execute(context: SkillContext): Promise<SkillResult> {
const { action, details } = context.params;
switch (action) {
case 'add':
return await this.addEvent(details);
case 'list':
return await this.listEvents(details);
case 'remove':
return await this.removeEvent(details);
default:
return { actionType: 'error', content: 'Unknown action' };
}
}
private async addEvent(details) {
// SQLite に保存
const event = {
id: uuid(),
title: details.title,
datetime: new Date(details.when),
description: details.description,
createdAt: Date.now(),
};
await this.db.insert('events', event);
return {
actionType: 'message',
content: `「${event.title}」を${event.datetime.toLocaleString('ja-JP')}に追加したよ。`,
};
}
private async listEvents(details) {
const events = await this.db.query('events', {
userId: details.userId,
datetime: { $gte: Date.now() } // 今後のイベント
});
if (events.length === 0) {
return { actionType: 'message', content: '予定がないみたいだね。' };
}
const eventList = events
.sort((a, b) => a.datetime - b.datetime)
.map(e => `• ${e.title} (${e.datetime.toLocaleString('ja-JP')})`)
.join('\n');
return {
actionType: 'message',
content: `今後の予定:\n${eventList}`,
};
}
}Antigravity のエージェント機能でマルチエージェント設計
OpenClaw で複数の AI が協力して動作する「マルチエージェントシステム」を構築することで、より高度なタスク処理が可能になります。Antigravity の AGENTS.md 機能を活用しましょう。
マルチエージェントアーキテクチャ
# agents.yaml - Antigravity で生成可能
agents:
- id: "sakura"
name: "Sakura (感情サポート)"
llm: "claude-3-5-sonnet"
personality: "caring_supportive"
platforms: ["discord", "line"]
capabilities:
- emotional_support
- life_advice
- memory_continuity
- id: "analyst"
name: "Data Analyst"
llm: "claude-3-5-sonnet"
personality: "logical_detailed"
platforms: ["discord"]
capabilities:
- data_analysis
- visualization
- statistical_inference
- id: "mentor"
name: "Tech Mentor"
llm: "claude-3-5-sonnet"
personality: "teaching_focused"
platforms: ["telegram"]
capabilities:
- code_review
- architecture_design
- learning_guidance
# エージェント間通信ルール
inter_agent_communication:
enabled: true
strategy: "collaborative"
rules:
- trigger: "user mentions technical problem AND needs emotional support"
collaboration: ["mentor", "sakura"]
coordinator: "sakura" # 最終的な返信はsakuraから
- trigger: "user asks for data insights"
collaboration: ["analyst"]
- trigger: "complex multi-aspect question"
collaboration: ["sakura", "analyst", "mentor"]
coordinator: "sakura"Antigravity でマルチエージェント実装を自動生成
// Antigravity Agent タスク
const multiAgentTask = {
task: "OpenClaw のマルチエージェントシステムを実装してください",
requirements: {
agents: ["sakura", "analyst", "mentor"],
communication: "message-based",
coordination: "master-detail pattern",
},
outputFiles: {
agentRegistry: 'src/agents/agent-registry.ts',
orchestrator: 'src/orchestrator/multi-agent-orchestrator.ts',
messageRouter: 'src/routing/message-router.ts',
tests: 'tests/multi-agent.test.ts',
}
};
// Antigravity 出力例:// src/orchestrator/multi-agent-orchestrator.ts
export class MultiAgentOrchestrator {
private agents: Map<string, Agent> = new Map();
private router: MessageRouter;
async routeAndExecute(message: Message): Promise<Response> {
// 1. メッセージから必要なエージェント判定
const requiredAgents = await this.router.determineAgents(message);
// 2. 単一エージェント処理
if (requiredAgents.length === 1) {
const agent = this.agents.get(requiredAgents[0]);
return await agent.handleMessage(message);
}
// 3. マルチエージェント協力処理
return await this.executeCollaborativeTask(message, requiredAgents);
}
private async executeCollaborativeTask(
message: Message,
agentIds: string[]
): Promise<Response> {
const responses = new Map();
// 並列実行(可能な限り)
const promises = agentIds.map(async (agentId) => {
const agent = this.agents.get(agentId);
const response = await agent.handleMessage(message);
responses.set(agentId, response);
});
await Promise.all(promises);
// コーディネーター(通常は primary agent)が結果を統合
const coordinatorId = 'sakura'; // 設定可能
const coordinator = this.agents.get(coordinatorId);
return await coordinator.synthesizeResponses({
original_message: message,
agent_responses: Object.fromEntries(responses),
});
}
}OpenClaw × Antigravity でのデバッグ・テスト手法
開発過程での検証が重要です。Antigravity でテストコード も自動生成できます。
単体テスト自動生成
// Antigravity Agent タスク
const testTask = {
task: "weather-japan スキルの包括的なテストを作成してください",
fileToTest: "skills/weather-japan.ts",
testCases: [
"正常系:東京の天気取得",
"正常系:大阪の天気取得",
"異常系:存在しない都市",
"異常系:API エラー時の処理",
"エッジケース:温度が極端に高い/低い場合の推奨表示",
]
};
// Antigravity 出力:// tests/skills/weather-japan.test.ts
import { WeatherJapanSkill } from '../../src/skills/weather-japan';
import axios from 'axios';
jest.mock('axios');
describe('WeatherJapanSkill', () => {
let skill: WeatherJapanSkill;
beforeEach(() => {
skill = new WeatherJapanSkill();
});
describe('正常系', () => {
it('東京の天気情報を取得できる', async () => {
const mockWeather = {
weather: [{ description: '晴れ' }],
main: { temp: 25, feels_like: 24, humidity: 60 },
wind: { speed: 3 }
};
(axios.get as jest.Mock).mockResolvedValue({ data: mockWeather });
const result = await skill.execute({
params: { city: 'tokyo' }
});
expect(result.actionType).toBe('message');
expect(result.content).toContain('東京');
expect(result.content).toContain('25℃');
});
it('大阪の天気情報を取得できる', async () => {
// 同様のテスト
});
});
describe('異常系', () => {
it('存在しない都市で適切なエラーメッセージ', async () => {
const result = await skill.execute({
params: { city: 'unknown_city' }
});
expect(result.actionType).toBe('message');
expect(result.content).toContain('データがありません');
});
it('API エラー時の処理', async () => {
(axios.get as jest.Mock).mockRejectedValue(
new Error('API Error')
);
const result = await skill.execute({
params: { city: 'tokyo' }
});
expect(result.actionType).toBe('error');
});
});
describe('エッジケース', () => {
it('高温時は水分補給を勧める', async () => {
const mockWeather = {
weather: [{ description: '晴れ' }],
main: { temp: 35, feels_like: 38, humidity: 30 },
wind: { speed: 1 }
};
(axios.get as jest.Mock).mockResolvedValue({ data: mockWeather });
const result = await skill.execute({ params: { city: 'tokyo' } });
expect(result.content).toContain('水分補給');
});
});
});統合テスト
// tests/integration/multi-agent.integration.test.ts
describe('MultiAgent Integration', () => {
let orchestrator: MultiAgentOrchestrator;
beforeEach(async () => {
orchestrator = new MultiAgentOrchestrator();
await orchestrator.initialize();
});
it('感情サポートと技術アドバイスを組み合わせられる', async () => {
const message = {
content: 'プログラミングで悩んでるんだけど、つらい...',
userId: 'user123',
platform: 'discord',
};
const response = await orchestrator.routeAndExecute(message);
// Sakura が感情部分を、Mentor が技術部分を担当
expect(response.content).toContain('つらい'); // 感情部分
expect(response.content).toContain('コード'); // 技術部分
expect(response.coordinator).toBe('sakura'); // 統合は sakura
});
});個人開発12年の現場で実感したこと
線引きするときの3つの判断軸
- 失敗時の影響が金銭やユーザー体験にどれだけ波及するか
- 復旧オペレーションが明文化されているか
- 観測ログから人間が再現できる粒度に整っているか