Skip to content

开发与运行环境

定义项目的目录结构、依赖管理、配置方案、测试策略和运维基础。

原则:前期零外部依赖,渐进式引入基础设施。

项目目录结构

deep-thought/
├── pyproject.toml              # 项目元数据和依赖
├── README.md                   # 项目说明
├── .env.example                # 环境变量模板
├── .env                        # 实际环境变量(gitignore)

├── src/
│   └── deep_thought/
│       ├── __init__.py
│       ├── config.py           # 配置管理
│       ├── main.py             # 入口点
│       │
│       ├── core/               # 核心基础设施
│       │   ├── event_bus.py    # 事件总线
│       │   ├── db.py           # 数据库连接和 ORM
│       │   ├── llm.py          # LLM 统一调用层(LiteLLM)
│       │   ├── scheduler.py    # 任务调度(APScheduler)
│       │   └── embedding.py    # 向量化接口
│       │
│       ├── models/             # 数据模型(Pydantic)
│       │   ├── enums.py        # 所有枚举定义
│       │   ├── narrative.py    # 叙事模型
│       │   ├── persona.py      # Persona 模型
│       │   ├── indicator.py    # 指标模型
│       │   ├── sentiment.py    # 情绪模型
│       │   ├── judgment.py     # 判断模型
│       │   ├── mismatch.py     # 错配模型
│       │   └── trade.py        # 交易模型(预留)
│       │
│       ├── engines/            # 六大引擎
│       │   ├── narrative/      # 叙事发现引擎
│       │   │   ├── extractor.py    # 叙事提取
│       │   │   ├── matcher.py      # 语义匹配
│       │   │   └── state_machine.py # 状态机
│       │   │
│       │   ├── data/           # 数据采集引擎
│       │   │   ├── providers/      # 数据源实现
│       │   │   │   ├── openbb.py
│       │   │   │   ├── ibkr.py
│       │   │   │   ├── longbridge.py
│       │   │   │   └── binance.py
│       │   │   ├── calculator.py   # 指标计算
│       │   │   └── collector.py    # 采集调度
│       │   │
│       │   ├── sentiment/      # 情绪引擎
│       │   │   ├── retail.py       # 散户情绪
│       │   │   ├── media.py        # 媒体情绪
│       │   │   └── institutional.py # 机构行为
│       │   │
│       │   ├── think_tank/     # 智库引擎
│       │   │   ├── persona_agent.py # Persona 分析
│       │   │   ├── debate.py        # 对抗辩论
│       │   │   └── ensemble.py      # 合成
│       │   │
│       │   ├── mismatch/       # 错配检测引擎
│       │   │   └── detector.py
│       │   │
│       │   └── memory/         # 记忆系统
│       │       ├── event_store.py  # 事件持久化
│       │       ├── snapshot.py     # 快照生成
│       │       └── meta_learner.py # 元学习
│       │
│       ├── stores/             # 数据存储接口
│       │   ├── narrative_store.py
│       │   ├── indicator_store.py
│       │   ├── sentiment_store.py
│       │   └── judgment_store.py
│       │
│       └── utils/              # 工具函数
│           ├── llm_prompts.py  # Prompt 模板
│           └── validators.py   # JSON 校验

├── personas/                   # Persona Skill(git submodules,独立版本管理)
│   ├── .gitmodules             # submodule 注册表
│   ├── soros/                  # → git@github.com:xxx/persona-soros.git
│   │   ├── skill.md            # 分析框架(source of truth)
│   │   ├── metadata.yaml       # 元数据(id、tags、初始权重)
│   │   └── release.md          # 版本变更记录(自学习日志)
│   ├── buffett/                # → git@github.com:xxx/persona-buffett.git
│   │   ├── skill.md
│   │   ├── metadata.yaml
│   │   └── release.md
│   ├── dalio/
│   ├── thorp/
│   ├── simons/
│   └── zhou_jintao/

│   # 仓库结构:
│   # 每个 Persona 是独立 repo,独立迭代、独立版本
│   # 可选:组合层 repo (personas-composite) 聚合所有 persona 引用
│   # deep-thought 通过 submodule 引入组合层或逐个引入

├── defaults/                   # 默认配置模板(首次运行时复制到数据目录)
│   ├── settings.yaml
│   ├── personas.yaml
│   └── data_sources.yaml

├── tests/
│   ├── unit/                   # 单元测试
│   │   ├── test_calculator.py
│   │   ├── test_state_machine.py
│   │   └── test_mismatch.py
│   ├── integration/            # 集成测试
│   │   ├── test_pipeline.py
│   │   └── test_db.py
│   └── backtest/               # 回测
│       ├── test_persona_accuracy.py
│       └── test_signal_quality.py

