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層は独立サービス/将来Marketplace化可能」を両立できます。

確定する設計判断(要点)

2. 推奨アーキテクチャ

サービス分割

サービス役割スタック
line-bot / interfaceLINE等チャネル受信・通知。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-orchestratorTask Intent受領→Agent選定→Plan→Approval要求→実行指示→結果集約Next.js(API) + Queue
agent-workerPlanner/Executor/Tool/MCP/Browser/Code/Research を実行。冪等・retry・heartbeatNode worker(BullMQ等)
tool/mcp registryTool定義・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/webhookLINE受信→Intent化→Task作成署名検証。Coreが担当
POST /api/tasksTask作成(自然文/構造化)capability推定を付与
GET /api/tasks/:idTask+run+eventsの状態
POST /api/tasks/:id/approve承認→runを進めるapproverを記録(audit)
POST /api/tasks/:id/reject却下→runを停止
POST /api/agents/runOrchestratorへ実行依頼(Intent+context+policy)idempotencyKey必須
GET /api/agents登録Agent一覧/capabilityRouter/Marketplace用
POST /api/tools/connectTool/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実装手順

  1. DB/SDK土台:Prisma schema(上記の最小: Task/TaskEvent/AgentRun/AgentStep/Approval/AuditLog)。agent-sdk雛形。
  2. Intent化:LINE自然文 → TaskIntent(action/object/target)。CoreのLLMで。曖昧は要確認(既存方針流用)。
  3. Agent Router:canHandleで Research/Scheduler/Document/Admin から選定。
  4. Plan + Approval gate:capability=medium/highなら Approval作成→LINEに承認カード(postback)。
  5. Worker実行:claim(lease+冪等)→step記録→complete→Core書き戻し→LINE通知。
  6. 監査:全遷移をTaskEvent/AuditLogへ。evidence追記。
MVPは Research / Scheduler の2体に絞る(low-risk中心)。Document/Adminは Tool接続が要るので Phase後段。

MVP Agent

AgentcapabilityMVPで実装
Researchlow(検索+要約・外部送信なし)◎ 最初
Scheduler/Reminderlowmedium(社内リマインド=low / カレンダー作成=medium承認)◎ 最初
Documentmedium(Drive/Notion読み取り→要約)○ Tool接続後
Adminlow(ナレッジ検索/案内)

9. セキュリティ・権限設計

レベル承認
lowノート追記・要約・社内検索・Slack/LINE報告ポリシーで自動可
medium外部SaaSの新規作成/更新(カレンダー予定・issue)事前承認 or 信頼executorのみ
high削除・課金・公開投稿・契約・deploy・権限変更・外部送信毎回 人間承認必須

10. Claude Code 実装TODO(着手順)

  1. monorepo初期化(pnpm+turbo, apps/packages雛形)
  2. packages/db:Prisma schema(MVP最小)+migrate+client
  3. packages/agent-sdk:Agent/Intent/Plan型+BaseAgent
  4. packages/shared:TaskIntent/Event/capability定義
  5. apps/core-api:/api/tasks, /tasks/:id, approve/reject, audit-logs
  6. Intent化:自然文→TaskIntent(LLM, subscription経路) +capability推定
  7. apps/agent-orchestrator:Router(canHandle)+Plan+Approval生成
  8. Approval:LINE承認カード(postback)→/tasks/:id/approve
  9. apps/agent-worker:claim/step/complete/heartbeat+冪等+retry
  10. ResearchAgent実装(検索+要約, 外部送信なし=low)
  11. SchedulerAgent実装(社内リマインド=low / カレンダー=medium承認)
  12. 結果をLINE通知+TaskEvent/AuditLog記録
  13. 既存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コアは疎結合で併存。