diff --git a/src/theta/analyze.ts b/src/theta/analyze.ts new file mode 100644 index 000000000..f25dca556 --- /dev/null +++ b/src/theta/analyze.ts @@ -0,0 +1,202 @@ +/** + * θ₂ ANALYZE: 意図解析、コンテキスト取得 + * + * 観測データを分析し、意図を抽出する。 + */ + +import { emitAgentEvent } from "../infra/agent-events.js"; +import { + type AnalysisResult, + ThetaCycleState, + ThetaEvent, + ThetaEventType, + ThetaPhase, +} from "./types.js"; + +// 型のエクスポート +export type { AnalysisResult }; + +/** + * 分析を実行する + * + * @param state - θサイクル状態 + * @returns 更新された状態と分析結果 + */ +export async function analyze( + state: ThetaCycleState, +): Promise<{ state: ThetaCycleState; result: AnalysisResult }> { + const { runId } = state; + + // フェーズ開始イベント + emitPhaseEvent(runId, ThetaEventType.PHASE_START, { + phase: ThetaPhase.ANALYZE, + }); + + try { + // 入力データの取得 + const input = state.context.get("observe.input"); + const metadata = state.context.get("observe.metadata") as Record | undefined; + + // 意図解析 + const intent = extractIntent(input); + const entities = extractEntities(input); + const context = buildContext(input, metadata); + const confidence = calculateConfidence(intent, entities); + + const result: AnalysisResult = { + intent, + entities, + context, + confidence, + }; + + // 分析イベント記録 + const analysisEvent: ThetaEvent = { + runId, + phase: ThetaPhase.ANALYZE, + timestamp: Date.now(), + type: ThetaEventType.ANALYSIS, + data: { + result, + }, + }; + + state.events.push(analysisEvent); + state.currentPhase = ThetaPhase.ANALYZE; + state.context.set("analyze.result", result); + + // Agentイベントを発行 + emitAgentEvent({ + runId, + stream: "tool", + data: { + type: "analyze", + intent, + entities, + confidence, + }, + }); + + // フェーズ完了イベント + emitPhaseEvent(runId, ThetaEventType.PHASE_COMPLETE, { + phase: ThetaPhase.ANALYZE, + intent, + confidence, + }); + + return { state, result }; + } catch (error) { + // エラーイベント + emitPhaseEvent(runId, ThetaEventType.PHASE_ERROR, { + phase: ThetaPhase.ANALYZE, + error: error instanceof Error ? error.message : String(error), + }); + throw error; + } +} + +/** + * 入力から意図を抽出する + */ +function extractIntent(input: unknown): string { + if (typeof input === "string") { + // 簡易的な意図抽出(実際はLLM等を使用) + const lowerInput = input.toLowerCase(); + if (lowerInput.includes("create") || lowerInput.includes("new")) { + return "create"; + } + if (lowerInput.includes("delete") || lowerInput.includes("remove")) { + return "delete"; + } + if (lowerInput.includes("update") || lowerInput.includes("modify")) { + return "update"; + } + if (lowerInput.includes("search") || lowerInput.includes("find")) { + return "search"; + } + return "unknown"; + } + if (typeof input === "object" && input !== null) { + const obj = input as Record; + if (typeof obj.intent === "string") { + return obj.intent; + } + } + return "unknown"; +} + +/** + * 入力からエンティティを抽出する + */ +function extractEntities(input: unknown): Record { + const entities: Record = {}; + + if (typeof input === "object" && input !== null) { + const obj = input as Record; + // よく使われるエンティティキーを抽出 + const entityKeys = ["name", "id", "type", "value", "target", "source"]; + for (const key of entityKeys) { + if (key in obj) { + entities[key] = obj[key]; + } + } + } + + return entities; +} + +/** + * コンテキストを構築する + */ +function buildContext(input: unknown, metadata?: Record): Record { + return { + inputType: typeof input, + hasMetadata: metadata !== undefined, + metadataKeys: metadata ? Object.keys(metadata) : [], + timestamp: Date.now(), + }; +} + +/** + * 信頼度を計算する + */ +function calculateConfidence(intent: string, entities: Record): number { + let score = 0.5; // 基本スコア + + // 意図が特定できている + if (intent !== "unknown") { + score += 0.2; + } + + // エンティティが抽出できている + if (Object.keys(entities).length > 0) { + score += 0.1 * Math.min(Object.keys(entities).length, 3); + } + + return Math.min(score, 1.0); +} + +/** + * 分析結果を取得する + * + * @param state - θサイクル状態 + * @returns 分析結果 + */ +export function getAnalysisResult(state: ThetaCycleState): AnalysisResult | undefined { + return state.context.get("analyze.result") as AnalysisResult | undefined; +} + +/** + * フェーズイベントを発行する + */ +function emitPhaseEvent(runId: string, type: ThetaEventType, data: Record): void { + emitAgentEvent({ + runId, + stream: "tool", + data: { + type: "theta_event", + thetaEventType: type, + ...data, + }, + }); +} diff --git a/src/theta/decide.ts b/src/theta/decide.ts new file mode 100644 index 000000000..544e12272 --- /dev/null +++ b/src/theta/decide.ts @@ -0,0 +1,238 @@ +/** + * θ₃ DECIDE: 処理方針決定、Agent選択 + * + * 分析結果に基づいて、最適な処理方針とエージェントを選択する。 + */ + +import { emitAgentEvent } from "../infra/agent-events.js"; +import { + type AnalysisResult, + ThetaCycleState, + ThetaEvent, + ThetaEventType, + ThetaPhase, +} from "./types.js"; +import type { DecisionResult } from "./types.js"; + +// 型のエクスポート +export type { DecisionResult }; + +/** + * 利用可能なエージェントの定義 + */ +interface AgentDefinition { + /** エージェントID */ + id: string; + /** 対応する意図 */ + intents: string[]; + /** キャパシティ */ + capacity: number; +} + +/** 利用可能なエージェント一覧 */ +const AVAILABLE_AGENTS: AgentDefinition[] = [ + { id: "conductor", intents: ["*"], capacity: 10 }, + { id: "codegen", intents: ["create", "update"], capacity: 5 }, + { id: "review", intents: ["verify", "check"], capacity: 3 }, + { id: "deploy", intents: ["deploy", "release"], capacity: 2 }, +]; + +/** + * 処理方針の定義 + */ +interface StrategyDefinition { + /** 戦略ID */ + id: string; + /** 対応する意図 */ + intents: string[]; + /** 推奨エージェント */ + defaultAgent: string; + /** 推定実行時間(ms) */ + estimatedDuration: number; +} + +/** 利用可能な処理方針一覧 */ +const AVAILABLE_STRATEGIES: StrategyDefinition[] = [ + { + id: "direct_execution", + intents: ["create", "update", "delete"], + defaultAgent: "codegen", + estimatedDuration: 5000, + }, + { + id: "search_and_retrieve", + intents: ["search", "find"], + defaultAgent: "conductor", + estimatedDuration: 2000, + }, + { + id: "verification", + intents: ["verify", "check"], + defaultAgent: "review", + estimatedDuration: 3000, + }, + { + id: "deployment", + intents: ["deploy", "release"], + defaultAgent: "deploy", + estimatedDuration: 10000, + }, +]; + +/** + * 決定を実行する + * + * @param state - θサイクル状態 + * @param analysis - 分析結果 + * @returns 更新された状態と決定結果 + */ +export async function decide( + state: ThetaCycleState, + analysis?: AnalysisResult, +): Promise<{ state: ThetaCycleState; result: DecisionResult }> { + const { runId } = state; + + // フェーズ開始イベント + emitPhaseEvent(runId, ThetaEventType.PHASE_START, { + phase: ThetaPhase.DECIDE, + }); + + try { + // 分析結果の取得 + const analysisResult = analysis ?? (state.context.get("analyze.result") as AnalysisResult); + if (!analysisResult) { + throw new Error("Analysis result not found"); + } + + // 処理方針の決定 + const strategy = selectStrategy(analysisResult.intent); + + // エージェントの選択 + const agent = selectAgent(analysisResult.intent); + + // パラメータの構築 + const params = buildParams(analysisResult, strategy); + + // 推定実行時間の設定 + const strategyDef = AVAILABLE_STRATEGIES.find((s) => s.id === strategy); + const estimatedDuration = strategyDef?.estimatedDuration; + + const result: DecisionResult = { + strategy, + agent, + params, + estimatedDuration, + }; + + // 決定イベント記録 + const decisionEvent: ThetaEvent = { + runId, + phase: ThetaPhase.DECIDE, + timestamp: Date.now(), + type: ThetaEventType.DECISION, + data: { + result, + }, + }; + + state.events.push(decisionEvent); + state.currentPhase = ThetaPhase.DECIDE; + state.context.set("decide.result", result); + + // Agentイベントを発行 + emitAgentEvent({ + runId, + stream: "tool", + data: { + type: "decide", + strategy, + agent, + params, + }, + }); + + // フェーズ完了イベント + emitPhaseEvent(runId, ThetaEventType.PHASE_COMPLETE, { + phase: ThetaPhase.DECIDE, + strategy, + agent, + }); + + return { state, result }; + } catch (error) { + // エラーイベント + emitPhaseEvent(runId, ThetaEventType.PHASE_ERROR, { + phase: ThetaPhase.DECIDE, + error: error instanceof Error ? error.message : String(error), + }); + throw error; + } +} + +/** + * 意図に基づいて処理方針を選択する + */ +function selectStrategy(intent: string): string { + const strategy = AVAILABLE_STRATEGIES.find((s) => s.intents.includes(intent)); + return strategy?.id ?? "default"; +} + +/** + * 意図に基づいてエージェントを選択する + */ +function selectAgent(intent: string): string | undefined { + const agent = AVAILABLE_AGENTS.find((a) => a.intents.includes(intent) || a.intents.includes("*")); + return agent?.id; +} + +/** + * パラメータを構築する + */ +function buildParams(analysis: AnalysisResult, strategy: string): Record { + return { + intent: analysis.intent, + entities: analysis.entities, + strategy, + confidence: analysis.confidence, + timestamp: Date.now(), + }; +} + +/** + * 決定結果を取得する + * + * @param state - θサイクル状態 + * @returns 決定結果 + */ +export function getDecisionResult(state: ThetaCycleState): DecisionResult | undefined { + return state.context.get("decide.result") as DecisionResult | undefined; +} + +/** + * 利用可能なエージェント一覧を取得する + */ +export function getAvailableAgents(): AgentDefinition[] { + return [...AVAILABLE_AGENTS]; +} + +/** + * 利用可能な処理方針一覧を取得する + */ +export function getAvailableStrategies(): StrategyDefinition[] { + return [...AVAILABLE_STRATEGIES]; +} + +/** + * フェーズイベントを発行する + */ +function emitPhaseEvent(runId: string, type: ThetaEventType, data: Record): void { + emitAgentEvent({ + runId, + stream: "tool", + data: { + type: "theta_event", + thetaEventType: type, + ...data, + }, + }); +} diff --git a/src/theta/execute.ts b/src/theta/execute.ts new file mode 100644 index 000000000..862de758d --- /dev/null +++ b/src/theta/execute.ts @@ -0,0 +1,253 @@ +/** + * θ₄ EXECUTE: 実行(タイムアウト管理) + * + * 決定された処理方針に基づいて実行を行う。 + * タイムアウトとリトライを管理する。 + */ + +import { emitAgentEvent } from "../infra/agent-events.js"; +import { + type DecisionResult, + ThetaCycleState, + ThetaEvent, + ThetaEventType, + ThetaPhase, +} from "./types.js"; +import type { ExecuteOptions, ExecutionResult } from "./types.js"; + +// 型のエクスポート +export type { ExecuteOptions, ExecutionResult }; + +/** + * デフォルトの実行オプション + */ +const DEFAULT_OPTIONS: ExecuteOptions = { + timeout: 30000, // 30秒 + retries: 3, +}; + +/** + * 実行を実行する + * + * @param state - θサイクル状態 + * @param decision - 決定結果 + * @param executor - 実行関数 + * @param options - 実行オプション + * @returns 更新された状態と実行結果 + */ +export async function execute( + state: ThetaCycleState, + decision: DecisionResult, + executor: (params: Record) => Promise, + options?: Partial, +): Promise<{ state: ThetaCycleState; result: ExecutionResult }> { + const { runId } = state; + const opts = { ...DEFAULT_OPTIONS, ...options }; + + // フェーズ開始イベント + emitPhaseEvent(runId, ThetaEventType.PHASE_START, { + phase: ThetaPhase.EXECUTE, + strategy: decision.strategy, + agent: decision.agent, + }); + + const startTime = Date.now(); + + try { + // リトライ付き実行 + const data = await executeWithRetry( + executor, + decision.params, + opts.timeout, + opts.retries, + opts.onProgress, + ); + + const duration = Date.now() - startTime; + const result: ExecutionResult = { + success: true, + data, + duration, + }; + + // 実行イベント記録 + const executionEvent: ThetaEvent = { + runId, + phase: ThetaPhase.EXECUTE, + timestamp: Date.now(), + type: ThetaEventType.EXECUTION, + data: { + result, + }, + }; + + state.events.push(executionEvent); + state.currentPhase = ThetaPhase.EXECUTE; + state.context.set("execute.result", result); + + // Agentイベントを発行 + emitAgentEvent({ + runId, + stream: "tool", + data: { + type: "execute", + success: true, + duration, + }, + }); + + // フェーズ完了イベント + emitPhaseEvent(runId, ThetaEventType.PHASE_COMPLETE, { + phase: ThetaPhase.EXECUTE, + duration, + }); + + return { state, result }; + } catch (error) { + const duration = Date.now() - startTime; + const execError = error instanceof Error ? error : new Error(String(error)); + + const result: ExecutionResult = { + success: false, + error: execError, + duration, + }; + + // 実行エラーイベント記録 + const executionEvent: ThetaEvent = { + runId, + phase: ThetaPhase.EXECUTE, + timestamp: Date.now(), + type: ThetaEventType.EXECUTION, + data: { + result, + }, + }; + + state.events.push(executionEvent); + state.context.set("execute.result", result); + + // エラーイベント + emitPhaseEvent(runId, ThetaEventType.PHASE_ERROR, { + phase: ThetaPhase.EXECUTE, + error: execError.message, + duration, + }); + + return { state, result }; + } +} + +/** + * リトライ付きで実行する + */ +async function executeWithRetry( + executor: (params: Record) => Promise, + params: Record, + timeout: number, + retries: number, + onProgress?: (progress: number) => void, +): Promise { + let lastError: Error | null = null; + + for (let attempt = 0; attempt <= retries; attempt++) { + try { + // 進度通知 + onProgress?.(attempt / (retries + 1)); + + // タイムアウト付き実行 + const result = await withTimeout(executor(params), timeout); + return result; + } catch (error) { + lastError = error instanceof Error ? error : new Error(String(error)); + + // 最後の試行で失敗した場合はエラーを投げる + if (attempt >= retries) { + throw new Error(`Execution failed after ${retries + 1} attempts: ${lastError.message}`); + } + + // 指数バックオフで待機 + const delay = Math.min(1000 * 2 ** attempt, 10000); + await sleep(delay); + } + } + + throw lastError ?? new Error("Execution failed"); +} + +/** + * タイムアウト付きで実行する + */ +function withTimeout(promise: Promise, timeoutMs: number): Promise { + return Promise.race([ + promise, + new Promise((_, reject) => + setTimeout(() => reject(new Error(`Timeout after ${timeoutMs}ms`)), timeoutMs), + ), + ]); +} + +/** + * 指定ミリ秒待機する + */ +function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +/** + * 実行結果を取得する + * + * @param state - θサイクル状態 + * @returns 実行結果 + */ +export function getExecutionResult(state: ThetaCycleState): ExecutionResult | undefined { + return state.context.get("execute.result") as ExecutionResult | undefined; +} + +/** + * フェーズイベントを発行する + */ +function emitPhaseEvent(runId: string, type: ThetaEventType, data: Record): void { + emitAgentEvent({ + runId, + stream: "tool", + data: { + type: "theta_event", + thetaEventType: type, + ...data, + }, + }); +} + +/** + * バックグラウンド実行を開始する(完了を待たない) + * + * @param state - θサイクル状態 + * @param decision - 決定結果 + * @param executor - 実行関数 + * @param options - 実行オプション + * @returns 実行ID(後で結果を取得するために使用) + */ +export function executeBackground( + state: ThetaCycleState, + decision: DecisionResult, + executor: (params: Record) => Promise, + options?: Partial, +): string { + const executionId = `bg_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`; + + // 非同期実行(エラーは無視) + execute(state, decision, executor, options).catch(() => { + // バックグラウンド実行のエラーはログのみ記録 + emitAgentEvent({ + runId: state.runId, + stream: "error", + data: { + type: "background_execution_error", + executionId, + }, + }); + }); + + return executionId; +} diff --git a/src/theta/improve.ts b/src/theta/improve.ts new file mode 100644 index 000000000..cdb062a90 --- /dev/null +++ b/src/theta/improve.ts @@ -0,0 +1,279 @@ +/** + * θ₆ IMPROVE: ログ照合、改善提案 + * + * 実行結果を分析し、改善提案を生成する。 + * 学習サイクルを完結させる。 + */ + +import { emitAgentEvent } from "../infra/agent-events.js"; +import { ThetaCycleState, ThetaEvent, ThetaEventType, ThetaPhase } from "./types.js"; +import type { AnalysisResult, ImprovementSuggestion, VerificationResult } from "./types.js"; + +// 型のエクスポート +export type { ImprovementSuggestion }; + +/** + * 改善パターンの定義 + */ +interface ImprovementPattern { + /** パターンID */ + id: string; + /** パターンマッチ条件 */ + match: (context: ThetaCycleState) => boolean; + /** 改善提案生成関数 */ + suggest: (context: ThetaCycleState) => ImprovementSuggestion; +} + +/** デフォルトの改善パターン一覧 */ +const DEFAULT_IMPROVEMENT_PATTERNS: ImprovementPattern[] = [ + { + id: "failed_execution", + match: (ctx) => { + const result = ctx.context.get("execute.result") as { success?: boolean } | undefined; + return result?.success === false; + }, + suggest: () => ({ + suggestion: + "Execution failed. Review error logs and consider retry with different parameters.", + target: "execution", + priority: "high", + }), + }, + { + id: "low_quality_score", + match: (ctx) => { + const result = ctx.context.get("verify.result") as VerificationResult | undefined; + return result?.qualityScore !== undefined && result.qualityScore < 0.6; + }, + suggest: (ctx) => { + const result = ctx.context.get("verify.result") as VerificationResult | undefined; + return { + suggestion: `Quality score below threshold (${result?.qualityScore?.toFixed(2)}). Review failed checks: ${result?.failures?.join(", ")}`, + target: "quality", + priority: "medium", + }; + }, + }, + { + id: "slow_execution", + match: (ctx) => { + const result = ctx.context.get("execute.result") as { duration?: number } | undefined; + return result?.duration !== undefined && result.duration > 10000; // 10秒超過 + }, + suggest: (ctx) => { + const result = ctx.context.get("execute.result") as { duration?: number } | undefined; + return { + suggestion: `Execution took ${result?.duration}ms. Consider optimization or caching.`, + target: "performance", + priority: "low", + metadata: { duration: result?.duration }, + }; + }, + }, + { + id: "unknown_intent", + match: (ctx) => { + const result = ctx.context.get("analyze.result") as AnalysisResult | undefined; + return result?.intent === "unknown"; + }, + suggest: () => ({ + suggestion: + "Intent was not recognized. Consider expanding intent patterns or adding training data.", + target: "analysis", + priority: "medium", + }), + }, + { + id: "low_confidence", + match: (ctx) => { + const result = ctx.context.get("analyze.result") as AnalysisResult | undefined; + return result?.confidence !== undefined && result.confidence < 0.5; + }, + suggest: (ctx) => { + const result = ctx.context.get("analyze.result") as AnalysisResult | undefined; + return { + suggestion: `Analysis confidence low (${result?.confidence?.toFixed(2)}). Additional context may improve accuracy.`, + target: "analysis", + priority: "low", + metadata: { confidence: result?.confidence }, + }; + }, + }, + { + id: "successful_execution", + match: (ctx) => { + const execResult = ctx.context.get("execute.result") as { success?: boolean } | undefined; + const verifyResult = ctx.context.get("verify.result") as VerificationResult | undefined; + return execResult?.success === true && verifyResult?.passed === true; + }, + suggest: () => ({ + suggestion: + "Execution completed successfully. Consider documenting this pattern for future reference.", + target: "documentation", + priority: "low", + }), + }, +]; + +/** + * 改善を実行する + * + * @param state - θサイクル状態 + * @param customPatterns - カスタム改善パターン(オプション) + * @returns 更新された状態と改善提案の配列 + */ +export async function improve( + state: ThetaCycleState, + customPatterns?: ImprovementPattern[], +): Promise<{ state: ThetaCycleState; suggestions: ImprovementSuggestion[] }> { + const { runId } = state; + + // フェーズ開始イベント + emitPhaseEvent(runId, ThetaEventType.PHASE_START, { + phase: ThetaPhase.IMPROVE, + }); + + try { + // 改善パターンの評価 + const patterns = customPatterns ?? DEFAULT_IMPROVEMENT_PATTERNS; + const suggestions: ImprovementSuggestion[] = []; + + for (const pattern of patterns) { + if (pattern.match(state)) { + suggestions.push(pattern.suggest(state)); + } + } + + // 改善イベント記録 + const improvementEvent: ThetaEvent = { + runId, + phase: ThetaPhase.IMPROVE, + timestamp: Date.now(), + type: ThetaEventType.IMPROVEMENT, + data: { + suggestions, + count: suggestions.length, + }, + }; + + state.events.push(improvementEvent); + state.currentPhase = ThetaPhase.IMPROVE; + state.context.set("improve.suggestions", suggestions); + + // Agentイベントを発行 + emitAgentEvent({ + runId, + stream: "tool", + data: { + type: "improve", + suggestions, + count: suggestions.length, + }, + }); + + // フェーズ完了イベント + emitPhaseEvent(runId, ThetaEventType.PHASE_COMPLETE, { + phase: ThetaPhase.IMPROVE, + suggestionCount: suggestions.length, + }); + + return { state, suggestions }; + } catch (error) { + // エラーイベント + emitPhaseEvent(runId, ThetaEventType.PHASE_ERROR, { + phase: ThetaPhase.IMPROVE, + error: error instanceof Error ? error.message : String(error), + }); + throw error; + } +} + +/** + * 改善提案を取得する + * + * @param state - θサイクル状態 + * @returns 改善提案の配列 + */ +export function getImprovementSuggestions(state: ThetaCycleState): ImprovementSuggestion[] { + return (state.context.get("improve.suggestions") as ImprovementSuggestion[]) ?? []; +} + +/** + * 改善パターンを追加する + * + * @param patterns - 既存パターン配列 + * @param newPattern - 追加するパターン + * @returns 更新されたパターン配列 + */ +export function addImprovementPattern( + patterns: ImprovementPattern[], + newPattern: ImprovementPattern, +): ImprovementPattern[] { + return [...patterns, newPattern]; +} + +/** + * カスタム改善パターンを作成する + * + * @param id - パターンID + * @param match - マッチ条件関数 + * @param suggest - 改善提案生成関数 + * @returns 改善パターン定義 + */ +export function createImprovementPattern( + id: string, + match: (context: ThetaCycleState) => boolean, + suggest: (context: ThetaCycleState) => ImprovementSuggestion, +): ImprovementPattern { + return { id, match, suggest }; +} + +/** + * サイクル全体のサマリーを生成する + * + * @param state - θサイクル状態 + * @returns サマリーオブジェクト + */ +export function generateCycleSummary(state: ThetaCycleState): { + runId: string; + duration: number; + phases: ThetaPhase[]; + eventCount: number; + success: boolean; + suggestions: ImprovementSuggestion[]; +} { + const startTime = state.startTime; + const endTime = Date.now(); + const duration = endTime - startTime; + + const phases = state.events.map((e) => e.phase).filter((p, i, arr) => arr.indexOf(p) === i); // ユニークなフェーズのみ + + const executionResult = state.context.get("execute.result") as { success?: boolean } | undefined; + const success = executionResult?.success ?? false; + + const suggestions = getImprovementSuggestions(state); + + return { + runId: state.runId, + duration, + phases, + eventCount: state.events.length, + success, + suggestions, + }; +} + +/** + * フェーズイベントを発行する + */ +function emitPhaseEvent(runId: string, type: ThetaEventType, data: Record): void { + emitAgentEvent({ + runId, + stream: "tool", + data: { + type: "theta_event", + thetaEventType: type, + ...data, + }, + }); +} diff --git a/src/theta/index.ts b/src/theta/index.ts new file mode 100644 index 000000000..acff658c1 --- /dev/null +++ b/src/theta/index.ts @@ -0,0 +1,261 @@ +/** + * θサイクル (Theta Cycle) - エントリーポイント + * + * Agent(Intent, World₀) = lim_{n→∞} (θ₆ ∘ θ₅ ∘ θ₄ ∘ θ₃ ∘ θ₂ ∘ θ₁)ⁿ + * + * 完全なθサイクル実行を提供する。 + */ + +// 型定義のエクスポート +export type { + // 共通型 + ThetaCycleState, + ThetaCycleOptions, + ThetaEvent, + ThetaEventType, + + // 各フェーズ関連 + ObserveContext, + AnalysisResult, + DecisionResult, + ExecuteOptions, + ExecutionResult, + VerificationResult, + ImprovementSuggestion, +} from "./types.js"; + +export { ThetaPhase } from "./types.js"; + +// θ₁ OBSERVE +export { observe, handleReaction, getObservations } from "./observe.js"; + +// θ₂ ANALYZE +export { analyze, getAnalysisResult } from "./analyze.js"; + +// θ₃ DECIDE +export { decide, getDecisionResult, getAvailableAgents, getAvailableStrategies } from "./decide.js"; + +// θ₄ EXECUTE +export { execute, executeBackground, getExecutionResult } from "./execute.js"; + +// θ₅ VERIFY +export { verify, getVerificationResult, addQualityCheck, createQualityCheck } from "./verify.js"; + +// θ₆ IMPROVE +export { + improve, + getImprovementSuggestions, + addImprovementPattern, + createImprovementPattern, + generateCycleSummary, +} from "./improve.js"; + +// 完全なサイクル実行 +import { observe, handleReaction, type ObserveContext } from "./observe.js"; +import { analyze, type AnalysisResult } from "./analyze.js"; +import { decide, type DecisionResult } from "./decide.js"; +import { execute, type ExecuteOptions, type ExecutionResult } from "./execute.js"; +import { verify, type VerificationResult } from "./verify.js"; +import { improve, generateCycleSummary, type ImprovementSuggestion } from "./improve.js"; +import type { ThetaCycleOptions, ThetaCycleState } from "./types.js"; +import { ThetaPhase } from "./types.js"; + +/** + * 新しいθサイクル状態を作成する + */ +export function createThetaCycleState(runId: string, options?: ThetaCycleOptions): ThetaCycleState { + return { + runId, + startTime: Date.now(), + currentPhase: null, + events: [], + context: new Map(), + }; +} + +/** + * 完全なθサイクルを実行する + * + * @param input - 入力データ + * @param executor - 実行関数 + * @param options - サイクルオプション + * @returns サイクル結果 + */ +export async function runThetaCycle( + input: unknown, + executor: (params: Record) => Promise, + options?: ThetaCycleOptions & Partial, +): Promise<{ + state: ThetaCycleState; + analysis: AnalysisResult; + decision: DecisionResult; + execution: ExecutionResult; + verification: VerificationResult; + suggestions: ImprovementSuggestion[]; +}> { + const runId = `theta_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`; + const state = createThetaCycleState(runId, options); + + // オプションのマージ + const timeout = options?.defaultTimeout ?? 30000; + const retries = options?.defaultRetries ?? 3; + + // θ₁ OBSERVE + const observeState = await observe(state, { input }); + + // θ₂ ANALYZE + const analyzeResult = await analyze(observeState); + const analyzeState = analyzeResult.state; + + // θ₃ DECIDE + const decideResult = await decide(analyzeState); + const decideState = decideResult.state; + + // θ₄ EXECUTE + const executeResult = await execute(decideState, decideResult.result, executor, { + timeout, + retries, + }); + const executeState = executeResult.state; + + // θ₅ VERIFY + const verifyResult = await verify(executeState); + const verifyState = verifyResult.state; + + // θ₆ IMPROVE + const improveResult = await improve(verifyState); + const improveState = improveResult.state; + + // サマリー生成 + const summary = generateCycleSummary(improveState); + + // オプションのコールバック + if (options?.onEvent) { + for (const event of improveState.events) { + options.onEvent(event); + } + } + + return { + state: improveState, + analysis: analyzeResult.result, + decision: decideResult.result, + execution: executeResult.result, + verification: verifyResult.result, + suggestions: improveResult.suggestions, + }; +} + +/** + * θサイクルをストリームモードで実行する + * 各フェーズの完了時にコールバックを呼び出す + * + * @param input - 入力データ + * @param executor - 実行関数 + * @param onPhase - フェーズ完了時のコールバック + * @param options - サイクルオプション + * @returns 最終的なサイクル結果 + */ +export async function runThetaCycleStream( + input: unknown, + executor: (params: Record) => Promise, + onPhase: (phase: ThetaPhase, data: unknown) => void, + options?: ThetaCycleOptions & Partial, +): Promise<{ + state: ThetaCycleState; + analysis: AnalysisResult; + decision: DecisionResult; + execution: ExecutionResult; + verification: VerificationResult; + suggestions: ImprovementSuggestion[]; +}> { + const runId = `theta_stream_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`; + const state = createThetaCycleState(runId, options); + + const timeout = options?.defaultTimeout ?? 30000; + const retries = options?.defaultRetries ?? 3; + + // θ₁ OBSERVE + let currentState = await observe(state, { input }); + onPhase(ThetaPhase.OBSERVE, currentState.events); + + // θ₂ ANALYZE + const analyzeResult = await analyze(currentState); + currentState = analyzeResult.state; + onPhase(ThetaPhase.ANALYZE, analyzeResult.result); + + // θ₃ DECIDE + const decideResult = await decide(currentState); + currentState = decideResult.state; + onPhase(ThetaPhase.DECIDE, decideResult.result); + + // θ₄ EXECUTE + const executeResult = await execute(currentState, decideResult.result, executor, { + timeout, + retries, + }); + currentState = executeResult.state; + onPhase(ThetaPhase.EXECUTE, executeResult.result); + + // θ₅ VERIFY + const verifyResult = await verify(currentState); + currentState = verifyResult.state; + onPhase(ThetaPhase.VERIFY, verifyResult.result); + + // θ₆ IMPROVE + const improveResult = await improve(currentState); + currentState = improveResult.state; + onPhase(ThetaPhase.IMPROVE, improveResult.suggestions); + + return { + state: currentState, + analysis: analyzeResult.result, + decision: decideResult.result, + execution: executeResult.result, + verification: verifyResult.result, + suggestions: improveResult.suggestions, + }; +} + +/** + * θサイクルの実行を一時停止/再開するためのチェックポイントを作成する + * + * @param state - 現在のサイクル状態 + * @returns シリアライズ可能なチェックポイントデータ + */ +export function createCheckpoint(state: ThetaCycleState): { + runId: string; + startTime: number; + currentPhase: ThetaPhase | null; + context: Record; + eventCount: number; +} { + return { + runId: state.runId, + startTime: state.startTime, + currentPhase: state.currentPhase, + context: Object.fromEntries(state.context.entries()), + eventCount: state.events.length, + }; +} + +/** + * チェックポイントからサイクル状態を復元する + * + * @param checkpoint - チェックポイントデータ + * @returns 復元されたサイクル状態 + */ +export function restoreFromCheckpoint(checkpoint: { + runId: string; + startTime: number; + currentPhase: ThetaPhase | null; + context: Record; +}): ThetaCycleState { + return { + runId: checkpoint.runId, + startTime: checkpoint.startTime, + currentPhase: checkpoint.currentPhase, + events: [], + context: new Map(Object.entries(checkpoint.context)), + }; +} diff --git a/src/theta/observe.ts b/src/theta/observe.ts new file mode 100644 index 000000000..f65f2dfc3 --- /dev/null +++ b/src/theta/observe.ts @@ -0,0 +1,152 @@ +/** + * θ₁ OBSERVE: イベントログ記録、リアクション処理 + * + * 問題理解・分析の最初のフェーズ。 + * 入力データを観測し、イベントログを記録する。 + */ + +import { emitAgentEvent } from "../infra/agent-events.js"; +import { + type ObserveContext, + ThetaCycleState, + ThetaEvent, + ThetaEventType, + ThetaPhase, +} from "./types.js"; + +/** + * 観測を実行する + * + * @param state - θサイクル状態 + * @param context - 観測コンテキスト + * @returns 更新された状態 + */ +export async function observe( + state: ThetaCycleState, + context: ObserveContext, +): Promise { + const { runId } = state; + + // フェーズ開始イベント + emitPhaseEvent(runId, ThetaEventType.PHASE_START, { + phase: ThetaPhase.OBSERVE, + input: context.input, + }); + + try { + // 入力データの観測 + const observation: ThetaEvent = { + runId, + phase: ThetaPhase.OBSERVE, + timestamp: Date.now(), + type: ThetaEventType.OBSERVATION, + data: { + input: context.input, + metadata: context.metadata, + sessionKey: context.sessionKey, + }, + }; + + // 状態を更新 + state.events.push(observation); + state.currentPhase = ThetaPhase.OBSERVE; + state.context.set("observe.input", context.input); + state.context.set("observe.metadata", context.metadata); + + // Agentイベントを発行 + emitAgentEvent({ + runId, + stream: "tool", + data: { + type: "observe", + input: context.input, + }, + }); + + // フェーズ完了イベント + emitPhaseEvent(runId, ThetaEventType.PHASE_COMPLETE, { + phase: ThetaPhase.OBSERVE, + observationCount: 1, + }); + + return state; + } catch (error) { + // エラーイベント + emitPhaseEvent(runId, ThetaEventType.PHASE_ERROR, { + phase: ThetaPhase.OBSERVE, + error: error instanceof Error ? error.message : String(error), + }); + throw error; + } +} + +/** + * リアクションを処理する + * + * @param state - θサイクル状態 + * @param reaction - リアクションデータ + * @returns 更新された状態 + */ +export async function handleReaction( + state: ThetaCycleState, + reaction: Record, +): Promise { + const { runId } = state; + + // リアクション記録 + const reactionEvent: ThetaEvent = { + runId, + phase: ThetaPhase.OBSERVE, + timestamp: Date.now(), + type: ThetaEventType.OBSERVATION, + data: { + type: "reaction", + reaction, + }, + }; + + state.events.push(reactionEvent); + state.context.set("observe.reaction", reaction); + + // Agentイベントを発行 + emitAgentEvent({ + runId, + stream: "tool", + data: { + type: "reaction", + reaction, + }, + }); + + return state; +} + +/** + * フェーズイベントを発行する + */ +function emitPhaseEvent(runId: string, type: ThetaEventType, data: Record): void { + emitAgentEvent({ + runId, + stream: "tool", + data: { + type: "theta_event", + thetaEventType: type, + ...data, + }, + }); +} + +/** + * 観測ログを取得する + * + * @param state - θサイクル状態 + * @returns 観測イベントの配列 + */ +export function getObservations(state: ThetaCycleState): ThetaEvent[] { + return state.events.filter( + (e) => e.phase === ThetaPhase.OBSERVE && e.type === ThetaEventType.OBSERVATION, + ); +} + +// 型のエクスポート +export type { ObserveContext }; diff --git a/src/theta/types.ts b/src/theta/types.ts new file mode 100644 index 000000000..b2506f87d --- /dev/null +++ b/src/theta/types.ts @@ -0,0 +1,181 @@ +/** + * θサイクル (Theta Cycle) の型定義 + * + * Agent(Intent, World₀) = lim_{n→∞} (θ₆ ∘ θ₅ ∘ θ₄ ∘ θ₃ ∘ θ₂ ∘ θ₁)ⁿ + */ + +/** + * θサイクルの各フェーズ + */ +export enum ThetaPhase { + OBSERVE = "θ₁ OBSERVE", + ANALYZE = "θ₂ ANALYZE", + DECIDE = "θ₃ DECIDE", + EXECUTE = "θ₄ EXECUTE", + VERIFY = "θ₅ VERIFY", + IMPROVE = "θ₆ IMPROVE", +} + +/** + * イベントログの型 + */ +export interface ThetaEvent { + /** 実行ID */ + runId: string; + /** フェーズ */ + phase: ThetaPhase; + /** タイムスタンプ */ + timestamp: number; + /** イベント種別 */ + type: ThetaEventType; + /** データ */ + data: Record; +} + +/** + * イベント種別 + */ +export enum ThetaEventType { + /** フェーズ開始 */ + PHASE_START = "phase_start", + /** フェーズ完了 */ + PHASE_COMPLETE = "phase_complete", + /** フェーズエラー */ + PHASE_ERROR = "phase_error", + /** 観測データ記録 */ + OBSERVATION = "observation", + /** 分析結果 */ + ANALYSIS = "analysis", + /** 決定 */ + DECISION = "decision", + /** 実行結果 */ + EXECUTION = "execution", + /** 検証結果 */ + VERIFICATION = "verification", + /** 改善提案 */ + IMPROVEMENT = "improvement", +} + +/** + * 観測コンテキスト (OBSERVE用) + */ +export interface ObserveContext { + /** セッションキー */ + sessionKey?: string; + /** 入力データ */ + input: unknown; + /** メタデータ */ + metadata?: Record; +} + +/** + * 分析結果 (ANALYZE用) + */ +export interface AnalysisResult { + /** 意図解析結果 */ + intent: string; + /** 抽出されたエンティティ */ + entities: Record; + /** コンテキスト情報 */ + context: Record; + /** 信頼度 */ + confidence: number; +} + +/** + * 決定結果 (DECIDE用) + */ +export interface DecisionResult { + /** 選択された処理方針 */ + strategy: string; + /** 選択されたエージェント */ + agent?: string; + /** パラメータ */ + params: Record; + /** 推定実行時間(ms) */ + estimatedDuration?: number; +} + +/** + * 実行オプション (EXECUTE用) + */ +export interface ExecuteOptions { + /** タイムアウト(ms) */ + timeout: number; + /** リトライ回数 */ + retries: number; + /** 进度回调 */ + onProgress?: (progress: number) => void; +} + +/** + * 実行結果 (EXECUTE用) + */ +export interface ExecutionResult { + /** 成功判定 */ + success: boolean; + /** 結果データ */ + data?: unknown; + /** エラー情報 */ + error?: Error; + /** 実行時間(ms) */ + duration: number; +} + +/** + * 検証結果 (VERIFY用) + */ +export interface VerificationResult { + /** 検証パス */ + passed: boolean; + /** 品質スコア (0-1) */ + qualityScore: number; + /** 検証項目ごとの結果 */ + checks: Record; + /** 失敗理由 */ + failures?: string[]; +} + +/** + * 改善提案 (IMPROVE用) + */ +export interface ImprovementSuggestion { + /** 改善内容 */ + suggestion: string; + /** 適用対象 */ + target: string; + /** 優先度 */ + priority: "low" | "medium" | "high"; + /** メタデータ */ + metadata?: Record; +} + +/** + * θサイクルの完全な実行状態 + */ +export interface ThetaCycleState { + /** 実行ID */ + runId: string; + /** 開始時刻 */ + startTime: number; + /** 現在のフェーズ */ + currentPhase: ThetaPhase | null; + /** イベントログ */ + events: ThetaEvent[]; + /** コンテキスト */ + context: Map; +} + +/** + * θサイクルの設定 + */ +export interface ThetaCycleOptions { + /** デフォルトタイムアウト(ms) */ + defaultTimeout?: number; + /** デフォルトリトライ回数 */ + defaultRetries?: number; + /** 詳細ログレベル */ + verbose?: boolean; + /** カスタムイベントハンドラ */ + onEvent?: (event: ThetaEvent) => void; +} diff --git a/src/theta/verify.ts b/src/theta/verify.ts new file mode 100644 index 000000000..eed1faafb --- /dev/null +++ b/src/theta/verify.ts @@ -0,0 +1,213 @@ +/** + * θ₅ VERIFY: 結果検証、品質チェック + * + * 実行結果を検証し、品質を評価する。 + */ + +import { emitAgentEvent } from "../infra/agent-events.js"; +import { + type ExecutionResult, + ThetaCycleState, + ThetaEvent, + ThetaEventType, + ThetaPhase, +} from "./types.js"; +import type { VerificationResult } from "./types.js"; + +// 型のエクスポート +export type { VerificationResult }; + +/** + * 品質チェック項目の定義 + */ +interface QualityCheck { + /** チェック名 */ + name: string; + /** チェック関数 */ + check: (result: ExecutionResult) => boolean | Promise; + /** 重大度 */ + severity: "critical" | "major" | "minor"; +} + +/** デフォルトの品質チェック一覧 */ +const DEFAULT_QUALITY_CHECKS: QualityCheck[] = [ + { + name: "execution_success", + check: (result) => result.success, + severity: "critical", + }, + { + name: "has_output", + check: (result) => result.data !== undefined && result.data !== null, + severity: "major", + }, + { + name: "reasonable_duration", + check: (result) => result.duration < 60000, // 1分以内 + severity: "minor", + }, + { + name: "no_critical_errors", + check: (result) => { + if (!result.error) return true; + const errorMsg = result.error.message.toLowerCase(); + return !errorMsg.includes("critical") && !errorMsg.includes("fatal"); + }, + severity: "critical", + }, +]; + +/** + * 検証を実行する + * + * @param state - θサイクル状態 + * @param customChecks - カスタム品質チェック(オプション) + * @returns 更新された状態と検証結果 + */ +export async function verify( + state: ThetaCycleState, + customChecks?: QualityCheck[], +): Promise<{ state: ThetaCycleState; result: VerificationResult }> { + const { runId } = state; + + // フェーズ開始イベント + emitPhaseEvent(runId, ThetaEventType.PHASE_START, { + phase: ThetaPhase.VERIFY, + }); + + try { + // 実行結果の取得 + const executionResult = state.context.get("execute.result") as ExecutionResult | undefined; + + if (!executionResult) { + throw new Error("Execution result not found"); + } + + // 品質チェックの実行 + const checks = customChecks ?? DEFAULT_QUALITY_CHECKS; + const checkResults: Record = {}; + const failures: string[] = []; + let totalScore = 0; + let totalWeight = 0; + + for (const check of checks) { + const passed = await check.check(executionResult); + checkResults[check.name] = passed; + + // 重み付け(critical: 5, major: 3, minor: 1) + const weight = check.severity === "critical" ? 5 : check.severity === "major" ? 3 : 1; + totalWeight += weight; + + if (passed) { + totalScore += weight; + } else { + failures.push(`${check.name} (${check.severity})`); + } + } + + // 品質スコアの計算 (0-1) + const qualityScore = totalWeight > 0 ? totalScore / totalWeight : 0; + + const result: VerificationResult = { + passed: qualityScore >= 0.6, // 60%以上でパス + qualityScore, + checks: checkResults, + failures: failures.length > 0 ? failures : undefined, + }; + + // 検証イベント記録 + const verificationEvent: ThetaEvent = { + runId, + phase: ThetaPhase.VERIFY, + timestamp: Date.now(), + type: ThetaEventType.VERIFICATION, + data: { + result, + }, + }; + + state.events.push(verificationEvent); + state.currentPhase = ThetaPhase.VERIFY; + state.context.set("verify.result", result); + + // Agentイベントを発行 + emitAgentEvent({ + runId, + stream: "tool", + data: { + type: "verify", + passed: result.passed, + qualityScore: result.qualityScore, + failures: result.failures, + }, + }); + + // フェーズ完了イベント + emitPhaseEvent(runId, ThetaEventType.PHASE_COMPLETE, { + phase: ThetaPhase.VERIFY, + passed: result.passed, + qualityScore: result.qualityScore, + }); + + return { state, result }; + } catch (error) { + // エラーイベント + emitPhaseEvent(runId, ThetaEventType.PHASE_ERROR, { + phase: ThetaPhase.VERIFY, + error: error instanceof Error ? error.message : String(error), + }); + throw error; + } +} + +/** + * 検証結果を取得する + * + * @param state - θサイクル状態 + * @returns 検証結果 + */ +export function getVerificationResult(state: ThetaCycleState): VerificationResult | undefined { + return state.context.get("verify.result") as VerificationResult | undefined; +} + +/** + * 品質チェックを追加する + * + * @param checks - 既存チェック配列 + * @param newCheck - 追加するチェック + * @returns 更新されたチェック配列 + */ +export function addQualityCheck(checks: QualityCheck[], newCheck: QualityCheck): QualityCheck[] { + return [...checks, newCheck]; +} + +/** + * カスタム品質チェックを作成する + * + * @param name - チェック名 + * @param check - チェック関数 + * @param severity - 重大度 + * @returns 品質チェック定義 + */ +export function createQualityCheck( + name: string, + check: (result: ExecutionResult) => boolean | Promise, + severity: "critical" | "major" | "minor" = "major", +): QualityCheck { + return { name, check, severity }; +} + +/** + * フェーズイベントを発行する + */ +function emitPhaseEvent(runId: string, type: ThetaEventType, data: Record): void { + emitAgentEvent({ + runId, + stream: "tool", + data: { + type: "theta_event", + thetaEventType: type, + ...data, + }, + }); +}