└── scripts/
    ├── init_db.py              # 数据库初始化
    ├── sync_personas.py        # Persona 同步
    └── run_analysis.py         # 手动触发分析

依赖管理

toml
# pyproject.toml
[project]
name = "deep-thought"
version = "0.1.0"
requires-python = ">=3.11"

dependencies = [
    # 核心框架
    "fastapi>=0.110",
    "uvicorn>=0.29",
    "pydantic>=2.7",

    # 数据库
    "sqlalchemy[asyncio]>=2.0",
    "asyncpg>=0.29",
    "embedded-postgres>=1.0",
    "pgvector>=0.3",

    # LLM
    "litellm>=1.40",

    # 任务调度
    "apscheduler>=3.10",

    # 缓存
    "diskcache>=5.6",

    # 数据处理
    "numpy>=1.26",
    "pandas>=2.2",

    # 外部数据源
    "openbb>=4.2",
    "ccxt>=4.3",

    # 工具
    "httpx>=0.27",
    "python-dotenv>=1.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.2",
    "pytest-asyncio>=0.23",
    "pytest-cov>=5.0",
    "ruff>=0.4",
    "mypy>=1.10",
]

[tool.ruff]
target-version = "py311"
line-length = 100

[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]

数据目录设计

核心原则:代码与数据分离。 代码目录可独立升级,数据目录可独立备份/迁移。

数据目录默认位于 ~/.deep-thought/,可通过环境变量 DT_DATA_DIR 自定义。

目录结构

~/.deep-thought/
├── config/
│   ├── settings.yaml         # 主配置(LLM provider/model、调度、阈值等)
│   ├── personas.yaml         # Persona 模型分配映射
│   └── data_sources.yaml     # 数据源 API keys 和连接配置

├── db/
│   ├── pg/                   # embedded-postgres 数据目录
│   │   ├── base/             # PG 基础数据
│   │   └── wal/              # WAL 日志
│   └── backups/              # 数据库备份
│       └── 2026-05-25.dump

├── cache/
│   ├── embeddings/           # Embedding 缓存(避免重复调用 LLM)
│   └── indicators/           # 指标历史缓存

├── logs/
│   ├── deep-thought.log      # 主日志
│   ├── llm_calls.jsonl       # LLM 调用日志(每条一行 JSON)
│   └── errors.log            # 错误日志

├── output/
│   ├── reports/              # 生成的分析报告
│   │   └── 2026-05-25-daily-report.md
│   ├── snapshots/            # 定期系统状态快照(用于恢复)
│   │   └── 2026-05-25T08-00-00.json
│   └── exports/              # 数据导出(回测用)
│       └── judgments_2026-05.csv

└── (personas 不在数据目录,而是代码目录的 git submodules)

设计理由

config/     配置与代码分离,升级代码不丢配置
db/         数据库独立,可单独备份/迁移
cache/      可删除重建,不影响核心数据
logs/       日志独立,便于排查和归档
output/     输出独立,便于分享和审查

personas/   在代码目录中作为 git submodules,独立版本管理
            每个 Persona 是独立仓库,可通过 git pull 独立更新
            启用/禁用在 ~/.deep-thought/config/personas.yaml 中控制

Persona 仓库结构

每个 Persona 仓库的标准结构:

persona-soros/
├── skill.md            # 分析框架(system prompt + 分析方法论)
├── metadata.yaml       # 元数据
└── release.md          # 版本变更记录

skill.md

Persona 的核心——世界观、分析框架、打分标准。系统运行时加载到 LLM 的 system prompt。

metadata.yaml

yaml
id: soros
name: George Soros
version: "1.2.0"                # 语义化版本
core_framework: "reflexivity_theory"
tags: ["macro", "reflexivity", "contrarian"]
created_at: "2026-05-24"
author: "jazzqi"

release.md(自学习记录)

markdown
# Persona: George Soros — 版本记录

## v1.2.0 (2026-06-15)

**变更原因**: 元学习分析发现本 Persona 在 stagflation regime 下准确率仅 35%,
主要问题是对通胀信号不敏感,容易被"叙事驱动型"牛市误导。

**变更内容**:
- scoring_rubric 增加通胀数据权重(CPI、PCE、资金费率)
- 分析框架增加"通胀叙事 vs 实际数据"的强制对比步骤
- 新增对大宗商品周期的交叉验证逻辑

**验证依据**: 回测 2022-2024 数据,修改后 stagflation 场景准确率提升至 58%。

---

## v1.1.0 (2026-06-01)

