# xpeech
**Repository Path**: luojiaaoo/xpeech
## Basic Information
- **Project Name**: xpeech
- **Description**: Xpeech Super Feishu Agent, Reject all complex concepts and keep it simple.
- **Primary Language**: Python
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-07-20
- **Last Updated**: 2026-07-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
Xpeech

Xpeech 是一个基于 FastAPI 的 Agent 服务。它提供一个 `/chat` 接口,可以接收文本、图片和文件,调用大模型生成流式回复,并在需要时调用工具完成任务。
适合用来快速启动一个可扩展的 AI Agent API 服务。
## 功能
- 提供 HTTP API 和 SSE 流式响应
- 支持文本、图片和文件输入
- 支持多轮会话和独立工作区
- 支持 LiteLLM 兼容的大模型服务
- 支持内置工具和自定义 Python 工具
- 支持通过 MCP Server 扩展 Agent 工具
- 支持飞书消息桥接
- 使用 `conf.toml` 统一管理应用配置和密钥
- YAML 格式存储会话历史,可读性更好
- 自动历史消息压缩(三级压缩策略),避免超出上下文限制
- 内置记忆系统,自动总结和保存关键信息
- 支持视频输入
- Token 使用率实时监控
- 丰富的内置工具集:文件读写、Shell 执行、Web 搜索与网页抓取、`agent-browser` 浏览器自动化、Office 文档读取、文件发送、向用户提问
## 安装
需要 Python 3.12+ 和 uv。项目依赖通过 `uv sync` 安装:
```bash
uv sync
```
Shell 工具只支持 Linux,并依赖 bubblewrap 沙盒。Debian/Ubuntu 可这样安装:
```bash
sudo apt-get install bubblewrap
```
提前安装内置技能依赖:
```bash
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
npm config set registry https://registry.npmmirror.com/
```
Docker 镜像已安装 `agent-browser`。Compose 通过 Browserless Chromium 容器提供 CDP 服务,Xpeech 不会在 backend 容器内安装、启动或管理本地浏览器。
## 配置
普通配置写在 `conf.toml`:
```toml
[path]
session_path = "data/session"
session_history_path = "data/session/history"
workspace_base_path = "data/workspace_base"
sandbox_home_path = "data/sandbox-home"
cache_path = "data/cache"
log_path = "data/logs"
[logging]
retention_days = 7
max_file_size_mb = 10
[llm]
api_key = "your_api_key_here"
api_base = "https://api.siliconflow.cn/v1"
default_model = "openai/Pro/moonshotai/Kimi-K2.6"
default_context_token = 256000
default_top_p = 0.7
tools_python_package = "custom_tools"
default_tools = ["echo", "hello"]
system_name = ""
custom_system_prompt = ""
# default_reasoning_effort = "normal"
support_image = true
support_video = true
support_json_output = true
parallel = 4
max_iterations = 40
[tool]
[tool.browser_preview]
browser_preview_base_url = "http://backend:7878/browser_preview"
browser_preview_path = "data/browser_preview"
[tool.mcpServers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "."]
# env = { DATABASE_URL = "postgres://user:pass@localhost:5432/db" }
enabled_tools = ["*"]
tool_timeout = 30
# [tool.mcpServers.my-api]
# url = "https://mcp.example.com/sse"
# headers = { Authorization = "Bearer xxx" }
# enabled_tools = ["*"]
# tool_timeout = 120
[feishu]
app_id = "cli_xxx"
app_secret = "your_feishu_app_secret_here"
idle_timeout = 3
```
可以从模板创建本地配置:
```bash
cp conf.toml.exmple conf.toml
```
进程环境变量写在 `.env`,例如 PPT 导出脚本使用的远程 CDP 地址:
```env
CDP_URL=ws://browserless:3000
```
## 启动
启动 API 服务:
```bash
uv run -m xpeech api
```
如果不指定服务,默认也是启动 API:
```bash
uv run -m xpeech
```
服务默认运行在:
```text
http://localhost:7878
```
启动后可以打开:
- Swagger UI: `http://localhost:7878/docs`
- ReDoc: `http://localhost:7878/redoc`
启动飞书桥接:
```bash
uv run -m xpeech feishu
```
飞书桥接会从配置中读取:
- `feishu.app_id`:飞书应用 ID
- `feishu.idle_timeout`:同一会话消息合并等待时间,单位秒
- `feishu.app_secret`:飞书应用密钥
如需连接非默认 API 地址,可以传入 API 基地址:
```bash
uv run -m xpeech feishu --chat-url http://127.0.0.1:7878
```
## Docker Compose 部署
Compose 会启动三个容器:
- `browserless`:Browserless Chromium CDP 服务,仅限 Docker 内网访问
- `backend`:Xpeech API、Agent 和工具执行服务
- `feishu`:飞书长连接桥接服务,通过 Docker 内网访问后端
先准备配置和环境变量:
```bash
cp conf.toml.exmple conf.toml
cp .env.example .env
```
填写 `conf.toml` 中的 `llm.api_key` 和 `feishu.app_secret`,并确认
`.env` 中的 `CDP_URL` 与容器网络一致,再确认
`conf.toml` 中的 `llm`、`feishu.app_id` 等普通配置正确,然后构建并启动:
```bash
docker compose up -d --build
```
后端默认暴露在 `http://localhost:7878`。可通过环境变量 `BACKEND_PORT` 修改端口:
```bash
BACKEND_PORT=8080 docker compose up -d --build
```
查看运行状态和日志:
```bash
docker compose ps
docker compose logs -f browserless backend feishu
```
持久化数据统一映射到宿主机
的 `./docker_data/` 目录,其中包含 `session`、`workspace_base`、`sandbox-home` 和
`browser_preview`;缓存目录不做宿主机磁盘映射。`conf.toml` 以只读方式挂载,`.env`
通过 `env_file` 注入进程,修改后重建容器即可生效:
```bash
docker compose up -d --force-recreate browserless backend feishu
```
## 发送消息
`/chat` 需要通过请求头传入会话 ID:
```bash
curl -N -X POST "http://localhost:7878/chat" \
-H "x-session-id: demo-session" \
-F 'session_metadata={"channel":"curl"}' \
-F 'content=[{"text":"你好,介绍一下你自己"}]'
```
上传文件:
```bash
curl -N -X POST "http://localhost:7878/chat" \
-H "x-session-id: demo-session" \
-F 'session_metadata={"channel":"curl"}' \
-F 'content=[{"text":"帮我看看这个文件"}]' \
-F "files=@example.txt"
```
响应是 SSE 流,可以边生成边读取。
### 内置命令
在聊天中输入以下命令可以使用快捷功能:
- `/help` - 显示帮助信息
- `/new` - 开始一个新会话,自动总结并保存当前会话记忆
## 自定义工具
在 `conf.toml` 里指定工具包:
```toml
[llm]
tools_python_package = "custom_tools"
default_tools = ["echo", "hello"]
```
工具包示例:
```text
custom_tools/
__init__.py
test_tools.py
```
`custom_tools/__init__.py`:
```python
from .test_tools import echo, hello
__all__ = ["echo", "hello"]
```
`custom_tools/test_tools.py`:
```python
from typing import Annotated
from pydantic import BaseModel, Field
def hello():
"""Return a hello message."""
return "hello"
class Message(BaseModel):
content: Annotated[str, Field(description="The content to echo")]
def echo(message: Message):
"""Echo the message content."""
return message.content
```
工具函数需要有 docstring。函数可以不接收参数,也可以接收一个 Pydantic `BaseModel` 参数。
## 浏览器自动化
浏览器自动化通过内置 `agent-browser` 技能完成。Compose 内的 backend 通过
`ws://browserless:3000` 连接 Browserless;browserless 不对外暴露端口,仅限容器内网访问。Agent 使用 Shell 执行
`agent-browser` 命令时,执行层会自动追加当前请求的 `--session` 和配置的
`--cdp` 参数。
模型在首次进行浏览器操作前会加载
`xpeech/agent/skills/buildin/agent-browser/SKILL.md`,并按其约束复用注入的
CDP 连接和 session。Xpeech 不提供本地浏览器回退;CDP 连接失败时会直接报错。
## MCP 工具
可以在 `conf.toml` 的 `[tool.mcpServers.]` 下配置 MCP Server。启动会话时,Xpeech 会连接这些 Server、发现可用工具,并把它们注册为 Agent 默认工具。
stdio Server 示例:
```toml
[tool.mcpServers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "."]
enabled_tools = ["*"]
tool_timeout = 30
```
远程 MCP Server 示例:
```toml
[tool.mcpServers.my-api]
url = "https://mcp.example.com/sse"
headers = { Authorization = "Bearer xxx" }
enabled_tools = ["search", "read_record"]
tool_timeout = 120
```
每个会话都会把当前用户 workspace 作为 MCP workspace root。stdio MCP
进程同时以该目录作为 `cwd`;HTTP/SSE MCP 通过标准 `roots/list` 获取同一目录。
因此 MCP、内置文件工具和 Shell 的相对路径基准保持一致。远程 MCP 服务若需
直接读写文件,必须能以相同绝对路径访问该目录。
普通搜索和网页文本抓取使用 `web_search` 和 `web_fetch`;需要浏览器渲染或
交互操作时使用 `agent-browser`。`browser_preview_base_url` 只负责生成
`agent-browser` 能访问的 URL 前缀,FastAPI 路由由该 URL 的 path 部分自动注册;
`browser_preview_path` 只负责存放预览文件,两者没有
路径推导关系。`create_browser_preview` 会把目录复制到 `//`;
传入目录时返回该 UUID 目录的 URL 前缀;传入单个 HTML 时保留源文件名,
并返回完整的文件 URL。
字段说明:
- `command` / `args`:启动 stdio MCP Server 的命令和参数。
- `url`:连接远程 MCP Server。`/sse` 结尾的地址使用 SSE transport,其他地址默认使用 streamable HTTP。
- `env`:stdio Server 的环境变量。
- `headers`:远程 MCP Server 的请求头。
- `enabled_tools`:允许注册的 MCP 工具名,`["*"]` 表示全部注册。
- `tool_timeout`:单次 MCP 工具调用超时时间,单位秒。
`command` 和 `url` 只能二选一。MCP Server 配置会按原样传入,不会做运行时字符串替换。
注册后的工具名会加上 `mcp__` 前缀,例如 `filesystem` Server 暴露的 `read_file` 会注册为 `mcp_filesystem_read_file`。`enabled_tools` 可以填写 MCP 原始工具名,也可以填写加前缀后的工具名。
## 内置工具
Xpeech 提供丰富的内置工具,Agent 可以在对话中自动调用:
| 工具 | 说明 |
|------|------|
| `read_file` | 读取工作区或内置技能目录中的文件内容 |
| `write_file` | 向工作区写入文件 |
| `edit_file` | 编辑工作区内的文件 |
| `list_dir` | 列出工作区或内置技能目录内容 |
| `shell` | 执行 Bash 命令(带工作区路径限制和 bwrap 沙盒) |
| `web_fetch` | 抓取网页内容并转为 Markdown |
| `web_search` | 搜索网页并返回结果 |
| `create_browser_preview` | 将工作区内目录或单个 HTML 复制到 UUID 预览目录,返回目录 URL 前缀或完整文件 URL |
| `shell` + `agent-browser` | 通过注入的 CDP 连接搜索、打开、读取、检查和操作网页 |
| `office_read` | 读取 Office 文档(docx/xlsx/pdf/pptx 等) |
| `send_file` | 向用户发送工作区或内置技能目录中的文件 |
| `ask_user_question` | 向用户发送表单提问 |
### 工具安全
- **统一路径解析**:文件工具、文件发送工具和 Shell 绝对路径检查统一通过 `xpeech/agent/tools/helper.py` 解析路径;默认限制在工作区内,读取类操作可额外访问内置技能目录,写入和编辑仍只能落在工作区内
- **预览目录隔离**:`create_browser_preview` 只能复制当前会话工作区内的目录或 HTML,每次写入 `//`,请求子路径不能越出对应 UUID 目录
- **Shell 沙盒**:Shell 命令通过 bubblewrap 运行,工作区可读写,内置技能目录只读挂载,系统运行时路径只读挂载,临时目录和工作区父目录使用 tmpfs 隔离
- **依赖安装隔离**:Shell 工具首次使用时自动创建 `/.venv`;项目 Python 依赖进入当前工作区虚拟环境,`uv tool install` 和 `npm install -g` 安装到共享沙盒 HOME,便于不同会话复用 CLI 工具
- **Shell 黑名单**:禁止执行 `rm -rf`、`format`、`dd` 等危险命令
- **路径遍历检测**:拦截包含 `..` 的路径操作
- **内网 URL 拦截**:防止访问内部网络接口
### 沙盒
Shell 命令会在 bwrap 进程沙盒中执行:
- 每个会话工作区会绑定为可读写目录,并作为命令工作目录。
- 内置技能目录以只读方式挂载,便于读取技能脚本和资源。
- `/tmp` 和工作区父目录会使用临时文件系统隔离,避免命令看到其他工作区。
- `/usr`、`/bin`、`/lib`、证书和 DNS 配置等系统路径以只读方式挂载,提供基础运行环境。
- `HOME` 会指向工作区父目录下的共享沙盒目录 `.xpeech-sandbox-home`,并挂载为可读写目录。
- `PATH` 会优先包含共享沙盒 HOME 下的 `.local/bin` 和 `.npm-global/bin`,因此 `uv tool install`、`npm install -g` 安装的命令可被后续会话直接找到。
Shell 工具首次运行时会在工作区内执行 `uv venv .venv`,为该工作区创建独立 Python 环境。
Python 命令必须通过 `uv run python ...` 启动,直接执行 `python` / `pip` 会被安全检查拦截。
沙盒内设置了 `PIP_REQUIRE_VIRTUALENV=true` 和 `UV_PROJECT_ENVIRONMENT=/.venv`,确保项目 Python 依赖安装进入当前工作区的 `.venv`。
同时设置 `UV_CACHE_DIR=/.cache/uv` 和 `NPM_CONFIG_PREFIX=/.npm-global`,让 uv 缓存、uv tool 工具和 npm 全局工具在沙盒 HOME 内共享。
如果 `npx` 来自 nvm 或真实用户 HOME 中的 Node 安装,沙盒默认看不到它;需要把 Node/npm/npx 安装到 `/usr`、`/bin`、`/opt` 等沙盒可见路径,或在沙盒内通过可见的 npm 安装全局工具。
## 开发
```bash
uv sync
uv run pytest
uv run -m xpeech api
```
如果需要检查配置是否能读取:
```bash
uv run python -c "from xpeech.config.settings import settings; print(settings.model_dump())"
```
## TODO
- [ ] 添加 cron
- [ ] 添加心跳
- [ ] 添加飞书 CLI