Sfoglia il codice sorgente

Initial mobile lab scaffold

Rockz-Home 1 mese fa
commit
2cb11abfb0
100 ha cambiato i file con 7998 aggiunte e 0 eliminazioni
  1. 21 0
      .gitignore
  2. 5 0
      backend/.gitignore
  3. 30 0
      backend/README.md
  4. 1 0
      backend/app/__init__.py
  5. 1 0
      backend/app/api/__init__.py
  6. 14 0
      backend/app/api/router.py
  7. 1 0
      backend/app/api/routes/__init__.py
  8. 39 0
      backend/app/api/routes/analysis.py
  9. 10 0
      backend/app/api/routes/health.py
  10. 75 0
      backend/app/api/routes/observations.py
  11. 1 0
      backend/app/core/__init__.py
  12. 23 0
      backend/app/core/config.py
  13. 51 0
      backend/app/core/db.py
  14. 26 0
      backend/app/main.py
  15. 1 0
      backend/app/models/__init__.py
  16. 1 0
      backend/app/repositories/__init__.py
  17. 66 0
      backend/app/repositories/analysis_repository.py
  18. 47 0
      backend/app/repositories/observation_repository.py
  19. 1 0
      backend/app/schemas/__init__.py
  20. 35 0
      backend/app/schemas/analysis.py
  21. 5 0
      backend/app/schemas/common.py
  22. 15 0
      backend/app/schemas/conclusion.py
  23. 9 0
      backend/app/schemas/evidence.py
  24. 19 0
      backend/app/schemas/experiment.py
  25. 6 0
      backend/app/schemas/health.py
  26. 20 0
      backend/app/schemas/observation.py
  27. 35 0
      backend/app/schemas/orchestration.py
  28. 1 0
      backend/app/services/__init__.py
  29. 83 0
      backend/app/services/analysis_service.py
  30. 78 0
      backend/app/services/observation_service.py
  31. 136 0
      backend/app/services/stub_orchestrator_service.py
  32. 168 0
      backend/app/services/stub_runner_service.py
  33. 1 0
      backend/data/sample-upload.wav
  34. 5 0
      backend/requirements.txt
  35. 332 0
      doc/产品方向/音频优先产品方向.md
  36. 269 0
      doc/产品方向/音频优先移动信号实验室方案.md
  37. 142 0
      doc/实施规划/第一阶段实施路线图.md
  38. 241 0
      doc/平台设计/AI分析员定位与职责.md
  39. 301 0
      doc/平台设计/AI编排输入输出规范.md
  40. 255 0
      doc/平台设计/Flutter终端架构设计.md
  41. 183 0
      doc/平台设计/后端技术栈方案.md
  42. 175 0
      doc/平台设计/后端服务拆分设计.md
  43. 388 0
      doc/平台设计/实验执行器设计.md
  44. 449 0
      doc/平台设计/核心数据模型.md
  45. 397 0
      doc/平台设计/模块注册表设计.md
  46. 522 0
      doc/平台设计/算法模块与插件系统.md
  47. 526 0
      doc/平台设计/算法链分层设计.md
  48. 570 0
      doc/总体架构/便携式信号分析仪架构说明.md
  49. 371 0
      doc/总体架构/多模态分析框架.md
  50. 361 0
      doc/总体架构/应用版图与优先级.md
  51. 71 0
      doc/文档目录.md
  52. 80 0
      doc/规范/文档规范.md
  53. 45 0
      frontend/.gitignore
  54. 45 0
      frontend/.metadata
  55. 16 0
      frontend/README.md
  56. 28 0
      frontend/analysis_options.yaml
  57. 14 0
      frontend/android/.gitignore
  58. 44 0
      frontend/android/app/build.gradle.kts
  59. 7 0
      frontend/android/app/src/debug/AndroidManifest.xml
  60. 47 0
      frontend/android/app/src/main/AndroidManifest.xml
  61. 5 0
      frontend/android/app/src/main/kotlin/com/tc/tc_frontend/MainActivity.kt
  62. 12 0
      frontend/android/app/src/main/res/drawable-v21/launch_background.xml
  63. 12 0
      frontend/android/app/src/main/res/drawable/launch_background.xml
  64. BIN
      frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
  65. BIN
      frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
  66. BIN
      frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  67. BIN
      frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  68. BIN
      frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  69. 18 0
      frontend/android/app/src/main/res/values-night/styles.xml
  70. 18 0
      frontend/android/app/src/main/res/values/styles.xml
  71. 7 0
      frontend/android/app/src/profile/AndroidManifest.xml
  72. 24 0
      frontend/android/build.gradle.kts
  73. 2 0
      frontend/android/gradle.properties
  74. 5 0
      frontend/android/gradle/wrapper/gradle-wrapper.properties
  75. 26 0
      frontend/android/settings.gradle.kts
  76. 34 0
      frontend/ios/.gitignore
  77. 26 0
      frontend/ios/Flutter/AppFrameworkInfo.plist
  78. 1 0
      frontend/ios/Flutter/Debug.xcconfig
  79. 1 0
      frontend/ios/Flutter/Release.xcconfig
  80. 616 0
      frontend/ios/Runner.xcodeproj/project.pbxproj
  81. 7 0
      frontend/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  82. 8 0
      frontend/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  83. 8 0
      frontend/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  84. 101 0
      frontend/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
  85. 7 0
      frontend/ios/Runner.xcworkspace/contents.xcworkspacedata
  86. 8 0
      frontend/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  87. 8 0
      frontend/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  88. 13 0
      frontend/ios/Runner/AppDelegate.swift
  89. 122 0
      frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
  90. BIN
      frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
  91. BIN
      frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
  92. BIN
      frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
  93. BIN
      frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
  94. BIN
      frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
  95. BIN
      frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
  96. BIN
      frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
  97. BIN
      frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
  98. BIN
      frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
  99. BIN
      frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
  100. BIN
      frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png

+ 21 - 0
.gitignore

@@ -0,0 +1,21 @@
+# Repository-level ignores
+.DS_Store
+Thumbs.db
+
+# Local editor state
+.idea/
+.vscode/
+
+# Backend runtime data
+backend/data/*.db
+backend/data/uploads/
+backend/uvicorn.out.log
+backend/uvicorn.err.log
+
+# Python caches outside package-local .gitignore
+**/__pycache__/
+**/.pytest_cache/
+
+# Flutter build artifacts outside frontend/.gitignore
+frontend/build/
+frontend/.dart_tool/

+ 5 - 0
backend/.gitignore

@@ -0,0 +1,5 @@
+.venv/
+__pycache__/
+.pytest_cache/
+*.pyc
+.env

+ 30 - 0
backend/README.md

@@ -0,0 +1,30 @@
+# TC Backend
+
+Minimal FastAPI backend skeleton for the TC mobile signal analysis lab.
+
+## Python version
+
+Use Python `3.12` or `3.13` for now.
+
+The current dependency set is blocked on Python `3.14` because `pydantic-core`
+build support is not yet available in this environment.
+
+## Storage
+
+The current backend uses SQLite via the Python standard library.
+
+Default database path:
+
+`data/tc_backend.db`
+
+Uploaded audio files are stored under:
+
+`data/uploads/`
+
+## Run
+
+```bash
+.venv\Scripts\activate
+pip install -r requirements.txt
+uvicorn app.main:app --reload
+```

+ 1 - 0
backend/app/__init__.py

@@ -0,0 +1 @@
+"""Backend application package."""

+ 1 - 0
backend/app/api/__init__.py

@@ -0,0 +1 @@
+"""API package."""

+ 14 - 0
backend/app/api/router.py

@@ -0,0 +1,14 @@
+from fastapi import APIRouter
+
+from app.api.routes.analysis import router as analysis_router
+from app.api.routes.health import router as health_router
+from app.api.routes.observations import router as observation_router
+
+api_router = APIRouter()
+api_router.include_router(health_router, tags=["health"])
+api_router.include_router(
+    observation_router,
+    prefix="/observations",
+    tags=["observations"],
+)
+api_router.include_router(analysis_router, prefix="/analysis", tags=["analysis"])

+ 1 - 0
backend/app/api/routes/__init__.py

@@ -0,0 +1 @@
+"""Route modules."""

+ 39 - 0
backend/app/api/routes/analysis.py

@@ -0,0 +1,39 @@
+from fastapi import APIRouter, HTTPException, status
+
+from app.schemas.analysis import (
+    AnalysisSessionCreateRequest,
+    AnalysisSessionListResponse,
+    AnalysisSessionResponse,
+    AnalysisSessionSnapshot,
+)
+from app.services.analysis_service import AnalysisService
+
+router = APIRouter()
+service = AnalysisService()
+
+
+@router.post(
+    "/sessions",
+    response_model=AnalysisSessionResponse,
+    status_code=status.HTTP_201_CREATED,
+)
+def create_analysis_session(
+    payload: AnalysisSessionCreateRequest,
+) -> AnalysisSessionResponse:
+    return service.create_session(payload)
+
+
+@router.get("/sessions", response_model=AnalysisSessionListResponse)
+def list_analysis_sessions() -> AnalysisSessionListResponse:
+    return service.list_sessions()
+
+
+@router.get("/sessions/{session_id}", response_model=AnalysisSessionSnapshot)
+def get_analysis_session(session_id: str) -> AnalysisSessionSnapshot:
+    snapshot = service.get_session(session_id)
+    if snapshot is None:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=f"Analysis session '{session_id}' was not found.",
+        )
+    return snapshot

+ 10 - 0
backend/app/api/routes/health.py

@@ -0,0 +1,10 @@
+from fastapi import APIRouter
+
+from app.schemas.health import HealthResponse
+
+router = APIRouter()
+
+
+@router.get("/health", response_model=HealthResponse)
+def get_health() -> HealthResponse:
+    return HealthResponse(status="ok", service="tc-backend")

+ 75 - 0
backend/app/api/routes/observations.py

@@ -0,0 +1,75 @@
+import json
+
+from fastapi import APIRouter, File, Form, HTTPException, UploadFile, status
+
+from app.schemas.common import ListResponse
+from app.schemas.observation import ObservationCreateRequest, ObservationSummary
+from app.services.observation_service import ObservationService
+
+router = APIRouter()
+service = ObservationService()
+
+
+@router.post(
+    "",
+    response_model=ObservationSummary,
+    status_code=status.HTTP_201_CREATED,
+)
+def create_observation(payload: ObservationCreateRequest) -> ObservationSummary:
+    return service.create_observation(payload)
+
+
+@router.post(
+    "/upload",
+    response_model=ObservationSummary,
+    status_code=status.HTTP_201_CREATED,
+)
+async def upload_observation(
+    audio_file: UploadFile = File(...),
+    duration_ms: int = Form(...),
+    sample_rate: int = Form(...),
+    channels: int = Form(...),
+    tags_json: str = Form(default="[]"),
+    capture_metadata_json: str = Form(default="{}"),
+) -> ObservationSummary:
+    try:
+        tags = json.loads(tags_json)
+        capture_metadata = json.loads(capture_metadata_json)
+    except json.JSONDecodeError as error:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=f"Invalid JSON form payload: {error}",
+        ) from error
+
+    content = await audio_file.read()
+    if not content:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail="Uploaded file is empty.",
+        )
+
+    return service.create_observation_from_upload(
+        filename=audio_file.filename or "capture.bin",
+        content=content,
+        duration_ms=duration_ms,
+        sample_rate=sample_rate,
+        channels=channels,
+        tags=tags,
+        capture_metadata=capture_metadata,
+    )
+
+
+@router.get("", response_model=ListResponse[ObservationSummary])
+def list_observations() -> ListResponse[ObservationSummary]:
+    return service.list_observations()
+
+
+@router.get("/{observation_id}", response_model=ObservationSummary)
+def get_observation(observation_id: str) -> ObservationSummary:
+    observation = service.get_observation(observation_id)
+    if observation is None:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=f"Observation '{observation_id}' was not found.",
+        )
+    return observation

+ 1 - 0
backend/app/core/__init__.py

@@ -0,0 +1 @@
+"""Core app configuration and shared utilities."""

+ 23 - 0
backend/app/core/config.py

@@ -0,0 +1,23 @@
+from functools import lru_cache
+
+from pydantic_settings import BaseSettings, SettingsConfigDict
+
+
+class Settings(BaseSettings):
+    app_name: str = "TC Backend"
+    app_version: str = "0.1.0"
+    api_prefix: str = "/api"
+    sqlite_path: str = "data/tc_backend.db"
+    upload_dir: str = "data/uploads"
+
+    model_config = SettingsConfigDict(
+        env_prefix="TC_",
+        env_file=".env",
+        env_file_encoding="utf-8",
+        extra="ignore",
+    )
+
+
+@lru_cache
+def get_settings() -> Settings:
+    return Settings()

+ 51 - 0
backend/app/core/db.py

@@ -0,0 +1,51 @@
+import sqlite3
+from contextlib import contextmanager
+from pathlib import Path
+from typing import Iterator
+
+from app.core.config import get_settings
+
+
+def _ensure_parent_dir(db_path: str) -> None:
+    Path(db_path).parent.mkdir(parents=True, exist_ok=True)
+
+
+def initialize_database() -> None:
+    settings = get_settings()
+    _ensure_parent_dir(settings.sqlite_path)
+    Path(settings.upload_dir).mkdir(parents=True, exist_ok=True)
+
+    with sqlite3.connect(settings.sqlite_path) as connection:
+        connection.execute(
+            """
+            CREATE TABLE IF NOT EXISTS observations (
+                id TEXT PRIMARY KEY,
+                payload_json TEXT NOT NULL,
+                created_at TEXT DEFAULT CURRENT_TIMESTAMP
+            )
+            """
+        )
+        connection.execute(
+            """
+            CREATE TABLE IF NOT EXISTS analysis_sessions (
+                session_id TEXT PRIMARY KEY,
+                observation_id TEXT NOT NULL,
+                status TEXT NOT NULL,
+                payload_json TEXT NOT NULL,
+                created_at TEXT DEFAULT CURRENT_TIMESTAMP
+            )
+            """
+        )
+        connection.commit()
+
+
+@contextmanager
+def get_connection() -> Iterator[sqlite3.Connection]:
+    settings = get_settings()
+    _ensure_parent_dir(settings.sqlite_path)
+    connection = sqlite3.connect(settings.sqlite_path)
+    connection.row_factory = sqlite3.Row
+    try:
+        yield connection
+    finally:
+        connection.close()

+ 26 - 0
backend/app/main.py

@@ -0,0 +1,26 @@
+from fastapi import FastAPI
+
+from app.api.router import api_router
+from app.core.config import get_settings
+from app.core.db import initialize_database
+
+
+def create_app() -> FastAPI:
+    settings = get_settings()
+
+    app = FastAPI(
+        title=settings.app_name,
+        version=settings.app_version,
+        docs_url="/docs",
+        redoc_url="/redoc",
+    )
+    app.include_router(api_router, prefix=settings.api_prefix)
+    return app
+
+
+app = create_app()
+
+
+@app.on_event("startup")
+def on_startup() -> None:
+    initialize_database()

+ 1 - 0
backend/app/models/__init__.py

@@ -0,0 +1 @@
+"""Persistence models package."""

+ 1 - 0
backend/app/repositories/__init__.py

@@ -0,0 +1 @@
+"""Repository package."""

+ 66 - 0
backend/app/repositories/analysis_repository.py

@@ -0,0 +1,66 @@
+import json
+
+from app.core.db import get_connection
+from app.schemas.analysis import (
+    AnalysisSessionListResponse,
+    AnalysisSessionResponse,
+    AnalysisSessionSnapshot,
+)
+
+
+class AnalysisRepository:
+    def save_snapshot(self, snapshot: AnalysisSessionSnapshot) -> None:
+        payload_json = json.dumps(snapshot.model_dump())
+        with get_connection() as connection:
+            connection.execute(
+                """
+                INSERT OR REPLACE INTO analysis_sessions (
+                    session_id,
+                    observation_id,
+                    status,
+                    payload_json
+                )
+                VALUES (?, ?, ?, ?)
+                """,
+                (
+                    snapshot.session_id,
+                    snapshot.observation_id,
+                    snapshot.status,
+                    payload_json,
+                ),
+            )
+            connection.commit()
+
+    def get_snapshot(self, session_id: str) -> AnalysisSessionSnapshot | None:
+        with get_connection() as connection:
+            row = connection.execute(
+                """
+                SELECT payload_json
+                FROM analysis_sessions
+                WHERE session_id = ?
+                """,
+                (session_id,),
+            ).fetchone()
+        if row is None:
+            return None
+        return AnalysisSessionSnapshot.model_validate_json(row["payload_json"])
+
+    def list_sessions(self) -> AnalysisSessionListResponse:
+        with get_connection() as connection:
+            rows = connection.execute(
+                """
+                SELECT session_id, observation_id, status
+                FROM analysis_sessions
+                ORDER BY created_at DESC
+                """
+            ).fetchall()
+        items = [
+            AnalysisSessionResponse(
+                session_id=row["session_id"],
+                observation_id=row["observation_id"],
+                status=row["status"],
+                message="Session snapshot available.",
+            )
+            for row in rows
+        ]
+        return AnalysisSessionListResponse(items=items)

+ 47 - 0
backend/app/repositories/observation_repository.py

@@ -0,0 +1,47 @@
+import json
+
+from app.core.db import get_connection
+from app.schemas.common import ListResponse
+from app.schemas.observation import ObservationSummary
+
+
+class ObservationRepository:
+    def save(self, observation: ObservationSummary) -> None:
+        payload_json = json.dumps(observation.model_dump())
+        with get_connection() as connection:
+            connection.execute(
+                """
+                INSERT OR REPLACE INTO observations (id, payload_json)
+                VALUES (?, ?)
+                """,
+                (observation.id, payload_json),
+            )
+            connection.commit()
+
+    def get(self, observation_id: str) -> ObservationSummary | None:
+        with get_connection() as connection:
+            row = connection.execute(
+                """
+                SELECT payload_json
+                FROM observations
+                WHERE id = ?
+                """,
+                (observation_id,),
+            ).fetchone()
+        if row is None:
+            return None
+        return ObservationSummary.model_validate_json(row["payload_json"])
+
+    def list(self) -> ListResponse[ObservationSummary]:
+        with get_connection() as connection:
+            rows = connection.execute(
+                """
+                SELECT payload_json
+                FROM observations
+                ORDER BY created_at DESC
+                """
+            ).fetchall()
+        items = [
+            ObservationSummary.model_validate_json(row["payload_json"]) for row in rows
+        ]
+        return ListResponse(items=items)

+ 1 - 0
backend/app/schemas/__init__.py

@@ -0,0 +1 @@
+"""Schema package."""

+ 35 - 0
backend/app/schemas/analysis.py

@@ -0,0 +1,35 @@
+from pydantic import BaseModel, Field
+
+from app.schemas.conclusion import ConclusionSummary
+from app.schemas.evidence import EvidenceSummary
+from app.schemas.experiment import ExperimentSummary
+from app.schemas.observation import ObservationSummary
+from app.schemas.orchestration import Hypothesis
+
+
+class AnalysisSessionCreateRequest(BaseModel):
+    observation_id: str = Field(..., min_length=1)
+    mode: str = Field(default="initial_analysis", min_length=1)
+
+
+class AnalysisSessionResponse(BaseModel):
+    session_id: str
+    observation_id: str
+    status: str
+    message: str
+
+
+class AnalysisSessionSnapshot(BaseModel):
+    session_id: str
+    observation_id: str
+    status: str
+    mode: str
+    observation: ObservationSummary
+    hypotheses: list[Hypothesis]
+    probe_evidence: list[EvidenceSummary]
+    experiments: list[ExperimentSummary]
+    conclusion: ConclusionSummary
+
+
+class AnalysisSessionListResponse(BaseModel):
+    items: list[AnalysisSessionResponse]

+ 5 - 0
backend/app/schemas/common.py

@@ -0,0 +1,5 @@
+from pydantic import BaseModel
+
+
+class ListResponse[T](BaseModel):
+    items: list[T]

+ 15 - 0
backend/app/schemas/conclusion.py

@@ -0,0 +1,15 @@
+from pydantic import BaseModel
+
+
+class RankedFinding(BaseModel):
+    label: str
+    confidence: float
+    supporting_evidence: list[str]
+    contradicting_evidence: list[str]
+
+
+class ConclusionSummary(BaseModel):
+    summary: str
+    findings: list[RankedFinding]
+    uncertainty: list[str]
+    suggested_next_actions: list[str]

+ 9 - 0
backend/app/schemas/evidence.py

@@ -0,0 +1,9 @@
+from pydantic import BaseModel
+
+
+class EvidenceSummary(BaseModel):
+    id: str
+    producer_module_id: str | None = None
+    category: str
+    values: dict[str, object]
+    confidence: float

+ 19 - 0
backend/app/schemas/experiment.py

@@ -0,0 +1,19 @@
+from pydantic import BaseModel
+
+
+class ScoreCard(BaseModel):
+    total: float
+    components: dict[str, float]
+    penalties: dict[str, float] = {}
+    notes: list[str] = []
+
+
+class ExperimentSummary(BaseModel):
+    id: str
+    parent_id: str | None = None
+    hypothesis_id: str | None = None
+    title: str
+    status: str
+    pipeline_summary: list[str]
+    score: ScoreCard
+    failure_reasons: list[str]

+ 6 - 0
backend/app/schemas/health.py

@@ -0,0 +1,6 @@
+from pydantic import BaseModel
+
+
+class HealthResponse(BaseModel):
+    status: str
+    service: str

+ 20 - 0
backend/app/schemas/observation.py

@@ -0,0 +1,20 @@
+from pydantic import BaseModel
+
+
+class ObservationCreateRequest(BaseModel):
+    modality: str = "audio"
+    duration_ms: int
+    sample_rate: int
+    channels: int
+    tags: list[str] = []
+    capture_metadata: dict[str, object] = {}
+
+
+class ObservationSummary(BaseModel):
+    id: str
+    modality: str
+    duration_ms: int
+    sample_rate: int
+    channels: int
+    tags: list[str]
+    capture_metadata: dict[str, object] = {}

+ 35 - 0
backend/app/schemas/orchestration.py

@@ -0,0 +1,35 @@
+from pydantic import BaseModel
+
+
+class Hypothesis(BaseModel):
+    id: str
+    label: str
+    rationale: str
+    confidence: float
+    related_signal_profiles: list[str]
+
+
+class PlannedNode(BaseModel):
+    module_id: str
+    params: dict[str, object]
+
+
+class PlannedExperiment(BaseModel):
+    hypothesis_id: str | None = None
+    goal: str
+    preferred_chain_id: str | None = None
+    pipeline: list[PlannedNode]
+    expected_evidence: list[str]
+    stop_if: list[str]
+
+
+class StopDecision(BaseModel):
+    should_stop: bool
+    reason: str
+
+
+class OrchestratorPlan(BaseModel):
+    hypotheses: list[Hypothesis]
+    experiments: list[PlannedExperiment]
+    stop_decision: StopDecision
+    notes: list[str] = []

+ 1 - 0
backend/app/services/__init__.py

@@ -0,0 +1 @@
+"""Application services."""

+ 83 - 0
backend/app/services/analysis_service.py

@@ -0,0 +1,83 @@
+from uuid import uuid4
+
+from fastapi import HTTPException, status
+
+from app.repositories.analysis_repository import AnalysisRepository
+from app.repositories.observation_repository import ObservationRepository
+from app.schemas.analysis import (
+    AnalysisSessionCreateRequest,
+    AnalysisSessionListResponse,
+    AnalysisSessionResponse,
+    AnalysisSessionSnapshot,
+)
+from app.schemas.observation import ObservationSummary
+from app.schemas.orchestration import OrchestratorPlan
+from app.services.stub_orchestrator_service import StubOrchestratorService
+from app.services.stub_runner_service import StubRunnerService
+
+
+class AnalysisService:
+    def __init__(self) -> None:
+        self.repository = AnalysisRepository()
+        self.observation_repository = ObservationRepository()
+        self.orchestrator = StubOrchestratorService()
+        self.runner = StubRunnerService()
+
+    def create_session(
+        self,
+        payload: AnalysisSessionCreateRequest,
+    ) -> AnalysisSessionResponse:
+        session_id = str(uuid4())
+        observation = self.observation_repository.get(payload.observation_id)
+        if observation is None:
+            raise HTTPException(
+                status_code=status.HTTP_404_NOT_FOUND,
+                detail=f"Observation '{payload.observation_id}' was not found.",
+            )
+        plan = self.orchestrator.create_initial_plan(observation)
+        snapshot = self._build_stub_snapshot(
+            session_id=session_id,
+            observation=observation,
+            mode=payload.mode,
+            plan=plan,
+        )
+        self.repository.save_snapshot(snapshot)
+
+        return AnalysisSessionResponse(
+            session_id=session_id,
+            observation_id=payload.observation_id,
+            status=snapshot.status,
+            message="Analysis session created and seeded with stub experiment data.",
+        )
+
+    def get_session(self, session_id: str) -> AnalysisSessionSnapshot | None:
+        return self.repository.get_snapshot(session_id)
+
+    def list_sessions(self) -> AnalysisSessionListResponse:
+        return self.repository.list_sessions()
+
+    def _build_stub_snapshot(
+        self,
+        *,
+        session_id: str,
+        observation: ObservationSummary,
+        mode: str,
+        plan: OrchestratorPlan,
+    ) -> AnalysisSessionSnapshot:
+        run = self.runner.run_initial_plan(
+            session_id=session_id,
+            observation=observation,
+            plan=plan,
+        )
+
+        return AnalysisSessionSnapshot(
+            session_id=session_id,
+            observation_id=observation.id,
+            status=run.status,
+            mode=mode,
+            observation=observation,
+            hypotheses=plan.hypotheses,
+            probe_evidence=run.probe_evidence,
+            experiments=run.experiments,
+            conclusion=run.conclusion,
+        )

+ 78 - 0
backend/app/services/observation_service.py

@@ -0,0 +1,78 @@
+from pathlib import Path
+from uuid import uuid4
+
+from app.core.config import get_settings
+from app.repositories.observation_repository import ObservationRepository
+from app.schemas.common import ListResponse
+from app.schemas.observation import ObservationCreateRequest, ObservationSummary
+
+
+class ObservationService:
+    def __init__(self) -> None:
+        self.repository = ObservationRepository()
+
+    def create_observation(
+        self,
+        payload: ObservationCreateRequest,
+    ) -> ObservationSummary:
+        observation = ObservationSummary(
+            id=str(uuid4()),
+            modality=payload.modality,
+            duration_ms=payload.duration_ms,
+            sample_rate=payload.sample_rate,
+            channels=payload.channels,
+            tags=payload.tags,
+            capture_metadata=payload.capture_metadata,
+        )
+        self.repository.save(observation)
+        return observation
+
+    def create_observation_from_upload(
+        self,
+        *,
+        filename: str,
+        content: bytes,
+        duration_ms: int,
+        sample_rate: int,
+        channels: int,
+        tags: list[str],
+        capture_metadata: dict[str, object],
+    ) -> ObservationSummary:
+        observation_id = str(uuid4())
+        suffix = Path(filename).suffix or ".bin"
+        upload_name = f"{observation_id}{suffix}"
+
+        settings = get_settings()
+        upload_dir = Path(settings.upload_dir)
+        upload_dir.mkdir(parents=True, exist_ok=True)
+        stored_path = upload_dir / upload_name
+        stored_path.write_bytes(content)
+
+        metadata = dict(capture_metadata)
+        metadata.update(
+            {
+                "source": metadata.get("source", "upload"),
+                "original_filename": filename,
+                "stored_filename": upload_name,
+                "stored_path": str(stored_path),
+                "file_size_bytes": len(content),
+            }
+        )
+
+        observation = ObservationSummary(
+            id=observation_id,
+            modality="audio",
+            duration_ms=duration_ms,
+            sample_rate=sample_rate,
+            channels=channels,
+            tags=tags,
+            capture_metadata=metadata,
+        )
+        self.repository.save(observation)
+        return observation
+
+    def get_observation(self, observation_id: str) -> ObservationSummary | None:
+        return self.repository.get(observation_id)
+
+    def list_observations(self) -> ListResponse[ObservationSummary]:
+        return self.repository.list()

+ 136 - 0
backend/app/services/stub_orchestrator_service.py

@@ -0,0 +1,136 @@
+from app.schemas.observation import ObservationSummary
+from app.schemas.orchestration import (
+    Hypothesis,
+    OrchestratorPlan,
+    PlannedExperiment,
+    PlannedNode,
+    StopDecision,
+)
+
+
+class StubOrchestratorService:
+    def create_initial_plan(self, observation: ObservationSummary) -> OrchestratorPlan:
+        tags = set(observation.tags)
+        looks_periodic = "periodic" in tags or observation.duration_ms >= 4500
+        looks_mixed = "mixed" in tags
+
+        hypotheses = [
+            Hypothesis(
+                id="periodic-alert-h1",
+                label="Periodic alert tone",
+                rationale=(
+                    "Observation metadata and initial capture profile suggest a "
+                    "repeatable alert-like signal rather than a diffuse ambient event."
+                ),
+                confidence=0.78 if looks_periodic else 0.56,
+                related_signal_profiles=["periodic_pulse", "steady_tonal"],
+            ),
+            Hypothesis(
+                id="mechanical-sequence-h2",
+                label="Mechanical click sequence",
+                rationale=(
+                    "A secondary possibility is a short repeating mechanical event "
+                    "pattern with less tonal stability."
+                ),
+                confidence=0.33 if looks_periodic else 0.48,
+                related_signal_profiles=["multi_event_sequence", "transient_event"],
+            ),
+        ]
+
+        if looks_mixed:
+            hypotheses.append(
+                Hypothesis(
+                    id="mixed-source-h3",
+                    label="Mixed source contamination",
+                    rationale=(
+                        "Capture tags indicate the possibility of overlapping sources, "
+                        "which could distort classifier confidence."
+                    ),
+                    confidence=0.42,
+                    related_signal_profiles=["mixed_source"],
+                )
+            )
+
+        experiments = [
+            PlannedExperiment(
+                hypothesis_id="periodic-alert-h1",
+                goal="Validate periodic repeat structure",
+                preferred_chain_id="periodic_validation_chain",
+                pipeline=[
+                    PlannedNode(module_id="segment_repeat_based", params={}),
+                    PlannedNode(
+                        module_id="autocorrelation_period_check",
+                        params={"window_ms": 1200},
+                    ),
+                    PlannedNode(
+                        module_id="cepstrum_period_check",
+                        params={"min_interval_ms": 200, "max_interval_ms": 1000},
+                    ),
+                ],
+                expected_evidence=[
+                    "repeat_interval_ms",
+                    "repeat_stability",
+                    "periodicity_confidence",
+                ],
+                stop_if=["repeat_stability >= 0.85"],
+            ),
+            PlannedExperiment(
+                hypothesis_id="periodic-alert-h1",
+                goal="Check tonal stability of the leading hypothesis",
+                preferred_chain_id="harmonic_validation_chain",
+                pipeline=[
+                    PlannedNode(module_id="spectrogram_extract", params={}),
+                    PlannedNode(
+                        module_id="f0_tracking",
+                        params={"frame_hop_ms": 20},
+                    ),
+                    PlannedNode(
+                        module_id="harmonicity_check",
+                        params={"harmonic_threshold": 0.65},
+                    ),
+                ],
+                expected_evidence=[
+                    "f0_track",
+                    "harmonic_ratio",
+                    "tonal_stability",
+                ],
+                stop_if=["tonal_stability >= 0.70"],
+            ),
+        ]
+
+        if looks_mixed:
+            experiments.append(
+                PlannedExperiment(
+                    hypothesis_id="mixed-source-h3",
+                    goal="Check whether source separation improves confidence",
+                    preferred_chain_id="mixed_source_chain",
+                    pipeline=[
+                        PlannedNode(module_id="denoise_basic", params={}),
+                        PlannedNode(
+                            module_id="source_separation_basic",
+                            params={"max_sources": 2},
+                        ),
+                        PlannedNode(
+                            module_id="reclassify_with_alt_window",
+                            params={"window_ms": 800},
+                        ),
+                    ],
+                    expected_evidence=[
+                        "mixed_source_probability",
+                        "post_separation_top_k",
+                    ],
+                    stop_if=["mixed_source_probability >= 0.60"],
+                )
+            )
+
+        return OrchestratorPlan(
+            hypotheses=hypotheses,
+            experiments=experiments,
+            stop_decision=StopDecision(
+                should_stop=False,
+                reason="Initial plan created. Probe and validation experiments should run first.",
+            ),
+            notes=[
+                "Stub orchestrator generated a plan from observation metadata and tags.",
+            ],
+        )

+ 168 - 0
backend/app/services/stub_runner_service.py

@@ -0,0 +1,168 @@
+from dataclasses import dataclass
+
+from app.schemas.conclusion import ConclusionSummary, RankedFinding
+from app.schemas.evidence import EvidenceSummary
+from app.schemas.experiment import ExperimentSummary, ScoreCard
+from app.schemas.observation import ObservationSummary
+from app.schemas.orchestration import OrchestratorPlan
+
+
+@dataclass(slots=True)
+class StubRunnerResult:
+    probe_evidence: list[EvidenceSummary]
+    experiments: list[ExperimentSummary]
+    conclusion: ConclusionSummary
+    status: str
+
+
+class StubRunnerService:
+    def run_initial_plan(
+        self,
+        *,
+        session_id: str,
+        observation: ObservationSummary,
+        plan: OrchestratorPlan,
+    ) -> StubRunnerResult:
+        periodic_hint = 0.79 if "periodic" in observation.tags else 0.68
+        tonal_hint = 0.74 if observation.duration_ms >= 4500 else 0.61
+        classifier_top_score = 0.64 if "periodic" in observation.tags else 0.55
+
+        probe_evidence = [
+            EvidenceSummary(
+                id=f"{session_id}-e1",
+                producer_module_id="energy_probe",
+                category="feature",
+                values={
+                    "rms": 0.41,
+                    "peak_count": 7,
+                    "duration_ms": observation.duration_ms,
+                },
+                confidence=0.86,
+            ),
+            EvidenceSummary(
+                id=f"{session_id}-e2",
+                producer_module_id="periodicity_probe",
+                category="pattern",
+                values={
+                    "repeat_interval_ms": 620,
+                    "stability": periodic_hint,
+                },
+                confidence=0.82,
+            ),
+            EvidenceSummary(
+                id=f"{session_id}-e3",
+                producer_module_id="baseline_sound_classifier",
+                category="classification",
+                values={
+                    "top_k": [
+                        {
+                            "label": "periodic_alert_tone",
+                            "score": classifier_top_score,
+                        },
+                        {
+                            "label": "mechanical_click_sequence",
+                            "score": 0.27,
+                        },
+                    ]
+                },
+                confidence=classifier_top_score,
+            ),
+        ]
+
+        experiments: list[ExperimentSummary] = []
+        parent_id: str | None = None
+
+        for index, planned_experiment in enumerate(plan.experiments, start=1):
+            experiment_id = f"{session_id}-x{index}"
+            title = planned_experiment.goal
+            score_total = 0.72 + (index * 0.06)
+            notes = [
+                f"Executed planned chain '{planned_experiment.preferred_chain_id or 'ad-hoc'}'."
+            ]
+
+            if planned_experiment.preferred_chain_id == "periodic_validation_chain":
+                components = {
+                    "repeat_consistency": 0.89,
+                    "pattern_clarity": periodic_hint,
+                    "resource_cost": 0.18,
+                }
+                penalties = {"mixed_source_risk": 0.05}
+                notes.append("Autocorrelation and cepstrum align on repeat interval.")
+                failure_reasons: list[str] = []
+            elif planned_experiment.preferred_chain_id == "harmonic_validation_chain":
+                components = {
+                    "tonal_stability": tonal_hint,
+                    "harmonic_ratio": 0.71,
+                    "resource_cost": 0.22,
+                }
+                penalties = {"short_clip_penalty": 0.04}
+                notes.append("Harmonic structure is present but not fully dominant.")
+                failure_reasons = []
+            else:
+                components = {
+                    "mixed_source_probability": 0.43,
+                    "post_separation_gain": 0.18,
+                    "resource_cost": 0.31,
+                }
+                penalties = {"separation_uncertainty": 0.08}
+                notes.append(
+                    "Source separation did not improve confidence enough to overtake the periodic hypothesis."
+                )
+                failure_reasons = []
+
+            experiments.append(
+                ExperimentSummary(
+                    id=experiment_id,
+                    parent_id=parent_id,
+                    hypothesis_id=planned_experiment.hypothesis_id,
+                    title=title,
+                    status="done",
+                    pipeline_summary=[
+                        node.module_id for node in planned_experiment.pipeline
+                    ],
+                    score=ScoreCard(
+                        total=round(score_total, 2),
+                        components=components,
+                        penalties=penalties,
+                        notes=notes,
+                    ),
+                    failure_reasons=failure_reasons,
+                )
+            )
+            parent_id = experiment_id
+
+        conclusion = ConclusionSummary(
+            summary=(
+                "The sample most likely contains a periodic alert-like tone rather "
+                "than an unstructured environmental event."
+            ),
+            findings=[
+                RankedFinding(
+                    label="Periodic alert tone",
+                    confidence=0.84 if "periodic" in observation.tags else 0.76,
+                    supporting_evidence=[
+                        "Repeat interval remains stable near 620 ms.",
+                        "Probe chain shows clear onset grouping.",
+                        "Validation chains favor periodic structure over diffuse events.",
+                    ],
+                    contradicting_evidence=[
+                        "Mixed-source contamination is not fully ruled out.",
+                    ],
+                )
+            ],
+            uncertainty=[
+                "The current stub runner does not yet incorporate source separation output.",
+                "A longer clip may improve confidence around tonal stability.",
+            ],
+            suggested_next_actions=[
+                "Run harmonic validation against a longer sample window.",
+                "Add a mixed-source validation branch if future captures include overlap tags.",
+            ],
+        )
+
+        return StubRunnerResult(
+            probe_evidence=probe_evidence,
+            experiments=experiments,
+            conclusion=conclusion,
+            status="reviewing",
+        )

+ 1 - 0
backend/data/sample-upload.wav

@@ -0,0 +1 @@
+hello audio

+ 5 - 0
backend/requirements.txt

@@ -0,0 +1,5 @@
+fastapi==0.116.1
+uvicorn[standard]==0.35.0
+pydantic==2.11.7
+pydantic-settings==2.10.1
+python-multipart==0.0.20

+ 332 - 0
doc/产品方向/音频优先产品方向.md

@@ -0,0 +1,332 @@
+# 音频优先产品方向
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+说明:本版为按规范整理的历史文档,正文暂保留原英文内容。
+
+## 1. Purpose
+
+This document defines a practical first product direction based on audio as the initial modality for the multimodal analysis framework.
+
+The purpose of this direction is to:
+
+- choose a narrow and achievable first modality
+- create an implementation path that is easier than RF-first or multimodal-first execution
+- support a child-friendly product direction
+- validate the framework architecture through a concrete, lower-friction application
+
+This document complements:
+
+- [多模态分析框架.md](D:/dev/TC/doc/总体架构/多模态分析框架.md)
+- [应用版图与优先级.md](D:/dev/TC/doc/总体架构/应用版图与优先级.md)
+- [算法模块与插件系统.md](D:/dev/TC/doc/平台设计/算法模块与插件系统.md)
+
+## 2. Why Audio Is a Good Starting Point
+
+Audio is a strong first modality for several reasons.
+
+### 2.1 Lower System Complexity
+
+Compared with RF, video, or full multimodal sensing:
+
+- hardware access is simpler
+- recording is easy on phones and tablets
+- file formats are straightforward
+- algorithm prototyping is faster
+- user testing is easier to organize
+
+### 2.2 Fast Feedback Loop
+
+Audio allows quick iteration:
+
+- capture a sample
+- run a pipeline
+- inspect features
+- classify or cluster events
+- compare explanations
+
+This is ideal for validating the framework's:
+
+- module registry
+- experiment runner
+- scoring engine
+- evidence model
+- AI explanation layer
+
+### 2.3 Child-Friendly Interaction
+
+Audio products can feel natural and approachable for children:
+
+- listen to the world around them
+- identify interesting sounds
+- ask the assistant what a sound might be
+- learn through friendly explanations
+
+This makes audio a better first consumer-facing modality than raw spectrum analysis.
+
+## 3. Recommended Product Framing
+
+The first product should not try to be "an app that understands every sound in the world".
+
+A better framing is:
+
+- a child-friendly sound explorer
+- a smart sound observation assistant
+- an interactive audio interpretation tool
+
+This keeps the experience educational, engaging, and credible.
+
+## 4. Recommended First Use Case
+
+The best first use case is:
+
+**Children's environmental sound identification and explanation**
+
+Examples of candidate sounds:
+
+- birds
+- rain
+- thunder
+- dog bark
+- cat meow
+- doorbell
+- car horn
+- footsteps
+- musical instruments
+- household sounds
+
+This use case is strong because:
+
+- it is easy to demonstrate
+- it has broad appeal
+- it is easier to validate than open-ended emotional interpretation
+- it is a natural fit for mobile devices as the user terminal
+
+## 5. Why Not Start With "Animal Translator"
+
+The broader idea of interpreting animal signals is compelling, but it should not be the very first shipped experience.
+
+Reasons:
+
+- the phrase "translator" invites overclaiming
+- ground truth is hard to establish
+- audio alone is often insufficient
+- multimodal context is usually needed
+
+A more credible progression is:
+
+1. environmental sound understanding
+2. simple animal sound identification
+3. animal-state interpretation with context
+4. richer cross-modal animal behavior products later
+
+## 6. Product Experience Vision
+
+The user flow should be simple:
+
+1. the child records or listens to a sound
+2. the system identifies likely sound candidates
+3. the assistant explains what it might be in child-friendly language
+4. the system shows why it made that guess
+5. the child can ask follow-up questions
+
+Example outputs:
+
+- "This sounds more like a dog bark than a doorbell."
+- "I heard short repeating bursts with strong high-frequency energy."
+- "That often happens when a dog is alert or excited."
+
+The assistant should avoid pretending to have certainty when the evidence is weak.
+
+## 7. Suggested Product Principles
+
+The product should be:
+
+- curious rather than authoritarian
+- educational rather than overly diagnostic
+- explainable rather than magical
+- safe for children
+- usable on a phone as the main terminal
+
+The product should avoid:
+
+- exaggerated certainty
+- medical or behavioral overreach
+- emotionally manipulative claims
+- claims of literal language translation
+
+## 8. Technical Scope for a First Version
+
+The first version should be intentionally constrained.
+
+Suggested capabilities:
+
+- record or import short audio clips
+- detect sound events
+- classify into a limited sound vocabulary
+- provide confidence-ranked results
+- produce child-friendly explanations
+- save observations for replay and improvement
+
+This is enough to exercise the platform without overwhelming the first implementation.
+
+## 9. Suggested First Algorithm Chain
+
+An initial audio pipeline might look like:
+
+```text
+audio_input
+-> basic_denoise
+-> framing / segmentation
+-> spectrogram extraction
+-> feature extraction
+-> sound event detection
+-> classifier
+-> evidence builder
+-> AI explanation
+```
+
+Candidate first modules:
+
+- `audio_normalize`
+- `denoise_basic`
+- `segment_energy_based`
+- `spectrogram_extract`
+- `mfcc_extract`
+- `audio_embedding`
+- `sound_event_detect`
+- `sound_classifier`
+- `similarity_search`
+- `explanation_formatter`
+
+## 10. Evidence Model for Audio
+
+The audio-first product should still follow the evidence-first design.
+
+Examples of evidence:
+
+- dominant frequency bands
+- temporal repetition pattern
+- onset shape
+- classifier top-k labels
+- similarity to known examples
+- background noise level
+- confidence spread between candidates
+
+This prevents the assistant from acting like a black box.
+
+## 11. Role of AI in the Audio Product
+
+AI should not replace the core audio pipeline.
+
+AI should:
+
+- choose or adjust candidate analysis paths
+- summarize evidence into natural language
+- respond to user questions
+- explain uncertainty
+- suggest what to record next
+
+For example:
+
+- "Try recording a longer clip."
+- "Move closer to the sound source."
+- "This result is uncertain because the clip includes overlapping background noise."
+
+## 12. Why This Product Validates the Platform
+
+An audio-first product validates the framework's most important platform concepts:
+
+- `InputSource`
+- `Observation`
+- `AlgorithmModule`
+- `Evidence`
+- `Experiment`
+- module registration
+- pipeline execution
+- scoring
+- AI explanation
+
+If these work well for audio, the same architecture can later support:
+
+- animal sound interpretation
+- machine acoustic diagnostics
+- optical pulse decoding
+- RF and spectrum workflows
+
+## 13. Mobile as the Main Terminal
+
+Audio-first execution works especially well with a phone as the user terminal.
+
+The phone can provide:
+
+- recording
+- playback
+- interaction
+- questions and answers
+- simple visual explanations
+- history of observations
+
+This reduces hardware friction for the first product and makes early testing easier.
+
+## 14. Data Strategy
+
+The first version should use a small, well-defined dataset strategy.
+
+Recommended phases:
+
+1. start with a small curated label set
+2. collect replayable clips and associated metadata
+3. track difficult or ambiguous clips
+4. refine classifier and explanation policies
+
+Avoid trying to ingest an uncontrolled universe of sounds too early.
+
+## 15. Good First Success Criteria
+
+The first version should be judged by concrete metrics such as:
+
+- correct top-1 or top-3 classification on the chosen sound vocabulary
+- explanation quality as judged by users
+- ability to distinguish similar environmental sounds
+- robustness to moderate noise
+- ease of use for parents and children
+
+These are more useful than broad claims about "understanding sound".
+
+## 16. Expansion Path
+
+A disciplined expansion path from the audio-first product might be:
+
+1. environmental sound identification
+2. richer sound categories
+3. animal sound identification
+4. animal-state interpretation with context
+5. multimodal sound + video behavior interpretation
+
+This preserves credibility while still supporting the larger long-term vision.
+
+## 17. Recommended Immediate Next Step
+
+The next engineering step after this product direction should be:
+
+- define the core shared data model
+- specialize it for audio observations and audio modules
+- build a minimal experiment runner around audio clips
+
+This creates a practical path from concept to implementation.
+
+## 18. Summary
+
+Audio is a strong first modality because it is:
+
+- easier to prototype
+- easier to test
+- easier to explain
+- compatible with mobile-first interaction
+- suitable for child-friendly product experiences
+
+The recommended first product is not a universal sound interpreter and not an animal translator.
+
+It is a child-friendly environmental sound exploration and explanation product that validates the larger multimodal framework in a disciplined way.

+ 269 - 0
doc/产品方向/音频优先移动信号实验室方案.md

@@ -0,0 +1,269 @@
+# 音频优先移动信号实验室方案
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+## 1. 目的
+
+本文档用于固化当前项目的第一阶段方向:
+
+- 以音频作为首个输入模态
+- 以手机作为主要交互终端
+- 以“信号分析移动实验室”为产品定位
+- 以“算法链 + AI 编排”为核心系统方法
+
+## 2. 产品定位
+
+本项目的第一阶段不是普通的声音识别应用,也不是单纯的对话助手。
+
+更准确的定位是:
+
+`一个以手机为终端的 AI 编排信号分析移动实验室`
+
+其核心能力包括:
+
+- 采集音频信号
+- 运行分阶段算法链
+- 形成结构化证据
+- 由 AI 提出假设、选择验证路径、分析结果并迭代
+- 输出带依据和不确定性的结论
+
+## 3. 核心原则
+
+### 3.1 算法负责分析,AI 负责编排
+
+AI 不替代算法层。
+
+算法层负责:
+
+- 预处理
+- 探测
+- 分段
+- 特征提取
+- 模式验证
+- 归因和识别
+
+AI 负责:
+
+- 读取结构化证据
+- 提出模式假设
+- 选择下一轮算法链
+- 调整参数
+- 分析实验结果
+- 输出结论和下一步建议
+
+### 3.2 结论必须来自证据链
+
+系统输出不能只是一句主观判断,而必须附带:
+
+- 支持证据
+- 反证或冲突证据
+- 置信度
+- 不确定性来源
+
+### 3.3 每次运行都视为实验
+
+每条算法链的执行都应被记录为一次实验,具备:
+
+- 输入样本
+- 使用的算法链
+- 参数
+- 输出结果
+- 评分
+- 失败原因
+- 可复现性
+
+## 4. 系统结构
+
+第一版系统建议分为五层。
+
+### 4.1 采集层
+
+职责:
+
+- 录音
+- 导入音频文件
+- 生成标准化 `Observation`
+
+### 4.2 基础探针层
+
+职责:
+
+- 先运行一批便宜、稳定、通用的基础分析
+- 生成第一批 `Evidence`
+
+典型探针:
+
+- 音量包络
+- 频谱特征
+- onset 探测
+- 周期性探测
+- 谐波性探测
+- 基础分类器 top-k
+
+### 4.3 AI 编排层
+
+职责:
+
+- 基于已有证据生成候选假设
+- 选择下一批验证算法链
+- 调整参数范围
+- 决定停止、继续还是换路
+
+### 4.4 实验执行层
+
+职责:
+
+- 执行 AI 选择的算法链
+- 输出新的 `Evidence`
+- 返回 `ScoreCard` 和 `failureReasons`
+
+### 4.5 结论层
+
+职责:
+
+- 汇总多轮实验
+- 生成结论
+- 说明依据与不确定性
+
+## 5. 算法链体系
+
+算法链必须按阶段、按信号类型、按目标进行组织,不能只有单一 pipeline。
+
+### 5.1 按阶段划分
+
+建议划分为:
+
+1. 预处理阶段
+2. 探测阶段
+3. 分段阶段
+4. 特征提取阶段
+5. 假设验证阶段
+6. 归因/识别阶段
+7. 解释与决策阶段
+
+### 5.2 按信号类型划分
+
+第一版建议先支持下列信号画像:
+
+- 连续稳态信号
+- 瞬态事件信号
+- 周期重复信号
+- 多事件组合信号
+- 混合声源信号
+- 未知/异常信号
+
+### 5.3 按目标划分
+
+不同目标应对应不同链模板:
+
+- 发现模式
+- 验证周期性
+- 验证谐波结构
+- 检查混合声源
+- 做候选分类
+- 解释异常
+
+## 6. 第一版算法链建议
+
+### 6.1 通用探针链
+
+```text
+normalize
+-> resample
+-> energy_probe
+-> spectral_probe
+-> onset_probe
+-> periodicity_probe
+```
+
+### 6.2 周期模式验证链
+
+```text
+segment
+-> autocorrelation_check
+-> cepstrum_check
+-> repeat_interval_estimate
+```
+
+### 6.3 谐波结构验证链
+
+```text
+spectrogram
+-> f0_tracking
+-> harmonicity_check
+-> tonal_stability_check
+```
+
+### 6.4 瞬态事件分析链
+
+```text
+onset_detect
+-> transient_density
+-> event_cluster
+-> template_match
+```
+
+### 6.5 混合/异常分析链
+
+```text
+denoise
+-> separation_basic
+-> embedding_extract
+-> anomaly_distance_check
+-> alt_classifier
+```
+
+## 7. 技术栈建议
+
+当前方向建议如下:
+
+- 手机终端:Flutter
+- 本地算法核心:Rust
+- Flutter 与本地核心集成方式:FFI
+- AI 编排模型:Gemma 4
+- Gemma 4 部署位置:远端主机或边缘服务
+
+该组合的原因是:
+
+- Flutter 适合作为跨平台移动终端
+- Rust 适合承载可复用、可移植的底层算法模块
+- Gemma 4 更适合做编排与分析,而非手机本地重推理核心
+
+## 8. 第一版工程重点
+
+第一阶段最重要的不是界面,而是底层实验能力建设。
+
+优先顺序建议为:
+
+1. 定义核心数据模型
+2. 建立模块注册表
+3. 建立算法链模板目录
+4. 实现基础探针链
+5. 实现实验执行器
+6. 接入 AI 编排层
+7. 最后再完善移动端交互体验
+
+## 9. 后续文档建议
+
+后续优先补齐以下文档:
+
+- `核心数据模型(中文)`
+- `算法链分层设计`
+- `模块注册表设计`
+- `实验执行器接口规范`
+- `AI 编排输入输出规范`
+- `Flutter 终端架构设计`
+
+## 10. 结论
+
+本项目第一阶段应明确为:
+
+- 音频优先
+- 手机终端优先
+- 算法链优先
+- AI 编排优先
+- 证据链和实验记录优先
+
+这使项目从一开始就不是普通音频应用,而是一个可逐步扩展到更多模态的移动信号分析实验室原型。
+

+ 142 - 0
doc/实施规划/第一阶段实施路线图.md

@@ -0,0 +1,142 @@
+# 第一阶段实施路线图
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+## 1. 目的
+
+本文档用于定义第一阶段的实施顺序,确保项目从文档设计逐步落到可运行的最小系统,而不是长期停留在概念层。
+
+## 2. 第一阶段目标
+
+第一阶段的目标不是做完全部能力,而是跑通最小闭环:
+
+- 手机录入音频
+- 本地跑基础探针
+- 远端 AI 制定实验计划
+- 实验执行器执行验证链
+- 系统返回结构化结论
+
+## 3. 实施原则
+
+- 先打通主链路,再扩展能力
+- 先保证结构化和可复现,再追求模型聪明程度
+- 先保证实验闭环,再追求视觉精细度
+
+## 4. 里程碑划分
+
+### 4.1 里程碑一:核心模型与文档固化
+
+交付物:
+
+- 核心数据模型
+- 算法链分层设计
+- 模块注册表设计
+- 实验执行器设计
+- AI 编排接口规范
+
+### 4.2 里程碑二:本地探针引擎 MVP
+
+交付物:
+
+- Rust 本地探针库
+- Flutter FFI 桥接
+- 通用探针链可运行
+- 初始 evidence 可生成
+
+### 4.3 里程碑三:注册表与执行器 MVP
+
+交付物:
+
+- 模块注册信息
+- 链模板注册信息
+- 实验计划校验
+- 串行实验执行
+- 实验记录持久化
+
+### 4.4 里程碑四:AI 编排闭环 MVP
+
+交付物:
+
+- AI 输入构造
+- AI 输出 schema 校验
+- 假设生成
+- 下一轮实验规划
+- 停止条件初版
+
+### 4.5 里程碑五:Flutter 终端 MVP
+
+交付物:
+
+- 录音入口
+- 样本详情页
+- 实验过程页
+- 结论页
+- 历史记录页
+
+## 5. 第一批必须先实现的能力
+
+### 5.1 数据对象
+
+- `Observation`
+- `Evidence`
+- `Experiment`
+- `ScoreCard`
+- `ExperimentPlan`
+
+### 5.2 第一批模块
+
+- `audio_normalize`
+- `mono_convert`
+- `resample_16k`
+- `energy_probe`
+- `spectral_probe`
+- `onset_probe`
+- `periodicity_probe`
+- `autocorrelation_period_check`
+- `f0_tracking`
+- `baseline_sound_classifier`
+
+### 5.3 第一批链模板
+
+- 通用探针链
+- 周期模式验证链
+- 谐波结构验证链
+- 异常判断链
+
+## 6. 第一阶段暂不做的内容
+
+- 完整离线模式
+- 大规模插件市场
+- 多模态同步分析
+- 复杂并行调度
+- 高级 UI 视觉包装
+- 自动修改底层算法代码
+
+## 7. 推荐实现顺序
+
+1. 建仓库代码骨架
+2. 落 schema 和类型定义
+3. 实现本地探针引擎
+4. 实现注册表加载与校验
+5. 实现实验执行器
+6. 接 AI 编排接口
+7. 接 Flutter 终端页面
+8. 跑通完整样本闭环
+
+## 8. 第一阶段成功标准
+
+第一阶段完成时,系统至少应能做到:
+
+- 对一段音频生成初始 probe evidence
+- 由 AI 给出至少一个可信假设
+- 由 AI 选择至少一条验证链
+- 执行器完成至少一轮验证实验
+- 输出带依据和不确定性的结论
+
+## 9. 结论
+
+第一阶段的关键不是“做很多”,而是“把最小实验闭环做实”。
+
+只要这一闭环打通,后续扩展算法链、扩展模型能力、扩展模态都会更顺。
+

+ 241 - 0
doc/平台设计/AI分析员定位与职责.md

@@ -0,0 +1,241 @@
+# AI分析员定位与职责
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+## 1. 目的
+
+本文档用于明确本项目中 AI 的真实角色、职责边界和系统位置,避免后续设计时把 AI 错误地当成算法替代品或单纯的聊天层。
+
+## 2. 核心定义
+
+在本项目中,AI 替代的不是算法本身,而是传统信号分析流程中的使用者。
+
+更准确地说,AI 替代的是下列角色的主要工作:
+
+- 仪器使用者
+- 实验操作员
+- 分析员
+- 初级到中级诊断工程师
+
+因此,AI 在系统中的定位应定义为:
+
+`AI分析员`
+
+其本质作用是:
+
+- 观察已有结果
+- 提出模式假设
+- 选择分析工具
+- 设计验证步骤
+- 分析实验结果
+- 决定下一步动作
+- 最终形成带依据的结论
+
+## 3. AI 不替代什么
+
+AI 不直接替代底层算法模块。
+
+AI 不应直接充当:
+
+- 传感器
+- DSP 内核
+- 信号预处理模块
+- 特征提取模块
+- 分类器本体
+- 检测器本体
+- 分帧器本体
+
+这些能力仍由确定性算法、统计方法和专门模型模块承担。
+
+## 4. AI 替代的具体工作
+
+在没有 AI 的情况下,使用者通常需要人工完成以下工作:
+
+1. 观察原始输入和初始分析结果
+2. 判断可能存在的模式
+3. 选择合适算法
+4. 设定参数范围
+5. 运行验证
+6. 分析输出
+7. 调整方案并重复实验
+8. 评估可信度
+9. 输出结论
+
+本项目的目标是把这套流程尽可能交给 AI 完成。
+
+## 5. AI分析员的职责
+
+### 5.1 初始研判
+
+AI 应能够读取样本元信息和基础探针结果,对当前输入做初步判断。
+
+例如:
+
+- 当前信号更像稳态信号还是瞬态事件
+- 是否存在明显周期性
+- 是否疑似多声源混合
+- 是否更像未知异常样本
+
+### 5.2 假设生成
+
+AI 应能够基于已有证据提出候选假设。
+
+例如:
+
+- 存在重复脉冲模式
+- 存在稳定窄带成分
+- 存在多个事件模板
+- 当前片段不适合直接分类,应先分离或重分段
+
+### 5.3 工具选择
+
+AI 应能够从模块注册表和算法链模板目录中选择合适工具。
+
+这意味着 AI 要具备:
+
+- 识别不同算法链适用场景的能力
+- 理解不同阶段算法用途的能力
+- 在成本、收益和风险之间做权衡的能力
+
+### 5.4 实验设计
+
+AI 应能够设计下一轮实验。
+
+包括但不限于:
+
+- 选择哪条算法链先运行
+- 选择哪些参数可调
+- 先做低成本验证还是重型验证
+- 是否需要对照实验
+- 是否需要更多样本
+
+### 5.5 结果分析
+
+AI 应能够读取实验输出并做结构化分析。
+
+包括:
+
+- 哪个假设得到支持
+- 哪个假设被削弱或证伪
+- 当前失败更可能发生在哪一阶段
+- 哪种调整更值得优先尝试
+
+### 5.6 结论生成
+
+AI 应能够生成最终或阶段性结论。
+
+结论必须包括:
+
+- 核心判断
+- 支持证据
+- 冲突证据
+- 置信度
+- 不确定性说明
+- 建议下一步
+
+## 6. AI分析员的权限边界
+
+AI 可以:
+
+- 读取结构化证据
+- 读取模块元数据
+- 选择已注册算法链
+- 调整允许范围内的参数
+- 决定实验顺序
+- 请求更多数据
+- 生成结论和解释
+
+AI 不可以:
+
+- 绕过模块注册表直接调用未登记代码
+- 绕过信任策略加载未知模块
+- 直接修改底层生产算法实现
+- 用自由文本替代结构化评分与证据
+- 在证据不足时伪装成确定结论
+
+## 7. AI分析员与系统其他部分的关系
+
+### 7.1 与用户的关系
+
+用户不再直接拼装复杂算法链。
+
+用户主要负责:
+
+- 提供样本
+- 提供目标
+- 提供高层约束
+- 查看结果
+
+AI 负责把用户目标转化为实验流程。
+
+### 7.2 与算法链的关系
+
+算法链是 AI 的实验工具箱。
+
+AI 不创造分析原理本身,但会:
+
+- 选择链模板
+- 组合模块
+- 调整参数
+- 判断何时切换链路
+
+### 7.3 与实验执行器的关系
+
+实验执行器负责按计划执行。
+
+AI 负责决定:
+
+- 执行什么
+- 为什么执行
+- 执行后如何解释
+
+### 7.4 与证据模型的关系
+
+证据模型是 AI分析员的工作依据。
+
+AI 的判断应建立在:
+
+- `Evidence`
+- `ScoreCard`
+- `failureReasons`
+- 历史实验记录
+
+而不是建立在不可追踪的自由发挥之上。
+
+## 8. 对系统架构的影响
+
+一旦明确 AI 替代的是使用者,系统就必须围绕“自动化分析流程”来设计,而不是围绕“单次分类”来设计。
+
+这意味着系统重点应落在:
+
+- 模块注册表
+- 算法链体系
+- 实验模型
+- 证据模型
+- 评分机制
+- AI 决策输入输出规范
+
+而不仅仅是一个模型接口。
+
+## 9. 对产品形态的影响
+
+这一定义意味着本项目的产品本质不是普通识别应用,而是:
+
+- 移动信号分析实验室
+- AI 驱动的自动化实验分析终端
+- 面向音频起步、可扩展到更多模态的 tricorder 原型
+
+## 10. 结论
+
+本项目中的 AI 不负责替代底层算法,而负责替代传统分析流程中的使用者。
+
+因此,AI 的正确角色是:
+
+- 分析员
+- 操作员
+- 实验设计者
+- 结果解释者
+
+系统的核心目标不是让用户自己拼装算法链,而是让 AI 接管这套专业操作流程,并在结构化证据基础上得出可信结论。
+

+ 301 - 0
doc/平台设计/AI编排输入输出规范.md

@@ -0,0 +1,301 @@
+# AI编排输入输出规范
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+## 1. 目的
+
+本文档用于定义 AI分析员与系统其他部分之间的标准输入输出结构,确保 AI 编排过程具备:
+
+- 明确输入边界
+- 明确输出结构
+- 可校验
+- 可复现
+- 可被实验执行器安全消费
+
+## 2. 核心原则
+
+### 2.1 AI 读取结构化上下文,不直接依赖隐式状态
+
+AI分析员的输入应来自显式对象,而不是隐藏在代码、日志或会话上下文中的零散信息。
+
+### 2.2 AI 输出必须结构化
+
+AI 输出不能只是自由文本分析。
+
+AI 必须输出:
+
+- 假设
+- 实验计划
+- 终止或继续判断
+- 结论
+
+### 2.3 AI 输出不等于最终执行结果
+
+AI 负责规划,不负责宣称计划已经执行。
+
+执行是否合法、是否成功,由实验执行器决定并返回。
+
+## 3. AI 输入分类
+
+AI分析员输入建议分为四类:
+
+1. 观测样本摘要
+2. 已有证据
+3. 可用工具目录
+4. 历史实验结果
+
+## 4. AI 初始输入
+
+当系统刚接收到一个样本、尚未开始复杂分析时,应向 AI 提供初始输入。
+
+建议结构:
+
+```ts
+type OrchestratorInput = {
+  observation: ObservationSummary
+  probeEvidence: EvidenceSummary[]
+  availableChains: ChainTemplateSummary[]
+  availableModules?: ModuleSummary[]
+  priorExperiments: ExperimentSummary[]
+  budget: AnalysisBudget
+  mode: "initial_analysis" | "iterative_analysis" | "final_review"
+}
+```
+
+## 5. 输入对象定义
+
+### 5.1 ObservationSummary
+
+```ts
+type ObservationSummary = {
+  id: string
+  modality: "audio"
+  durationMs: number
+  sampleRate: number
+  channels: number
+  tags: string[]
+  captureMetadata?: Record<string, unknown>
+}
+```
+
+### 5.2 EvidenceSummary
+
+```ts
+type EvidenceSummary = {
+  id: string
+  producerModuleId?: string
+  category: "feature" | "pattern" | "classification" | "anomaly"
+  values: Record<string, unknown>
+  confidence: number
+}
+```
+
+### 5.3 ChainTemplateSummary
+
+```ts
+type ChainTemplateSummary = {
+  id: string
+  name: string
+  purpose: string
+  signalProfiles: string[]
+  goals: string[]
+  costClass: "low" | "medium" | "high"
+}
+```
+
+### 5.4 ModuleSummary
+
+```ts
+type ModuleSummary = {
+  id: string
+  stage: string
+  signalProfiles: string[]
+  goals: string[]
+  inputFormat: string
+  outputFormat: string
+  tunableParams: string[]
+}
+```
+
+### 5.5 ExperimentSummary
+
+```ts
+type ExperimentSummary = {
+  id: string
+  parentId?: string
+  hypothesisId?: string
+  pipelineSummary: string[]
+  scoreTotal: number
+  failureReasons: string[]
+  keyEvidence: string[]
+}
+```
+
+### 5.6 AnalysisBudget
+
+```ts
+type AnalysisBudget = {
+  maxExperiments: number
+  maxDepth: number
+  latencyBudgetMs?: number
+  preferLocalFirst?: boolean
+}
+```
+
+## 6. AI 第一类输出:假设与实验计划
+
+AI 在大多数轮次都应输出“候选假设 + 下一轮实验计划”。
+
+建议结构:
+
+```ts
+type OrchestratorPlan = {
+  hypotheses: Hypothesis[]
+  experiments: PlannedExperiment[]
+  stopDecision: StopDecision
+  notes?: string[]
+}
+```
+
+### 6.1 Hypothesis
+
+```ts
+type Hypothesis = {
+  id: string
+  label: string
+  rationale: string
+  confidence: number
+  relatedSignalProfiles: string[]
+}
+```
+
+### 6.2 PlannedExperiment
+
+```ts
+type PlannedExperiment = {
+  hypothesisId?: string
+  goal: string
+  preferredChainId?: string
+  pipeline: PlannedNode[]
+  expectedEvidence: string[]
+  stopIf: string[]
+}
+```
+
+```ts
+type PlannedNode = {
+  moduleId: string
+  params: Record<string, unknown>
+}
+```
+
+### 6.3 StopDecision
+
+```ts
+type StopDecision = {
+  shouldStop: boolean
+  reason: string
+}
+```
+
+## 7. AI 第二类输出:阶段性结论
+
+当证据已足够但尚未进入最终汇报阶段时,AI 可以输出阶段性结论。
+
+建议结构:
+
+```ts
+type InterimConclusion = {
+  summary: string
+  topHypotheses: RankedFinding[]
+  unresolvedQuestions: string[]
+  suggestedNextActions: string[]
+}
+```
+
+```ts
+type RankedFinding = {
+  label: string
+  confidence: number
+  supportingEvidence: string[]
+  contradictingEvidence: string[]
+}
+```
+
+## 8. AI 第三类输出:最终结论
+
+当 AI 判断分析可以结束时,应输出最终结论对象。
+
+建议结构:
+
+```ts
+type FinalConclusion = {
+  summary: string
+  findings: RankedFinding[]
+  experimentsRun: string[]
+  uncertainty: string[]
+  suggestedNextActions: string[]
+}
+```
+
+## 9. AI 不应输出的内容
+
+AI 不应输出:
+
+- 未注册模块的调用计划
+- 超出参数范围的计划
+- 宣称“已执行成功”但实际未执行的描述
+- 缺乏证据支撑的强确定性结论
+- 与输入结构无关的随意发挥
+
+## 10. 实验执行器的消费规则
+
+实验执行器在消费 AI 输出时,应只读取结构化部分。
+
+推荐规则:
+
+1. 忽略自由解释性文本中的任何执行指令
+2. 只解析 `experiments` 字段中的节点信息
+3. 再次用注册表校验合法性
+4. 不合法则拒绝执行
+
+## 11. 多轮分析循环
+
+建议标准循环如下:
+
+1. 系统生成 `OrchestratorInput`
+2. AI 输出 `OrchestratorPlan`
+3. 执行器运行计划中的实验
+4. 系统汇总新证据和新实验摘要
+5. 下一轮 AI 再次读取更新后的输入
+6. 直到 AI 输出 `shouldStop = true`
+7. AI 生成 `FinalConclusion`
+
+## 12. 停止条件建议
+
+AI 可以在下列情况下建议停止:
+
+- 已达到预算上限
+- 最优假设明显领先
+- 后续实验预期收益很低
+- 连续多轮无明显改进
+- 当前输入质量不足,继续实验意义不大
+
+## 13. 第一版实现建议
+
+第一版建议使用严格 JSON 输出,而不是混合自由文本格式。
+
+建议做法:
+
+- 使用固定 schema
+- 使用 schema 校验
+- 输出失败时要求 AI 重新生成
+- 把解释文本限制为附属字段
+
+## 14. 结论
+
+AI 编排层的价值不在于“会说”,而在于“能以结构化方式规划和迭代实验”。
+
+因此第一版必须把 AI 输入输出规范固化为正式接口,而不是依赖随意 prompt 和自由回答。
+

+ 255 - 0
doc/平台设计/Flutter终端架构设计.md

@@ -0,0 +1,255 @@
+# Flutter终端架构设计
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+## 1. 目的
+
+本文档用于定义本项目移动终端的第一版架构,明确 Flutter 在系统中的职责、分层方式和与本地算法核心、远端服务之间的边界。
+
+## 2. 终端定位
+
+本项目中的 Flutter 终端不是单纯展示层,而是“移动实验室终端”。
+
+它需要承担:
+
+- 采集音频
+- 展示观测结果
+- 展示实验树
+- 展示证据和结论
+- 发起分析流程
+- 管理本地会话和样本历史
+
+但它不应承担:
+
+- 重型 DSP 主计算
+- 重型声源分离
+- Gemma 4 主编排推理
+
+## 3. 技术栈建议
+
+第一版建议:
+
+- 框架:Flutter
+- 语言:Dart
+- 状态管理:Riverpod
+- 路由:go_router
+- 本地数据库:SQLite
+- 网络通信:HTTP + WebSocket
+- 本地算法核心:Rust
+- Flutter 与 Rust 集成:FFI
+
+## 4. 分层结构
+
+建议分为五层:
+
+1. 表现层
+2. 交互状态层
+3. 应用服务层
+4. 平台能力层
+5. 本地算法桥接层
+
+## 5. 表现层
+
+职责:
+
+- 页面 UI
+- 图形化结果展示
+- 操作入口
+
+第一版建议页面:
+
+- 首页
+- 录音页
+- 样本详情页
+- 实验过程页
+- 结果结论页
+- 历史记录页
+- 设置页
+
+## 6. 交互状态层
+
+职责:
+
+- 管理页面状态
+- 管理分析任务状态
+- 管理实验树展示状态
+
+建议以 feature 为单位组织 provider,而不是按技术层随意分散。
+
+## 7. 应用服务层
+
+职责:
+
+- 协调录音、上传、分析启动、结果轮询、历史查询
+- 封装对本地数据库和远端服务的访问
+
+建议拆分为:
+
+- `ObservationService`
+- `AnalysisSessionService`
+- `ExperimentService`
+- `ConclusionService`
+- `SettingsService`
+
+## 8. 平台能力层
+
+职责:
+
+- 麦克风录音
+- 文件导入
+- 权限管理
+- 后台任务
+- 网络可用性检测
+
+这一层可使用插件,也可在需要时补自定义平台通道。
+
+## 9. 本地算法桥接层
+
+职责:
+
+- 调用 Rust 本地探针算法
+- 管理输入输出序列化
+- 将结果映射回 Dart 侧模型
+
+建议第一版仅在本地桥接层承载:
+
+- normalize
+- resample
+- energy probe
+- spectral probe
+- onset probe
+- periodicity probe
+
+## 10. 本地与远端边界
+
+### 10.1 本地优先处理
+
+建议本地先做:
+
+- 录音预处理
+- 通用探针链
+- 初始 evidence 生成
+
+### 10.2 远端处理
+
+建议远端负责:
+
+- Gemma 4 编排
+- 重型验证链
+- 多轮实验管理
+- 最终结论整合
+
+## 11. 数据流
+
+建议第一版数据流如下:
+
+1. 用户录音或导入音频
+2. Flutter 生成 `Observation`
+3. 本地 Rust 探针引擎生成初始 `Evidence`
+4. Flutter 将样本摘要和 evidence 发给远端
+5. 远端 AI 编排并返回实验计划
+6. 远端或本地执行实验
+7. Flutter 展示实验过程、证据和结果
+
+## 12. 页面重点
+
+### 12.1 首页
+
+重点展示:
+
+- 开始分析
+- 最近样本
+- 当前分析任务
+
+### 12.2 样本详情页
+
+重点展示:
+
+- 波形摘要
+- 录音信息
+- 初始 probe evidence
+
+### 12.3 实验过程页
+
+重点展示:
+
+- 当前假设
+- 已执行实验
+- 分数变化
+- 失败原因
+
+### 12.4 结果页
+
+重点展示:
+
+- 结论摘要
+- 支持证据
+- 冲突证据
+- 不确定性
+- 下一步建议
+
+## 13. 终端状态模型建议
+
+建议明确区分下列状态:
+
+- `idle`
+- `recording`
+- `probing`
+- `planning`
+- `executing`
+- `reviewing`
+- `completed`
+- `failed`
+
+终端不应把整个分析过程只表示成一个模糊的 loading 状态。
+
+## 14. 第一版目录建议
+
+```text
+lib/
+  app/
+  features/
+    capture/
+    observation/
+    analysis/
+    experiment/
+    conclusion/
+    history/
+    settings/
+  services/
+  data/
+  ffi/
+  routing/
+```
+
+## 15. 第一版实现重点
+
+第一版应优先保证:
+
+- 录音稳定
+- 本地探针可跑
+- 远端编排闭环打通
+- 实验过程可视化
+- 结论结构清晰
+
+第一版不应优先追求:
+
+- 复杂视觉设计
+- 离线全功能
+- 大量插件化能力
+- 多平台差异细节优化
+
+## 16. 结论
+
+Flutter 在本项目中的正确角色是移动实验室终端。
+
+它负责:
+
+- 采集
+- 展示
+- 会话管理
+- 分析流程可视化
+
+而本地 Rust 和远端 AI 服务分别承担基础探针与高级编排能力。
+

