diff --git a/.gitignore b/.gitignore index fdcc3b0..949a4ec 100644 --- a/.gitignore +++ b/.gitignore @@ -35,10 +35,10 @@ client/Assets/Plugins/Editor/JetBrains* .vs/ .idea/ -# Autogenerated solution & project files -*.csproj -*.unityproj -*.sln +# Autogenerated solution & project files (Unity only) +client/*.csproj +client/*.unityproj +client/*.sln *.suo *.tmp *.user @@ -51,6 +51,12 @@ client/Assets/Plugins/Editor/JetBrains* *.opendb *.VC.db +# .NET build artifacts +**/bin/ +**/obj/ +launcher/PCL-CE/**/bin/ +launcher/PCL-CE/**/obj/ + # Unity meta for generated *.pidb.meta *.pdb.meta @@ -69,5 +75,10 @@ sysinfo.txt Thumbs.db desktop.ini +# ===================== .NET SDK (local install) ===================== +.dotnet/ +.nuget/ +.userdata/ + # ===================== Docker ===================== docker-compose.override.yml diff --git a/Makefile b/Makefile index 14e70b2..e3ad050 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: dev build-all run-auth run-economy run-blueprint run-match run-social run-relay tidy lint test +.PHONY: dev build-all run-auth run-economy run-blueprint run-match run-social run-relay run-ai tidy lint test # 启动开发环境基础设施 dev: @@ -30,6 +30,9 @@ run-social: run-relay: cd server && go run ./cmd/relay-server +run-ai: + cd server && go run ./cmd/ai-service + # 后端:工具 tidy: cd server && go mod tidy diff --git a/client/Assets/Scripts/RedCircuit.AIAssistant/AIActionExecutor.cs b/client/Assets/Scripts/RedCircuit.AIAssistant/AIActionExecutor.cs new file mode 100644 index 0000000..a874b26 --- /dev/null +++ b/client/Assets/Scripts/RedCircuit.AIAssistant/AIActionExecutor.cs @@ -0,0 +1,140 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace RedCircuit.AIAssistant +{ + /// + /// 操作执行器,将 AI 操作指令转换为游戏内动作。 + /// 所有操作必须经用户确认后才执行,支持撤销。 + /// + public class AIActionExecutor : MonoBehaviour + { + /// 待确认的操作队列 + private readonly Queue _pendingQueue = new(); + + /// 已执行的操作批次 (用于撤销) + private readonly Stack _executedBatches = new(); + + /// 单次操作元件上限 + private const int MaxActionsPerBatch = 20; + + /// 将 AI 操作加入待确认队列 + public void QueueActions(List actions) + { + if (actions.Count > MaxActionsPerBatch) + { + Debug.LogWarning($"[AIAction] 操作数 {actions.Count} 超过单批上限 {MaxActionsPerBatch},将截断"); + actions = actions.GetRange(0, MaxActionsPerBatch); + } + + foreach (var action in actions) + { + _pendingQueue.Enqueue(action); + } + + // TODO: 在画布上预览待执行操作 (半透明高亮) + PreviewActions(actions); + } + + /// 执行队列中所有待确认的操作 + public void ExecuteQueuedActions() + { + if (_pendingQueue.Count == 0) return; + + // 记录操作前快照 (用于撤销) + var batch = new ActionBatch(); + batch.SnapshotBefore = CaptureCanvasSnapshot(); + + while (_pendingQueue.TryDequeue(out var action)) + { + ExecuteAction(action); + batch.Actions.Add(action); + } + + batch.SnapshotAfter = CaptureCanvasSnapshot(); + _executedBatches.Push(batch); + + Debug.Log($"[AIAction] 已执行 {batch.Actions.Count} 个操作"); + ClearPreviews(); + } + + /// 取消所有待确认的操作 + public void CancelQueuedActions() + { + var count = _pendingQueue.Count; + _pendingQueue.Clear(); + ClearPreviews(); + Debug.Log($"[AIAction] 已取消 {count} 个待确认操作"); + } + + /// 撤销上一次 AI 操作批次 + public void UndoLastBatch() + { + if (!_executedBatches.TryPop(out var batch)) + { + Debug.LogWarning("[AIAction] 没有可撤销的 AI 操作"); + return; + } + + // 恢复操作前快照 + RestoreCanvasSnapshot(batch.SnapshotBefore); + Debug.Log($"[AIAction] 已撤销 {batch.Actions.Count} 个操作"); + } + + private void ExecuteAction(AIAction action) + { + switch (action.Type) + { + case ActionType.Place: + Debug.Log($"[AIAction] 放置 {action.Component} 于 ({action.X},{action.Y}) 旋转={action.Rotation}"); + // TODO: 调用 PlacementController.PlaceComponent() + break; + + case ActionType.Delete: + Debug.Log($"[AIAction] 删除 ({action.X},{action.Y})"); + // TODO: 调用 PlacementController.DeleteComponent() + break; + + case ActionType.Move: + Debug.Log($"[AIAction] 移动 ({action.FromX},{action.FromY}) -> ({action.X},{action.Y})"); + // TODO: 调用 PlacementController.MoveComponent() + break; + + case ActionType.Rotate: + Debug.Log($"[AIAction] 旋转 ({action.X},{action.Y}) -> {action.Rotation}"); + // TODO: 调用 PlacementController.RotateComponent() + break; + + case ActionType.Wire: + Debug.Log($"[AIAction] 连线 ({action.FromX},{action.FromY}) -> ({action.X},{action.Y})"); + // TODO: 调用 PlacementController.PlaceWire() + break; + } + } + + private void PreviewActions(List actions) + { + // TODO: 在画布上以半透明预览所有待执行操作 + foreach (var action in actions) + { + // 创建预览 Ghost 对象 + } + } + + private void ClearPreviews() + { + // TODO: 清除所有预览 Ghost 对象 + } + + private string CaptureCanvasSnapshot() + { + // TODO: 序列化当前画布状态为 JSON + return "{}"; + } + + private void RestoreCanvasSnapshot(string snapshot) + { + // TODO: 从 JSON 快照恢复画布状态 + } + } +} diff --git a/client/Assets/Scripts/RedCircuit.AIAssistant/AIAssistantManager.cs b/client/Assets/Scripts/RedCircuit.AIAssistant/AIAssistantManager.cs new file mode 100644 index 0000000..4c016a7 --- /dev/null +++ b/client/Assets/Scripts/RedCircuit.AIAssistant/AIAssistantManager.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using UnityEngine; + +namespace RedCircuit.AIAssistant +{ + /// + /// AI 助搭核心协调器,管理 AI 会话生命周期,协调各子系统。 + /// + public class AIAssistantManager : MonoBehaviour + { + public static AIAssistantManager Instance { get; private set; } + + /// 当前会话 ID + public string SessionId { get; private set; } = Guid.NewGuid().ToString("N")[..16]; + + /// AI 是否就绪 + public bool IsReady { get; private set; } + + /// 聊天历史记录 + public List History { get; } = new(); + + private AIChatController _chatController; + private AICircuitAdvisor _advisor; + private AIActionExecutor _executor; + private AIContextProvider _contextProvider; + + private void Awake() + { + if (Instance != null) { Destroy(gameObject); return; } + Instance = this; + } + + private void Start() + { + _chatController = GetComponent() ?? gameObject.AddComponent(); + _advisor = GetComponent() ?? gameObject.AddComponent(); + _executor = GetComponent() ?? gameObject.AddComponent(); + _contextProvider = GetComponent() ?? gameObject.AddComponent(); + + IsReady = true; + Debug.Log("[AIAssistant] Manager initialized"); + } + + /// + /// 处理用户输入的消息。 + /// + public async void HandleUserMessage(string message) + { + if (!IsReady) + { + _chatController.AppendSystemMessage("AI 助手尚未就绪,请稍候..."); + return; + } + + // 记录用户消息 + History.Add(new ChatMessage { Role = MessageRole.User, Content = message, Timestamp = DateTime.Now }); + _chatController.AppendUserMessage(message); + + // 采集上下文 + var context = _contextProvider.CollectContext(); + + // 发送到服务端 (TODO: 实现网络请求) + var response = await RequestAIChat(message, context); + + // 处理响应 + ProcessAIResponse(response); + } + + private async Task RequestAIChat(string message, GameContext context) + { + // TODO: 调用 ai-service POST /api/ai/chat + // 使用 SSE 流式接收响应 + await Task.Delay(100); // 模拟网络延迟 + return new AIResponse + { + Type = ResponseType.Text, + Content = "AI 助搭功能正在开发中,敬请期待!", + Actions = new List(), + RequiresConfirmation = false + }; + } + + private void ProcessAIResponse(AIResponse response) + { + // 记录 AI 消息 + History.Add(new ChatMessage { Role = MessageRole.Assistant, Content = response.Content, Timestamp = DateTime.Now }); + + switch (response.Type) + { + case ResponseType.Text: + _chatController.AppendAIMessage(response.Content); + break; + + case ResponseType.Analysis: + _chatController.AppendAIMessage(response.Content); + _advisor.ShowReport(response.Analysis); + break; + + case ResponseType.Action: + _chatController.AppendAIMessage(response.Content); + if (response.RequiresConfirmation && response.Actions.Count > 0) + { + _executor.QueueActions(response.Actions); + _chatController.ShowConfirmationPrompt(response.Actions.Count); + } + break; + + case ResponseType.Error: + _chatController.AppendErrorMessage(response.Content); + break; + } + } + + /// 用户确认执行 AI 操作 + public void ConfirmActions() + { + _executor.ExecuteQueuedActions(); + } + + /// 用户拒绝执行 AI 操作 + public void RejectActions() + { + _executor.CancelQueuedActions(); + } + + /// 撤销上一次 AI 操作 + public void UndoLastAIAction() + { + _executor.UndoLastBatch(); + } + } +} diff --git a/client/Assets/Scripts/RedCircuit.AIAssistant/AIChatController.cs b/client/Assets/Scripts/RedCircuit.AIAssistant/AIChatController.cs new file mode 100644 index 0000000..d1c1d9e --- /dev/null +++ b/client/Assets/Scripts/RedCircuit.AIAssistant/AIChatController.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace RedCircuit.AIAssistant +{ + /// + /// 聊天 UI 控制器,处理消息收发与流式显示。 + /// + public class AIChatController : MonoBehaviour + { + /// 聊天面板是否可见 + public bool IsVisible { get; private set; } + + /// 消息列表 (用于 UI 渲染) + public List Messages { get; } = new(); + + /// 显示聊天面板 + public void Show() + { + IsVisible = true; + // TODO: 播放面板展开动画 + } + + /// 隐藏聊天面板 + public void Hide() + { + IsVisible = false; + // TODO: 播放面板收起动画 + } + + /// 追加用户消息到 UI + public void AppendUserMessage(string content) + { + var msg = new ChatMessage + { + Role = MessageRole.User, + Content = content, + Timestamp = DateTime.Now + }; + Messages.Add(msg); + // TODO: 实例化用户消息气泡 UI + } + + /// 追加 AI 消息到 UI (支持流式追加) + public void AppendAIMessage(string content) + { + var msg = new ChatMessage + { + Role = MessageRole.Assistant, + Content = content, + Timestamp = DateTime.Now + }; + Messages.Add(msg); + // TODO: 实例化 AI 消息气泡 UI,支持打字机效果 + } + + /// 追加系统消息 + public void AppendSystemMessage(string content) + { + var msg = new ChatMessage + { + Role = MessageRole.System, + Content = content, + Timestamp = DateTime.Now + }; + Messages.Add(msg); + // TODO: 实例化系统消息 (居中灰色样式) + } + + /// 追加错误消息 + public void AppendErrorMessage(string content) + { + AppendSystemMessage($"[错误] {content}"); + } + + /// 显示操作确认提示 + public void ShowConfirmationPrompt(int actionCount) + { + // TODO: 显示 "AI 建议执行 N 个操作,是否确认?" 确认栏 + Debug.Log($"[AIChat] 等待用户确认 {actionCount} 个操作"); + } + + /// 清空聊天记录 + public void ClearHistory() + { + Messages.Clear(); + // TODO: 清空 UI 消息列表 + } + } +} diff --git a/client/Assets/Scripts/RedCircuit.AIAssistant/AICircuitAdvisor.cs b/client/Assets/Scripts/RedCircuit.AIAssistant/AICircuitAdvisor.cs new file mode 100644 index 0000000..3a411ad --- /dev/null +++ b/client/Assets/Scripts/RedCircuit.AIAssistant/AICircuitAdvisor.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace RedCircuit.AIAssistant +{ + /// + /// 电路分析与建议,解析 AI 返回的电路方案并展示诊断报告。 + /// + public class AICircuitAdvisor : MonoBehaviour + { + /// 显示电路分析报告 + public void ShowReport(CircuitAnalysisReport report) + { + if (report == null) return; + + Debug.Log($"[AIAdvisor] 电路分析: {report.Summary}"); + Debug.Log($"[AIAdvisor] 元件数: {report.Metrics.ComponentCount}, 成本: {report.Metrics.TotalCost}, 最大延迟: {report.Metrics.MaxDelay} tick"); + + foreach (var issue in report.Issues) + { + var icon = issue.Severity == IssueSeverity.Error ? "[ERROR]" : "[WARN]"; + Debug.Log($"[AIAdvisor] {icon} ({issue.Location.X},{issue.Location.Y}) {issue.Description}"); + if (!string.IsNullOrEmpty(issue.Suggestion)) + Debug.Log($"[AIAdvisor] -> 建议: {issue.Suggestion}"); + } + + foreach (var opt in report.Optimizations) + { + Debug.Log($"[AIAdvisor] [OPT] {opt.Description} ({opt.EstimatedImprovement})"); + } + + // TODO: 在 UI 上展示分析报告面板 + } + + /// 显示电路搭建方案供用户选择 + public void ShowCircuitPlan(List plans) + { + foreach (var plan in plans) + { + Debug.Log($"[AIAdvisor] 方案: {plan.Name} | 成本: {plan.Cost} | 延迟: {plan.Delay} tick | 元件: {plan.ComponentCount}"); + Debug.Log($"[AIAdvisor] {plan.Description}"); + } + // TODO: 在 UI 上展示方案对比卡片 + } + + /// 高亮显示问题位置 + public void HighlightIssue(GridPosition location, IssueSeverity severity) + { + // TODO: 在画布上高亮显示问题格子 + // Error -> 红色边框, Warning -> 黄色边框 + } + + /// 清除所有高亮 + public void ClearHighlights() + { + // TODO: 移除画布上的所有问题高亮 + } + } +} diff --git a/client/Assets/Scripts/RedCircuit.AIAssistant/AIContextProvider.cs b/client/Assets/Scripts/RedCircuit.AIAssistant/AIContextProvider.cs new file mode 100644 index 0000000..a8c0eeb --- /dev/null +++ b/client/Assets/Scripts/RedCircuit.AIAssistant/AIContextProvider.cs @@ -0,0 +1,83 @@ +using UnityEngine; + +namespace RedCircuit.AIAssistant +{ + /// + /// 上下文采集器,收集当前游戏状态供 AI 参考。 + /// + public class AIContextProvider : MonoBehaviour + { + /// 采集当前游戏上下文 + public GameContext CollectContext() + { + var context = new GameContext + { + Mode = GetCurrentMode(), + CanvasWidth = GetCanvasWidth(), + CanvasHeight = GetCanvasHeight(), + ComponentCount = GetComponentCount(), + SelectedComponentId = GetSelectedComponentId(), + SelectedComponentType = GetSelectedComponentType(), + LevelId = GetCurrentLevelId(), + LevelTitle = GetCurrentLevelTitle(), + RedstoneCoins = GetRedstoneCoins() + }; + + return context; + } + + private string GetCurrentMode() + { + // TODO: 从 GameManager 获取当前游戏模式 + return "creative"; // creative / puzzle / automation / multiplayer + } + + private int GetCanvasWidth() + { + // TODO: 从 GridCanvas 获取画布宽度 + return 128; + } + + private int GetCanvasHeight() + { + // TODO: 从 GridCanvas 获取画布高度 + return 128; + } + + private int GetComponentCount() + { + // TODO: 从 CircuitGraph 获取当前元件数 + return 0; + } + + private string GetSelectedComponentId() + { + // TODO: 从 PlacementController 获取选中元件 ID + return null; + } + + private string GetSelectedComponentType() + { + // TODO: 从 PlacementController 获取选中元件类型 + return null; + } + + private int? GetCurrentLevelId() + { + // TODO: 解谜模式下获取当前关卡 ID + return null; + } + + private string GetCurrentLevelTitle() + { + // TODO: 解谜模式下获取当前关卡标题 + return null; + } + + private long GetRedstoneCoins() + { + // TODO: 从 EconomyManager 获取红石币余额 + return 0; + } + } +} diff --git a/client/Assets/Scripts/RedCircuit.AIAssistant/AIModels.cs b/client/Assets/Scripts/RedCircuit.AIAssistant/AIModels.cs new file mode 100644 index 0000000..0f82211 --- /dev/null +++ b/client/Assets/Scripts/RedCircuit.AIAssistant/AIModels.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; + +namespace RedCircuit.AIAssistant +{ + // ==================== 消息模型 ==================== + + public enum MessageRole { User, Assistant, System } + + [Serializable] + public class ChatMessage + { + public MessageRole Role; + public string Content; + public DateTime Timestamp; + } + + // ==================== 响应模型 ==================== + + public enum ResponseType { Text, Analysis, Action, Error } + + [Serializable] + public class AIResponse + { + public ResponseType Type; + public string Content; + public List Actions = new(); + public bool RequiresConfirmation; + public CircuitAnalysisReport Analysis; + } + + // ==================== 操作指令模型 ==================== + + public enum ActionType { Place, Delete, Move, Rotate, Wire } + + [Serializable] + public class AIAction + { + public ActionType Type; + public string Component; // 元件类型 (Place 时使用) + public int X; // 目标 X 坐标 + public int Y; // 目标 Y 坐标 + public int FromX; // 源 X 坐标 (Move/Wire 时使用) + public int FromY; // 源 Y 坐标 (Move/Wire 时使用) + public int Rotation; // 旋转角度 (0/90/180/270) + public string Description; // 操作描述 (供用户确认时显示) + } + + // ==================== 上下文模型 ==================== + + [Serializable] + public class GameContext + { + public string Mode; // creative / puzzle / automation / multiplayer + public int CanvasWidth; + public int CanvasHeight; + public int ComponentCount; + public string SelectedComponentId; + public string SelectedComponentType; + public int? LevelId; + public string LevelTitle; + public long RedstoneCoins; + } + + // ==================== 电路分析模型 ==================== + + [Serializable] + public class CircuitAnalysisReport + { + public string Summary; + public CircuitMetrics Metrics; + public List Issues = new(); + public List Optimizations = new(); + } + + [Serializable] + public class CircuitMetrics + { + public int ComponentCount; + public int TotalCost; + public int MaxDelay; + public int SignalPaths; + } + + public enum IssueSeverity { Error, Warning, Info } + + [Serializable] + public class CircuitIssue + { + public IssueSeverity Severity; + public string Type; // signal_loss / short_circuit / redundant / etc. + public GridPosition Location; + public string Description; + public string Suggestion; + } + + [Serializable] + public class CircuitOptimization + { + public string Type; // delay_reduction / cost_reduction / etc. + public string Description; + public string EstimatedImprovement; + } + + // ==================== 电路方案模型 ==================== + + [Serializable] + public class CircuitPlan + { + public string Name; + public string Description; + public int Cost; + public int Delay; + public int ComponentCount; + public List Steps = new(); + } + + // ==================== 辅助类型 ==================== + + [Serializable] + public struct GridPosition + { + public int X; + public int Y; + + public GridPosition(int x, int y) { X = x; Y = y; } + } + + /// 操作批次 (用于撤销) + public class ActionBatch + { + public List Actions = new(); + public string SnapshotBefore; + public string SnapshotAfter; + } +} diff --git a/client/Assets/Scripts/RedCircuit.AIAssistant/RedCircuit.AIAssistant.asmdef b/client/Assets/Scripts/RedCircuit.AIAssistant/RedCircuit.AIAssistant.asmdef new file mode 100644 index 0000000..5b10ce9 --- /dev/null +++ b/client/Assets/Scripts/RedCircuit.AIAssistant/RedCircuit.AIAssistant.asmdef @@ -0,0 +1,10 @@ +{ + "name": "RedCircuit.AIAssistant", + "rootNamespace": "RedCircuit.AIAssistant", + "references": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "autoReferenced": true, + "defineConstraints": [] +} diff --git a/docs/AI助搭功能策划文档.md b/docs/AI助搭功能策划文档.md new file mode 100644 index 0000000..15767ba --- /dev/null +++ b/docs/AI助搭功能策划文档.md @@ -0,0 +1,292 @@ +# MRCC 游戏内 AI 助搭功能策划文档 + +> 版本: v1.0 | 日期: 2026-08-07 | 状态: 设计阶段 + +## 1. 功能概述 + +### 1.1 定义 + +**AI 助搭**是 MRCC 游戏内置的智能助手系统,具备三大核心能力: + +| 能力 | 描述 | 示例 | +|------|------|------| +| **问答解惑** | 解答电路原理、元件属性、游戏机制相关问题 | "红石中继器怎么用?" "AND 门真值表是什么?" | +| **助搭电路** | 根据需求描述,建议元件选型与放置方案 | "帮我搭一个二进制计数器" "这个电路怎么优化延迟?" | +| **操作游戏** | 经用户确认后,AI 直接执行放置/连线/删除等游戏操作 | "把红石灯放到 (10,5)" "连接拉杆和第一个中继器" | + +### 1.2 设计目标 + +- **零门槛上手**:新手无需查阅文档,直接用自然语言提问 +- **渐进式辅助**:从问答 -> 建议 -> 自动搭建,用户始终拥有最终决策权 +- **上下文感知**:AI 能感知当前画布状态、选中元件、游戏模式等上下文 +- **安全可控**:AI 的游戏操作必须经用户确认,支持撤销 + +### 1.3 用户场景 + +``` +场景 A:新手学习 + 用户:"红石信号会衰减吗?" + AI:"会。红石粉每传播 1 格衰减 1 点强度,满强度 15 最多传 15 格。 + 中继器可以恢复至满强度。需要我在画布上演示吗?" + 用户:"好的" + AI:[放置 电源 -> 15格红石粉 -> 红石灯,并标注信号强度] + +场景 B:电路设计辅助 + 用户:"帮我搭一个 XOR 门" + AI:"XOR 门可以用以下方案实现: + 方案1: 2x NOT + 1x AND + 1x OR (成本 550 红石币, 延迟 3 tick) + 方案2: 4x NAND (成本 880 红石币, 延迟 2 tick) + 推荐方案1,性价比更高。需要我自动放置吗?" + 用户:"用方案1" + AI:[在画布上放置元件并连线,用户可预览后确认] + +场景 C:电路分析与优化 + 用户:"分析一下我当前的电路" + AI:"当前电路分析结果: + - 元件数: 23, 总成本: 1,840 红石币 + - 信号路径: 电源 -> 中继器 x2 -> AND 门 -> 红石灯 + - 最大延迟: 4 tick (可优化) + - 问题: 第 7 格红石粉信号已衰减至 0,导致断路 + - 建议: 在第 5 格处添加中继器恢复信号" +``` + +## 2. 技术架构 + +### 2.1 整体架构 + +``` +┌─────────────────────────────────────────────────────┐ +│ Unity 客户端 │ +│ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │ +│ │ AIChatPanel │ │ AIAdvisor │ │ AIAction │ │ +│ │ (聊天 UI) │ │ (电路分析) │ │ Executor │ │ +│ │ │ │ │ │ (操作执行) │ │ +│ └──────┬───────┘ └──────┬───────┘ └─────┬─────┘ │ +│ │ │ │ │ +│ ┌──────┴─────────────────┴────────────────┴─────┐ │ +│ │ AIAssistantManager (核心协调器) │ │ +│ └──────────────────────┬─────────────────────────┘ │ +│ │ │ +│ ┌──────────────────────┴─────────────────────────┐ │ +│ │ AIContextProvider (上下文采集) │ │ +│ │ - 当前画布状态 - 选中元件 - 游戏模式 - 关卡信息│ │ +│ └──────────────────────────────────────────────────┘ │ +└─────────────────────────┬───────────────────────────┘ + │ HTTPS (REST + SSE) +┌─────────────────────────┴───────────────────────────┐ +│ ai-service (:8087) │ +│ ┌──────────┐ ┌───────────┐ ┌──────────────────┐ │ +│ │ Chat │ │ Circuit │ │ Action Planner │ │ +│ │ Handler │ │ Analyzer │ │ (操作规划器) │ │ +│ └────┬─────┘ └─────┬─────┘ └────────┬─────────┘ │ +│ │ │ │ │ +│ ┌────┴──────────────┴─────────────────┴──────────┐ │ +│ │ LLM Gateway (大模型网关) │ │ +│ │ - 意图识别 - 电路知识检索 - 操作序列生成 │ │ +│ └───────────────────────┬────────────────────────┘ │ +│ │ │ +│ ┌───────────────────────┴────────────────────────┐ │ +│ │ Knowledge Base (知识库) │ │ +│ │ - 80 种元件规格 - 电路设计模式 - 关卡攻略 │ │ +│ └────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────┘ +``` + +### 2.2 技术选型 + +| 领域 | 方案 | 说明 | +|------|------|------| +| LLM 引擎 | OpenAI GPT-4o / Claude 3.5 / 国产模型 | 支持多模型切换,按成本和延迟选择 | +| 检索增强 | RAG (元件手册 + 电路模式库) | 减少幻觉,确保元件数据准确 | +| 流式输出 | Server-Sent Events (SSE) | 打字机效果,降低用户等待感 | +| 操作规划 | Function Calling / Tool Use | LLM 输出结构化操作指令 | +| 上下文管理 | 滑动窗口 + 电路状态摘要 | 控制 Token 消耗 | +| 本地缓存 | 常见问题本地缓存 | 减少 API 调用,支持离线问答 | + +## 3. API 设计 + +### 3.1 REST API + +| 方法 | 路径 | 说明 | 鉴权 | +|------|------|------|------| +| POST | `/api/ai/chat` | 发送聊天消息,返回 AI 回复 | 是 | +| POST | `/api/ai/analyze` | 分析当前电路,返回诊断报告 | 是 | +| POST | `/api/ai/suggest` | 根据需求生成电路搭建方案 | 是 | +| POST | `/api/ai/execute` | 执行 AI 规划的操作序列 | 是 | +| GET | `/api/ai/history` | 获取聊天历史 | 是 | +| POST | `/api/ai/feedback` | 用户对 AI 回复反馈 (赞/踩) | 是 | + +### 3.2 核心数据结构 + +```json +// 聊天请求 +{ + "sessionId": "sess_abc123", + "message": "帮我搭一个 XOR 门", + "context": { + "mode": "creative", + "canvasSize": "128x128", + "componentCount": 0, + "selectedComponent": null, + "levelId": null + } +} + +// 聊天响应 (SSE 流式) +{ + "sessionId": "sess_abc123", + "type": "text|action|analysis|error", + "content": "XOR 门可以用以下方案实现...", + "actions": [ + { + "type": "place", + "component": "NOT_GATE", + "x": 10, "y": 20, + "rotation": 0, + "description": "放置 NOT 门 (输入反相器 1)" + }, + { + "type": "wire", + "from": {"x": 10, "y": 20}, + "to": {"x": 12, "y": 20}, + "description": "连接 NOT 门到 AND 门" + } + ], + "requiresConfirmation": true +} + +// 电路分析报告 +{ + "summary": "当前电路共 23 个元件,存在 1 处断路", + "metrics": { + "componentCount": 23, + "totalCost": 1840, + "maxDelay": 4, + "signalPaths": 2 + }, + "issues": [ + { + "severity": "error", + "type": "signal_loss", + "location": {"x": 7, "y": 5}, + "description": "信号在第 7 格衰减至 0,导致断路", + "suggestion": "在第 5 格处添加中继器恢复信号" + } + ], + "optimizations": [ + { + "type": "delay_reduction", + "description": "移除冗余中继器可减少 1 tick 延迟", + "estimatedImprovement": "-1 tick" + } + ] +} +``` + +## 4. 客户端模块设计 + +### 4.1 模块结构 + +| 脚本 | 职责 | +|------|------| +| `AIAssistantManager` | 核心协调器,管理 AI 会话生命周期,协调各子系统 | +| `AIChatController` | 聊天 UI 控制器,处理消息收发与流式显示 | +| `AICircuitAdvisor` | 电路分析与建议,解析 AI 返回的电路方案 | +| `AIActionExecutor` | 操作执行器,将 AI 操作指令转换为游戏内动作 | +| `AIContextProvider` | 上下文采集器,收集当前游戏状态供 AI 参考 | +| `AIModels` | 数据模型定义 (请求/响应/操作指令) | + +### 4.2 操作执行流程 + +``` +用户发送消息 + │ + ▼ +AIAssistantManager.HandleUserMessage() + │ + ├─► AIContextProvider.CollectContext() // 采集画布状态 + │ + ├─► ai-service POST /api/ai/chat // 发送到服务端 + │ │ + │ ▼ (SSE 流式响应) + │ 解析响应类型: + │ ├─ text → AIChatController.AppendText() + │ ├─ action → AIActionExecutor.QueueActions() + │ └─ analysis → AICircuitAdvisor.ShowReport() + │ + ├─► AIActionExecutor (如果有操作指令) + │ │ + │ ├─ 显示操作预览 (高亮待放置位置) + │ ├─ 用户确认 → 执行操作 (调用 PlacementController) + │ ├─ 用户拒绝 → 取消,记录反馈 + │ └─ 用户编辑 → 修改后执行 + │ + └─► AIChatController.UpdateChatHistory() +``` + +### 4.3 安全约束 + +- AI 操作**必须经用户确认**后才执行,不可自动执行 +- 每次操作最多放置 **20 个元件**,超出需分批确认 +- AI 不可操作**命令方块**和**结构方块**(仅创意模式手动放置) +- 解谜模式下,AI 仅提供文字提示,**不可直接放置元件** +- 所有 AI 操作支持**一键撤销** (记录操作前快照) + +## 5. 服务端设计 + +### 5.1 ai-service 微服务 + +| 属性 | 值 | +|------|-----| +| 端口 | 8087 | +| 语言 | Go 1.22 / Gin | +| 依赖 | LLM API、Redis (会话缓存)、PostgreSQL (历史记录) | + +### 5.2 LLM Gateway 设计 + +``` +用户消息 + 上下文 + │ + ▼ +┌──────────────┐ +│ 意图识别 │ → 问答 / 助搭 / 分析 / 操作 +└──────┬───────┘ + │ + ├─ 问答 → 检索知识库 → LLM 生成回复 + ├─ 助搭 → 生成电路方案 → LLM 验证可行性 + ├─ 分析 → 本地电路引擎分析 → LLM 生成建议 + └─ 操作 → LLM Function Calling → 生成操作序列 + │ + ▼ +┌──────────────┐ +│ 响应组装 │ → 统一格式输出 +└──────────────┘ +``` + +### 5.3 知识库构建 + +知识库包含以下结构化数据,供 RAG 检索: + +- **元件百科**:80 种元件的编号、属性、成本、使用说明 +- **电路模式库**:常见电路设计模式 (XOR 门、计数器、时钟发生器等) +- **关卡攻略库**:240 关的通关提示 (仅提示,不给完整答案) +- **红石原理**:信号衰减、延迟、BUD 更新等技术原理 + +## 6. 开发计划 + +| 阶段 | 内容 | 依赖 | +|------|------|------| +| Phase 1 | 基础聊天 + 元件问答 (接入 LLM API) | ai-service 骨架 | +| Phase 2 | 电路上下文采集 + 电路分析报告 | 客户端仿真引擎 | +| Phase 3 | AI 操作预览 + 确认执行 + 撤销 | 客户端编辑器 | +| Phase 4 | RAG 知识库 + 电路方案生成 | 全部元件数据 | +| Phase 5 | 解谜模式提示 (不操作) + 关卡攻略 | 关卡数据 | + +## 7. 成本估算 + +| 项目 | 预估 | +|------|------| +| LLM API 调用 | $0.01-0.05 / 次对话 (GPT-4o) | +| 月度 API 成本 (1000 DAU, 日均 5 次) | ~$1,500-7,500 | +| 缓存命中率目标 | >40% (常见问题本地缓存) | +| 本地模型备选 | Qwen2.5-7B (降低成本,需 GPU 服务器) | diff --git a/launcher/MRCC.Launcher.sln b/launcher/MRCC.Launcher.sln new file mode 100644 index 0000000..6bc6baf --- /dev/null +++ b/launcher/MRCC.Launcher.sln @@ -0,0 +1,33 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MRCC.Launcher", "MRCC.Launcher\MRCC.Launcher.csproj", "{72F4C810-435B-463C-B65E-56AE39577261}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCL.Core", "PCL-CE\PCL.Core\PCL.Core.csproj", "{A1294BFE-A9D2-44C0-87B8-7774AF7DB1D4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCL.Core.SourceGenerators", "PCL-CE\PCL.Core.SourceGenerators\PCL.Core.SourceGenerators.csproj", "{75040580-B94B-404E-BDA3-88242DE8D8EC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {72F4C810-435B-463C-B65E-56AE39577261}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72F4C810-435B-463C-B65E-56AE39577261}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72F4C810-435B-463C-B65E-56AE39577261}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72F4C810-435B-463C-B65E-56AE39577261}.Release|Any CPU.Build.0 = Release|Any CPU + {A1294BFE-A9D2-44C0-87B8-7774AF7DB1D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1294BFE-A9D2-44C0-87B8-7774AF7DB1D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1294BFE-A9D2-44C0-87B8-7774AF7DB1D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1294BFE-A9D2-44C0-87B8-7774AF7DB1D4}.Release|Any CPU.Build.0 = Release|Any CPU + {75040580-B94B-404E-BDA3-88242DE8D8EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75040580-B94B-404E-BDA3-88242DE8D8EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75040580-B94B-404E-BDA3-88242DE8D8EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75040580-B94B-404E-BDA3-88242DE8D8EC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/launcher/MRCC.Launcher/App.xaml b/launcher/MRCC.Launcher/App.xaml new file mode 100644 index 0000000..4e40b88 --- /dev/null +++ b/launcher/MRCC.Launcher/App.xaml @@ -0,0 +1,6 @@ + + + + diff --git a/launcher/MRCC.Launcher/App.xaml.cs b/launcher/MRCC.Launcher/App.xaml.cs new file mode 100644 index 0000000..b9a5d57 --- /dev/null +++ b/launcher/MRCC.Launcher/App.xaml.cs @@ -0,0 +1,7 @@ +using System.Windows; + +namespace MRCC.Launcher; + +public partial class App : Application +{ +} diff --git a/launcher/MRCC.Launcher/MRCC.Launcher.csproj b/launcher/MRCC.Launcher/MRCC.Launcher.csproj new file mode 100644 index 0000000..f7c8643 --- /dev/null +++ b/launcher/MRCC.Launcher/MRCC.Launcher.csproj @@ -0,0 +1,31 @@ + + + + WinExe + net10.0-windows + true + true + MRCC.Launcher + MRCC.Launcher + enable + 14.0 + enable + app.manifest + MRCC.Launcher.Program + + + + + + + + + + + + + PCL.metadata.json + + + + diff --git a/launcher/MRCC.Launcher/MainWindow.xaml b/launcher/MRCC.Launcher/MainWindow.xaml new file mode 100644 index 0000000..f0fc1a7 --- /dev/null +++ b/launcher/MRCC.Launcher/MainWindow.xaml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +