feat(aibox): add dedicated workspace directory to prevent file pollution

- Add WORKSPACE_DIR constant pointing to ~/.aibox/workspace by default
- Update file operations prompt to require writing temp files to workspace
- Update command execution prompt to use workspace as default work_dir
- Add command output size limit note in command execution prompt
- Update README with workspace directory structure and specification
- Add system.workspace_dir config item documentation
- Add v2.4 changelog entry
This commit is contained in:
qing committed 2026-08-01 03:49:25 +08:00
1 parent 53ce69c8dd
commit 164bc65b95
2 files changed
+57 -8

No files matched your search

+22 -1
View File
@@ -102,6 +102,7 @@ aibox/
│ ├── 99_SOUL.md # AI 灵魂定义(角色、性格、能力边界) │ ├── 99_SOUL.md # AI 灵魂定义(角色、性格、能力边界)
│ └── 100_STATUS.md # AI 状态记录(最后加载) │ └── 100_STATUS.md # AI 状态记录(最后加载)
├── skills/ # 扩展技能目录(运行时从仓库 skills/ 复制或用户自建) ├── skills/ # 扩展技能目录(运行时从仓库 skills/ 复制或用户自建)
├── workspace/ # AI 专用工作目录(临时文件、脚本、数据文件默认存放位置)
├── memories.db # 记忆和任务数据库(SQLite) ├── memories.db # 记忆和任务数据库(SQLite)
├── current_conversation.json # 当前对话 ├── current_conversation.json # 当前对话
├── conversation_history.json # 对话索引 ├── conversation_history.json # 对话索引
@@ -109,6 +110,16 @@ aibox/
└── tool_results/ # 工具结果缓存 └── tool_results/ # 工具结果缓存
``` ```
## 📂 工作目录规范
为避免 AI 产生的临时文件污染系统其他目录,所有 AI 主动创建的临时文件、脚本、数据文件默认写入 `~/.aibox/workspace/` 目录:
- **文件操作**:`write_file` 工具默认路径为 `~/.aibox/workspace/`(用户明确指定路径时除外)
- **命令执行**:`run_command` 工具默认 `work_dir` 为 `~/.aibox/workspace/`,命令产生的文件落在该专用工作区
- **用户指定路径优先**:当用户明确要求在特定路径操作时,按用户指定路径执行
- **读取不受限**:读取系统文件、项目文件时使用实际路径,不受工作目录限制
- **技能调用**:`~/.aibox/skills/` 下的技能执行不受此限制
## 🔧 可用工具 ## 🔧 可用工具
AI 智能体可以调用以下工具来完成任务(由 `Tool` 基类派生,在 `DeepSeekChat` 中注册): AI 智能体可以调用以下工具来完成任务(由 `Tool` 基类派生,在 `DeepSeekChat` 中注册):
@@ -404,7 +415,8 @@ AI 可以通过 `update_soul` 工具动态更新 `99_SOUL.md`,通过 `update_i
"websocket_host": "localhost", "websocket_host": "localhost",
"websocket_port": 8765, "websocket_port": 8765,
"skills_dir": "~/.aibox/skills", "skills_dir": "~/.aibox/skills",
"prompts_dir": "~/.aibox/prompts" "prompts_dir": "~/.aibox/prompts",
"workspace_dir": "~/.aibox/workspace"
}, },
"scheduler": { "scheduler": {
"enabled": true, "enabled": true,
@@ -689,6 +701,15 @@ elif command == "/mycommand":
## 📄 更新日志 ## 📄 更新日志
### v2.4
- ✅ **新增专用工作目录规范**:
- AI 主动创建的临时文件、脚本、数据文件默认写入 `~/.aibox/workspace/`,避免污染系统其他目录
- `write_file` 工具默认路径设为工作目录(用户明确指定路径时除外)
- `run_command` 工具默认 `work_dir` 设为工作目录,命令产生的文件落在专用工作区
- 新增 `system.workspace_dir` 配置项(默认 `~/.aibox/workspace`),可自定义工作目录位置
- 在文件操作和命令执行提示词中明确工作目录规范
- ✅ 命令执行提示词补充 1MB 输出大小限制说明
### v2.3 ### v2.3
- ✅ **修复工具输出OOM崩溃问题**: - ✅ **修复工具输出OOM崩溃问题**:
- `read_file`:不再一次性 `readlines()` 加载整个文件,改为按行范围按需读取;大文件 `tail=true` 使用反向块读取(>1MB文件不全文遍历);新增单行长度限制(10KB/行,超长截断);`max_lines` 硬上限500行防止请求超大范围 - `read_file`:不再一次性 `readlines()` 加载整个文件,改为按行范围按需读取;大文件 `tail=true` 使用反向块读取(>1MB文件不全文遍历);新增单行长度限制(10KB/行,超长截断);`max_lines` 硬上限500行防止请求超大范围
+35 -7
View File
@@ -706,6 +706,12 @@ save_memory_and_end_conversation(
'filename': '3_file_operations.md', 'filename': '3_file_operations.md',
'content': """# 文件操作能力 'content': """# 文件操作能力
## 工作目录规范(重要)
**所有你主动创建的临时文件、脚本、数据文件,必须写入 `~/.aibox/workspace/` 目录,不要在其他目录随便写文件。**
- 该目录是你的专用工作区,用于存放代码片段、临时脚本、下载的数据、生成的文件等
- 用户明确指定文件路径时,按照用户要求的路径操作
- 读取系统文件、项目文件时使用实际路径,不限制
## 可用工具 ## 可用工具
- **read_file**: 读取文件内容 - **read_file**: 读取文件内容
- **write_file**: 写入文件 - **write_file**: 写入文件
@@ -783,6 +789,12 @@ web action="get" url="https://www.baidu.com/s?wd=人工智能"
'filename': '6_command_execution.md', 'filename': '6_command_execution.md',
'content': """# 命令执行能力 'content': """# 命令执行能力
## 工作目录规范(重要)
**执行命令时,默认 work_dir 设置为 `~/.aibox/workspace/`**,所有命令执行产生的文件都落在这个专用工作区,不会污染其他目录。
- 只有当用户明确要求在特定目录执行命令时,才修改 work_dir
- 技能调用(~/.aibox/skills/)不受此限制
- 需要在用户项目目录操作时,按用户指定路径设置 work_dir
## 可用工具 ## 可用工具
- **run_command**: 执行系统命令 - **run_command**: 执行系统命令
@@ -790,17 +802,18 @@ web action="get" url="https://www.baidu.com/s?wd=人工智能"
- command: 要执行的命令(必填) - command: 要执行的命令(必填)
- timeout: 超时时间(秒),默认30秒 - timeout: 超时时间(秒),默认30秒
- stream: 是否实时输出,默认true - stream: 是否实时输出,默认true
- work_dir: 工作目录,默认当前目录 - work_dir: 工作目录,默认 `~/.aibox/workspace/`
## 安全提醒 ## 安全提醒
- 命令执行功能可能被禁用(根据配置) - 命令执行功能可能被禁用(根据配置)
- 执行危险命令前请三思 - 执行危险命令前请三思
- 超时机制可以防止命令卡死 - 超时机制可以防止命令卡死
- 命令输出有大小限制(1MB),超出会自动截断
## 示例 ## 示例
``` ```
run_command command="ls -la" timeout=10 run_command command="ls -la" timeout=10
run_command command="python script.py" stream=true run_command command="python script.py" stream=true work_dir="~/.aibox/workspace"
``` ```
""" """
}, },
@@ -1142,6 +1155,7 @@ class ConfigManager:
"log_backup_count": 5, "log_backup_count": 5,
"skills_dir": "~/.aibox/skills", "skills_dir": "~/.aibox/skills",
"prompts_dir": "~/.aibox/prompts", "prompts_dir": "~/.aibox/prompts",
"workspace_dir": "~/.aibox/workspace",
"websocket_host": "localhost", "websocket_host": "localhost",
"websocket_port": 8765, "websocket_port": 8765,
}, },
@@ -1372,6 +1386,10 @@ class Constants:
def SKILLS_DIR(cls): def SKILLS_DIR(cls):
return os.path.expanduser(cls._get_config().get("system.skills_dir", "~/.aibox/skills")) return os.path.expanduser(cls._get_config().get("system.skills_dir", "~/.aibox/skills"))
@property
def WORKSPACE_DIR(cls):
return os.path.expanduser(cls._get_config().get("system.workspace_dir", "~/.aibox/workspace"))
@property @property
def PROMPTS_DIR(cls): def PROMPTS_DIR(cls):
return os.path.expanduser(cls._get_config().get("system.prompts_dir", "~/.aibox/prompts")) return os.path.expanduser(cls._get_config().get("system.prompts_dir", "~/.aibox/prompts"))
@@ -3559,12 +3577,16 @@ class SubprocessCommandExecutor(CommandExecutor):
return ''.join(output_chunks), timeout_occurred, truncated return ''.join(output_chunks), timeout_occurred, truncated
def execute(self, command: str, timeout: int = 30, work_dir: str = ".", env: Dict = None, **kwargs) -> str: def execute(self, command: str, timeout: int = 30, work_dir: str = None, env: Dict = None, **kwargs) -> str:
"""执行命令并返回完整结果(带输出大小限制)""" """执行命令并返回完整结果(带输出大小限制)"""
if not self.is_enabled(): if not self.is_enabled():
return "❌ 命令执行功能已禁用" return "❌ 命令执行功能已禁用"
self.logger.info(f"执行命令: {command}") # 默认工作目录为workspace,避免在启动目录散落文件
if work_dir is None or work_dir == ".":
work_dir = constants.WORKSPACE_DIR
self.logger.info(f"执行命令: {command} (work_dir={work_dir})")
# 准备环境变量 # 准备环境变量
exec_env = os.environ.copy() exec_env = os.environ.copy()
@@ -3629,18 +3651,21 @@ class SubprocessCommandExecutor(CommandExecutor):
process.kill() process.kill()
def execute_streaming(self, command: str, output_queue: queue.Queue, timeout: int = 30, def execute_streaming(self, command: str, output_queue: queue.Queue, timeout: int = 30,
work_dir: str = ".", env: Dict = None, **kwargs): work_dir: str = None, env: Dict = None, **kwargs):
"""流式执行命令,支持超时控制、工作目录和输出大小限制""" """流式执行命令,支持超时控制、工作目录和输出大小限制"""
if not self.is_enabled(): if not self.is_enabled():
output_queue.put(("line", "❌ 命令执行功能已禁用\n")) output_queue.put(("line", "❌ 命令执行功能已禁用\n"))
output_queue.put(("complete", "❌ 命令执行功能已禁用")) output_queue.put(("complete", "❌ 命令执行功能已禁用"))
return return
self.logger.info(f"流式执行命令: {command}") # 默认工作目录为workspace,避免在启动目录散落文件
if work_dir is None or work_dir == ".":
work_dir = constants.WORKSPACE_DIR
self.logger.info(f"流式执行命令: {command} (work_dir={work_dir})")
output_queue.put(("line", f"\n🔧 执行命令: {command}\n")) output_queue.put(("line", f"\n🔧 执行命令: {command}\n"))
output_queue.put(("line", f"⏱️ 超时设置: {timeout} 秒\n")) output_queue.put(("line", f"⏱️ 超时设置: {timeout} 秒\n"))
output_queue.put(("line", f"📦 输出限制: {self.MAX_OUTPUT_BYTES/1024/1024:.0f}MB\n")) output_queue.put(("line", f"📦 输出限制: {self.MAX_OUTPUT_BYTES/1024/1024:.0f}MB\n"))
if work_dir != ".":
output_queue.put(("line", f"📁 工作目录: {work_dir}\n")) output_queue.put(("line", f"📁 工作目录: {work_dir}\n"))
output_queue.put(("line", "-" * 50 + "\n")) output_queue.put(("line", "-" * 50 + "\n"))
@@ -8403,6 +8428,9 @@ def main():
# 确保 prompts 目录存在 # 确保 prompts 目录存在
os.makedirs(constants.PROMPTS_DIR, exist_ok=True) os.makedirs(constants.PROMPTS_DIR, exist_ok=True)
# 确保 workspace 工作目录存在(智能体专用文件区)
os.makedirs(constants.WORKSPACE_DIR, exist_ok=True)
Logger.init_file_logging() Logger.init_file_logging()
# 创建命令执行器 - 现在只使用本地执行器 # 创建命令执行器 - 现在只使用本地执行器