L-memory Agent Architecture 設計書
LINE×Memory基盤に、外部SaaS/API/Browser/MCP/Scheduler を使う自律Agent実行を足すための設計。社内用・v1。
1. 結論
方向性は正しい。「L-memory=コントロールプレーン/Agent実行=独立レイヤー、疎結合・承認制・監査」は妥当で、codexの推奨とも一致。そのまま進めてよい設計です。ただし1点だけ現実を踏まえた修正を入れます。
重要な前提修正: 現行 L-memory は
Python / FastAPI / SQLite(per-client) で本番稼働中(LINE bot群・ノート・CoS)。ご提示の設計は
TS / Next.js / Prisma。これは“別スタックの新プラットフォーム”です。
→
working な Python コアを書き直さない。次の
ハイブリッドを推奨:
- Agent Execution Cloud(Layer 3)を新規 TS/Next/Prisma サービスとして新設(ご提示のmonorepo)。
- L-memory Core(Layer 2)は当面 既存Python を“Control Plane API”として薄く公開し、徐々にTSへ寄せる(or 併存)。
- 境界は HTTP/Queue の契約のみ。既存の gyoza/botchan(Agent Bridge) は Executor の一実装として接続。
これで「動いている資産を壊さず」「Agent層は独立サービス/将来Marketplace化可能」を両立できます。
確定する設計判断(要点)
- SoTと実行状態を分離:タスク本体(
tasks)はSoT、実行は別テーブル(agent_runs/agent_steps/task_events)。本文は上書きせずevidence追記。
- 承認は「能力(capability)単位」:タスク単位でなく“何をするか”で lowmediumhigh に分け承認要否を決める。
- 冪等性とlease を最初に:
idempotency_key + claim(lease) で多重実行を防ぐ。
- ロールバックは補償操作:DB rollbackでなく「投稿削除/予定キャンセル」等。
undo_payloadをevidenceに残す。
- 段階導入:read-only提案 → 実行台帳 → low-risk自動 → 承認付き外部書込 → executor拡大。
2. 推奨アーキテクチャ
サービス分割
| サービス | 役割 | スタック |
| line-bot / interface | LINE等チャネル受信・通知。Webhook→Intent化の入口 | 既存Python(当面) → 将来 apps/line-bot(TS) |
| core (Control Plane) | Identity/Workspace/Memory/Permission/Context/Agent Router/Task Registry/Approval/Notification | 既存Python公開API → 段階的にNext/Prisma |
| agent-orchestrator | Task Intent受領→Agent選定→Plan→Approval要求→実行指示→結果集約 | Next.js(API) + Queue |
| agent-worker | Planner/Executor/Tool/MCP/Browser/Code/Research を実行。冪等・retry・heartbeat | Node worker(BullMQ等) |
| tool/mcp registry | Tool定義・MCP接続・資格情報(暗号化)・per-workspace分離 | packages/tools, packages/mcp |
境界の原則:Core は「誰が・どの文脈で・何をしたいか」だけ持つ。Worker は「どう実行するか」だけ持つ。両者は Task/Run の契約(API+Event) でのみ会話。Agent は Core を知らなくても動ける=独立販売可能。
3. 全体図
[LINE/Slack/Web/Voice] ← Interface Layer
│ 自然文
▼
┌───────────────── L-memory Core (Control Plane) ─────────────────┐
│ Identity / Workspace / Memory / Permission / Context Resolver │
│ Intent化 → Agent Router → Task Registry → Approval → Notify │
└───────┬───────────────────────────────────────────────┬─────────┘
│ POST /agents/run (Task Intent + context + policy)│ 通知/承認結果
▼ (Queue: task.requested) ▲
┌──────────────── Agent Execution Cloud (Layer 3) ────────────────┐
│ Orchestrator → Planner → [Approval gate] → Executor │
│ Tool Registry / MCP Client / Browser / Code / Research / Sched │
│ agent_runs・agent_steps・evidence・audit (run state はここ) │
│ Executor 実装: gyoza(Claude Code) / botchan(OpenClaw) / 専用worker│
└─────────────────────────────────────────────────────────────────┘
│ task.completed / needs_approval / failed (Event Bus)
▼
Core が status 書き戻し → LINEへ通知 / 承認カード提示
4. 実装ディレクトリ(monorepo / pnpm + turbo)
l-memory-os/
├─ apps/
│ ├─ web/ # 管理UI/承認ダッシュボード(Next.js)
│ ├─ line-bot/ # チャネルadapter(将来。当面は既存Python)
│ ├─ core-api/ # Control Plane API(Next route handlers)
│ ├─ agent-orchestrator/ # Intent→route→plan→approval→dispatch
│ └─ agent-worker/ # 実行worker(BullMQ consumer)
├─ packages/
│ ├─ core/ # Identity/Workspace/Permission/Context
│ ├─ memory/ # Memory読み書き(既存SQLite/将来pg)
│ ├─ db/ # Prisma schema & client
│ ├─ agent-sdk/ # Agentインターフェース・基底クラス
│ ├─ tools/ # Tool実装(Drive/Notion/Calendar/HTTP…)
│ ├─ mcp/ # MCP client/registry
│ └─ shared/ # 型・Intent schema・Event定義
├─ prisma/ (schema in packages/db)
└─ turbo.json / pnpm-workspace.yaml
既存Python資産との接続は packages/core 内に LMemoryControlPlane adapter(HTTP client)を置き、当面は既存 connect_app の薄いAPIを叩く。将来 core-api(TS) に移植。
5. Prisma schema
// packages/db/schema.prisma
generator client { provider = "prisma-client-js" }
datasource db { provider = "postgresql"; url = env("DATABASE_URL") }
model User {
id String @id @default(cuid())
channelId String? // LINE userId 等
channel String @default("line")
name String?
createdAt DateTime @default(now())
members WorkspaceMember[]
@@unique([channel, channelId])
}
model Workspace {
id String @id @default(cuid())
name String
createdAt DateTime @default(now())
members WorkspaceMember[]
}
model WorkspaceMember {
id String @id @default(cuid())
workspaceId String
userId String
role String @default("member") // owner|admin|member|guest
workspace Workspace @relation(fields:[workspaceId],references:[id])
user User @relation(fields:[userId],references:[id])
@@unique([workspaceId, userId])
}
model Conversation {
id String @id @default(cuid())
workspaceId String
channel String
externalId String? // LINE group_id 等
createdAt DateTime @default(now())
messages Message[]
@@index([workspaceId])
}
model Message {
id String @id @default(cuid())
conversationId String
senderUserId String?
text String
createdAt DateTime @default(now())
conversation Conversation @relation(fields:[conversationId],references:[id])
@@index([conversationId, createdAt])
}
model Memory {
id String @id @default(cuid())
workspaceId String
scope String @default("workspace") // user|workspace|conversation
key String?
text String
visibility String @default("group") // group|personal
ownerUserId String?
createdAt DateTime @default(now())
@@index([workspaceId, scope])
}
// ── Task = SoT、実行状態は別 ──
model Task {
id String @id @default(cuid())
workspaceId String
title String
detail String?
intent Json? // 構造化Intent(action/object/target/actor)
status String @default("open") // open|planning|awaiting_approval|running|done|failed|cancelled
assigneeUserId String?
capability String @default("low") // low|medium|high(承認制御)
sourceMessageId String?
parentTaskId String?
dueAt DateTime?
createdAt DateTime @default(now())
events TaskEvent[]
runs AgentRun[]
approvals Approval[]
@@index([workspaceId, status])
}
model TaskEvent {
id String @id @default(cuid())
taskId String
type String // created|claimed|planned|approved|rejected|step|completed|failed|note
payload Json?
actor String? // user/agent/system
createdAt DateTime @default(now())
task Task @relation(fields:[taskId],references:[id])
@@index([taskId, createdAt])
}
model Agent {
id String @id @default(cuid())
slug String @unique // research|scheduler|document|admin
name String
description String
capabilities String[] // ["research","calendar.write",...]
enabled Boolean @default(true)
runs AgentRun[]
}
model AgentRun {
id String @id @default(cuid())
taskId String
agentId String
idempotencyKey String @unique // 多重実行防止
leaseOwner String? // claim中のworker id
leaseUntil DateTime?
status String @default("queued") // queued|running|needs_approval|done|failed|compensated
plan Json?
result Json?
error String?
startedAt DateTime?
finishedAt DateTime?
createdAt DateTime @default(now())
task Task @relation(fields:[taskId],references:[id])
agent Agent @relation(fields:[agentId],references:[id])
steps AgentStep[]
@@index([status])
}
model AgentStep {
id String @id @default(cuid())
runId String
seq Int
tool String? // 使用ツール/MCP
capability String @default("low")
input Json?
output Json? // evidenceは追記。本文上書きしない
undoPayload Json? // 補償操作用
status String @default("ok") // ok|error|skipped
createdAt DateTime @default(now())
run AgentRun @relation(fields:[runId],references:[id])
@@index([runId, seq])
}
model Tool {
id String @id @default(cuid())
slug String @unique // gdrive|notion|gcal|http|browser
name String
capability String @default("medium")
schema Json?
}
model ToolCredential {
id String @id @default(cuid())
workspaceId String
toolSlug String
encrypted String // KMS/封筒暗号化
createdAt DateTime @default(now())
@@unique([workspaceId, toolSlug])
}
model Approval {
id String @id @default(cuid())
taskId String
runId String?
capability String
summary String // 何を実行するか(人が読む)
status String @default("pending") // pending|approved|rejected|expired
approverUserId String?
decidedAt DateTime?
createdAt DateTime @default(now())
task Task @relation(fields:[taskId],references:[id])
@@index([status])
}
model AuditLog {
id String @id @default(cuid())
workspaceId String
actor String // user/agent/system + id
action String
target String?
runKey String?
payload Json?
createdAt DateTime @default(now())
@@index([workspaceId, createdAt])
}
model Notification {
id String @id @default(cuid())
workspaceId String
channel String @default("line")
target String
kind String // approval|result|error
payload Json?
status String @default("sent")
createdAt DateTime @default(now())
}
6. API設計
| Method/Path | 役割 | 備考 |
POST /api/line/webhook | LINE受信→Intent化→Task作成 | 署名検証。Coreが担当 |
POST /api/tasks | Task作成(自然文/構造化) | capability推定を付与 |
GET /api/tasks/:id | Task+run+eventsの状態 | |
POST /api/tasks/:id/approve | 承認→runを進める | approverを記録(audit) |
POST /api/tasks/:id/reject | 却下→runを停止 | |
POST /api/agents/run | Orchestratorへ実行依頼(Intent+context+policy) | idempotencyKey必須 |
GET /api/agents | 登録Agent一覧/capability | Router/Marketplace用 |
POST /api/tools/connect | Tool/MCP接続(OAuth/credential) | per-workspace暗号化 |
GET /api/audit-logs | 監査ログ参照 | workspaceスコープ |
Worker向け(MCP/内部API・最小)
POST /api/runs/claim { runId, worker, leaseSec } // 排他lease取得(冪等)
POST /api/runs/:id/step { tool, input, output, undoPayload, capability }
POST /api/runs/:id/heartbeat
POST /api/runs/:id/complete { result } // status書き戻し+notify
POST /api/runs/:id/fail { error, retryable }
GET /api/tasks/:id/evidence
7. Agent SDK設計
// packages/agent-sdk
export interface TaskIntent {
action: string; // research|remind|find_doc|answer ...
object?: string; // 目的語
target?: string; // 固有名詞(対象)
actor?: string;
raw: string; // 元の自然文
workspaceId: string;
}
export interface AgentContext {
intent: TaskIntent;
memory: MemoryAccessor; // 読み取り中心
tools: ToolRegistry; // 許可されたツールのみ
policy: { capabilityCeiling: "low"|"medium"|"high" };
}
export interface PlanStep { tool: string; input: unknown; capability: "low"|"medium"|"high"; summary: string; }
export interface AgentPlan { steps: PlanStep[]; requiresApproval: boolean; summary: string; }
export interface AgentResult { ok: boolean; output: unknown; evidence: unknown[]; }
export interface Agent {
id: string; name: string; description: string; capabilities: string[];
canHandle(intent: TaskIntent): Promise;
plan(ctx: AgentContext): Promise;
execute(plan: AgentPlan, ctx: AgentContext): Promise; // 各stepでstep記録+冪等
}
// 基底: claim/heartbeat/step記録/承認待ち/補償を共通化
export abstract class BaseAgent implements Agent { /* ... */ }
Routerは agents.filter(a=>a.canHandle(intent)) → スコア/優先度で1つ選定。新Agentは SDK実装+登録(Agentテーブル) だけで追加=Marketplace化の素地。
8. MVP実装手順
- DB/SDK土台:Prisma schema(上記の最小: Task/TaskEvent/AgentRun/AgentStep/Approval/AuditLog)。agent-sdk雛形。
- Intent化:LINE自然文 →
TaskIntent(action/object/target)。CoreのLLMで。曖昧は要確認(既存方針流用)。
- Agent Router:canHandleで Research/Scheduler/Document/Admin から選定。
- Plan + Approval gate:capability=medium/highなら
Approval作成→LINEに承認カード(postback)。
- Worker実行:claim(lease+冪等)→step記録→complete→Core書き戻し→LINE通知。
- 監査:全遷移をTaskEvent/AuditLogへ。evidence追記。
MVPは Research / Scheduler の2体に絞る(low-risk中心)。Document/Adminは Tool接続が要るので Phase後段。
MVP Agent
| Agent | capability | MVPで実装 |
| Research | low(検索+要約・外部送信なし) | ◎ 最初 |
| Scheduler/Reminder | low〜medium(社内リマインド=low / カレンダー作成=medium承認) | ◎ 最初 |
| Document | medium(Drive/Notion読み取り→要約) | ○ Tool接続後 |
| Admin | low(ナレッジ検索/案内) | ○ |
9. セキュリティ・権限設計
| レベル | 例 | 承認 |
| low | ノート追記・要約・社内検索・Slack/LINE報告 | ポリシーで自動可 |
| medium | 外部SaaSの新規作成/更新(カレンダー予定・issue) | 事前承認 or 信頼executorのみ |
| high | 削除・課金・公開投稿・契約・deploy・権限変更・外部送信 | 毎回 人間承認必須 |
- Permission:workspaceロール × capability の二重チェック。Agentは「許可されたToolのみ」見える(最小権限)。
- 資格情報:ToolCredentialは封筒暗号化(KMS)、per-workspace分離、worker実行時のみ復号。
- 冪等/lease:
AgentRun.idempotencyKey+lease。二重承認・二重実行を防止。
- 監査:claim/approve/step/complete を AuditLog+TaskEvent に。誰が承認したか必須。
- ロールバック:補償操作(undoPayload)。high操作は必ずundo手順を残す。
- テナント分離:全クエリに workspaceId。クロステナント禁止。
10. Claude Code 実装TODO(着手順)
- monorepo初期化(pnpm+turbo, apps/packages雛形)
packages/db:Prisma schema(MVP最小)+migrate+client
packages/agent-sdk:Agent/Intent/Plan型+BaseAgent
packages/shared:TaskIntent/Event/capability定義
apps/core-api:/api/tasks, /tasks/:id, approve/reject, audit-logs
- Intent化:自然文→TaskIntent(LLM, subscription経路) +capability推定
apps/agent-orchestrator:Router(canHandle)+Plan+Approval生成
- Approval:LINE承認カード(postback)→/tasks/:id/approve
apps/agent-worker:claim/step/complete/heartbeat+冪等+retry
- ResearchAgent実装(検索+要約, 外部送信なし=low)
- SchedulerAgent実装(社内リマインド=low / カレンダー=medium承認)
- 結果をLINE通知+TaskEvent/AuditLog記録
- 既存Python L-memoryと接続(Control Plane adapter / 当面は connect_app薄API)
進め方の推奨:いきなり全monorepoを建てず、(a)既存hobby版(Next/Prisma)に近いのでそこを土台に Agent層を切り出す か、(b)新規 l-memory-os monorepo を Phase0(提案のみ)+Phase1(run台帳+claim) だけ作って検証。低リスクで“動く骨格”を見てから拡張するのが安全です。
L-memory Agent Architecture v1 — 内部設計書。実装はTS/Next/Prisma想定、既存Pythonコアは疎結合で併存。