**变更原因**: 对抗辩论中经常被 Buffett 说服但实际 Buffett 判断错误,
说明自我辩护强度不足,容易在压力下改变正确判断。

**变更内容**:
- 辩论策略增加"坚持阈值"——确信度 > 0.7 时不轻易改变立场
- 增加"反身性加速"检测——当叙事自我强化时提高确信度

---

## v1.0.0 (2026-05-24)

**初始版本**: 从 Soros 公开著作和投资案例中蒸馏。
- 核心框架:反身性理论
- 分析偏好:关注叙事的自我强化/否定循环
- 输出倾向:偏好极端判断,置信度呈 U 型分布

release.md 的价值:

  • 人类可读的 Persona 演化历史
  • 每次变更都有"为什么"(元学习发现的问题)和"改了什么"(具体修改)
  • 可以追溯哪些改进有效、哪些无效
  • 与数据库中的 PersonaVersion 形成双轨——release.md 是叙事,数据库是结构化记录

版本回退:

bash
# release.md 中的版本号 = git tag
# 回退到任意历史版本
cd personas/soros && git checkout v1.1.0

# 元学习自动回退:当新版本表现下降时
# system 建议回退到上一个 tag,人工确认后执行

Persona 仓库架构

独立 Repo 层(每个 Persona 独立仓库):
  github.com/xxx/persona-soros       # 独立迭代,独立版本标签
  github.com/xxx/persona-buffett
  github.com/xxx/persona-dalio
  ...

可选组合层(聚合所有 Persona 引用):
  github.com/xxx/personas-composite  # 包含所有 persona repo 作为 submodule
                                      # deep-thought 引用这一个 repo 即可

导入 deep-thought:
  方式 A(逐个引入):
    git submodule add ... personas/soros
    git submodule add ... personas/buffett
    # 灵活,可只引入需要的 persona

  方式 B(通过组合层引入):
    git submodule add ... personas  # 一次引入所有 persona
    # 简单,适合全量使用

备份策略

bash
# 完整备份(复制整个目录)
cp -r ~/.deep-thought/ /backup/deep-thought-$(date +%Y%m%d)/

# 增量备份(仅配置 + 数据库)
tar czf /backup/dt-config-$(date +%Y%m%d).tar.gz ~/.deep-thought/config/
pg_dump ... > /backup/dt-db-$(date +%Y%m%d).dump

# 迁移到新机器
rsync -av ~/.deep-thought/ new-host:~/.deep-thought/

Docker 运行

bash
# 数据目录挂载为 volume,代码在容器内
docker run -v ~/.deep-thought:/data deep-thought:latest

# 容器内通过 DT_DATA_DIR=/data 指向挂载点

配置管理

配置文件采用 YAML 格式(比 .env 更结构化,支持嵌套),存放在 ~/.deep-thought/config/

yaml
# ~/.deep-thought/config/settings.yaml

# 数据目录(默认 ~/.deep-thought,通常不需要改)
data_dir: ~/.deep-thought

# 数据库
database:
  use_embedded: true              # true = embedded-postgres,false = 外部 PG
  url: "postgresql://localhost:5432/deep_thought"  # use_embedded=false 时使用
  port: 5432                      # embedded PG 端口

# LLM —— 多 Provider 多 Model
llm:
  providers:
    anthropic:
      api_key: "sk-ant-..."
      base_url: null              # 自定义 endpoint(可选)
      models:
        - claude-sonnet-4-20250514
        - claude-opus-4-6
        - claude-haiku-4-5-20251001
    openai:
      api_key: "sk-..."
      base_url: null
      models:
        - gpt-4o
        - gpt-4o-mini
    deepseek:
      api_key: "sk-..."
      base_url: "https://api.deepseek.com"
      models:
        - deepseek-chat

  default_model: "anthropic/claude-sonnet-4-20250514"
  temperature: 0.1

  # 任务级模型分配
  task_models:
    narrative_extraction: "anthropic/claude-haiku-4-5-20251001"  # 高频用快模型
    sentiment_scoring: "anthropic/claude-haiku-4-5-20251001"
    persona_analysis: null          # null = 使用 persona_models
    synthesis: "anthropic/claude-opus-4-6"           # 合成用强模型
    debate: "anthropic/claude-sonnet-4-20250514"

# Embedding
embedding:
  provider: "openai"
  model: "text-embedding-3-small"
  api_key: "sk-..."

# 调度
scheduler:
  data_collection: "*/5 * * * *"   # 每5分钟
  sentiment: "0 * * * *"           # 每小时
  narrative_check: "*/10 * * * *"  # 每10分钟
  meta_learning: "0 8 * * 0"       # 每周日 08:00
  snapshot: "0 */6 * * *"          # 每6小时

