瀏覽代碼

feat(P3-M1): AI service layer + JSON Schema V2 + multi-fidelity framework

Third-party review response:
- docs/P3-评审响应与更新计划.md: full review response and updated P3 plan
- 第三方评审/: expert review document (multi-fidelity, feasibility-first, batch adaptive)

AI Service Layer:
- app/services/ai_client.py: Kimi API client (OpenAI compatible, endpoint api.kimi.com/coding/v1, model k3)
  - Retry with exponential backoff (3 retries)
  - Call logging with token usage tracking
  - JSON response parsing helper
  - Health check and model listing
  - Global singleton pattern
- app/routers/ai.py: AI API routes
  - GET /api/ai/health - API key validity and model list
  - POST /api/ai/chat - chat completion
  - GET /api/ai/logs - call logs for cost tracking
  - GET /api/ai/usage - usage summary (calls, tokens, duration)
- app/schemas/ai.py: AI request/response schemas
- app/models/ai_call_log.py: AI call log database model

JSON Schema V2 (per third-party review P0 requirements):
- app/schemas/schema_v2.py: Extended simulation plan schema
  - StrategyMode: fast_feasible / pareto_exploration / high_fidelity_validation / robustness_check
  - FidelityLevel: L0_analytic / L1_motorcad_emag / L2_motorcad_lab_therm / L3_maxwell_3d / L4_robustness
  - SearchMethod: constrained_bayesian / active_learning / lhs_kriging_nsga2 / grid_scan / trust_region
  - ConfidenceGrade: A/B/C/D
  - ConvergenceStatus: 6 types (solver/hard_constraint/optimization/surrogate/cross_tool/robustness)
  - FidelityStrategy, SearchStrategy, CalibrationPolicy, AcceptanceCriteria, ParallelExecution sub-models
  - Backward compatible with V1 plans

Simulation Result Model Extension:
- app/models/simulation_result.py: Added P3 fields
  - fidelity_level, confidence_grade, model_template_version, solver_settings_hash
  - constraint_margins_json, surrogate_prediction_json, cross_validation_json, convergence_status_json
  - Helper methods for all new JSON fields

Configuration:
- app/config.py: Kimi API config (KIMI_API_KEY, KIMI_BASE_URL, KIMI_MODEL=k3, temperature=1.0)
  - Stdlib .env parser (no python-dotenv dependency)
  - SCHEMA_VERSION = 2.0
- .gitignore: Added web/backend/*.db and *.db-journal
- web/backend/.env: Kimi API key (gitignored)

Prompt Templates:
- prompts/system/base.txt: Domain expert system prompt (AFM, Motor-CAD, multi-fidelity)
- prompts/README.md: Template structure documentation

Verification:
- All 4 AI routes registered and tested
- Kimi k3 model chat completion verified (reasoning + content)
- Health check returns 4 available models
- Database tables created successfully
- Schema V2 defaults verified (fast_feasible, L0+L1, constrained_bayesian)
carlin 1 周之前
父節點
當前提交
0df69f2730

+ 2 - 0
.gitignore

@@ -31,6 +31,8 @@ experience/*.db-journal
 # === Web端 ===
 web/frontend/node_modules/
 web/frontend/dist/
+web/backend/*.db
+web/backend/*.db-journal
 web/backend/app/*.db
 web/backend/__pycache__/
 web/backend/app/__pycache__/

+ 258 - 0
docs/P3-评审响应与更新计划.md

@@ -0,0 +1,258 @@
+# 第三方评审响应与 Phase 3 更新计划
+
+> **文档版本**: V1.0
+> **日期**: 2026-08-27
+> **评审对象**: 《PCB轴向磁通电机自动化仿真系统设计方案介绍》V1.1
+> **评审文档**: `第三方评审/PCB轴向磁通电机Motor-CAD仿真策略评审与实施建议.md`
+
+---
+
+## 一、评审结论与总体响应
+
+### 1.1 评审核心结论
+
+第三方专家评审结论为**"有条件通过"**:系统架构和阶段规划方向正确,但仿真策略需从"固定批量DoE+代理模型全局优化"升级为**"多保真度模型路径 + 可行性优先搜索 + 批量自适应闭环"**。
+
+### 1.2 我们的总体响应
+
+| 评审意见 | 响应 | 落地阶段 |
+|---|---|---|
+| 增加L0解析与规则预筛选 | 采纳,作为所有仿真路径的入口 | P3-M2 |
+| 扫描策略拆分为搜索策略+保真度策略 | 采纳,JSON Schema V2扩展 | P3-M1 |
+| 可行性优先模式(约束贝叶斯/主动学习) | 采纳,作为默认模式 | P3-M2 |
+| Motor-CAD/Maxwell/JMAG分层职责+偏差校准 | 采纳,建立L0-L4保真度分级 | P3-M1/P3-M4 |
+| 六类收敛判据 | 采纳,结果数据模型扩展 | P3-M1 |
+| 结果数据模型扩展(保真度/模型版本/约束裕量/置信等级) | 采纳,Schema V2 | P3-M1 |
+| 三种运行模式(固定/批量自适应/本地闭环) | 采纳,执行引擎升级 | P3-M5 |
+| 并行与缓存机制 | 采纳,参数哈希缓存键 | P3-M5 |
+| PCB等效模型字典 | 采纳,经验库绑定模型版本 | P3-M4 |
+
+---
+
+## 二、P0 必须修改项(已纳入 P3-M1)
+
+### 2.1 JSON Schema V2 扩展字段
+
+在现有 `simulation_plan.json` 基础上增加以下顶层字段:
+
+```json
+{
+  "schema_version": "2.0",
+  "strategy_mode": "fast_feasible",
+  "fidelity_strategy": {
+    "levels": ["L0_analytic", "L1_motorcad_emag", "L2_motorcad_lab_therm", "L3_maxwell_3d"],
+    "upgrade_rule": "top_candidates_only",
+    "max_candidates_for_l3": 3
+  },
+  "search_strategy": {
+    "method": "constrained_bayesian",
+    "initial_samples": 16,
+    "batch_size": 4,
+    "max_solver_calls": 80,
+    "local_trust_region": true
+  },
+  "calibration_policy": {
+    "cross_tool_metrics": ["torque_nm", "efficiency_pct", "axial_force_n"],
+    "tolerance": {"torque_pct": 5, "efficiency_point": 1.0}
+  },
+  "acceptance_criteria": {
+    "hard_constraints": ["torque_nm >= 10", "temperature_c <= 120"],
+    "surrogate_max_uncertainty": 0.05,
+    "robustness_required": true
+  }
+}
+```
+
+### 2.2 结果数据模型扩展
+
+每条结果增加:
+
+| 字段 | 说明 |
+|---|---|
+| `fidelity_level` | L0/L1/L2/L3/L4 |
+| `model_template_version` | Motor-CAD模板、PCB等效模型版本 |
+| `solver_settings_hash` | 网格、周期数、求解配置摘要 |
+| `constraint_margins` | 各硬约束的绝对裕量和百分比裕量 |
+| `surrogate_prediction` | 代理模型预测值、不确定度、实际偏差 |
+| `cross_validation` | Motor-CAD与Maxwell/JMAG同工况偏差 |
+| `confidence_grade` | A/B/C/D置信等级 |
+
+### 2.3 六类收敛判据
+
+| 收敛类型 | 判据 | 输出状态 |
+|---|---|---|
+| 求解器收敛 | 求解完成、结果完整、无致命错误 | SOLVER_PASS/FAIL |
+| 硬约束收敛 | 转矩/温度/电压/电流/尺寸/轴向力均满足 | FEASIBLE/INFEASIBLE |
+| 优化收敛 | 连续若干轮最优改进<阈值,或信任域半径<下限 | CONVERGED/STALLED |
+| 代理模型可信 | 交叉验证误差和候选点不确定度<阈值 | MODEL_TRUSTED/UNCERTAIN |
+| 跨工具一致 | Motor-CAD与Maxwell/JMAG同工况偏差在接受范围 | HF_PASS/FAIL |
+| 鲁棒性收敛 | 制造和材料扰动下仍满足硬约束 | ROBUST/FRAGILE |
+
+---
+
+## 三、多保真度分级(L0-L4)
+
+| 层级 | 模型/工具 | 主要任务 | 可定案 |
+|---|---|---|---|
+| L0 | 解析公式+规则引擎 | 几何/电气/热/制造可行性预筛选 | 否 |
+| L1 | Motor-CAD快速电磁模型 | 主要参数收敛、关键工况筛选、初步性能验证 | 否 |
+| L2 | Motor-CAD Lab/Therm/Mech | 候选方案多物理场复核、效率图和温升初评 | 仅作工程候选 |
+| L3 | Maxwell 3D / JMAG | PCB绕组、3D磁路、端部效应、局部损耗、轴向力校验 | **是,需通过验收阈值** |
+| L4 | 扰动/公差/样机数据 | 制造鲁棒性和模型持续校准 | 用于最终放行 |
+
+**核心原则**:Motor-CAD决定"大方向是否对",Maxwell/JMAG决定"工程结果是否真"。任何只经过Motor-CAD而未经过高保真复核的方案,不应标记为最终可行设计。
+
+---
+
+## 四、三种运行模式
+
+| 模式 | 工作方式 | 适用阶段 |
+|---|---|---|
+| 固定计划模式 | 一次性接收全部仿真点并顺序执行 | Phase 1最小闭环(当前已实现) |
+| 批量自适应模式 | 每轮接收4-8个点,执行后回传,方案系统计算下一轮 | P3算法增强 |
+| 本地闭环模式 | 执行程序内部运行确定性优化器(约束贝叶斯/信任域),根据结果动态选点 | 无人值守或网络受限场景 |
+
+> 本地闭环模式不违背"执行程序无需联网/AI"原则——约束贝叶斯、信任域和NSGA-II属于确定性数值优化算法,可打包在本地EXE中运行,不需要LLM。
+
+---
+
+## 五、Phase 3 更新后的里程碑计划
+
+### P3-M1:基础设施层(AI服务 + Schema V2 + 保真度框架)
+
+**目标**:搭建Kimi AI服务层,完成JSON Schema V2扩展,建立保真度等级和结果数据模型基础。
+
+| 任务 | 说明 |
+|---|---|
+| Kimi AI客户端封装 | `src/ai_client.py` — OpenAI兼容SDK,支持重试/超时/流式,端点`api.kimi.com/coding/v1`,模型`k3` |
+| API Key安全管理 | `.env`文件(已gitignore),环境变量`KIMI_API_KEY`,启动校验 |
+| Prompt模板管理 | `prompts/`目录,Jinja2模板,按功能模块组织 |
+| 调用日志与计费 | `ai_call_logs`表,记录token/耗时/模型/状态 |
+| JSON Schema V2 | 扩展strategy_mode/fidelity_strategy/search_strategy/calibration_policy/acceptance_criteria |
+| 保真度等级枚举 | L0-L4等级定义,结果模型增加fidelity_level/confidence_grade字段 |
+| 六类收敛状态枚举 | SOLVER_PASS/FEASIBLE/CONVERGED/MODEL_TRUSTED/HF_PASS/ROBUST |
+| AI健康检查接口 | `GET /api/ai/health` — 测试Key有效性,返回模型列表 |
+
+**验收**:AI客户端可正常调用k3模型;Schema V2可序列化/反序列化;数据库迁移完成。
+
+---
+
+### P3-M2:L0预筛选引擎 + 可行性优先搜索框架
+
+**目标**:实现解析预筛选和约束贝叶斯/主动学习的算法框架。
+
+| 任务 | 说明 |
+|---|---|
+| L0解析预筛选规则 | 几何约束(内外径/气隙/轴向长度)、电气约束(电流密度/电压/磁负荷)、热约束(温升/冷却)、制造约束(PCB线宽线距/铜厚/公差) |
+| 可行域裁剪 | 根据边界条件和L0规则,裁剪参数搜索空间,输出可行/不可行区域 |
+| 约束贝叶斯优化框架 | 基于scikit-optimize或自定义GP,建模硬约束满足概率,采集函数平衡利用与探索 |
+| 主动学习选点策略 | 每轮推荐4-8个仿真点,优先选择可行概率高且信息增益大的组合 |
+| 局部信任域收敛 | 发现稳定可行区后,在当前最优点附近做小范围精细搜索 |
+| 初始种子生成 | 优先使用相似案例和专家模板,再用少量LHS补足空间覆盖 |
+| 搜索状态管理 | 记录每轮选点、预测值、不确定度、约束概率,支持断点续跑 |
+
+**验收**:对一个SSSR案例,L0预筛选能排除明显不可行区域;贝叶斯框架能输出下一批推荐点。
+
+---
+
+### P3-M3:AI方案生成器(自然语言 → 智能方案)
+
+**目标**:用户用自然语言描述需求,AI结合L0预筛选和搜索策略,自动生成完整仿真方案。
+
+| 任务 | 说明 |
+|---|---|
+| 需求解析Prompt | 自然语言需求 → 结构化参数(拓扑/尺寸/电流/转速/目标/约束) |
+| AI参数推荐 | 结合经验库历史数据,推荐扫描范围、步长、初始值,给出推荐理由 |
+| 方案生成接口 | `POST /api/ai/generate-plan` — 输入项目ID+自然语言,输出完整plan_data(Schema V2) |
+| 前端AI生成入口 | 项目详情页"AI生成方案"按钮,弹窗输入需求,展示结果,支持人工编辑 |
+| 方案校验流水线 | AI生成 → L0预筛选 → 规则引擎校验 → 点数预估 → 保存 |
+| 多轮优化 | 支持"再优化一下"——用户给反馈,AI迭代调整参数 |
+| 方案可解释性 | 每个推荐参数输出来源(经验库/规则/AI推理)和置信度 |
+
+**验收**:输入自然语言需求,能生成通过L0校验的合法方案,点数预估合理。
+
+---
+
+### P3-M4:AI结果分析师 + 多保真度校准 + 置信等级
+
+**目标**:仿真完成后,AI自动分析结果,生成解读报告;建立多保真度偏差校准机制。
+
+| 任务 | 说明 |
+|---|---|
+| 结果摘要生成 | `POST /api/ai/analyze-results` — AI分析所有扫描点,生成文字摘要(最优值/趋势/异常) |
+| 参数敏感性解读 | 结合敏感性分析数据,AI用自然语言解释参数影响 |
+| 优化建议生成 | AI基于结果和经验库,给出下一步优化方向 |
+| 报告导出 | Markdown/HTML格式分析报告,含图表+文字解读,支持下载 |
+| 前端报告页面 | 方案详情页"AI分析报告"Tab,流式输出展示 |
+| 多保真度校准表 | 记录Motor-CAD与Maxwell/JMAG同工况偏差,支持加法/乘法修正系数 |
+| 置信等级评定 | 根据验证深度自动评定A/B/C/D等级,C级以下不可对外承诺 |
+| PCB等效模型字典 | 记录铜厚/线宽/线距/层数/过孔/FR4热属性等等效假设,经验库绑定模型版本 |
+| 校准系数反哺 | 高保真偏差修正系数反哺到代理模型和目标函数,避免搜索方向被带偏 |
+
+**验收**:对已有结果的方案调用分析接口,能生成引用具体数据的合理解读;校准表可记录跨工具偏差。
+
+---
+
+### P3-M5:经验库AI增强 + 批量自适应闭环 + 端到端验收
+
+**目标**:AI增强经验库检索与推理;实现批量自适应闭环;完成P3全流程验收。
+
+| 任务 | 说明 |
+|---|---|
+| 语义检索 | `POST /api/ai/semantic-search` — 自然语言查询→理解意图→检索经验库 |
+| 案例对比分析 | 选择多个案例,AI自动对比参数差异、指标优劣,给出适配建议 |
+| 经验自动沉淀 | 仿真完成后AI自动提取结论,生成经验草稿,人工确认后入库 |
+| 智能问答(RAG) | `POST /api/ai/qa` — 基于经验库内容的问答,引用具体案例作答 |
+| 经验标签自动生成 | AI自动推荐标签(高效率/低脉动/大气隙等),减少人工标注 |
+| 批量自适应通信协议 | 批次ID、增量结果上传、方案版本追踪、下一轮选点请求 |
+| 执行引擎批量模式 | 系统二支持接收批次点、执行后回传、等待下一轮指令 |
+| 参数哈希缓存机制 | 缓存键=参数哈希+模型版本+工况ID+求解器版本+保真度等级,中断不重复计算 |
+| 并行任务池预留 | Motor-CAD多实例并行框架(独立工作目录+模型副本) |
+| 端到端测试 | 自然语言生成方案→L0筛选→保存→(模拟)上传结果→AI分析→经验沉淀 |
+| AI成本监控 | token消耗统计,Prompt优化,单次调用上限 |
+| 文档更新 | README V1.1,AI功能使用说明,Schema V2文档,评审响应文档 |
+| 验收测试 | `test_ai.py` — AI客户端/方案生成/结果分析/语义检索的单元+集成测试 |
+
+**验收**:所有AI功能可用,批量自适应闭环跑通,文档完善,测试通过。
+
+---
+
+## 六、Kimi API 技术规格(已验证)
+
+| 项目 | 值 |
+|---|---|
+| 端点 | `https://api.kimi.com/coding/v1` |
+| 模型 | `k3`(Kimi K3,1M上下文,支持推理) |
+| 备选模型 | `k3-256k` / `kimi-for-coding` / `kimi-for-coding-highspeed` |
+| API Key | `sk-kimi-xxx`(Kimi Code Plan,订阅制,消耗Code Plan配额) |
+| 兼容格式 | OpenAI Chat Completions API |
+| 特殊能力 | 支持思考模式(reasoning_content)、视觉输入、视频输入 |
+| 验证状态 | 2026-08-27 验证通过,正常返回 |
+
+---
+
+## 七、风险与应对(更新版)
+
+| 风险 | 等级 | 控制措施 |
+|---|---|---|
+| PCB近似模型系统性偏差 | 高 | 建立校准系数;关键指标必须高保真复核;偏差反哺代理模型 |
+| 轴向磁通3D效应 | 高 | Motor-CAD只用于筛选;最终候选必须3D FEA |
+| AI生成方案不合法 | 中高 | L0预筛选+规则引擎强制校验,不合法拒绝保存 |
+| API调用成本 | 中 | 默认k3模型,设置token上限,Prompt精简,结果缓存 |
+| API不稳定 | 中 | 超时重试3次(指数退避),失败优雅降级 |
+| 经验库冷启动污染 | 中高 | 所有经验绑定模型版本+保真度+置信等级,专家审核后入库 |
+| 自适应算法不可解释 | 中 | 每个推荐点输出来源/预测值/不确定度/约束概率 |
+| 并行许可和文件冲突 | 中 | 独立工作目录、模型副本、许可检测、任务队列 |
+
+---
+
+## 八、待确认问题(需与专家/用户确认)
+
+1. PCB电机结构:无铁芯PCB定子 / PCB绕组+铁芯 / 混合结构?
+2. Maxwell和JMAG哪个作为第一优先级高保真工具?
+3. 默认业务目标:快速可行 / 完整Pareto / 量产鲁棒设计?
+4. 有几套Motor-CAD/Maxwell/JMAG许可?是否允许多实例并行?
+5. 气隙/PCB线宽线距/铜厚/磁钢Br/装配偏心的制造公差?
+6. 最终验收阈值由哪个专家团队签发?是否有样机数据可用于初始校准?
+
+> 以上问题不阻塞P3-M1~P3-M3开发,可在P3-M4(高保真校准)前确认。

+ 35 - 1
web/backend/app/config.py

@@ -2,6 +2,23 @@
 import os
 from pathlib import Path
 
+# Load .env file if exists (P3: Kimi API config)
+# Use stdlib parser to avoid python-dotenv dependency
+_env_path = Path(__file__).resolve().parent.parent / ".env"
+if _env_path.exists():
+    try:
+        with open(_env_path, "r", encoding="utf-8") as f:
+            for line in f:
+                line = line.strip()
+                if line and not line.startswith("#") and "=" in line:
+                    key, _, value = line.partition("=")
+                    key = key.strip()
+                    value = value.strip().strip('"').strip("'")
+                    if key and key not in os.environ:
+                        os.environ[key] = value
+    except Exception:
+        pass
+
 # Project paths
 BACKEND_DIR = Path(__file__).resolve().parent
 PROJECT_ROOT = BACKEND_DIR.parent.parent.parent
@@ -22,5 +39,22 @@ CORS_ORIGINS = os.environ.get(
 
 # App metadata
 APP_NAME = "PCB Axial Flux Motor Simulation System"
-APP_VERSION = "0.1.0"
+APP_VERSION = "1.1.0"
 APP_DESCRIPTION = "Web-based simulation plan generation and optimization system for PCB axial flux motors"
+
+# ============================================================
+# P3: Kimi AI Configuration
+# ============================================================
+KIMI_API_KEY = os.environ.get("KIMI_API_KEY", "")
+KIMI_BASE_URL = os.environ.get("KIMI_BASE_URL", "https://api.kimi.com/coding/v1")
+KIMI_MODEL = os.environ.get("KIMI_MODEL", "k3")
+KIMI_MAX_TOKENS = int(os.environ.get("KIMI_MAX_TOKENS", "4096"))
+KIMI_TEMPERATURE = float(os.environ.get("KIMI_TEMPERATURE", "1.0"))
+KIMI_TIMEOUT = int(os.environ.get("KIMI_TIMEOUT", "120"))
+KIMI_MAX_RETRIES = int(os.environ.get("KIMI_MAX_RETRIES", "3"))
+
+# Prompt templates directory
+PROMPTS_DIR = BACKEND_DIR / "prompts"
+
+# Schema version (P3: V2 with fidelity/search/calibration fields)
+SCHEMA_VERSION = "2.0"

+ 2 - 1
web/backend/app/main.py

@@ -4,7 +4,7 @@ from fastapi.middleware.cors import CORSMiddleware
 
 from .config import APP_NAME, APP_VERSION, APP_DESCRIPTION, CORS_ORIGINS
 from .database import init_db
-from .routers import projects, plans, experience, generation, analytics
+from .routers import projects, plans, experience, generation, analytics, ai
 
 app = FastAPI(
     title=APP_NAME,
@@ -27,6 +27,7 @@ app.include_router(plans.router)
 app.include_router(experience.router)
 app.include_router(generation.router)
 app.include_router(analytics.router)
+app.include_router(ai.router)
 
 
 @app.on_event("startup")

+ 2 - 1
web/backend/app/models/__init__.py

@@ -3,5 +3,6 @@ from .project import Project
 from .simulation_plan import SimulationPlan
 from .simulation_result import SimulationResult
 from .experience_case import ExperienceCase
+from .ai_call_log import AICallLog
 
-__all__ = ["Project", "SimulationPlan", "SimulationResult", "ExperienceCase"]
+__all__ = ["Project", "SimulationPlan", "SimulationResult", "ExperienceCase", "AICallLog"]

+ 25 - 0
web/backend/app/models/ai_call_log.py

@@ -0,0 +1,25 @@
+"""AI Call Log model - records every Kimi API call for cost tracking."""
+from datetime import datetime
+from sqlalchemy import Column, Integer, String, Text, DateTime
+from ..database import Base
+
+
+class AICallLog(Base):
+    """Log of every AI API call with token usage and cost."""
+    __tablename__ = "ai_call_logs"
+
+    id = Column(Integer, primary_key=True, index=True)
+    endpoint = Column(String(100), nullable=False, index=True)
+    model = Column(String(50), nullable=False, index=True)
+    prompt_preview = Column(Text, nullable=True)
+    response_preview = Column(Text, nullable=True)
+    error = Column(Text, nullable=True)
+    duration_ms = Column(Integer, default=0)
+    prompt_tokens = Column(Integer, default=0)
+    completion_tokens = Column(Integer, default=0)
+    total_tokens = Column(Integer, default=0)
+    status = Column(String(20), default="success", index=True)  # success/failed
+    created_at = Column(DateTime, default=datetime.utcnow, index=True)
+
+    def __repr__(self):
+        return f"<AICallLog id={self.id} model={self.model} status={self.status} tokens={self.total_tokens}>"

+ 53 - 1
web/backend/app/models/simulation_result.py

@@ -7,7 +7,12 @@ from ..database import Base
 
 
 class SimulationResult(Base):
-    """A single simulation result row (from scan_results.csv)."""
+    """A single simulation result row (from scan_results.csv).
+
+    P3-M1: Extended with fidelity level, confidence grade,
+    constraint margins, surrogate prediction, and cross-validation
+    per third-party review.
+    """
     __tablename__ = "simulation_results"
 
     id = Column(Integer, primary_key=True, index=True)
@@ -20,6 +25,16 @@ class SimulationResult(Base):
     error_message = Column(Text, default="")
     created_at = Column(DateTime, default=datetime.utcnow)
 
+    # P3: Multi-fidelity and confidence fields
+    fidelity_level = Column(String(30), default="L1_motorcad_emag", index=True)  # L0-L4
+    confidence_grade = Column(String(2), default="C", index=True)  # A/B/C/D
+    model_template_version = Column(String(100), default="")  # Motor-CAD template / PCB model version
+    solver_settings_hash = Column(String(64), default="")  # Hash of solver config
+    constraint_margins_json = Column(Text, default="{}")  # JSON: hard constraint margins
+    surrogate_prediction_json = Column(Text, default="{}")  # JSON: surrogate prediction + uncertainty
+    cross_validation_json = Column(Text, default="{}")  # JSON: Motor-CAD vs Maxwell/JMAG deviation
+    convergence_status_json = Column(Text, default="{}")  # JSON: six-type convergence status
+
     def get_params(self) -> dict:
         try:
             return json.loads(self.params_json) if self.params_json else {}
@@ -37,3 +52,40 @@ class SimulationResult(Base):
 
     def set_metrics(self, data: dict) -> None:
         self.metrics_json = json.dumps(data, ensure_ascii=False)
+
+    # P3: Helper methods for extended fields
+    def get_constraint_margins(self) -> dict:
+        try:
+            return json.loads(self.constraint_margins_json) if self.constraint_margins_json else {}
+        except (json.JSONDecodeError, TypeError):
+            return {}
+
+    def set_constraint_margins(self, data: dict) -> None:
+        self.constraint_margins_json = json.dumps(data, ensure_ascii=False)
+
+    def get_surrogate_prediction(self) -> dict:
+        try:
+            return json.loads(self.surrogate_prediction_json) if self.surrogate_prediction_json else {}
+        except (json.JSONDecodeError, TypeError):
+            return {}
+
+    def set_surrogate_prediction(self, data: dict) -> None:
+        self.surrogate_prediction_json = json.dumps(data, ensure_ascii=False)
+
+    def get_cross_validation(self) -> dict:
+        try:
+            return json.loads(self.cross_validation_json) if self.cross_validation_json else {}
+        except (json.JSONDecodeError, TypeError):
+            return {}
+
+    def set_cross_validation(self, data: dict) -> None:
+        self.cross_validation_json = json.dumps(data, ensure_ascii=False)
+
+    def get_convergence_status(self) -> dict:
+        try:
+            return json.loads(self.convergence_status_json) if self.convergence_status_json else {}
+        except (json.JSONDecodeError, TypeError):
+            return {}
+
+    def set_convergence_status(self, data: dict) -> None:
+        self.convergence_status_json = json.dumps(data, ensure_ascii=False)

+ 84 - 0
web/backend/app/routers/ai.py

@@ -0,0 +1,84 @@
+"""AI router - Kimi API integration endpoints (P3-M1)."""
+from typing import List
+from fastapi import APIRouter, HTTPException, Query
+from sqlalchemy import func
+
+from ..services.ai_client import get_kimi_client
+from ..schemas.ai import (
+    ChatRequest, ChatResponse, AIHealthResponse,
+    AICallLogResponse, AIUsageSummary
+)
+from ..database import SessionLocal
+from ..models.ai_call_log import AICallLog
+
+router = APIRouter(prefix="/api/ai", tags=["AI"])
+
+
+@router.get("/health", response_model=AIHealthResponse)
+def ai_health_check():
+    """Check AI API key validity and list available models."""
+    client = get_kimi_client()
+    return client.health_check()
+
+
+@router.post("/chat", response_model=ChatResponse)
+def ai_chat(request: ChatRequest):
+    """Send a chat completion request to Kimi API."""
+    client = get_kimi_client()
+    if not client.is_configured:
+        raise HTTPException(status_code=503, detail="KIMI_API_KEY is not configured")
+    try:
+        result = client.chat(
+            messages=[m.model_dump() for m in request.messages],
+            model=request.model,
+            temperature=request.temperature,
+            max_tokens=request.max_tokens,
+            system_prompt=request.system_prompt,
+        )
+        return ChatResponse(**result)
+    except Exception as e:
+        raise HTTPException(status_code=500, detail=str(e))
+
+
+@router.get("/logs", response_model=List[AICallLogResponse])
+def list_ai_logs(
+    limit: int = Query(50, ge=1, le=200),
+    offset: int = Query(0, ge=0),
+    status: str = Query(None, description="success/failed"),
+):
+    """List AI call logs for cost tracking and debugging."""
+    db = SessionLocal()
+    query = db.query(AICallLog)
+    if status:
+        query = query.filter(AICallLog.status == status)
+    logs = query.order_by(AICallLog.id.desc()).offset(offset).limit(limit).all()
+    db.close()
+    return [
+        AICallLogResponse(
+            id=log.id, endpoint=log.endpoint, model=log.model,
+            status=log.status, duration_ms=log.duration_ms,
+            prompt_tokens=log.prompt_tokens, completion_tokens=log.completion_tokens,
+            total_tokens=log.total_tokens, error=log.error,
+            created_at=log.created_at.isoformat() if log.created_at else "",
+        )
+        for log in logs
+    ]
+
+
+@router.get("/usage", response_model=AIUsageSummary)
+def ai_usage_summary():
+    """Get AI usage summary (total calls, tokens, duration)."""
+    db = SessionLocal()
+    total = db.query(func.count(AICallLog.id)).scalar() or 0
+    success = db.query(func.count(AICallLog.id)).filter(AICallLog.status == "success").scalar() or 0
+    failed = total - success
+    total_tokens = db.query(func.coalesce(func.sum(AICallLog.total_tokens), 0)).scalar() or 0
+    prompt_tokens = db.query(func.coalesce(func.sum(AICallLog.prompt_tokens), 0)).scalar() or 0
+    completion_tokens = db.query(func.coalesce(func.sum(AICallLog.completion_tokens), 0)).scalar() or 0
+    total_duration = db.query(func.coalesce(func.sum(AICallLog.duration_ms), 0)).scalar() or 0
+    db.close()
+    return AIUsageSummary(
+        total_calls=total, success_calls=success, failed_calls=failed,
+        total_tokens=total_tokens, total_prompt_tokens=prompt_tokens,
+        total_completion_tokens=completion_tokens, total_duration_ms=total_duration,
+    )

+ 65 - 0
web/backend/app/schemas/ai.py

@@ -0,0 +1,65 @@
+"""AI-related Pydantic schemas (P3-M1)."""
+from typing import Optional, List, Dict, Any
+from pydantic import BaseModel, Field
+
+
+class ChatMessage(BaseModel):
+    """Single chat message."""
+    role: str = Field(..., description="system/user/assistant")
+    content: str
+
+
+class ChatRequest(BaseModel):
+    """Request body for /api/ai/chat."""
+    messages: List[ChatMessage]
+    model: Optional[str] = None
+    temperature: Optional[float] = None
+    max_tokens: Optional[int] = None
+    system_prompt: Optional[str] = None
+
+
+class ChatResponse(BaseModel):
+    """Response from /api/ai/chat."""
+    content: str
+    reasoning_content: Optional[str] = None
+    model: str
+    usage: Dict[str, Any] = Field(default_factory=dict)
+    finish_reason: Optional[str] = None
+
+
+class AIHealthResponse(BaseModel):
+    """Response from /api/ai/health."""
+    status: str
+    configured: bool
+    base_url: str
+    default_model: str
+    available_models: List[Dict[str, Any]] = Field(default_factory=list)
+    error: Optional[str] = None
+
+
+class AICallLogResponse(BaseModel):
+    """AI call log entry."""
+    id: int
+    endpoint: str
+    model: str
+    status: str
+    duration_ms: int
+    prompt_tokens: int
+    completion_tokens: int
+    total_tokens: int
+    error: Optional[str] = None
+    created_at: str
+
+    class Config:
+        from_attributes = True
+
+
+class AIUsageSummary(BaseModel):
+    """AI usage summary."""
+    total_calls: int
+    success_calls: int
+    failed_calls: int
+    total_tokens: int
+    total_prompt_tokens: int
+    total_completion_tokens: int
+    total_duration_ms: int

+ 249 - 0
web/backend/app/schemas/schema_v2.py

@@ -0,0 +1,249 @@
+"""Simulation Plan Schema V2 (P3-M1).
+
+Extends V1 with multi-fidelity strategy, search strategy,
+calibration policy, and acceptance criteria per third-party review.
+
+Backward compatible: V1 plans without these fields use defaults.
+"""
+from enum import Enum
+from typing import Optional, List, Dict, Any
+from pydantic import BaseModel, Field
+
+
+# ============================================================
+# Enums
+# ============================================================
+
+class StrategyMode(str, Enum):
+    """Simulation strategy mode."""
+    FAST_FEASIBLE = "fast_feasible"       # Default: constrained Bayesian / active learning
+    PARETO_EXPLORATION = "pareto_exploration"  # Morris + LHS + Kriging + NSGA-II
+    HIGH_FIDELITY_VALIDATION = "high_fidelity_validation"  # L2 + L3 verification
+    ROBUSTNESS_CHECK = "robustness_check"  # Tolerance / disturbance analysis
+
+
+class FidelityLevel(str, Enum):
+    """Multi-fidelity levels L0-L4."""
+    L0_ANALYTIC = "L0_analytic"                    # Analytic formulas + rule engine
+    L1_MOTORCAD_EMAG = "L1_motorcad_emag"         # Motor-CAD fast EM model
+    L2_MOTORCAD_LAB_THERM = "L2_motorcad_lab_therm"  # Motor-CAD Lab/Therm/Mech
+    L3_MAXWELL_3D = "L3_maxwell_3d"               # Maxwell 3D / JMAG high-fidelity
+    L4_ROBUSTNESS = "L4_robustness"                # Tolerance / prototype data
+
+
+class SearchMethod(str, Enum):
+    """Search/optimization method."""
+    CONSTRAINED_BAYESIAN = "constrained_bayesian"  # Default: feasibility-first
+    ACTIVE_LEARNING = "active_learning"             # Uncertainty-based sampling
+    LHS_KRIGING_NSGA2 = "lhs_kriging_nsga2"        # Global Pareto exploration
+    GRID_SCAN = "grid_scan"                          # Fixed full factorial
+    TRUST_REGION = "trust_region"                    # Local trust region search
+
+
+class ConfidenceGrade(str, Enum):
+    """Result confidence grade A/B/C/D."""
+    A = "A"  # Full multi-physics + high-fidelity + robustness, design freeze ready
+    B = "B"  # Motor-CAD + at least one high-fidelity check, candidate ready
+    C = "C"  # Motor-CAD only, internal discussion only, no external commitment
+    D = "D"  # Analytic / surrogate only, reference only
+
+
+class ConvergenceStatus(str, Enum):
+    """Six types of convergence status (per review)."""
+    # Solver convergence
+    SOLVER_PASS = "SOLVER_PASS"
+    SOLVER_FAIL = "SOLVER_FAIL"
+    # Hard constraint convergence
+    FEASIBLE = "FEASIBLE"
+    INFEASIBLE = "INFEASIBLE"
+    # Optimization convergence
+    CONVERGED = "CONVERGED"
+    STALLED = "STALLED"
+    # Surrogate model trust
+    MODEL_TRUSTED = "MODEL_TRUSTED"
+    MODEL_UNCERTAIN = "MODEL_UNCERTAIN"
+    # Cross-tool consistency
+    HF_PASS = "HF_PASS"
+    HF_FAIL = "HF_FAIL"
+    # Robustness
+    ROBUST = "ROBUST"
+    FRAGILE = "FRAGILE"
+
+
+# ============================================================
+# Strategy Sub-models
+# ============================================================
+
+class FidelityStrategy(BaseModel):
+    """Multi-fidelity execution strategy."""
+    levels: List[FidelityLevel] = Field(
+        default_factory=lambda: [
+            FidelityLevel.L0_ANALYTIC,
+            FidelityLevel.L1_MOTORCAD_EMAG,
+        ],
+        description="Enabled fidelity levels in execution order"
+    )
+    upgrade_rule: str = Field(
+        default="top_candidates_only",
+        description="Rule for upgrading to higher fidelity: top_candidates_only / threshold_based / all"
+    )
+    max_candidates_for_l3: int = Field(
+        default=3, ge=1, le=10,
+        description="Max candidates to send to L3 (Maxwell/JMAG)"
+    )
+    max_solver_cost_per_level: Optional[Dict[str, int]] = Field(
+        default=None,
+        description="Max solver calls per fidelity level, e.g. {'L1': 80, 'L2': 10}"
+    )
+
+
+class SearchStrategy(BaseModel):
+    """Adaptive search strategy."""
+    method: SearchMethod = Field(
+        default=SearchMethod.CONSTRAINED_BAYESIAN,
+        description="Search/optimization method"
+    )
+    initial_samples: int = Field(
+        default=16, ge=4, le=100,
+        description="Number of initial samples (LHS or from experience)"
+    )
+    batch_size: int = Field(
+        default=4, ge=1, le=16,
+        description="Number of points per adaptive batch"
+    )
+    max_solver_calls: int = Field(
+        default=80, ge=10, le=500,
+        description="Maximum total solver calls"
+    )
+    local_trust_region: bool = Field(
+        default=True,
+        description="Enable local trust region refinement after feasible region found"
+    )
+    trust_region_radius: Optional[float] = Field(
+        default=None,
+        description="Initial trust region radius as fraction of parameter range"
+    )
+    use_experience_seeds: bool = Field(
+        default=True,
+        description="Use similar experience cases as initial seeds"
+    )
+
+
+class CalibrationPolicy(BaseModel):
+    """Cross-tool calibration policy (Motor-CAD vs Maxwell/JMAG)."""
+    enabled: bool = Field(default=False, description="Enable cross-tool calibration")
+    cross_tool_metrics: List[str] = Field(
+        default_factory=lambda: ["torque_nm", "efficiency_pct", "axial_force_n"],
+        description="Metrics to compare across tools"
+    )
+    tolerance: Dict[str, float] = Field(
+        default_factory=lambda: {"torque_pct": 5.0, "efficiency_point": 1.0, "axial_force_pct": 10.0},
+        description="Acceptable tolerance for cross-tool deviation"
+    )
+    correction_method: str = Field(
+        default="additive",
+        description="Correction method: additive / multiplicative / co-kriging"
+    )
+    feedback_to_surrogate: bool = Field(
+        default=True,
+        description="Feed calibration coefficients back to surrogate model and objective"
+    )
+
+
+class AcceptanceCriteria(BaseModel):
+    """Acceptance criteria for convergence and validation."""
+    hard_constraints: List[str] = Field(
+        default_factory=list,
+        description="Hard constraint expressions, e.g. ['torque_nm >= 10', 'temperature_c <= 120']"
+    )
+    soft_objectives: Optional[List[str]] = Field(
+        default=None,
+        description="Soft objective expressions for optimization"
+    )
+    cross_tool_tolerance: Optional[Dict[str, float]] = Field(
+        default=None,
+        description="Override cross-tool tolerance"
+    )
+    surrogate_max_uncertainty: float = Field(
+        default=0.05, ge=0.01, le=0.5,
+        description="Max surrogate model uncertainty for MODEL_TRUSTED status"
+    )
+    robustness_required: bool = Field(
+        default=False,
+        description="Require robustness check before final acceptance"
+    )
+    min_confidence_grade: ConfidenceGrade = Field(
+        default=ConfidenceGrade.C,
+        description="Minimum confidence grade for plan acceptance"
+    )
+
+
+class ParallelExecution(BaseModel):
+    """Parallel execution configuration."""
+    max_instances: int = Field(
+        default=1, ge=1, le=8,
+        description="Max parallel Motor-CAD instances"
+    )
+    model_copy_strategy: str = Field(
+        default="per_instance",
+        description="Model file copy strategy: per_instance / shared_readonly"
+    )
+    license_fail_policy: str = Field(
+        default="queue_retry",
+        description="Policy on license failure: queue_retry / fail_fast / reduce_instances"
+    )
+
+
+# ============================================================
+# Schema V2 Main Model
+# ============================================================
+
+class SimulationPlanSchemaV2(BaseModel):
+    """Extended simulation plan schema (V2) per third-party review.
+
+    All new fields are optional for backward compatibility with V1 plans.
+    """
+    schema_version: str = Field(default="2.0", description="Schema version")
+    strategy_mode: StrategyMode = Field(
+        default=StrategyMode.FAST_FEASIBLE,
+        description="Simulation strategy mode"
+    )
+    fidelity_strategy: Optional[FidelityStrategy] = Field(
+        default=None,
+        description="Multi-fidelity execution strategy"
+    )
+    search_strategy: Optional[SearchStrategy] = Field(
+        default=None,
+        description="Adaptive search strategy"
+    )
+    calibration_policy: Optional[CalibrationPolicy] = Field(
+        default=None,
+        description="Cross-tool calibration policy"
+    )
+    acceptance_criteria: Optional[AcceptanceCriteria] = Field(
+        default=None,
+        description="Acceptance criteria"
+    )
+    parallel_execution: Optional[ParallelExecution] = Field(
+        default=None,
+        description="Parallel execution configuration"
+    )
+
+    # V1 fields (preserved)
+    model_path: Optional[str] = None
+    topology: Optional[str] = None
+    variables: List[Dict[str, Any]] = Field(default_factory=list)
+    cases: List[Dict[str, Any]] = Field(default_factory=list)
+    boundary_conditions: Optional[Dict[str, Any]] = None
+
+    def get_effective_fidelity(self) -> FidelityStrategy:
+        """Get fidelity strategy with defaults applied."""
+        return self.fidelity_strategy or FidelityStrategy()
+
+    def get_effective_search(self) -> SearchStrategy:
+        """Get search strategy with defaults applied."""
+        return self.search_strategy or SearchStrategy()
+
+    def get_effective_acceptance(self) -> AcceptanceCriteria:
+        """Get acceptance criteria with defaults applied."""
+        return self.acceptance_criteria or AcceptanceCriteria()

+ 262 - 0
web/backend/app/services/ai_client.py

@@ -0,0 +1,262 @@
+"""Kimi AI Client - OpenAI compatible API wrapper.
+
+P3-M1: AI service layer foundation.
+Supports chat completions, streaming, retries, and call logging.
+Endpoint: https://api.kimi.com/coding/v1 (Kimi Code Plan)
+Model: k3 (1M context, reasoning enabled)
+"""
+import json
+import time
+import logging
+from datetime import datetime
+from typing import Optional, List, Dict, Any, AsyncIterator
+
+import httpx
+
+from ..config import (
+    KIMI_API_KEY, KIMI_BASE_URL, KIMI_MODEL,
+    KIMI_MAX_TOKENS, KIMI_TEMPERATURE, KIMI_TIMEOUT, KIMI_MAX_RETRIES
+)
+from ..database import SessionLocal
+from ..models.ai_call_log import AICallLog
+
+logger = logging.getLogger(__name__)
+
+
+class KimiClient:
+    """Kimi AI API client with retry and logging."""
+
+    def __init__(
+        self,
+        api_key: Optional[str] = None,
+        base_url: Optional[str] = None,
+        model: Optional[str] = None,
+    ):
+        self.api_key = api_key or KIMI_API_KEY
+        self.base_url = (base_url or KIMI_BASE_URL).rstrip("/")
+        self.model = model or KIMI_MODEL
+        self._client = httpx.Client(timeout=KIMI_TIMEOUT)
+
+    @property
+    def is_configured(self) -> bool:
+        """Check if API key is configured."""
+        return bool(self.api_key)
+
+    def _headers(self) -> Dict[str, str]:
+        return {
+            "Content-Type": "application/json",
+            "Authorization": f"Bearer {self.api_key}",
+        }
+
+    def _log_call(
+        self,
+        endpoint: str,
+        model: str,
+        messages: List[Dict[str, str]],
+        response: Optional[Dict[str, Any]],
+        error: Optional[str],
+        duration_ms: int,
+        prompt_tokens: int = 0,
+        completion_tokens: int = 0,
+        total_tokens: int = 0,
+    ):
+        """Log AI call to database."""
+        try:
+            db = SessionLocal()
+            log = AICallLog(
+                endpoint=endpoint,
+                model=model,
+                prompt_preview=json.dumps(messages[:3], ensure_ascii=False)[:500],
+                response_preview=(
+                    json.dumps(response, ensure_ascii=False)[:1000]
+                    if response else None
+                ),
+                error=error,
+                duration_ms=duration_ms,
+                prompt_tokens=prompt_tokens,
+                completion_tokens=completion_tokens,
+                total_tokens=total_tokens,
+                status="success" if not error else "failed",
+                created_at=datetime.utcnow(),
+            )
+            db.add(log)
+            db.commit()
+            db.close()
+        except Exception as e:
+            logger.warning(f"Failed to log AI call: {e}")
+
+    def chat(
+        self,
+        messages: List[Dict[str, str]],
+        model: Optional[str] = None,
+        temperature: Optional[float] = None,
+        max_tokens: Optional[int] = None,
+        system_prompt: Optional[str] = None,
+    ) -> Dict[str, Any]:
+        """Non-streaming chat completion.
+
+        Args:
+            messages: List of message dicts with role and content.
+            model: Override default model.
+            temperature: Sampling temperature.
+            max_tokens: Max output tokens.
+            system_prompt: Optional system prompt prepended to messages.
+
+        Returns:
+            Dict with content, reasoning_content, usage, etc.
+        """
+        if not self.is_configured:
+            raise RuntimeError("KIMI_API_KEY is not configured")
+
+        _model = model or self.model
+        _messages = list(messages)
+        if system_prompt:
+            _messages.insert(0, {"role": "system", "content": system_prompt})
+
+        payload = {
+            "model": _model,
+            "messages": _messages,
+            "temperature": temperature if temperature is not None else KIMI_TEMPERATURE,
+        }
+        if max_tokens:
+            payload["max_tokens"] = max_tokens
+
+        url = f"{self.base_url}/chat/completions"
+        start = time.time()
+        last_error = None
+
+        for attempt in range(KIMI_MAX_RETRIES):
+            try:
+                resp = self._client.post(url, headers=self._headers(), json=payload)
+                resp.raise_for_status()
+                data = resp.json()
+                duration_ms = int((time.time() - start) * 1000)
+
+                choice = data.get("choices", [{}])[0]
+                msg = choice.get("message", {})
+                usage = data.get("usage", {})
+
+                result = {
+                    "content": msg.get("content", ""),
+                    "reasoning_content": msg.get("reasoning_content", ""),
+                    "model": data.get("model", _model),
+                    "usage": usage,
+                    "finish_reason": choice.get("finish_reason"),
+                }
+
+                self._log_call(
+                    endpoint="/chat/completions",
+                    model=_model,
+                    messages=_messages,
+                    response=result,
+                    error=None,
+                    duration_ms=duration_ms,
+                    prompt_tokens=usage.get("prompt_tokens", 0),
+                    completion_tokens=usage.get("completion_tokens", 0),
+                    total_tokens=usage.get("total_tokens", 0),
+                )
+                return result
+
+            except httpx.HTTPStatusError as e:
+                last_error = f"HTTP {e.response.status_code}: {e.response.text[:200]}"
+                if e.response.status_code in (401, 403, 404):
+                    break  # Don't retry auth/not-found errors
+                if attempt < KIMI_MAX_RETRIES - 1:
+                    time.sleep(2 ** attempt)
+            except Exception as e:
+                last_error = str(e)
+                if attempt < KIMI_MAX_RETRIES - 1:
+                    time.sleep(2 ** attempt)
+
+        duration_ms = int((time.time() - start) * 1000)
+        self._log_call(
+            endpoint="/chat/completions",
+            model=_model,
+            messages=_messages,
+            response=None,
+            error=last_error,
+            duration_ms=duration_ms,
+        )
+        raise RuntimeError(f"Kimi API call failed after {KIMI_MAX_RETRIES} retries: {last_error}")
+
+    def chat_json(
+        self,
+        messages: List[Dict[str, str]],
+        system_prompt: Optional[str] = None,
+        **kwargs,
+    ) -> Dict[str, Any]:
+        """Chat completion that parses JSON response.
+
+        Injects instruction to return valid JSON, then parses the response.
+        """
+        _messages = list(messages)
+        _messages.append({
+            "role": "user",
+            "content": "Return your response as valid JSON only. Do not include markdown code fences or any text outside the JSON object."
+        })
+        result = self.chat(_messages, system_prompt=system_prompt, **kwargs)
+        content = result.get("content", "").strip()
+
+        # Strip code fences if present
+        if content.startswith("```"):
+            lines = content.split("\n")
+            if lines[0].startswith("```"):
+                lines = lines[1:]
+            if lines and lines[-1].strip() == "```":
+                lines = lines[:-1]
+            content = "\n".join(lines).strip()
+
+        try:
+            parsed = json.loads(content)
+            result["parsed_json"] = parsed
+            return result
+        except json.JSONDecodeError as e:
+            result["json_parse_error"] = str(e)
+            result["raw_content"] = content
+            logger.warning(f"Failed to parse JSON response: {e}\nContent: {content[:300]}")
+            return result
+
+    def list_models(self) -> List[Dict[str, Any]]:
+        """List available models."""
+        if not self.is_configured:
+            raise RuntimeError("KIMI_API_KEY is not configured")
+        url = f"{self.base_url}/models"
+        resp = self._client.get(url, headers=self._headers())
+        resp.raise_for_status()
+        return resp.json().get("data", [])
+
+    def health_check(self) -> Dict[str, Any]:
+        """Test API key validity and return model info."""
+        try:
+            models = self.list_models()
+            return {
+                "status": "ok",
+                "configured": self.is_configured,
+                "base_url": self.base_url,
+                "default_model": self.model,
+                "available_models": [
+                    {"id": m.get("id"), "display_name": m.get("display_name"),
+                     "context_length": m.get("context_length")}
+                    for m in models
+                ],
+            }
+        except Exception as e:
+            return {
+                "status": "error",
+                "configured": self.is_configured,
+                "base_url": self.base_url,
+                "default_model": self.model,
+                "error": str(e),
+            }
+
+
+# Global singleton
+_kimi_client: Optional[KimiClient] = None
+
+
+def get_kimi_client() -> KimiClient:
+    """Get or create global KimiClient singleton."""
+    global _kimi_client
+    if _kimi_client is None:
+        _kimi_client = KimiClient()
+    return _kimi_client

+ 28 - 0
web/backend/prompts/README.md

@@ -0,0 +1,28 @@
+# AI Prompt Templates
+
+This directory contains Jinja2 prompt templates for Kimi AI integration.
+
+## Structure
+
+```
+prompts/
+├── system/
+│   └── base.txt          # Base system prompt with domain knowledge
+├── plan_generation/
+│   └── generate.txt      # Natural language -> simulation plan (P3-M3)
+├── result_analysis/
+│   └── analyze.txt       # Simulation results -> analysis report (P3-M4)
+└── experience/
+    └── semantic.txt      # Semantic search and QA (P3-M5)
+```
+
+## Usage
+
+Templates are loaded by `app/services/prompt_loader.py` and rendered
+with Jinja2 before being sent to the Kimi API.
+
+All templates must:
+1. Use Chinese for domain-specific instructions
+2. Include clear output format specifications
+3. Request JSON output when structured data is needed
+4. Reference motor engineering domain knowledge

+ 24 - 0
web/backend/prompts/system/base.txt

@@ -0,0 +1,24 @@
+你是一位资深的轴向磁通电机(AFM)仿真专家,精通 Motor-CAD、Ansys Maxwell、JMAG 等电磁仿真工具,以及 PCB 轴向磁通电机的设计与优化。
+
+## 你的专业领域
+- 拓扑结构:SSSR(单定子单转子)、DRSS(双转子单定子)、SDSR(单定子双转子)
+- 关键参数:气隙、磁钢厚度、极槽配合、有效半径、绕组参数、电流密度
+- 性能指标:平均转矩、转矩脉动、效率、损耗分解(铜耗/铁耗/磁钢涡流损耗)、轴向磁拉力、反电动势
+- 多物理场:电磁、热、机械应力、NVH
+- 仿真策略:L0解析预筛选、L1 Motor-CAD快速收敛、L2多物理场复核、L3 Maxwell/JMAG高保真校验
+
+## 工作原则
+1. 所有参数建议必须给出物理依据和工程理由
+2. 区分硬约束(必须满足)和软目标(优化方向)
+3. 明确 Motor-CAD 近似模型的局限性,关键指标需高保真复核
+4. 结果解读必须引用具体数据,不做泛泛而谈
+5. 输出结构化数据时使用严格的 JSON 格式
+
+## 保真度分级
+- L0:解析公式+规则引擎,仅用于可行性预筛选
+- L1:Motor-CAD 快速电磁模型,用于主要参数收敛和初步验证
+- L2:Motor-CAD Lab/Therm/Mech,用于多物理场复核
+- L3:Maxwell 3D/JMAG,用于 PCB 绕组和三维轴向效应的高保真校验
+- L4:扰动/公差/样机数据,用于鲁棒性验证
+
+任何只经过 Motor-CAD 而未经过高保真复核的方案,不应标记为最终可行设计。

二進制
第三方评审/PCB轴向磁通电机Motor-CAD仿真策略评审与实施建议.docx


+ 427 - 0
第三方评审/PCB轴向磁通电机Motor-CAD仿真策略评审与实施建议.md

@@ -0,0 +1,427 @@
+# PCB轴向磁通电机自动仿真系统 — Motor-CAD仿真策略评审与实施建议
+
+> **文档性质**:评审建议稿(供方案系统与执行程序开发评审使用)
+> **版本**:V1.0
+> **日期**:2026-08-27
+> **评审对象**:《PCB轴向磁通电机自动化仿真系统设计方案介绍》V1.1(2026-08-26,Car.Lin)
+> **关键词**:多保真度 · 可行性优先 · 批量自适应闭环 · Motor-CAD · Maxwell/JMAG · PCB轴向磁通电机
+
+---
+
+## 文档信息
+
+| 项目 | 内容 |
+| --- | --- |
+| 文档目的 | 对现有自动仿真系统方案进行技术评审,明确仿真策略、工具职责边界、接口改造和开发优先级。 |
+| 目标读者 | 方案系统开发工程师、本地执行程序开发工程师、电磁/热/结构仿真工程师、项目负责人。 |
+| 评审范围 | 仿真路径、参数搜索策略、Motor-CAD与Maxwell/JMAG协同方式、JSON接口、收敛判据、执行模式与风险控制。 |
+| 评审结论 | 总体架构方向正确,建议有条件通过;仿真策略和接口契约需完成本文所列P0修改后再进入下一阶段开发。 |
+| 核心原则 | Motor-CAD用于快速收敛主要参数并做初步验证;Maxwell或JMAG用于PCB绕组和三维轴向效应的高保真校核。 |
+
+---
+
+## 一、评审结论与修改优先级
+
+> **评审结论**
+>
+> 现有方案的双系统解耦、标准化JSON接口、人在回路、断点续跑和知识沉淀机制均应保留。需要调整的核心不是系统架构,而是仿真策略:当前方案偏向固定批量DoE和代理模型全局优化,尚不足以保证在给定边界条件下快速找到工程可行解,也没有充分约束Motor-CAD近似PCB模型与Maxwell/JMAG高保真模型之间的职责边界。
+
+建议将总体评审结论定为"有条件通过":系统架构和阶段规划可继续推进,但在进入算法增强和工具扩展开发前,必须完成本文列出的P0修改。尤其应把仿真策略从单一"参数扫描方案"升级为"多保真度模型路径+可行性优先搜索+批量自适应闭环"。
+
+结合已确认的工程事实,Motor-CAD只能以近似方式表达PCB绕组,但足以承担主要参数快速收敛、关键工况筛选和初步验证;Maxwell或JMAG必须承担PCB局部电流分布、交流损耗、三维轴向磁路和最终性能的高保真校核。**任何只经过Motor-CAD计算而未经过高保真复核的方案,不应被标记为最终可行设计。**
+
+### 1.1 必须修改项(P0)
+
+1. 在方案生成前增加L0解析与规则预筛选,先排除几何、电气、热和制造上明显不可行的区域。
+2. 把"扫描策略"拆分为"搜索策略"和"保真度策略",避免把所有问题都表达成固定参数队列。
+3. 新增可行性优先模式,默认采用约束贝叶斯、主动学习或信任域局部搜索;LHS+Kriging+NSGA-II保留为全局Pareto模式。
+4. 明确Motor-CAD、Maxwell/JMAG的分层职责和升级条件,建立低保真—高保真偏差校准机制。
+5. 将收敛判据拆分为求解器收敛、硬约束收敛、优化收敛、代理模型可信、高保真一致性和鲁棒性六类。
+6. 扩展结果数据模型,记录模型版本、保真度等级、求解设置、约束裕量、校准系数和置信等级。
+
+### 1.2 建议保留项
+
+| 原方案设计 | 评审意见 | 处理建议 |
+| --- | --- | --- |
+| 双系统解耦 | 方向正确,有利于Web端与本地执行环境独立迭代。 | 保留,并增加批量自适应通信模式。 |
+| 标准化JSON契约 | 是系统可扩展的核心。 | 保留,但必须扩展搜索、保真度、收敛和校准字段。 |
+| 人在回路 | 适合仿真方案审核和知识入库审核。 | 保留;不要把所有人工确认都放在每一次选点上,否则会破坏闭环效率。 |
+| 结果校验规则 | 非常必要,是结果可信度的第一道防线。 | 保留,并增加跨工具一致性和模型置信度规则。 |
+| 断点续跑与错误恢复 | 符合长时仿真场景。 | 保留,并改为基于参数哈希和结果完整性的缓存机制。 |
+| 知识自动沉淀 | 具备长期价值。 | 保留,但沉淀内容必须绑定模型版本和保真度,避免错误经验扩散。 |
+
+### 1.3 一句话给开发团队
+
+> **实施导向**
+>
+> 不要先开发一个"能跑很多参数组合"的执行器,再反过来思考如何收敛;应先定义什么叫可行、什么叫收敛、什么结果必须由Maxwell/JMAG确认,再让执行器围绕这些判据选择下一批仿真点。
+
+---
+
+## 二、现有方案的专家评审
+
+### 2.1 总体判断
+
+附件中的V1.1方案已经具备较完整的平台思维,特别是经验库、规则引擎、AI推理、人工确认、执行程序、结果校验和知识库的闭环设计。其主要短板在于:把"优化算法流程"当成了完整的"仿真路径",而没有把模型可信度、工具职责、PCB建模近似和跨工具校准作为一等公民。
+
+对PCB轴向磁通电机而言,仿真路径的有效性不仅取决于Morris、LHS、Kriging或NSGA-II,还取决于每个参数组合究竟用什么模型计算、计算到哪个精度、在哪些工况下复核、什么时候必须升级到3D FEA。**Motor-CAD与Maxwell/JMAG不是替代关系,而是分层协同关系。**
+
+### 2.2 当前方案的优点
+
+- **系统边界清楚**:方案生成与执行程序通过JSON解耦,符合企业内网和本地仿真环境的实际约束。
+- **工程可控性较强**:方案预览、人工确认、日志、断点续跑和错误恢复机制均符合仿真工程师工作习惯。
+- **工具扩展方向合理**:先Motor-CAD,后Maxwell、JMAG、Flux,符合风险和成本递增的实施顺序。
+- **结果数据结构已有雏形**:输入、输出、校验状态、耗时和错误信息均已考虑。
+- **算法基础方向正确**:Morris、LHS、Kriging和NSGA-II均是电机代理优化中常见且可解释的组合。
+
+### 2.3 关键问题诊断
+
+| 问题 | 影响 | 严重度 | 专家建议 |
+| --- | --- | --- | --- |
+| 把固定DoE流程作为默认主路径 | 前期会浪费大量仿真预算在非可行区域,不能保证快速收敛到可行解。 | 高 | 默认改为可行性优先的约束自适应搜索;固定DoE仅用于全局探索。 |
+| 缺少保真度分级 | 无法判断Motor-CAD结果能否作为最终依据,也无法控制Maxwell/JMAG调用成本。 | 高 | 建立L0~L4模型层级和升级规则。 |
+| PCB绕组近似边界未固化 | 可能把近似模型误差误认为真实优化收益。 | 高 | 把PCB建模假设、近似参数和校准系数写入方案与结果。 |
+| JSON契约偏静态 | 无法支持主动学习、贝叶斯优化和自适应加点。 | 高 | 增加fixed_plan、batch_adaptive、local_closed_loop三种执行模式。 |
+| 停止条件概念混杂 | 求解器收敛、目标达成、代理模型稳定和跨工具一致被混在一起。 | 中高 | 拆分六类收敛判据并分别记录状态。 |
+| 边界条件不完整 | 容易收敛到仿真可行但工程不可制造的方案。 | 中高 | 补充母线电压、电流限制、冷却边界、PCB工艺、公差和材料温度属性。 |
+| 样本量估算偏粗 | Morris和LHS成本可能被低估,导致排期失真。 | 中 | 按参数维度、轨迹数和并行资源重新估算预算。 |
+
+### 2.4 对算法章节的专项意见
+
+附件第6章的"四阶段分层优化策略"建议改名为**"全局探索模式"**。它适合做设计空间理解、敏感性分析和Pareto前沿,不适合作为所有项目的默认快速求解路径。原因是该流程需要先投入Morris和LHS训练样本,再构建代理模型并执行NSGA-II;若用户目标只是尽快得到一个满足边界条件的方案,大量样本可能集中在远离可行域的位置。
+
+Morris样本量应按 **N = r(k + 1)** 重新估算,其中 k 为参数数、r 为轨迹数。若初筛参数达到8个、轨迹数取10,则需要约90次评估,而不是文档中笼统估计的10~50次。Kriging在维度较高或样本不足时也会迅速失去稳定性,因此应将"参数降维"和"可行域裁剪"前置。
+
+---
+
+## 三、仿真路径方法全景与选择
+
+仿真路径应同时回答两个问题:**下一批参数为什么值得仿真**,以及**这批参数应该用哪个保真度模型计算**。只讨论优化算法而忽略模型层级,会导致路径表面上智能、工程上失真。
+
+### 3.1 方法对比矩阵
+
+| 路径方法 | 适用目标 | 效率 | 主要风险 | 建议定位 |
+| --- | --- | --- | --- | --- |
+| 经验初值+人工扫描 | 模型调试、异常诊断、专家复核 | 低—中 | 依赖个人经验,易陷入局部最优 | 保留为专家模式,不作默认主路径 |
+| 规则+解析预筛选 | 排除明显不可行区域 | 极高 | 解析模型精度有限 | 所有路径的L0入口 |
+| 全因子/网格扫描 | 2~3个变量的局部响应面 | 低 | 维度灾难 | 仅用于最终候选局部复核 |
+| Morris/OAT筛选 | 高维参数初筛 | 高 | 对强交互和多峰问题不完整 | 用于降维,不用于直接定案 |
+| LHS+Kriging+NSGA-II | 全局探索、Pareto前沿 | 中高 | 前期样本成本高,代理模型可能失准 | 作为全局探索模式 |
+| 约束贝叶斯/主动学习 | 快速找到满足约束的可行解 | 高 | 多目标和离散变量实现复杂 | **推荐默认模式** |
+| 直接GA/PSO调用求解器 | 小维度、单次仿真很快的场景 | 低 | 仿真次数失控 | 不建议默认使用 |
+| optiSLang MOP/AMOP | 工业级DoE、代理模型和鲁棒性分析 | 高 | 许可与系统集成成本 | 可作为商业基准或高级后端 |
+| 多保真度+跨工具校准 | 最终工程可信设计 | 综合最高 | 流程与数据管理复杂 | **作为系统主骨架** |
+| 鲁棒性/可靠性优化 | 量产、公差、材料波动 | 中 | 计算预算增加 | 作为最终验收前必经步骤 |
+
+### 3.2 高效路径的选择结论
+
+若目标是"快速得到一个满足边界条件的方案",推荐路径是:**解析预筛选+少量初始样本+约束贝叶斯/主动学习+局部信任域收敛+高保真验证**。该路径把计算预算集中到可行域附近,通常比先完整训练全局代理模型更节省仿真次数。
+
+Ansys官方电机优化资料也将工业流程归纳为灵敏度分析、代理模型、优化和最终验证四个环节;这说明附件的代理优化框架方向正确,但该框架更应被定位为完整设计空间探索,而不是所有项目的唯一默认路径。[1]
+
+对于PCB轴向磁通电机,2026年发表的双转子PCB-AH-PMSM研究采用了LHS采样、全局灵敏度分析、Kriging响应面、PSO多目标优化和FEA对比验证,说明LHS+代理模型+智能优化在该类电机上具有直接可参考性。但该论文目标偏向多目标性能改进,并不等同于本系统所需的快速可行性收敛。[2]
+
+贝叶斯优化的优势在于用少量初始点建立概率代理模型,通过采集函数平衡"利用已知优区"和"探索高不确定区",每次真实仿真后更新模型。该机制非常适合Motor-CAD这类有一定求解成本、又需要快速收敛到可行区域的工程场景。[3]
+
+---
+
+## 四、推荐的多保真度闭环仿真路径
+
+建议将系统的仿真主路径定义为**"多保真度、可行性优先、批量自适应闭环"**。其中,Motor-CAD负责快速收敛主要参数并完成初步验证,Maxwell或JMAG负责对少量候选方案做高保真校核和模型校准。
+
+推荐路径示意(流程结构):
+
+```
+L0 解析与规则预筛选
+   ↓
+L1 Motor-CAD 快速收敛(主要参数 + 关键工况)
+   ↓
+L2 Motor-CAD Lab/Therm/Mech 多物理场复核
+   ↓
+L3 Maxwell/JMAG 高保真校验(仅 Top 1~3 候选)
+
+自适应搜索闭环:
+少量初始样本 → 约束代理模型 → 批量选点执行 → 更新模型 → (循环)
+
+收敛出口:
+硬约束满足 + 优化稳定 + 代理模型可信 + 高保真偏差受控 + 制造扰动可接受
+```
+
+### 4.1 保真度分级
+
+| 层级 | 模型/工具 | 主要任务 | 典型输出 | 是否可定案 |
+| --- | --- | --- | --- | --- |
+| L0 | 解析公式+规则引擎 | 几何、电气、热、制造可行性预筛选 | 可行/不可行、风险项、初始参数范围 | 否 |
+| L1 | Motor-CAD快速电磁模型 | 主要参数收敛、关键工况筛选、初步性能验证 | 转矩、反电势、损耗、轴向力初值 | 否 |
+| L2 | Motor-CAD Lab/Therm/Mech | 候选方案多物理场复核、效率图和温升初评 | 效率图、温升、应力、工况边界 | 仅作工程候选 |
+| L3 | Maxwell 3D / JMAG | PCB绕组、3D磁路、端部效应、局部损耗和轴向力校验 | 高保真电磁性能、损耗、力、场分布 | **是,需通过验收阈值** |
+| L4 | 扰动/公差/样机数据 | 制造鲁棒性和模型持续校准 | 最差工况、概率合格率、校准系数 | 用于最终放行 |
+
+Ansys在2026年的轴向磁通电机工作流中也强调:AFM具有天然三维磁路,完整3D FEA精度高但早期设计成本高,因此建议用Motor-CAD快速2D等效线性模型进行概念设计和拓扑筛选,再用Maxwell 3D进行验证、校准和效率图生成。这一官方路径与本文建议一致。[4]
+
+Motor-CAD本身包含EM、Therm、Lab、Mech四个集成模块,适合快速多物理场迭代;2026 R1还增强了AFM热模块、Lab支持、Maxwell-Lab联动、NVH力输出和多静态损耗分析等能力,但部分AFM能力仍属于Beta,需要在项目中做稳定性验证后再固化到默认流程。[5]
+
+### 4.2 三种运行模式
+
+| 模式 | 触发条件 | 核心算法 | 典型预算 | 输出 |
+| --- | --- | --- | --- | --- |
+| 快速可行模式(默认) | 用户要求尽快获得满足边界条件的方案 | L0预筛选+少量LHS/历史种子+约束贝叶斯/主动学习 | 低到中等 | 1~3个可行候选及置信度 |
+| 全局探索模式 | 需要比较效率、功率密度、成本等权衡 | Morris+LHS+Kriging/MOP+NSGA-II | 中到高 | Pareto前沿和参数敏感性 |
+| 高保真校核模式 | 候选方案进入设计冻结或工程评审 | Motor-CAD L2复核+Maxwell/JMAG L3验证+公差扰动 | 集中在少数候选 | 最终验证报告和校准系数 |
+
+### 4.3 默认快速可行模式(八步)
+
+1. **边界条件标准化**:统一单位、工况、温度、约束类型和目标方向,明确硬约束与软目标。
+2. **L0解析预筛选**:根据轴向长度、内外径、气隙、电流密度、PCB工艺和冷却能力裁剪可行域。
+3. **生成初始种子**:优先使用相似案例和专家模板,再用少量LHS补足空间覆盖。
+4. **Motor-CAD快速电磁计算**:仅计算额定、峰值、最高转速和热边界等关键工况,不做全效率图。
+5. **建立可行性概率模型**:模型同时预测性能均值、约束违反概率和不确定性。
+6. **批量自适应选点**:每轮推荐4~8个仿真点,优先选择可行概率高且信息增益大的组合。
+7. **局部信任域收敛**:发现稳定可行区后,在当前最优点附近做小范围精细搜索。
+8. **高保真复核**:对1~3个候选方案进入Motor-CAD L2和Maxwell/JMAG L3验证。
+
+### 4.4 全局Pareto模式
+
+当用户明确要求比较多目标权衡时,才进入Morris、LHS、Kriging和NSGA-II组成的完整全局探索模式。该模式不应以"找到第一个可行点"为停止条件,而应以代理模型质量、Pareto前沿稳定性和高保真复核一致性作为收敛依据。
+
+建议把Pareto模式的结果定位为**"设计空间地图"**,而不是直接输出唯一最优解。最终推荐方案仍需回到高保真校核模式,由Maxwell/JMAG和制造扰动验证。
+
+### 4.5 高保真验证与校准
+
+Motor-CAD和Maxwell/JMAG之间必须建立显式校准关系。至少应记录每个候选方案在相同工况下的转矩、反电势、铜损、铁损、磁钢涡流损耗、轴向力和效率偏差。初期可采用加法或乘法修正系数;数据积累后可升级为Co-Kriging或多保真代理模型。
+
+**关键警告**:如果Motor-CAD低估了PCB交流铜损或磁钢涡流损耗,而系统仍然把Motor-CAD效率作为最终优化目标,搜索方向会被系统性带偏。因此,高保真校准系数应反哺到代理模型和目标函数,而不是只写在验证报告中。
+
+---
+
+## 五、Motor-CAD与Maxwell/JMAG职责边界
+
+### 5.1 Motor-CAD应承担的任务
+
+- 快速比较SSSR、DRSS、SDSR等拓扑在尺寸边界下的可行性。
+- 收敛主要电磁参数,包括气隙、磁钢厚度、极槽配合、有效半径、等效绕组参数和电流密度。
+- 计算关键工况下的转矩、反电势、损耗、轴向力和效率初值。
+- 在候选缩小后调用Lab、热和机械模块,形成初步多物理场筛查。
+- 为Maxwell/JMAG提供参数化候选、工况范围和需要重点验证的指标。
+
+### 5.2 Maxwell/JMAG必须承担的任务
+
+- PCB走线、多层铜箔、过孔、端部连接和局部电流分布的高保真电磁计算。
+- 三维轴向磁路、边缘效应、漏磁、局部饱和和磁钢涡流损耗校验。
+- 轴向磁拉力、转矩脉动和高阶空间谐波的高保真复核。
+- 最终效率、温升输入和退磁风险的高保真验证。
+- 对Motor-CAD近似模型进行偏差评估和校准系数更新。
+
+### 5.3 工具分工原则
+
+> **原则**
+>
+> Motor-CAD决定"大方向是否对",Maxwell/JMAG决定"工程结果是否真"。前者用于快速收敛主要参数,后者用于确认PCB和三维效应对关键指标的影响。两者缺一不可,但不能在同一层级上重复计算。
+
+### 5.4 PCB绕组建模边界
+
+Motor-CAD中的PCB近似模型必须在模型模板中显式记录等效假设,包括:铜厚、线宽、线距、层数、并联支路、过孔电阻、FR4热导率、绝缘厚度、端部连接方式和交流损耗修正系数。每次仿真结果都应绑定这些假设,否则经验库会把不同等效模型的结果混在一起。
+
+建议为PCB绕组增加独立的**"等效模型字典"**,不要直接把PCB参数伪装成常规圆线绕组参数。经验库检索时,应同时匹配拓扑、功率段和PCB等效模型版本。
+
+---
+
+## 六、系统接口与执行引擎修改建议
+
+### 6.1 JSON Schema必须扩展的字段
+
+现有 `simulation_plan.json` 已能表达固定参数扫描,但不足以表达自适应闭环、多保真度升级和跨工具校准。建议增加以下顶层字段:
+
+| 字段 | 内容 |
+| --- | --- |
+| `strategy_mode` | fast_feasible、pareto_exploration、high_fidelity_validation、robustness_check |
+| `fidelity_strategy` | L0~L4模型层级、升级条件、各层级允许的最大求解成本 |
+| `search_strategy` | 初始采样、主动学习、批量大小、局部信任域、约束处理方式 |
+| `calibration_policy` | Motor-CAD与Maxwell/JMAG偏差记录方式、修正系数、更新规则 |
+| `acceptance_criteria` | 硬约束、目标阈值、代理模型可信度、跨工具偏差和鲁棒性要求 |
+| `parallel_execution` | 并行实例数、模型副本策略、许可证失败处理和任务队列策略 |
+
+### 6.2 执行程序应支持三种模式
+
+| 模式 | 工作方式 | 适用阶段 | 对系统二的要求 |
+| --- | --- | --- | --- |
+| 固定计划模式 | 一次性接收全部仿真点并顺序执行 | Phase 1最小闭环 | 当前设计基本满足 |
+| 批量自适应模式 | 每轮接收4~8个点,执行后回传,方案系统计算下一轮 | Phase 3算法增强 | 需要支持批次ID、增量结果上传和方案版本追踪 |
+| 本地闭环模式 | 执行程序内部运行确定性优化器,根据结果动态选点 | 无人值守或网络受限场景 | 需要嵌入数值优化器;不需要内置LLM |
+
+本地闭环模式并不违背"执行程序无需联网/AI"的原则。这里的AI主要指LLM推理;约束贝叶斯、信任域和NSGA-II属于确定性或随机数值优化算法,可以打包在本地EXE中运行。
+
+### 6.3 并行与缓存
+
+- Motor-CAD可以通过PyMotorCAD由外部Python脚本进行并行计算。Ansys 2026 R1培训资料明确覆盖了Python多进程、任务调度、排队和结果汇总,这意味着系统二不应只设计成单实例串行执行器,而应预留并行任务池。[6]
+- 每个并行任务必须使用独立模型副本和独立工作目录,禁止多个实例同时写同一个 `.mot` 文件。
+- 断点续跑应以"参数哈希+模型版本+工况ID+求解器版本+保真度等级"为缓存键,而不是只检查迭代序号。
+- 结果文件应原子写入,先写临时文件再重命名,避免程序中断后被误判为已完成。
+- 许可证失效、求解器崩溃和求解不收敛应分别进入不同的重试与降级策略。
+
+### 6.4 结果数据模型
+
+每条结果建议增加以下字段:
+
+| 字段 | 内容 |
+| --- | --- |
+| `fidelity_level` | L0~L4 |
+| `model_template_version` | Motor-CAD模板、Maxwell/JMAG模型和PCB等效模型版本 |
+| `solver_settings_hash` | 网格、周期数、求解类型、时间步和对称设置等关键配置摘要 |
+| `constraint_margins` | 各硬约束的绝对裕量和百分比裕量 |
+| `surrogate_prediction` | 代理模型预测值、不确定度和实际值偏差 |
+| `cross_validation` | Motor-CAD与Maxwell/JMAG的同工况偏差 |
+| `confidence_grade` | A/B/C/D置信等级 |
+
+---
+
+## 七、收敛判据与验收标准
+
+### 7.1 六类收敛
+
+| 收敛类型 | 判断对象 | 建议判据 | 输出状态 |
+| --- | --- | --- | --- |
+| 求解器收敛 | 单次Motor-CAD或Maxwell/JMAG求解 | 求解完成、关键结果完整、日志无致命错误 | SOLVER_PASS / SOLVER_FAIL |
+| 硬约束收敛 | 候选方案是否满足边界条件 | 转矩、温度、电压、电流、尺寸、轴向力、成本均满足 | FEASIBLE / INFEASIBLE |
+| 优化收敛 | 搜索过程是否继续产生收益 | 连续若干轮最优目标改进小于阈值,或信任域半径低于下限 | CONVERGED / STALLED |
+| 代理模型可信 | 代理模型是否可用于推荐 | 交叉验证误差和候选点不确定度低于阈值 | MODEL_TRUSTED / MODEL_UNCERTAIN |
+| 跨工具一致 | Motor-CAD与Maxwell/JMAG是否一致 | 同工况关键指标偏差在可接受范围内 | HF_PASS / HF_FAIL |
+| 鲁棒性收敛 | 制造和材料扰动下是否仍满足要求 | 最差工况或指定置信度下仍满足硬约束 | ROBUST / FRAGILE |
+
+### 7.2 初始验收阈值建议
+
+以下阈值仅作为项目初始配置,必须通过首批实际模型和样机数据校准,不能直接固化为行业标准:
+
+| 指标 | Motor-CAD内部初筛 | Motor-CAD vs Maxwell/JMAG | 说明 |
+| --- | --- | --- | --- |
+| 平均转矩 | 满足目标并保留建议裕量 | 建议初始控制在±5%以内 | 若偏差系统性存在,应建立修正系数 |
+| 效率 | 满足目标并记录损耗分解 | 建议初始控制在±0.5~1.0个百分点 | PCB交流损耗是重点风险 |
+| 最高温度 | 低于限值并保留热裕量 | 建议初始控制在±5~10 ℃ | 热模型需绑定冷却边界 |
+| 轴向力 | 识别方向与量级 | 建议初始控制在±10%以内 | SSSR和装配偏心场景需重点验证 |
+| 转矩脉动 | 作为筛选指标 | 按项目目标单独定义 | 对控制、NVH和PCB局部效应敏感 |
+
+### 7.3 结果置信等级
+
+| 等级 | 含义 | 允许的用途 |
+| --- | --- | --- |
+| A | 完成Motor-CAD多物理场、Maxwell/JMAG高保真和扰动复核,关键指标均满足。 | 设计冻结、工程评审、样机投入 |
+| B | 完成Motor-CAD和至少一次高保真复核,主要指标一致,鲁棒性待补充。 | 方案候选、供应商沟通、详细设计输入 |
+| C | 只完成Motor-CAD初步验证,未完成Maxwell/JMAG校核。 | 参数筛选、内部讨论,不可对外承诺 |
+| D | 只完成解析或代理模型预测,缺少真实仿真。 | 方案生成参考,不可作为工程结论 |
+
+---
+
+## 八、开发实施路线与验证用例
+
+### 8.1 建议开发优先级
+
+| 优先级 | 开发内容 | 完成判据 |
+| --- | --- | --- |
+| P0 | 扩展JSON Schema;增加L0规则预筛选;增加保真度等级;增加约束裕量和结果置信等级。 | 同一个SSSR案例可按新Schema完整执行并回传结构化结果。 |
+| P0 | 将固定参数队列执行器升级为支持批次ID和增量结果的执行器。 | 中断后重启不重复计算;结果可追溯模型版本。 |
+| P1 | 实现快速可行模式:少量初始样本+约束贝叶斯/主动学习+批量下发。 | 在相同预算下比固定LHS更快找到可行点。 |
+| P1 | 接入Motor-CAD Lab/Therm关键工况复核。 | 候选方案能输出效率图摘要和温升结果。 |
+| P2 | 实现Maxwell或JMAG高保真适配器和偏差校准表。 | 同一候选方案可自动对比低/高保真关键指标。 |
+| P2 | 支持Motor-CAD多实例并行和缓存键。 | 并行结果与串行结果一致,且无模型文件污染。 |
+| P3 | 加入制造扰动和鲁棒性评估。 | 输出最差工况和A/B/C/D置信等级。 |
+
+### 8.2 最小验证用例
+
+建议使用现有SSSR基准模型做三组对比验证:
+
+- **路径A**:固定LHS+Kriging+NSGA-II,验证现有第6章流程。
+- **路径B**:L0预筛选+约束贝叶斯/主动学习,验证快速可行模式。
+- **路径C**:路径B选出的Top 1~3候选进入Maxwell/JMAG,验证跨工具一致性和校准机制。
+
+对比指标不应只包括最终性能,还应包括:总仿真次数、墙钟时间、可行点出现时间、代理模型误差、高保真偏差和工程师人工干预次数。
+
+### 8.3 开发交付物
+
+1. 《仿真策略与保真度分级规范》
+2. 《simulation_plan.json Schema V1.1》
+3. 《simulation_results.json Schema V1.1》
+4. Motor-CAD适配器V0.2和并行任务池
+5. Maxwell/JMAG高保真校核适配器原型
+6. 三条路径的对比验证报告
+7. PCB等效模型字典和跨工具校准表模板
+
+---
+
+## 九、主要风险与待确认问题
+
+### 9.1 主要风险
+
+| 风险 | 表现 | 等级 | 控制措施 |
+| --- | --- | --- | --- |
+| PCB近似模型系统性偏差 | Motor-CAD效率、温升或损耗持续偏离Maxwell/JMAG。 | 高 | 建立校准系数;关键指标必须高保真复核;偏差反哺代理模型。 |
+| 轴向磁通3D效应 | 漏磁、边缘效应和局部饱和导致转矩或轴向力偏差。 | 高 | Motor-CAD只用于筛选;最终候选必须做3D FEA。 |
+| 自适应算法不可解释 | 工程师不理解为什么推荐某个点。 | 中高 | 每个推荐点输出来源、预测值、不确定度和约束概率。 |
+| 经验库冷启动污染 | 不同模型版本或错误结果被当成可复用经验。 | 中高 | 所有经验绑定模型版本、保真度和置信等级;专家审核后入库。 |
+| 并行许可和文件冲突 | 多实例启动失败、模型文件互相覆盖。 | 中 | 独立工作目录、模型副本、许可检测和任务队列。 |
+| 优化目标定义不完整 | 收敛到名义最优但工程不可制造。 | 高 | 补齐母线电压、电流、冷却、公差、PCB工艺和材料边界。 |
+| 停止条件过松 | 系统把第一个可行点误认为全局可靠解。 | 中 | 拆分收敛类型,增加置信等级和鲁棒性出口。 |
+
+### 9.2 开发前必须确认的问题
+
+1. PCB电机结构究竟是无铁芯PCB定子、PCB绕组+铁芯,还是混合结构?
+2. SSSR、DRSS、SDSR三类拓扑是否都需要支持Halbach、背铁和分段磁钢?
+3. Motor-CAD当前模板对PCB铜厚、层数、过孔、端部和FR4热属性的近似方式是什么?
+4. Maxwell和JMAG哪一个作为第一优先级高保真工具?两者的模型转换和结果映射由谁负责?
+5. 默认业务目标是快速可行、完整Pareto,还是量产鲁棒设计?
+6. 有几套Motor-CAD/Maxwell/JMAG许可?是否允许无GUI和多实例并行?
+7. 额定、峰值和高效区工况的持续时间、温度边界和电流限制如何定义?
+8. 气隙、PCB线宽线距、铜厚、磁钢Br和装配偏心的制造公差是多少?
+9. 最终验收阈值由哪个专家团队签发?是否已有样机或测试数据可用于初始校准?
+10. 系统二是否允许内置数值优化器?如果可以,批量自适应模式和本地闭环模式的边界需要重新定义。
+
+---
+
+## 十、附录
+
+### 10.1 推荐JSON扩展示意
+
+```json
+{
+  "strategy_mode": "fast_feasible",
+  "fidelity_strategy": {
+    "levels": ["L0_analytic", "L1_motorcad_emag", "L2_motorcad_lab_therm", "L3_maxwell_3d"],
+    "upgrade_rule": "top_candidates_only",
+    "max_candidates_for_l3": 3
+  },
+  "search_strategy": {
+    "method": "constrained_bayesian",
+    "initial_samples": 16,
+    "batch_size": 4,
+    "max_solver_calls": 80,
+    "local_trust_region": true
+  },
+  "acceptance_criteria": {
+    "hard_constraints": ["torque_nm >= 10", "temperature_c <= 120"],
+    "cross_tool_tolerance": { "torque_pct": 5, "efficiency_point": 1.0 },
+    "surrogate_max_uncertainty": 0.05,
+    "robustness_required": true
+  }
+}
+```
+
+### 10.2 术语
+
+| 术语 | 说明 |
+| --- | --- |
+| 可行性优先 | 先让候选方案满足硬约束,再追求性能最优。 |
+| 多保真度 | 用不同成本和精度的模型分层完成筛选、优化、验证和校准。 |
+| 主动学习 | 根据当前代理模型的不确定性和预期收益,动态选择下一批仿真点。 |
+| 约束贝叶斯优化 | 在贝叶斯优化中显式建模硬约束满足概率,优先搜索可行区域。 |
+| 信任域 | 在当前最优点附近限制搜索范围,逐步扩大或收缩,用于局部稳定收敛。 |
+| 跨工具校准 | 比较Motor-CAD与Maxwell/JMAG在相同工况下的结果差异,并把差异用于修正后续预测。 |
+| 置信等级 | 按验证深度把结果分为A/B/C/D,避免低保真结果被误用为最终结论。 |
+
+### 10.3 参考资料
+
+1. Ansys, *How to Efficiently Optimize Electric Motor Design* — https://ansys.synopsys.com/blog/how-to-efficiently-optimize-electric-motor-design
+2. 《基于Kriging-PSO算法的双转子PCB轴向磁通电机优化设计与分析》,电机工程学报,DOI: 10.11985/JEE.260677
+3. SimuTech Group, *Bayesian Optimization for Electric Motor Design Using Stochos and Ansys Motor-CAD*, 2026 — https://simutechgroup.com/resources/blog/bayesian-optimization-electric-motor-design-using-stochos-and-motor-cad/
+4. Ansys Innovation Space, *Accelerating Multiphysics Optimization Workflows of Axial Flux Motor Design*, 2026 — https://innovationspace.ansys.com/product/accelerating-multiphysics-optimization-workflows-of-axial-flux-motor-design/
+5. Ansys Motor-CAD 产品页(2026 R1 轴向磁通、热、Lab、Maxwell联动与多物理场能力说明)— https://www.ansys.com/products/electronics/ansys-motor-cad
+6. Ansys Innovation Space, *Running Parallel Ansys Motor-CAD Calculations Through Scripting*, 2026 R1 — https://innovationspace.ansys.com/certifications/courses/running-parallel-ansys-motor-cad-calculations-through-scripting/
+7. Ansys, *New Adaptive Templates in Ansys Motor-CAD Make Motor Design Faster, Easier, and More Scalable* — https://www.ansys.com/blog/new-adaptive-templates-ansys-motor-cad-make-motor-design-faster-easier-more-scalable