内容
# 卡利维
一款适用于Ollama和OpenAI兼容服务器的自托管、多用户聊天界面。
您可以选择在哪台服务器上运行哪个模型:先选择服务器,然后选择模型。
您自己的GPU盒、Ollama Cloud、OpenRouter、Moonshot、LM Studio、vLLM、
llama.cpp-server — 所有这些都可以在同一个聊天窗口中使用。
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/images/app-dark.png">
<img
src="docs/images/app-light.png"
width="900"
alt="卡利维聊天窗口:顶部栏中的服务器和模型选择器,流式传输的答案包含bash代码块和复制按钮,tokens-per-second读数,以及下一个消息下的web_search工具芯片。"
/>
</picture>
</p>
<p align="center"><sub>界面渲染带有占位数据 — 不是真实对话。</sub></p>
```
┌──────────────┐ ┌──────────────┐ ┌────────────────────────┐
│ 浏览器 │────▶│ nginx (:8090)│────▶│ FastAPI后端 │
└──────────────┘ │ SPA + /api │ │ SQLite · 工具循环 · │
└──────────────┘ │ 审批 │
└───────────┬────────────┘
│
┌───────────────┬───────────────────────┼───────────────┐
▼ ▼ ▼ ▼
Ollama服务器 OpenAI兼容 SearXNG(捆绑) MCP服务器
(局域网/远程) API(云/本地) 网络搜索 ├─ HTTP,直接
└─ stdio,通过
沙盒桥容器
```
> ### ⚠️ 高度开发中 — 请自行承担风险
>
> 卡利维今天就能用,而且我每天都在用,但它处于**高度开发中**。
> 提交之间可能会出现问题,默认值可能会更改,安全漏洞可能会被发现和修复,
> 因为代码一直在变动。没有稳定的发布渠道,没有版本升级路径,也没有
> 保证今天的数据库在明天不会被破坏。
>
> 如果你运行它:**请关注提交记录,经常更新,并保持自己的备份**。在拉取之前
> 请阅读差异。**请自行承担风险**。
---
## 亮点
- **多服务器,手动选择** — 你从顶部栏选择服务器和模型;
没有自动路由。只有可达(`up`)的服务器才会出现在选择器中。
- **两种服务器类型** — `ollama`(原生 `/api/chat`)和 `openai`(OpenAI兼容
`/v1/chat/completions`)。一个界面驱动两者。
- **流式传输与推理** — 响应流式传输;推理模型的思考标记
在单独的框中显示。按 **停止** 按钮或 **Esc** (已经生成的部分将被保留)中断。
- **多用户** — httpOnly cookie + JWT会话,管理员/用户角色。每个用户只能看到
自己的聊天记录。可以由管理员关闭注册。
- **视觉** — 向具有视觉能力的模型发送图像,包括从剪贴板粘贴和
全屏查看器(灯箱)。
- **文档附件** — PDF / docx / txt / 代码 / csv / json 被提取为
**文本** 并交给模型(无损文本,不是OCR)。
- **工具(🔧)** — 作曲器中的一个切换器为模型提供工具层,然后
调用工具 *主动*;哪个工具运行在对话中可见
并在重新加载时保存。捆绑的SearXNG提供网络搜索。
- **MCP服务器** — 连接 [Model Context Protocol](https://modelcontextprotocol.io) 服务器
(Context7,GitHub,Exa…)及其工具在内置工具旁边对模型可用。直接HTTP;
stdio服务器通过捆绑的沙盒桥容器。
- **审批后更改** — 只读工具自行运行;可以更改
某些内容时工具 **关闭直到您启用它**,然后在每次运行前询问您。沉默
是拒绝,从不表示同意。
- **静止时加密的秘密** — MCP令牌和提供商API密钥在数据库中加密,
因此它的副本(备份,被盗的卷)不会泄露您的凭证。
- **消息编辑** — 编辑消息并 **更新** (从该点重新生成,
可选地在不同的模型上)或 **新建聊天** (分支同时保留历史记录)。
- **每个答案都被记录** — 每个回复都带有生成它的模型和服务器
以及令牌/秒数,存储在消息中,以便在重新加载后仍然可见 — 聊天记录
恢复后仍然告诉您是什么回答,在哪里,速度有多快。可以复制任何单个答案,
或整个对话到剪贴板;删除消息以回溯聊天记录。
- **Markdown + 数学** — 代码块带有复制按钮,表格,KaTeX。
- **9种语言** — TR,EN,DE,ES,IT,PT,RU,JA,ZH。
- **浅色/深色主题** 带有可选的强调色。
---
## 安装
**要求:** Docker 和 Docker Compose。除此之外不需要任何东西。
```bash
git clone https://github.com/orkun-soylu/calivi.git
cd calivi
docker compose up -d --build
```
打开 **http://localhost:8090** (或主机的局域网地址:`http://192.168.x.x:8090`)。
> **第一个注册用户成为管理员。** 从注册屏幕创建第一个帐户 —
> 它是 **超级管理员** (ID 1),不能被删除或
> 降级。后续注册用户成为普通用户;您可以在设置 → 常规下完全关闭注册。
### 添加服务器
设置(⚙)→ **服务器** → 在底部使用表单:
| 类型 | 字段 | 示例 |
|---|---|---|
| `ollama` | 主机 + 端口 | `192.168.1.50` : `11434` |
| `openai` | 基础URL + API密钥 | `https://openrouter.ai/api/v1` |
模型列表自动获取(`/api/tags` 或 `/v1/models`)。行中的绿色指示灯
表示服务器可达;红色服务器在聊天选择器中隐藏。
> **Ollama 必须在网络上可达。** 默认情况下,Ollama 仅监听
> `127.0.0.1`。要从另一台机器访问它,请运行 `OLLAMA_HOST=0.0.0.0`。
### 添加MCP服务器
设置(⚙)→ **MCP** → Context7、GitHub 和 Exa 的预设填充了 URL 和身份
验证头,或者您自己填写表单。绿色指示灯表示握手成功;该行
展开以显示已注册的工具 — 每个工具都有一个复选框,因此可以单独关闭工具
而无需删除服务器。
将作曲器中的 **🔧 切换** 打开,以便任何工具都可以访问模型:它控制整个
工具层,包括网络搜索和MCP。
| 服务器 | URL | 验证 |
|---|---|---|
| Context7 | `https://mcp.context7.com/mcp` | `CONTEXT7_API_KEY` 头 — 可选,提高速率限制 |
| GitHub | `https://api.githubcopilot.com/mcp/readonly` | `Authorization: Bearer <PAT>` |
| Exa | `https://mcp.exa.ai/mcp` | `Authorization: Bearer <key>` |
传输是 **HTTP** — 要么是可流式传输的HTTP,要么是旧版HTTP + SSE(选择 `SSE` 用于
像Linear这样的服务器,它们仍然提供SSE)。对于stdio服务器(`npx …`),请参见下文。
### stdio MCP服务器 — 捆绑的桥
卡利维从不运行stdio服务器:这将执行一个npm或PyPI包,名为
网页表单中的内容,在后端容器中,靠近数据库和会话密钥。
一个捆绑的、按需开启的桥接容器 대신运行它们,并与卡利维以HTTP通信。
```bash
cp stdio-bridge/servers.example.json stdio-bridge/servers.json
# 将您的服务器添加到stdio-bridge/Dockerfile(固定)和servers.json
docker compose --profile stdio-bridge up -d --build
```
然后在设置 → MCP中像其他服务器一样添加它,传输 **HTTP**,无需密钥:
```
http://calivi-mcp-bridge:8096/servers/time/mcp
```
示例自带 `mcp-server-time`,它回答模型最不擅长的問題 —
现在是什么时间。
**服务器安装在镜像中,固定** (`stdio-bridge/Dockerfile`),每个都在其自己的
虚拟环境中。不要在配置中放入 `npx -y <package>`:这会下载并运行无论
注册中心在调用时提供的内容,这是桥接器避免这种情况的原因。
> **⚠️ 桥接器运行卡利维无法控制的代码。** 因此,它默认是锁定的:它自己的
> `internal: true` 网络(**无互联网,无LAN**),非root用户,读取
> 权限受限的文件系统,所有功能都已删除,无发布端口和自己的身份验证。
>
> 需要互联网的服务器 — 例如 `mcp-server-fetch` — **将无法工作**,直到您
> 从 `mcp-bridge` 网络中删除 `internal: true`。这授予它互联网和您的
> LAN;Docker以相同的方式路由两者,需要 `DOCKER-USER` 防火墙规则。
> 请谨慎做出决定。
如果桥接服务器显示 **无工具**,几乎总是只读门:一个服务器
如果没有设置 `readOnlyHint`,其工具默认为 `off`,您需要在设置中
启用它们。`mcp-server-time` 设置它;`mcp-server-fetch` 不设置。
> **⚠️ 默认情况下,仅提供只读工具。** 如果服务器标记工具为只读,则该工具
> 单独提供;否则,它 **关闭**。您可以在MCP选项卡中逐一开启工具 —
> `auto` 让它运行,或者 **`approve`** 让它停止并在每次运行前询问您 —
> 因此,变更工具始终是一种刻意的行为,而不是发现。
>
> 该标记是 *服务器自己的声明* — 将其视为过滤器,而不是沙盒。 **给予MCP服务器
> 权限最少的凭证**:一个精细的、只读的GitHub令牌限定在您实际想要的
> 仓库中,而不是一个广泛的令牌。GitHub预设指向 `/readonly`
> 端点,因此限制由GitHub强制执行。
### 配置(环境变量)
一切都有一个合理的默认值。要覆盖,请在项目根目录中创建一个 `.env` 文件
(Compose会自动读取它):
```bash
# .env
CALIVI_PORT=8090 # 暴露的端口
COOKIE_SECURE=false # 在HTTPS后面设置为true — 参见下面的警告
CALIVI_SECRET_KEY=... # 签署会话并加密存储的秘密 — 参见下面的说明
```
> ### 如果您备份数据卷,请设置 `CALIVI_SECRET_KEY`
> 未设置时,后端会在 `/data/secret_key` 处生成并存储密钥 —
> 与 `calivi.db` 相同的卷。它的副本然后包含您的数据 *和* 密钥,
> 因此拥有它的人可以为任何用户(包括管理员)创建有效的会话并
> 解密数据库中存储的MCP令牌和提供商API密钥。设置变量会阻止
> 密钥写入那里:
```bash
openssl rand -hex 32 # 将输出放入.env,然后:docker compose up -d
```
> **更改现有密钥** 会一次性注销所有用户 *和* 使所有存储的秘密
> 不可读,因为同一个密钥会加密它们。要更改而不丢失它们,请将旧密钥
> 传递一次:
```bash
CALIVI_SECRET_KEY=<新密钥>
CALIVI_SECRET_KEY_OLD=<旧密钥>
```
启动时,每个存储的秘密都会使用新密钥重新加密;删除
`CALIVI_SECRET_KEY_OLD` 之后。跳过此操作,秘密只需在设置中
重新输入 — 没有其他问题,应用程序仍然启动。
后端理解的其他变量(`backend/app/config.py`):`DB_PATH`,
`SYSTEM_PROMPTS_PATH`,`TOOLS_CONFIG_PATH`,`SEARXNG_URL`,`CORS_ORIGINS`,
`LOGIN_MAX_ATTEMPTS`,`LOGIN_WINDOW_SECONDS`,`REGISTER_MAX_SUCCESS`,
`REGISTER_WINDOW_SECONDS`,`CALIVI_SECRET_KEY_OLD`(密钥轮换,上面)。
> ### ⚠️ 将其放在HTTPS后面:设置 `COOKIE_SECURE=true`
> 此Compose文件提供纯HTTP,因此默认为`false`。如果您将Calivi
> 放在TLS终止的反向代理后面(Traefik,Caddy,nginx ...),
> **设置为 `true`** — 否则会话cookie将被发送而没有 `Secure` 标志。
---
## 配置文件
`config/`下的YAML文件被挂载到容器中,并在每次请求时重新读取 —
**无需重启**。大多数也可以从设置UI编辑。
| 文件 | 目的 |
|---|---|
| `config/system_prompts.yml` | 每个模型的系统提示(`default`键是默认值) — **不在git中**,请参见下文 |
| `config/tools.yml` | 工具(函数调用)层:开/关,循环上限,`web_search`选项 |
| `config/vision_models.yml` | 视觉检测的手动覆盖(`force_vision` / `force_text`) |
| `searxng/settings.yml` | 捆绑的SearXNG。无需暴露端口;如果公开暴露,请更改`secret_key` |
`config/system_prompts.yml` 是 **git-ignored**:系统提示倾向于命名您自己的
硬件和模型,它们是唯一真正个人化的配置。从示例开始:
```bash
cp config/system_prompts.example.yml config/system_prompts.yml
```
运行它时没问题 — 没有发送系统消息,设置 →
系统提示编辑器会在您第一次保存时创建文件。编辑器中的“默认”按钮会从
`backend/app/defaults/system_prompts.yml` 恢复出厂默认值。
---
## 备份
所有用户数据(`calivi.db` 加会话签名密钥)都存储在名为
`calivi-data` 的命名卷中 — 不是绑定挂载。
```bash
docker compose stop calivi-backend
VOL=$(docker volume inspect calivi_calivi-data --format '{{.Mountpoint}}')
sudo tar czf calivi-backup-$(date +%F).tar.gz --numeric-owner -C "$VOL" .
docker compose start calivi-backend
```
使用 `--numeric-owner` 恢复:`calivi.db` 和 `secret_key` 属于不同的
用户。
---
## 开发
```bash
# 前端(vite开发服务器在:5173)
cd frontend && npm install && npm run dev
npm test # vitest + jsdom
# 后端
cd backend
python3 -m venv .venv-test && ./.venv-test/bin/pip install -r requirements-dev.txt
./.venv-test/bin/pytest # 真实的HTTP层(httpx ASGITransport),无需实时服务器
./.venv-test/bin/uvicorn app.main:app --reload --port 8000
```
当后端单独运行时,前端来自不同的源
(`:5173`),因此 `CORS_ORIGINS` 生效 — 它已经默认为
`http://localhost:5173`。
**参见 [`ARCHITECTURE.md`](ARCHITECTURE.md) 了解架构,设计决策和已知
陷阱 — 组件映射,工具循环,安全说明和“不要再这样做”的警告。
---
## 构建
**后端:** FastAPI · SQLAlchemy · SQLite · httpx · PyJWT · bcrypt
**前端:** React · Vite · Tailwind · react-markdown · KaTeX
**部署:** Docker Compose(后端 + nginx/前端 + SearXNG)
---
## 安全
卡利维设计为单个租户应用程序,在您自己的网络上运行。在
直接暴露给互联网之前,请注意:
- 网络搜索结果和文档附件被视为 **不受信任的内容** 并
传递给模型内的限定块(提示注入防御)。
- 前端使用严格的内容安全策略提供;模型输出中的远程图像不会被获取
(数据外流向量)。
- 登录尝试按帐户限制(默认:5次尝试/15分钟)。
- 工具层目前 **只读** — 状态更改工具被拒绝。
---
## 贡献
欢迎贡献 — 参见 [CONTRIBUTING.md](CONTRIBUTING.md)。
提交必须由 [DCO](DCO) 签署(`git commit -s`)。没有CLA。
## 许可证
[MIT](LICENSE) © 2026 Orkun Soylu
图标由 [Lucide](https://lucide.dev) (ISC)提供。字体:JetBrains Mono Nerd Font
(SIL OFL 1.1)。 [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md) 中有关于捆绑的第三方资产的完整署名。