+ 183 - 0
doc/平台设计/后端技术栈方案.md

@@ -0,0 +1,183 @@
+# 后端技术栈方案
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+## 1. 目的
+
+本文档用于定义本项目第一阶段的后端技术栈选择,并说明每一层技术的职责边界与选择理由。
+
+## 2. 设计目标
+
+后端技术栈应满足以下要求:
+
+- 能支撑 AI 编排工作流
+- 能支撑算法链执行
+- 能支撑结构化实验记录
+- 能与 Flutter 终端稳定通信
+- 能支持后续扩展到更多模态和更多实验类型
+
+## 3. 总体建议
+
+第一阶段建议采用混合后端架构:
+
+- API 层:Python + FastAPI
+- AI 编排层:Python
+- 算法执行层:Rust
+- 模型服务层:vLLM + Gemma 4
+- 数据存储层:PostgreSQL
+
+## 4. 为什么不建议单语言统一
+
+如果后端全部使用单一语言,会在某一侧明显吃亏:
+
+- 全 Dart:模型编排与算法生态不占优
+- 全 Rust:第一阶段开发速度较慢,模型编排迭代成本高
+- 全 Python:算法执行层和本地可复用核心不够理想
+
+因此第一阶段更合理的做法是:
+
+- 用 Python 负责“快迭代、强表达”的编排层
+- 用 Rust 负责“高性能、可复用”的算法执行层
+
+## 5. API 层选型:FastAPI
+
+### 5.1 职责
+
+API 层负责:
+
+- Flutter 终端访问入口
+- 音频样本上传
+- 分析任务发起
+- 实验过程查询
+- 结论查询
+- WebSocket 实时状态推送
+
+### 5.2 选择理由
+
+FastAPI 适合第一阶段,原因包括:
+
+- 开发效率高
+- 类型与 schema 表达清晰
+- 适合结构化 API
+- 适合快速构建内部平台接口
+- 和 Python 编排层天然协同
+
+## 6. AI 编排层选型:Python 服务
+
+### 6.1 职责
+
+AI 编排层负责:
+
+- 读取 `Observation`、`Evidence`、`ExperimentSummary`
+- 构造 AI 输入
+- 调用 Gemma 4
+- 生成结构化 `OrchestratorPlan`
+- 输出阶段性结论和最终结论
+
+### 6.2 选择理由
+
+Python 适合这一层,原因包括:
+
+- LLM 适配生态成熟
+- JSON schema 和结构化输出处理方便
+- prompt 与编排策略迭代速度快
+- 更适合快速试错与优化
+
+## 7. 算法执行层选型:Rust
+
+### 7.1 职责
+
+算法执行层负责:
+
+- 执行本地和远端算法链
+- 跑基础探针和验证模块
+- 生成结构化证据
+- 生成评分与失败原因
+
+### 7.2 选择理由
+
+Rust 适合这一层,原因包括:
+
+- 性能稳定
+- 便于控制资源消耗
+- 适合做可复用核心库
+- 可与 Flutter 终端本地核心共享部分实现
+
+## 8. 模型服务层选型:vLLM + Gemma 4
+
+### 8.1 职责
+
+模型服务层负责:
+
+- 承载 Gemma 4
+- 为 AI 编排层提供稳定推理接口
+
+### 8.2 选择理由
+
+第一阶段建议把 Gemma 4 放在后端,而不是手机本地,原因包括:
+
+- 编排任务比终端展示更适合后端集中运行
+- 便于统一模型版本和行为
+- 便于控制成本与日志
+- 便于后续扩展到更复杂实验循环
+
+## 9. 数据存储层选型:PostgreSQL
+
+### 9.1 职责
+
+数据存储层负责保存:
+
+- Observation 元数据
+- Evidence
+- Experiment
+- ScoreCard
+- Conclusion
+- 会话状态
+
+### 9.2 选择理由
+
+PostgreSQL 适合本项目第一阶段,原因包括:
+
+- 结构化数据支持成熟
+- 事务与查询能力稳定
+- 适合实验树和状态流转管理
+- `jsonb` 适合保存部分灵活证据结构
+
+## 10. 第一阶段暂不引入的基础设施
+
+第一阶段建议暂不引入:
+
+- 复杂微服务网格
+- 大规模消息中间件
+- 分布式工作流平台
+- 多数据库混搭
+
+原因:
+
+- 当前阶段重点是跑通最小实验闭环
+- 过早引入复杂基础设施会拖慢主线
+
+## 11. 第一阶段推荐后端结构
+
+```text
+Flutter App
+  -> FastAPI API
+      -> Python Orchestrator
+      -> Rust Experiment Runner
+      -> PostgreSQL
+      -> vLLM (Gemma 4)
+```
+
+## 12. 结论
+
+第一阶段后端建议采用:
+
+- `FastAPI` 作为 API 与会话入口
+- `Python` 作为 AI 编排层
+- `Rust` 作为算法执行层
+- `vLLM + Gemma 4` 作为模型服务层
+- `PostgreSQL` 作为实验数据存储层
+
+该方案在开发效率、性能、结构清晰度和后续扩展性之间较为平衡。
+

+ 175 - 0
doc/平台设计/后端服务拆分设计.md

@@ -0,0 +1,175 @@
+# 后端服务拆分设计
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+## 1. 目的
+
+本文档用于定义第一阶段后端服务的拆分方式,明确各服务的职责、边界和协作关系。
+
+## 2. 拆分原则
+
+第一阶段的服务拆分应遵循以下原则:
+
+- 按职责拆分,而不是按技术炫技拆分
+- 先保证主链路清晰,再考虑进一步解耦
+- 能单体时不强拆,能边界清晰时不混杂
+
+## 3. 第一阶段服务建议
+
+第一阶段建议逻辑上拆为四个核心部分:
+
+1. API 服务
+2. AI 编排服务
+3. 实验执行服务
+4. 模型服务
+
+其中:
+
+- API 服务与 AI 编排服务在第一阶段可以部署在同一 Python 进程或同一应用中
+- 模型服务可以独立部署
+- 实验执行服务建议独立出来
+
+## 4. API 服务
+
+### 4.1 职责
+
+API 服务负责:
+
+- 接收终端请求
+- 鉴权与会话入口
+- Observation 创建
+- 分析任务创建
+- 查询 Experiment 和 Conclusion
+- 推送状态变更
+
+### 4.2 不负责
+
+API 服务不负责:
+
+- 直接实现复杂 AI 决策
+- 直接运行重型算法链
+
+## 5. AI 编排服务
+
+### 5.1 职责
+
+AI 编排服务负责:
+
+- 汇总当前上下文
+- 调用模型
+- 生成假设
+- 生成实验计划
+- 生成阶段性结论和最终结论
+
+### 5.2 输入
+
+- Observation 摘要
+- Evidence 列表
+- Experiment 历史摘要
+- 可用链模板与模块摘要
+
+### 5.3 输出
+
+- `OrchestratorPlan`
+- `InterimConclusion`
+- `FinalConclusion`
+
+## 6. 实验执行服务
+
+### 6.1 职责
+
+实验执行服务负责:
+
+- 读取实验计划
+- 用注册表校验计划
+- 调用算法模块
+- 收集中间输出
+- 生成 `Evidence`
+- 生成 `ScoreCard`
+- 记录 `failureReasons`
+
+### 6.2 不负责
+
+实验执行服务不负责:
+
+- 解释业务意义
+- 直接面向用户输出最终结论
+
+## 7. 模型服务
+
+### 7.1 职责
+
+模型服务负责:
+
+- 托管 Gemma 4
+- 提供稳定推理接口
+- 统一模型版本和配置
+
+### 7.2 与 AI 编排服务的关系
+
+AI 编排服务是模型服务的调用方。
+
+模型服务不理解实验业务,只负责模型推理。
+
+## 8. 存储层职责
+
+虽然存储层不是“服务”,但它在拆分设计中非常关键。
+
+建议统一保存:
+
+- Observation
+- Evidence
+- Experiment
+- Conclusion
+- Session 状态
+
+这样所有服务都围绕同一批核心实体协作,而不是各自维护私有状态。
+
+## 9. 推荐协作流程
+
+推荐第一阶段流程如下:
+
+1. Flutter 终端上传样本
+2. API 服务创建 Observation 和分析任务
+3. API 服务触发本地或远端初始 probe
+4. AI 编排服务读取当前上下文,生成实验计划
+5. 实验执行服务执行计划并写回 Experiment/Evidence
+6. AI 编排服务继续判断是否进入下一轮
+7. 形成 Conclusion
+8. API 服务返回给终端
+
+## 10. 第一阶段部署建议
+
+第一阶段建议偏保守部署:
+
+- API 服务 + AI 编排服务:一起部署
+- 实验执行服务:独立部署
+- 模型服务:独立部署
+- PostgreSQL:独立实例
+
+这样可以在保持边界清晰的同时,避免第一阶段服务过多导致运维复杂。
+
+## 11. 后续可扩展方向
+
+当系统成熟后,可进一步扩展:
+
+- 独立任务队列
+- 独立调度器
+- 多执行 worker
+- 多模型路由
+- 多模态执行器拆分
+
+但这些不应成为第一阶段前置条件。
+
+## 12. 结论
+
+第一阶段后端服务拆分建议为:
+
+- API 服务
+- AI 编排服务
+- 实验执行服务
+- 模型服务
+
+其中 API 与 AI 编排可暂时合并部署,实验执行与模型服务保持独立,既保证边界清晰,也保持实现可控。
+

+ 388 - 0
doc/平台设计/实验执行器设计.md

@@ -0,0 +1,388 @@
+# 实验执行器设计
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+## 1. 目的
+
+本文档用于定义本项目中实验执行器的职责、输入输出、运行流程和记录机制。
+
+实验执行器的作用是把 AI分析员生成的实验计划真正落地为可运行、可校验、可记录、可复现的实验过程。
+
+## 2. 核心定位
+
+实验执行器不是算法仓库,也不是 AI 决策器。
+
+它在系统中的位置是:
+
+- 接收实验计划
+- 校验实验计划是否合法
+- 按计划执行算法链
+- 收集输出与运行信息
+- 生成实验记录
+- 返回结构化结果给 AI分析员
+
+可以把实验执行器理解为:
+
+`自动化实验运行底座`
+
+## 3. 核心职责
+
+实验执行器至少应承担以下职责:
+
+1. 接收实验计划
+2. 根据模块注册表校验计划
+3. 构建可执行链路
+4. 调用模块完成执行
+5. 管理中间输出
+6. 收集证据
+7. 计算评分
+8. 记录失败原因
+9. 生成标准化实验结果
+
+## 4. 为什么实验执行器必须独立存在
+
+如果没有独立实验执行器,系统会退化成:
+
+- AI 直接调代码
+- 各模块输出结构不统一
+- 无法复现一次分析过程
+- 结果不可比较
+- 失败归因不稳定
+
+独立实验执行器的意义在于:
+
+- 统一实验行为
+- 统一结果结构
+- 统一异常处理
+- 为多轮分析提供稳定基础
+
+## 5. 输入对象
+
+实验执行器的核心输入是:
+
+- `Observation`
+- `ExperimentPlan`
+- 模块注册表
+- 链模板注册表
+
+### 5.1 Observation
+
+表示本次实验所针对的样本。
+
+### 5.2 ExperimentPlan
+
+表示 AI分析员提出的一次具体实验计划。
+
+建议结构如下:
+
+```ts
+type ExperimentPlan = {
+  id: string
+  observationId: string
+  hypothesisId?: string
+  pipeline: PlannedNode[]
+  budget?: ExecutionBudget
+  notes?: string[]
+}
+```
+
+```ts
+type PlannedNode = {
+  moduleId: string
+  params: Record<string, unknown>
+}
+```
+
+```ts
+type ExecutionBudget = {
+  maxLatencyMs?: number
+  maxMemoryMb?: number
+  preferLocal?: boolean
+}
+```
+
+## 6. 运行前校验
+
+实验执行器在运行前必须做计划校验。
+
+至少包括:
+
+1. `observationId` 是否存在
+2. 所有 `moduleId` 是否已注册
+3. 节点顺序是否兼容
+4. 输入输出格式是否匹配
+5. 参数是否在允许范围内
+6. 参数是否允许 AI 调整
+7. 所需运行位置是否可用
+8. 模块是否启用
+9. 模块信任等级是否满足当前模式
+
+如果任何一项失败,应直接拒绝执行,并生成结构化错误返回。
+
+## 7. 执行模式
+
+第一版建议支持两类执行模式:
+
+### 7.1 本地执行
+
+适用于:
+
+- 轻量探针
+- 低延迟分析
+- 手机端可承受计算
+
+### 7.2 远端执行
+
+适用于:
+
+- 重型特征提取
+- 重型验证链
+- 声源分离
+- 更高成本模型调用
+
+实验执行器不一定自己完成所有计算,但必须负责协调这些执行位置。
+
+## 8. 执行流程
+
+建议标准执行流程如下:
+
+1. 读取 `Observation`
+2. 校验 `ExperimentPlan`
+3. 构造运行上下文
+4. 按顺序执行各节点
+5. 保存阶段输出
+6. 收集中间证据
+7. 汇总最终证据
+8. 计算评分
+9. 记录失败原因
+10. 生成 `Experiment`
+
+## 9. 运行上下文
+
+为了支持模块间协作,执行器应维护统一运行上下文。
+
+建议结构:
+
+```ts
+type ExecutionContext = {
+  observationId: string
+  inputRef: string
+  workingSet: Record<string, unknown>
+  intermediateOutputs: StageOutput[]
+  evidenceBuffer: Evidence[]
+  warnings: string[]
+}
+```
+
+其中:
+
+- `workingSet` 用于保存当前执行状态
+- `intermediateOutputs` 记录阶段输出
+- `evidenceBuffer` 累积本次实验证据
+- `warnings` 记录非致命问题
+
+## 10. 阶段输出管理
+
+每个节点执行后,执行器应记录输出,而不是只保留最后结果。
+
+建议结构:
+
+```ts
+type StageOutput = {
+  stage: string
+  moduleId: string
+  format: string
+  payloadRef: string
+  metadata?: Record<string, unknown>
+}
+```
+
+这有三个重要用途:
+
+- 回放和复核
+- 调试失败链路
+- 支持后续复用中间结果
+
+## 11. 证据收集机制
+
+实验执行器应负责从模块输出中收集标准化证据。
+
+第一版建议由模块返回两类内容:
+
+1. 主输出
+2. 可选证据条目
+
+执行器将这些内容统一整理为 `Evidence`。
+
+建议 `Evidence` 至少包含:
+
+- 产生它的模块
+- 所属实验
+- 分类
+- 值
+- 置信度
+- 追踪引用
+
+## 12. 评分机制
+
+实验执行器不一定拥有最复杂的评分逻辑,但必须支持标准评分接口。
+
+建议评分至少包括:
+
+- 结果质量
+- 结构稳定性
+- 模式清晰度
+- 成本惩罚
+
+建议结构:
+
+```ts
+type ScoreCard = {
+  total: number
+  components: Record<string, number>
+  penalties?: Record<string, number>
+  notes?: string[]
+}
+```
+
+评分器可以是执行器内部组件,也可以是被调用的独立服务,但结果必须由执行器统一归档。
+
+## 13. 失败归因
+
+实验执行器不能只返回“失败”。
+
+必须尽可能输出结构化失败原因,供 AI分析员后续决策使用。
+
+建议分为三类失败:
+
+### 13.1 计划非法
+
+例如:
+
+- `module_not_registered`
+- `format_mismatch`
+- `param_out_of_range`
+
+### 13.2 运行失败
+
+例如:
+
+- `remote_worker_unavailable`
+- `module_runtime_error`
+- `resource_budget_exceeded`
+
+### 13.3 结果无效或弱结果
+
+例如:
+
+- `no_clear_periodicity`
+- `unstable_segmentation`
+- `classifier_confidence_collapsed`
+- `insufficient_signal_structure`
+
+这类失败尤其重要,因为它们会直接影响 AI 的下一轮选择。
+
+## 14. 实验记录对象
+
+执行完成后,实验执行器应生成标准化 `Experiment` 对象。
+
+建议结构:
+
+```ts
+type Experiment = {
+  id: string
+  observationId: string
+  parentId?: string
+  hypothesisId?: string
+  pipeline: PipelineNode[]
+  status: "pending" | "running" | "done" | "failed" | "cancelled"
+  outputs: StageOutput[]
+  evidenceIds: string[]
+  score: ScoreCard
+  failureReasons: string[]
+  runtimeStats?: RuntimeStats
+  createdAt: string
+  startedAt?: string
+  finishedAt?: string
+}
+```
+
+```ts
+type RuntimeStats = {
+  elapsedMs: number
+  cpuMs?: number
+  peakMemoryMb?: number
+  location?: "local_mobile" | "remote_host" | "mixed"
+}
+```
+
+## 15. 父子实验关系
+
+实验执行器必须支持实验之间的继承关系。
+
+这是因为 AI分析员不会只跑一次实验,而会基于上一次结果继续调整。
+
+例如:
+
+- 第一次跑通用探针链
+- 第二次基于周期假设跑验证链
+- 第三次调整参数重跑验证链
+
+因此 `Experiment.parentId` 非常关键,它能帮助系统形成实验树。
+
+## 16. 与 AI分析员的协作方式
+
+实验执行器与 AI分析员之间应形成闭环:
+
+1. AI分析员生成实验计划
+2. 实验执行器校验并执行
+3. 实验执行器返回结构化结果
+4. AI分析员分析结果并决定下一步
+
+AI 不应直接调用模块。
+AI 应始终通过实验执行器来使用算法能力。
+
+## 17. 停止条件支持
+
+实验执行器本身不决定最终何时停止分析,但应支持停止条件相关信息的输出。
+
+例如:
+
+- 当前实验已接近预算上限
+- 连续三次参数调整无明显改善
+- 某条链的结果稳定低分
+
+这些信息可以帮助 AI分析员更理性地结束或切换分析路径。
+
+## 18. 第一版实现建议
+
+第一版实验执行器建议先做成简单、稳定、可复现的形式。
+
+建议优先支持:
+
+- 串行节点执行
+- 本地与远端两种运行位置
+- 中间输出记录
+- 基础证据收集
+- 基础评分
+- 结构化失败原因
+
+第一版可以暂不支持:
+
+- 并行执行复杂调度
+- 自动缓存重用
+- 大规模分布式实验树
+
+## 19. 结论
+
+实验执行器是本项目从“有算法模块”走向“可自动化实验分析系统”的关键一步。
+
+它的核心价值在于:
+
+- 把实验计划变成真正可运行的链路
+- 把执行结果变成结构化实验记录
+- 为 AI分析员提供可信、统一、可迭代的反馈基础
+
+没有实验执行器,AI 无法真正替代使用者完成系统化分析流程。
+

+ 449 - 0
doc/平台设计/核心数据模型.md

