OpenClaw 風の AI エージェント。Vercel AI SDK + Chat SDK + OpenAI 互換 LLM + Discord で構築する。
- ファイルベースのコアメモリ・セッション管理(write-through キャッシュ付き)
- SQLite による日記永続化と直近範囲検索、LLM による定期メモリ整理
- SQLite によるユーザー情報・プロフィール永続化と応答後の自動評価更新
data/AGENT.md/data/RULES.md/data/skills/*/SKILL.md/data/system-skills/*/SKILL.mdによる Markdown-first の prompt / skill 拡張- trusted prompt context / skills は
fs.watch()で eager reload、memory は write-through + watcher で外部変更に追随 webFetch/webSearchによる Web 情報取得(Readability + Brave Search API)KARAKURI_WORLD_BOT_IDSに一致する Discord ユーザー向けの karakuri-world 専用 KW モード(Discord のnotification_idから自動でget_notificationし、取得したnotification.choices[]からkarakuri_world_commandを1回だけ実行。commentのキャラ口調の判断コメントを返信に使用)- provider namespaced な
sns_<provider>_<action>ツールによる Mastodon / X / ELYTH 向け SNS 投稿・取得・通知確認・メディアアップロード(skill-gated。例:sns_mastodon_post,sns_x_like,sns_elyth_get_thread)。SNS 専用ループが provider ごとにビルトイン SNS スキル(sns-mastodon/sns-x/sns-elyth)を自動ロードし、投稿や通知対応を行う。X / ELYTH はpublic投稿のみ対応、ELYTH は repost / quote / メディアアップロード非対応) data/HEARTBEAT.mdとdata/cron/*/CRON.mdによる Heartbeat / Cron 実行MEMORY_MAINTENANCE_INTERVAL_MINUTESによるコアメモリ・日記の自動整理postMessage/manageCronツールによる管理者限定のプロアクティブ投稿と Cron 管理REPORT_CHANNEL_IDへの Heartbeat / Cron / memory maintenance / SNS ループの実行結果、Cron 登録変更、チャット処理エラー詳細、SNS context のカーソル保存まわり警告の通知- Discord メッセージに処理状態を表すリアクション絵文字を表示(完了は 2 秒後に除去、エラーは保持)
- 各層をインターフェースで抽象化し、実装の差し替えが容易
- v1 はテキストメッセージのみ対応
cp .env.example .env.envに Discord / LLM の設定を入力(LLM_BASE_URLは OpenAI 互換 API を使うときのみ設定。http/httpsのみ受け付け、末尾の/は正規化される。BRAVE_API_KEYを設定するとwebSearchも有効化。未設定でもwebFetchは利用可能。KARAKURI_WORLD_API_BASE_URLとKARAKURI_WORLD_API_KEYを両方設定すると、KARAKURI_WORLD_BOT_IDSに一致する Discord ユーザーは karakuri-world 専用 KW モードで動作する。Base URL は最新の karakuri-world と同じhttps://.../api形式を正とし、/apiなしの値は起動時に補完される。Discord 通知本文のnotification_idから保存済み通知を自動取得し、LLM には汎用karakuri_world_commandだけを公開する。commentフィールドのキャラ口調の判断コメントが Discord 返信として使われる。get_notificationが失敗した通知(ログアウト通知など)は LLM に渡さずログだけ残してスキップする。SNS は provider ごとの環境変数で同時有効化される。Mastodon はMASTODON_INSTANCE_URL/MASTODON_ACCESS_TOKEN、X はX_ACCESS_TOKEN(必要ならX_CLIENT_ID/X_CLIENT_SECRET/X_REFRESH_TOKENまたはX_API_KEY/X_API_SECRET/X_ACCESS_TOKEN_SECRET)、ELYTH はELYTH_API_KEY/ELYTH_API_BASE(例:https://elythworld.com)を両方設定する。system ユーザー向けには provider 別のビルトイン SNS スキル(sns-mastodon/sns-x/sns-elyth)が追加され、cron ではloadSkill("sns-mastodon")のように provider 名付き skill を使う。SNS 専用ループでは provider ごとに動的コンテキストとsns_<provider>_<action>ツールが自動ロードされる。SNS_LOOP_MIN_INTERVAL_MINUTES/SNS_LOOP_MAX_INTERVAL_MINUTESでループ間隔を指定できる(デフォルト 60〜180 分)。data/system-skills/sns-*/SKILL.mdは不要で、同名の system skill が存在しても system ユーザー文脈ではビルトイン定義が優先される。対話ユーザーにも公開したい場合は、運用側でdata/skills/*/SKILL.mdに shared skill を追加する。必要ならPOST_RESPONSE_LLM_MODEL/POST_RESPONSE_LLM_API_KEY/POST_RESPONSE_LLM_BASE_URLで応答後評価専用モデルを分離できる)- 旧
SNS_PROVIDER/SNS_*credentials は読み込まれない。既存の Mastodon 運用はMASTODON_INSTANCE_URL+MASTODON_ACCESS_TOKENへ移行する。旧data/sns-activity.dbがある場合は初回起動前にSNS_LEGACY_DB_MIGRATE_TO=mastodon|x|elyth|skipを一度だけ指定する - X で
X_REFRESH_TOKENを使う場合、OAuth 2.0 の refresh-token rotation 後の状態はDATA_DIR/sns-token-state.jsonに保存される。再起動後も継続利用するにはDATA_DIRを永続化する LLM_MODELはopenai/gpt-4oのような OpenAI Responses API セレクタ、またはopenai/chat/gpt-4oのような OpenAI Chat API セレクタで指定する- 旧形式の bare model 名(例:
gpt-4o)も互換用に受け付けるが、内部ではopenai/gpt-4oとして扱う LLM_API_KEY未設定時のエラーでは legacy alias のOPENAI_API_KEYも案内する- Heartbeat / Cron を使う場合は
ALLOWED_CHANNEL_IDSとADMIN_USER_IDSを設定し、必要に応じてREPORT_CHANNEL_ID/HEARTBEAT_INTERVAL_MINUTESも指定(デフォルトは 120 分) MEMORY_MAINTENANCE_INTERVAL_MINUTESを設定すると、post-response 用 LLM 設定(未設定ならメイン LLM)を使う専用メモリメンテナンスループが有効化され、runExclusiveSystemTurn+ shared persistence mutex 下で core memory / diary を read → LLM → overwrite / rewrite / delete まで atomic に実行する。post-response evaluator / SNS 観測ユーザー評価は core memory 読み取りと LLM 判定を lock 外で行い、append/write の apply だけ同じ mutex に入るため、maintenance は background evaluator の LLM 待ちで system turn を止めない。maintenance は全 diary 日付を常に参照しつつ、MEMORY_MAINTENANCE_RECENT_DIARY_DAYSで既定 30 日分の diary 本文読み込み範囲だけを広げられるLLM_ENABLE_THINKING=falseにすると、通常応答・要約・post-response evaluator が no-thinking 設定を使う。memory maintenance は issue #58 の設計どおり、この設定に関係なく常に no-thinking で実行される
- 旧
cp -r data.example datanpm installnpm run dev
data.example/ にはサンプルの AGENT.md・RULES.md・スキル定義に加えて、HEARTBEAT.md と cron/daily-summary/CRON.md も含まれている。
data/ はユーザーごとにカスタマイズするため .gitignore で除外されている。
SNS 専用ループ対応へ更新する既存環境では、ローカルの data/HEARTBEAT.md も手動で見直す。以前の SNS 活動手順や legacy loadSkill("sns") 前提の記述が残っている場合は削除し、heartbeat には本来の監視・報告だけを残す。
同様に KW モード移行後は、ローカルの data/skills/karakuri-world/SKILL.md と data/system-skills/karakuri-world/SKILL.md を削除する。これらの legacy ファイルは通常モードでは無視されるが、今後の運用混乱を避けるためにも手動で消しておく。
Discord Developer Portal では DISCORD_PUBLIC_KEY / DISCORD_APPLICATION_ID を取得し、
Interactions Endpoint を POST /webhooks/discord に向ける。通常メッセージ受信には
Gateway 接続も必要なため、npm run dev / npm run start は HTTP サーバーと
Discord Gateway listener を同時に起動する。
Gateway listener にはローカルの /webhooks/discord URL を渡し、Discord bot メッセージも webhook forwarding 経由で受信する。
npm run dev- 開発起動npm run start- 本番起動npm run typecheck- TypeScript 型検査npm test- unit test 実行npm run docker:build- Docker イメージビルドnpm run docker:up- Docker Compose で本番起動npm run docker:dev- Docker Compose で開発モード起動(フォアグラウンド)npm run docker:dev:up- Docker Compose で開発モード起動(バックグラウンド)npm run docker:down- Docker Compose 停止
cp .env.example .env.envを設定-
Discord / LLM 系の値を入力する
-
Docker Compose 用の
UID/GIDには 数値 を入れる(.envは$(id -u)のような command substitution を展開しない) -
Linux / macOS / WSL では
id -u/id -gの出力結果をそのままUID/GIDに書く -
Docker Desktop on Windows など bind mount の所有者差分を気にしなくてよい環境では
UID=1000/GID=1000のような固定値でも運用できる -
例:
printf 'UID=%s\nGID=%s\n' "$(id -u)" "$(id -g)"
-
cp -r data.example datanpm run docker:buildnpm run docker:up
- アプリは
http://localhost:${PORT:-3000}で待ち受ける。GET /healthzは Discord Gateway listener が 5 秒以上生存したかを基準に接続状態を判定し、初回接続前・listener の起動失敗/早期終了・shutdown 時は503を返す。初回接続後は通常の listener 切り替え中も healthy を維持する - 永続データは
./dataを/app/dataに bind mount して保持する - Compose は container 内の
DATA_DIRを/app/dataに固定している。ホスト側の保存先を変えたい場合は.envのDATA_DIRではなくdocker-compose.ymlの volume 側を編集する docker-compose.ymlのuser:は.envのUID/GIDを必須にしており、未設定のまま1000:1000にフォールバックしてホスト側のdata/を書けなくなる事故を防いでいる- 停止は
npm run docker:down。Compose 側のstop_grace_period: 15sにより、アプリの graceful shutdown に余裕を持たせている
tsx watch をコンテナ内で使う場合は、オーバーライドを重ねて起動する。
npm run docker:devdocker-compose.dev.ymlはdepsステージを使い、devDependencies を含む状態で起動するdocker-compose.dev.ymlではsrc/とtsconfig.jsonだけを bind mount し、イメージ内の/app/node_modulesはそのまま使うため、tsxなどの devDependencies が bind mount で隠れない- 起動コマンドは
npxではなく/app/node_modules/.bin/tsxを直接実行する - 開発コンテナでは
HOME/ npm cache を/tmp/karakuri-agentに寄せているため、Compose 側でホストの任意 UID / GID に合わせて実行しても npm cache が/.npmに落ちず権限エラーを踏みにくい - 依存を更新した後は
npm run docker:devでイメージを作り直す
data/AGENT.mdはエージェント人格、data/RULES.mdは trusted な行動ルール、data/skills/*/SKILL.mdは全ユーザー向けスキル、data/system-skills/*/SKILL.mdはuserId === 'system'(Cron / Heartbeat)でのみ見える system 専用スキル定義data/HEARTBEAT.mdがあると定期 Heartbeat を実行し、Heartbeat は単発の ephemeral session で走る。SNS 活動は heartbeat から分離された専用ループで実行される。data/cron/*/CRON.mdで Cron ジョブも定義できるMEMORY_MAINTENANCE_INTERVAL_MINUTESを設定すると、専用のメモリメンテナンスループがrunExclusiveSystemTurn+ shared persistence mutex 内で core memory / diary を read → LLM → overwrite / rewrite / delete まで atomic に実行し、REPORT_CHANNEL_IDには成功時 summary と失敗時メタ情報のみを投稿する。post-response evaluator / SNS 観測ユーザー評価は snapshot read + LLM を lock 外で行い、apply だけ同じ mutex に入るため、heartbeat / cron / SNS loop の system turn が evaluator の LLM 待ちで詰まらない。全 diary 日付は常に inspection 対象で、MEMORY_MAINTENANCE_RECENT_DIARY_DAYSは本文を読む範囲だけを広げる- 既存環境の
data/HEARTBEAT.mdは.gitignoreされて自動更新されないため、旧来の SNS 指示や legacyloadSkill("sns")が残っていないか確認する。SNS 活動の正本は専用ループ側のコード内指示 - 1 つ以上のスキルが存在するときだけ
loadSkillツールが公開され、システムプロンプトには利用可能なスキル一覧だけを注入する - 通常ユーザーには
data/skills/*/SKILL.mdのみ公開され、data/system-skills/*/SKILL.mdはuserId === 'system'のときだけ一覧表示・loadSkill対象になる allowed-toolsを持つスキルはloadSkill後に対応ツールを動的登録する。karakuri-worldはallowed-toolsの有無に関係なく通常の skill discovery /loadSkillから常に除外され、karakuri_world_commandはKARAKURI_WORLD_*設定済みかつKARAKURI_WORLD_BOT_IDSに一致する Discord ユーザーの KW モードでのみ直接公開し、事前に自動取得したnotification.choices[]以外の command は実行させないMASTODON_*/X_*/ELYTH_*のうち必要項目がそろった provider ごとに、system ユーザー向けビルトイン SNS skill(sns-mastodon/sns-x/sns-elyth)が追加される。cron ではloadSkill("sns-mastodon")などで provider namespaced skill をロードし、sns_mastodon_post/sns_x_like/sns_elyth_get_threadのようなsns_<provider>_<action>ツール群を遅延公開する。SNS 専用ループだけがautoLoadSnsSkillと動的コンテキストを使って provider ごとに自動実行する。動的コンテキストには新着通知・トレンド・直近行動ログが含まれ、重複いいね/リポスト/返信/引用をツール層で防ぐ。ループ間隔はSNS_LOOP_MIN_INTERVAL_MINUTES/SNS_LOOP_MAX_INTERVAL_MINUTESで制御する。SNS ループ自体の成功/失敗はREPORT_CHANNEL_IDに通知され、追加の活動レポート本文をpostMessageで同じチャンネルへ送らせたい場合だけREPORT_CHANNEL_IDをpostMessageの送信許可チャンネルにも含める。X / ELYTH は*_postの公開範囲がpublicのみ。ELYTH は repost / quote / メディアアップロード非対応のため、sns_elyth_repost/sns_elyth_upload_mediaツールは公開されず、引用も Zod スキーマで拒否される。対話ユーザーに公開する場合は運用側で shared skill を定義するwebFetchは常に有効。URL を取得し Readability + Turndown で Markdown 化して返すwebFetchは各 redirect hop を再検証し、http/https以外のスキームや private / loopback / link-local 宛てへの遷移を拒否して SSRF を抑止する。15 秒のタイムアウトは DNS 解決も含めて適用するsns_mastodon_upload_media/sns_x_upload_mediaもwebFetchと同じ URL 検証を使い、http/https以外のスキームや private / loopback / link-local 宛て、そこへ向かう redirect を拒否する。こちらも DNS 解決を含めてタイムアウトを適用する- Mastodon のメディア処理が非同期な場合、
sns_mastodon_upload_mediaはGET /api/v1/media/:idを短時間ポーリングして ready を確認する。X では chunked upload (initializeUpload/appendUpload/finalizeUpload) の完了を待つ。制限時間内に ready にならない場合はエラーとして再試行を促す webSearchはBRAVE_API_KEY設定時のみ有効。Brave Search API で Web 検索を行うpostMessage/manageCronはALLOWED_CHANNEL_IDSとADMIN_USER_IDSが設定された管理者コンテキストでのみ公開される- Heartbeat は
ALLOWED_CHANNEL_IDS設定時のみ有効化され、REPORT_CHANNEL_IDは空欄のままでも省略設定として扱われる REPORT_CHANNEL_IDを設定すると Heartbeat / Cron / memory maintenance / SNS ループの実行成否、manageCronによる登録/解除、チャット処理エラー詳細、SNS context のカーソル予約/保存失敗警告を自動投稿する(エージェント応答本文は自動投稿しない)- Chat SDK の state は
DATA_DIR/state/chat-state.jsonに保存するカスタム JSON アダプターを使用 - コアメモリ / Session は
data/配下にファイル保存し、日記はDATA_DIR/diary.dbに保存する - メモリメンテナンスは evaluator ではなく専用パイプラインで行い、
coreMemoryAction/diaryOps[]の structured output をストア層プリミティブへ適用する - メモリメンテナンスは共有 persistence mutex を read → LLM → overwrite / replace / delete 全体で保持し、post-response evaluator / SNS 観測ユーザー評価は core memory snapshot read と LLM 判定を lock 外、append/write の apply だけ同じ mutex に入る。これにより background append と maintenance の競合を防ぎつつ、maintenance が evaluator の LLM 待ちで system turn を保持したまま詰まることを避ける。agent 側では evaluator を user ごとに直列化するため、同一 user の後続評価は直前 apply 済みの core memory を見る
- 初回起動時は旧
DATA_DIR/memory/diary/*.mdを検出するとdiary.dbへ一度だけ自動インポートする - ユーザー情報は
DATA_DIR/users.dbに保存され、userLookupツールと<user-profile>コンテキストに利用される - 元メッセージへのリアクションで
queued/thinking/ tool 実行中 /done/errorを表示し、doneは 2 秒後に自動除去する - 添付ファイルは未対応。添付付きメッセージはテキスト部分のみ処理し、注意メッセージを返す
SNS configuration is provider-specific. Set any combination of MASTODON_INSTANCE_URL + MASTODON_ACCESS_TOKEN, X_ACCESS_TOKEN (+ optional X OAuth fields), and ELYTH_API_KEY + ELYTH_API_BASE (for example https://elythworld.com); all fully configured providers run concurrently. SNS tools are provider-namespaced, e.g. sns_mastodon_post, sns_x_like, sns_elyth_get_thread, and skills are named sns-mastodon, sns-x, sns-elyth.
Legacy SNS_PROVIDER / SNS_* credentials are no longer read. If data/sns-activity.db exists, set SNS_LEGACY_DB_MIGRATE_TO=mastodon|x|elyth|skip once before startup.
Admins can link observed accounts that represent the same person by asking the bot to use linkUser (for example, link sns:mastodon:1234 to discord:abcd). Prefer discord: IDs as primary. Use unlinkUser then linkUser to correct a link.