feat(theta): implement θ-cycle for autonomous processing
- Add 6-phase processing cycle: OBSERVE → ANALYZE → DECIDE → EXECUTE → VERIFY → IMPROVE - Types and interfaces in types.ts - Each phase has dedicated module - Entry point in index.ts Closes #1801
This commit is contained in:
parent
afb659487c
commit
76e87167c9
202
src/theta/analyze.ts
Normal file
202
src/theta/analyze.ts
Normal file
@ -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<string, unknown> | 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<string, unknown>;
|
||||
if (typeof obj.intent === "string") {
|
||||
return obj.intent;
|
||||
}
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* 入力からエンティティを抽出する
|
||||
*/
|
||||
function extractEntities(input: unknown): Record<string, unknown> {
|
||||
const entities: Record<string, unknown> = {};
|
||||
|
||||
if (typeof input === "object" && input !== null) {
|
||||
const obj = input as Record<string, unknown>;
|
||||
// よく使われるエンティティキーを抽出
|
||||
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<string, unknown>): Record<string, unknown> {
|
||||
return {
|
||||
inputType: typeof input,
|
||||
hasMetadata: metadata !== undefined,
|
||||
metadataKeys: metadata ? Object.keys(metadata) : [],
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 信頼度を計算する
|
||||
*/
|
||||
function calculateConfidence(intent: string, entities: Record<string, unknown>): 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<string, unknown>): void {
|
||||
emitAgentEvent({
|
||||
runId,
|
||||
stream: "tool",
|
||||
data: {
|
||||
type: "theta_event",
|
||||
thetaEventType: type,
|
||||
...data,
|
||||
},
|
||||
});
|
||||
}
|
||||
238
src/theta/decide.ts
Normal file
238
src/theta/decide.ts
Normal file
@ -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<string, unknown> {
|
||||
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<string, unknown>): void {
|
||||
emitAgentEvent({
|
||||
runId,
|
||||
stream: "tool",
|
||||
data: {
|
||||
type: "theta_event",
|
||||
thetaEventType: type,
|
||||
...data,
|
||||
},
|
||||
});
|
||||
}
|
||||
253
src/theta/execute.ts
Normal file
253
src/theta/execute.ts
Normal file
@ -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<T = unknown>(
|
||||
state: ThetaCycleState,
|
||||
decision: DecisionResult,
|
||||
executor: (params: Record<string, unknown>) => Promise<T>,
|
||||
options?: Partial<ExecuteOptions>,
|
||||
): 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<T>(
|
||||
executor: (params: Record<string, unknown>) => Promise<T>,
|
||||
params: Record<string, unknown>,
|
||||
timeout: number,
|
||||
retries: number,
|
||||
onProgress?: (progress: number) => void,
|
||||
): Promise<T> {
|
||||
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<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
|
||||
return Promise.race([
|
||||
promise,
|
||||
new Promise<T>((_, reject) =>
|
||||
setTimeout(() => reject(new Error(`Timeout after ${timeoutMs}ms`)), timeoutMs),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定ミリ秒待機する
|
||||
*/
|
||||
function sleep(ms: number): Promise<void> {
|
||||
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<string, unknown>): 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<T = unknown>(
|
||||
state: ThetaCycleState,
|
||||
decision: DecisionResult,
|
||||
executor: (params: Record<string, unknown>) => Promise<T>,
|
||||
options?: Partial<ExecuteOptions>,
|
||||
): 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;
|
||||
}
|
||||
279
src/theta/improve.ts
Normal file
279
src/theta/improve.ts
Normal file
@ -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<string, unknown>): void {
|
||||
emitAgentEvent({
|
||||
runId,
|
||||
stream: "tool",
|
||||
data: {
|
||||
type: "theta_event",
|
||||
thetaEventType: type,
|
||||
...data,
|
||||
},
|
||||
});
|
||||
}
|
||||
261
src/theta/index.ts
Normal file
261
src/theta/index.ts
Normal file
@ -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<T = unknown>(
|
||||
input: unknown,
|
||||
executor: (params: Record<string, unknown>) => Promise<T>,
|
||||
options?: ThetaCycleOptions & Partial<ExecuteOptions>,
|
||||
): 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<T>(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<T = unknown>(
|
||||
input: unknown,
|
||||
executor: (params: Record<string, unknown>) => Promise<T>,
|
||||
onPhase: (phase: ThetaPhase, data: unknown) => void,
|
||||
options?: ThetaCycleOptions & Partial<ExecuteOptions>,
|
||||
): 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<T>(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<string, unknown>;
|
||||
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<string, unknown>;
|
||||
}): ThetaCycleState {
|
||||
return {
|
||||
runId: checkpoint.runId,
|
||||
startTime: checkpoint.startTime,
|
||||
currentPhase: checkpoint.currentPhase,
|
||||
events: [],
|
||||
context: new Map(Object.entries(checkpoint.context)),
|
||||
};
|
||||
}
|
||||
152
src/theta/observe.ts
Normal file
152
src/theta/observe.ts
Normal file
@ -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<ThetaCycleState> {
|
||||
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<string, unknown>,
|
||||
): Promise<ThetaCycleState> {
|
||||
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<string, unknown>): 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 };
|
||||
181
src/theta/types.ts
Normal file
181
src/theta/types.ts
Normal file
@ -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<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* イベント種別
|
||||
*/
|
||||
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<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析結果 (ANALYZE用)
|
||||
*/
|
||||
export interface AnalysisResult {
|
||||
/** 意図解析結果 */
|
||||
intent: string;
|
||||
/** 抽出されたエンティティ */
|
||||
entities: Record<string, unknown>;
|
||||
/** コンテキスト情報 */
|
||||
context: Record<string, unknown>;
|
||||
/** 信頼度 */
|
||||
confidence: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 決定結果 (DECIDE用)
|
||||
*/
|
||||
export interface DecisionResult {
|
||||
/** 選択された処理方針 */
|
||||
strategy: string;
|
||||
/** 選択されたエージェント */
|
||||
agent?: string;
|
||||
/** パラメータ */
|
||||
params: Record<string, unknown>;
|
||||
/** 推定実行時間(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<string, boolean>;
|
||||
/** 失敗理由 */
|
||||
failures?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 改善提案 (IMPROVE用)
|
||||
*/
|
||||
export interface ImprovementSuggestion {
|
||||
/** 改善内容 */
|
||||
suggestion: string;
|
||||
/** 適用対象 */
|
||||
target: string;
|
||||
/** 優先度 */
|
||||
priority: "low" | "medium" | "high";
|
||||
/** メタデータ */
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* θサイクルの完全な実行状態
|
||||
*/
|
||||
export interface ThetaCycleState {
|
||||
/** 実行ID */
|
||||
runId: string;
|
||||
/** 開始時刻 */
|
||||
startTime: number;
|
||||
/** 現在のフェーズ */
|
||||
currentPhase: ThetaPhase | null;
|
||||
/** イベントログ */
|
||||
events: ThetaEvent[];
|
||||
/** コンテキスト */
|
||||
context: Map<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* θサイクルの設定
|
||||
*/
|
||||
export interface ThetaCycleOptions {
|
||||
/** デフォルトタイムアウト(ms) */
|
||||
defaultTimeout?: number;
|
||||
/** デフォルトリトライ回数 */
|
||||
defaultRetries?: number;
|
||||
/** 詳細ログレベル */
|
||||
verbose?: boolean;
|
||||
/** カスタムイベントハンドラ */
|
||||
onEvent?: (event: ThetaEvent) => void;
|
||||
}
|
||||
213
src/theta/verify.ts
Normal file
213
src/theta/verify.ts
Normal file
@ -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<boolean>;
|
||||
/** 重大度 */
|
||||
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<string, boolean> = {};
|
||||
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<boolean>,
|
||||
severity: "critical" | "major" | "minor" = "major",
|
||||
): QualityCheck {
|
||||
return { name, check, severity };
|
||||
}
|
||||
|
||||
/**
|
||||
* フェーズイベントを発行する
|
||||
*/
|
||||
function emitPhaseEvent(runId: string, type: ThetaEventType, data: Record<string, unknown>): void {
|
||||
emitAgentEvent({
|
||||
runId,
|
||||
stream: "tool",
|
||||
data: {
|
||||
type: "theta_event",
|
||||
thetaEventType: type,
|
||||
...data,
|
||||
},
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user