メインコンテンツまでスキップ

Agent エンジン概要

Elftia の Agent システムはマルチエンジンディスパッチアーキテクチャに基づいています。EngineDispatcher は統一リクエストディスパッチャーとして機能し、EngineType に基づいてリクエストを対応するエンジン実装にルーティングします。各エンジンは IEngine インターフェースを実装し、異なるバックエンドサービスをカプセル化します。

アーキテクチャ図

graph TB
Router["AgentRouter / MagiService"] --> Dispatcher["EngineDispatcher"]

Dispatcher --> TinyElf["TinyElfEngine<br/>tinyelf"]
Dispatcher --> ClaudeSdk["ClaudeSdkEngine<br/>claude-sdk"]
Dispatcher --> CliRunner["CliRunnerEngine<br/>cli"]
Dispatcher --> Chat["ChatEngine<br/>chat"]
Dispatcher --> STChat["STChatEngine<br/>st-chat"]
Dispatcher --> Api["ApiEngine<br/>api"]

TinyElf --> TinyLoop["TinyElfAgentLoop<br/>Tool call loop"]
TinyElf --> ToolReg["ToolRegistry<br/>Tool registry"]
TinyElf --> LLMAdapter["TinyElfLLMAdapter<br/>LLM adapter"]

ClaudeSdk --> AgentSvc["AgentService<br/>Claude Agent SDK"]
ClaudeSdk --> Proxy["AgentProxyServer<br/>HTTP proxy"]

CliRunner --> Supervisor["ProcessSupervisor<br/>Process management"]

Chat --> Pipeline["ChatCompletionPipeline<br/>Chat pipeline"]

IEngine インターフェース

すべてのエンジンは IEngine インターフェースを実装する必要があります:

interface IEngine {
readonly engineType: EngineType;
startSession(ctx: EngineSessionContext): Promise<void>;
resumeSession(ctx: EngineSessionContext): Promise<void>;
interrupt(dbSessionId: string): Promise<boolean>;
isActive(dbSessionId: string): boolean;
reloadMcpForAllActive?(): Promise<void>;
}
メソッド説明
startSession新しいセッションを開始する(ユーザーメッセージを保存し、エンジンループを実行)
resumeSession既存のセッションで会話を継続する
interruptアクティブなセッションを中断し、成功したかどうかを返す
isActiveセッションが実行中かどうかを確認する
reloadMcpForAllActiveすべてのアクティブセッションの MCP ツールをホットリロードする(オプション)

EngineSessionContext

すべてのエンジンは同じセッションコンテキスト構造を共有します。各エンジンは必要に応じてフィールドを使用します:

interface EngineSessionContext {
dbSessionId: string; // Database session ID
session: ChatSession; // Full session record
sender: WebContents; // Electron IPC sender
prompt: string; // User message
attachments?: Array<...>; // Multimodal attachments
providerId?: string; // LLM provider ID
model?: string; // Model ID (v89+ is bare SDK id)
/**
* v89+: CLI backend binding (from chat_sessions.cliBackend column).
* 'claude-code' goes through ClaudeSdkEngine + AgentProxyServer.passThrough;
* 'codex' / 'gemini-cli' goes through CliRunnerEngine.
*/
cliBackend?: 'claude-code' | 'codex' | 'gemini-cli' | null;
/**
* v89+: 1M-context preference (from chat_sessions.useExtendedContext column).
* Engines / AgentProxyServer / TransformerChainExecutor decide whether to
* inject 'context-1m-2025-08-07' into anthropic_beta based on this.
*/
useExtendedContext?: boolean;
engineConfig?: EngineConfig; // Engine-specific configuration
projectPath?: string; // Project path
agentId?: string; // Agent ID

// Chat engine specific
systemPrompt?: string;
parentId?: string | null;
thinkingLevel?: string;
searchOrchestration?: PipelineSearchConfig;
mcpConfig?: PipelineMcpConfig;

// Magi/Agent engine specific
providerEnv?: Record<string, string>;
onSessionEnd?: () => void;
isOfficialProvider?: boolean;
customAgentOptions?: { ... };
permissionMode?: string;
channelSource?: string;
channelId?: string;
channelSenderId?: string;
channelUserPermissions?: { canUseTool: boolean; requireConfirmation: boolean };
}

EngineDispatcher

EngineDispatcher はエンジン登録とディスパッチの中核です:

class EngineDispatcher {
private engines = new Map<EngineType, IEngine>();

registerEngine(engine: IEngine): void;
getEngine(type: EngineType): IEngine;
hasEngine(type: EngineType): boolean;
dispatch(type: EngineType, ctx: EngineSessionContext): Promise<void>;
resume(type: EngineType, ctx: EngineSessionContext): Promise<void>;
interrupt(dbSessionId: string): Promise<boolean>;
reloadMcpForAllActive(): Promise<void>;
getEngineInfos(): EngineInfo[];
}

