Aizex MCP
by 94series
README.md
# Aizex MCP
通过 Playwright 浏览器自动化控制 Aizex 号池(ChatGPT/Claude 共享账号平台),
以 MCP (Model Context Protocol) 协议暴露为工具,让 agent 能操控aizex界面获取高级模型的回复,也可以写脚本批量自动生成内容。
## 架构概览
```
Mac (本机) VPS (远程)
┌─────────────────────┐ ┌──────────────┐
│ Chrome (非headless) │ │ nanobot │
│ ↑ │ │ ↑ │
│ Playwright 控制 │ │ MCP Client │
│ ↑ │ │ ↑ │
│ BrowserEngine │ SSH隧道 │ 127.0.0.1 │
│ ↑ │ ◄────────► │ :8000 │
│ AizexService │ └──────────────┘
│ ↑ │
│ FastMCP Server │
│ (port 8000) │
└─────────────────────┘
```
## 核心组件
### 浏览器层 (`src/browser/`)
- **engine.py** — BrowserEngine:管理 Playwright persistent context 的生命周期。
非 headless 模式启动(headless 被 Cloudflare 拦截)。支持 `adopt_context()` 用于 session 恢复后接管新浏览器。
- **auth.py** — LoginManager:处理 Authing 登录流程。
检测是否已登录、处理登录表单、等待跳转完成。支持 session 未过期时直接跳过登录。
- **actions.py** — BrowserActions:所有浏览器原子操作。
包括发送消息、获取回复、检测生成状态、切换模型、管理对话列表、文件上传下载等。
所有操作通过 CSS 选择器(配置在 aizex.yaml)定位页面元素。
- **recovery.py** — SessionRecovery:号池 session 恢复。
当 session 被踢(其他用户登录同一账号)时,打开新浏览器重新走 Authing → 选号池 → 进入聊天的完整流程。恢复后通过 `engine.adopt_context()` 替换主浏览器。
- **selectors.py** — SelectorManager:从 YAML 加载 CSS 选择器,支持 primary/fallback。
- **state.py** — StateMachine:服务状态管理(READY/BUSY/ERROR/SESSION_KICKED/LOGIN_REQUIRED)。
- **service.py** — AizexService:编排层。
协调 engine、auth、actions,提供锁机制(`run_locked`)保证操作串行化。
实现 `wait_and_get` 的短锁轮询模式:短暂获取锁检查状态 → 释放锁等待 → 再次获取锁确认稳定。
- **page_sentinel.py** — PageSentinel:页面健康监控。
在每次操作前后检查页面状态,检测异常(如被踢、登录过期、网络错误)。
### MCP 服务层 (`src/mcp_server/`)
- **server.py** — FastMCP 服务端,注册 13 个工具。
包含服务端防护:send_message 3 秒冷却期、浏览器单例(多 session 共享)。
使用统一的结果工厂模式(`_build_success_response` 和 `_build_error_response`)处理所有工具响应。
工具 docstring 使用中文,通过 tools/list 传递给 AI 客户端作为使用规则。
- **schemas.py** — Pydantic 数据模型,定义每个工具的输入输出结构。
### 核心层 (`src/core/`)
- **settings.py** — Pydantic Settings,从 `.env` 加载配置。
关键配置:browser_headless(默认 False)、poll_interval_ms、text_stability_window_ms。
遵循单一职责原则,只管理静态配置。
- **session_store.py** — SessionStore:运行时会话状态管理。
管理聊天URL和cookies的持久化,与Settings类分离,遵守单一职责原则。
- **errors.py** — 自定义异常类,包含标准化的错误字段提取(`error_fields_from_exception`)。
## MCP 工具列表
| 工具 | 用途 |
|------|------|
| `aizex_health` | 检查服务状态(READY/SESSION_KICKED) |
| `aizex_new_chat` | 新建对话 |
| `aizex_send_message` | 发送消息(3秒冷却) |
| `aizex_wait_and_get` | 等待回复完成并获取(默认超时900秒) |
| `aizex_get_last_response` | 获取最后一条回复 |
| `aizex_is_generating` | 检查是否正在生成 |
| `aizex_list_models` | 列出可用模型(支持SVG检测当前选中模型) |
| `aizex_select_model` | 切换模型 |
| `aizex_list_conversations` | 列出对话列表 |
| `aizex_select_conversation` | 切换对话 |
| `aizex_upload_file` | 上传文件到当前对话 |
| `aizex_check_downloads` | 检查当前页面可下载的文件 |
| `aizex_download_file` | 下载文件(index=-1下载全部) |
| `aizex_refresh_page` | 刷新当前页面(处理生成卡住或页面异常) |
| `aizex_recover_session` | 恢复被踢的 session(弹窗操作) |
## 最新功能更新
### 0. 教材批量生成系统(新增)
- **功能**:基于 batch 系统的教材自动生成工具
- **组件**:
- `textbook/prepare.py`:读取 config.yaml,生成 job.json 和课程提示词
- `textbook/run.sh`:一键运行脚本(自动 prepare + batch run)
- `textbook/chapters/`:参考教材内容目录
- `textbook/runs/`:运行配置目录
- `textbook/output/`:生成结果目录
- **特点**:
- 多轮共享会话:每 N 轮自动开新会话,上传参考文件
- 支持 system_prompt + 每课 prompt 的组合
- 自动检测错误回复(请求过于频繁、登录态异常等)
- 读回验证:生成 job.json 后立即验证关键字段
- **使用示例**:
```bash
# 一键运行教材生成
./textbook/run.sh textbook/runs/calculus_v1
# 续跑失败的任务
./textbook/run.sh textbook/runs/calculus_v1 resume
# 手动运行 prepare
python3 textbook/prepare.py textbook/runs/calculus_v1
```
### 1. SessionStore 重构
- **问题**:Settings类同时管理静态配置和运行时状态,违反单一职责原则
- **解决方案**:新建`SessionStore`类专门管理运行时状态
- **功能**:
- 保存/加载聊天URL(`.last_chat_url`)
- 保存/加载会话cookies(`.last_cookies.json`)
- 检查当前页面是否正确
- 获取导航URL
### 2. 统一结果工厂模式
- **问题**:server.py中每个工具函数都有重复的try-except模板
- **解决方案**:提取`_build_success_response`和`_build_error_response`辅助函数
- **优势**:
- 代码更简洁,减少重复
- 错误处理标准化
- 统一的响应格式
### 3. 智能模型检测
- **问题**:`is_current`字段检测不准确,文本匹配容易出错
- **解决方案**:使用SVG检测代替文本匹配
- **实现**:
- 当前选中的模型有checkmark图标(SVG子元素)
- 使用`await item.locator('svg').count() > 0`检测
- 分组入口(有展开箭头SVG)通过`_is_group_entry`过滤
### 4. 文件管理功能
- **上传**:支持上传任意文件到当前对话
- **下载**:自动检测AI生成的可下载文件
- **特性**:
- 智能文件路径提取
- 支持批量下载
- 安全的文件操作
### 5. 页面刷新功能
- **新增工具**:`aizex_refresh_page` 用于处理页面状态异常
- **使用场景**:
- AI 生成卡住不动时
- 页面状态异常时
- `wait_and_get` 超时后需要重置页面状态
- **特性**:刷新后可能需要重新发送消息
### 6. 下载功能增强
- **URL 解码**:自动解码下载链接中的编码文件名
- **智能文件名选择**:优先使用从 URL path 参数提取的文件名,避免使用 Playwright 的 UUID 文件名
- **批量下载支持**:支持逗号和连接号组合的索引表达式(如 "0,2-4,7")
- **全面消息扫描**:`aizex_check_downloads` 支持扫描所有对话消息,而不仅仅是最后一条
### 7. 会话状态跟踪
- **新增响应字段**:所有工具响应现在包含 `session_model` 和 `session_conversation_id` 字段
- **实时状态反馈**:AI agent 每次操作后都能看到当前会话状态
- **智能提取**:从页面 URL 自动提取对话 ID,从快照中获取当前模型
### 8. 使用规则优化
- **模型选择规则**:明确禁止在已有消息的对话中切换模型,尤其是 Claude 对话
- **正确使用顺序**:`aizex_new_chat` → `aizex_select_model` → `aizex_send_message`
- **文档更新**:更新相关工具的 docstring 以提供更清晰的使用指导
### 9. 多实例隔离支持
- **问题**:多个实例共用同一个 Chromium profile 目录导致 SingletonLock 冲突
- **解决方案**:按 `instance_id` 隔离所有浏览器相关路径
- **核心修改**:
- **Settings 配置扩展**:新增 `instance_id` 和 `pool_index` 字段,支持环境变量配置
- **路径隔离**:浏览器 profile、下载目录、artifacts 目录按 instance_id 添加后缀
- **SessionStore 隔离**:URL 和 cookies 文件按 instance_id 添加后缀
- **端口隔离**:Server 端口支持 `AIZEX_PORT` 环境变量配置
- **启动策略**:非默认实例(instance_id != "0")启动时强制走 recovery 进入指定号池
- **管理脚本**:新增 `aizex-multi.sh` 脚本支持多实例生命周期管理
- **使用示例**:
```bash
# 启动 2 个实例,分别进 0 号和 3 号池
./aizex-multi.sh start 2 0,3
# 查看状态
./aizex-multi.sh status
# 停止所有实例
./aizex-multi.sh stop
```
### 10. 多 slot 批处理支持与附件丢失修复
- **问题**:多 slot 并行处理时,上传的附件在发送消息前被不必要的导航冲掉
- **根因分析**:
1. **域名判断不完整**:SessionStore 只识别 `*.memofun.net`,不识别 `*.aizex.net` 域名
2. **目录路径重复追加**:`Settings.ensure_directories()` 非幂等,多次调用重复追加 `-instance_id` 后缀
3. **导航误判**:进入 `*.aizex.net` 域名时被误判为错误页面,触发不必要的重新导航
- **解决方案**:
1. **扩展域名支持**:修改 `SessionStore._is_supported_chat_host()` 同时支持 `*.memofun.net` 和 `*.aizex.net`
2. **幂等目录创建**:修改 `Settings.ensure_directories()` 使用 `_ensure_instance_suffix()` helper 确保路径只追加一次后缀
3. **优化 URL 更新**:recovery 成功后同时更新内存中的 active base URL 和磁盘文件
4. **添加导航诊断日志**:在 `ensure_ready_page()` 中添加详细日志,便于问题诊断
- **批处理框架**:
- **SlotPool** (`src/batch/engine.py`):管理多个独立的 AizexService 实例
- **JobRunner** (`src/batch/runner.py`):读取 job 配置,分发任务到多个 slot 并行执行
- **任务历史** (`src/batch/history.py`):JSON Lines 格式记录任务执行历史
- **CLI 接口** (`src/batch/__main__.py`):支持 run、status、resume、create 命令
- **使用示例**:
```bash
# 创建批处理任务配置
python -m src.batch create --name my_job --files file1.txt file2.txt --slots 3
# 运行批处理任务
python -m src.batch run job_my_job.json
# 查看任务状态
python -m src.batch status job_my_job.json
```
## 关键技术决策
1. **非 headless 模式**:headless 浏览器被 Cloudflare 拦截,必须用非 headless。
2. **persistent context**:号池 session 依赖 WebSocket 连接,关闭浏览器即失效。使用 persistent context 保持 cookie 和连接。
3. **恢复时不关旧浏览器**:recovery 完成后通过 `adopt_context()` 直接替换主浏览器引用,避免 WebSocket 断开。
4. **短锁轮询**:`wait_and_get` 不长期持有锁,用"短锁检查 → 释放 → 等待 → 再检查"的模式,避免阻塞其他操作。
5. **send_message 冷却**:服务端强制 3 秒间隔,防止 AI 客户端连续发送导致页面混乱。
6. **动态 URL**:号池域名每次不同(如 a.memofun.net),恢复后通过 `settings.update_base_url()` 更新。
7. **单一职责原则**:Settings管配置,SessionStore管运行时状态,职责清晰分离。
8. **统一错误处理**:所有工具使用相同的结果工厂,错误响应格式一致。
## 部署架构
- **Mac 端**:运行 MCP Server + Chrome 浏览器 + SSH 隧道
- **VPS 端**:运行 nanobot(通过 SSH 隧道连接 Mac 的 MCP Server)
- **控制脚本**:
- 单实例:`./aizex.sh start/stop/restart/status/test/log/tunnel`
- 多实例:`./aizex-multi.sh start/stop/status/log`
## 快速开始
### 安装
```bash
git clone <repo-url> ~/aizex_mcp
cd ~/aizex_mcp
pip install -e .
playwright install chromium
```
### 配置
```bash
cp .env.example .env
# 编辑 .env,填入 Aizex 账号密码和 URL
```
关键配置项:
```bash
# Aizex URLs
AIZEX_BASE_URL=https://chat.aizex.com
AIZEX_CHAT_URL=/chat
AIZEX_LOGIN_URL=/login
# Authentication (required for login)
AIZEX_USERNAME=your_username_here
AIZEX_PASSWORD=your_password_here
# Browser settings (必须设置为 false)
AIZEX_BROWSER_HEADLESS=false
# 运行时状态存储
AIZEX_SESSION_STORE_DIR=./.session
```
### 运行
```bash
chmod +x aizex.sh
./aizex.sh start # 启动 MCP 服务 + SSH 隧道
./aizex.sh test # 端到端测试
./aizex.sh status # 查看状态
```
### 开发模式运行
```bash
make install # 安装依赖
make run # 启动 MCP 服务器(开发模式)
```
### nanobot 连接
在 nanobot 的 config.json 中添加:
```json
{
"tools": {
"mcpServers": {
"aizex": {
"url": "http://127.0.0.1:8000/mcp"
}
}
}
}
```
### IDE中mcp的配置
{
"mcpServers": {
"aizex-mcp": {
"command": "python3",
"args": [
"run_stdio.py"
],
"cwd": "/Users/csbot/aizex_mcp",
"alwaysAllow": [
"aizex_health",
"aizex_list_models",
"aizex_list_conversations",
"aizex_select_model",
"aizex_new_chat",
"aizex_send_message",
"aizex_wait_and_get",
"aizex_check_downloads",
"aizex_upload_file",
"aizex_get_last_response",
"aizex_is_generating",
"aizex_select_conversation",
"aizex_download_file",
"aizex_refresh_page",
"aizex_recover_session"
],
"timeout": 300
}
}
}
## 开发指南
### 项目结构
```
aizex_mcp/
├── src/
│ ├── browser/ # 浏览器自动化层
│ │ ├── actions.py # 原子操作(发送消息、切换模型等)
│ │ ├── service.py # 服务编排层
│ │ ├── engine.py # 浏览器引擎
│ │ ├── recovery.py # 会话恢复
│ │ ├── page_sentinel.py # 页面监控
│ │ └── ...
│ ├── mcp_server/ # MCP 服务层
│ │ ├── server.py # FastMCP 服务端
│ │ └── schemas.py # 数据模型
│ ├── core/ # 核心层
│ │ ├── settings.py # 配置管理
│ │ ├── session_store.py # 运行时状态管理
│ │ └── errors.py # 错误处理
│ └── batch/ # 批处理层(新增)
│ ├── __init__.py
│ ├── engine.py # SlotPool:管理多个 AizexService 实例
│ ├── runner.py # JobRunner:任务分发和执行
│ ├── history.py # 任务历史记录(JSON Lines 格式)
│ └── __main__.py # CLI 接口
├── textbook/ # 教材批量生成系统(新增)
│ ├── prepare.py # 配置解析和 job.json 生成
│ ├── run.sh # 一键运行脚本
│ ├── chapters/ # 参考教材内容
│ ├── runs/ # 运行配置
│ └── output/ # 生成结果
├── config/
│ └── selectors/
│ └── aizex.yaml # CSS 选择器配置
├── tests/ # 测试
├── .env.example # 环境变量示例
├── aizex.sh # 单实例控制脚本
├── aizex-multi.sh # 多实例控制脚本
├── job_example.json # 批处理任务配置示例
├── Makefile # 构建脚本
└── pyproject.toml # 项目配置
```
### 测试
```bash
# 运行所有测试
make test
# 运行特定测试
pytest tests/test_mcp_tools.py -xvs
# 测试模型列表功能
pytest tests/test_mcp_tools.py::test_aizex_list_models_tool -xvs
```
### 通过inspector直接测试
### 代码质量
```bash
# 代码格式化
make format
# 代码检查
make lint
# 类型检查
make type-check
```
## 故障排除
### 常见问题
1. **浏览器无法启动**
- 检查 Chrome 是否已安装
- 确认 `AIZEX_BROWSER_HEADLESS=false`
- 查看日志:`./aizex.sh log`
2. **登录失败**
- 检查 `.env` 中的用户名密码
- 确认网络连接正常
- 查看浏览器控制台错误
3. **会话被踢**
- 自动恢复:`aizex_recover_session`
- 手动恢复:重启服务 `./aizex.sh restart`
4. **文件上传失败**
- 检查文件路径是否存在
- 确认文件大小不超过限制
- 查看页面是否支持文件上传
### 日志查看
```bash
./aizex.sh log # 查看服务日志
tail -f aizex.log # 实时查看日志
```
## AizexService 完整方法清单和调用顺序约束
### 方法清单
| 方法 | 参数 | 返回值 | 用途 |
|------|------|--------|------|
| `startup()` | 无 | 无 | 启动服务:启动浏览器 → 注入cookies → 导航 → 登录检查 → 备用recovery |
| `shutdown()` | 无 | 无 | 关闭服务:停止浏览器引擎 |
| `ensure_ready_page()` | 无 | `Page` | 确保页面就绪:检查URL → 检查登录 → 自动recovery |
| `health()` | 无 | `HealthResult` | 健康检查:返回状态/URL/uptime |
| `new_chat()` | 无 | `ActionResult` | 新建对话 |
| `list_models()` | 无 | `ModelListResult` | 列出可用模型 |
| `select_model(model_name)` | `str` | `ActionResult` | 选择模型 |
| `send_message(text)` | `str` | `ActionResult` | 发送消息 |
| `is_generating(use_text_fallback)` | `bool` | `GeneratingResult` | 检查是否正在生成 |
| `get_last_response()` | 无 | `ResponseResult` | 获取最后一条回复 |
| `wait_and_get(timeout_sec)` | `int` | `ResponseResult` | 等待生成完成并获取回复 |
| `list_conversations()` | 无 | `ConversationListResult` | 列出对话列表 |
| `select_conversation(conversation_id, title)` | `str, str` | `ActionResult` | 选择对话 |
| `upload_file(file_path)` | `str` | `UploadResult` | 上传文件 |
| `check_downloads(scan_all)` | `bool` | `CheckDownloadsResult` | 检查可下载文件 |
| `download_file(index)` | `int` | `DownloadActionResult` | 下载指定文件 |
| `download_all()` | 无 | `DownloadActionResult` | 下载所有文件 |
| `download_multiple(indices)` | `list` | `DownloadActionResult` | 下载多个文件 |
| `refresh_page()` | 无 | `ActionResult` | 刷新页面 |
| `recover_session(timeout_sec)` | `int` | `RecoveryResult` | 恢复session |
| `check_credits(force_refresh)` | `bool` | `dict` | 查询积分余额 |
### 调用顺序约束
```
startup() # 必须首先调用
↓
new_chat() → select_model() # 建议顺序(Claude对话禁止中途切模型)
↓
send_message() # 发送消息
↓
wait_and_get() # 等待回复(内部自动轮询)
↓
[可选] upload_file() / check_downloads() / download_file()
↓
shutdown() # 最后调用
```
**关键约束**:
1. `startup()` → `new_chat()` → `send_message()` 是标准链路
2. Claude 对话**禁止**在已有消息后切模型(会导致session失效)
3. `wait_and_get()` 内部使用短锁轮询,不会阻塞其他操作的lock获取
4. `upload_file()` 必须在 `send_message()` 之前调用
5. `recover_session()` 成功后需重新 `new_chat()` 开始新对话
## 认证和会话生命周期
### Cookies 来源
1. **启动时注入**:`startup()` 从 `.last_cookies.json` 读取并注入到浏览器context
2. **Recovery 时获取**:`recover_session()` 完成 pool re-entry 后,从新context提取全量cookies并保存到 `.last_cookies.json`
3. **持久化文件**:`.last_cookies` 或 `.last_cookies.{instance_id}.json`(多实例隔离)
### Cookies 过期场景
| 场景 | 原因 | 检测方式 |
|------|------|----------|
| 其他用户登录同一号池 | Aizex号池session被踢 | PageSentinel检测"其他地方登录"等关键词 |
| Authing session过期 | Authing SSO登录态过期 | 登录检查失败 |
| 浏览器被强杀 | Ctrl+C或kill -9 | 重启后inject的cookies可能失效 |
| Cloudflare challenge | Cloudflare验证拦截 | 页面URL跳转到challenge页 |
### Recovery 流程
`recover_session()` 的完整流程:
```
1. 启动临时非headless浏览器(独立profile: /tmp/aizex-recovery-profile-{instance_id})
2. 导航到 https://aizex.net/plusPool
3. 如果需要登录 → 点击"登录/注册" → Authing认证 → 验证pool可访问
4. 如果已有Authing session → 直接验证pool可访问
5. 切换到高级号池(如果有)
6. 采集积分信息(顺路)
7. 点击指定pool卡片(pool_index)
8. 等待新tab打开 → 提取新URL
9. 返回 RecoveryResult(包含 new_base_url, cookies, browser_context, credits)
10. 主服务调用 engine.adopt_context() 接管新浏览器
11. 更新 settings.base_url 和 session_store
12. 保存cookies到 .last_cookies.json
```
**注意**:Recovery 使用独立的浏览器profile,不复用主浏览器的profile,避免SingletonLock冲突。
### Session 失效的触发条件
1. **响应中检测到关键词**:`check_and_recover_response()` 检查回复文本是否包含"其他地方登录"、"号池重进"等
2. **PageSentinel pre_check 检测**:每次 `run_locked()` 操作前,PageSentinel 检查页面是否有session kicked弹窗
3. **手动触发**:用户调用 `aizex_recover_session` 工具
### 手动输入密码
系统**不支持**在运行时手动输入密码。认证凭据必须在 `.env` 中预先配置:
```bash
AIZEX_USERNAME=your_username
AIZEX_PASSWORD=your_password
```
## 所有错误状态和对应的恢复路径
| 错误状态 | 触发条件 | 恢复方法 | 自动/手动 |
|----------|----------|----------|-----------|
| `SESSION_KICKED` | 响应含"其他地方登录"等关键词;PageSentinel检测到踢出弹窗 | 调用 `aizex_recover_session` | 手动(需用户确认) |
| `LOGIN_REQUIRED` | Authing登录态过期;启动时检测未登录 | 调用 `aizex_recover_session`(recovery会走完整登录流程) | 手动 |
| `CLOUDFLARE_BLOCKED` | PageSentinel检测到challenge页 | 调用 `aizex_recover_session`(非headless浏览器可绕过) | 手动 |
| `PAGE_NAVIGATION_LOST` | 页面URL离开Aizex域名;about:blank | 调用 `aizex_refresh_page` 或等待自动recovery | 自动(run_locked内) |
| `SELECTOR_NOT_FOUND` | 页面结构变化导致CSS选择器失效 | 调用 `aizex_refresh_page`;检查selectors配置 | 手动 |
| `SEND_COOLDOWN` | 发送间隔小于3秒 | 等待3秒后重试 | 自动(服务端强制) |
| `TIMEOUT` | `wait_and_get` 超时(默认900秒) | 调用 `aizex_refresh_page`;检查AI是否卡住 | 手动 |
| `ATTACHMENT_LIMIT` | 附件数量超过上限 | 减少附件数量 | 手动 |
| `GENERATION_ERROR` | AI生成过程中出错 | 调用 `aizex_refresh_page`;重新发送消息 | 手动 |
| `FILE_NOT_FOUND` | 上传/下载文件路径不存在 | 检查文件路径 | 手动 |
| `SingletonLock` | 多个浏览器实例共用同一profile | 使用不同的 `instance_id` 隔离 | 配置层面 |
### 自动 Recovery 触发条件
在 `run_locked()` 内部,以下情况会自动触发 `_auto_recover_in_place()`:
1. **ensure_ready_page()**:导航后发现未登录,或URL不在Aizex域名
2. **PageSentinel pre_check**:检测到needs_recovery(SESSION_KICKED、CLOUDFLARE_BLOCKED)
3. **简单导航失败**:尝试导航后仍然不健康
**冷却限制**:自动recovery有5分钟冷却期,防止频繁触发。
## SlotPool 和单实例 MCP 的关系
### 架构关系
```
┌─────────────────────────────────────────────────────────────┐
│ MCP Server (单实例) │
│ instance_id="0" │
│ pool_index=0 │
│ profile: /srv/aizex/profile │
└─────────────────────────────────────────────────────────────┘
↓ 完全独立
┌─────────────────────────────────────────────────────────────┐
│ SlotPool (批处理) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Slot 0 │ │ Slot 1 │ │ Slot 2 │ │
│ │ instance_id │ │ instance_id │ │ instance_id │ │
│ │ = "batch_0" │ │ = "1" │ │ = "2" │ │
│ │ pool_index= │ │ pool_index= │ │ pool_index= │ │
│ │ 0 │ │ 1 │ │ 2 │ │
│ │ profile: │ │ profile: │ │ profile: │ │
│ │ /srv/aizex/ │ │ /srv/aizex/ │ │ /srv/aizex/ │ │
│ │ profile- │ │ profile-1 │ │ profile-2 │ │
│ │ batch_0 │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
### 隔离机制
| 资源 | MCP Server | SlotPool Slot 0 | SlotPool Slot 1 |
|------|------------|-----------------|-----------------|
| instance_id | "0" | "batch_0" | "1" |
| Browser profile | `/srv/aizex/profile` | `/srv/aizex/profile-batch_0` | `/srv/aizex/profile-1` |
| Cookies | `.last_cookies.json` | `.last_cookies.batch_0.json` | `.last_cookies.1.json` |
| Chat URL | `.last_chat_url` | `.last_chat_url.batch_0` | `.last_chat_url.1` |
| Downloads | `/srv/aizex/downloads` | `/srv/aizex/downloads-batch_0` | `/srv/aizex/downloads-1` |
| Artifacts | `/srv/aizex/artifacts` | `/srv/aizex/artifacts-batch_0` | `/srv/aizex/artifacts-1` |
### 是否会冲突
**不会冲突**,因为:
1. **浏览器profile隔离**:每个instance_id使用不同的Chromium profile目录,避免SingletonLock冲突
2. **Cookies隔离**:每个instance_id使用不同的cookies文件,不会互相覆盖
3. **SessionStore隔离**:每个instance_id维护独立的活跃URL
4. **Pool选择**:每个slot可以指定不同的pool_index,进入不同的号池
### 共享的资源
1. **代码和配置**:所有实例共享同一份代码和 `.env` 配置(但通过环境变量可以覆盖)
2. **Aizex账号**:所有实例使用同一个Aizex账号登录(通过Authing SSO)
3. **号池**:不同slot可以进入不同pool_index的号池,也可以进入同一个(但会被Aizex踢出)
### 注意事项
1. **不要让多个实例进入同一个pool卡片**:会被Aizex检测为并发登录,导致session被踢
2. **MCP Server 运行时可以同时跑 SlotPool**:因为使用不同的instance_id,完全隔离
3. **Ctrl+C 强杀的影响**:只会影响被强杀的实例,其他实例不受影响
## 贡献指南
1. Fork 项目
2. 创建功能分支 (`git checkout -b feature/amazing-feature`)
3. 提交更改 (`git commit -m 'Add amazing feature'`)
4. 推送到分支 (`git push origin feature/amazing-feature`)
5. 创建 Pull Request
## 许可证
MIT License
## 联系方式
如有问题或建议,请通过项目 Issues 页面反馈。This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessUnresponsive