Week 9-10:项目架构设计
🎯 本周目标:设计整体架构,把 Month 1-2 的代码组织成一个清晰、可维护的项目结构。
1. 项目总体架构
flowchart TD
subgraph 前端 "前端(可选,用你熟悉的 React/Vue)"
F1[对话 UI]
F2[WebSocket / SSE]
end
subgraph 后端 FastAPI
B1[main.py<br/>入口 + CORS]
B2[routes/<br/>路由层]
B3[agent/<br/>LangGraph Agent]
B4[rag/<br/>RAG Pipeline]
B5[llm/<br/>LLM 适配层]
end
subgraph 数据层
D1[(ChromaDB<br/>向量库)]
D2[(SQLite<br/>会话存储)]
D3[(Mock 数据)]
end
subgraph 外部服务
E1[硅基流动 / Deepseek<br/>LLM API]
E2[Mock 行情 API]
end
F1 --> F2
F2 --> B1
B1 --> B2
B2 --> B3
B3 --> B4
B3 --> B5
B4 --> D1
B3 --> D2
B3 --> D3
B5 --> E1
B3 --> E2
分层职责
| 层 | 职责 | TS 类比 |
|---|---|---|
| routes/ | HTTP 路由,参数校验,返回格式 | Express.js 的 routes |
| agent/ | LangGraph 图编排 + Tool 定义 | 业务逻辑层 |
| rag/ | 文档加载、分块、向量检索 | 数据访问层 |
| llm/ | LLM 客户端封装(统一接口) | SDK / Adapter |
| models/ | Pydantic 数据模型 | zod schema / interface |
2. 推荐的项目目录结构
electric-trading-ai/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI 入口
│ │
│ ├── config.py # 配置(API Key、模型名、端口等)
│ │
│ ├── models/ # 数据模型
│ │ ├── __init__.py
│ │ ├── schemas.py # Pydantic 请求/响应模型
│ │ └── state.py # LangGraph State 定义
│ │
│ ├── routes/ # 路由层
│ │ ├── __init__.py
│ │ ├── chat.py # 对话相关(含流式)
│ │ ├── trading.py # 交易相关
│ │ └── health.py # 健康检查
│ │
│ ├── agent/ # LangGraph Agent
│ │ ├── __init__.py
│ │ ├── graph.py # StateGraph 定义
│ │ ├── nodes.py # 各节点处理函数
│ │ ├── tools.py # Tool 定义
│ │ └── prompts.py # System Prompt 模板
│ │
│ ├── rag/ # RAG Pipeline
│ │ ├── __init__.py
│ │ ├── pipeline.py # 加载/分块/向量化
│ │ ├── retriever.py # 检索器
│ │ └── chroma_client.py # ChromaDB 连接管理
│ │
│ ├── llm/ # LLM 适配层
│ │ ├── __init__.py
│ │ └── client.py # 统一 LLM 客户端
│ │
│ └── utils/ # 工具函数
│ ├── __init__.py
│ └── helpers.py
│
├── data/ # 运行时数据
│ ├── chroma_db/ # ChromaDB 持久化目录
│ └── sessions.db # SQLite 会话存储
│
├── docs/ # VuePress 学习文档(就是你现在看的)
├── .env.example
├── .gitignore
├── pyproject.toml
├── README.md
└── Dockerfile # Month 3 Week 12 会加
3. 配置层设计(config.py)
把所有配置集中管理,用 .env 文件存储敏感信息:
# app/config.py
from pydantic_settings import BaseSettings
from pathlib import Path
class Settings(BaseSettings):
"""应用配置,从 .env 文件自动加载"""
# LLM 配置
llm_provider: str = "siliconflow" # siliconflow | deepseek | openai
llm_model: str = "Qwen/Qwen2.5-7B-Instruct"
llm_api_key: str = ""
llm_base_url: str = "https://api.siliconflow.cn/v1"
# Embedding 配置
embedding_model: str = "shibing624/text2vec-base-chinese"
# 应用配置
app_host: str = "0.0.0.0"
app_port: int = 8000
debug: bool = True
# 数据路径
base_dir: Path = Path(__file__).resolve().parent.parent
chroma_persist_dir: Path = Path("./data/chroma_db")
session_db_path: Path = Path("./data/sessions.db")
model_config = {
"env_file": ".env",
"env_file_encoding": "utf-8",
}
# 全局单例
settings = Settings()
.env.example
# ===== LLM 配置 =====
LLM_PROVIDER=siliconflow
LLM_MODEL=Qwen/Qwen2.5-7B-Instruct
LLM_API_KEY=your-api-key-here
LLM_BASE_URL=https://api.siliconflow.cn/v1
# ===== 应用配置 =====
APP_HOST=0.0.0.0
APP_PORT=8000
DEBUG=true
# ===== Embedding =====
EMBEDDING_MODEL=shibing624/text2vec-base-chinese
💡 TS 对照:这相当于
dotenv+process.env,但更安全——Pydantic 会在启动时校验类型。
4. 关键接口设计
REST API 接口
| 方法 | 路径 | 功能 | 请求体 | 响应 |
|---|---|---|---|---|
| POST | /api/chat | Agent 对话(非流式) | ChatRequest | ChatResponse |
| POST | /api/chat/stream | Agent 对话(流式) | ChatRequest | SSE 流 |
| GET | /api/chat/history/{session_id} | 获取会话历史 | - | list[Message] |
| POST | /api/trading/orders | 下单 | PlaceOrderRequest | OrderResponse |
| GET | /api/trading/orders | 订单列表 | - | list[OrderResponse] |
| DELETE | /api/trading/orders/{id} | 取消订单 | - | {"ok": true} |
| GET | /health | 健康检查 | - | HealthResponse |
数据模型
# app/models/schemas.py
from pydantic import BaseModel, Field
from typing import Literal, Optional
class ChatRequest(BaseModel):
user_input: str = Field(..., description="用户输入")
session_id: Optional[str] = Field(None, description="会话 ID(多轮对话)")
class ChatResponse(BaseModel):
session_id: str
answer: str
tools_used: list[str] = []
class TradingOrderSide = Literal["buy", "sell"]
class PlaceOrderRequest(BaseModel):
symbol: str
side: TradingOrderSide
price: Optional[float] = Field(None, gt=0)
quantity: int = Field(gt=0)
class OrderResponse(BaseModel):
order_id: str
symbol: str
side: TradingOrderSide
quantity: int
price: Optional[float]
status: Literal["pending", "filled", "cancelled", "failed"]
created_at: str
class HealthResponse(BaseModel):
status: Literal["ok"]
version: str
llm_connected: bool
5. 流式输出设计
前端要的是逐字显示的效果(打字机效果),所以后端需要用 SSE(Server-Sent Events)。
为什么不用 WebSocket?
| 技术 | 适用场景 | 复杂度 |
|---|---|---|
| SSE | 服务器向客户端单向推送(聊天回复) | 低,HTTP 协议原生支持 |
| WebSocket | 双向实时通信(多人协作、游戏) | 高,需要握手和状态管理 |
聊天场景用 SSE 就够了。
LangGraph 流式 + FastAPI SSE
# 核心思路伪代码
from fastapi.responses import StreamingResponse
async def stream_chat(user_input: str):
# LangGraph 的 astream() 会 yield 每个节点的输出
async for chunk in app.astream(initial_state):
# 把每个 chunk 转成 SSE 格式
yield f"data: {json.dumps(chunk)}\n\n"
6. 本周任务清单
| # | 任务 | 产出 |
|---|---|---|
| 1 | 按上面的目录结构创建所有文件夹和空的 __init__.py | 骨架目录 |
| 2 | 写好 config.py + .env + .env.example | 配置层 |
| 3 | 写好所有 Pydantic 数据模型(schemas.py) | 类型定义 |
| 4 | 拆分 graph.py / nodes.py / tools.py / prompts.py,把 Month 2 Week 5 的 LangGraph 代码拆开 | Agent 模块化 |
| 5 | 设计 SSE 流式接口路由(先写 schema,不写实现) | API 契约 |