エンジン登録

エンジンは packages/desktop/app/main/index.ts で登録されます:

const dispatcher = new EngineDispatcher();
dispatcher.registerEngine(new TinyElfEngine(...));
dispatcher.registerEngine(new ClaudeSdkEngine(...));
dispatcher.registerEngine(new CliRunnerEngine(getProcessSupervisor(), db, logger));
dispatcher.registerEngine(new ChatEngine(...));
dispatcher.registerEngine(new STChatEngine(...));
dispatcher.registerEngine(new ApiEngine(...));

中断メカニズム

interrupt() メソッドはすべてのエンジンを反復処理し、対象セッションを保有するエンジンを見つけて中断します:

async interrupt(dbSessionId: string): Promise<boolean> {
for (const engine of this.engines.values()) {
if (engine.isActive(dbSessionId)) {
return engine.interrupt(dbSessionId);
}
}
return false;
}

エンジンタイプレジストリ

EngineTypeクラスバックエンドサービスアクティブセッション追跡
tinyelfTinyElfEngine組み込み Agent ループMap<string, ActiveTinyElfSession>
claude-sdkClaudeSdkEngineAgentService (SDK)AgentService 内部管理
cliCliRunnerEngineProcessSupervisorMap<string, ActiveCliSession>
chatChatEngineChatCompletionPipeline内部 Map
st-chatSTChatEngineCompletionService内部 Map
apiApiEngineCompletionServiceステートレス

アクティブセッション追跡

TinyElf エンジンは ActiveTinyElfSession を使用して各アクティブセッションの状態を追跡します:

interface ActiveTinyElfSession {
dbSessionId: string;
projectPath: string;
abortController: AbortController;
parentMessageId?: string;
lastAssistantMessageId?: string;
directMcpConnections?: Array<...>;
hookExecutor?: HookExecutor;
sdkDualWrite?: {
sdkSessionId: string;
lastUuid: string;
seqNum: number;
};
subagentManager?: SubagentManager;
}

IPC イベント

エンジンは sender.send('agent:event', ...) を通じてレンダラープロセスにイベントを送信します:

イベントタイプペイロード説明
init{ sessionId, systemInfo }セッションの初期化
sessionCreated{ sessionId }セッション作成完了
userMessage{ sessionId, message }ユーザーメッセージの保存
streamDelta{ sessionId, delta }ストリーミングテキスト/思考デルタ
toolCallStart{ sessionId, block }ツール呼び出し開始
toolCallEnd{ sessionId, block, status }ツール呼び出し完了
assistantMessage{ sessionId, message }アシスタントメッセージの保存
permissionRequest{ sessionId, requestId, toolName, input }権限確認リクエスト
result{ sessionId, stats }実行統計結果
complete{ sessionId }セッション完了
error{ sessionId, error }エラーイベント
retry{ sessionId, retry }API リトライ通知
processing{ sessionId, message }処理中の状態

主要ファイル

ファイルパス説明
IEngine インターフェースservices/agent-core/engine/types.tsエンジンインターフェースとコンテキスト定義
EngineDispatcherservices/agent-core/engine/EngineDispatcher.tsエンジン登録とディスパッチ
モジュールエントリservices/agent-core/engine/index.tsすべてのエンジンのエクスポート
TinyElfEngineservices/agent-core/engine/tinyelf/TinyElfEngine.tsTinyElf エンジン実装
ClaudeSdkEngineservices/agent-core/engine/ClaudeSdkEngine.tsClaude SDK エンジン
CliRunnerEngineservices/agent-core/engine/cli/CliRunnerEngine.tsCLI エンジン
ChatEngineservices/agent-core/engine/ChatEngine.tsチャットエンジン
STChatEngineservices/agent-core/engine/STChatEngine.tsシングルターンチャットエンジン
ApiEngineservices/agent-core/engine/ApiEngine.tsAPI エンジン
EngineType 定義@shared/contracts/elftia-agent-types.ts型定義

すべてのパスは packages/desktop/app/main/ からの相対パスです。

拡張ポイント

  • 新しいエンジンを追加するIEngine インターフェースを実装 + EngineDispatcher に登録 + EngineType を追加
  • 新しい IPC イベントを追加する:エンジンのコールバック内で sender.send() を通じて送信
  • MCP ホットリロードreloadMcpForAllActive() メソッドを実装

関連モジュール

モジュールパス関係
AgentRouterservices/routers/AgentRouter.tsIPC 呼び出しを受け取りディスパッチ
MagiServiceservices/agent-core/magi/MagiService.ts高レベルオーケストレーション、エンジン選択
CompletionServiceservices/capabilities/llm/completion/CompletionService.tsLLM API 呼び出し
AgentServiceservices/agent-core/agent/AgentService.tsClaude SDK セッション管理