## 查询取数接入路线

以下是推荐阅读顺序，不代表已完成的进度；可选步骤可以跳过。

1. [获取与使用 API Key](/docs/authentication)
2. [完成第一次查询](/docs/quickstart)
3. [关键词与语义检索](/docs/search) ← 当前页
4. [用户、话题、标签与分类](/docs/browse)（可选）
5. [读取内容与历史](/docs/documents)
6. [下载图片与附件](/docs/attachments)（可选）
7. [限流、重试与排错](/docs/production)

**开始前**：已跑通首次查询，明确目标是关键词、语义相似，还是按用户或标签精确筛选。

# 关键词与语义检索

本文是 `POST /v1/search` 系列接口的完整参考。所有接口都需要：


```http
Authorization: Bearer whale_consumer_xxx
Content-Type: application/json
```


生产 Base URL：`https://www.marsyoo.com`。

## 选择哪个接口

| 接口 | 用途 |
| --- | --- |
| `POST /v1/search` | 默认入口：历史索引，可选实时补齐 |
| `POST /v1/search/history` | 只查历史索引，绝不触发采集 |
| `POST /v1/live-queries` | 直接创建按需采集任务，见[按需采集新数据](/docs/live-query) |
| `POST /v1/contents/search` | 旧兼容入口，行为等同 `/v1/search`，不推荐新接入 |
| `POST /v1/search/keyword`、`/vector`、`/hybrid` | 对外专项检索；一般优先使用统一入口的 `mode` |

按用户、话题、标签或外部分类精确取数，使用[用户、话题、标签与分类](/docs/browse)。目录筛选与语义相似检索是不同能力，目前不能将这些目录条件直接传入向量/混合检索。

## 请求字段

`POST /v1/search` 与 `POST /v1/search/history` 请求体相同，唯一区别是 `/v1/search` 支持 `live` 选项。

| 字段 | 类型 | 必填 | 默认 | 说明 |
| --- | --- | --- | --- | --- |
| `mode` | string | 否 | 见说明 | `keyword` / `vector` / `hybrid`。省略时：传了 `query_vector` 走 `hybrid`，否则走 `keyword` |
| `query` | string | 条件 | — | keyword/hybrid 的查询文本；vector 模式可只传 `query_vector` |
| `query_vector` | number[] | 否 | — | 自带 embedding 时使用 |
| `datasets` | string[] | 否 | 订阅全集 | 数据集过滤，只能缩小授权范围 |
| `platforms` | string[] | 否 | 订阅全集 | 平台过滤，枚举见[平台覆盖](/docs/platforms) |
| `languages` | string[] | 否 | 不限 | ISO 639-1，如 `["zh","en"]` |
| `published_from` / `published_to` | string(RFC3339) | 否 | 不限 | 平台发布时间窗口 |
| `limit` | int | 否 | 20 | 每页条数，最大 100 |
| `cursor` | string | 否 | — | 仅 keyword 模式支持，取自上一页响应的 `cursor` |
| `fields` | string[] | 否 | 授权全集 | 返回字段白名单，只能缩小不能扩大授权 |
| `include_highlights` | bool | 否 | false | 返回命中高亮片段 |
| `include_chunks` | bool | 否 | false | 返回命中分块明细 `matched_chunks` |
| `include_non_current` | bool | 否 | false | 包含历史版本内容 |
| `live` | object | 否 | — | 仅 `/v1/search`，见下 |

### live 选项

| 字段 | 类型 | 必填 | 默认 | 说明 |
| --- | --- | --- | --- | --- |
| `live.enabled` | bool | 是 | — | 开启实时补齐 |
| `live.resource_kind` | string | 是 | — | `content` / `account` / `user` / `channel` / `video` |
| `live.max_items_per_platform` | int | 否 | 20 | 每平台采集上限，受订阅配额约束 |
| `live.deadline_seconds` | int | 否 | 300 | 采集截止，10–900 |

开启 `live.enabled` 时请求必须携带 `Idempotency-Key` 头，且当前版本只允许一个 dataset、1–5 个 platform。

## 响应字段


```json
{
  "mode": "hybrid",
  "results": [
    {
      "document_id": "doc_01J...",
      "version_id": "ver_01J...",
      "best_chunk_id": "chunk_01J...",
      "dataset_id": "social_media_raw",
      "source_platform": "weibo",
      "title": "...",
      "body": "最佳命中分块的摘要（不是完整正文）",
      "content_type": "post",
      "language": "zh",
      "published_at": "2026-09-01T08:00:00Z",
      "origin": "indexed",
      "score": 0.83,
      "keyword_score": 0.91,
      "vector_score": 0.74,
      "rerank_score": 0.83,
      "highlights": ["...<em>...</em>..."],
      "matched_chunks": [
        {"chunk_id": "chunk_01J...", "chunk_index": 3, "body": "...", "score": 0.91}
      ]
    }
  ],
  "total": 1,
  "total_documents": 1,
  "total_chunks": 1432,
  "top_k": 20,
  "degraded": false,
  "warnings": [],
  "live_query": null
}
```


| 字段 | 说明 |
| --- | --- |
| `results[].document_id` | 文档 ID，读详情用它 |
| `results[].body` | 最佳命中分块摘要；完整正文见[读取内容与历史](/docs/documents) |
| `results[].origin` | `indexed`（历史索引）/ `live`（实时采集） |
| `results[].score` | 最终排序分，hybrid 为 rerank 后分数 |
| `total` / `total_documents` | 本页去重后的文档数，不是全库命中文档总数 |
| `total_chunks` | 检索后端报告的分块命中量；vector/hybrid 可能仅代表候选集合，不能当作全库精确总数 |
| `cursor` | 存在表示有下一页，原样放入下一次请求的 `cursor` |
| `degraded` | true 表示部分后端不可用已降级 |
| `warnings[]` | 降级或部分失败原因，见错误与降级 |
| `live_query` | 实时补齐句柄，含 `id`、`status`、`runs[]` |

同一页同一 `document_id` 只出现一次。

## 分页

`limit` 默认 20、最大 100。只有 keyword 模式支持游标翻页；vector/hybrid 当前只返回 Top-K 候选，不支持遍历全库，传入 `cursor` 返回 `400 invalid_query`。keyword 响应包含 `cursor` 时可原样传回，并保持查询条件不变；目前不承诺固定的游标有效期。需要完整同步时使用目录时间窗口或 Change Feed，不要用语义检索当导出接口。

`include_non_current=true` 允许历史版本参与检索，但结果仍在每页按 `document_id` 去重，不是版本列表。需要一个文档的版本历史时[查看正文版本历史](/docs/documents#查看正文版本历史)。

## 实时补齐

`live.enabled=true` 时响应先返回 `origin=indexed` 的历史结果，并附带：


```json
"live_query": {
  "id": "lq_01J...",
  "status": "pending",
  "resource_kind": "video",
  "runs": [
    {"id": "lqr_01J...", "platform": "youtube", "status": "pending", "collection_task_id": "ctask_01J..."}
  ]
}
```


之后用 `GET /v1/live-queries/{id}` 轮询状态、`GET /v1/live-queries/{id}/results` 读结果，详见[按需采集新数据](/docs/live-query)。实时链路不可用时仍返回历史结果，`warnings[]` 说明原因。

## 检索模式怎么选

| mode | 适用 | 特点 |
| --- | --- | --- |
| `keyword` | 精确词、品牌名、人名、正文中的话题词 | 标题/正文检索，不等同于 topics/tags 精确筛选 |
| `vector` | 语义相似、跨语言、表达不一致 | 可只传 `query`，服务端生成 embedding |
| `hybrid` | 默认推荐 | 融合词面、向量、质量分、时效分；向量分支最多等 5s，超时会降级为关键词结果并置 `degraded=true`、`warnings[].code=vector_search_unavailable` |

## 权限与配额

- `datasets` / `platforms` / `fields` 只能缩小订阅授权范围；越界返回 `403 entitlement_denied` 或被裁剪。
- 每日配额按 UTC 自然日、按应用、按操作统计，进入检索后端前原子占用；因 `429` 拒绝的请求不计入。
- 实时并发/RPS 由订阅策略控制，触发返回 `429 rate_limit_exceeded` 与 `Retry-After`。

## 错误与降级

| HTTP | code | 含义 | 处理 |
| --- | --- | --- | --- |
| 400 | `invalid_query` | 参数错误或 mode 非法 | 对照本文修正 |
| 401 | `unauthorized` | key 缺失/无效 | 检查 Authorization 头 |
| 403 | `entitlement_denied` | 超出订阅 dataset/platform/字段 | 缩小范围或申请授权 |
| 429 | `quota_exceeded` | 日配额耗尽 | 等 UTC 次日或联系平台加配额 |
| 429 | `rate_limit_exceeded` | 触发实时限流 | 按 `Retry-After` 重试 |
| 409 | `live_query_results_not_ready` | 实时结果未就绪 | 继续轮询状态 |
| 503 | `search_backend_unavailable` | 检索后端整体不可用 | 指数退避重试 |

降级（HTTP 仍 200，`degraded=true`）：

| warnings[].code | 含义 |
| --- | --- |
| `vector_search_unavailable` | 向量分支超时/不可用，仅返回关键词结果 |
| `keyword_search_timed_out` | 关键词分支超时，该模式结果为空；缩小平台/时间范围重试 |
| `live_query_unavailable` | 实时补齐创建失败，仅返回历史结果 |

## 检查与继续

**完成标准**：能够选择检索模式、设置平台与时间范围，并理解搜索摘要及各模式分页限制；需要精确筛选时转到下一页。

上一步：[完成第一次查询](/docs/quickstart)

**推荐下一步**：[用户、话题、标签与分类](/docs/browse)（可选，无需此能力可跳过）

跳过可选步骤：[读取内容与历史](/docs/documents)