diff --git a/asserts/ARCHITECTURE.md b/asserts/ARCHITECTURE.md index f5f2579..06c423e 100644 --- a/asserts/ARCHITECTURE.md +++ b/asserts/ARCHITECTURE.md @@ -1,9 +1,9 @@ -# 项目架构 +# Luxx 项目架构 ## 技术栈 - **框架**: FastAPI 0.109+ -- **数据库**: SQLAlchemy 2.0+ +- **数据库**: SQLAlchemy 2.0+ (同步模式) - **认证**: JWT (PyJWT) - **HTTP客户端**: httpx, requests - **配置**: YAML (PyYAML) @@ -18,40 +18,49 @@ ``` luxx/ ├── __init__.py # FastAPI 应用工厂 -├── run.py # 入口文件 ├── config.py # 配置管理(YAML) ├── database.py # 数据库连接 ├── models.py # ORM 模型 ├── routes/ # API 路由层 -│ ├── auth.py # 认证 -│ ├── conversations.py # 会话管理 -│ ├── messages.py # 消息处理 +│ ├── __init__.py # 路由聚合 +│ ├── auth.py # 认证 (登录/注册) +│ ├── conversations.py # 会话管理 (CRUD) +│ ├── messages.py # 消息处理 (流式/同步) │ ├── providers.py # LLM 提供商管理 │ └── tools.py # 工具管理 ├── services/ # 服务层 -│ ├── chat.py # 聊天服务 +│ ├── chat.py # 聊天服务 (Agentic Loop) │ └── llm_client.py # LLM 客户端 ├── tools/ # 工具系统 -│ ├── core.py # 核心类 -│ ├── factory.py # 装饰器 -│ ├── executor.py # 执行器 +│ ├── core.py # 核心类 (ToolRegistry, ToolDefinition, ToolResult) +│ ├── factory.py # @tool 装饰器 +│ ├── executor.py # 工具执行器 (缓存/并行) +│ ├── services.py # 工具服务层 │ └── builtin/ # 内置工具 -│ ├── code.py # 代码执行 -│ ├── crawler.py # 网页爬虫 -│ ├── data.py # 数据处理 -│ └── weather.py # 天气查询 -│ └── services.py # 工具服务层 +│ ├── __init__.py # 工具注册入口 +│ ├── code.py # 代码执行 (python_execute, python_eval) +│ ├── crawler.py # 网页爬虫 (web_search, web_fetch, batch_fetch) +│ └── data.py # 数据处理 (process_data) └── utils/ # 工具函数 - └── helpers.py + └── helpers.py # 密码哈希、ID生成、响应封装 + +run.py # 应用入口文件 +config.yaml # 配置文件 ``` ## 核心组件 ### 1. 应用工厂 (`__init__.py`) FastAPI 应用入口,使用 lifespan 管理生命周期: -- 启动:初始化数据库、加载工具 +- 启动:初始化数据库、注册内置工具、创建默认管理员用户 - 关闭:清理资源 +```python +# 默认管理员账号 +username: admin +password: admin123 +``` + ### 2. 配置管理 (`config.py`) 使用 YAML 文件管理配置: - 配置文件:`config.yaml` @@ -65,13 +74,18 @@ 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 异步支持 +- SQLAlchemy 同步支持 - SQLite 默认数据库 - 依赖注入获取会话 @@ -89,10 +103,20 @@ erDiagram 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 - string project_id FK + int provider_id FK "optional" + string project_id FK "optional" string title string model text system_prompt @@ -109,14 +133,10 @@ erDiagram string role longtext content "JSON 格式" int token_count + text usage "JSON 格式" 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 @@ -125,11 +145,19 @@ erDiagram 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 结构 @@ -157,7 +185,7 @@ erDiagram {"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": "..."} + {"id": "step-3", "index": 3, "type": "tool_result", "id_ref": "call_xxx", "name": "...", "content": "...", "success": true} ] } ``` @@ -191,16 +219,21 @@ classDiagram +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 { - +registry: ToolRegistry +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, max_workers) list + +process_tool_calls_parallel(tool_calls, context) list + +clear_cache() void + +get_history(limit) List } ``` @@ -213,7 +246,6 @@ classDiagram | `web_search` | DuckDuckGo HTML | DuckDuckGo HTML 搜索 | | `web_fetch` | 网页抓取 | httpx + BeautifulSoup,支持 text/links/structured | | `batch_fetch` | 批量抓取 | 并发获取多个页面 | -| `get_weather` | 天气查询 | 支持城市名查询 | | `process_data` | 数据处理 | JSON 转换、格式化等 | #### 工具开发规范 @@ -269,38 +301,22 @@ ToolRegistry.execute(name, args) ├─ 包装返回格式 └─ 返回 ToolResult ↓ +ToolExecutor 返回结果 + ↓ 前端 ProcessBlock 显示 ``` -| 工具函数返回 | 装饰器转换为 | -|-------------|-------------| -| `{"result": "ok"}` | `{"success": true, "data": {...}, "error": null}` | -| `{"error": "msg"}` | `{"success": false, "data": null, "error": "msg"}` | -| `raise Exception()` | `{"success": false, "data": null, "error": "..."}` | - -**工具调用流程** - -``` -LLM 请求 → ToolRegistry.execute() → @tool 装饰器包装 - ↓ -工具函数执行 - ↓ -返回 ToolResult 或 dict - ↓ -自动转换为标准格式 {"success": bool, "data": any, "error": str} - ↓ -前端 ProcessBlock 显示结果 -``` - ### 6. 服务层 #### ChatService (`services/chat.py`) 核心聊天服务: -- Agentic Loop 迭代执行 +- Agentic Loop 迭代执行(最多 10 轮) - 流式 SSE 响应 -- 工具调用编排 +- 工具调用编排(并行执行) - 消息历史管理 - 自动重试机制 +- 支持 thinking_content 提取 +- Token 用量追踪 #### LLMClient (`services/llm_client.py`) LLM API 客户端: @@ -320,13 +336,24 @@ LLM 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 | 提供商详情/更新/删除 | +| `/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 | 服务信息 | ## 数据流 @@ -340,10 +367,10 @@ sequenceDiagram participant LLM as LLM API participant TE as ToolExecutor - Client->>API: POST {content, tools} + Client->>API: POST {content, tools, thinking_enabled} API->>CS: stream_response() - loop MAX_ITERATIONS + loop MAX_ITERATIONS (10) CS->>LLM: call(messages, tools) LLM-->>CS: SSE Stream @@ -354,6 +381,7 @@ sequenceDiagram end end + CS->>CS: _save_message() CS->>API: SSE Stream API-->>Client: 流式响应 ``` @@ -363,7 +391,7 @@ sequenceDiagram | 事件 | 说明 | |------|------| | `process_step` | 结构化步骤(thinking/text/tool_call/tool_result),携带 `id`、`index` 确保渲染顺序 | -| `done` | 响应完成 | +| `done` | 响应完成,携带 message_id、token_count、usage | | `error` | 错误信息 | ### process_step 事件格式 @@ -372,7 +400,7 @@ sequenceDiagram {"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, ...}"}} +{"type": "process_step", "step": {"id": "step-3", "index": 3, "type": "tool_result", "id_ref": "call_abc", "name": "web_search", "content": "{...}", "success": true}} ``` | 字段 | 说明 | @@ -384,6 +412,13 @@ sequenceDiagram | `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}} +``` ## 配置示例 @@ -419,3 +454,35 @@ tools: | `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 用量