Luxx/asserts/ARCHITECTURE.md

489 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Luxx 项目架构
## 技术栈
- **框架**: FastAPI 0.109+
- **数据库**: SQLAlchemy 2.0+ (同步模式)
- **认证**: JWT (PyJWT)
- **HTTP客户端**: httpx, requests
- **配置**: YAML (PyYAML)
- **代码执行**: Python 原生执行
- **网页爬虫**:
- `httpx` - HTTP 客户端
- `beautifulsoup4` - HTML 解析
- `lxml` - XML/HTML 解析器
## 目录结构
```
luxx/
├── __init__.py # FastAPI 应用工厂
├── config.py # 配置管理YAML
├── database.py # 数据库连接
├── models.py # ORM 模型
├── routes/ # API 路由层
│ ├── __init__.py # 路由聚合
│ ├── auth.py # 认证 (登录/注册)
│ ├── conversations.py # 会话管理 (CRUD)
│ ├── messages.py # 消息处理 (流式/同步)
│ ├── providers.py # LLM 提供商管理
│ └── tools.py # 工具管理
├── services/ # 服务层
│ ├── chat.py # 聊天服务 (Agentic Loop)
│ └── llm_client.py # LLM 客户端
├── tools/ # 工具系统
│ ├── core.py # 核心类 (ToolRegistry, ToolDefinition, ToolResult)
│ ├── factory.py # @tool 装饰器
│ ├── executor.py # 工具执行器 (缓存/并行)
│ ├── services.py # 工具服务层
│ └── builtin/ # 内置工具
│ ├── __init__.py # 工具注册入口
│ ├── code.py # 代码执行 (python_execute, python_eval)
│ ├── crawler.py # 网页爬虫 (web_search, web_fetch, batch_fetch)
│ └── data.py # 数据处理 (process_data)
└── utils/ # 工具函数
└── helpers.py # 密码哈希、ID生成、响应封装
run.py # 应用入口文件
config.yaml # 配置文件
```
## 核心组件
### 1. 应用工厂 (`__init__.py`)
FastAPI 应用入口,使用 lifespan 管理生命周期:
- 启动:初始化数据库、注册内置工具、创建默认管理员用户
- 关闭:清理资源
```python
# 默认管理员账号
username: admin
password: admin123
```
### 2. 配置管理 (`config.py`)
使用 YAML 文件管理配置:
- 配置文件:`config.yaml`
- 环境变量替换:`${VAR_NAME}`
- 单例模式全局访问
- 默认值支持
```yaml
# config.yaml 示例
app:
secret_key: ${APP_SECRET_KEY}
debug: true
database:
type: sqlite
url: sqlite:///./chat.db
llm:
provider: deepseek
api_key: ${DEEPSEEK_API_KEY}
api_url: https://api.deepseek.com/v1
```
### 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
}
PROJECT {
string id PK
int user_id FK
string name
text description
datetime created_at
datetime updated_at
}
CONVERSATION {
string id PK
int user_id FK
int provider_id FK "optional"
string project_id FK "optional"
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
text usage "JSON 格式"
datetime created_at
}
LLM_PROVIDER {
int id PK
int user_id FK
string name
string provider_type
string base_url
string api_key
string default_model
int max_tokens
boolean is_default
boolean enabled
datetime created_at
datetime updated_at
}
USER ||--o{ PROJECT : "has"
USER ||--o{ CONVERSATION : "has"
USER ||--o{ LLM_PROVIDER : "configures"
PROJECT ||--o{ CONVERSATION : "contains"
LLM_PROVIDER ||--o{ CONVERSATION : "uses"
CONVERSATION ||--o{ MESSAGE : "has"
```
### 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": "...", "success": true}
]
}
```
`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~
+list_by_category(category) List~dict~
+execute(name, arguments) dict
+remove(name) bool
}
class ToolExecutor {
+enable_cache: bool
+cache_ttl: int
+max_workers: int
+_cache: Dict
+_call_history: List
+process_tool_calls(tool_calls, context) list
+process_tool_calls_parallel(tool_calls, context) list
+clear_cache() void
+get_history(limit) List
}
```
#### 内置工具
| 工具 | 功能 | 说明 |
|------|------|------|
| `python_execute` | 执行 Python 代码 | 支持 print 输出、变量访问 |
| `python_eval` | 计算表达式 | 快速求值 |
| `web_search` | DuckDuckGo HTML | DuckDuckGo HTML 搜索 |
| `web_fetch` | 网页抓取 | httpx + BeautifulSoup支持 text/links/structured |
| `batch_fetch` | 批量抓取 | 并发获取多个页面 |
| `process_data` | 数据处理 | JSON 转换、格式化等 |
#### 工具开发规范
所有工具必须遵循统一的开发规范,确保错误处理和返回格式一致。
**核心原则:装饰器自动处理一切,工具函数只写业务逻辑**
```python
from luxx.tools.factory import tool
@tool(
name="my_tool",
description="工具描述",
parameters={...},
required_params=["arg1"], # 自动验证
category="my_category"
)
def my_tool(arguments: dict):
# 业务逻辑 - 只管返回数据
data = fetch_data(arguments["arg1"])
return {"items": data, "count": len(data)}
# 或者直接抛出异常(装饰器自动捕获并转换)
if invalid:
raise ValueError("Invalid input")
```
**装饰器自动处理:**
1. 必需参数验证(`required_params`
2. 所有异常捕获和转换
3. 结果格式统一包装
**返回格式转换**
| 工具函数返回/抛出 | 装饰器转换为 |
|-------------------|-------------|
| `return {"result": "ok"}` | `{"success": true, "data": {...}, "error": null}` |
| `raise ValueError("msg")` | `{"success": false, "data": null, "error": "ValueError: msg"}` |
| `raise Exception()` | `{"success": false, "data": null, "error": "..."}` |
**工具调用流程**
```
LLM 请求
ToolRegistry.execute(name, args)
@tool 装饰器
├─ 验证 required_params
├─ 执行工具函数 (try-except 包裹)
├─ 捕获异常 → 转换为 error
├─ 包装返回格式
└─ 返回 ToolResult
ToolExecutor 返回结果
前端 ProcessBlock 显示
```
### 6. 服务层
#### ChatService (`services/chat.py`)
核心聊天服务:
- Agentic Loop 迭代执行(最多 10 轮)
- 流式 SSE 响应
- 工具调用编排(并行执行)
- 消息历史管理
- 自动重试机制
- 支持 thinking_content 提取
- Token 用量追踪
#### 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 | 会话列表(分页) |
| `/conversations` | POST | 创建会话 |
| `/conversations/{id}` | GET | 会话详情 |
| `/conversations/{id}` | PUT | 更新会话 |
| `/conversations/{id}` | DELETE | 删除会话 |
| `/messages` | GET | 消息列表 |
| `/messages` | POST | 发送消息(同步) |
| `/messages/stream` | POST | 发送消息(流式 SSE |
| `/messages/{id}` | DELETE | 删除消息 |
| `/providers` | GET | LLM 提供商列表 |
| `/providers` | POST | 创建提供商 |
| `/providers/{id}` | GET | 提供商详情 |
| `/providers/{id}` | PUT | 更新提供商 |
| `/providers/{id}` | DELETE | 删除提供商 |
| `/providers/{id}/test` | POST | 测试提供商连接 |
| `/tools` | GET | 可用工具列表 |
| `/health` | GET | 健康检查 |
| `/` | 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, thinking_enabled}
API->>CS: stream_response()
loop MAX_ITERATIONS (10)
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->>CS: _save_message()
CS->>API: SSE Stream
API-->>Client: 流式响应
```
## SSE 事件
| 事件 | 说明 |
|------|------|
| `process_step` | 结构化步骤thinking/text/tool_call/tool_result携带 `id`、`index` 确保渲染顺序 |
| `done` | 响应完成,携带 message_id、token_count、usage |
| `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 的返回结果) |
| `success` | 工具执行是否成功(仅 tool_result |
### done 事件格式
```json
{"type": "done", "message_id": "uuid", "token_count": 1234, "usage": {"prompt_tokens": 100, "completion_tokens": 200, "total_tokens": 300}}
```
## 配置示例
### 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` |
## 项目结构说明
### 入口文件
- `run.py` - 启动 Uvicorn 服务器
### 响应格式
所有 API 统一使用响应封装:
```json
// 成功
{"success": true, "data": {...}, "message": "操作成功"}
// 错误
{"success": false, "error": "错误信息", "code": 404}
```
### 工具缓存机制
ToolExecutor 支持结果缓存:
- TTL: 5 分钟(可配置)
- 缓存 Key: `{tool_name}:{sorted_arguments_json}`
- 调用历史记录最近 1000 条
### 流式响应特点
1. 实时返回 thinking_content模型思考过程
2. 实时返回 text 增量更新
3. 工具调用串行执行,结果批量返回
4. 最终 `done` 事件包含完整 message_id 和 token 用量