[Add] Synaptic AI Pro
https://assetstore.unity.com/packages/tools/generative-ai/synaptic-ai-pro-natural-language-control-for-unity-336030
This commit is contained in:
@@ -0,0 +1,542 @@
|
||||
import { z } from 'zod';
|
||||
import { contextManager, analyzeUserIntent } from './context-manager.js';
|
||||
|
||||
// ===== 高度な対話・計画ツール =====
|
||||
export function registerAdvancedTools(mcpServer, sendUnityCommand) {
|
||||
|
||||
// プロジェクト計画ツール
|
||||
mcpServer.registerTool('unity_plan_project', {
|
||||
title: 'Plan Unity Project',
|
||||
description: 'Create a detailed plan and todo list for a Unity project based on natural language description',
|
||||
inputSchema: {
|
||||
description: z.string().describe('Natural language description of what to create'),
|
||||
projectType: z.enum(['game', 'tool', 'simulation', 'visualization', 'prototype']).optional(),
|
||||
complexity: z.enum(['simple', 'medium', 'complex']).optional().default('medium')
|
||||
}
|
||||
}, async (params) => {
|
||||
const plan = await analyzeAndPlanProject(params.description);
|
||||
await sendUnityCommand('create_project_plan', plan);
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: `プロジェクト計画を作成しました:\n${formatProjectPlan(plan)}`
|
||||
}]
|
||||
};
|
||||
});
|
||||
|
||||
// タスク分解ツール
|
||||
mcpServer.registerTool('unity_decompose_task', {
|
||||
title: 'Decompose Unity Task',
|
||||
description: 'Break down a complex task into smaller, manageable subtasks',
|
||||
inputSchema: {
|
||||
task: z.string().describe('Task to decompose'),
|
||||
context: z.string().optional().describe('Additional context or constraints'),
|
||||
maxDepth: z.number().optional().default(3)
|
||||
}
|
||||
}, async (params) => {
|
||||
const subtasks = await decomposeTask(params.task, params.context);
|
||||
await sendUnityCommand('create_task_list', { tasks: subtasks });
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: `タスクを${subtasks.length}個のサブタスクに分解しました:\n${formatTaskList(subtasks)}`
|
||||
}]
|
||||
};
|
||||
});
|
||||
|
||||
// バッチ実行ツール
|
||||
mcpServer.registerTool('unity_execute_batch', {
|
||||
title: 'Execute Multiple Unity Operations',
|
||||
description: 'Execute a series of Unity operations in sequence with progress feedback',
|
||||
inputSchema: {
|
||||
tasks: z.array(z.object({
|
||||
tool: z.string().describe('Tool name to execute'),
|
||||
parameters: z.record(z.any()).describe('Parameters for the tool'),
|
||||
description: z.string().describe('Human readable description')
|
||||
})),
|
||||
progressFeedback: z.boolean().default(true).describe('Send progress updates')
|
||||
}
|
||||
}, async (params) => {
|
||||
const results = [];
|
||||
const totalTasks = params.tasks.length;
|
||||
|
||||
for (let i = 0; i < params.tasks.length; i++) {
|
||||
const task = params.tasks[i];
|
||||
|
||||
try {
|
||||
// 進捗フィードバック
|
||||
if (params.progressFeedback) {
|
||||
console.log(`[Batch ${i+1}/${totalTasks}] ${task.description}`);
|
||||
}
|
||||
|
||||
// ツール実行
|
||||
const result = await sendUnityCommand(task.tool, task.parameters);
|
||||
|
||||
results.push({
|
||||
task: task.description,
|
||||
success: result.success,
|
||||
result: result.result || result.error,
|
||||
index: i + 1
|
||||
});
|
||||
|
||||
// エラーの場合は継続するかどうか判定
|
||||
if (!result.success) {
|
||||
console.error(`Task ${i+1} failed: ${result.error}`);
|
||||
// 重要でないエラーは継続、重大なエラーは停止
|
||||
if (result.error?.includes('not found') || result.error?.includes('Unknown operation')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 短い間隔をあける(Unity側の処理待ち)
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
|
||||
} catch (error) {
|
||||
results.push({
|
||||
task: task.description,
|
||||
success: false,
|
||||
result: error.message,
|
||||
index: i + 1
|
||||
});
|
||||
console.error(`Batch execution error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
const successCount = results.filter(r => r.success).length;
|
||||
const summary = `バッチ実行完了: ${successCount}/${totalTasks}個のタスクが成功\n\n` +
|
||||
results.map(r => `${r.index}. ${r.task}: ${r.success ? '✅' : '❌'} ${r.result}`).join('\n');
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: summary
|
||||
}]
|
||||
};
|
||||
});
|
||||
|
||||
// インテリジェント実装ツール
|
||||
mcpServer.registerTool('unity_implement_feature', {
|
||||
title: 'Implement Unity Feature',
|
||||
description: 'Intelligently implement a feature based on description and context',
|
||||
inputSchema: {
|
||||
feature: z.string().describe('Feature description'),
|
||||
requirements: z.array(z.string()).optional().describe('Specific requirements'),
|
||||
constraints: z.array(z.string()).optional().describe('Constraints or limitations'),
|
||||
style: z.enum(['minimal', 'standard', 'detailed']).optional().default('standard')
|
||||
}
|
||||
}, async (params) => {
|
||||
const implementation = await planFeatureImplementation(params);
|
||||
const steps = implementation.steps;
|
||||
|
||||
// 各ステップを順番に実行
|
||||
for (const step of steps) {
|
||||
await executeImplementationStep(step, sendUnityCommand);
|
||||
}
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: `機能「${params.feature}」を実装しました。\n実行したステップ:\n${steps.map((s, i) => `${i+1}. ${s.description}`).join('\n')}`
|
||||
}]
|
||||
};
|
||||
});
|
||||
|
||||
// コンテキスト保持ツール
|
||||
mcpServer.registerTool('unity_set_context', {
|
||||
title: 'Set Project Context',
|
||||
description: 'Set or update the current project context for more intelligent responses',
|
||||
inputSchema: {
|
||||
projectName: z.string().optional(),
|
||||
projectType: z.string().optional(),
|
||||
currentPhase: z.enum(['planning', 'prototyping', 'development', 'testing', 'polish']).optional(),
|
||||
technologies: z.array(z.string()).optional(),
|
||||
goals: z.array(z.string()).optional()
|
||||
}
|
||||
}, async (params) => {
|
||||
await updateProjectContext(params);
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: `プロジェクトコンテキストを更新しました:\n${JSON.stringify(params, null, 2)}`
|
||||
}]
|
||||
};
|
||||
});
|
||||
|
||||
// 進捗確認ツール
|
||||
mcpServer.registerTool('unity_check_progress', {
|
||||
title: 'Check Project Progress',
|
||||
description: 'Check the current progress of tasks and implementations',
|
||||
inputSchema: {
|
||||
scope: z.enum(['all', 'current', 'completed', 'pending']).optional().default('current'),
|
||||
detailed: z.boolean().optional().default(false)
|
||||
}
|
||||
}, async (params) => {
|
||||
const progress = await getProjectProgress(params.scope);
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: formatProgressReport(progress, params.detailed)
|
||||
}]
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// ===== ヘルパー関数 =====
|
||||
|
||||
async function analyzeAndPlanProject(description) {
|
||||
// 自然言語の説明を分析してプロジェクト計画を生成
|
||||
const plan = {
|
||||
title: extractProjectTitle(description),
|
||||
overview: description,
|
||||
phases: [],
|
||||
tasks: [],
|
||||
components: [],
|
||||
assets: []
|
||||
};
|
||||
|
||||
// キーワード分析
|
||||
const keywords = extractKeywords(description);
|
||||
|
||||
// ゲームプロジェクトの例
|
||||
if (keywords.includes('ゲーム') || keywords.includes('game')) {
|
||||
plan.phases = [
|
||||
{ name: 'セットアップ', tasks: ['シーン作成', 'フォルダ構造作成'] },
|
||||
{ name: 'プロトタイプ', tasks: ['基本操作実装', 'コアメカニクス'] },
|
||||
{ name: '本実装', tasks: ['UI作成', 'ゲームロジック', 'エフェクト'] },
|
||||
{ name: '仕上げ', tasks: ['バランス調整', 'パフォーマンス最適化'] }
|
||||
];
|
||||
}
|
||||
|
||||
// 具体的なタスクを生成
|
||||
plan.tasks = generateTasksFromDescription(description, keywords);
|
||||
|
||||
return plan;
|
||||
}
|
||||
|
||||
async function decomposeTask(task, context) {
|
||||
const subtasks = [];
|
||||
|
||||
// タスクの種類を判定
|
||||
const taskType = identifyTaskType(task);
|
||||
|
||||
switch (taskType) {
|
||||
case 'ui_creation':
|
||||
subtasks.push(
|
||||
{ name: 'UIレイアウト設計', priority: 'high' },
|
||||
{ name: 'Canvas作成', priority: 'high' },
|
||||
{ name: 'UI要素配置', priority: 'medium' },
|
||||
{ name: 'スタイル適用', priority: 'low' },
|
||||
{ name: 'インタラクション実装', priority: 'medium' }
|
||||
);
|
||||
break;
|
||||
|
||||
case 'character_creation':
|
||||
subtasks.push(
|
||||
{ name: 'キャラクターGameObject作成', priority: 'high' },
|
||||
{ name: 'モデル/スプライト設定', priority: 'high' },
|
||||
{ name: 'Collider追加', priority: 'medium' },
|
||||
{ name: '移動スクリプト作成', priority: 'high' },
|
||||
{ name: 'アニメーション設定', priority: 'medium' }
|
||||
);
|
||||
break;
|
||||
|
||||
case 'system_creation':
|
||||
subtasks.push(
|
||||
{ name: 'システム設計', priority: 'high' },
|
||||
{ name: 'コアクラス作成', priority: 'high' },
|
||||
{ name: 'インターフェース定義', priority: 'medium' },
|
||||
{ name: 'テスト実装', priority: 'medium' },
|
||||
{ name: 'ドキュメント作成', priority: 'low' }
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
// 一般的なタスク分解
|
||||
subtasks.push(
|
||||
{ name: '要件分析', priority: 'high' },
|
||||
{ name: '設計', priority: 'high' },
|
||||
{ name: '実装', priority: 'high' },
|
||||
{ name: 'テスト', priority: 'medium' },
|
||||
{ name: '最適化', priority: 'low' }
|
||||
);
|
||||
}
|
||||
|
||||
return subtasks;
|
||||
}
|
||||
|
||||
async function planFeatureImplementation(params) {
|
||||
const { feature, requirements, constraints } = params;
|
||||
const steps = [];
|
||||
|
||||
// 機能の種類を分析
|
||||
const featureAnalysis = analyzeFeature(feature);
|
||||
|
||||
// 実装ステップを生成
|
||||
if (featureAnalysis.needsUI) {
|
||||
steps.push({
|
||||
type: 'create_ui',
|
||||
description: 'UI要素の作成',
|
||||
params: {
|
||||
elementType: featureAnalysis.uiType || 'Panel',
|
||||
name: featureAnalysis.name + '_UI'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (featureAnalysis.needsScript) {
|
||||
steps.push({
|
||||
type: 'create_script',
|
||||
description: 'スクリプトの作成',
|
||||
params: {
|
||||
name: featureAnalysis.name + 'Controller',
|
||||
template: featureAnalysis.scriptTemplate || 'MonoBehaviour'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (featureAnalysis.needsGameObject) {
|
||||
steps.push({
|
||||
type: 'create_gameobject',
|
||||
description: 'GameObjectの作成',
|
||||
params: {
|
||||
objectType: featureAnalysis.objectType || 'Empty',
|
||||
name: featureAnalysis.name
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 要件に基づいて追加ステップ
|
||||
if (requirements) {
|
||||
requirements.forEach(req => {
|
||||
const additionalSteps = generateStepsFromRequirement(req);
|
||||
steps.push(...additionalSteps);
|
||||
});
|
||||
}
|
||||
|
||||
return { steps };
|
||||
}
|
||||
|
||||
async function executeImplementationStep(step, sendUnityCommand) {
|
||||
console.error(`Executing step: ${step.description}`);
|
||||
await sendUnityCommand(step.type, step.params);
|
||||
|
||||
// ステップ間の待機
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
}
|
||||
|
||||
// プロジェクトコンテキスト管理
|
||||
const projectContext = {
|
||||
projectName: '',
|
||||
projectType: '',
|
||||
currentPhase: 'planning',
|
||||
technologies: [],
|
||||
goals: [],
|
||||
completedTasks: [],
|
||||
pendingTasks: []
|
||||
};
|
||||
|
||||
async function updateProjectContext(params) {
|
||||
Object.assign(projectContext, params);
|
||||
}
|
||||
|
||||
async function getProjectProgress(scope) {
|
||||
return {
|
||||
total: projectContext.pendingTasks.length + projectContext.completedTasks.length,
|
||||
completed: projectContext.completedTasks.length,
|
||||
pending: projectContext.pendingTasks.length,
|
||||
tasks: scope === 'all' ?
|
||||
[...projectContext.completedTasks, ...projectContext.pendingTasks] :
|
||||
scope === 'completed' ? projectContext.completedTasks :
|
||||
scope === 'pending' ? projectContext.pendingTasks :
|
||||
projectContext.pendingTasks.slice(0, 5)
|
||||
};
|
||||
}
|
||||
|
||||
// ===== ユーティリティ関数 =====
|
||||
|
||||
function extractProjectTitle(description) {
|
||||
// 「〜を作りたい」「〜のような」などのパターンから抽出
|
||||
const patterns = [
|
||||
/「(.+?)」/,
|
||||
/(.+?)を作りたい/,
|
||||
/(.+?)のような/,
|
||||
/(.+?)みたいな/
|
||||
];
|
||||
|
||||
for (const pattern of patterns) {
|
||||
const match = description.match(pattern);
|
||||
if (match) return match[1];
|
||||
}
|
||||
|
||||
return 'Unityプロジェクト';
|
||||
}
|
||||
|
||||
function extractKeywords(text) {
|
||||
const keywords = [];
|
||||
const patterns = {
|
||||
game: ['ゲーム', 'game', 'プレイ', 'play'],
|
||||
ui: ['UI', 'ボタン', 'メニュー', 'button', 'menu'],
|
||||
character: ['キャラ', 'プレイヤー', 'character', 'player'],
|
||||
system: ['システム', 'system', '機能', 'feature']
|
||||
};
|
||||
|
||||
for (const [category, words] of Object.entries(patterns)) {
|
||||
if (words.some(word => text.toLowerCase().includes(word))) {
|
||||
keywords.push(category);
|
||||
}
|
||||
}
|
||||
|
||||
return keywords;
|
||||
}
|
||||
|
||||
function identifyTaskType(task) {
|
||||
const taskLower = task.toLowerCase();
|
||||
|
||||
if (taskLower.includes('ui') || taskLower.includes('ボタン') || taskLower.includes('メニュー')) {
|
||||
return 'ui_creation';
|
||||
} else if (taskLower.includes('キャラ') || taskLower.includes('プレイヤー')) {
|
||||
return 'character_creation';
|
||||
} else if (taskLower.includes('システム') || taskLower.includes('機能')) {
|
||||
return 'system_creation';
|
||||
}
|
||||
|
||||
return 'general';
|
||||
}
|
||||
|
||||
function analyzeFeature(feature) {
|
||||
const analysis = {
|
||||
name: feature.split(/[\s ]+/)[0],
|
||||
needsUI: false,
|
||||
needsScript: false,
|
||||
needsGameObject: false,
|
||||
uiType: null,
|
||||
scriptTemplate: null,
|
||||
objectType: null
|
||||
};
|
||||
|
||||
const featureLower = feature.toLowerCase();
|
||||
|
||||
// UI関連
|
||||
if (featureLower.includes('ボタン') || featureLower.includes('button')) {
|
||||
analysis.needsUI = true;
|
||||
analysis.uiType = 'Button';
|
||||
} else if (featureLower.includes('メニュー') || featureLower.includes('menu')) {
|
||||
analysis.needsUI = true;
|
||||
analysis.uiType = 'Panel';
|
||||
}
|
||||
|
||||
// スクリプト関連
|
||||
if (featureLower.includes('動') || featureLower.includes('制御') || featureLower.includes('システム')) {
|
||||
analysis.needsScript = true;
|
||||
}
|
||||
|
||||
// GameObject関連
|
||||
if (featureLower.includes('オブジェクト') || featureLower.includes('キャラ')) {
|
||||
analysis.needsGameObject = true;
|
||||
}
|
||||
|
||||
return analysis;
|
||||
}
|
||||
|
||||
function generateTasksFromDescription(description, keywords) {
|
||||
const tasks = [];
|
||||
let taskId = 1;
|
||||
|
||||
// 基本タスク
|
||||
tasks.push({
|
||||
id: taskId++,
|
||||
name: 'プロジェクトセットアップ',
|
||||
status: 'pending',
|
||||
priority: 'high'
|
||||
});
|
||||
|
||||
// キーワードに基づくタスク生成
|
||||
if (keywords.includes('game')) {
|
||||
tasks.push(
|
||||
{ id: taskId++, name: 'ゲームマネージャー作成', status: 'pending', priority: 'high' },
|
||||
{ id: taskId++, name: 'プレイヤーコントローラー実装', status: 'pending', priority: 'high' },
|
||||
{ id: taskId++, name: 'ゲームループ実装', status: 'pending', priority: 'medium' }
|
||||
);
|
||||
}
|
||||
|
||||
if (keywords.includes('ui')) {
|
||||
tasks.push(
|
||||
{ id: taskId++, name: 'UIシステム構築', status: 'pending', priority: 'high' },
|
||||
{ id: taskId++, name: 'メインメニュー作成', status: 'pending', priority: 'medium' }
|
||||
);
|
||||
}
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
function generateStepsFromRequirement(requirement) {
|
||||
const steps = [];
|
||||
const reqLower = requirement.toLowerCase();
|
||||
|
||||
if (reqLower.includes('アニメーション') || reqLower.includes('animation')) {
|
||||
steps.push({
|
||||
type: 'create_animation',
|
||||
description: 'アニメーション設定',
|
||||
params: { animationName: 'DefaultAnimation' }
|
||||
});
|
||||
}
|
||||
|
||||
if (reqLower.includes('物理') || reqLower.includes('physics')) {
|
||||
steps.push({
|
||||
type: 'setup_physics',
|
||||
description: '物理演算設定',
|
||||
params: { addRigidbody: true }
|
||||
});
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
function formatProjectPlan(plan) {
|
||||
let output = `📋 ${plan.title}\n\n`;
|
||||
output += `概要: ${plan.overview}\n\n`;
|
||||
|
||||
if (plan.phases.length > 0) {
|
||||
output += '📅 フェーズ:\n';
|
||||
plan.phases.forEach((phase, i) => {
|
||||
output += `${i+1}. ${phase.name}\n`;
|
||||
phase.tasks.forEach(task => {
|
||||
output += ` - ${task}\n`;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (plan.tasks.length > 0) {
|
||||
output += '\n✅ タスク一覧:\n';
|
||||
plan.tasks.forEach(task => {
|
||||
output += `- [${task.status === 'completed' ? 'x' : ' '}] ${task.name} (${task.priority})\n`;
|
||||
});
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
function formatTaskList(tasks) {
|
||||
return tasks.map((task, i) =>
|
||||
`${i+1}. ${task.name} [${task.priority}]`
|
||||
).join('\n');
|
||||
}
|
||||
|
||||
function formatProgressReport(progress, detailed) {
|
||||
let report = `📊 プロジェクト進捗\n`;
|
||||
report += `完了: ${progress.completed}/${progress.total} (${Math.round(progress.completed/progress.total*100)}%)\n\n`;
|
||||
|
||||
if (detailed && progress.tasks.length > 0) {
|
||||
report += '📋 タスク詳細:\n';
|
||||
progress.tasks.forEach(task => {
|
||||
const status = task.status === 'completed' ? '✅' : '⏳';
|
||||
report += `${status} ${task.name}\n`;
|
||||
});
|
||||
}
|
||||
|
||||
return report;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a43deefbf5324d82a7288afe235c36c
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 336030
|
||||
packageName: Synaptic AI Pro - Natural Language Control for Unity
|
||||
packageVersion: 1.2.23
|
||||
assetPath: Assets/Synaptic AI Pro/MCPServer/tools/advanced-tools.js
|
||||
uploadId: 920982
|
||||
@@ -0,0 +1,461 @@
|
||||
// ===== AIコンテキスト管理システム =====
|
||||
|
||||
class ContextManager {
|
||||
constructor() {
|
||||
this.conversationHistory = [];
|
||||
this.projectState = {
|
||||
currentProject: null,
|
||||
tasks: [],
|
||||
completedTasks: [],
|
||||
pendingTasks: [],
|
||||
unityState: {},
|
||||
createdObjects: [],
|
||||
modifiedObjects: []
|
||||
};
|
||||
this.conditionalFlows = new Map();
|
||||
this.userPreferences = this.loadUserPreferences();
|
||||
this.lastActions = [];
|
||||
this.dependencies = new Map();
|
||||
this.maxHistorySize = 100;
|
||||
}
|
||||
|
||||
// 会話履歴の追加
|
||||
addToHistory(entry) {
|
||||
this.conversationHistory.push({
|
||||
timestamp: new Date(),
|
||||
...entry
|
||||
});
|
||||
|
||||
// 最大サイズを超えたら古いものを削除
|
||||
if (this.conversationHistory.length > this.maxHistorySize) {
|
||||
this.conversationHistory = this.conversationHistory.slice(-this.maxHistorySize);
|
||||
}
|
||||
}
|
||||
|
||||
// コンテキストから意図を推測
|
||||
inferIntent(message) {
|
||||
const lowerMessage = message.toLowerCase();
|
||||
const lastAction = this.lastActions[this.lastActions.length - 1];
|
||||
|
||||
// 条件分岐パターン
|
||||
const conditionalPatterns = [
|
||||
{ pattern: /もし(.+)なら(.+)/i, type: 'if_then' },
|
||||
{ pattern: /(.+)の場合は(.+)/i, type: 'when_then' },
|
||||
{ pattern: /(.+)かどうか/i, type: 'check_condition' },
|
||||
{ pattern: /(.+)または(.+)/i, type: 'or_condition' },
|
||||
{ pattern: /(.+)かつ(.+)/i, type: 'and_condition' }
|
||||
];
|
||||
|
||||
// 続きの操作パターン
|
||||
const continuationPatterns = [
|
||||
{ pattern: /次に|それから|その後/i, type: 'sequence' },
|
||||
{ pattern: /同じ|もう一つ|さらに/i, type: 'repeat' },
|
||||
{ pattern: /代わりに|ではなく/i, type: 'alternative' },
|
||||
{ pattern: /全部|すべて|まとめて/i, type: 'batch' }
|
||||
];
|
||||
|
||||
// パターンマッチング
|
||||
for (const { pattern, type } of conditionalPatterns) {
|
||||
const match = message.match(pattern);
|
||||
if (match) {
|
||||
return { type, match, context: this.getRelevantContext() };
|
||||
}
|
||||
}
|
||||
|
||||
for (const { pattern, type } of continuationPatterns) {
|
||||
if (pattern.test(message)) {
|
||||
return { type, lastAction, context: this.getRelevantContext() };
|
||||
}
|
||||
}
|
||||
|
||||
// デフォルトの意図推測
|
||||
return this.inferFromContext(message);
|
||||
}
|
||||
|
||||
// コンテキストベースの意図推測
|
||||
inferFromContext(message) {
|
||||
const intent = {
|
||||
type: 'direct',
|
||||
confidence: 0.5,
|
||||
suggestedActions: []
|
||||
};
|
||||
|
||||
// 現在のプロジェクト状態を考慮
|
||||
if (this.projectState.pendingTasks.length > 0) {
|
||||
intent.suggestedActions.push({
|
||||
action: 'continue_tasks',
|
||||
tasks: this.projectState.pendingTasks.slice(0, 3)
|
||||
});
|
||||
intent.confidence += 0.2;
|
||||
}
|
||||
|
||||
// 最近の操作パターンを考慮
|
||||
const recentPatterns = this.analyzeRecentActions();
|
||||
if (recentPatterns.repeatingPattern) {
|
||||
intent.suggestedActions.push({
|
||||
action: 'repeat_pattern',
|
||||
pattern: recentPatterns.pattern
|
||||
});
|
||||
intent.confidence += 0.15;
|
||||
}
|
||||
|
||||
// ユーザーの好みを考慮
|
||||
const preferences = this.matchUserPreferences(message);
|
||||
if (preferences.length > 0) {
|
||||
intent.preferences = preferences;
|
||||
intent.confidence += 0.15;
|
||||
}
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
// 条件分岐フローの作成
|
||||
createConditionalFlow(condition, trueActions, falseActions = []) {
|
||||
const flowId = `flow_${Date.now()}`;
|
||||
this.conditionalFlows.set(flowId, {
|
||||
condition,
|
||||
trueActions,
|
||||
falseActions,
|
||||
executed: false,
|
||||
result: null
|
||||
});
|
||||
return flowId;
|
||||
}
|
||||
|
||||
// 条件の評価
|
||||
async evaluateCondition(condition, unityState) {
|
||||
// シンプルな条件評価
|
||||
if (condition.type === 'object_exists') {
|
||||
return unityState.gameObjects?.some(obj => obj.name === condition.objectName);
|
||||
}
|
||||
|
||||
if (condition.type === 'property_check') {
|
||||
const obj = unityState.gameObjects?.find(o => o.name === condition.objectName);
|
||||
if (obj && condition.property in obj) {
|
||||
return obj[condition.property] === condition.value;
|
||||
}
|
||||
}
|
||||
|
||||
if (condition.type === 'scene_check') {
|
||||
return unityState.activeScene === condition.sceneName;
|
||||
}
|
||||
|
||||
// より複雑な条件は Unity 側で評価
|
||||
return false;
|
||||
}
|
||||
|
||||
// 最近の操作パターン分析
|
||||
analyzeRecentActions() {
|
||||
if (this.lastActions.length < 3) {
|
||||
return { repeatingPattern: false };
|
||||
}
|
||||
|
||||
// 繰り返しパターンの検出
|
||||
const recentActions = this.lastActions.slice(-5);
|
||||
const patterns = {};
|
||||
|
||||
for (let i = 0; i < recentActions.length - 1; i++) {
|
||||
const pattern = `${recentActions[i].type}_${recentActions[i + 1].type}`;
|
||||
patterns[pattern] = (patterns[pattern] || 0) + 1;
|
||||
}
|
||||
|
||||
// 最も頻繁なパターンを見つける
|
||||
const mostFrequent = Object.entries(patterns)
|
||||
.sort(([, a], [, b]) => b - a)[0];
|
||||
|
||||
if (mostFrequent && mostFrequent[1] >= 2) {
|
||||
return {
|
||||
repeatingPattern: true,
|
||||
pattern: mostFrequent[0],
|
||||
frequency: mostFrequent[1]
|
||||
};
|
||||
}
|
||||
|
||||
return { repeatingPattern: false };
|
||||
}
|
||||
|
||||
// ユーザーの好みをマッチング
|
||||
matchUserPreferences(message) {
|
||||
const preferences = [];
|
||||
const lowerMessage = message.toLowerCase();
|
||||
|
||||
// 色の好み
|
||||
if (this.userPreferences.favoriteColors) {
|
||||
for (const color of this.userPreferences.favoriteColors) {
|
||||
if (lowerMessage.includes(color)) {
|
||||
preferences.push({ type: 'color', value: color });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UIレイアウトの好み
|
||||
if (this.userPreferences.uiLayout && lowerMessage.includes('ui')) {
|
||||
preferences.push({
|
||||
type: 'ui_layout',
|
||||
value: this.userPreferences.uiLayout
|
||||
});
|
||||
}
|
||||
|
||||
// 命名規則の好み
|
||||
if (this.userPreferences.namingConvention) {
|
||||
preferences.push({
|
||||
type: 'naming',
|
||||
value: this.userPreferences.namingConvention
|
||||
});
|
||||
}
|
||||
|
||||
return preferences;
|
||||
}
|
||||
|
||||
// タスク間の依存関係を設定
|
||||
setDependency(taskId, dependsOn) {
|
||||
if (!this.dependencies.has(taskId)) {
|
||||
this.dependencies.set(taskId, []);
|
||||
}
|
||||
this.dependencies.get(taskId).push(dependsOn);
|
||||
}
|
||||
|
||||
// 実行可能なタスクを取得
|
||||
getExecutableTasks() {
|
||||
const completed = new Set(this.projectState.completedTasks.map(t => t.id));
|
||||
const executable = [];
|
||||
|
||||
for (const task of this.projectState.pendingTasks) {
|
||||
const deps = this.dependencies.get(task.id) || [];
|
||||
if (deps.every(dep => completed.has(dep))) {
|
||||
executable.push(task);
|
||||
}
|
||||
}
|
||||
|
||||
return executable;
|
||||
}
|
||||
|
||||
// プロジェクト状態の更新
|
||||
updateProjectState(update) {
|
||||
Object.assign(this.projectState, update);
|
||||
|
||||
// Unity状態の同期
|
||||
if (update.unityState) {
|
||||
this.projectState.unityState = {
|
||||
...this.projectState.unityState,
|
||||
...update.unityState
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// アクションの記録
|
||||
recordAction(action) {
|
||||
this.lastActions.push({
|
||||
...action,
|
||||
timestamp: new Date()
|
||||
});
|
||||
|
||||
// 最大20アクションまで保持
|
||||
if (this.lastActions.length > 20) {
|
||||
this.lastActions = this.lastActions.slice(-20);
|
||||
}
|
||||
}
|
||||
|
||||
// ユーザー設定の読み込み
|
||||
loadUserPreferences() {
|
||||
// 実際にはファイルやDBから読み込む
|
||||
return {
|
||||
favoriteColors: ['blue', 'green', 'purple'],
|
||||
uiLayout: 'center',
|
||||
namingConvention: 'camelCase',
|
||||
defaultMaterial: 'Standard',
|
||||
preferredUnits: 'meters'
|
||||
};
|
||||
}
|
||||
|
||||
// 関連するコンテキストを取得
|
||||
getRelevantContext() {
|
||||
return {
|
||||
recentObjects: this.projectState.createdObjects.slice(-5),
|
||||
pendingTasks: this.projectState.pendingTasks.slice(0, 5),
|
||||
lastActions: this.lastActions.slice(-3),
|
||||
currentScene: this.projectState.unityState.activeScene
|
||||
};
|
||||
}
|
||||
|
||||
// コンテキストのサマリーを生成
|
||||
generateContextSummary() {
|
||||
const summary = {
|
||||
projectName: this.projectState.currentProject?.name || 'Unnamed Project',
|
||||
totalTasks: this.projectState.tasks.length,
|
||||
completedTasks: this.projectState.completedTasks.length,
|
||||
pendingTasks: this.projectState.pendingTasks.length,
|
||||
createdObjects: this.projectState.createdObjects.length,
|
||||
recentActions: this.lastActions.slice(-5).map(a => a.type),
|
||||
activeFlows: this.conditionalFlows.size
|
||||
};
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
// コンテキストのクリア
|
||||
clearContext() {
|
||||
this.conversationHistory = [];
|
||||
this.projectState = {
|
||||
currentProject: null,
|
||||
tasks: [],
|
||||
completedTasks: [],
|
||||
pendingTasks: [],
|
||||
unityState: {},
|
||||
createdObjects: [],
|
||||
modifiedObjects: []
|
||||
};
|
||||
this.conditionalFlows.clear();
|
||||
this.lastActions = [];
|
||||
this.dependencies.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// シングルトンインスタンス
|
||||
export const contextManager = new ContextManager();
|
||||
|
||||
// ヘルパー関数
|
||||
export function analyzeUserIntent(message, sendUnityCommand) {
|
||||
const intent = contextManager.inferIntent(message);
|
||||
|
||||
// 条件分岐の処理
|
||||
if (intent.type === 'if_then') {
|
||||
const [, condition, action] = intent.match;
|
||||
return {
|
||||
type: 'conditional',
|
||||
condition: parseCondition(condition),
|
||||
action: parseAction(action),
|
||||
execute: async () => {
|
||||
const state = await sendUnityCommand('get_scene_info', {});
|
||||
const result = await contextManager.evaluateCondition(
|
||||
parseCondition(condition),
|
||||
state
|
||||
);
|
||||
|
||||
if (result) {
|
||||
return await executeAction(parseAction(action), sendUnityCommand);
|
||||
}
|
||||
|
||||
return 'Condition not met';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 連続操作の処理
|
||||
if (intent.type === 'sequence') {
|
||||
return {
|
||||
type: 'sequence',
|
||||
previousAction: intent.lastAction,
|
||||
suggestedNext: getNextInSequence(intent.lastAction),
|
||||
execute: async () => {
|
||||
const next = getNextInSequence(intent.lastAction);
|
||||
if (next) {
|
||||
return await executeAction(next, sendUnityCommand);
|
||||
}
|
||||
return 'No next action in sequence';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// デフォルト処理
|
||||
return {
|
||||
type: 'direct',
|
||||
intent,
|
||||
execute: async () => {
|
||||
return await processDirectCommand(message, sendUnityCommand);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 条件のパース
|
||||
function parseCondition(conditionText) {
|
||||
// シンプルな条件パース実装
|
||||
if (conditionText.includes('存在')) {
|
||||
const match = conditionText.match(/(.+)が存在/);
|
||||
if (match) {
|
||||
return {
|
||||
type: 'object_exists',
|
||||
objectName: match[1].trim()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { type: 'unknown', text: conditionText };
|
||||
}
|
||||
|
||||
// アクションのパース
|
||||
function parseAction(actionText) {
|
||||
// シンプルなアクションパース実装
|
||||
return {
|
||||
type: 'parsed_action',
|
||||
text: actionText,
|
||||
commands: extractCommands(actionText)
|
||||
};
|
||||
}
|
||||
|
||||
// コマンド抽出
|
||||
function extractCommands(text) {
|
||||
const commands = [];
|
||||
|
||||
// 作成系コマンド
|
||||
if (text.includes('作成') || text.includes('作る')) {
|
||||
commands.push({ type: 'create', text });
|
||||
}
|
||||
|
||||
// 移動系コマンド
|
||||
if (text.includes('移動') || text.includes('動かす')) {
|
||||
commands.push({ type: 'move', text });
|
||||
}
|
||||
|
||||
// 変更系コマンド
|
||||
if (text.includes('変更') || text.includes('変える')) {
|
||||
commands.push({ type: 'modify', text });
|
||||
}
|
||||
|
||||
return commands;
|
||||
}
|
||||
|
||||
// アクション実行
|
||||
async function executeAction(action, sendUnityCommand) {
|
||||
// アクションタイプに基づいて適切なUnityコマンドを実行
|
||||
for (const command of action.commands) {
|
||||
switch (command.type) {
|
||||
case 'create':
|
||||
await sendUnityCommand('create_gameobject', {
|
||||
name: 'NewObject',
|
||||
description: command.text
|
||||
});
|
||||
break;
|
||||
case 'move':
|
||||
// 移動コマンドの実装
|
||||
break;
|
||||
case 'modify':
|
||||
// 変更コマンドの実装
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return `Executed ${action.commands.length} commands`;
|
||||
}
|
||||
|
||||
// 直接コマンドの処理
|
||||
async function processDirectCommand(message, sendUnityCommand) {
|
||||
// 既存のコマンド処理ロジック
|
||||
return 'Direct command processed';
|
||||
}
|
||||
|
||||
// シーケンスの次のアクション取得
|
||||
function getNextInSequence(lastAction) {
|
||||
// シーケンスパターンの定義
|
||||
const sequences = {
|
||||
'create_ui_button': { next: 'add_button_script', type: 'enhance' },
|
||||
'create_gameobject': { next: 'add_component', type: 'enhance' },
|
||||
'create_material': { next: 'assign_material', type: 'apply' }
|
||||
};
|
||||
|
||||
if (lastAction && sequences[lastAction.type]) {
|
||||
return sequences[lastAction.type];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fdb842e62448446808fa9a8cada6519a
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 336030
|
||||
packageName: Synaptic AI Pro - Natural Language Control for Unity
|
||||
packageVersion: 1.2.23
|
||||
assetPath: Assets/Synaptic AI Pro/MCPServer/tools/context-manager.js
|
||||
uploadId: 920982
|
||||
Reference in New Issue
Block a user