You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
10 KiB
189 lines
10 KiB
|
1 week ago
|
# LLM API 网关 (OneAPI) 与 Skill 模型路由客户端
|
||
|
|
|
||
|
|
**文档版本:** 2026-03-16
|
||
|
|
**用途:** 供 AI 与后续维护人员理解 OneAPI 网关部署与 Skills 共享 LLM 客户端的架构、功能与使用方式。
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. 架构概览
|
||
|
|
|
||
|
|
- **OneAPI 网关**:独立于 OpenClaw 进程的 Docker 服务,对外提供 OpenAI 兼容的 Chat Completions API,实现多模型统一管理、按需路由与跨服迁移(迁移时打包 `infrastructure/oneapi/data` 即可无损恢复)。
|
||
|
|
- **Skill 共享客户端**:`skills/shared/llm_client.js` 供视觉、Coding、金融等 Skill 按「模型名」调用网关,通过环境变量 `LLM_BASE_URL`、`LLM_API_KEY` 配置,与现有 remote-blueprints 的 Agent 配置命名一致。
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────────────────────────────┐
|
||
|
|
│ Skills (daily-horoscope, tavily, 视觉/Coding/金融 等) │
|
||
|
|
│ require('../shared/llm_client').callSpecificModel(model, msgs) │
|
||
|
|
└─────────────────────────────┬─────────────────────────────────────┘
|
||
|
|
│
|
||
|
|
▼
|
||
|
|
┌─────────────────────────────────────────────────────────────────┐
|
||
|
|
│ skills/shared/llm_client.js │
|
||
|
|
│ - 读取 LLM_BASE_URL, LLM_API_KEY │
|
||
|
|
│ - URL 智能拼接、超时(默认 60s)、错误解析与 [LLM_Client] 日志 │
|
||
|
|
└─────────────────────────────┬─────────────────────────────────────┘
|
||
|
|
│ HTTP POST /v1/chat/completions
|
||
|
|
▼
|
||
|
|
┌─────────────────────────────────────────────────────────────────┐
|
||
|
|
│ OneAPI (openclaw-llm-gateway) │
|
||
|
|
│ - 绑定 TAILSCALE_IP:3000,仅内网访问 │
|
||
|
|
│ - 数据持久化: ./data → SQLite │
|
||
|
|
└─────────────────────────────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. 文件与目录结构
|
||
|
|
|
||
|
|
### 2.1 OneAPI 基础设施(独立于 OpenClaw)
|
||
|
|
|
||
|
|
| 路径 | 说明 |
|
||
|
|
|------|------|
|
||
|
|
| `workspace/infrastructure/oneapi/docker-compose.yml` | 服务定义:镜像 `justsong/one-api:latest`,容器名 `openclaw-llm-gateway`,端口 `${TAILSCALE_IP}:3000:3000`,数据卷 `./data:/data`,时区 `TZ=Asia/Shanghai`。 |
|
||
|
|
| `workspace/infrastructure/oneapi/.env.example` | 环境变量模板,仅 `TAILSCALE_IP=xxx.xxx.xxx.xxx`,需改为本机 Tailscale IP。 |
|
||
|
|
| `workspace/infrastructure/oneapi/deploy_gateway.sh` | 部署脚本:先切换到脚本所在目录(CWD 无关),若无 `.env` 则从 `.env.example` 复制,再执行 `docker compose up -d`,并打印管理后台 URL 与默认账密(root/123456)。需 `chmod +x`。 |
|
||
|
|
| `workspace/infrastructure/oneapi/data/` | 运行时由 Docker 创建,持久化 OneAPI 的 SQLite;**迁移时打包此目录即可恢复**。 |
|
||
|
|
|
||
|
|
### 2.2 Skill 共享客户端
|
||
|
|
|
||
|
|
| 路径 | 说明 |
|
||
|
|
|------|------|
|
||
|
|
| `workspace/skills/shared/llm_client.js` | 共享模块:导出 `callSpecificModel(modelName, messages, options)`,无 npm 依赖,仅依赖 Node 内置 `fetch`(Node 18+)。 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. 环境变量(与 Agent 配置对齐)
|
||
|
|
|
||
|
|
| 变量 | 说明 | 使用处 |
|
||
|
|
|------|------|--------|
|
||
|
|
| `LLM_BASE_URL` | 网关基础 URL,如 `http://100.x.x.x:3000`;可带或不带 `/v1`,客户端会智能拼接。 | llm_client.js、remote-blueprints 的 Agent 模型配置 |
|
||
|
|
| `LLM_API_KEY` | OneAPI 分配的 API Key(在 OneAPI 管理后台创建)。 | llm_client.js、Agent 模型配置 |
|
||
|
|
| `TAILSCALE_IP` | 本机 Tailscale IP,用于 OneAPI 端口绑定与访问地址。 | infrastructure/oneapi/.env、deploy_gateway.sh |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. 部署 OneAPI 网关
|
||
|
|
|
||
|
|
1. 进入目录:`cd workspace/infrastructure/oneapi`(或从任意路径执行 `./infrastructure/oneapi/deploy_gateway.sh`,脚本会自行切换到所在目录)。
|
||
|
|
2. 若首次部署且无 `.env`,脚本会从 `.env.example` 复制;**务必编辑 `.env` 将 `TAILSCALE_IP` 改为本机真实 Tailscale IP**,否则端口可能绑定失败或无法访问。
|
||
|
|
3. 执行:`./deploy_gateway.sh`。
|
||
|
|
4. 访问管理后台:`http://<TAILSCALE_IP>:3000`,默认登录 root / 123456;在后台添加渠道与模型并创建 API Key,将 Key 填入使用方的 `LLM_API_KEY`。
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. Skill 侧使用 llm_client
|
||
|
|
|
||
|
|
### 5.1 基本用法
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
const { callSpecificModel } = require('../shared/llm_client'); // 路径按实际 Skill 位置调整
|
||
|
|
|
||
|
|
const messages = [
|
||
|
|
{ role: 'system', content: 'You are a helpful assistant.' },
|
||
|
|
{ role: 'user', content: 'Hello' },
|
||
|
|
];
|
||
|
|
const response = await callSpecificModel('qwen3.5-plus', messages, {
|
||
|
|
temperature: 0.7,
|
||
|
|
max_tokens: 2048,
|
||
|
|
timeoutMs: 120000, // 可选,默认 60000
|
||
|
|
});
|
||
|
|
// response 为 OpenAI 兼容的 JSON,如 { choices: [...], usage: {...} }
|
||
|
|
const text = response.choices?.[0]?.message?.content ?? '';
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5.2 行为与约束
|
||
|
|
|
||
|
|
- **URL 智能拼接**:若 `LLM_BASE_URL` 已以 `/v1` 结尾(如 `http://100.x:3000/v1`),则只拼接 `/chat/completions`;否则拼接 `/v1/chat/completions`,避免出现 `/v1/v1/chat/completions` 导致 404。
|
||
|
|
- **超时**:默认 60 秒,可通过 `options.timeoutMs` 覆盖;超时后抛出带 `code: 'ETIMEDOUT'` 的 Error,便于 Skill 降级。
|
||
|
|
- **仅支持非流式**:当前实现不支持 `stream: true`,传入 `stream: true` 会抛出配置错误;流式需后续扩展或由 Skill 自行请求网关。
|
||
|
|
- **参数**:`messages` 必须为数组;`options` 中除 `timeoutMs` 外(仅客户端使用)会原样传给 API(如 `temperature`、`max_tokens`)。
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. 错误处理与日志
|
||
|
|
|
||
|
|
- **HTTP 非 200**:客户端会尝试解析响应体中的 `error.message` 或 `message`(兼容 OneAPI/OpenAI 格式),将摘要写入抛出 Error 的 `message` 与 `cause`,并在控制台打印 `[LLM_Client] Error calling <modelName>: <摘要>`,便于在 OpenClaw Gateway 日志中直接看到失败原因。
|
||
|
|
- **超时**:抛出 `Error`,`code: 'ETIMEDOUT'`,并打印 `[LLM_Client] Error calling <modelName>: timeout (<timeoutMs>ms)`。
|
||
|
|
- **配置错误**:未设置 `LLM_BASE_URL`/`LLM_API_KEY`、`messages` 非数组、`stream: true` 等,抛出带 `code: 'LLM_CLIENT_CONFIG'` 的 Error。
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. 与现有系统关系
|
||
|
|
|
||
|
|
- Agent 的 `models.providers.default_llm.baseUrl` 已指向同一 OneAPI 实例(`http://100.115.94.1:3000/v1`),Chat 请求统一由 OneAPI 路由。
|
||
|
|
- 与 `workspace/remote-blueprints/template/.env.tpl` 中的 `LLM_BASE_URL`、`LLM_API_KEY`、`LLM_MODEL_ID`、`EMBEDDING_MODEL_ID` 命名一致;Skill 侧通过 `callSpecificModel` 按模型名路由,Agent 侧使用 `LLM_MODEL_ID` 作为默认模型。
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. mem0 记忆系统与 OneAPI 的集成(2026-03-16)
|
||
|
|
|
||
|
|
mem0-integration Skill 的 LLM 和 Embedder 也全面切换到 OneAPI 网关,不再直连 DashScope。
|
||
|
|
|
||
|
|
### 8.1 完整请求路径
|
||
|
|
|
||
|
|
```
|
||
|
|
用户消息
|
||
|
|
│
|
||
|
|
├─→ OpenClaw Agent 对话 ──→ default_llm/MiniMax-M2.5 ──→ OneAPI :3000
|
||
|
|
│ │
|
||
|
|
│ 路由到渠道
|
||
|
|
│
|
||
|
|
└─→ mem0-integration plugin (Python 子进程)
|
||
|
|
│
|
||
|
|
├─ Pre-Hook: Embedder (text-embedding-v4) ──→ OneAPI :3000
|
||
|
|
│ 向量化查询 → Qdrant 检索
|
||
|
|
│
|
||
|
|
└─ Post-Hook: LLM (MiniMax-M2.5) ──→ OneAPI :3000
|
||
|
|
提取/合并记忆 → Embedder → Qdrant 写入
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8.2 LLM 与 Embedder 的分工
|
||
|
|
|
||
|
|
| 模型 | 分工 | 调用时机 |
|
||
|
|
|------|------|----------|
|
||
|
|
| **LLM** (`MiniMax-M2.5`) | 从对话中**提取**有价值的记忆;**去重/合并**已有记忆;生成结构化记忆摘要 | Post-Hook 写入时(后台异步) |
|
||
|
|
| **Embedder** (`text-embedding-v4`) | 将文本向量化(1024 维);写入 Qdrant + Pre-Hook 检索时均需调用 | 读写均需 |
|
||
|
|
|
||
|
|
> mem0 中 LLM 和 Embedder **相互独立**,可以使用不同模型。当前两者都走 OneAPI,但 OneAPI 内部可为 Chat 和 Embedding 配置不同渠道。
|
||
|
|
|
||
|
|
### 8.3 环境变量传递路径
|
||
|
|
|
||
|
|
```
|
||
|
|
/.openclaw/.env
|
||
|
|
LLM_BASE_URL=http://100.115.94.1:3000/v1
|
||
|
|
LLM_API_KEY=sk-...
|
||
|
|
LLM_MODEL_ID=MiniMax-M2.5
|
||
|
|
EMBEDDING_MODEL_ID=text-embedding-v4
|
||
|
|
│
|
||
|
|
▼ OpenClaw Gateway 加载 .env → process.env
|
||
|
|
│
|
||
|
|
▼ index.js: spawn(python, args, { env: process.env })
|
||
|
|
│
|
||
|
|
▼ mem0_client.py 模块加载时
|
||
|
|
OPENAI_BASE_URL ← LLM_BASE_URL
|
||
|
|
OPENAI_API_KEY ← LLM_API_KEY
|
||
|
|
LLM model ← LLM_MODEL_ID
|
||
|
|
Embedder model ← EMBEDDING_MODEL_ID
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8.4 systemd 服务配置说明
|
||
|
|
|
||
|
|
`/etc/systemd/system/openclaw-gateway.service` 已移除原来的三行硬编码 DashScope 环境变量:
|
||
|
|
|
||
|
|
```ini
|
||
|
|
# 已移除(不再需要):
|
||
|
|
# Environment=MEM0_DASHSCOPE_API_KEY=sk-...
|
||
|
|
# Environment=OPENAI_API_BASE=https://dashscope.aliyuncs.com/...
|
||
|
|
# Environment=OPENAI_BASE_URL=https://dashscope.aliyuncs.com/...
|
||
|
|
```
|
||
|
|
|
||
|
|
API 端点和密钥现在完全由 `.env` 文件管理,通过 OpenClaw→Python 子进程链传递,与 Agent 配置保持单一来源。
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9. 修复与审查记录
|
||
|
|
|
||
|
|
- **llm_client.js**:增加 `messages` 数组校验;明确拒绝 `stream: true` 并抛出配置错误,避免对流式响应调用 `response.json()` 导致异常;`parseErrorBody` 支持 `error` 为字符串的网关格式;对 `LLM_BASE_URL`/`LLM_API_KEY` 做 `.trim()` 避免 .env 尾随空格。
|
||
|
|
- **deploy_gateway.sh**:逻辑与目录穿透已按计划实现,无需修改;文档中强调首次部署后须编辑 `.env` 设置真实 `TAILSCALE_IP`。
|
||
|
|
- **文档**:本文件汇总架构、文件结构、环境变量、部署步骤、Skill 使用方式、错误与日志、与现有系统关系,便于 AI 与维护人员查阅。
|