案例 #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 诉求
- 集成方想用 JSON body 传:
query、top_k、filter、vector_weight等 - GET 的语义是 幂等、无 body;部分 HTTP 客户端/网关 丢弃 GET body
- URL query string 对长 query、复杂 filter 长度与可读性 都差
谁受影响
| 调用方 | 痛点 |
|---|---|
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 明确:
- POST = recommended
- GET = backward compatible(含「GET 带 JSON body」的旧集成)
3. 解决方案(How)
设计原则
- POST 与 GET 共享 service 层 — 避免双份检索逻辑分叉(同 case-1774 三层思想)
- 兼容优先 — 不 break 已有 GET 集成
- 文档与 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 docs、go 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. 可复用模式
- API 演进:新 POST + 旧 GET 并存,文档标 recommended
- 大 PR 拆读:先看 handler 净增,再扫 docs
- MCP 与 HTTP 同步改 — Agent 生态入口不能落后 REST
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. 实验建议
- 对同一 KB、同一 query,对比 GET(若仍可用)与 POST 返回的 chunk id 列表是否一致
- 用 eval_weknora_ragas.py
--dry-run后,实跑时将 retrieval 步改为 POST hybrid-search - MCP server 本地起 tool,验证 POST 路径
8. 思考点
- 若 filter 含 嵌套 JSON(多 tag、时间范围),URL encoding 与 POST body 各有什么失败模式?
- POST hybrid-search 如何做 幂等缓存(CDN/网关)?检索 API 应否声明 non-cacheable?
- case-1784 强调错误语义 — 为本 POST 设计 3 个 可行动 4xx/5xx 响应例。
- 权重调整(#1248 Phase2)应放在 hybrid-search body 还是独立 endpoint?
章末自检
- 说清 GET vs POST 适用场景与兼容策略
- 指出 MCP 与 HTTP API 的对应关系
- 解释为何 eval 应直打 hybrid-search