From d47ef6efb4c35ce99016eb1b0e873133a9100545 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Thu, 13 Aug 2026 16:26:41 +0800 Subject: [PATCH] =?UTF-8?q?docs(aibox):=20=E5=90=8C=E6=AD=A5v3.0=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=8C=96=E6=96=87=E6=A1=A3=EF=BC=8C=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=B7=AF=E5=BE=84=E6=BC=82=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README.md: 更新行数(8450->3706)、重写目录结构(含tools/subsystems/tests包)、补充包依赖方向与测试运行说明、新增v3.0更新日志 - test_basic_tools.py: 4个ReadFileTool测试改用tmp_path fixture,消除cwd依赖 - 新增 docs/reports/aibox_refactor_progress_20260814.md: 重构进度报告 --- aibox/README.md | 79 ++++++++- aibox/tests/test_basic_tools.py | 28 ++- .../aibox_refactor_progress_20260814.md | 167 ++++++++++++++++++ 3 files changed, 260 insertions(+), 14 deletions(-) create mode 100644 docs/reports/aibox_refactor_progress_20260814.md diff --git a/aibox/README.md b/aibox/README.md index 501434c..2ee5284 100644 --- a/aibox/README.md +++ b/aibox/README.md @@ -55,21 +55,47 @@ python aibox.py 启动后,WebSocket 服务器将运行在 `ws://localhost:8765`(地址与端口可在 `config.json` 的 `system.websocket_host` / `system.websocket_port` 中修改)。 -> 注意:本模块入口文件为 `aibox.py`(单文件约 8450 行,集成 WebSocket 服务器、AI 引擎、工具/技能系统、任务调度器于一体)。`client.py` 为示例 WebSocket 客户端,`voice_client.py` 为语音客户端,`_run.sh` 为被 `core` 拉起的启动脚本。 +> 注意:本模块入口文件为 `aibox.py`(当前约 3706 行,集成 WebSocket 服务器、AI 引擎、工具/技能系统、任务调度器于一体;自 v3.0 起工具类与子系统已抽离至 `tools/`、`subsystems/`、`common/`、`tests/` 包,aibox.py 仅保留核心类与编排逻辑)。`client.py` 为示例 WebSocket 客户端,`voice_client.py` 为语音客户端,`llm_client.py` 为多厂商大模型客户端抽象层,`_run.sh` 为被 `core` 拉起的启动脚本。 ## 📁 目录结构 ``` aibox/ -├── aibox.py # 服务主程序(WebSocket 服务器 + AI 引擎 + 工具/技能系统 + 任务调度器) +├── aibox.py # 服务主程序(WebSocket 服务器 + AI 引擎 + 任务调度器 + 核心类 DeepSeekChat/WebSocketHandler/EnhancedAIManager) ├── client.py # 示例 WebSocket 客户端 ├── voice_client.py # 语音客户端 +├── llm_client.py # 大模型客户端抽象层(多厂商切换,DeepSeekClient 兼容 OpenAI 协议) ├── entry.html # 前端入口页 ├── entry1.html # 前端入口页(变体) ├── frontend.html # 前端主页面 -├── llm_client.py # 大模型客户端抽象层(多厂商切换) ├── common/ # 基础设施包(i18n / 配置 / 常量 / 日志) -├── tools/ # 工具系统基础包(Tool 基类 + ToolRegistry 注册器) +│ ├── config.py # ConfigManager 配置管理 +│ ├── constants.py # 常量定义 +│ ├── i18n.py # 多语言 +│ └── logger.py # 日志器 +├── subsystems/ # 子系统包(Wave 1:从 aibox.py 抽离的基础设施) +│ ├── executors.py # CommandExecutor + SubprocessCommandExecutor +│ ├── prompt_manager.py # PromptManager(单例,提示词按数字前缀排序加载) +│ ├── db.py # MemoryDatabase + MemoDatabase(SQLite) +│ └── skill_manager.py # SkillManager(技能发现/加载/热重载) +├── tools/ # 工具系统包(A 批 + B 批共 17 个工具类) +│ ├── base.py # Tool 抽象基类 +│ ├── registry.py # ToolRegistry 工具注册器 +│ ├── basic_tools.py # A 批:ReadFile/WriteFile/ListFiles/SimpleWebCrawler/GetCurrentTime/Calculator +│ ├── command_tool.py # B 批:CommandLineTool(run_command) +│ ├── skill_tools.py # B 批:SearchSkills/ReloadSkills/InstallSkill/CreateSkill +│ ├── db_tools.py # B 批:MemoryTool/MemoTool +│ ├── prompt_tools.py # B 批:UpdateIdentity/UpdateSoul/SaveMemoryAndEndConversation +│ ├── proxy_tools.py # B 批:SetProxy/GetProxy +│ └── misc_tools.py # B 批:AskUserTool +├── tests/ # 单元测试套件(171 项全过) +│ ├── conftest.py # sys.path 配置 + websockets 桩 +│ ├── test_basic_tools.py # A 批工具 43 项 +│ ├── test_b_tools.py # B 批工具 58 项 +│ ├── test_executors.py # 命令执行器 +│ ├── test_prompt_manager.py # 提示词管理器 +│ ├── test_db.py # 记忆/备忘录数据库 +│ └── test_skill_manager.py # 技能管理器 ├── skills/ # 内置扩展技能(*.py 可执行脚本 / *.md Markdown 技能) │ ├── hello-world.py │ ├── aes-crypto.py @@ -79,6 +105,7 @@ aibox/ │ ├── ssh-exec.py │ ├── sftp-transfer.py │ └── ... +├── prompts/ # 系统提示词源文件(运行期复制到 ~/.aibox/prompts/) ├── SKILLS_FORMAT.md # 技能文件格式说明 ├── _run.sh # 启动脚本(被 core 守护时调用) ├── _runcli.sh # 命令行启动脚本 @@ -87,6 +114,25 @@ aibox/ └── README.md ``` +**包依赖方向**(单向,禁止反向引用): + +``` +common ← subsystems ← tools ← aibox.py +``` + +- `common`:仅依赖标准库,不引用任何其他包 +- `subsystems`:依赖 `common`,不引用 `tools` 或 `aibox` +- `tools`:依赖 `common` + `subsystems`,不引用 `aibox` +- `aibox.py`:聚合层,导入上述三个包 + +**运行测试**: + +```bash +# 从仓库根目录运行 +py -m pytest aibox/tests -q +# 当前:171 项全过,约 11s +``` + 运行期数据存放于 `~/.aibox/`: ``` @@ -632,10 +678,19 @@ Server -> Client: {type: "dialogue_reset", content: "✅ 对话状态已完全 ### 添加新工具 -1. 继承 `Tool` 基类(位于 `tools/base.py`) +1. 继承 `Tool` 基类(位于 [tools/base.py](tools/base.py)) 2. 实现 `get_name()`、`get_description()`、`get_parameters()`、`execute()` 3. 可选实现 `execute_streaming()` 支持流式输出(通过 `output_queue` 发送 `("line", content)` 和 `("complete", final_result)`) -4. 在 `DeepSeekChat._register_tools()` 中注册 +4. 按工具类别放入对应文件: + - 基础工具(文件/网页/计算/时间)→ `tools/basic_tools.py` + - 命令执行 → `tools/command_tool.py` + - 技能管理 → `tools/skill_tools.py` + - 数据库工具 → `tools/db_tools.py` + - 提示词/身份更新 → `tools/prompt_tools.py` + - 代理配置 → `tools/proxy_tools.py` + - 其他 → `tools/misc_tools.py` +5. 在 [tools/__init__.py](tools/__init__.py) 的 `__all__` 中导出新类 +6. 在 `DeepSeekChat._register_tools()` 中按 `tool_profile`(orchestrator/worker)注册 ```python class MyTool(Tool): @@ -656,6 +711,8 @@ class MyTool(Tool): return f"执行结果: {param}" ``` +> **依赖约束**:`tools/` 包只能依赖 `common` + `subsystems` + 标准库,不得反向引用 `aibox.py` 任何符号;如需访问 aibox 内部状态,应通过构造函数注入(参考 `MemoryTool(memory_db=...)` 模式)。 + > **注意**:`execute()` 必须返回字符串。工具结果会经过多层安全防护: > 1. **工具层源头限制**:文件/网页工具返回 markdown 指引,实际 I/O 由 `run_command` 完成(`run_command` 输出≤1MB,超出立即终止进程;`read_file` 指引命令限制 500 行、单行≤10KB) > 2. **`_sanitize_tool_result()`**:None/非字符串转为安全字符串,过滤控制字符 @@ -734,6 +791,16 @@ elif command == "/mycommand": ## 📄 更新日志 +### v3.0 +- ✅ **模块化重构(aibox.py 8450 行 → 3706 行,缩减 56%)**: + - **A 批基础工具抽离**([tools/basic_tools.py](tools/basic_tools.py)):`FileToolBase` / `ReadFileTool` / `WriteFileTool` / `ListFilesTool` / `SimpleWebCrawlerTool` / `GetCurrentTimeTool` / `CalculatorTool` 共 7 类,无外部子系统依赖;配套 43 项单元测试 + - **B 批管理面工具抽离**([tools/](tools/) 目录):`CommandLineTool` / `SearchSkillsTool` / `ReloadSkillsTool` / `InstallSkillTool` / `CreateSkillTool` / `MemoryTool` / `MemoTool` / `UpdateIdentityTool` / `UpdateSoulTool` / `SaveMemoryAndEndConversationTool` / `SetProxyTool` / `GetProxyTool` / `AskUserTool` 共 13 类,按职责分 7 个文件;配套 58 项单元测试 + - **Wave 1 子系统抽离**([subsystems/](subsystems/) 目录):`CommandExecutor` + `SubprocessCommandExecutor`(executors.py)、`PromptManager`(prompt_manager.py)、`MemoryDatabase` + `MemoDatabase`(db.py)、`SkillManager`(skill_manager.py)共 4 子系统;配套 70 项单元测试 + - **测试套件**:[tests/](tests/) 目录 171 项全过,覆盖 A/B 批工具与 Wave 1 子系统 + - **修复**:`subsystems/db.py` 的 `get_recent_memories(limit=None)` SQL datatype mismatch bug(`LIMIT ?` 传 None 时移除 LIMIT 子句) + - **架构约束**:建立 `common ← subsystems ← tools ← aibox` 单向依赖,禁止反向引用;`tools/__init__.py` 与 `subsystems/__init__.py` 采用"重导出"策略,调用点无需关心子模块位置 + - **未抽离**:Wave 2(`TaskScheduler` / `ConversationHistory`)、Wave 3(`ConversationCompressor`)、C 批工具(`ExpressEmotionTool` / `SubAgentTool`)、核心类(`DeepSeekChat` / `WebSocketHandler` / `EnhancedAIManager`)仍保留在 `aibox.py` 内 + ### v2.9 - ✅ **编排者-执行者工具分层(工具集 profile)**: - `DeepSeekChat` 新增 `tool_profile` 参数(`orchestrator` / `worker`),`_register_tools()` 按 profile 分组装配工具集 diff --git a/aibox/tests/test_basic_tools.py b/aibox/tests/test_basic_tools.py index 4579d62..0def309 100644 --- a/aibox/tests/test_basic_tools.py +++ b/aibox/tests/test_basic_tools.py @@ -138,29 +138,41 @@ def test_calculator_unknown_operation(): # ==================== ReadFileTool ==================== +# +# 说明:使用 pytest 的 tmp_path fixture 创建临时文件作为 read_file 入参, +# 不依赖任何相对路径或 cwd——早期版本用 filepath="aibox.py" 假设从 +# aibox/ 目录运行 pytest,从仓库根运行会因路径漂移失败(2026-08-14 修复)。 -def test_read_file_head_returns_command_prompt(): +def test_read_file_head_returns_command_prompt(tmp_path): """head 模式应返回含 head 命令 + run_command 指引的 markdown。""" - r = ReadFileTool().execute(filepath="aibox.py", head=True, max_lines=10) + f = tmp_path / "sample.txt" + f.write_text("line1\nline2\n") + r = ReadFileTool().execute(filepath=str(f), head=True, max_lines=10) assert "head -n 10" in r assert "run_command" in r -def test_read_file_tail_returns_command_prompt(): - r = ReadFileTool().execute(filepath="aibox.py", tail=True, max_lines=20) +def test_read_file_tail_returns_command_prompt(tmp_path): + f = tmp_path / "sample.txt" + f.write_text("line1\nline2\n") + r = ReadFileTool().execute(filepath=str(f), tail=True, max_lines=20) assert "tail -n 20" in r assert "run_command" in r -def test_read_file_range_returns_sed_command(): - r = ReadFileTool().execute(filepath="aibox.py", start_line=5, end_line=15) +def test_read_file_range_returns_sed_command(tmp_path): + f = tmp_path / "sample.txt" + f.write_text("line1\nline2\n") + r = ReadFileTool().execute(filepath=str(f), start_line=5, end_line=15) assert "sed -n '5,15p'" in r assert "run_command" in r -def test_read_file_max_lines_cap_at_500(): +def test_read_file_max_lines_cap_at_500(tmp_path): """max_lines 应被钳制到 500 防止超大范围。""" - r = ReadFileTool().execute(filepath="aibox.py", max_lines=99999) + f = tmp_path / "sample.txt" + f.write_text("line1\n") + r = ReadFileTool().execute(filepath=str(f), max_lines=99999) assert "sed -n '1,500p'" in r diff --git a/docs/reports/aibox_refactor_progress_20260814.md b/docs/reports/aibox_refactor_progress_20260814.md new file mode 100644 index 0000000..abd886c --- /dev/null +++ b/docs/reports/aibox_refactor_progress_20260814.md @@ -0,0 +1,167 @@ +# aibox 模块化重构进度报告 + +> 状态:A/B 批工具 + Wave 1 子系统已抽离 · 日期:2026-08-14 · 基线:aibox v3.0 +> 测试:171 项全过(10.79s) +> 关联文档: +> - [aibox_orchestrator_worker_plan.md](aibox_orchestrator_worker_plan.md)(v2.9 编排者-执行者分层方案) +> - [aibox_open_dev_plan.md](aibox_open_dev_plan.md)(开放版安全裁剪方案) +> - [aibox_SECURITY_AUDIT_20260801.md](../security/aibox_SECURITY_AUDIT_20260801.md)(安全审计基线) + +## 1. 重构目标 + +将单文件 8450 行的 `aibox.py` 按职责拆分为多个内聚包,建立清晰的依赖方向与测试基线,降低维护成本、为后续工具/子系统扩展提供稳定骨架。 + +**目标架构**(单向依赖,禁止反向引用): + +``` +common ← subsystems ← tools ← aibox.py +``` + +| 层 | 职责 | 依赖 | +|---|---|---| +| `common/` | 配置、常量、i18n、日志 | 标准库 | +| `subsystems/` | 命令执行器、提示词、数据库、技能管理 | `common` | +| `tools/` | Tool 基类 + 17 个工具类 | `common` + `subsystems` | +| `aibox.py` | 核心类聚合(DeepSeekChat/WebSocketHandler/EnhancedAIManager + 调度/压缩/历史) | 上述三包 | + +## 2. 已完成工作 + +### 2.1 A 批基础工具抽离(`tools/basic_tools.py`,342 行) + +7 个无外部子系统依赖的工具类: + +| 类 | 工具名 | 说明 | +|---|---|---| +| `FileToolBase` | — | 文件操作基类(normalize_path / check_path_safety) | +| `ReadFileTool` | `read_file` | 读文件指引(head/tail/sed) | +| `WriteFileTool` | `write_file` | 写文件指引(heredoc) | +| `ListFilesTool` | `list_files` | 列目录指引(ls) | +| `SimpleWebCrawlerTool` | `web` | 网页访问指引(curl + 代理) | +| `GetCurrentTimeTool` | `get_current_time` | 时间获取 | +| `CalculatorTool` | `calculator` | 四则运算 | + +**测试**:[tests/test_basic_tools.py](../../aibox/tests/test_basic_tools.py) 43 项全过 + +### 2.2 B 批管理面工具抽离(`tools/` 目录,共 13 类) + +| 文件 | 类 | 工具名 | +|---|---|---| +| `command_tool.py` | `CommandLineTool` | `run_command` | +| `skill_tools.py` | `SearchSkillsTool` / `ReloadSkillsTool` / `InstallSkillTool` / `CreateSkillTool` | `search_skills` / `reload_skills` / `install_skill` / `create_skill` | +| `db_tools.py` | `MemoryTool` / `MemoTool` | `memory` / `memo` | +| `prompt_tools.py` | `UpdateIdentityTool` / `UpdateSoulTool` / `SaveMemoryAndEndConversationTool` | `update_identity` / `update_soul` / `save_memory_and_end_conversation` | +| `proxy_tools.py` | `SetProxyTool` / `GetProxyTool` | `set_proxy` / `get_proxy` | +| `misc_tools.py` | `AskUserTool` | `ask_user` | + +**测试**:[tests/test_b_tools.py](../../aibox/tests/test_b_tools.py) 58 项全过 + +### 2.3 Wave 1 子系统抽离(`subsystems/` 目录) + +| 文件 | 类 | 行数 | +|---|---|---| +| `executors.py` | `CommandExecutor` + `SubprocessCommandExecutor` | 279 | +| `prompt_manager.py` | `PromptManager`(单例,数字前缀排序加载) | 241 | +| `db.py` | `MemoryDatabase` + `MemoDatabase`(SQLite) | 530 | +| `skill_manager.py` | `SkillManager` | 186 | + +**测试**: +- [tests/test_executors.py](../../aibox/tests/test_executors.py) +- [tests/test_prompt_manager.py](../../aibox/tests/test_prompt_manager.py) +- [tests/test_db.py](../../aibox/tests/test_db.py) +- [tests/test_skill_manager.py](../../aibox/tests/test_skill_manager.py) + +共 70 项全过。 + +### 2.4 工具系统基础设施 + +- [tools/base.py](../../aibox/tools/base.py)(115 行):`Tool` 抽象基类,定义 `get_name` / `get_description` / `get_parameters` / `execute` / `execute_streaming` / `validate_params` / `format_result` 接口 +- [tools/registry.py](../../aibox/tools/registry.py)(135 行):`ToolRegistry` 注册器,支持热重载与流式输出分发 +- [tools/__init__.py](../../aibox/tools/__init__.py) / [subsystems/__init__.py](../../aibox/subsystems/__init__.py):采用"重导出"策略,外部统一用 `from tools import Tool, ReadFileTool`,调用点无需关心子模块位置 + +### 2.5 关键 Bug 修复 + +- **`subsystems/db.py` 的 `get_recent_memories(limit=None)` SQL datatype mismatch**:`LIMIT ?` 传 `None` 时 SQLite 报错;修复为 `limit` 为 `None` 时移除 `LIMIT` 子句 +- **`test_basic_tools.py` 路径漂移**(2026-08-14 修复):4 个测试用 `filepath="aibox.py"` 假设 pytest 从 `aibox/` 目录运行;改用 pytest `tmp_path` fixture 创建临时文件,测试不再依赖 cwd + +## 3. 量化指标 + +| 指标 | 起点(v2.8) | 当前(v3.0) | 变化 | +|---|---|---|---| +| `aibox.py` 行数 | 8450 | 3706 | -56% | +| 抽离工具类数 | 0 | 17 | +17 | +| 抽离子系统数 | 0 | 4 | +4 | +| 测试用例数 | 0 | 171 | +171 | +| 测试运行时长 | — | 10.79s | — | + +## 4. 测试套件现状 + +``` +aibox/tests/ (171 项,10.79s) +├── test_basic_tools.py 43 项 A 批工具(文件/网页/时间/计算器) +├── test_b_tools.py 58 项 B 批工具(13 个管理面工具) +├── test_executors.py — 命令执行器 +├── test_prompt_manager.py — 提示词管理器 +├── test_db.py — 记忆/备忘录数据库 +└── test_skill_manager.py — 技能管理器 +``` + +运行命令: + +```bash +# 从仓库根目录 +py -m pytest aibox/tests -q +``` + +## 5. 剩余工作(未抽离) + +### 5.1 aibox.py 剩余类清单(3706 行) + +| 类 | 起始行 | 批次 | 说明 | +|---|---|---|---| +| `ConversationCompressor` | L70 | Wave 3 | 对话压缩器,依赖 `llm_client` | +| `TaskScheduler` | L570 | Wave 2 | 任务调度器,依赖 `MemoDatabase` | +| `ConversationHistory` | L986 | Wave 2 | 对话历史管理 | +| `ExpressEmotionTool` | L1309 | C 批 | 情绪表达工具,依赖 `run_light_subagent` | +| `SubAgentTool` | L1574 | C 批 | 子代理派生工具,依赖 `run_full_subagent` | +| `DeepSeekChat` | L1700 | 核心类 | 保留入口(编排逻辑) | +| `WebSocketHandler` | L2715 | 核心类 | 保留入口 | +| `EnhancedAIManager` | L3377 | 核心类 | 保留入口 | + +### 5.2 建议优先级 + +| 优先级 | 批次 | 内容 | 风险 | +|---|---|---|---| +| 高 | Wave 2 | `TaskScheduler` + `ConversationHistory` 抽离至 `subsystems/` | 低(依赖已抽离的 `MemoDatabase`) | +| 中 | Wave 3 | `ConversationCompressor` 抽离至 `subsystems/` | 中(需评估与 `DeepSeekChat` 的循环依赖,参考 lessons learned) | +| 中 | C 批 | `ExpressEmotionTool` + `SubAgentTool` 抽离至 `tools/` | 中(依赖 `DeepSeekChat` 的 `run_light_subagent` / `run_full_subagent`,需通过依赖注入或协议接口打破循环) | +| 低 | 核心类 | `DeepSeekChat` / `WebSocketHandler` / `EnhancedAIManager` | 保留在 `aibox.py`,作为聚合入口 | + +### 5.3 风险点 + +1. **`ConversationCompressor` 与 `DeepSeekChat` 的循环依赖**:压缩器需调用 LLM,而 `DeepSeekChat` 持有压缩器;需通过依赖注入或协议接口打破(参考 project_memory 中 lessons learned) +2. **C 批工具依赖 `DeepSeekChat` 内部方法**:`run_light_subagent` / `run_full_subagent` 是 `DeepSeekChat` 的方法,抽离工具时需先抽离这些方法或改为函数式调用 +3. **测试覆盖盲区**:C 批工具(`ExpressEmotionTool` / `SubAgentTool`)和 Wave 2/3 子系统尚无单元测试,抽离时应同步补测 + +## 6. 文档同步状态 + +| 文档 | 状态 | 备注 | +|---|---|---| +| [aibox/README.md](../../aibox/README.md) | ✅ 已同步 v3.0 | 行数、目录结构、开发指南、v3.0 更新日志已更新 | +| 本报告 | ✅ 新建 | 归档已完成工作与剩余规划 | +| [aibox_orchestrator_worker_plan.md](aibox_orchestrator_worker_plan.md) | 历史 | v2.9 分层方案(已实施) | +| [aibox_open_dev_plan.md](aibox_open_dev_plan.md) | 待评估 | 开放版裁剪方案,与本次模块化正交 | +| [aibox_file_network_cli_refactor.md](aibox_file_network_cli_refactor.md) | 待评估 | 文件/网络/CLI 重构计划 | + +## 7. 验证记录 + +``` +$ py -m pytest aibox/tests -q +........................................................................ [ 42%] +........................................................................ [ 84%] +........................... [100%] +171 passed in 10.79s +``` + +- 测试基线:2026-08-14 +- 运行环境:Windows + PowerShell 5 +- Python:通过 `py` 启动器调用(`python` 不在 PATH)