@@ -0,0 +1,449 @@
+# 核心数据模型
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+说明:本版为按规范整理的历史文档,正文暂保留原英文内容。
+
+## 1. Purpose
+
+This document defines the core shared data model for the multimodal analysis framework.
+
+These data structures are the foundation for:
+
+- modality integration
+- algorithm module registration
+- pipeline execution
+- experiment tracking
+- evidence fusion
+- AI orchestration
+
+This document is intended to be the first implementation-facing specification after the architecture documents.
+
+It complements:
+
+- [多模态分析框架.md](D:/dev/TC/doc/总体架构/多模态分析框架.md)
+- [算法模块与插件系统.md](D:/dev/TC/doc/平台设计/算法模块与插件系统.md)
+- [音频优先产品方向.md](D:/dev/TC/doc/产品方向/音频优先产品方向.md)
+
+## 2. Design Principles
+
+The core data model should be:
+
+- modality-agnostic at the top level
+- specific enough to support real execution
+- serializable
+- versioned
+- stable across runtime, storage, and network boundaries
+
+The first implementation should prefer simple, explicit schemas over clever abstractions.
+
+## 3. Core Entities
+
+The first version of the system should standardize five primary entities:
+
+1. `InputSource`
+2. `Observation`
+3. `AlgorithmModule`
+4. `Evidence`
+5. `Experiment`
+
+These entities should be sufficient to support:
+
+- audio-first execution
+- later RF extension
+- future multimodal expansion
+
+## 4. InputSource
+
+### 4.1 Purpose
+
+`InputSource` describes where data comes from and what it is capable of producing.
+
+Examples:
+
+- phone microphone
+- external USB microphone
+- SDR receiver
+- camera
+- photodiode board
+- file replay source
+
+### 4.2 Suggested Shape
+
+```ts
+type InputSource = {
+  id: string
+  kind: "audio" | "rf" | "video" | "optical" | "bus" | "file" | "log"
+  name: string
+  capabilities: string[]
+  deviceInfo?: Record<string, unknown>
+  supportedFormats: DataFormat[]
+  location?: SourceLocation
+  status: "available" | "busy" | "offline" | "error"
+  createdAt: string
+  updatedAt: string
+}
+```
+
+### 4.3 Notes
+
+- `InputSource` is not the captured data itself.
+- It represents a reusable source of observations.
+- Capabilities should be machine-readable whenever possible.
+
+## 5. Observation
+
+### 5.1 Purpose
+
+`Observation` is a concrete captured sample or replayable unit of data plus its context.
+
+Examples:
+
+- a 5-second audio clip
+- a segment of IQ samples
+- a short video clip
+- an imported WAV file
+
+### 5.2 Suggested Shape
+
+```ts
+type Observation = {
+  id: string
+  sourceId: string
+  modality: "audio" | "rf" | "video" | "optical" | "bus" | "log"
+  format: DataFormat
+  payloadRef: string
+  byteSize?: number
+  timeRange: {
+    start: string
+    end: string
+  }
+  captureMetadata: Record<string, unknown>
+  tags: string[]
+  createdAt: string
+}
+```
+
+### 5.3 Audio-Specific Metadata Examples
+
+For the audio-first product, `captureMetadata` may include:
+
+- sample rate
+- channels
+- bit depth
+- microphone type
+- estimated noise floor
+- device model
+
+### 5.4 Notes
+
+- `payloadRef` should point to a stable storage reference rather than embedding large binary data directly.
+- `Observation` should be replayable.
+
+## 6. DataFormat
+
+### 6.1 Purpose
+
+`DataFormat` describes the structural format flowing between modules.
+
+### 6.2 Suggested Shape
+
+```ts
+type DataFormat = {
+  family: string
+  encoding: string
+  shape?: Record<string, unknown>
+}
+```
+
+### 6.3 Examples
+
+- `{ family: "audio", encoding: "wav_pcm16" }`
+- `{ family: "audio", encoding: "spectrogram_f32" }`
+- `{ family: "rf", encoding: "complex_iq_f32" }`
+- `{ family: "signal", encoding: "soft_bits" }`
+- `{ family: "packet", encoding: "pcap_frame" }`
+
+## 7. AlgorithmModule
+
+### 7.1 Purpose
+
+`AlgorithmModule` defines a registered processing block that can be used in a pipeline.
+
+Examples:
+
+- denoiser
+- spectrogram extractor
+- MFCC extractor
+- sound event detector
+- demodulator
+- framer
+- classifier
+
+### 7.2 Suggested Shape
+
+```ts
+type AlgorithmModule = {
+  id: string
+  name: string
+  version: string
+  modality: string[]
+  stage: string
+  inputFormat: DataFormat
+  outputFormat: DataFormat
+  params: Record<string, ParamSpec>
+  metrics: string[]
+  executionModel: "in_process" | "out_of_process" | "sandboxed"
+  trustLevel: "core" | "trusted" | "partner" | "experimental" | "untrusted"
+  realtimeSafe: boolean
+  source: ModuleSource
+  dependencies?: string[]
+  createdAt: string
+  updatedAt: string
+}
+```
+
+### 7.3 ParamSpec
+
+```ts
+type ParamSpec = {
+  type: "int" | "float" | "bool" | "string" | "enum"
+  required?: boolean
+  defaultValue?: unknown
+  range?: [number, number]
+  choices?: string[]
+  description?: string
+}
+```
+
+### 7.4 ModuleSource
+
+```ts
+type ModuleSource = {
+  provider: string
+  packageName?: string
+  license?: string
+  originType: "builtin" | "pack" | "third_party" | "ai_generated"
+}
+```
+
+## 8. PipelineNode
+
+### 8.1 Purpose
+
+`PipelineNode` captures one configured module instance inside an experiment pipeline.
+
+### 8.2 Suggested Shape
+
+```ts
+type PipelineNode = {
+  moduleId: string
+  params: Record<string, unknown>
+  enabled: boolean
+}
+```
+
+### 8.3 Notes
+
+- `AlgorithmModule` is a registry definition.
+- `PipelineNode` is a concrete use of that module with specific parameters.
+
+## 9. Evidence
+
+### 9.1 Purpose
+
+`Evidence` is the structured result that AI and downstream logic reason over.
+
+It should represent facts, measurements, ranked hypotheses, or anomalies produced by algorithms.
+
+### 9.2 Suggested Shape
+
+```ts
+type Evidence = {
+  id: string
+  observationId: string
+  experimentId: string
+  producerModuleId?: string
+  category: "signal" | "feature" | "frame" | "protocol" | "semantic" | "anomaly"
+  values: Record<string, unknown>
+  confidence: number
+  scoreContribution?: number
+  traceRefs: string[]
+  createdAt: string
+}
+```
+
+### 9.3 Audio Examples
+
+Examples for audio-first execution:
+
+- dominant frequency band
+- onset count
+- top-3 sound-class predictions
+- clip similarity score
+- background noise estimate
+- uncertainty indicator
+
+### 9.4 Notes
+
+- `Evidence` should be human-reviewable and machine-consumable.
+- Confidence should always be explicit.
+
+## 10. ScoreCard
+
+### 10.1 Purpose
+
+`ScoreCard` summarizes how well an experiment performed.
+
+### 10.2 Suggested Shape
+
+```ts
+type ScoreCard = {
+  total: number
+  components: Record<string, number>
+  penalties?: Record<string, number>
+  notes?: string[]
+}
+```
+
+### 10.3 Example Components
+
+- signal_quality
+- segmentation_quality
+- classification_confidence
+- structural_consistency
+- resource_cost
+
+## 11. Experiment
+
+### 11.1 Purpose
+
+`Experiment` records one pipeline execution attempt over an observation.
+
+This is the main unit of search, replay, validation, and AI orchestration.
+
+### 11.2 Suggested Shape
+
+```ts
+type Experiment = {
+  id: string
+  observationId: string
+  parentId?: string
+  pipeline: PipelineNode[]
+  status: "pending" | "running" | "done" | "failed" | "cancelled"
+  outputs: ExperimentOutput[]
+  evidenceIds: string[]
+  score: ScoreCard
+  failureReasons: string[]
+  runtimeStats?: RuntimeStats
+  createdAt: string
+  startedAt?: string
+  finishedAt?: string
+}
+```
+
+### 11.3 ExperimentOutput
+
+```ts
+type ExperimentOutput = {
+  stage: string
+  format: DataFormat
+  payloadRef: string
+  metadata?: Record<string, unknown>
+}
+```
+
+### 11.4 RuntimeStats
+
+```ts
+type RuntimeStats = {
+  elapsedMs: number
+  cpuMs?: number
+  peakMemoryMb?: number
+}
+```
+
+## 12. Relationship Between Entities
+
+The first-order relationships are:
+
+- one `InputSource` produces many `Observation`
+- one `Observation` can have many `Experiment`
+- one `Experiment` contains many `PipelineNode`
+- one `Experiment` produces many `Evidence`
+- one `AlgorithmModule` may be used by many `PipelineNode`
+
+Simple view:
+
+```text
+InputSource
+  -> Observation
+      -> Experiment
+          -> PipelineNode
+          -> Evidence
+```
+
+## 13. Versioning and Serialization
+
+The core model should be serializable to JSON and representable in Protobuf later.
+
+Recommended rule:
+
+- first define in a language-neutral schema mindset
+- initially persist as JSON
+- promote to Protobuf once the shapes stabilize
+
+This avoids locking in transport too early while still keeping future RPC clean.
+
+## 14. First Audio-First Specialization
+
+The first implementation should specialize the generic model only where necessary.
+
+Recommended first concrete instances:
+
+- `InputSource.kind = "audio"`
+- `Observation.modality = "audio"`
+- `DataFormat.family = "audio"`
+- `AlgorithmModule.stage` values such as:
+  - `preprocess`
+  - `segment`
+  - `feature_extract`
+  - `detect`
+  - `classify`
+  - `explain`
+
+This keeps the shared model intact while letting audio move quickly.
+
+## 15. Anti-Patterns to Avoid
+
+Do not:
+
+- embed large binary payloads directly into entity objects
+- make the first model deeply modality-specific
+- hide confidence or uncertainty
+- mix registry definitions with runtime instances
+- make AI outputs unstructured free text only
+
+These would make later scaling much harder.
+
+## 16. Recommended Immediate Next Step
+
+After this model is accepted, the next implementation-facing work should be:
+
+1. define JSON schemas or Protobuf drafts for these entities
+2. define the module registry API
+3. define experiment-runner input/output contracts
+4. define an initial audio-specific module catalog
+
+## 17. Summary
+
+The first implementation should standardize the system around five entities:
+
+- `InputSource`
+- `Observation`
+- `AlgorithmModule`
+- `Evidence`
+- `Experiment`
+
+These should remain the stable backbone of the platform while modalities, algorithms, and products evolve on top of them.

+ 397 - 0
doc/平台设计/模块注册表设计.md

@@ -0,0 +1,397 @@
+# 模块注册表设计
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+## 1. 目的
+
+本文档用于定义本项目的模块注册表设计,用于支撑以下能力:
+
+- 管理可用算法模块
+- 管理可用算法链模板
+- 支持 AI分析员发现和选择工具
+- 约束参数、权限和执行边界
+- 为实验执行器提供可验证的运行依据
+
+模块注册表是本项目的重要基础设施,不是附属功能。
+
+## 2. 核心定位
+
+模块注册表是系统中的“工具目录”和“能力边界层”。
+
+其职责不是执行算法,而是回答以下问题:
+
+- 系统当前有哪些可用模块
+- 这些模块分别属于哪个阶段
+- 它们适用于哪些信号画像和目标
+- 输入输出是否兼容
+- 允许哪些参数被 AI 调整
+- 哪些模块或链模板可进入实验流程
+
+换句话说,AI分析员不能直接面对一堆裸代码,而必须通过模块注册表来理解和调用系统能力。
+
+## 3. 设计原则
+
+### 3.1 元数据优先
+
+模块是否可用,不应由使用者记忆或硬编码决定,而应由机器可读元数据决定。
+
+### 3.2 注册优先于执行
+
+任何模块在进入实验链之前,应先被注册、校验和分类。
+
+### 3.3 注册表既管理模块,也管理链模板
+
+如果只注册单模块,不注册链模板,AI分析员仍然很难高效规划实验。
+
+因此注册表中应同时存在:
+
+- 模块定义
+- 链模板定义
+
+### 3.4 AI 只能操作注册表可见能力
+
+AI分析员的可选工具集合必须来自注册表。
+
+AI 不应绕过注册表直接调用未知代码、隐藏模块或未声明参数。
+
+## 4. 注册对象
+
+第一版建议注册三类对象:
+
+1. 算法模块
+2. 算法链模板
+3. 数据格式定义
+
+## 5. 算法模块定义
+
+### 5.1 作用
+
+算法模块是算法链中的最小可组合单元。
+
+模块通常只负责一个明确步骤,例如:
+
+- 归一化
+- 分段
+- 周期检测
+- 谐波检测
+- 分类
+- 相似度检索
+
+### 5.2 建议字段
+
+```ts
+type ModuleSpec = {
+  id: string
+  name: string
+  version: string
+  stage: string
+  signalProfiles: string[]
+  goals: string[]
+  inputFormat: string
+  outputFormat: string
+  params: Record<string, ParamSpec>
+  evidenceKinds: string[]
+  failureModes: string[]
+  cost: CostSpec
+  runtime: RuntimeSpec
+  trustLevel: "core" | "trusted" | "experimental"
+  enabled: boolean
+}
+```
+
+### 5.3 字段说明
+
+- `id`
+  - 全局唯一标识
+- `name`
+  - 人类可读名称
+- `version`
+  - 模块版本
+- `stage`
+  - 所属阶段
+- `signalProfiles`
+  - 适用信号画像
+- `goals`
+  - 适用分析目标
+- `inputFormat`
+  - 输入格式标识
+- `outputFormat`
+  - 输出格式标识
+- `params`
+  - 可配置参数定义
+- `evidenceKinds`
+  - 可能产出的证据类型
+- `failureModes`
+  - 常见失败原因
+- `cost`
+  - 成本估计
+- `runtime`
+  - 执行位置和调用方式
+- `trustLevel`
+  - 信任等级
+- `enabled`
+  - 当前是否可用
+
+## 6. 参数定义
+
+模块参数必须显式声明,不能依赖 AI 或调用方猜测。
+
+建议结构:
+
+```ts
+type ParamSpec = {
+  type: "int" | "float" | "bool" | "string" | "enum"
+  required?: boolean
+  defaultValue?: unknown
+  range?: [number, number]
+  choices?: string[]
+  description?: string
+  tunableByAI: boolean
+  riskLevel?: "low" | "medium" | "high"
+}
+```
+
+关键点:
+
+- `tunableByAI` 必须明确
+- `range` 和 `choices` 必须明确
+- 对高风险参数应附带风险等级
+
+## 7. 成本与运行信息
+
+AI分析员不应只知道“能不能跑”,还应知道“跑起来代价大不大”。
+
+建议结构:
+
+```ts
+type CostSpec = {
+  cpu?: number
+  memory?: number
+  latency?: number
+  battery?: number
+}
+```
+
+```ts
+type RuntimeSpec = {
+  location: "local_mobile" | "remote_host" | "either"
+  invocation: "ffi" | "native" | "rpc"
+  realtimeSafe: boolean
+}
+```
+
+这些信息会直接影响 AI分析员的实验规划。
+
+例如:
+
+- 先跑低成本本地探针
+- 复杂分离或重型分类再交给远端
+
+## 8. 算法链模板定义
+
+### 8.1 作用
+
+链模板是对常见分析方法的标准化封装。
+
+它的意义在于:
+
+- 给 AI分析员提供高层实验工具
+- 避免每次都从零拼装所有节点
+- 让实验设计更快、更稳、更可控
+
+### 8.2 建议字段
+
+```ts
+type ChainTemplateSpec = {
+  id: string
+  name: string
+  version: string
+  purpose: string
+  signalProfiles: string[]
+  goals: string[]
+  inputFormat: string
+  nodes: ChainTemplateNode[]
+  expectedEvidenceKinds: string[]
+  typicalFailureModes: string[]
+  costClass: "low" | "medium" | "high"
+  enabled: boolean
+}
+```
+
+```ts
+type ChainTemplateNode = {
+  moduleId: string
+  required: boolean
+  presetParams?: Record<string, unknown>
+}
+```
+
+### 8.3 链模板和模块的区别
+
+- 模块是最小工具单元
+- 链模板是经过组织的实验套路
+
+AI分析员在大多数情况下应优先从链模板出发,而不是直接从裸模块拼接。
+
+## 9. 数据格式注册
+
+为了避免模块间随意拼接,输入输出格式也应进入注册表统一管理。
+
+建议格式:
+
+```ts
+type DataFormatSpec = {
+  id: string
+  family: string
+  encoding: string
+  description?: string
+}
+```
+
+示例:
+
+- `audio.wav_pcm16`
+- `audio.spectrogram_f32`
+- `audio.embedding_f32`
+- `audio.segment_list`
+- `analysis.evidence_list`
+
+## 10. 注册表应支持的查询能力
+
+模块注册表至少应支持以下查询:
+
+### 10.1 按阶段查询
+
+例如:
+
+- 找到所有 `detect` 阶段模块
+- 找到所有 `classify` 阶段模块
+
+### 10.2 按信号画像查询
+
+例如:
+
+- 找到适合 `periodic_pulse` 的模块或链模板
+
+### 10.3 按分析目标查询
+
+例如:
+
+- 找到适合 `周期验证` 的链模板
+
+### 10.4 按输入输出格式兼容性查询
+
+例如:
+
+- 查找能接在 `spectrogram_extract` 后面的模块
+
+### 10.5 按执行位置查询
+
+例如:
+
+- 本地可执行模块
+- 远端可执行模块
+
+### 10.6 按可调参数能力查询
+
+例如:
+
+- 查找哪些模块允许 AI 调整阈值参数
+
+## 11. AI 可见性与权限控制
+
+不是所有注册对象都必须对 AI分析员完全可见。
+
+建议至少区分三类:
+
+### 11.1 全可见对象
+
+- 核心稳定模块
+- 核心链模板
+
+AI 可以自由选择和调度。
+
+### 11.2 条件可见对象
+
+- 实验模块
+- 高成本模块
+- 高风险参数模块
+
+AI 需要额外条件才能使用,例如:
+
+- 仅在远端模式可见
+- 仅在指定目标下可见
+- 仅在人工授权后可见
+
+### 11.3 不可见对象
+
+- 内部测试模块
+- 已禁用模块
+- 兼容性失效模块
+
+AI 不应看到,更不应调用。
+
+## 12. 校验规则
+
+模块或链模板注册时应至少通过以下检查:
+
+1. 必填字段齐全
+2. 输入输出格式合法
+3. 参数定义完整
+4. 引用的模块存在
+5. 信任等级合法
+6. 运行位置合法
+7. 链模板的模块顺序兼容
+
+未通过校验的对象不得进入可用目录。
+
+## 13. 第一版实现建议
+
+第一版建议采用最小可行方案:
+
+- 使用本地 JSON 或 YAML 保存注册信息
+- 应用启动时加载注册表
+- 先只支持第一方模块
+- 先只支持手工维护的链模板
+- 先不做动态下载和在线安装
+
+这足以验证:
+
+- AI 是否能基于注册表选工具
+- 实验执行器是否能基于注册表构建链路
+- 是否能稳定产出结构化实验记录
+
+## 14. 与 AI分析员的接口关系
+
+模块注册表不是给用户直接使用的,而是给 AI分析员和实验执行器共同使用的。
+
+推荐关系如下:
+
+- AI分析员读取注册表,生成实验计划
+- 实验执行器根据注册表校验计划合法性
+- 若计划不合法,则拒绝执行并返回原因
+
+这样可以形成双保险:
+
+- AI 负责规划
+- 注册表负责约束
+
+## 15. 结论
+
+模块注册表是本项目的核心基础设施之一。
+
+它的价值在于:
+
+- 把算法能力变成可发现、可组合、可约束、可验证的系统资产
+- 让 AI分析员能够真正像使用者一样选择工具和设计实验
+- 防止系统退化为“模型自由发挥 + 隐式调用代码”的不透明结构
+
+第一版应优先支持:
+
+- 模块注册
+- 链模板注册
+- 参数边界声明
+- 格式兼容性校验
+- AI 可见性控制
+

+ 522 - 0
doc/平台设计/算法模块与插件系统.md

@@ -0,0 +1,522 @@
+# 算法模块与插件系统
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+说明:本版为按规范整理的历史文档,正文暂保留原英文内容。
+
+## 1. Purpose
+
+This document defines how algorithms should be packaged, discovered, loaded, validated, and distributed inside the multimodal analysis framework.
+
+This is necessary because:
+
+- the system cannot ship every possible algorithm in the core binary
+- different modalities require different algorithm families
+- the framework needs a stable way to support internal, third-party, and experimental modules
+- AI orchestration requires machine-readable metadata about available modules
+
+This document complements:
+
+- [多模态分析框架.md](D:/dev/TC/doc/总体架构/多模态分析框架.md)
+- [便携式信号分析仪架构说明.md](D:/dev/TC/doc/总体架构/便携式信号分析仪架构说明.md)
+
+## 2. Design Goals
+
+The module system should satisfy these goals:
+
+- support multiple modalities
+- allow modular growth without bloating the core application
+- expose enough metadata for AI orchestration
+- support safe experimentation
+- allow trusted and untrusted modules to coexist under different policies
+- make updates possible without rebuilding the entire application
+
+## 3. Core Principle
+
+Do not build the first version as one giant binary with every algorithm linked in.
+
+Instead, separate the system into:
+
+- core runtime
+- built-in core algorithms
+- installable algorithm packs
+- experimental modules
+
+This keeps the product maintainable and makes future platformization possible.
+
+## 4. Algorithm Source Categories
+
+Algorithms will usually come from four sources.
+
+### 4.1 First-Party Algorithms
+
+Algorithms implemented and maintained by the core product team.
+
+Examples:
+
+- core preprocessing
+- core signal quality analysis
+- scoring engine components
+- framing logic
+- protocol-adaptation utilities
+
+These should be the most trusted modules and should form the baseline product capability.
+
+### 4.2 Open-Source Algorithms
+
+Algorithms integrated from external open-source projects.
+
+Selection criteria should include:
+
+- license compatibility
+- maintenance activity
+- code quality
+- platform support
+- dependency weight
+- input/output compatibility
+- deterministic behavior under replay
+
+Open-source code should not be copied in casually.
+Every imported algorithm should be reviewed as a product dependency.
+
+### 4.3 Third-Party Partner Modules
+
+Algorithms supplied by partners, vendors, researchers, or domain teams.
+
+These are important for scaling the ecosystem but should usually be isolated behind clear packaging and trust rules.
+
+### 4.4 AI-Generated or AI-Modified Experimental Modules
+
+These may be useful for rapid experimentation, but they must be treated as experimental only.
+
+They must not enter the production path automatically.
+They should first pass:
+
+- compatibility checks
+- replay validation
+- scoring validation
+- regression tests
+
+## 5. Delivery Model
+
+The system should use a layered delivery model.
+
+### 5.1 Built-In Core
+
+Always shipped with the main application.
+
+Recommended contents:
+
+- runtime and scheduler
+- module registry
+- experiment manager
+- scoring engine
+- a small number of essential modality-specific modules
+
+The core should remain intentionally small.
+
+### 5.2 Trusted Packs
+
+Installable packs for specific domains.
+
+Examples:
+
+- RF base pack
+- audio pack
+- vision pack
+- optical decoding pack
+- industrial bus pack
+
+These can be versioned and installed independently of the main application.
+
+### 5.3 Experimental Modules
+
+Modules that are not yet promoted to trusted packs.
+
+Examples:
+
+- research algorithms
+- low-confidence ports
+- AI-generated modules
+- partner-preview modules
+
+These should run under stricter policy and default isolation.
+
+## 6. Loading Strategy
+
+Do not start with unrestricted remote code download and execution.
+
+The loading strategy should mature in phases.
+
+### 6.1 Phase 1: Local Discovery and Registration
+
+The application scans known directories and registers available modules.
+
+This is the minimum viable plugin system and should come first.
+
+### 6.2 Phase 2: Managed Pack Installation
+
+The system can install signed or trusted algorithm packs from a local or remote repository, then register them after validation.
+
+### 6.3 Phase 3: AI-Assisted Pack Recommendation
+
+AI may recommend installing additional modules when current capability is insufficient.
+However, AI should not be allowed to silently install and execute arbitrary code.
+
+The correct flow is:
+
+1. AI recommends a pack
+2. system downloads or locates it
+3. compatibility and signature checks run
+4. validation runs in sandbox or replay mode
+5. pack is promoted to installed state if approved
+
+## 7. Plugin Execution Models
+
+Different algorithm types have different isolation and performance needs.
+
+The framework should support multiple execution models.
+
+### 7.1 In-Process Native Modules
+
+Examples:
+
+- `.dll`
+- `.so`
+- direct statically linked modules
+
+Best for:
+
+- high-performance real-time stages
+- low-latency DSP
+- trusted first-party runtime-critical modules
+
+Tradeoffs:
+
+- ABI management complexity
+- crash risk if module quality is poor
+- tighter coupling with host process
+
+### 7.2 Out-of-Process Modules
+
+Modules run as separate processes and communicate via RPC, pipes, sockets, or structured IPC.
+
+Best for:
+
+- isolation
+- multi-language module support
+- crash containment
+- medium-cost experimental algorithms
+
+Tradeoffs:
+
+- IPC overhead
+- more operational complexity
+
+### 7.3 Sandboxed Modules
+
+Examples:
+
+- Wasm-based modules
+- restricted containerized workers
+- sandboxed runners
+
+Best for:
+
+- third-party modules
+- AI-generated modules
+- untrusted or semi-trusted experimentation
+
+Tradeoffs:
+
+- performance limits for very heavy real-time workloads
+- additional runtime complexity
+
+## 8. Recommended Execution Policy
+
+Recommended default policy:
+
+- real-time production-critical modules -> in-process native
+- replay and heavy analysis modules -> out-of-process
+- third-party and AI-generated modules -> sandboxed first
+
+Do not force all algorithms into one execution model.
+Different layers of the system need different safety and performance tradeoffs.
+
+## 9. Module Metadata
+
+Every algorithm module must expose machine-readable metadata.
+
+Suggested minimum shape:
+
+```json
+{
+  "name": "gfsk_demod_basic",
+  "version": "0.1.0",
+  "stage": "demod",
+  "modality": ["rf"],
+  "input_format": "complex_iq",
+  "output_format": "soft_bits",
+  "params": {
+    "symbol_rate": {
+      "type": "float",
+      "range": [1000, 1000000]
+    },
+    "deviation": {
+      "type": "float",
+      "range": [100, 500000]
+    }
+  },
+  "metrics": ["snr_estimate", "lock_stability"],
+  "realtime_safe": true,
+  "trust_level": "trusted",
+  "license": "MIT"
+}
+```
+
+This metadata is not optional.
+Without it:
+
+- the runtime cannot compose pipelines safely
+- AI cannot reason about compatibility
+- the UI cannot explain what is installed
+- pack management becomes fragile
+
+## 10. Required Metadata Fields
+
+Every module should declare:
+
+- `name`
+- `version`
+- `author`
+- `source`
+- `license`
+- `modality`
+- `stage`
+- `input_format`
+- `output_format`
+- `params`
+- `metrics`
+- `platforms`
+- `realtime_safe`
+- `trust_level`
+- `execution_model`
+
+Optional but valuable:
+
+- `hardware_requirements`
+- `estimated_cost`
+- `known_limitations`
+- `supported_sample_ranges`
+- `preferred_predecessors`
+- `preferred_successors`
+
+## 11. Pack Structure
+
+A pack should be more than a raw binary.
+
+Suggested pack contents:
+
+- manifest file
+- one or more modules
+- dependency declaration
+- optional test vectors
+- optional replay validation cases
+- optional documentation
+- signature or checksum metadata
+
+Example structure:
+
+```text
+rf-base-pack/
+  manifest.json
+  modules/
+    gfsk_demod_basic.dll
+    burst_detect.dll
+    crc_scan.dll
+  tests/
+    replay_case_01.json
+    replay_case_02.json
+  docs/
+    README.md
+  signatures/
+    sha256.txt
+```
+
+## 12. Discovery and Registration
+
+The runtime should have a deterministic registration process.
+
+Suggested startup flow:
+
+1. scan built-in modules
+2. scan installed trusted-pack directories
+3. scan experimental-module directories
+4. load manifest metadata
+5. validate schema and compatibility
+6. register modules in the module registry
+7. expose only approved modules to the orchestrator
+
+The registry should track:
+
+- module identity
+- version
+- load state
+- trust state
+- dependency state
+- runtime errors
+
+## 13. Dependency Management
+
+Modules should be able to declare dependencies on:
+
+- runtime version
+- host API version
+- other modules
+- hardware capabilities
+- external libraries
+
+The host must reject incompatible modules early.
+
+This avoids problems such as:
+
+- ABI mismatch
+- unsupported platform
+- missing runtime features
+- invalid pipeline assembly
+
+## 14. Validation Pipeline
+
+Before a module becomes usable, it should pass validation.
+
+Validation should include:
+
+- manifest schema validation
+- compatibility checks
+- signature/checksum validation where applicable
+- sandbox smoke test
+- replay validation against known samples
+- optional performance validation
+
+For trusted promotion, add:
+
+- regression comparison
+- scoring sanity checks
+- resource stability checks
+
+## 15. Trust Levels
+
+The framework should explicitly track trust levels.
+
+Suggested levels:
+
+- `core`
+- `trusted`
+- `partner`
+- `experimental`
+- `untrusted`
+
+Trust level should influence:
+
+- execution model
+- default enablement
+- visibility to AI orchestrator
+- whether use is allowed in live mode
+
+For example:
+
+- `core` and `trusted` modules can be used in live mode
+- `experimental` modules require replay or manual approval
+- `untrusted` modules are disabled by default
+
+## 16. AI Interaction Rules
+
+AI should only operate over registered metadata and installed modules.
+
+AI may:
+
+- discover available modules
+- choose among installed modules
+- request installation of a missing pack
+- recommend parameter ranges
+- recommend promoting or demoting modules based on evidence
+
+AI should not:
+
+- download and execute arbitrary code without validation
+- bypass trust policy
+- silently enable experimental modules in production
+- write new production modules directly into the live path
+
+## 17. Distribution Strategy
+
+The long-term distribution model can evolve into:
+
+- built-in modules with the main app
+- local pack installation
+- remote pack repositories
+- enterprise private registries
+- domain marketplaces
+
+But the implementation order should remain conservative:
+
+1. local pack loading
+2. signed pack installation
+3. repository-backed updates
+4. AI-assisted pack recommendation
+5. optional ecosystem / marketplace
+
+## 18. Why This Matters Strategically
+
+The algorithm module system is not just an implementation detail.
+It is one of the main leverage points for the whole platform.
+
+It enables:
+
+- independent growth of algorithm inventory
+- narrower core binaries
+- field updates without full rebuilds
+- third-party ecosystem participation
+- safer experimentation
+- AI-driven orchestration at scale
+
+This is also the foundation for turning the framework into a real platform rather than a single-device product.
+
+## 19. Recommended Immediate Starting Point
+
+The first implementation should be intentionally small.
+
+Recommended order:
+
+1. define manifest schema for modules
+2. define module registry interfaces
+3. support local module discovery from one directory
+4. support one execution model first
+5. add trust-level metadata
+6. add replay validation hooks
+7. add pack installation later
+
+For the first version, it is acceptable to support only:
+
+- first-party modules
+- local discovery
+- one trusted-pack folder
+- manual installation
+
+That is enough to prove the architecture.
+
+## 20. Summary
+
+The correct architecture is not to embed every algorithm in the main application.
+
+The correct architecture is:
+
+- small core runtime
+- metadata-driven module registry
+- installable algorithm packs
+- trust-aware execution policies
+- validation before activation
+- AI operating only on registered and policy-approved modules
+
+This keeps the framework scalable, safe, and extensible.

