docs(README): 更新项目说明文档,完善架构与启动指南

- 调整 CLAUDE.md 内容,增加使用指导和架构概览
- 补充微服务启动说明及单服务启动命令
- 明确 Gateway 服务器四阶段启动流程和职责划分
- 细化后端目录结构说明,补充主要文件职责描述
- 新增系统分层结构图,优化整体架构理解
- 更新 .gitignore,添加 runs 目录忽略规则
- 同步 .omc 相关状态文件,更新项目状态跟踪信息
This commit is contained in:
2026-03-24 17:19:31 +08:00
parent da6d642aaa
commit 16bb3c4211
20 changed files with 556 additions and 563 deletions

View File

@@ -41,6 +41,8 @@ class SkillsManager:
)
self.runs_root = self.project_root / "runs"
self._lock = Lock()
# Instance-level pending skill changes (thread-safe via self._lock)
self._pending_skill_changes: Dict[str, Set[Path]] = {}
def get_active_root(self, config_name: str) -> Path:
return self.runs_root / config_name / "skills" / "active"
@@ -739,7 +741,7 @@ class SkillsManager:
if local_root.exists():
watched_paths.append(local_root)
handler = _SkillsChangeHandler(watched_paths, callback, self._lock)
handler = _SkillsChangeHandler(watched_paths, self._pending_skill_changes, callback, self._lock)
observer = Observer()
for path in watched_paths:
observer.schedule(handler, str(path), recursive=True)
@@ -773,6 +775,7 @@ class SkillsManager:
# -------------------------------------------------------------------------
# Internal change-tracking state (populated by _SkillsChangeHandler)
# -------------------------------------------------------------------------
# Legacy class-level reference kept for migration compatibility
_pending_skill_changes: Dict[str, Set[Path]] = {}
def _resolve_disabled_skill_names(
@@ -824,11 +827,13 @@ class _SkillsChangeHandler(FileSystemEventHandler):
def __init__(
self,
watched_paths: List[Path],
pending_changes: Dict[str, Set[Path]],
callback: Optional[Any] = None,
lock: Optional[Lock] = None,
) -> None:
super().__init__()
self._watched_paths = watched_paths
self._pending_changes = pending_changes
self._callback = callback
self._lock = lock
@@ -841,13 +846,9 @@ class _SkillsChangeHandler(FileSystemEventHandler):
run_id = self._run_id_from_path(src_path)
if self._lock:
with self._lock:
SkillsManager._pending_skill_changes.setdefault(
run_id, set()
).add(src_path)
self._pending_changes.setdefault(run_id, set()).add(src_path)
else:
SkillsManager._pending_skill_changes.setdefault(
run_id, set()
).add(src_path)
self._pending_changes.setdefault(run_id, set()).add(src_path)
if self._callback:
self._callback([src_path])
break

View File

@@ -9,6 +9,7 @@ from .runtime_service import app as runtime_app
from .runtime_service import create_app as create_runtime_app
from .trading_service import app as trading_app
from .trading_service import create_app as create_trading_app
from .cors import add_cors_middleware, get_cors_origins
app = agent_app
create_app = create_agent_app
@@ -24,4 +25,6 @@ __all__ = [
"create_runtime_app",
"trading_app",
"create_trading_app",
"add_cors_middleware",
"get_cors_origins",
]

View File

@@ -8,7 +8,8 @@ from pathlib import Path
from typing import AsyncGenerator
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from backend.apps.cors import add_cors_middleware
from backend.api import agents_router, guard_router, workspaces_router
from backend.agents import AgentFactory, WorkspaceManager, get_registry
@@ -47,13 +48,7 @@ def create_app(project_root: Path | None = None) -> FastAPI:
lifespan=lifespan,
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
add_cors_middleware(app)
@app.get("/health")
async def health_check() -> dict[str, object]:

View File

@@ -4,10 +4,10 @@
from __future__ import annotations
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from backend.api import runtime_router
from backend.api.runtime import get_runtime_state
from backend.apps.cors import add_cors_middleware
def create_app() -> FastAPI:
@@ -18,13 +18,7 @@ def create_app() -> FastAPI:
version="0.1.0",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
add_cors_middleware(app)
@app.get("/health")
async def health_check() -> dict[str, object]:

View File

@@ -6,7 +6,7 @@ from __future__ import annotations
from typing import Any
from fastapi import FastAPI, Query
from fastapi.middleware.cors import CORSMiddleware
from backend.apps.cors import add_cors_middleware
from backend.domains import trading as trading_domain
from shared.schema import (
@@ -26,13 +26,7 @@ def create_app() -> FastAPI:
version="0.1.0",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
add_cors_middleware(app)
@app.get("/health")
async def health_check() -> dict[str, str]:

View File

@@ -266,10 +266,6 @@ async def run_pipeline(
set_global_runtime_manager(runtime_manager)
# Register runtime manager with API
from backend.api.runtime import register_runtime_manager
register_runtime_manager(runtime_manager)
# ======================================================================
# PHASE 1 & 2: Create infrastructure services (Market, Storage)
# These will be started by Gateway in the correct order

View File

@@ -9,7 +9,7 @@ import os
import sqlite3
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Iterable
from typing import Any, Iterable, Optional
SCHEMA = """
@@ -147,12 +147,30 @@ def _utc_timestamp() -> str:
class MarketStore:
"""SQLite-backed market research warehouse."""
"""SQLite-backed market research warehouse. Use get_instance() for the singleton."""
_instance: Optional["MarketStore"] = None
def __new__(cls, db_path: Path | None = None) -> "MarketStore":
if cls._instance is not None:
if db_path is None or cls._instance.db_path == Path(db_path or get_market_db_path()):
return cls._instance
instance = super().__new__(cls)
cls._instance = instance
return instance
def __init__(self, db_path: Path | None = None):
if getattr(self, "_initialized", False):
return
self.db_path = Path(db_path or get_market_db_path())
self.db_path.parent.mkdir(parents=True, exist_ok=True)
self._init_db()
self._initialized = True
@classmethod
def get_instance(cls, db_path: Path | None = None) -> "MarketStore":
"""Get the MarketStore singleton instance."""
return cls(db_path)
def _connect(self) -> sqlite3.Connection:
conn = sqlite3.connect(self.db_path)

View File

@@ -226,7 +226,6 @@ async def run_with_gateway(args):
)
runtime_manager.prepare_run()
set_global_runtime_manager(runtime_manager)
register_runtime_manager(runtime_manager)
# Create market service
market_service = MarketService(

View File

@@ -13,15 +13,30 @@ from .registry import RuntimeRegistry
_global_runtime_manager: Optional["TradingRuntimeManager"] = None
_shutdown_event: Optional[asyncio.Event] = None
# Lazy import to avoid circular dependency
_api_runtime = None
def _get_api_runtime():
global _api_runtime
if _api_runtime is None:
from backend.api import runtime as api_runtime_module
_api_runtime = api_runtime_module
return _api_runtime
def set_global_runtime_manager(manager: "TradingRuntimeManager") -> None:
global _global_runtime_manager
_global_runtime_manager = manager
# Sync to RuntimeState for consistency
_get_api_runtime().register_runtime_manager(manager)
def clear_global_runtime_manager() -> None:
global _global_runtime_manager
_global_runtime_manager = None
# Sync to RuntimeState for consistency
_get_api_runtime().unregister_runtime_manager()
def get_global_runtime_manager() -> Optional["TradingRuntimeManager"]: