# Embeddings

把文本或 token 数组转成向量，用于语义搜索、相似度、聚类、推荐、分类、RAG 检索等。

```text
POST https://api.clomio.ai/v1/embeddings
```

> **分组要求:** **仅 OpenAI 分组**支持。其他分组返回 404，见 [端点 × 分组](#/api-usage)。新接入统一使用 `/v1/embeddings`。

---

## 请求头

| 请求头 | 必填 | 值 |
|--------|:----:|-----|
| `Authorization` | 是 | `Bearer 你的密钥` |
| `Content-Type` | 是 | `application/json` |

---

## 请求校验与字段边界

服务只做必要的请求格式校验：

- 请求体不能为空。
- 请求体必须是合法 JSON，否则返回 400 `Failed to parse request body`。
- 请求体超过服务限制时返回 413 `invalid_request_error`。
- `model` 必须存在、必须是非空 string，否则返回 400 `model is required`。
- 端点只允许 OpenAI 分组；Grok/Claude/Gemini/其他分组返回 404 `Embeddings API is not supported for this platform`。

除 `model` 可能经过模型映射外，`input`、`encoding_format`、`dimensions`、`user` 等字段基本按原 JSON 透传给 OpenAI-compatible 上游；输入是否为空、token 上限、`dimensions` 是否被该模型支持、`encoding_format` 是否有效，主要由上游按 OpenAI Embeddings API 规则判断并返回错误。Embeddings 当前只面向 OpenAI 分组；如果持续出现 upstream error，请带 `x-request-id` 和错误体提交工单。

OpenAI 官方参数参考：<https://developers.openai.com/api/reference/resources/embeddings/methods/create>；向量用途参考：<https://developers.openai.com/api/docs/guides/embeddings>。

Embeddings 是同步 HTTP JSON 请求,没有 SSE/WebSocket 流式输出,也不会返回 job id 或轮询进度；服务会一次性返回完整结果。即使客户端误传 `stream`,也按同步请求处理；需要进度显示时请在应用层分批调用。

---

## 请求参数

| 参数 | 类型 | 必填 | 处理 / 说明 |
|------|------|:----:|------|
| `model` | string | 是 | 必须是当前分组可用的 Embeddings 模型 |
| `input` | string / string[] / number[] / number[][] | OpenAI 语义必填 | 支持单条文本、批量文本、token 数组或 token 数组批量；具体上限以模型说明为准 |
| `encoding_format` | string | 否 | `float` 或 `base64`；透传上游 |
| `dimensions` | integer | 否 | 输出维度裁剪；OpenAI 文档说明仅 `text-embedding-3` 及后续模型支持 |
| `user` | string | 否 | 终端用户标识；透传上游 |

OpenAI 官方限制会随模型变化；单个输入不能超过模型 token 上限，数组批量也有总 token 上限。超出范围时服务会返回对应错误。

---

## 响应

服务返回 OpenAI 兼容的响应体和 usage。

| 字段 | 类型 | 说明 |
|------|------|------|
| `object` | string | 通常为 `list` |
| `data` | array | 每条输入对应一个 `{embedding, index, object}` |
| `data[].embedding` | number[] 或 string | `encoding_format:"float"` 时是浮点数组；`base64` 时由上游/SDK 表现决定 |
| `model` | string | 上游实际使用模型 |
| `usage` | object | `prompt_tokens` / `total_tokens` |

```json
{
  "object": "list",
  "data": [
    { "object": "embedding", "index": 0, "embedding": [0.0123, -0.0456] }
  ],
  "model": "text-embedding-3-small",
  "usage": { "prompt_tokens": 6, "total_tokens": 6 }
}
```

---

## 最小可验证 curl

“单条”示例可验证 OpenAI-only 路由、`model` 必填校验、`input` 处理和成功响应中的 `data[].embedding`/`usage`；删除 `model` 可验证 400 `model is required`。

## 示例: 单条

```bash
curl https://api.clomio.ai/v1/embeddings \
  -H "Authorization: Bearer 你的密钥" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "text-embedding-3-small",
    "input": "今天天气真好",
    "encoding_format": "float"
  }'
```

```python
from openai import OpenAI
client = OpenAI(api_key="你的密钥", base_url="https://api.clomio.ai/v1")

r = client.embeddings.create(
    model="text-embedding-3-small",
    input="今天天气真好",
    encoding_format="float",
)
print(len(r.data[0].embedding), "维")
```

```javascript
import OpenAI from "openai";
const client = new OpenAI({ apiKey: "你的密钥", baseURL: "https://api.clomio.ai/v1" });

const r = await client.embeddings.create({
  model: "text-embedding-3-small",
  input: "今天天气真好",
  encoding_format: "float",
});
console.log(r.data[0].embedding.length, "维");
```

---

## 示例: 批量与降维

`input` 传数组即可一次嵌入多条，`data` 按 `index` 对应。支持的 `dimensions` 取决于上游模型：

```bash
curl https://api.clomio.ai/v1/embeddings \
  -H "Authorization: Bearer 你的密钥" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "text-embedding-3-small",
    "input": ["第一句", "第二句", "第三句"],
    "dimensions": 512,
    "encoding_format": "float"
  }'
```

---

## 实战场景: 语义搜索

把一批文档嵌入，再用余弦相似度找出和提问最相关的一条：

```python
import numpy as np
from openai import OpenAI

client = OpenAI(api_key="你的密钥", base_url="https://api.clomio.ai/v1")
MODEL = "text-embedding-3-small"

docs = [
    "Clomio 提供三条网关线路:直连、CF 优化、三网优化。",
    "Claude Code 用 ANTHROPIC_BASE_URL 接入网关。",
    "Codex 的配置写在 config.toml 里。",
]

def embed(texts):
    r = client.embeddings.create(model=MODEL, input=texts, encoding_format="float")
    return [np.array(d.embedding) for d in r.data]

doc_vecs = embed(docs)
q_vec = embed(["怎么换线路?"])[0]

def cosine(a, b):
    return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))

ranked = sorted(zip(docs, doc_vecs), key=lambda x: cosine(q_vec, x[1]), reverse=True)
print("最相关:", ranked[0][0])
```

实际项目里，可把文档向量存入向量检索系统；查询时只对问题做一次嵌入即可。

---

## 排障

| 现象 | 常见原因 | 处理 |
|------|----------|------|
| 404 `Embeddings API is not supported for this platform` | API Key 不在 OpenAI 分组 | 换 OpenAI 分组 Key |
| 400 `Request body is empty` | 空 body | 发送 JSON 请求体 |
| 400 `Failed to parse request body` | JSON 非法或 Content-Type/SDK 传参错误 | 用 `jq`/SDK 检查最终 body |
| 400 `model is required` | `model` 缺失、非 string 或空字符串 | 传入非空嵌入模型名 |
| 413 `invalid_request_error` | 请求体超过网关限制 | 缩小批量或分批调用 |
| 403 `permission_error` / `The current group does not support the requested model ... Available models: ...` | 当前分组不支持请求模型 | 按 Available models 换模型 |
| 404 `model_not_found` | 当前分组没有该模型 | 核对模型名和模型列表 |
| 503 `api_error` / `Service temporarily unavailable` | 当前没有可用服务资源或请求过载 | 稍后重试、降低并发或更换分组 |
| 502 `upstream_error` / `api_error` | 服务提供方请求失败 | 保存错误体和 `x-request-id` 提交支持 |
| 上游报 `dimensions` 不支持 | 模型不支持降维 | 换 `text-embedding-3*` 或移除 `dimensions` |
| 上游报 input/token 限制 | 输入为空、批量过大或超过模型 token 上限 | 分批、清理空字符串、缩短文本 |
| 返回维度和预期不同 | 模型默认维度不同或设置了 `dimensions` | 统一索引库维度；已有向量库不可混用不同维度 |

---

> Embeddings 常配合向量数据库做 RAG。对话端点见 [Responses](#/api-responses) 或 [Chat Completions](#/api-chat)。

## 字段约束速查

| 字段 | 可选值/范围 | 说明 |
|---|---|---|
| `model` | 当前 OpenAI 分组可用的 embedding 模型 | 必填 |
| `input` | 非空 string 或 string 数组 | 批量数组大小和 token 上限以上游模型为准 |
| `encoding_format` | `float` / `base64` | 默认通常为 `float` |
| `dimensions` | 正整数 | 仅支持降维的模型可用；超出模型维度会被上游拒绝 |
| `user` | string | 可选的最终用户标识 |

## 完整响应与错误

```json
{
  "object":"list",
  "data":[{"object":"embedding","index":0,"embedding":[0.01,-0.02]}],
  "model":"text-embedding-3-small",
  "usage":{"prompt_tokens":4,"total_tokens":4}
}
```

| HTTP | 典型错误 | 处理 |
|---:|---|---|
| 400 | body 非法、`model is required`、input 为空 | 修正 JSON、模型和 input |
| 400 | `dimensions`/`encoding_format` 不支持 | 删除参数或按模型文档调整 |
| 401/403 | Key 无效、分组不是 OpenAI | 更换 OpenAI 分组 Key |
| 413 | 请求体过大 | 缩小批量并分批提交 |
| 429 | 请求或上游限流 | 指数退避 |
| 502/503 | 上游请求失败或服务暂时不可用 | 带 `x-request-id` 排查 |