+ 526 - 0
doc/平台设计/算法链分层设计.md

@@ -0,0 +1,526 @@
+# 算法链分层设计
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+## 1. 目的
+
+本文档用于定义本项目中算法链的组织方式、分层原则、分类维度和第一版链模板目录。
+
+本文件的目标不是描述单个算法实现细节,而是建立一套可供 AI分析员调用、组合、评估和迭代的算法链体系。
+
+## 2. 核心原则
+
+### 2.1 算法链是一等对象
+
+算法链不能被视为临时拼接的内部实现细节。
+
+在本项目中,算法链应被视为正式的实验方法单元,具备:
+
+- 明确目的
+- 明确输入输出
+- 明确适用条件
+- 明确可调参数
+- 明确证据产出
+- 明确评分方式
+- 明确失败模式
+
+### 2.2 算法链必须分层
+
+不同分析阶段要解决的问题不同,因此不能用一条统一 pipeline 覆盖全部分析任务。
+
+系统必须允许:
+
+- 不同阶段使用不同算法
+- 不同信号类型使用不同链模板
+- 不同任务目标使用不同验证路径
+
+### 2.3 算法链必须服务于 AI分析员
+
+算法链体系的设计目标不是让人手动操作更方便,而是让 AI分析员能够:
+
+- 识别有哪些可用工具
+- 理解这些工具适合什么场景
+- 在不同链之间切换
+- 调整参数并比较结果
+- 根据失败原因重新规划实验
+
+## 3. 三个分类维度
+
+算法链体系建议按三个维度组织:
+
+1. 阶段
+2. 信号画像
+3. 分析目标
+
+## 4. 按阶段分层
+
+### 4.1 预处理阶段
+
+目的:
+
+- 统一输入质量
+- 去除明显干扰
+- 为后续分析建立稳定输入
+
+典型模块:
+
+- `audio_normalize`
+- `mono_convert`
+- `resample_16k`
+- `denoise_basic`
+- `bandpass_filter`
+
+### 4.2 探测阶段
+
+目的:
+
+- 判断输入中是否存在值得进一步分析的结构
+- 对信号进行快速初判
+
+典型模块:
+
+- `energy_probe`
+- `spectral_probe`
+- `onset_probe`
+- `periodicity_probe`
+- `harmonicity_probe`
+
+### 4.3 分段阶段
+
+目的:
+
+- 将连续音频切分为可分析的事件或片段
+- 为局部验证和重复模式分析提供单位
+
+典型模块:
+
+- `segment_energy_based`
+- `segment_vad_based`
+- `segment_onset_based`
+- `segment_repeat_based`
+
+### 4.4 特征提取阶段
+
+目的:
+
+- 生成更稳定、更可比较的结构表征
+
+典型模块:
+
+- `spectrogram_extract`
+- `mfcc_extract`
+- `cepstrum_extract`
+- `embedding_extract`
+- `temporal_stats_extract`
+
+### 4.5 假设验证阶段
+
+目的:
+
+- 对特定模式假设进行针对性验证
+- 不是泛泛分析,而是回答明确问题
+
+典型模块:
+
+- `autocorrelation_period_check`
+- `cepstrum_period_check`
+- `f0_tracking`
+- `harmonicity_check`
+- `transient_density_check`
+- `source_separation_basic`
+- `template_repeat_match`
+
+### 4.6 归因与识别阶段
+
+目的:
+
+- 在结构证据基础上进行候选解释和标签归因
+
+典型模块:
+
+- `baseline_sound_classifier`
+- `alt_sound_classifier`
+- `similarity_search`
+- `anomaly_distance_check`
+
+### 4.7 解释与决策阶段
+
+目的:
+
+- 汇总证据
+- 形成结论
+- 决定下一步
+
+说明:
+
+该阶段通常由 AI分析员主导,不作为普通信号处理模块实现。
+
+## 5. 按信号画像分类
+
+第一版建议先定义以下信号画像,用于帮助 AI分析员选择链模板。
+
+### 5.1 连续稳态信号
+
+特点:
+
+- 持续时间较长
+- 频谱相对稳定
+- 可能存在稳定窄带成分或谐波结构
+
+典型例子:
+
+- 空调声
+- 电机稳态声
+- 风扇声
+- 蜂鸣器持续音
+
+### 5.2 瞬态事件信号
+
+特点:
+
+- 短时爆发
+- onset 明显
+- 时域包络变化快
+
+典型例子:
+
+- 敲击
+- 关门
+- 点击
+- 瞬时碰撞
+
+### 5.3 周期重复信号
+
+特点:
+
+- 事件或音型重复出现
+- 重复间隔可能稳定或近似稳定
+
+典型例子:
+
+- 报警器
+- 机械节拍
+- 周期提示音
+
+### 5.4 多事件组合信号
+
+特点:
+
+- 由多个相似或不同事件组成
+- 需要做局部分段和聚类
+
+典型例子:
+
+- 脚步
+- 连续鸟叫
+- 机械异响序列
+
+### 5.5 混合声源信号
+
+特点:
+
+- 存在多个重叠声源
+- 背景噪声或其他主体干扰明显
+
+典型例子:
+
+- 人声背景中的设备声
+- 室外环境中的目标提示音
+
+### 5.6 未知或异常信号
+
+特点:
+
+- 不适合直接分类
+- 更适合先做结构分析和异常距离分析
+
+典型例子:
+
+- 未知设备声
+- 与已知样本差异显著的片段
+
+## 6. 按分析目标分类
+
+AI分析员在不同目标下应选择不同链模板。
+
+### 6.1 模式发现
+
+目的:
+
+- 先回答“这里面有没有值得注意的结构”
+
+### 6.2 周期验证
+
+目的:
+
+- 验证是否存在周期性、重复间隔和重复模板
+
+### 6.3 谐波结构验证
+
+目的:
+
+- 验证是否存在明显主频、谐波和稳态音调结构
+
+### 6.4 事件结构分析
+
+目的:
+
+- 分析事件边界、事件密度和事件序列模式
+
+### 6.5 声源分离与重分析
+
+目的:
+
+- 在复杂输入中剥离可疑成分后重新判断
+
+### 6.6 候选归因
+
+目的:
+
+- 形成“更像什么、不像什么”的解释结果
+
+### 6.7 异常判断
+
+目的:
+
+- 解释为什么当前样本不像既有类别或已知模式
+
+## 7. 第一版链模板目录
+
+第一版建议至少建设以下链模板。
+
+### 7.1 通用探针链
+
+用途:
+
+- 所有输入的默认第一步
+- 快速建立初始证据
+
+模板:
+
+```text
+audio_normalize
+-> mono_convert
+-> resample_16k
+-> energy_probe
+-> spectral_probe
+-> onset_probe
+-> periodicity_probe
+```
+
+主要产出:
+
+- 能量分布
+- 频谱统计
+- onset 密度
+- 周期性候选指标
+
+### 7.2 周期模式验证链
+
+用途:
+
+- 验证信号中是否存在稳定重复结构
+
+模板:
+
+```text
+segment_repeat_based
+-> autocorrelation_period_check
+-> cepstrum_period_check
+-> repeat_interval_estimate
+```
+
+主要产出:
+
+- 重复间隔估计
+- 周期稳定度
+- 重复模式置信度
+
+### 7.3 谐波结构验证链
+
+用途:
+
+- 验证稳态窄带和谐波结构
+
+模板:
+
+```text
+spectrogram_extract
+-> f0_tracking
+-> harmonicity_check
+-> tonal_stability_check
+```
+
+主要产出:
+
+- 基频轨迹
+- 谐波比
+- 音调稳定性
+
+### 7.4 瞬态事件分析链
+
+用途:
+
+- 分析短促事件和冲击型结构
+
+模板:
+
+```text
+segment_onset_based
+-> transient_density_check
+-> event_cluster
+-> template_repeat_match
+```
+
+主要产出:
+
+- 事件数
+- 事件密度
+- 重复事件模板
+
+### 7.5 混合声源分析链
+
+用途:
+
+- 处理分类失败或明显混合输入
+
+模板:
+
+```text
+denoise_basic
+-> source_separation_basic
+-> embedding_extract
+-> reclassify_with_alt_window
+```
+
+主要产出:
+
+- 声源分离结果
+- 分离后候选标签
+- 混合程度估计
+
+### 7.6 异常判断链
+
+用途:
+
+- 当常规分类无法给出稳定结果时,解释异常程度
+
+模板:
+
+```text
+embedding_extract
+-> anomaly_distance_check
+-> similarity_search
+```
+
+主要产出:
+
+- 与已知样本距离
+- 最近邻样本
+- 异常分数
+
+## 8. 算法链的元数据要求
+
+为了支持 AI分析员调度,每条链或每个模块至少应暴露以下元数据:
+
+- 标识符
+- 名称
+- 所属阶段
+- 适用信号画像
+- 适用目标
+- 输入格式
+- 输出格式
+- 参数说明
+- 主要证据产出类型
+- 常见失败模式
+- 成本估计
+
+建议结构如下:
+
+```ts
+type ModuleSpec = {
+  id: string
+  stage: string
+  signalProfiles: string[]
+  goals: string[]
+  inputFormat: string
+  outputFormat: string
+  params: Record<string, unknown>
+  evidenceKinds: string[]
+  failureModes: string[]
+  cost: {
+    cpu?: number
+    latency?: number
+    memory?: number
+  }
+}
+```
+
+## 9. 参数管理原则
+
+AI 并不应该对所有参数无限制搜索。
+
+第一版建议把参数分为三类:
+
+### 9.1 固定参数
+
+- 由系统或模块作者固定
+- 第一版不开放给 AI 调整
+
+### 9.2 可调参数
+
+- 允许 AI 在已定义范围内调整
+- 应有默认值、上下界和推荐步长
+
+### 9.3 高风险参数
+
+- 过度变化可能导致结果失真或成本失控
+- 需要更严格的策略或更高权限才能调整
+
+## 10. 评分与失败归因要求
+
+每条链执行完成后,不能只输出结果,还应输出:
+
+- `Evidence`
+- `ScoreCard`
+- `failureReasons`
+
+示例失败原因:
+
+- `no_clear_event_boundary`
+- `weak_periodicity_signal`
+- `unstable_f0_track`
+- `classifier_confidence_collapsed`
+- `separation_not_improved`
+- `unknown_pattern_out_of_catalog`
+
+AI分析员后续的调参与换链决策,应建立在这些结构化结果上。
+
+## 11. 第一版建设优先级
+
+第一版不应追求链模板过多,而应优先保证每类代表链可跑通。
+
+建议优先顺序:
+
+1. 通用探针链
+2. 周期模式验证链
+3. 谐波结构验证链
+4. 瞬态事件分析链
+5. 异常判断链
+6. 混合声源分析链
+
+## 12. 结论
+
+本项目的算法链体系不是普通的数据处理流水线,而是 AI分析员可调用的实验方法库。
+
+算法链必须:
+
+- 按阶段分层
+- 按信号画像分类
+- 按分析目标分类
+- 暴露足够元数据
+- 支持评分和失败归因
+
+只有在这个前提下,AI 才能真正替代专业使用者完成“选择、验证、分析、评估、结论”这整套流程。
+

+ 570 - 0
doc/总体架构/便携式信号分析仪架构说明.md

@@ -0,0 +1,570 @@
+# 便携式信号分析仪架构说明
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+说明:本版为按规范整理的历史文档,正文暂保留原英文内容。
+
+This document is a domain-specific product architecture under the higher-level framework design in [多模态分析框架.md](D:/dev/TC/doc/总体架构/多模态分析框架.md).
+
+## 1. Purpose
+
+This document captures a product and architecture direction for a portable signal analyzer inspired by the "tricorder" style workflow:
+
+- collect signals from multiple physical sources
+- detect, demodulate, decode, and frame them using deterministic algorithms
+- apply protocol analysis on framed data
+- use AI as an assistant, orchestrator, and experiment controller
+
+The core principle is:
+
+> AI should not replace the actual signal-processing and protocol-analysis engine.
+> Deterministic algorithms should do the decoding work.
+> AI should control experiments, compare results, explain outcomes, and choose the next action.
+
+## 2. Product Positioning
+
+The target device is not just a packet sniffer and not just an SDR receiver.
+It is a multi-stage analysis platform for:
+
+- RF and non-RF signal acquisition
+- physical-layer and link-layer recovery
+- protocol identification and interpretation
+- guided diagnostics and anomaly explanation
+
+Practical examples include:
+
+- identifying unknown digital bursts
+- recovering framed traffic from noisy captures
+- decoding standard or proprietary protocols
+- presenting operator-friendly summaries and next-step suggestions
+
+## 3. Wireshark Reuse Boundary
+
+Wireshark is useful, but only for the upper half of the stack.
+
+Wireshark is strong at:
+
+- framed packet dissection
+- protocol tree generation
+- display filtering
+- reassembly, statistics, and follow-stream style analysis
+- export and structured protocol interpretation
+
+Wireshark is not the right tool for:
+
+- raw RF analysis
+- blind modulation recognition
+- carrier recovery
+- symbol timing recovery
+- unknown physical-layer reconstruction
+
+The practical reuse boundary is:
+
+1. collect raw signal data
+2. perform DSP, demodulation, bit recovery, and framing
+3. convert recovered traffic into packets or events
+4. hand those results to Wireshark-related tooling such as:
+   - `pcap` / `pcapng`
+   - `tshark`
+   - `sharkd`
+   - custom dissectors where appropriate
+
+This makes Wireshark a protocol-analysis backend, not the full analyzer brain.
+
+## 4. System Architecture
+
+The system should be split into clear layers.
+
+### 4.1 Acquisition Layer
+
+Inputs may include:
+
+- IQ streams
+- IF or audio data
+- logic-level captures
+- UART / SPI / I2C / CAN buses
+- BLE / Wi-Fi / Ethernet mirrored traffic
+- file-based replay samples
+
+This layer should normalize access to multiple hardware front-ends and record:
+
+- timestamp
+- sample rate
+- center frequency
+- gain / front-end state
+- source identity
+- capture duration
+
+### 4.2 Signal Workspace
+
+The workspace is the canonical store for both raw and intermediate data.
+It should retain:
+
+- raw samples
+- derived features
+- intermediate bitstreams
+- framed outputs
+- experiment metadata
+- scores and failure reasons
+
+This is essential for reproducibility, offline replay, regression testing, and AI-driven iteration.
+
+### 4.3 DSP / Demodulation Pipeline
+
+This layer performs actual signal recovery.
+Typical module categories:
+
+- preprocessing
+  - DC removal
+  - AGC
+  - filtering
+  - resampling
+- detection
+  - energy detection
+  - burst detection
+  - coarse frequency estimation
+- synchronization
+  - carrier recovery
+  - symbol clock recovery
+  - preamble / sync-word search
+- demodulation
+  - OOK / ASK
+  - FSK / GFSK
+  - PSK / QPSK
+  - OFDM-family or chirp-style paths when supported
+- bit-domain processing
+  - hard or soft decision
+  - de-whitening
+  - de-interleaving
+  - FEC decoding
+  - CRC validation
+
+### 4.4 Frame Builder
+
+This layer transforms bitstreams into candidate frames or packets.
+It is the boundary between "signal recovery" and "protocol interpretation".
+
+Responsibilities:
+
+- frame boundary detection
+- fixed/variable length frame assembly
+- checksum / CRC validation
+- field boundary estimation
+- event extraction
+
+### 4.5 Protocol Analysis Layer
+
+Once data has become packets or events:
+
+- use Wireshark-compatible outputs for standard protocols
+- use internal parsers and heuristics for proprietary protocols
+- gradually migrate stable proprietary formats into custom dissectors if needed
+
+### 4.6 UI and Assistant Layer
+
+This layer provides:
+
+- live scan results
+- replay and lab analysis
+- confidence-ranked protocol candidates
+- anomaly explanations
+- next-step recommendations
+- exportable reports
+
+## 5. AI Role in the System
+
+AI should not directly replace DSP blocks.
+Its primary role is that of an orchestration and analysis controller.
+
+AI responsibilities:
+
+- choose candidate algorithm pipelines
+- tune parameters
+- compare candidate outputs
+- explain likely failure points
+- decide what to try next
+- summarize results for the operator
+
+AI should behave like an automated signal-analysis engineer, not like a magical decoder.
+
+## 6. AI Search for Algorithm Chains
+
+### 6.1 Problem Definition
+
+Algorithm-chain search is a constrained program-search problem.
+
+Input:
+
+- raw or partially processed signal data
+- prior device/context metadata
+- previous experiment history
+
+Output:
+
+- a ranked set of candidate pipelines
+- associated parameter settings
+- score and confidence estimates
+
+Optimization target:
+
+- maximize correctness and interpretability
+- minimize computational cost and false positives
+
+### 6.2 Canonical Pipeline Shape
+
+A pipeline can be modeled as:
+
+```text
+source
+-> preprocess
+-> detect
+-> sync
+-> demod
+-> decode
+-> frame
+-> proto
+```
+
+Each stage may have several interchangeable modules.
+
+### 6.3 Search State
+
+Each attempt should be tracked as an experiment node.
+
+```ts
+type Experiment = {
+  id: string
+  parentId?: string
+  pipeline: PipelineNode[]
+  inputRef: string
+  outputs: StageOutput[]
+  score: ScoreCard
+  status: "pending" | "running" | "done" | "failed"
+  notes?: string
+}
+
+type PipelineNode = {
+  module: string
+  params: Record<string, number | string | boolean>
+}
+```
+
+This allows AI to operate over an experiment tree instead of producing one-off guesses.
+
+### 6.4 Recommended Search Strategy
+
+Use a hybrid strategy:
+
+- rules for initialization
+- beam search for structure search
+- local optimization for parameter tuning
+
+Recommended control flow:
+
+1. classify signal at a coarse level
+2. generate a small number of high-probability candidate pipelines
+3. run short-window experiments
+4. score results and prune aggressively
+5. mutate the best pipelines locally
+6. rerun on longer samples for confirmation
+
+This is more stable than unconstrained random search.
+
+### 6.5 Why Beam Search Fits
+
+Beam search is a strong fit because it:
+
+- is resource-bounded
+- is easy to explain and debug
+- supports progressive refinement
+- works well with ranked experiment history
+
+Suggested pattern:
+
+- outer loop: beam search over module-chain structure
+- inner loop: bounded parameter tuning around the best chains
+
+### 6.6 Parameter Tuning
+
+Continuous or range-based parameters should be tuned separately from structure search.
+Typical tunables:
+
+- symbol rate
+- bandwidth
+- threshold values
+- timing recovery parameters
+- frequency offset compensation
+- framing tolerances
+
+Possible strategies:
+
+- bounded grid search
+- adaptive range narrowing
+- Bayesian optimization where available
+
+## 7. Scoring System
+
+The scoring system is the backbone of the AI loop.
+AI can only optimize what is measured.
+
+### 7.1 Physical-Layer Score
+
+Examples:
+
+- SNR improvement
+- carrier lock stability
+- clock recovery stability
+- cluster separation after demodulation
+- residual frequency error
+
+### 7.2 Frame-Level Score
+
+Examples:
+
+- preamble detection rate
+- frame length consistency
+- frame-boundary stability
+- CRC pass rate
+- repeated structure frequency
+
+### 7.3 Protocol-Level Score
+
+Examples:
+
+- known-header matches
+- valid field lengths
+- legal enum / field value ratios
+- session consistency
+- successful Wireshark-style protocol interpretation
+
+### 7.4 Cost Penalty
+
+Examples:
+
+- CPU cost
+- memory cost
+- latency
+- fragility under small parameter changes
+- overfitting to short windows
+
+### 7.5 Example Composite Score
+
+```text
+score =
+  0.25 * phy_score +
+  0.35 * frame_score +
+  0.30 * proto_score -
+  0.10 * cost_penalty
+```
+
+Weights should initially be hand-tuned and later adjusted using replay corpora.
+
+## 8. Failure Attribution
+
+Every experiment should return structured failure reasons.
+
+Example labels:
+
+- `no_signal_detected`
+- `unstable_symbol_clock`
+- `carrier_not_locked`
+- `frame_sync_failed`
+- `crc_failed`
+- `field_semantics_invalid`
+- `overfit_to_noise`
+
+This enables targeted next-step decisions.
+
+Examples:
+
+- `unstable_symbol_clock` -> adjust symbol-rate range or swap timing recovery module
+- `crc_failed` -> try bit inversion, whitening, byte order, CRC family changes
+- `field_semantics_invalid` -> reconsider framing or protocol family
+
+## 9. Module Registry
+
+Every algorithmic building block should be registered with machine-readable metadata.
+
+```ts
+type ModuleSpec = {
+  name: string
+  stage: "preprocess" | "sync" | "demod" | "decode" | "frame" | "proto"
+  inputFormat: string
+  outputFormat: string
+  params: Record<string, ParamSpec>
+  constraints: string[]
+  metrics: string[]
+  cost: { cpu: number; memory: number; latency: number }
+}
+```
+
+Without a registry, AI cannot safely orchestrate pipelines.
+
+The registry should allow the system to answer:
+
+- what can run after what
+- what parameters are tunable
+- what metrics each module produces
+- which modules are expensive
+- which modules are suitable for real-time use
+
+## 10. Knowledge Base and Priors
+
+The system should maintain a history of prior successful analyses.
+
+```ts
+type PriorCase = {
+  featureFingerprint: number[]
+  successfulPipelines: RankedPipeline[]
+}
+```
+
+Benefits:
+
+- faster startup on familiar signal families
+- reduced search cost
+- improved reliability over time
+- operator trust through precedent-based suggestions
+
+This lets the AI behave more like an experienced lab engineer.
+
+## 11. Runtime Modes
+
+At minimum, the product should support:
+
+### 11.1 Live Scan
+
+- real-time acquisition
+- limited local search
+- fast confidence-ranked hints
+- real-time alerting
+
+### 11.2 Lab Replay
+
+- deterministic offline reprocessing
+- multiple experiment branches
+- parameter tuning
+- regression validation
+
+### 11.3 Protocol Assist
+
+- packet/event summarization
+- protocol explanation
+- filter and query generation
+- reporting and export
+
+## 12. Device vs Host Split
+
+A portable device has strict CPU, memory, thermal, and battery limits.
+Do not assume the full AI search workload belongs on-device.
+
+Recommended split:
+
+- device side
+  - acquisition
+  - lightweight feature extraction
+  - small bounded search
+  - fast heuristic alerts
+- host / dock / edge side
+  - deep experiment search
+  - heavy replay analysis
+  - larger AI inference
+  - training / rule generation
+
+This split keeps the handheld usable under real operating conditions.
+
+## 13. Safety Boundaries for AI
+
+AI should be allowed to:
+
+- select pipelines
+- adjust parameters
+- reorder compatible modules
+- choose which experiment to run next
+- generate summaries
+
+AI should not directly and automatically:
+
+- patch low-level production DSP code in the live path
+- disable safety limits
+- bypass deterministic validation
+- replace scoring with free-form judgment
+
+If AI-generated changes extend beyond parameter or policy updates, they should be validated in replay or sandbox mode first.
+
+## 14. MVP Implementation Plan
+
+A practical first version should be intentionally small.
+
+### 14.1 MVP Scope
+
+- one or two acquisition sources
+- 10 to 20 reusable modules
+- experiment manager
+- scoring engine
+- beam-search controller
+- replay dataset support
+- export to `pcap` / `pcapng`
+- protocol analysis through `tshark` or `sharkd`
+
+### 14.2 Suggested Initial Modules
+
+- `dc_remove`
+- `agc`
+- `bandpass`
+- `resample`
+- `burst_detect`
+- `freq_offset_est`
+- `clock_recovery`
+- `ook_demod`
+- `2fsk_demod`
+- `gfsk_demod`
+- `slicer`
+- `manchester_decode`
+- `whitening_try`
+- `crc_scan`
+- `fixed_preamble_framer`
+- `variable_length_framer`
+
+### 14.3 Build Order
+
+1. acquisition and replay path
+2. module registry
+3. pipeline executor
+4. scoring engine
+5. experiment persistence
+6. AI orchestration loop
+7. Wireshark-compatible export and protocol backend
+8. handheld UI and reporting
+
+## 15. Key Risks
+
+Primary technical risks:
+
+- search-space explosion
+- weak scoring functions
+- overfitting to noise or short windows
+- mixing structure search and parameter search too early
+- lack of reproducible experiment logs
+
+Primary product risks:
+
+- placing AI too low in the stack
+- trying to make the first version too universal
+- failing to define a standard intermediate representation
+
+Primary integration risk:
+
+- misunderstanding Wireshark's role and pushing it below the framing boundary
+
+## 16. Summary
+
+The proposed portable signal analyzer should be designed as a layered system:
+
+- deterministic algorithms do the actual signal recovery
+- Wireshark-derived tooling handles protocol analysis after framing
+- AI operates above those layers as an experiment orchestrator, tuning controller, and explanation engine
+
+The winning architecture is not "AI decodes everything".
+It is "AI controls a rigorous decoding and analysis workflow".

