Luxx/assets/ARCHITECTURE.md

800 lines
21 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/ # 服务层
│ ├── __init__.py # 服务导出
│ ├── chat.py # 聊天服务门面
│ ├── agentic_loop.py # Agentic Loop 执行器
│ ├── stream_context.py # 流式状态管理
│ ├── llm_response.py # LLM 响应数据类
│ ├── process_result.py # [已移除]
│ ├── task.py # 任务系统 (Task/TaskGraph/TaskService)
│ ├── llm_client.py # LLM 客户端
│ └── llm_adapters/ # LLM API 适配器
│ ├── __init__.py # 适配器导出
│ ├── base.py # ProviderAdapter 基类
│ ├── openai_adapter.py # OpenAI/DeepSeek/GLM 适配器
│ └── anthropic_adapter.py # Anthropic Claude 适配器
├── tools/ # 工具系统
│ ├── __init__.py # 工具注册入口
│ ├── core.py # 核心类 (ToolRegistry, ToolDefinition, ToolResult, ToolContext)
│ ├── 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)
│ ├── file.py # 文件操作 (file_read, file_write, file_list, file_exists, file_grep)
│ └── shell.py # Shell 命令 (shell_execute)
└── utils/ # 工具函数
├── __init__.py
└── 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
host: 0.0.0.0
port: 8000
database:
type: sqlite
url: sqlite:///./chat.db
workspace:
root: ./workspaces # 用户工作空间根目录
auto_create: true # 自动创建用户目录
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
logging:
level: INFO
```
**工作空间隔离机制:**
- 每个用户的工作空间路径基于 `user_id` 的 SHA256 哈希值
- 格式:`{workspace_root}/{hash_of_user_id}`
- 所有文件操作必须在用户工作空间内,防止路径穿越攻击
```
### 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
int permission_level "1=READ_ONLY, 2=WRITE, 3=EXECUTE, 4=ADMIN"
string workspace_path "用户工作空间路径"
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"
```
**用户权限级别 (permission_level)**
| 级别 | 名称 | 说明 |
|------|------|------|
| 1 | READ_ONLY | 只读权限 |
| 2 | WRITE | 写入权限(文件写入) |
| 3 | EXECUTE | 执行权限代码执行、Shell命令 |
| 4 | ADMIN | 管理员权限 |
### Message Content JSON 结构
`content` 字段统一使用 JSON 格式存储:
**User 消息:**
```json
{
"text": "用户输入的文本内容",
"attachments": [
{"name": "utils.py", "extension": "py", "content": "..."}
]
}
```
**Assistant 消息:**
```json
{
"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 可以在多轮迭代中穿插出现。
**注意**`text` 和 `content` 字段通过解析 `steps` 中所有 `type: "text"` 的内容动态计算得出。
### 5. 工具系统
```mermaid
classDiagram
class ToolDefinition {
+str name
+str description
+dict parameters
+Callable handler
+str category
+CommandPermission required_permission
+to_openai_format() dict
}
class ToolContext {
+int user_id
+str username
+str workspace
+int user_permission_level
}
class CommandPermission {
<<enumeration>>
READ_ONLY = 1
WRITE = 2
EXECUTE = 3
ADMIN = 4
}
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, context) 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
}
```
#### 内置工具
**代码执行 (code.py)**
| 工具 | 功能 | 权限 |
|------|------|------|
| `python_execute` | 执行 Python 代码 | EXECUTE |
| `python_eval` | 计算表达式 | EXECUTE |
**文件操作 (file.py)**
| 工具 | 功能 | 权限 |
|------|------|------|
| `file_read` | 读取文件内容 | READ_ONLY |
| `file_write` | 写入文件内容 | WRITE |
| `file_list` | 列出目录内容 | READ_ONLY |
| `file_exists` | 检查文件是否存在 | READ_ONLY |
| `file_grep` | 正则搜索文件内容 | READ_ONLY |
**Shell 命令 (shell.py)**
| 工具 | 功能 | 权限 |
|------|------|------|
| `shell_execute` | 执行 Shell 命令 | EXECUTE |
**网页爬虫 (crawler.py)**
| 工具 | 功能 | 权限 |
|------|------|------|
| `web_search` | DuckDuckGo HTML 搜索 | READ_ONLY |
| `web_fetch` | 网页抓取 | READ_ONLY |
| `batch_fetch` | 批量并发抓取 | READ_ONLY |
**数据处理 (data.py)**
| 工具 | 功能 | 权限 |
|------|------|------|
| `process_data` | JSON 转换、格式化 | READ_ONLY |
#### 权限检查机制
工具执行时自动检查用户权限:
```
工具要求的权限 <= 用户拥有的权限 → 允许执行
工具要求的权限 > 用户拥有的权限 → 拒绝执行
```
#### 工具开发规范
所有工具必须遵循统一的开发规范,确保错误处理和返回格式一致。
**核心原则:装饰器自动处理一切,工具函数只写业务逻辑**
```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. 服务层
#### LLM 适配器 (`services/llm_adapters/`)
适配器模式统一处理不同 LLM API 格式:
```mermaid
classDiagram
class ProviderAdapter {
<<abstract>>
+str provider_type
+build_request() tuple
+parse_stream_chunk() AsyncGenerator
+parse_response() Dict
+supports_thinking() bool
+supports_tools() bool
}
class OpenAIAdapter {
+str provider_type = "openai"
+build_request() tuple
+parse_stream_chunk() AsyncGenerator
+parse_response() Dict
+supports_tools() bool
}
class AnthropicAdapter {
+str provider_type = "anthropic"
+build_request() tuple
+parse_stream_chunk() AsyncGenerator
+parse_response() Dict
+supports_thinking() bool
+supports_tools() bool
}
ProviderAdapter <|-- OpenAIAdapter
ProviderAdapter <|-- AnthropicAdapter
```
**支持的功能对比:**
| 适配器 | 工具调用 | Thinking/Reasoning | 流式响应 |
|--------|----------|-------------------|----------|
| OpenAI | ✅ | ✅ (DeepSeek) | ✅ |
| Anthropic | ✅ | ✅ | ✅ |
#### LLM 响应数据类 (`services/llm_response.py`)
```python
class StepType:
"""步骤类型常量"""
THINKING = "thinking"
TEXT = "text"
TOOL_CALL = "tool_call"
TOOL_RESULT = "tool_result"
@dataclass
class Step:
"""单个步骤 - 用于存储和传输"""
id: str
index: int
type: str # thinking, text, tool_call, tool_result
content: str = ""
name: str = "" # tool_call/tool_result
arguments: str = "" # tool_call
id_ref: str = "" # tool_result
success: bool = True
@dataclass
class ParsedDelta:
"""LLM 流式响应增量"""
thinking: str = "" # 思考内容(增量)
text: str = "" # 文本内容(增量)
tool_call: Optional[Dict] = None # 单个工具调用
usage: Dict[str, int] = {} # Token 用量
is_complete: bool = False
```
#### ChatService (`services/chat.py`)
核心聊天服务:
- Agentic Loop 迭代执行(最多 10 轮)
- 流式 SSE 响应
- 工具调用编排(并行执行)
- 消息历史管理
- Token 用量追踪
- 工作空间上下文传递
#### AgenticLoop (`services/agentic_loop.py`)
执行 Agentic Loop 的核心循环:
- 调用 LLM 获取响应(流式)
- 解析 ParsedDelta更新步骤状态
- 管理 thinking/text/tool_call/tool_result 步骤
- 工具并行执行
- 最大迭代次数10
```python
# 执行流程
async for delta in llm.stream_call(...):
events = self._process_delta(delta, context, total_usage)
yield from events
# 工具调用时
tool_results = self.tool_executor.process_tool_calls_parallel(...)
messages.append({"role": "assistant", ...})
messages.extend(tool_results)
```
#### StreamContext (`services/stream_context.py`)
流式状态管理:
- 追踪当前步骤类型和索引
- 累积 thinking 和 text 内容
- 管理 tool_calls 列表和 tool_results
- 生成 SSE 事件
- 构建完整消息内容
#### LLMClient (`services/llm_client.py`)
LLM API 客户端:
- 多提供商OpenAI、DeepSeek、Anthropic
- 自动适配器选择
- 流式/同步调用
- 错误处理和重试
- Token 计数
### 7. 任务系统 (`services/task.py`)
用于自主任务执行和依赖管理:
```mermaid
classDiagram
class Task {
+str id
+str name
+str goal
+TaskStatus status
+List~Step~ steps
+List~Task~ subtasks
}
class Step {
+str id
+str name
+List~str~ depends_on
+StepStatus status
}
class TaskGraph {
+topological_sort() List~Step~
+get_ready_steps() List~Step~
+detect_cycles() List~List~str~~
+validate() tuple
}
class TaskService {
+create_task() Task
+get_task() Task
+update_task_status() Task
+add_steps() List~Step~
+build_graph() TaskGraph
}
Task "1" o-- "*" Step
Task "1" o-- "*" Task
TaskService ..> TaskGraph
```
**任务状态 (TaskStatus)**
- `PENDING` - 待处理
- `READY` - 就绪
- `RUNNING` - 运行中
- `BLOCK` - 阻塞
- `TERMINATED` - 已终止
**步骤状态 (StepStatus)**
- `PENDING` - 待执行
- `RUNNING` - 执行中
- `COMPLETED` - 已完成
- `FAILED` - 失败
- `SKIPPED` - 跳过
### 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 AL as AgenticLoop
participant Parser as LLMResponseParser
participant LLM as LLM API
participant TE as ToolExecutor
Client->>API: POST {content, tools, thinking_enabled}
API->>CS: stream_response()
CS->>AL: execute()
loop MAX_ITERATIONS (10)
AL->>LLM: stream_call(messages, tools)
LLM-->>AL: SSE Stream
AL->>Parser: parse_chunk()
Parser-->>AL: ParsedDelta {thinking, text, tool_calls}
alt tool_calls
AL->>TE: process_tool_calls_parallel()
TE-->>AL: tool_results
AL->>AL: 追加到 messages
end
end
AL->>CS: done event
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
workspace:
root: ./workspaces # 用户工作空间根目录
auto_create: true # 自动创建用户工作空间
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
logging:
level: INFO
```
## 环境变量
| 变量 | 说明 | 示例 |
|------|------|------|
| `APP_SECRET_KEY` | 应用密钥 | `your-secret-key` |
| `DEEPSEEK_API_KEY` | DeepSeek API | `sk-xxxx` |
| `DATABASE_URL` | 数据库连接 | `sqlite:///./chat.db` |
## LLM 适配器配置
### OpenAI 兼容 (DeepSeek/GLM 等)
```yaml
llm:
provider: openai
api_key: ${API_KEY}
api_url: https://api.deepseek.com/v1 # 或其他兼容端点
```
### Anthropic Claude
```yaml
llm:
provider: anthropic
api_key: ${ANTHROPIC_API_KEY}
api_url: https://api.anthropic.com/v1
```
## 项目结构说明
### 入口文件
- `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 用量
### 工作空间隔离
每个用户的工作空间完全隔离:
- 用户目录基于 user_id 的 SHA256 哈希生成
- 所有文件操作强制在用户工作空间内
- 支持权限级别控制文件操作能力
### MessageBuilder
用于构建发送给 LLM 的消息列表:
- `add_system()` - 添加系统消息
- `add_user()` - 添加用户消息JSON 格式)
- `add_assistant()` - 添加助手消息
- `add_tool_result()` - 添加工具结果消息
- `extract_text()` - 从 JSON 内容中提取文本