# 运行模式
runtime:
  debug: false
  log_level: "INFO"
yaml
# ~/.deep-thought/config/personas.yaml

# 全局 Persona 设置
settings:
  max_active_personas: 8          # 最多同时参与分析的 Persona 数量
  min_personas_for_synthesis: 3   # 最少需要几个 Persona 才能启动合成
  default_model: null             # null = 使用主配置的 llm.default_model

# Persona 注册表——每个 submodule 中的 persona 都在此配置
personas:
  soros:
    enabled: true                 # 是否启用
    model: "anthropic/claude-opus-4-6"  # 使用的模型(null = 默认)
    weight_override: null         # 手动覆盖 ensemble 权重(null = 自动计算)
    tags: ["macro", "reflexivity", "contrarian"]

  buffett:
    enabled: true
    model: "anthropic/claude-sonnet-4-20250514"
    weight_override: null
    tags: ["value", "fundamentals", "long_term"]

  dalio:
    enabled: true
    model: "anthropic/claude-sonnet-4-20250514"
    weight_override: null
    tags: ["macro", "cycle", "deleveraging"]

  thorp:
    enabled: true
    model: "openai/gpt-4o"
    weight_override: null
    tags: ["quantitative", "probability", "kelly"]

  simons:
    enabled: true
    model: "openai/gpt-4o"
    weight_override: null
    tags: ["quantitative", "data_driven", "statistical"]

  zhou_jintao:
    enabled: true
    model: "deepseek/deepseek-chat"
    weight_override: null
    tags: ["cycle", "kondratiev", "commodity"]

  # 新增 Persona 只需:
  # 1. git submodule add <repo> personas/<name>
  # 2. 在此文件添加配置条目
yaml
# ~/.deep-thought/config/data_sources.yaml

# 数据源 API Keys
openbb:
  personal_access_token: "..."

ibkr:
  host: "127.0.0.1"
  port: 7497
  client_id: 1

longbridge:
  app_key: "..."
  app_secret: "..."
  access_token: "..."

binance:
  api_key: ""                   # 公开数据可留空
  api_secret: ""

配置加载逻辑

python
# src/deep_thought/config.py
from pathlib import Path
from pydantic import BaseModel
import yaml

class Settings:
    """配置管理——从 ~/.deep-thought/config/ 加载"""

    def __init__(self, data_dir: str | None = None):
        # 1. 确定数据目录
        self.data_dir = Path(
            data_dir
            or os.environ.get("DT_DATA_DIR")
            or Path.home() / ".deep-thought"
        )

        # 2. 确保目录存在
        for subdir in ["config", "db", "cache", "logs", "output", "personas"]:
            (self.data_dir / subdir).mkdir(parents=True, exist_ok=True)

        # 3. 加载配置文件
        self.main = self._load_yaml("config/settings.yaml")
        self.personas = self._load_yaml("config/personas.yaml")
        self.data_sources = self._load_yaml("config/data_sources.yaml")

        # 4. 环境变量覆盖(优先级最高)
        self._apply_env_overrides()

    def get_model_for_persona(self, persona_id: str) -> str:
        """获取 Persona 使用的模型"""
        return (
            self.personas.get("persona_models", {}).get(persona_id)
            or self.main["llm"]["default_model"]
        )

    def get_model_for_task(self, task_type: str) -> str:
        """获取任务使用的模型"""
        task_model = self.main["llm"]["task_models"].get(task_type)
        if task_model is None:
            return self.main["llm"]["default_model"]
        return task_model

    def _load_yaml(self, relative_path: str) -> dict:
        filepath = self.data_dir / relative_path
        if not filepath.exists():
            return {}
        with open(filepath) as f:
            return yaml.safe_load(f) or {}

首次运行初始化

python
# scripts/init_deep_thought.py
"""首次运行:创建数据目录 + 生成默认配置文件 + 初始化数据库"""

def init():
    settings = Settings()  # 自动创建目录

    # 生成默认配置文件(如果不存在)
    if not (settings.data_dir / "config/settings.yaml").exists():
        shutil.copy("defaults/settings.yaml", settings.data_dir / "config/settings.yaml")
        print(f"已生成默认配置: {settings.data_dir}/config/settings.yaml")
        print("请编辑配置文件,填入 API keys")

    # 初始化数据库
    if settings.main["database"]["use_embedded"]:
        init_embedded_pg(settings.data_dir / "db/pg")

    run_migrations()
    sync_personas(settings.data_dir / "personas")

    print("初始化完成!")
    print(f"数据目录: {settings.data_dir}")
    print(f"配置文件: {settings.data_dir}/config/")

