犀牛鸟 2026 研究笔记 estelledc.github.io

案例 #1754:hybrid-search POST API

PR:#1754 | Issue #1727 | 8 文件 +1137/-173 | 2026-06-22 合入 | 维护者 lyingbug

结论先行

外部评测脚本、Go SDK 与 MCP server 需要调用 hybrid-search(混合向量 + 关键词 + filter),但原先只有 GET 同路径 — 复杂 JSON body 要么塞不进 URL,要么依赖非标准的 GET + body。本 PR 新增 POST /knowledge-bases/:id/hybrid-search 为推荐方式,保留 GET 兼容旧客户端,并同步 Go client、Swagger、docs/api、MCP tool

适合谁读:做 RAG 评估、MCP 集成、竞赛 benchmark 打检索接口的贡献者。

Ch24 三路检索 · Ch46 MCP 互操作 · eval 脚本 · benchmark


1. 问题现象(What)

Issue #1727 诉求

谁受影响

调用方 痛点
curl/评测脚本 只能 hack GET + -d,行为因客户端而异
官方 Go SDK 缺少 idiomatic PostHybridSearch
MCP server Tool 定义需稳定 HTTP 契约
竞赛 eval Context Recall 应 直打检索 API,而非绕 chat

修复前 / 修复后

能力 修复前 修复后
复杂 filter body GET query / GET+body hack POST JSON(推荐)
旧客户端 GET GET 仍兼容
Go SDK / MCP 无 Post 方法 同步 POST

定位步骤

rg "hybrid-search|HybridSearch" internal/handler/ internal/router/ client/
rg "hybrid" mcp-server/

先读 handler/knowledgebase.go + router.go,swagger diff 可后 skim。


2. 根因分析(Why)

架构视角

flowchart TB
    subgraph clients [调用方]
        C1[Go SDK]
        C2[MCP Python]
        C3[curl / eval]
    end
    subgraph api [WeKnora API]
        H[handler/knowledgebase.go]
        S[同一 service 层检索逻辑]
    end
    C1 -->|旧: GET + body| H
    C2 -->|旧: GET + body| H
    C3 -->|旧: GET + body| H
    H --> S
    C1 -.->|新: POST JSON| H
    C2 -.->|新: POST JSON| H
    C3 -.->|新: POST JSON| H

根因不是检索算法,而是 HTTP 契约滞后于集成需求 — 检索核心早已存在,缺的是 RESTful 的 POST 入口与文档/MCP 对齐。

关键文件(先读这些,别看 swagger 吓退)

文件 职责
internal/handler/knowledgebase.go POST handler,复用 GET 背后 service
internal/router/router.go 注册 POST .../hybrid-search
client/knowledgebase.go SDK 新方法
mcp-server/weknora_mcp_server.py Tool 改调 POST
docs/swagger.*docs/api/knowledge-base.md 契约文档(行数大头)

PR Summary 明确:


3. 解决方案(How)

设计原则

  1. POST 与 GET 共享 service 层 — 避免双份检索逻辑分叉(同 case-1774 三层思想)
  2. 兼容优先 — 不 break 已有 GET 集成
  3. 文档与 MCP 同步 — API 变更必须可发现

调用示例

curl -X POST "$BASE/api/v1/knowledge-bases/$KB_ID/hybrid-search" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "WeKnora 三路检索如何配置",
    "top_k": 5
  }'

Go SDK(概念上):

// client/knowledgebase.go — 新增 PostHybridSearch,body 为 struct
resp, err := client.PostHybridSearch(ctx, kbID, &HybridSearchRequest{
    Query: "…",
    TopK:  5,
})

为何 +1137 行仍「教学分 5」?

组成部分 行数占比 阅读建议
swagger/OpenAPI 生成 diff 可 skim
handler + router + client 精读
MCP + markdown docs 看 request/response 示例

Test plan(PR):make docsgo build ./...client build — 说明 契约与编译 是合入门槛。

PR 四段式

What Why How Test
GET 无法承载复杂 hybrid body REST 语义 + 集成需求 POST + GET 兼容 + MCP make docs + curl POST

4. 与 RAG 管线 / 竞赛

用途 说明
离线 eval smoke-10.jsonl 可扩展为直调 hybrid-search 算 recall@k
Agent MCP 检索 tool 与 UI chat 同源 API,减少行为漂移
#1248 feedback 权重最终进检索 — 需 stable POST 更新 filter/weight 参数(Phase2)

5. 可复用模式


6. 常见误区

错误认知:8 文件 +1000 行 = 不适合新手贡献。
正确理解:净逻辑可能 <100 行;其余是 文档债一次性还 — 学如何 读 PR diff 优先级

错误认知:hybrid-search 只能走 chat completion。
正确理解:评测与调试应 直打检索 API,否则混淆 generation 噪声。

错误认知:GET 带 body 是标准做法。
正确理解:HTTP 语义与中间件实现都不保证 GET body — POST 才是可移植契约。

错误认知:保留 GET 就等于 POST 可有可无。
正确理解:GET 仅作 兼容旧客户端;文档与 MCP 应标 POST 为 recommended,新集成一律走 POST JSON。


7. 实验建议

  1. 对同一 KB、同一 query,对比 GET(若仍可用)与 POST 返回的 chunk id 列表是否一致
  2. eval_weknora_ragas.py --dry-run 后,实跑时将 retrieval 步改为 POST hybrid-search
  3. MCP server 本地起 tool,验证 POST 路径

8. 思考点

  1. 若 filter 含 嵌套 JSON(多 tag、时间范围),URL encoding 与 POST body 各有什么失败模式?
  2. POST hybrid-search 如何做 幂等缓存(CDN/网关)?检索 API 应否声明 non-cacheable?
  3. case-1784 强调错误语义 — 为本 POST 设计 3 个 可行动 4xx/5xx 响应例。
  4. 权重调整(#1248 Phase2)应放在 hybrid-search body 还是独立 endpoint?

章末自检


延伸阅读