+ 371 - 0
doc/总体架构/多模态分析框架.md

@@ -0,0 +1,371 @@
+# 多模态分析框架
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+说明:本版为按规范整理的历史文档,正文暂保留原英文内容。
+
+## 1. Purpose
+
+This document defines the higher-level framework architecture above the portable signal analyzer design.
+
+The portable signal analyzer should be treated as one product instantiation of a broader platform:
+
+- one framework
+- many input modalities
+- many algorithm families
+- one evidence model
+- one AI orchestration layer
+
+The long-term goal is to build a general analysis framework that can ingest different kinds of observations and drive deterministic algorithm pipelines with AI assistance.
+
+## 2. Core Thesis
+
+The framework should not be "AI analyzes everything directly".
+
+It should be:
+
+- input-normalized
+- algorithm-driven
+- evidence-centered
+- AI-orchestrated
+
+In practice:
+
+- deterministic algorithms do sensing, extraction, decoding, and measurement
+- AI selects algorithms, adjusts parameters, compares outcomes, and explains results
+- decisions are based on structured evidence rather than free-form intuition
+
+## 3. Framework Scope
+
+The framework should be able to support multiple modalities over time, such as:
+
+- wireless and RF signals
+- audio
+- optical/light signals
+- video
+- industrial buses
+- telemetry and logs
+
+Not every modality needs to be implemented at the beginning.
+The architecture should support them without hardcoding a single domain.
+
+## 4. Architectural Principle
+
+The platform should be designed around five stable abstractions:
+
+1. `InputSource`
+2. `Observation`
+3. `AlgorithmModule`
+4. `Evidence`
+5. `Experiment`
+
+If these abstractions are stable, the framework can grow without becoming a pile of special cases.
+
+## 5. Input Model
+
+### 5.1 InputSource
+
+An input source is where data comes from.
+
+Examples:
+
+- SDR receiver
+- microphone
+- camera
+- photodiode
+- logic analyzer
+- CAN/UART bridge
+- file replay
+
+Suggested shape:
+
+```ts
+type InputSource = {
+  id: string
+  kind: "rf" | "audio" | "video" | "optical" | "bus" | "file" | "log"
+  capabilities: string[]
+  metadata: Record<string, unknown>
+}
+```
+
+### 5.2 Observation
+
+An observation is a concrete chunk of captured data plus context.
+
+Suggested shape:
+
+```ts
+type Observation = {
+  id: string
+  sourceId: string
+  modality: string
+  timeRange: { start: string; end: string }
+  payloadRef: string
+  metadata: Record<string, unknown>
+}
+```
+
+This abstraction is important because all downstream algorithms should consume observations, not raw device details.
+
+## 6. Algorithm Layer
+
+### 6.1 AlgorithmModule
+
+An algorithm module is a reusable processing block.
+
+Examples:
+
+- FFT analysis
+- burst detector
+- frequency estimator
+- demodulator
+- object detector
+- OCR stage
+- audio event detector
+- frame decoder
+- protocol parser
+
+Suggested shape:
+
+```ts
+type AlgorithmModule = {
+  name: string
+  modality: string[]
+  stage: string
+  inputFormat: string
+  outputFormat: string
+  params: Record<string, ParamSpec>
+  metrics: string[]
+  constraints: string[]
+  cost: { cpu: number; memory: number; latency: number }
+}
+```
+
+### 6.2 Algorithm Chains
+
+The framework should not assume a single fixed pipeline.
+Instead, it should support candidate chains such as:
+
+- RF: preprocess -> detect -> sync -> demod -> decode -> frame -> protocol
+- Audio: denoise -> segment -> feature extract -> classify -> event correlate
+- Video: sample -> detect -> track -> OCR -> temporal reasoning
+- Optical: capture -> normalize -> pulse detect -> decode -> classify
+
+This is the main reason a framework approach is stronger than a single product implementation.
+
+## 7. Evidence Model
+
+The system should never rely on a single opaque output.
+It should accumulate evidence.
+
+Suggested shape:
+
+```ts
+type Evidence = {
+  id: string
+  observationId: string
+  producer: string
+  category: "signal" | "frame" | "protocol" | "semantic" | "anomaly"
+  values: Record<string, unknown>
+  confidence: number
+  traceRefs: string[]
+}
+```
+
+Examples of evidence:
+
+- detected carrier at a specific frequency
+- stable symbol timing candidate
+- valid CRC frames
+- recognized frame header
+- identified spoken keyword
+- recognized text in a video frame
+- repeated anomaly pattern
+
+The framework should make evidence first-class because AI needs something structured to reason over.
+
+## 8. Experiment Model
+
+The framework should be built around experiments rather than one-pass processing.
+
+Suggested shape:
+
+```ts
+type Experiment = {
+  id: string
+  observationId: string
+  pipeline: PipelineNode[]
+  outputs: string[]
+  evidenceIds: string[]
+  score: ScoreCard
+  status: "pending" | "running" | "done" | "failed"
+  failureReasons: string[]
+}
+```
+
+This allows:
+
+- controlled iteration
+- branching search
+- reproducibility
+- replay
+- regression
+
+## 9. AI Orchestration Layer
+
+The AI layer is an orchestrator, not the raw algorithm engine.
+
+Primary responsibilities:
+
+- choose candidate modules
+- assemble candidate pipelines
+- tune parameters
+- compare experiment results
+- request more data if needed
+- summarize confidence and uncertainty
+- explain likely next actions
+
+AI should make decisions based on:
+
+- observation metadata
+- extracted features
+- evidence history
+- prior successful cases
+- computational budget
+
+## 10. Decision Loop
+
+The framework should support a closed-loop controller:
+
+1. ingest observation
+2. extract cheap first-pass features
+3. generate candidate pipelines
+4. run bounded experiments
+5. score outputs
+6. accumulate evidence
+7. prune or mutate pipelines
+8. produce ranked hypotheses
+9. request more data or terminate
+
+This applies across modalities, even when the underlying algorithms differ.
+
+## 11. Scoring
+
+The scoring layer must be framework-wide, even if metrics differ by modality.
+
+Every score should combine:
+
+- correctness indicators
+- structural consistency
+- confidence quality
+- computational cost
+- stability under small perturbations
+
+High-level score groups:
+
+- signal quality score
+- structure extraction score
+- semantic interpretation score
+- anomaly relevance score
+- resource penalty
+
+This is one of the most important shared services in the framework.
+
+## 12. Why This Can Become a Platform
+
+The framework can evolve into more than a single device because the hard problem is not one algorithm.
+The hard problem is:
+
+- normalizing many inputs
+- managing many algorithm modules
+- assembling valid chains
+- evaluating evidence quality
+- automating search and comparison
+
+That creates reusable platform assets:
+
+- algorithm registry
+- pipeline runtime
+- experiment store
+- scoring engine
+- evidence graph
+- AI orchestration policies
+
+These assets can later support:
+
+- handheld products
+- desktop tools
+- edge devices
+- cloud-assisted analysis
+- domain-specific SDKs
+
+## 13. Product Strategy Implication
+
+The framework should be platformized internally but commercialized through narrow vertical products first.
+
+Good strategy:
+
+- design the architecture as a general framework
+- launch with one constrained, high-value modality
+- accumulate reusable modules and scoring logic
+- gradually expose the platform capability later
+
+Bad strategy:
+
+- start by marketing a universal tricorder for everything
+- build too many modalities before proving one workflow
+
+Platform-first architecture is good.
+Platform-first go-to-market is risky.
+
+## 14. Relationship to the Portable Signal Analyzer
+
+The portable signal analyzer document is a domain-specific specialization of this framework.
+
+Mapping:
+
+- framework input model -> handheld device capture sources
+- framework algorithm modules -> DSP, demodulation, framing, protocol stages
+- framework evidence model -> signal/frame/protocol evidence
+- framework AI orchestration -> pipeline selection and parameter tuning on-device or via host
+
+In other words:
+
+- `多模态分析框架.md` defines the parent architecture
+- `便携式信号分析仪架构说明.md` defines one concrete product path
+
+## 15. Recommended Immediate Starting Point
+
+Do not start by implementing every modality.
+Start by building the shared framework primitives in a narrow domain.
+
+Recommended first steps:
+
+1. choose one primary modality
+2. define `InputSource`, `Observation`, `AlgorithmModule`, `Evidence`, and `Experiment`
+3. build a module registry
+4. build an experiment runner
+5. build a scoring engine
+6. add a minimal AI orchestration loop
+
+For this project, the strongest first modality is still RF / signal analysis, because:
+
+- it aligns with the current handheld analyzer concept
+- deterministic pipeline boundaries are clearer
+- Wireshark-related protocol tooling can be reused after framing
+- it gives a disciplined environment for validating the framework model
+
+## 16. Summary
+
+The right long-term direction is a multimodal analysis framework, not just a single-purpose signal tool.
+
+But the right execution order is:
+
+- framework-shaped architecture
+- narrow first modality
+- deterministic algorithm modules
+- evidence-first design
+- AI as orchestration and explanation
+
+That combination gives both technical depth and a plausible product path.

+ 361 - 0
doc/总体架构/应用版图与优先级.md

@@ -0,0 +1,361 @@
+# 应用版图与优先级
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+说明:本版为按规范整理的历史文档,正文暂保留原英文内容。
+
+## 1. Purpose
+
+This document organizes the potential application space of the multimodal analysis framework and prioritizes where the platform should expand first.
+
+The framework can theoretically support many domains, but the product should not attempt to pursue all of them at once.
+
+This document answers three questions:
+
+- which application areas fit the architecture well
+- which areas are strategically attractive
+- which areas should be delayed until the platform is more mature
+
+This document complements:
+
+- [多模态分析框架.md](D:/dev/TC/doc/总体架构/多模态分析框架.md)
+- [便携式信号分析仪架构说明.md](D:/dev/TC/doc/总体架构/便携式信号分析仪架构说明.md)
+- [算法模块与插件系统.md](D:/dev/TC/doc/平台设计/算法模块与插件系统.md)
+
+## 2. Core Platform Capability
+
+At its core, the platform is not just a signal decoder and not just an AI assistant.
+
+It is a system that:
+
+- ingests observations from multiple modalities
+- runs deterministic algorithm chains
+- produces structured evidence
+- uses AI to orchestrate experiments and explain outcomes
+
+This means the platform is fundamentally suited to problems of:
+
+- detection
+- decoding
+- interpretation
+- anomaly explanation
+- multi-source evidence fusion
+
+## 3. Application Families
+
+The application space naturally clusters into several families.
+
+### 3.1 Signal and Spectrum Analysis
+
+Examples:
+
+- RF environment analysis
+- interference hunting
+- burst classification
+- wireless protocol recovery
+- field diagnostics
+
+Why it fits:
+
+- closest to the current architecture
+- deterministic pipeline structure is clear
+- Wireshark-style protocol analysis can be reused after framing
+- strong alignment with a handheld device concept
+
+### 3.2 Industrial and Embedded Diagnostics
+
+Examples:
+
+- industrial bus inspection
+- machine communication decoding
+- equipment fault detection
+- field maintenance diagnostics
+
+Why it fits:
+
+- combines electrical, protocol, and temporal evidence
+- strong commercial value
+- often benefits from portable tooling
+- explanations are useful to operators
+
+### 3.3 Audio and Acoustic Understanding
+
+Examples:
+
+- event detection
+- machine acoustic diagnostics
+- environmental sound interpretation
+- biological sound monitoring
+- animal vocalization analysis
+
+Why it fits:
+
+- waveform-based inputs fit the framework naturally
+- deterministic feature extraction and event detection remain important
+- AI can summarize states and anomalies instead of directly replacing signal algorithms
+
+### 3.4 Optical and Light-Signal Analysis
+
+Examples:
+
+- blink / pulse decoding
+- beacon identification
+- modulated light analysis
+- optical sensor diagnostics
+- environment light behavior analysis
+
+Why it fits:
+
+- signal-like processing pipeline
+- strong overlap with timing, framing, and classification logic
+
+### 3.5 Video and Visual Observation
+
+Examples:
+
+- scene event extraction
+- object and activity detection
+- OCR-assisted interpretation
+- motion analysis
+- cross-modal observation fusion
+
+Why it fits:
+
+- evidence model generalizes well
+- AI interpretation layer is useful
+- but algorithm volume and data complexity are much higher
+
+### 3.6 Biological and Animal Behavior Interpretation
+
+Examples:
+
+- animal vocalization analysis
+- animal state estimation
+- human-readable explanation of behavior cues
+- wildlife observation assistance
+- veterinary or husbandry support tools
+
+Why it fits:
+
+- naturally multimodal
+- benefits from evidence fusion across sound, motion, and context
+- AI is valuable as an explanation engine
+
+Why caution is needed:
+
+- "translation" is easy to overclaim
+- ground truth is hard to define
+- validation requires careful domain methodology
+
+## 4. Important Product Framing
+
+Some application areas are real opportunities, but they must be framed correctly.
+
+For example, animal-related applications should not initially be positioned as:
+
+- "animal to human translator"
+- "we understand exactly what your pet is saying"
+
+They should be framed more carefully as:
+
+- animal behavior interpretation assistant
+- animal state and signal understanding system
+- probabilistic cross-species signal analysis
+
+This matters because the architecture supports evidence-based interpretation better than science-fiction-grade literal translation.
+
+## 5. How to Judge a Good First Application
+
+A strong early application usually has these properties:
+
+- narrow enough to validate
+- expensive or painful problem today
+- clear user workflow
+- measurable success criteria
+- repeatable data collection
+- deterministic algorithm boundary before AI interpretation
+
+A weak first application usually has these properties:
+
+- vague problem definition
+- no stable ground truth
+- very broad modality scope
+- high emotional appeal but low validation discipline
+
+## 6. Recommended Priority Tiers
+
+### Tier 1: Strong Starting Domains
+
+These fit both the architecture and realistic execution constraints.
+
+#### 6.1 RF / Wireless Analysis
+
+Why first:
+
+- strongest match with current design
+- clean separation between deterministic decoding and AI explanation
+- easier to define algorithm chains
+- credible path to a handheld product
+- partial reuse of Wireshark-related protocol tooling
+
+#### 6.2 Industrial Device / Bus Diagnostics
+
+Why early:
+
+- strong operator pain points
+- good commercial value
+- portable tool form factor makes sense
+- explainability matters
+
+#### 6.3 Machine Acoustic Diagnostics
+
+Why early:
+
+- audio processing pipeline is manageable
+- useful in maintenance and monitoring
+- easier validation than open-ended animal semantics
+
+### Tier 2: Attractive but Should Follow Maturity
+
+These are promising, but should follow after the platform stabilizes.
+
+#### 6.4 Optical / Light-Signal Analysis
+
+Why later:
+
+- strong fit technically
+- smaller ecosystem and fewer immediate reusable assets
+- best added once the core plugin and scoring systems mature
+
+#### 6.5 Environmental Audio Interpretation
+
+Why later:
+
+- useful, but data diversity grows quickly
+- stronger context modeling is needed
+
+#### 6.6 Cross-Modal Field Observation
+
+Why later:
+
+- valuable for advanced products
+- more demanding on synchronization and evidence fusion
+
+### Tier 3: High-Imagination, High-Risk Domains
+
+These may become important later but are poor first products.
+
+#### 6.7 Animal-Human "Translation"
+
+Why risky early:
+
+- strong public appeal
+- weakly defined ground truth in many cases
+- easy to overpromise
+- high data-labeling difficulty
+
+The better long-term framing is animal-state interpretation, not literal translation.
+
+#### 6.8 General-Purpose Universal Tricorder
+
+Why risky early:
+
+- too broad
+- too many modalities
+- no disciplined wedge
+- hard to verify product-market fit
+
+This is better treated as a long-term platform narrative, not a first release.
+
+## 7. Strategic Insight
+
+The platform can eventually support many applications, but the architecture should grow by reusable assets rather than by uncontrolled product branching.
+
+Those reusable assets include:
+
+- module registry
+- experiment manager
+- scoring engine
+- evidence model
+- AI orchestration policies
+- replay and validation system
+
+If these assets are built well, each new application family becomes progressively cheaper to add.
+
+## 8. Suggested Expansion Path
+
+A disciplined expansion path could look like this:
+
+1. RF / wireless analysis
+2. industrial bus and field diagnostics
+3. machine acoustic diagnostics
+4. optical/light analysis
+5. richer multimodal field observation
+6. specialized biological or animal interpretation products
+
+This ordering keeps the platform grounded in applications with clearer validation paths before moving into more speculative interpretation-heavy domains.
+
+## 9. Commercial Reality
+
+The strongest early business opportunities are likely to come from domains where:
+
+- users already buy tools
+- failures are expensive
+- explanation reduces expert time
+- portability matters
+
+Examples:
+
+- wireless troubleshooting
+- industrial maintenance
+- equipment diagnostics
+- safety and field operations
+
+Consumer-facing "AI understands everything" narratives may be more exciting, but they are usually weaker first businesses.
+
+## 10. Product Positioning Guidance
+
+Internally:
+
+- think platform
+- build reusable infrastructure
+- keep modality boundaries clean
+
+Externally:
+
+- sell one clear use case first
+- avoid overly broad claims
+- emphasize assisted interpretation, not magic understanding
+
+This preserves both technical discipline and market credibility.
+
+## 11. Recommended Near-Term Focus
+
+For the next stage of work, the recommended focus remains:
+
+- use the framework architecture as the parent design
+- use RF / portable signal analysis as the first concrete execution path
+- keep future modality expansion in mind, but do not design the first implementation around all future applications simultaneously
+
+This gives the best balance of:
+
+- engineering tractability
+- platform value
+- product plausibility
+
+## 12. Summary
+
+The architecture has the potential to support many compelling applications, including long-term possibilities such as animal signal interpretation and multimodal observation systems.
+
+However, the correct strategy is:
+
+- recognize the large platform potential
+- prioritize domains with strong validation and clear workflows
+- start with narrow, high-value technical applications
+- expand into broader interpretation-driven products only after the core platform is proven
+
+In short:
+
+- platform ambition is good
+- product discipline is mandatory

+ 71 - 0
doc/文档目录.md

