开发与运行环境
定义项目的目录结构、依赖管理、配置方案、测试策略和运维基础。
原则:前期零外部依赖,渐进式引入基础设施。
项目目录结构
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 工具选型
- [ ] 监控和告警工具