339 lines
8.7 KiB
Markdown
339 lines
8.7 KiB
Markdown
# 项目架构
|
||
|
||
## 技术栈
|
||
|
||
- **框架**: FastAPI 0.109+
|
||
- **数据库**: SQLAlchemy 2.0+
|
||
- **认证**: JWT (PyJWT)
|
||
- **HTTP客户端**: httpx
|
||
- **配置**: YAML (PyYAML)
|
||
- **代码执行**: Python 原生执行
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
luxx/
|
||
├── __init__.py # FastAPI 应用工厂
|
||
├── run.py # 入口文件
|
||
├── config.py # 配置管理(YAML)
|
||
├── database.py # 数据库连接
|
||
├── models.py # ORM 模型
|
||
├── routes/ # API 路由层
|
||
│ ├── auth.py # 认证
|
||
│ ├── conversations.py # 会话管理
|
||
│ ├── messages.py # 消息处理
|
||
│ ├── providers.py # LLM 提供商管理
|
||
│ └── tools.py # 工具管理
|
||
├── services/ # 服务层
|
||
│ ├── chat.py # 聊天服务
|
||
│ └── llm_client.py # LLM 客户端
|
||
├── tools/ # 工具系统
|
||
│ ├── core.py # 核心类
|
||
│ ├── factory.py # 装饰器
|
||
│ ├── executor.py # 执行器
|
||
│ └── builtin/ # 内置工具
|
||
│ ├── code.py # 代码执行
|
||
│ ├── crawler.py # 网页爬虫
|
||
│ ├── data.py # 数据处理
|
||
│ └── weather.py # 天气查询
|
||
└── utils/ # 工具函数
|
||
└── helpers.py
|
||
```
|
||
|
||
## 核心组件
|
||
|
||
### 1. 应用工厂 (`__init__.py`)
|
||
FastAPI 应用入口,使用 lifespan 管理生命周期:
|
||
- 启动:初始化数据库、加载工具
|
||
- 关闭:清理资源
|
||
|
||
### 2. 配置管理 (`config.py`)
|
||
使用 YAML 文件管理配置:
|
||
- 配置文件:`config.yaml`
|
||
- 环境变量替换:`${VAR_NAME}`
|
||
- 单例模式全局访问
|
||
- 默认值支持
|
||
|
||
```yaml
|
||
# config.yaml 示例
|
||
app:
|
||
secret_key: ${APP_SECRET_KEY}
|
||
debug: true
|
||
|
||
llm:
|
||
provider: deepseek
|
||
api_key: ${DEEPSEEK_API_KEY}
|
||
```
|
||
|
||
### 3. 数据库 (`database.py`)
|
||
- SQLAlchemy 异步支持
|
||
- SQLite 默认数据库
|
||
- 依赖注入获取会话
|
||
|
||
### 4. ORM 模型 (`models.py`)
|
||
|
||
```mermaid
|
||
erDiagram
|
||
USER {
|
||
int id PK
|
||
string username UK
|
||
string email UK
|
||
string password_hash
|
||
string role
|
||
boolean is_active
|
||
datetime created_at
|
||
}
|
||
|
||
CONVERSATION {
|
||
string id PK
|
||
int user_id FK
|
||
string project_id FK
|
||
string title
|
||
string model
|
||
text system_prompt
|
||
float temperature
|
||
int max_tokens
|
||
boolean thinking_enabled
|
||
datetime created_at
|
||
datetime updated_at
|
||
}
|
||
|
||
MESSAGE {
|
||
string id PK
|
||
string conversation_id FK
|
||
string role
|
||
longtext content "JSON 格式"
|
||
int token_count
|
||
datetime created_at
|
||
}
|
||
|
||
USER ||--o{ CONVERSATION : "has"
|
||
CONVERSATION ||--o{ MESSAGE : "has"
|
||
|
||
USER ||--o{ LLM_PROVIDER : "configures"
|
||
|
||
LLM_PROVIDER {
|
||
int id PK
|
||
int user_id FK
|
||
string name
|
||
string provider_type
|
||
string base_url
|
||
string api_key
|
||
string default_model
|
||
boolean is_default
|
||
boolean enabled
|
||
datetime created_at
|
||
datetime updated_at
|
||
}
|
||
```
|
||
|
||
### Message Content JSON 结构
|
||
|
||
`content` 字段统一使用 JSON 格式存储:
|
||
|
||
**User 消息:**
|
||
|
||
```json
|
||
{
|
||
"text": "用户输入的文本内容",
|
||
"attachments": [
|
||
{"name": "utils.py", "extension": "py", "content": "..."}
|
||
]
|
||
}
|
||
```
|
||
|
||
**Assistant 消息:**
|
||
|
||
```json
|
||
{
|
||
"text": "AI 回复的文本内容",
|
||
"tool_calls": [...],
|
||
"steps": [
|
||
{"id": "step-0", "index": 0, "type": "thinking", "content": "..."},
|
||
{"id": "step-1", "index": 1, "type": "text", "content": "..."},
|
||
{"id": "step-2", "index": 2, "type": "tool_call", "id_ref": "call_xxx", "name": "...", "arguments": "..."},
|
||
{"id": "step-3", "index": 3, "type": "tool_result", "id_ref": "call_xxx", "name": "...", "content": "..."}
|
||
]
|
||
}
|
||
```
|
||
|
||
`steps` 字段是**渲染顺序的唯一数据源**,按 `index` 顺序排列。thinking、text、tool_call、tool_result 可以在多轮迭代中穿插出现。
|
||
|
||
### 5. 工具系统
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class ToolDefinition {
|
||
+str name
|
||
+str description
|
||
+dict parameters
|
||
+Callable handler
|
||
+str category
|
||
+to_openai_format() dict
|
||
}
|
||
|
||
class ToolResult {
|
||
+bool success
|
||
+Any data
|
||
+str error
|
||
+to_dict() dict
|
||
+ok(data) ToolResult$
|
||
+fail(error) ToolResult$
|
||
}
|
||
|
||
class ToolRegistry {
|
||
+_tools: Dict
|
||
+register(tool) void
|
||
+get(name) ToolDefinition?
|
||
+list_all() List~dict~
|
||
+execute(name, arguments) dict
|
||
}
|
||
|
||
class ToolExecutor {
|
||
+registry: ToolRegistry
|
||
+enable_cache: bool
|
||
+cache_ttl: int
|
||
+_cache: Dict
|
||
+process_tool_calls(tool_calls, context) list
|
||
+process_tool_calls_parallel(tool_calls, context, max_workers) list
|
||
}
|
||
```
|
||
|
||
#### 内置工具
|
||
|
||
| 工具 | 功能 | 说明 |
|
||
|------|------|------|
|
||
| `python_execute` | 执行 Python 代码 | 支持 print 输出、变量访问 |
|
||
| `python_eval` | 计算表达式 | 快速求值 |
|
||
| `web_crawl` | 网页抓取 | BeautifulSoup + httpx |
|
||
| `get_weather` | 天气查询 | 支持城市名查询 |
|
||
| `process_data` | 数据处理 | JSON 转换、格式化等 |
|
||
|
||
### 6. 服务层
|
||
|
||
#### ChatService (`services/chat.py`)
|
||
核心聊天服务:
|
||
- Agentic Loop 迭代执行
|
||
- 流式 SSE 响应
|
||
- 工具调用编排
|
||
- 消息历史管理
|
||
- 自动重试机制
|
||
|
||
#### LLMClient (`services/llm_client.py`)
|
||
LLM API 客户端:
|
||
- 多提供商:DeepSeek、GLM、OpenAI
|
||
- 流式/同步调用
|
||
- 错误处理和重试
|
||
- Token 计数
|
||
|
||
### 7. 认证系统 (`routes/auth.py`)
|
||
- JWT Bearer Token
|
||
- Bcrypt 密码哈希
|
||
- 用户注册/登录
|
||
|
||
### 8. API 路由
|
||
|
||
| 路由 | 方法 | 说明 |
|
||
|------|------|------|
|
||
| `/auth/register` | POST | 用户注册 |
|
||
| `/auth/login` | POST | 用户登录 |
|
||
| `/conversations` | GET/POST | 会话列表/创建 |
|
||
| `/conversations/{id}` | GET/DELETE | 会话详情/删除 |
|
||
| `/messages/stream` | POST | 流式消息发送 |
|
||
| `/providers` | GET/POST | LLM 提供商列表/创建 |
|
||
| `/providers/{id}` | GET/PUT/DELETE | 提供商详情/更新/删除 |
|
||
| `/providers/{id}/test` | POST | 测试提供商连接 |
|
||
| `/tools` | GET | 可用工具列表 |
|
||
|
||
## 数据流
|
||
|
||
### 消息处理流程
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Client
|
||
participant API as POST /messages/stream
|
||
participant CS as ChatService
|
||
participant LLM as LLM API
|
||
participant TE as ToolExecutor
|
||
|
||
Client->>API: POST {content, tools}
|
||
API->>CS: stream_response()
|
||
|
||
loop MAX_ITERATIONS
|
||
CS->>LLM: call(messages, tools)
|
||
LLM-->>CS: SSE Stream
|
||
|
||
alt tool_calls
|
||
CS->>TE: process_tool_calls_parallel()
|
||
TE-->>CS: tool_results
|
||
CS->>CS: 追加到 messages
|
||
end
|
||
end
|
||
|
||
CS->>API: SSE Stream
|
||
API-->>Client: 流式响应
|
||
```
|
||
|
||
## SSE 事件
|
||
|
||
| 事件 | 说明 |
|
||
|------|------|
|
||
| `process_step` | 结构化步骤(thinking/text/tool_call/tool_result),携带 `id`、`index` 确保渲染顺序 |
|
||
| `done` | 响应完成 |
|
||
| `error` | 错误信息 |
|
||
|
||
### process_step 事件格式
|
||
|
||
```json
|
||
{"type": "process_step", "step": {"id": "step-0", "index": 0, "type": "thinking", "content": "..."}}
|
||
{"type": "process_step", "step": {"id": "step-1", "index": 1, "type": "text", "content": "回复文本..."}}
|
||
{"type": "process_step", "step": {"id": "step-2", "index": 2, "type": "tool_call", "id_ref": "call_abc", "name": "web_search", "arguments": "{\"query\": \"...\"}"}}
|
||
{"type": "process_step", "step": {"id": "step-3", "index": 3, "type": "tool_result", "id_ref": "call_abc", "name": "web_search", "content": "{\"success\": true, ...}"}}
|
||
```
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| `id` | 步骤唯一标识(格式 `step-{index}`) |
|
||
| `index` | 步骤序号,确保按正确顺序显示 |
|
||
| `type` | 步骤类型:`thinking` / `text` / `tool_call` / `tool_result` |
|
||
| `id_ref` | 工具调用引用 ID(仅 tool_call/tool_result) |
|
||
| `name` | 工具名称(仅 tool_call/tool_result) |
|
||
| `arguments` | 工具调用参数 JSON 字符串(仅 tool_call) |
|
||
| `content` | 内容(thinking 的思考内容、text 的文本、tool_result 的返回结果) |
|
||
|
||
## 配置示例
|
||
|
||
### config.yaml
|
||
|
||
```yaml
|
||
app:
|
||
secret_key: ${APP_SECRET_KEY}
|
||
debug: true
|
||
host: 0.0.0.0
|
||
port: 8000
|
||
|
||
database:
|
||
type: sqlite
|
||
url: sqlite:///./chat.db
|
||
|
||
llm:
|
||
provider: deepseek
|
||
api_key: ${DEEPSEEK_API_KEY}
|
||
api_url: https://api.deepseek.com/v1
|
||
|
||
tools:
|
||
enable_cache: true
|
||
cache_ttl: 300
|
||
max_workers: 4
|
||
max_iterations: 10
|
||
```
|
||
|
||
## 环境变量
|
||
|
||
| 变量 | 说明 | 示例 |
|
||
|------|------|------|
|
||
| `APP_SECRET_KEY` | 应用密钥 | `your-secret-key` |
|
||
| `DEEPSEEK_API_KEY` | DeepSeek API | `sk-xxxx` |
|
||
| `DATABASE_URL` | 数据库连接 | `sqlite:///./chat.db` |
|