@@ -0,0 +1,71 @@
+# 文档目录
+
+版本号:v0.1.0
+最后更新:2026-04-04
+
+## 1. 目的
+
+本文档用于作为 `doc` 目录的统一入口,说明当前文档的分类和阅读顺序。
+
+## 2. 文档分类
+
+### 2.1 规范类
+
+- [文档规范.md](D:/dev/TC/doc/规范/文档规范.md)
+
+### 2.2 总体架构类
+
+- [多模态分析框架.md](D:/dev/TC/doc/总体架构/多模态分析框架.md)
+- [便携式信号分析仪架构说明.md](D:/dev/TC/doc/总体架构/便携式信号分析仪架构说明.md)
+- [应用版图与优先级.md](D:/dev/TC/doc/总体架构/应用版图与优先级.md)
+
+### 2.3 平台与实现基础类
+
+- [核心数据模型.md](D:/dev/TC/doc/平台设计/核心数据模型.md)
+- [算法模块与插件系统.md](D:/dev/TC/doc/平台设计/算法模块与插件系统.md)
+- [AI分析员定位与职责.md](D:/dev/TC/doc/平台设计/AI分析员定位与职责.md)
+- [算法链分层设计.md](D:/dev/TC/doc/平台设计/算法链分层设计.md)
+- [模块注册表设计.md](D:/dev/TC/doc/平台设计/模块注册表设计.md)
+- [实验执行器设计.md](D:/dev/TC/doc/平台设计/实验执行器设计.md)
+- [AI编排输入输出规范.md](D:/dev/TC/doc/平台设计/AI编排输入输出规范.md)
+- [Flutter终端架构设计.md](D:/dev/TC/doc/平台设计/Flutter终端架构设计.md)
+- [后端技术栈方案.md](D:/dev/TC/doc/平台设计/后端技术栈方案.md)
+- [后端服务拆分设计.md](D:/dev/TC/doc/平台设计/后端服务拆分设计.md)
+
+### 2.4 产品方向类
+
+- [音频优先产品方向.md](D:/dev/TC/doc/产品方向/音频优先产品方向.md)
+- [音频优先移动信号实验室方案.md](D:/dev/TC/doc/产品方向/音频优先移动信号实验室方案.md)
+
+### 2.5 实施规划类
+
+- [第一阶段实施路线图.md](D:/dev/TC/doc/实施规划/第一阶段实施路线图.md)
+
+## 3. 推荐阅读顺序
+
+建议按以下顺序阅读:
+
+1. [文档规范.md](D:/dev/TC/doc/规范/文档规范.md)
+2. [多模态分析框架.md](D:/dev/TC/doc/总体架构/多模态分析框架.md)
+3. [应用版图与优先级.md](D:/dev/TC/doc/总体架构/应用版图与优先级.md)
+4. [便携式信号分析仪架构说明.md](D:/dev/TC/doc/总体架构/便携式信号分析仪架构说明.md)
+5. [核心数据模型.md](D:/dev/TC/doc/平台设计/核心数据模型.md)
+6. [算法模块与插件系统.md](D:/dev/TC/doc/平台设计/算法模块与插件系统.md)
+7. [音频优先产品方向.md](D:/dev/TC/doc/产品方向/音频优先产品方向.md)
+8. [音频优先移动信号实验室方案.md](D:/dev/TC/doc/产品方向/音频优先移动信号实验室方案.md)
+9. [AI分析员定位与职责.md](D:/dev/TC/doc/平台设计/AI分析员定位与职责.md)
+10. [算法链分层设计.md](D:/dev/TC/doc/平台设计/算法链分层设计.md)
+11. [模块注册表设计.md](D:/dev/TC/doc/平台设计/模块注册表设计.md)
+12. [实验执行器设计.md](D:/dev/TC/doc/平台设计/实验执行器设计.md)
+13. [AI编排输入输出规范.md](D:/dev/TC/doc/平台设计/AI编排输入输出规范.md)
+14. [Flutter终端架构设计.md](D:/dev/TC/doc/平台设计/Flutter终端架构设计.md)
+15. [后端技术栈方案.md](D:/dev/TC/doc/平台设计/后端技术栈方案.md)
+16. [后端服务拆分设计.md](D:/dev/TC/doc/平台设计/后端服务拆分设计.md)
+17. [第一阶段实施路线图.md](D:/dev/TC/doc/实施规划/第一阶段实施路线图.md)
+
+## 4. 当前状态说明
+
+- 历史架构文档已统一为中文文件名并按类别归档。
+- 历史架构文档已补齐版本号和最后更新日期。
+- 历史架构文档正文当前保留英文原文,后续可分批汉化。
+- 新增方向文档已采用中文正文。

+ 80 - 0
doc/规范/文档规范.md

@@ -0,0 +1,80 @@
+# 文档规范
+
+版本号:v0.1.1
+最后更新:2026-04-04 14:30
+
+## 1. 目的
+
+本规范用于约束项目文档的命名、头部元信息和更新方式,保证后续讨论内容可以随时沉淀为一致、可追踪的正式文档。
+
+## 2. 文件命名规则
+
+- 文档文件名必须使用中文。
+- 文档文件名应直接表达主题,避免使用含糊名称。
+- 文件扩展名统一为 `.md`。
+
+示例:
+
+- `音频优先移动信号实验室方案.md`
+- `算法链分层设计.md`
+- `实验编排接口规范.md`
+
+## 3. 文档头部必填项
+
+每份正式文档开头必须包含以下字段:
+
+- 文档标题
+- 版本号
+- 最后更新日期
+
+推荐格式:
+
+```md
+# 文档标题
+
+版本号:v0.1.0
+最后更新:2026-04-04 14:30
+```
+
+## 4. 版本号规则
+
+- 版本号采用 `v主版本.次版本.修订版本` 格式。
+- 大范围结构调整或结论变化时,提升主版本或次版本。
+- 小幅补充、修正措辞或补充细节时,提升修订版本。
+
+示例:
+
+- `v0.1.0`:初稿
+- `v0.2.0`:新增章节或明显调整结构
+- `v0.2.1`:小修订
+
+## 5. 日期时间规则
+
+- `最后更新` 使用 `YYYY-MM-DD HH:mm` 格式。
+- 时间使用项目当前使用者所在时区。
+- 每次修改文档内容时,必须同步更新 `最后更新`。
+
+## 6. 内容风格
+
+- 以中文为主。
+- 优先写清目标、边界、结构、约束和下一步。
+- 尽量避免空泛描述,优先使用可落地的定义、流程和数据结构。
+- 当文档涉及工程实现时,应说明适用范围和不适用范围。
+
+## 7. 推荐章节结构
+
+按需选择,但建议优先包含以下内容:
+
+1. 目的
+2. 背景
+3. 核心定义
+4. 系统结构或流程
+5. 约束与边界
+6. 实施建议
+7. 后续工作
+
+## 8. 兼容说明
+
+- 现有历史文档如果暂未改名,可暂时保留。
+- 从本规范生效起,新文档必须遵守本规范。
+- 是否批量整理历史英文文档,后续单独决策。

+ 45 - 0
frontend/.gitignore

@@ -0,0 +1,45 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.build/
+.buildlog/
+.history
+.svn/
+.swiftpm/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins-dependencies
+.pub-cache/
+.pub/
+/build/
+/coverage/
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release

+ 45 - 0
frontend/.metadata

@@ -0,0 +1,45 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: "19074d12f7eaf6a8180cd4036a430c1d76de904e"
+  channel: "stable"
+
+project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+  platforms:
+    - platform: root
+      create_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+      base_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+    - platform: android
+      create_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+      base_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+    - platform: ios
+      create_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+      base_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+    - platform: linux
+      create_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+      base_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+    - platform: macos
+      create_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+      base_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+    - platform: web
+      create_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+      base_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+    - platform: windows
+      create_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+      base_revision: 19074d12f7eaf6a8180cd4036a430c1d76de904e
+
+  # User provided section
+
+  # List of Local paths (relative to this file) that should be
+  # ignored by the migrate tool.
+  #
+  # Files that are not part of the templates will be ignored by default.
+  unmanaged_files:
+    - 'lib/main.dart'
+    - 'ios/Runner.xcodeproj/project.pbxproj'

+ 16 - 0
frontend/README.md

@@ -0,0 +1,16 @@
+# tc_frontend
+
+A new Flutter project.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
+
+For help getting started with Flutter development, view the
+[online documentation](https://docs.flutter.dev/), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.

+ 28 - 0
frontend/analysis_options.yaml

@@ -0,0 +1,28 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+  # The lint rules applied to this project can be customized in the
+  # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+  # included above or to enable additional rules. A list of all available lints
+  # and their documentation is published at https://dart.dev/lints.
+  #
+  # Instead of disabling a lint rule for the entire project in the
+  # section below, it can also be suppressed for a single line of code
+  # or a specific dart file by using the `// ignore: name_of_lint` and
+  # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+  # producing the lint.
+  rules:
+    # avoid_print: false  # Uncomment to disable the `avoid_print` rule
+    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options

+ 14 - 0
frontend/android/.gitignore

@@ -0,0 +1,14 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+.cxx/
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/to/reference-keystore
+key.properties
+**/*.keystore
+**/*.jks

+ 44 - 0
frontend/android/app/build.gradle.kts

@@ -0,0 +1,44 @@
+plugins {
+    id("com.android.application")
+    id("kotlin-android")
+    // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+    id("dev.flutter.flutter-gradle-plugin")
+}
+
+android {
+    namespace = "com.tc.tc_frontend"
+    compileSdk = flutter.compileSdkVersion
+    ndkVersion = flutter.ndkVersion
+
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_17
+        targetCompatibility = JavaVersion.VERSION_17
+    }
+
+    kotlinOptions {
+        jvmTarget = JavaVersion.VERSION_17.toString()
+    }
+
+    defaultConfig {
+        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+        applicationId = "com.tc.tc_frontend"
+        // You can update the following values to match your application needs.
+        // For more information, see: https://flutter.dev/to/review-gradle-config.
+        minSdk = flutter.minSdkVersion
+        targetSdk = flutter.targetSdkVersion
+        versionCode = flutter.versionCode
+        versionName = flutter.versionName
+    }
+
+    buildTypes {
+        release {
+            // TODO: Add your own signing config for the release build.
+            // Signing with the debug keys for now, so `flutter run --release` works.
+            signingConfig = signingConfigs.getByName("debug")
+        }
+    }
+}
+
+flutter {
+    source = "../.."
+}

+ 7 - 0
frontend/android/app/src/debug/AndroidManifest.xml

@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- The INTERNET permission is required for development. Specifically,
+         the Flutter tool needs it to communicate with the running application
+         to allow setting breakpoints, to provide hot reload, etc.
+    -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>

+ 47 - 0
frontend/android/app/src/main/AndroidManifest.xml

@@ -0,0 +1,47 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+    <application
+        android:label="tc_frontend"
+        android:name="${applicationName}"
+        android:icon="@mipmap/ic_launcher">
+        <activity
+            android:name=".MainActivity"
+            android:exported="true"
+            android:launchMode="singleTop"
+            android:taskAffinity=""
+            android:theme="@style/LaunchTheme"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
+            android:hardwareAccelerated="true"
+            android:windowSoftInputMode="adjustResize">
+            <!-- Specifies an Android theme to apply to this Activity as soon as
+                 the Android process has started. This theme is visible to the user
+                 while the Flutter UI initializes. After that, this theme continues
+                 to determine the Window background behind the Flutter UI. -->
+            <meta-data
+              android:name="io.flutter.embedding.android.NormalTheme"
+              android:resource="@style/NormalTheme"
+              />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <!-- Don't delete the meta-data below.
+             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+        <meta-data
+            android:name="flutterEmbedding"
+            android:value="2" />
+    </application>
+    <!-- Required to query activities that can process text, see:
+         https://developer.android.com/training/package-visibility and
+         https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
+
+         In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
+    <queries>
+        <intent>
+            <action android:name="android.intent.action.PROCESS_TEXT"/>
+            <data android:mimeType="text/plain"/>
+        </intent>
+    </queries>
+</manifest>

+ 5 - 0
frontend/android/app/src/main/kotlin/com/tc/tc_frontend/MainActivity.kt

@@ -0,0 +1,5 @@
+package com.tc.tc_frontend
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity : FlutterActivity()

+ 12 - 0
frontend/android/app/src/main/res/drawable-v21/launch_background.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="?android:colorBackground" />
+
+    <!-- You can insert your own image assets here -->
+    <!-- <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@mipmap/launch_image" />
+    </item> -->
+</layer-list>

+ 12 - 0
frontend/android/app/src/main/res/drawable/launch_background.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@android:color/white" />
+
+    <!-- You can insert your own image assets here -->
+    <!-- <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@mipmap/launch_image" />
+    </item> -->
+</layer-list>

BIN
frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


+ 18 - 0
frontend/android/app/src/main/res/values-night/styles.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
+    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <!-- Show a splash screen on the activity. Automatically removed when
+             the Flutter engine draws its first frame -->
+        <item name="android:windowBackground">@drawable/launch_background</item>
+    </style>
+    <!-- Theme applied to the Android Window as soon as the process has started.
+         This theme determines the color of the Android Window while your
+         Flutter UI initializes, as well as behind your Flutter UI while its
+         running.
+
+         This Theme is only used starting with V2 of Flutter's Android embedding. -->
+    <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <item name="android:windowBackground">?android:colorBackground</item>
+    </style>
+</resources>

+ 18 - 0
frontend/android/app/src/main/res/values/styles.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
+    <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
+        <!-- Show a splash screen on the activity. Automatically removed when
+             the Flutter engine draws its first frame -->
+        <item name="android:windowBackground">@drawable/launch_background</item>
+    </style>
+    <!-- Theme applied to the Android Window as soon as the process has started.
+         This theme determines the color of the Android Window while your
+         Flutter UI initializes, as well as behind your Flutter UI while its
+         running.
+
+         This Theme is only used starting with V2 of Flutter's Android embedding. -->
+    <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
+        <item name="android:windowBackground">?android:colorBackground</item>
+    </style>
+</resources>

+ 7 - 0
frontend/android/app/src/profile/AndroidManifest.xml

@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- The INTERNET permission is required for development. Specifically,
+         the Flutter tool needs it to communicate with the running application
+         to allow setting breakpoints, to provide hot reload, etc.
+    -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>

+ 24 - 0
frontend/android/build.gradle.kts

@@ -0,0 +1,24 @@
+allprojects {
+    repositories {
+        google()
+        mavenCentral()
+    }
+}
+
+val newBuildDir: Directory =
+    rootProject.layout.buildDirectory
+        .dir("../../build")
+        .get()
+rootProject.layout.buildDirectory.value(newBuildDir)
+
+subprojects {
+    val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
+    project.layout.buildDirectory.value(newSubprojectBuildDir)
+}
+subprojects {
+    project.evaluationDependsOn(":app")
+}
+
+tasks.register<Delete>("clean") {
+    delete(rootProject.layout.buildDirectory)
+}

+ 2 - 0
frontend/android/gradle.properties

@@ -0,0 +1,2 @@
+org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
+android.useAndroidX=true

+ 5 - 0
frontend/android/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip

+ 26 - 0
frontend/android/settings.gradle.kts

@@ -0,0 +1,26 @@
+pluginManagement {
+    val flutterSdkPath =
+        run {
+            val properties = java.util.Properties()
+            file("local.properties").inputStream().use { properties.load(it) }
+            val flutterSdkPath = properties.getProperty("flutter.sdk")
+            require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
+            flutterSdkPath
+        }
+
+    includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+    repositories {
+        google()
+        mavenCentral()
+        gradlePluginPortal()
+    }
+}
+
+plugins {
+    id("dev.flutter.flutter-plugin-loader") version "1.0.0"
+    id("com.android.application") version "8.11.1" apply false
+    id("org.jetbrains.kotlin.android") version "2.2.20" apply false
+}
+
+include(":app")

+ 34 - 0
frontend/ios/.gitignore

@@ -0,0 +1,34 @@
+**/dgph
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3

+ 26 - 0
frontend/ios/Flutter/AppFrameworkInfo.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>App</string>
+  <key>CFBundleIdentifier</key>
+  <string>io.flutter.flutter.app</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>App</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>1.0</string>
+  <key>MinimumOSVersion</key>
+  <string>13.0</string>
+</dict>
+</plist>

+ 1 - 0
frontend/ios/Flutter/Debug.xcconfig

@@ -0,0 +1 @@
+#include "Generated.xcconfig"

+ 1 - 0
frontend/ios/Flutter/Release.xcconfig

@@ -0,0 +1 @@
+#include "Generated.xcconfig"

+ 616 - 0
frontend/ios/Runner.xcodeproj/project.pbxproj

@@ -0,0 +1,616 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 54;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+		331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
+		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+		74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+			remoteInfo = Runner;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
+		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+		331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
+		331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+		74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
+		74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
+		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
+		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		97C146EB1CF9000F007C117D /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		331C8082294A63A400263BE5 /* RunnerTests */ = {
+			isa = PBXGroup;
+			children = (
+				331C807B294A618700263BE5 /* RunnerTests.swift */,
+			);
+			path = RunnerTests;
+			sourceTree = "<group>";
+		};
+		9740EEB11CF90186004384FC /* Flutter */ = {
+			isa = PBXGroup;
+			children = (
+				3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+				9740EEB21CF90195004384FC /* Debug.xcconfig */,
+				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+				9740EEB31CF90195004384FC /* Generated.xcconfig */,
+			);
+			name = Flutter;
+			sourceTree = "<group>";
+		};
+		97C146E51CF9000F007C117D = {
+			isa = PBXGroup;
+			children = (
+				9740EEB11CF90186004384FC /* Flutter */,
+				97C146F01CF9000F007C117D /* Runner */,
+				97C146EF1CF9000F007C117D /* Products */,
+				331C8082294A63A400263BE5 /* RunnerTests */,
+			);
+			sourceTree = "<group>";
+		};
+		97C146EF1CF9000F007C117D /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				97C146EE1CF9000F007C117D /* Runner.app */,
+				331C8081294A63A400263BE5 /* RunnerTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		97C146F01CF9000F007C117D /* Runner */ = {
+			isa = PBXGroup;
+			children = (
+				97C146FA1CF9000F007C117D /* Main.storyboard */,
+				97C146FD1CF9000F007C117D /* Assets.xcassets */,
+				97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+				97C147021CF9000F007C117D /* Info.plist */,
+				1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+				1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+				74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+				74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+			);
+			path = Runner;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		331C8080294A63A400263BE5 /* RunnerTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+			buildPhases = (
+				331C807D294A63A400263BE5 /* Sources */,
+				331C807F294A63A400263BE5 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				331C8086294A63A400263BE5 /* PBXTargetDependency */,
+			);
+			name = RunnerTests;
+			productName = RunnerTests;
+			productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		97C146ED1CF9000F007C117D /* Runner */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+			buildPhases = (
+				9740EEB61CF901F6004384FC /* Run Script */,
+				97C146EA1CF9000F007C117D /* Sources */,
+				97C146EB1CF9000F007C117D /* Frameworks */,
+				97C146EC1CF9000F007C117D /* Resources */,
+				9705A1C41CF9048500538489 /* Embed Frameworks */,
+				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = Runner;
+			productName = Runner;
+			productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		97C146E61CF9000F007C117D /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				BuildIndependentTargetsInParallel = YES;
+				LastUpgradeCheck = 1510;
+				ORGANIZATIONNAME = "";
+				TargetAttributes = {
+					331C8080294A63A400263BE5 = {
+						CreatedOnToolsVersion = 14.0;
+						TestTargetID = 97C146ED1CF9000F007C117D;
+					};
+					97C146ED1CF9000F007C117D = {
+						CreatedOnToolsVersion = 7.3.1;
+						LastSwiftMigration = 1100;
+					};
+				};
+			};
+			buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+			compatibilityVersion = "Xcode 9.3";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 97C146E51CF9000F007C117D;
+			productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				97C146ED1CF9000F007C117D /* Runner */,
+				331C8080294A63A400263BE5 /* RunnerTests */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		331C807F294A63A400263BE5 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		97C146EC1CF9000F007C117D /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+				3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+				97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+				97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+			isa = PBXShellScriptBuildPhase;
+			alwaysOutOfDate = 1;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
+			);
+			name = "Thin Binary";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+		};
+		9740EEB61CF901F6004384FC /* Run Script */ = {
+			isa = PBXShellScriptBuildPhase;
+			alwaysOutOfDate = 1;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Run Script";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		331C807D294A63A400263BE5 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		97C146EA1CF9000F007C117D /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+				1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 97C146ED1CF9000F007C117D /* Runner */;
+			targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+		97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				97C146FB1CF9000F007C117D /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				97C147001CF9000F007C117D /* Base */,
+			);
+			name = LaunchScreen.storyboard;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		249021D3217E4FDB00AE95B9 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = NO;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				SUPPORTED_PLATFORMS = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Profile;
+		};
+		249021D4217E4FDB00AE95B9 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.tc.tcFrontend;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+				SWIFT_VERSION = 5.0;
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Profile;
+		};
+		331C8088294A63A400263BE5 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 1;
+				GENERATE_INFOPLIST_FILE = YES;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.tc.tcFrontend.RunnerTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 5.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+			};
+			name = Debug;
+		};
+		331C8089294A63A400263BE5 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 1;
+				GENERATE_INFOPLIST_FILE = YES;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.tc.tcFrontend.RunnerTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+			};
+			name = Release;
+		};
+		331C808A294A63A400263BE5 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 1;
+				GENERATE_INFOPLIST_FILE = YES;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.tc.tcFrontend.RunnerTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+			};
+			name = Profile;
+		};
+		97C147031CF9000F007C117D /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = NO;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		97C147041CF9000F007C117D /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = NO;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				SUPPORTED_PLATFORMS = iphoneos;
+				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_OPTIMIZATION_LEVEL = "-O";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		97C147061CF9000F007C117D /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.tc.tcFrontend;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 5.0;
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Debug;
+		};
+		97C147071CF9000F007C117D /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.tc.tcFrontend;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+				SWIFT_VERSION = 5.0;
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				331C8088294A63A400263BE5 /* Debug */,
+				331C8089294A63A400263BE5 /* Release */,
+				331C808A294A63A400263BE5 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				97C147031CF9000F007C117D /* Debug */,
+				97C147041CF9000F007C117D /* Release */,
+				249021D3217E4FDB00AE95B9 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				97C147061CF9000F007C117D /* Debug */,
+				97C147071CF9000F007C117D /* Release */,
+				249021D4217E4FDB00AE95B9 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}

+ 7 - 0
frontend/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:">
+   </FileRef>
+</Workspace>

+ 8 - 0
frontend/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>

+ 8 - 0
frontend/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>PreviewsEnabled</key>
+	<false/>
+</dict>
+</plist>

+ 101 - 0
frontend/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme

@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1510"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+               BuildableName = "Runner.app"
+               BlueprintName = "Runner"
+               ReferencedContainer = "container:Runner.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+            BuildableName = "Runner.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <Testables>
+         <TestableReference
+            skipped = "NO"
+            parallelizable = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "331C8080294A63A400263BE5"
+               BuildableName = "RunnerTests.xctest"
+               BlueprintName = "RunnerTests"
+               ReferencedContainer = "container:Runner.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      enableGPUValidationMode = "1"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+            BuildableName = "Runner.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Profile"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+            BuildableName = "Runner.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 7 - 0
frontend/ios/Runner.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "group:Runner.xcodeproj">
+   </FileRef>
+</Workspace>

+ 8 - 0
frontend/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>

+ 8 - 0
frontend/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>PreviewsEnabled</key>
+	<false/>
+</dict>
+</plist>

+ 13 - 0
frontend/ios/Runner/AppDelegate.swift

@@ -0,0 +1,13 @@
+import Flutter
+import UIKit
+
+@main
+@objc class AppDelegate: FlutterAppDelegate {
+  override func application(
+    _ application: UIApplication,
+    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+  ) -> Bool {
+    GeneratedPluginRegistrant.register(with: self)
+    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+  }
+}

+ 122 - 0
frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -0,0 +1,122 @@
+{
+  "images" : [
+    {
+      "size" : "20x20",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-20x20@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-20x20@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-29x29@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-29x29@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-29x29@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-40x40@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-40x40@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-60x60@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-60x60@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-20x20@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-20x20@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-29x29@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-29x29@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-40x40@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-40x40@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-76x76@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-76x76@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "83.5x83.5",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-83.5x83.5@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "1024x1024",
+      "idiom" : "ios-marketing",
+      "filename" : "Icon-App-1024x1024@1x.png",
+      "scale" : "1x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

BIN
frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png


BIN
frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png


BIN
frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png


BIN
frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png


BIN
frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png


BIN
frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png


BIN
frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png


BIN
frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png


BIN
frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png


BIN
frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png


BIN
frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png


Some files were not shown because too many files changed in this diff