本地开发环境

首次启动(零外部依赖)

bash
# 1. 克隆项目(含 submodules)
git clone --recurse-submodules ...
cd deep-thought

# 或分步拉取 submodule
git submodule init
git submodule update

# 2. 安装依赖
pip install -e ".[dev]"

# 3. 初始化数据目录和数据库
python scripts/init_deep_thought.py
# 自动生成 ~/.deep-thought/ 目录结构
# 复制默认配置到 ~/.deep-thought/config/
# 启动 embedded-postgres 并初始化表结构

# 4. 编辑配置,填入 API keys
vim ~/.deep-thought/config/settings.yaml
vim ~/.deep-thought/config/data_sources.yaml
vim ~/.deep-thought/config/personas.yaml

# 5. 运行测试
pytest

# 6. 启动系统
python -m deep_thought.main
# 或指定数据目录:
DT_DATA_DIR=/custom/path python -m deep_thought.main

更新 Persona(独立于主项目版本)

bash
# 更新单个 Persona 到最新版本
cd personas/soros && git pull origin main && cd ../..

# 更新所有 Personas
git submodule update --remote

# 切换某个 Persona 到特定版本
cd personas/buffett && git checkout v2.1 && cd ../..

# 添加新 Persona
git submodule add git@github.com:xxx/persona-ray_dalio.git personas/ray_dalio
# 然后在 ~/.deep-thought/config/personas.yaml 中添加配置

embedded-postgres 自动管理

python
# src/deep_thought/core/db.py
from embedded_postgres import Postgres
from sqlalchemy.ext.asyncio import create_async_engine

_pg_instance: Postgres | None = None

async def get_engine(settings: Settings):
    global _pg_instance

    if settings.main["database"]["use_embedded"]:
        if _pg_instance is None:
            pg_data_dir = settings.data_dir / "db" / "pg"
            _pg_instance = Postgres(
                database_url="postgresql://localhost:5432/deep_thought",
                port=settings.main["database"]["port"],
                data_dir=str(pg_data_dir),      # 数据存在 ~/.deep-thought/db/pg/
            )
            _pg_instance.start()
        url = _pg_instance.url()
    else:
        url = settings.main["database"]["url"]

    return create_async_engine(url, echo=settings.runtime.get("debug", False))

测试策略

三层测试

单元测试 (tests/unit/)
  - 每个模块独立测试,mock 外部依赖
  - 覆盖率目标:核心逻辑 > 90%
  - 重点:指标计算、状态机转换、错配公式、EWMA 更新

集成测试 (tests/integration/)
  - 测试引擎间协作,使用真实数据库
  - 使用 embedded-postgres,不依赖外部服务
  - 重点:事件总线传递、数据持久化、Persona 同步

回测 (tests/backtest/)
  - 用历史数据验证系统判断准确性
  - 不追求实时性,关注信号质量
  - 重点:Persona 准确率、错配信号有效性

测试数据

python
# tests/fixtures/
# - mock_indicators.json     # 模拟指标数据
# - mock_narratives.json     # 模拟叙事数据
# - mock_news.json           # 模拟新闻输入
# - historical_snapshots/    # 历史快照(用于回测)

日志

python
# 使用 Python 标准 logging,结构化输出
import structlog

logger = structlog.get_logger()

# 日志格式
# - 开发环境:彩色控制台输出
# - 生产环境:JSON 格式(便于聚合分析)

# 日志级别
# - DEBUG:每次 LLM 调用的输入输出
# - INFO:引擎运行状态、事件发布
# - WARNING:数据源降级、异常指标
# - ERROR:引擎故障、LLM 调用失败

关键依赖版本锁定

Python:         >= 3.11
PostgreSQL:     >= 15 (embedded-postgres 自带)
pgvector:       >= 0.3
SQLAlchemy:     >= 2.0
Pydantic:       >= 2.7
LiteLLM:        >= 1.40

已讨论决定

  • [x] Monorepo 结构,src/deep_thought/ 布局
  • [x] pyproject.toml 管理依赖
  • [x] Pydantic Settings 统一配置
  • [x] embedded-postgres 前期零配置
  • [x] 三层测试:单元 / 集成 / 回测
  • [x] structlog 结构化日志

待后续确定

  • [ ] Embedding 模型选型
  • [ ] 云部署方案
  • [ ] CI/CD 工具选型
  • [ ] 监控和告警工具

关联文档:规范总览 · 技术栈选型 · 实现阶段与验收标准

基于 VitePress 构建