Files
knowledge-adapter-service/docs/frontend-api-guide.md
T
姚章浩 693cd952cd feat(asset): 清理被截断的结尾图片引用,修复整单 502
仓库首次入库(此前仅存在于本地工作区与测试服构建产物中,未上传)。

改动(src/knowledge_adapter/asset_content_processor.py):
- 新增 _TRUNCATED_TRAILING_MARKDOWN_RESOURCE_IMAGE 规则,清除 chunk 末尾被截断的
  ![](resource://xxxx 残片(WeKnora 分块在图片引用中间截断时产生,旧逻辑无法匹配完整引用,
  残留 resource:// 触发 ContentProcessingError,导致整单 502)
- 回归测试 tests/test_asset_content_processor.py::
  test_truncated_trailing_markdown_resource_image_is_removed

验证证据:
- 测试服 120.76.217.51,镜像 knowledge-adapter-service:reviewed-test-aeab4b5fca2d
  (release aeab4b5fca2d8661):KB d015d05c-25c8-4d63-b2ab-e8590d3c2dfb 5 个查询全部
  http=200、records=5、残留 resource:// = 0;KB b9dacd62 无退化;容器日志无 error/exception
- 本地:pytest 141 项全通过、ruff 全通过
- 该文件与 258c31e0fe07fd08 源码的差异经逐文件 md5 比对,为唯一变更文件
2026-09-16 10:01:27 +08:00

357 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 测试环境 WeKnora 检索 API 调用说明
## 1. 当前状态
公网HTTPS链路已经通过真实验收:
```text
Dify/业务后端
→ https://weknora-api-test.dobot.cn/retrieval
→ 公网测试入口
→ 测试ECS Nginx:80
→ Knowledge Adapter:18080
→ WeKnora knowledge-search
```
真实验收结果:
```text
检索:HTTP 200
records:5
稳定图片UUID:15
图片:稳定UUID 302 → 私有OSS签名取图 HTTP 200
```
完整且已脱敏的真实请求与响应:
```text
docs/test-api-request-response.txt
```
## 2. 接口地址与安全边界
### 检索地址
```http
POST https://weknora-api-test.dobot.cn/retrieval
```
这是服务端到服务端接口:
```text
Dify可以直接调用
业务后端/BFF可以直接调用
浏览器前端不能携带Adapter API Key直接调用
```
浏览器推荐链路:
```text
浏览器前端
→ 业务后端/BFF
→ Adapter
→ WeKnora
```
禁止把真实API Key写入:
- 前端JavaScript或打包产物;
- Git仓库;
- URL查询参数;
- LocalStorage或SessionStorage;
- 普通日志、埋点或错误上报。
## 3. 请求方法
### Headers
```http
Authorization: Bearer [TEST_DIFY_KEY]
Content-Type: application/json
Accept: application/json
X-Request-ID: <可选,建议使用UUID>
```
对外调用使用的是Dify Bearer Key。WeKnora API Key和OSS凭据只由Adapter内部使用,不能提供给Dify或浏览器。
### Body
```json
{
"knowledge_id": "b9dacd62-87eb-4b38-a6b6-85f68e144796",
"query": "接线",
"retrieval_setting": {
"top_k": 5,
"score_threshold": 0.2
}
}
```
### 字段说明
| 字段 | 类型 | 必填 | 说明 |
|---|---:|---:|---|
| `knowledge_id` | UUID字符串 | 是 | 测试环境“对外知识库”固定为 `b9dacd62-87eb-4b38-a6b6-85f68e144796` |
| `query` | 字符串 | 是 | 用户问题;去除首尾空格后不能为空,最多2000字符 |
| `retrieval_setting.top_k` | 整数 | 是 | 至少为1,当前上限为5 |
| `retrieval_setting.score_threshold` | 浮点数 | 是 | 范围0至1;业务建议初始使用0.2,排查召回问题时可临时使用0 |
| `metadata_condition` | 对象 | 否 | 当前不支持非空过滤条件,调用方应省略 |
`knowledge_id`应由Dify或业务后端固定,不能接受浏览器任意指定。
## 4. 应答格式
成功响应:
```json
{
"records": [
{
"content": "Markdown格式的知识片段",
"score": 0.82,
"title": "文档标题",
"metadata": {
"knowledge_base_id": "知识库UUID",
"knowledge_id": "文档UUID",
"chunk_id": "Chunk UUID",
"chunk_index": 1,
"chunk_type": "text"
}
}
]
}
```
真实公网响应节选(`content`为缩写,完整正文见TXT样本):
```json
{
"records": [
{
"content": "<Markdown知识片段,包含稳定的 /i/<UUID> 图片地址>",
"score": 0.5314404987902756,
"title": "Dobot Magician Go用户手册V1.1_20240624_cn.pdf",
"metadata": {
"knowledge_base_id": "b9dacd62-87eb-4b38-a6b6-85f68e144796",
"knowledge_id": "3c878f4e-bfea-4c5c-b122-b63ba9db4e34",
"chunk_id": "d674ba02-7769-4e9a-8596-961cb50e1f75",
"chunk_index": 53,
"chunk_type": "text"
}
}
]
}
```
字段说明:
| 字段 | 类型 | 说明 |
|---|---:|---|
| `records` | 数组 | 检索结果;没有命中时为空数组 |
| `records[].content` | 字符串 | Markdown知识片段,不是大模型生成的最终答案 |
| `records[].score` | 浮点数 | 当前检索相关度分数;不要跨模型或跨版本硬编码绝对含义 |
| `records[].title` | 字符串 | 来源文档标题 |
| `records[].metadata` | 对象 | 知识库、文档和Chunk审计标识 |
该接口只负责检索。Dify拿到`records`后,再将知识片段交给大模型生成最终回答。
## 5. curl调用示例
真实Key应由运行环境或密钥系统注入,不要直接写进脚本:
```bash
curl --request POST \
'https://weknora-api-test.dobot.cn/retrieval' \
--header "Authorization: Bearer ${WEKNORA_ADAPTER_API_KEY}" \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header "X-Request-ID: $(python3 -c 'import uuid; print(uuid.uuid4())')" \
--data '{
"knowledge_id": "b9dacd62-87eb-4b38-a6b6-85f68e144796",
"query": "接线",
"retrieval_setting": {
"top_k": 5,
"score_threshold": 0.2
}
}'
```
## 6. Node.js服务端调用示例
下面代码只能运行在Node.js服务端或BFF,不能放入浏览器包:
```js
import { randomUUID } from "node:crypto";
const ADAPTER_URL = "https://weknora-api-test.dobot.cn/retrieval";
const KNOWLEDGE_ID = "b9dacd62-87eb-4b38-a6b6-85f68e144796";
export async function retrieveKnowledge(query) {
const apiKey = process.env.WEKNORA_ADAPTER_API_KEY;
if (!apiKey) {
throw new Error("WEKNORA_ADAPTER_API_KEY is not configured");
}
const response = await fetch(ADAPTER_URL, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
Accept: "application/json",
"X-Request-ID": randomUUID(),
},
body: JSON.stringify({
knowledge_id: KNOWLEDGE_ID,
query,
retrieval_setting: {
top_k: 5,
score_threshold: 0.2,
},
}),
signal: AbortSignal.timeout(25000),
});
if (!response.ok) {
throw new Error(`Knowledge retrieval failed: HTTP ${response.status}`);
}
const body = await response.json();
if (!body || !Array.isArray(body.records)) {
throw new Error("Invalid knowledge retrieval response");
}
return body.records;
}
```
日志中只建议记录:
```text
X-Request-ID、HTTP状态、耗时、records数量
```
不要记录:
```text
Authorization、API Key、完整问题、完整知识正文、图片302 Location
```
## 7. 浏览器调用BFF示例
浏览器只调用业务后端,不携带Adapter API Key:
```js
const response = await fetch("/api/knowledge/retrieve", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: userInput }),
});
if (!response.ok) {
throw new Error(`检索失败:HTTP ${response.status}`);
}
const { records } = await response.json();
```
业务后端负责补充:
```text
Authorization
knowledge_id
retrieval_setting
X-Request-ID
```
## 8. Dify配置
在Dify外部知识库API中填写:
```text
Endpoint:
https://weknora-api-test.dobot.cn/retrieval
API Key:
测试环境Adapter原始Key
External Knowledge ID:
b9dacd62-87eb-4b38-a6b6-85f68e144796
```
不要把Dify直接指向WeKnora原生`/api/v1/knowledge-search`。协议转换、知识库白名单、响应裁剪和图片稳定URL都由Adapter负责。
## 9. 图片调用方法
检索结果中的图片使用稳定地址:
```text
https://weknora-api-test.dobot.cn/i/<UUID>
```
真实样例:
```text
https://weknora-api-test.dobot.cn/i/02e269e8-c4f0-4b3f-a41e-82f5b222a31f
```
前端直接将稳定地址作为图片地址:
```html
<img src="https://weknora-api-test.dobot.cn/i/<UUID>" alt="知识图片">
```
图片访问不需要Bearer Key:
```text
浏览器GET /i/<UUID>
→ Adapter查询有效映射
→ HTTP 302
→ 私有OSS 10分钟签名URL
→ 图片HTTP 200
```
注意:
- 保存和渲染稳定`/i/<UUID>`地址;
- 不保存、不打印、不缓存302后的OSS签名地址;
- 10分钟过期的是OSS签名地址,不是稳定UUID地址;
- 稳定UUID属于能力链接,不应进入无关日志或公开页面;
- OSS Bucket继续保持Private。
真实图片验收:
```text
稳定UUID:HTTP 302
Cache-Control:no-store
Referrer-Policy:no-referrer
私有OSS匿名访问:HTTP 403
短期签名取图:HTTP 200
类型:image/jpeg
大小:56,025 bytes
JPEG结构:有效
```
## 10. 错误码
| HTTP状态 | 含义 | 调用方处理 |
|---:|---|---|
| 200 | 成功 | 读取`records`,允许空数组 |
| 400 | 请求格式错误或知识库未授权 | 不重试;检查请求字段和固定`knowledge_id` |
| 401 | API Key缺失或错误 | 不重试;检查服务端Secret配置 |
| 403 | 请求方法被Nginx拒绝 | 不重试;确认使用`POST /retrieval`或`GET /i/<UUID>` |
| 404 | 路径或图片UUID不存在 | 不重试;检查地址 |
| 502 | WeKnora响应异常或内容处理失败 | 记录Request ID,有限重试并告警 |
| 503 | WeKnora超时、暂时不可用或图片签名失败 | 建议退避重试1至2次,仍失败则降级 |
不要自动重试400、401、403和404。
## 11. 联调检查表
- [x] 公网HTTPS回源测试ECS Nginx 80;
- [x] 保留`Host`、`Authorization`、`Content-Type`和`X-Request-ID`;
- [x] 无Key请求返回401;
- [x] 正确Key请求返回HTTP 200和`records`;
- [x] 有图结果返回稳定UUID,图片302和最终下载通过;
- [ ] 前端构建产物中确认不存在Adapter API Key;
- [ ] 业务日志中确认没有Authorization、完整正文和OSS签名URL;
- [ ] 生产环境使用独立域名、独立Key和独立知识库白名单。