diff --git a/docs/api/agent.md b/docs/api/agent.md index 95151eef9..7f6f4e5be 100644 --- a/docs/api/agent.md +++ b/docs/api/agent.md @@ -6,6 +6,8 @@ 智能体 API 用于管理自定义智能体(Custom Agent)。系统提供了内置智能体,同时支持用户创建自定义智能体来满足不同的业务场景需求。 +> 智能体的共享与跨组织分发(`/agents/:id/shares` 等)属于组织协作能力,文档见 [组织管理 API](./organization.md)。本文件只覆盖智能体自身的 CRUD、复制、占位符、类型预设以及推荐问题接口。 + ### 内置智能体 系统默认提供以下内置智能体: @@ -25,27 +27,36 @@ ## API 列表 -| 方法 | 路径 | 描述 | -|------|------|------| -| POST | `/agents` | 创建智能体 | -| GET | `/agents` | 获取智能体列表 | -| GET | `/agents/:id` | 获取智能体详情 | -| PUT | `/agents/:id` | 更新智能体 | -| DELETE | `/agents/:id` | 删除智能体 | -| POST | `/agents/:id/copy` | 复制智能体 | -| GET | `/agents/placeholders` | 获取占位符定义 | +| 方法 | 路径 | 描述 | +| ------ | -------------------------- | -------------------------- | +| POST | `/agents` | 创建智能体 | +| GET | `/agents` | 获取智能体列表 | +| GET | `/agents/:id` | 获取智能体详情 | +| PUT | `/agents/:id` | 更新智能体 | +| DELETE | `/agents/:id` | 删除智能体 | +| POST | `/agents/:id/copy` | 复制智能体 | +| GET | `/agents/placeholders` | 获取占位符定义 | --- ## POST `/agents` - 创建智能体 -创建新的自定义智能体。 +创建新的自定义智能体。成功返回 HTTP 201。 + +**请求体参数**: + +| 参数 | 类型 | 必填 | 说明 | +| ------------- | ------ | ---- | ------------------------------------------------- | +| `name` | string | 是 | 智能体名称 | +| `description` | string | 否 | 智能体描述 | +| `avatar` | string | 否 | 头像(emoji 或图标名称) | +| `config` | object | 否 | 智能体配置,详见 [配置参数](#配置参数) | **请求**: ```curl curl --location 'http://localhost:8080/api/v1/agents' \ ---header 'X-API-Key: your_api_key' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "name": "我的智能体", @@ -64,15 +75,6 @@ curl --location 'http://localhost:8080/api/v1/agents' \ }' ``` -**请求参数**: - -| 参数 | 类型 | 必填 | 说明 | -|------|------|------|------| -| `name` | string | 是 | 智能体名称 | -| `description` | string | 否 | 智能体描述 | -| `avatar` | string | 否 | 智能体头像(emoji 或图标名称) | -| `config` | object | 否 | 智能体配置,详见 [配置参数](#配置参数) | - **响应**: ```json @@ -100,22 +102,22 @@ curl --location 'http://localhost:8080/api/v1/agents' \ **错误响应**: -| 状态码 | 错误码 | 错误 | 说明 | -|--------|--------|------|------| -| 400 | 1000 | Bad Request | 请求参数错误或智能体名称为空 | -| 500 | 1007 | Internal Server Error | 服务器内部错误 | +| 状态码 | 错误码 | 错误 | 说明 | +| ------ | ------ | --------------------- | --------------------------------- | +| 400 | 1000 | Bad Request | 请求参数错误或智能体名称为空 | +| 500 | 1007 | Internal Server Error | 服务器内部错误 | --- ## GET `/agents` - 获取智能体列表 -获取当前租户的所有智能体,包括内置智能体和自定义智能体。 +获取当前租户的所有智能体,包括内置智能体和自定义智能体。响应中额外返回 `disabled_own_agent_ids`,指示当前租户在前端对话下拉框中主动隐藏的本租户自有智能体 ID 列表(不影响其他租户)。 **请求**: ```curl curl --location 'http://localhost:8080/api/v1/agents' \ ---header 'X-API-Key: your_api_key' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -134,78 +136,54 @@ curl --location 'http://localhost:8080/api/v1/agents' \ "created_by": "", "config": { "agent_mode": "quick-answer", - "system_prompt": "你是一个专业的智能信息检索助手,名为WeKnora。你犹如专业的高级秘书,依据检索到的信息回答用户问题,不能利用任何先验知识。\n当用户提出问题时,助手会基于特定的信息进行解答。助手首先在心中思考推理过程,然后向用户提供答案。\n", - "context_template": "...", - "model_id": "...", - "rerank_model_id": "", "temperature": 0.3, "max_completion_tokens": 2048, - "max_iterations": 10, - "allowed_tools": [], - "reflection_enabled": false, - "mcp_selection_mode": "", - "mcp_services": null, "kb_selection_mode": "all", - "knowledge_bases": [], - "supported_file_types": null, - "faq_priority_enabled": false, - "faq_direct_answer_threshold": 0, - "faq_score_boost": 0, "web_search_enabled": false, - "web_search_max_results": 5, "multi_turn_enabled": true, - "history_turns": 5, - "embedding_top_k": 10, - "keyword_threshold": 0.3, - "vector_threshold": 0.5, - "rerank_top_k": 5, - "rerank_threshold": 0.5, - "enable_query_expansion": true, - "enable_rewrite": true, - "rewrite_prompt_system": "...", - "rewrite_prompt_user": "...", - "fallback_strategy": "fixed", - "fallback_response": "...", - "fallback_prompt": "..." + "history_turns": 5 }, "created_at": "2025-12-29T20:06:01.696308+08:00", "updated_at": "2025-12-29T20:06:01.696308+08:00", "deleted_at": null }, - { - "id": "builtin-smart-reasoning", - "name": "智能推理", - "description": "ReAct 推理框架,支持多步思考和工具调用", - "is_builtin": true, - "config": { - "agent_mode": "smart-reasoning" - - } - }, { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "我的智能体", - "description": "自定义智能体描述", "is_builtin": false, "config": { "agent_mode": "smart-reasoning" } } - ] + ], + "disabled_own_agent_ids": [] } ``` +**错误响应**: + +| 状态码 | 错误码 | 错误 | 说明 | +| ------ | ------ | --------------------- | ------------------ | +| 401 | 1001 | Unauthorized | 缺少租户上下文 | +| 500 | 1007 | Internal Server Error | 服务器内部错误 | + --- ## GET `/agents/:id` - 获取智能体详情 根据 ID 获取智能体的详细信息。 +**路径参数**: + +| 参数 | 类型 | 说明 | +| ---- | ------ | -------- | +| `id` | string | 智能体 ID | + **请求**: ```curl curl --location 'http://localhost:8080/api/v1/agents/builtin-quick-answer' \ ---header 'X-API-Key: your_api_key' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -238,23 +216,38 @@ curl --location 'http://localhost:8080/api/v1/agents/builtin-quick-answer' \ **错误响应**: -| 状态码 | 错误码 | 错误 | 说明 | -|--------|--------|------|------| -| 400 | 1000 | Bad Request | 智能体 ID 为空 | -| 404 | 1003 | Not Found | 智能体不存在 | -| 500 | 1007 | Internal Server Error | 服务器内部错误 | +| 状态码 | 错误码 | 错误 | 说明 | +| ------ | ------ | --------------------- | ------------------ | +| 400 | 1000 | Bad Request | 智能体 ID 为空 | +| 404 | 1003 | Not Found | 智能体不存在 | +| 500 | 1007 | Internal Server Error | 服务器内部错误 | --- ## PUT `/agents/:id` - 更新智能体 -更新智能体的名称、描述和配置。内置智能体不可修改。 +更新智能体的名称、描述、头像和配置。内置智能体不可修改。 + +**路径参数**: + +| 参数 | 类型 | 说明 | +| ---- | ------ | -------- | +| `id` | string | 智能体 ID | + +**请求体参数**: + +| 参数 | 类型 | 必填 | 说明 | +| ------------- | ------ | ---- | ------------ | +| `name` | string | 否 | 智能体名称 | +| `description` | string | 否 | 智能体描述 | +| `avatar` | string | 否 | 智能体头像 | +| `config` | object | 否 | 智能体配置 | **请求**: ```curl curl --location --request PUT 'http://localhost:8080/api/v1/agents/550e8400-e29b-41d4-a716-446655440000' \ ---header 'X-API-Key: your_api_key' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "name": "更新后的智能体", @@ -267,15 +260,6 @@ curl --location --request PUT 'http://localhost:8080/api/v1/agents/550e8400-e29b }' ``` -**请求参数**: - -| 参数 | 类型 | 必填 | 说明 | -|------|------|------|------| -| `name` | string | 否 | 智能体名称 | -| `description` | string | 否 | 智能体描述 | -| `avatar` | string | 否 | 智能体头像 | -| `config` | object | 否 | 智能体配置 | - **响应**: ```json @@ -297,12 +281,12 @@ curl --location --request PUT 'http://localhost:8080/api/v1/agents/550e8400-e29b **错误响应**: -| 状态码 | 错误码 | 错误 | 说明 | -|--------|--------|------|------| -| 400 | 1000 | Bad Request | 请求参数错误或智能体名称为空 | -| 403 | 1002 | Forbidden | 无法修改内置智能体的基本信息 | -| 404 | 1003 | Not Found | 智能体不存在 | -| 500 | 1007 | Internal Server Error | 服务器内部错误 | +| 状态码 | 错误码 | 错误 | 说明 | +| ------ | ------ | --------------------- | --------------------------------- | +| 400 | 1000 | Bad Request | 请求参数错误或智能体名称为空 | +| 403 | 1002 | Forbidden | 无法修改内置智能体 | +| 404 | 1003 | Not Found | 智能体不存在 | +| 500 | 1007 | Internal Server Error | 服务器内部错误 | --- @@ -310,11 +294,17 @@ curl --location --request PUT 'http://localhost:8080/api/v1/agents/550e8400-e29b 删除指定的自定义智能体。内置智能体不可删除。 +**路径参数**: + +| 参数 | 类型 | 说明 | +| ---- | ------ | -------- | +| `id` | string | 智能体 ID | + **请求**: ```curl curl --location --request DELETE 'http://localhost:8080/api/v1/agents/550e8400-e29b-41d4-a716-446655440000' \ ---header 'X-API-Key: your_api_key' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -328,24 +318,30 @@ curl --location --request DELETE 'http://localhost:8080/api/v1/agents/550e8400-e **错误响应**: -| 状态码 | 错误码 | 错误 | 说明 | -|--------|--------|------|------| -| 400 | 1000 | Bad Request | 智能体 ID 为空 | -| 403 | 1002 | Forbidden | 无法删除内置智能体 | -| 404 | 1003 | Not Found | 智能体不存在 | -| 500 | 1007 | Internal Server Error | 服务器内部错误 | +| 状态码 | 错误码 | 错误 | 说明 | +| ------ | ------ | --------------------- | --------------------- | +| 400 | 1000 | Bad Request | 智能体 ID 为空 | +| 403 | 1002 | Forbidden | 无法删除内置智能体 | +| 404 | 1003 | Not Found | 智能体不存在 | +| 500 | 1007 | Internal Server Error | 服务器内部错误 | --- ## POST `/agents/:id/copy` - 复制智能体 -复制指定的智能体,创建一个新的副本。支持复制内置智能体。 +复制指定的智能体,创建一个新的副本,副本始终为自定义智能体。支持复制内置智能体。成功返回 HTTP 201。 + +**路径参数**: + +| 参数 | 类型 | 说明 | +| ---- | ------ | -------------- | +| `id` | string | 源智能体 ID | **请求**: ```curl curl --location --request POST 'http://localhost:8080/api/v1/agents/builtin-smart-reasoning/copy' \ ---header 'X-API-Key: your_api_key' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -370,11 +366,11 @@ curl --location --request POST 'http://localhost:8080/api/v1/agents/builtin-smar **错误响应**: -| 状态码 | 错误码 | 错误 | 说明 | -|--------|--------|------|------| -| 400 | 1000 | Bad Request | 智能体 ID 为空 | -| 404 | 1003 | Not Found | 智能体不存在 | -| 500 | 1007 | Internal Server Error | 服务器内部错误 | +| 状态码 | 错误码 | 错误 | 说明 | +| ------ | ------ | --------------------- | ------------------ | +| 400 | 1000 | Bad Request | 智能体 ID 为空 | +| 404 | 1003 | Not Found | 智能体不存在 | +| 500 | 1007 | Internal Server Error | 服务器内部错误 | --- @@ -523,7 +519,7 @@ curl --location 'http://localhost:8080/api/v1/agents/placeholders' \ ```curl curl --location 'http://localhost:8080/api/v1/agent-chat/session-123' \ ---header 'X-API-Key: your_api_key' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "query": "帮我分析一下这份数据", @@ -531,3 +527,9 @@ curl --location 'http://localhost:8080/api/v1/agent-chat/session-123' \ "agent_id": "builtin-data-analyst" }' ``` + +## 相关文档 + +- 智能体的组织共享、跨租户分发与禁用(`/agents/:id/shares`、`/shared-agents` 等):见 [组织管理 API](./organization.md) +- 智能体绑定 IM 渠道(`/agents/:id/im-channels`):见组织/IM 渠道相关文档 +- 网络搜索提供者配置(被 `web_search_provider_id` 引用):见 [Web Search API](./web-search.md) diff --git a/docs/api/chunk.md b/docs/api/chunk.md index fd4fb06f0..460d35f1f 100644 --- a/docs/api/chunk.md +++ b/docs/api/chunk.md @@ -2,22 +2,35 @@ [返回目录](./README.md) -| 方法 | 路径 | 描述 | -| ------ | --------------------------- | ------------------------ | -| GET | `/chunks/:knowledge_id` | 获取知识的分块列表 | -| PUT | `/chunks/:knowledge_id/:id` | 更新分块 | -| DELETE | `/chunks/:knowledge_id/:id` | 删除分块 | -| DELETE | `/chunks/:knowledge_id` | 删除知识下的所有分块 | -| GET | `/chunks/get-by-id/:id` | 根据ID直接获取分块 | -| DELETE | `/chunks/:id/delete-question` | 删除分块的生成问题 | +| 方法 | 路径 | 描述 | +| ------ | --------------------------------- | -------------------------- | +| GET | `/chunks/:knowledge_id` | 获取知识的分块列表 | +| PUT | `/chunks/:knowledge_id/:id` | 更新分块 | +| DELETE | `/chunks/:knowledge_id/:id` | 删除单个分块 | +| DELETE | `/chunks/:knowledge_id` | 删除知识下的所有分块 | +| GET | `/chunks/by-id/:id` | 根据分块 ID 直接获取分块 | +| DELETE | `/chunks/by-id/:id/questions` | 删除分块下的某个生成问题 | -## GET `/chunks/:knowledge_id?page=&page_size=` - 获取知识的分块列表 +## GET `/chunks/:knowledge_id` - 获取知识的分块列表 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ------------- | ------ | ----------- | +| knowledge_id | string | 知识 ID | + +**查询参数**: + +| 字段 | 类型 | 默认 | 说明 | +| ---------- | ---- | ---- | ---------- | +| page | int | 1 | 页码 | +| page_size | int | 20 | 每页条数 | **请求**: ```curl curl --location 'http://localhost:8080/api/v1/chunks/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5?page=1&page_size=1' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' ``` @@ -61,21 +74,31 @@ curl --location 'http://localhost:8080/api/v1/chunks/4c4e7c1a-09cf-485b-a7b5-24b ## PUT `/chunks/:knowledge_id/:id` - 更新分块 -更新指定分块的内容和属性。 +更新指定分块的内容和属性。所有字段均可选,未传则保留原值。 -**请求参数**: -- `content`: 分块内容(可选) -- `chunk_index`: 分块索引(可选) -- `is_enabled`: 是否启用(可选) -- `start_at`: 起始位置(可选) -- `end_at`: 结束位置(可选) -- `image_info`: 图片信息(可选) +**路径参数**: + +| 字段 | 类型 | 说明 | +| ------------- | ------ | ----------- | +| knowledge_id | string | 知识 ID | +| id | string | 分块 ID | + +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ------------ | ------- | ---- | --------------------- | +| content | string | 否 | 分块内容 | +| chunk_index | int | 否 | 分块在知识中的序号 | +| is_enabled | boolean | 否 | 是否启用 | +| start_at | int | 否 | 起始位置(字符偏移) | +| end_at | int | 否 | 结束位置(字符偏移) | +| image_info | string | 否 | 图像分块的元信息(JSON 字符串) | **请求**: ```curl curl --location --request PUT 'http://localhost:8080/api/v1/chunks/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "content": "更新后的分块内容", @@ -89,41 +112,23 @@ curl --location --request PUT 'http://localhost:8080/api/v1/chunks/4c4e7c1a-09cf { "data": { "id": "df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7", - "tenant_id": 1, - "knowledge_id": "4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5", - "knowledge_base_id": "kb-00000001", - "tag_id": "", "content": "更新后的分块内容", - "chunk_index": 0, "is_enabled": true, - "status": 2, - "start_at": 0, - "end_at": 964, - "pre_chunk_id": "", - "next_chunk_id": "", - "chunk_type": "text", - "parent_chunk_id": "", - "relation_chunks": null, - "indirect_relation_chunks": null, - "metadata": null, - "content_hash": "", - "image_info": "", - "created_at": "2025-08-12T11:52:36.168632+08:00", - "updated_at": "2025-08-12T12:00:00.000000+08:00", - "deleted_at": null + "...": "其他字段同 GET 响应" }, "success": true } ``` -## DELETE `/chunks/:knowledge_id/:id` - 删除分块 +## DELETE `/chunks/:knowledge_id/:id` - 删除单个分块 + +**路径参数**: 同 PUT。 **请求**: ```curl curl --location --request DELETE 'http://localhost:8080/api/v1/chunks/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -137,12 +142,17 @@ curl --location --request DELETE 'http://localhost:8080/api/v1/chunks/4c4e7c1a-0 ## DELETE `/chunks/:knowledge_id` - 删除知识下的所有分块 +**路径参数**: + +| 字段 | 类型 | 说明 | +| ------------- | ------ | ----------- | +| knowledge_id | string | 知识 ID | + **请求**: ```curl curl --location --request DELETE 'http://localhost:8080/api/v1/chunks/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -154,60 +164,46 @@ curl --location --request DELETE 'http://localhost:8080/api/v1/chunks/4c4e7c1a-0 } ``` -## GET `/chunks/get-by-id/:id` - 根据ID直接获取分块 +## GET `/chunks/by-id/:id` - 根据 ID 直接获取分块 -根据分块ID直接获取分块信息,无需提供知识ID。 +无需提供 `knowledge_id` 即可获取分块。常用于跨知识库的引用展示。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | ------- | +| id | string | 分块 ID | **请求**: ```curl -curl --location 'http://localhost:8080/api/v1/chunks/get-by-id/df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' +curl --location 'http://localhost:8080/api/v1/chunks/by-id/df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7' \ +--header 'X-API-Key: sk-xxxxx' ``` -**响应**: +**响应**: 同 `GET /chunks/:knowledge_id` 列表中的单条 data。 -```json -{ - "data": { - "id": "df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7", - "tenant_id": 1, - "knowledge_id": "4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5", - "knowledge_base_id": "kb-00000001", - "tag_id": "", - "content": "彗星xxxx", - "chunk_index": 0, - "is_enabled": true, - "status": 2, - "start_at": 0, - "end_at": 964, - "pre_chunk_id": "", - "next_chunk_id": "", - "chunk_type": "text", - "parent_chunk_id": "", - "relation_chunks": null, - "indirect_relation_chunks": null, - "metadata": null, - "content_hash": "", - "image_info": "", - "created_at": "2025-08-12T11:52:36.168632+08:00", - "updated_at": "2025-08-12T11:52:53.376871+08:00", - "deleted_at": null - }, - "success": true -} -``` +## DELETE `/chunks/by-id/:id/questions` - 删除分块下的某个生成问题 -## DELETE `/chunks/:id/delete-question` - 删除分块的生成问题 +删除指定分块关联的某条生成问题。 -删除指定分块关联的生成问题。 +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | ------- | +| id | string | 分块 ID | + +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------- | ------ | ---- | ----------- | +| question_id | string | 是 | 问题 ID | **请求**: ```curl -curl --location --request DELETE 'http://localhost:8080/api/v1/chunks/df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7/delete-question' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +curl --location --request DELETE 'http://localhost:8080/api/v1/chunks/by-id/df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7/questions' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "question_id": "q-00000001" diff --git a/docs/api/evaluation.md b/docs/api/evaluation.md index b111262bd..c81aae79c 100644 --- a/docs/api/evaluation.md +++ b/docs/api/evaluation.md @@ -2,22 +2,26 @@ [返回目录](./README.md) -| 方法 | 路径 | 描述 | -| ---- | ------------- | --------------------- | -| GET | `/evaluation` | 获取评估任务 | -| POST | `/evaluation` | 创建评估任务 | +| 方法 | 路径 | 描述 | +| ---- | -------------- | --------------------- | +| GET | `/evaluation/` | 获取评估任务结果 | +| POST | `/evaluation/` | 创建评估任务 | -## GET `/evaluation` - 获取评估任务 +> 注:服务端路由带尾斜杠(Gin 会自动从 `/evaluation` 重定向到 `/evaluation/`),下方示例为方便阅读用了 `/evaluation`。 -**请求参数**: -- `task_id`: 从 `POST /evaluation` 接口中获取到的任务 ID -- `X-API-Key`: 用户 API Key +## GET `/evaluation` - 获取评估任务结果 + +**参数说明(查询参数)**: + +| 字段 | 类型 | 必填 | 说明 | +| -------- | ------ | ---- | --------------------------------------------------- | +| task_id | string | 是 | 从 `POST /evaluation` 返回的任务 ID | **请求**: ```bash curl --location 'http://localhost:8080/api/v1/evaluation?task_id=c34563ad-b09f-4858-b72e-e92beb80becb' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' ``` @@ -88,17 +92,20 @@ curl --location 'http://localhost:8080/api/v1/evaluation?task_id=c34563ad-b09f-4 ## POST `/evaluation` - 创建评估任务 -**请求参数**: -- `dataset_id`: 评估使用的数据集,暂时只支持官方测试数据集 `default` -- `knowledge_base_id`: 评估使用的知识库 -- `chat_id`: 评估使用的对话模型 -- `rerank_id`: 评估使用的重排序模型 +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------------- | ------ | ---- | ----------------------------------------------- | +| dataset_id | string | 是 | 评估数据集,目前仅支持 `default`(官方测试集) | +| knowledge_base_id | string | 是 | 评估使用的知识库 ID | +| chat_id | string | 是 | 评估使用的对话模型 ID | +| rerank_id | string | 是 | 评估使用的重排序模型 ID | **请求**: ```bash curl --location 'http://localhost:8080/api/v1/evaluation' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "dataset_id": "default", diff --git a/docs/api/faq.md b/docs/api/faq.md index 274b2cc04..94b4db867 100644 --- a/docs/api/faq.md +++ b/docs/api/faq.md @@ -2,55 +2,54 @@ [返回目录](./README.md) -| 方法 | 路径 | 描述 | -| ------ | ------------------------------------------- | ------------------------ | -| GET | `/knowledge-bases/:id/faq/entries` | 获取FAQ条目列表 | -| POST | `/knowledge-bases/:id/faq/entries` | 批量导入FAQ条目 | -| POST | `/knowledge-bases/:id/faq/entry` | 创建单个FAQ条目 | -| GET | `/knowledge-bases/:id/faq/entries/:entry_id`| 获取单个FAQ条目 | -| PUT | `/knowledge-bases/:id/faq/entries/:entry_id`| 更新单个FAQ条目 | -| POST | `/knowledge-bases/:id/faq/entries/:entry_id/similar-questions` | 添加相似问题 | -| PUT | `/knowledge-bases/:id/faq/entries/fields` | 批量更新FAQ字段 | -| PUT | `/knowledge-bases/:id/faq/entries/tags` | 批量更新FAQ标签 | -| DELETE | `/knowledge-bases/:id/faq/entries` | 批量删除FAQ条目 | -| POST | `/knowledge-bases/:id/faq/search` | 混合搜索FAQ | -| GET | `/knowledge-bases/:id/faq/entries/export` | 导出FAQ条目(CSV) | -| GET | `/faq/import/progress/:task_id` | 获取FAQ导入进度 | -| PUT | `/knowledge-bases/:id/faq/import/last-result/display` | 更新导入结果显示状态 | +FAQ 接口分为两组: -## GET `/knowledge-bases/:id/faq/entries` - 获取FAQ条目列表 +- `/knowledge-bases/:id/faq/*`:知识库范围内的 FAQ 条目 CRUD、批量操作、搜索与导入导出。 +- `/faq/import/progress/:task_id`:**不属于知识库分组**,用于查询异步导入/dry-run 任务的进度,仅需任务 ID 即可调用。 + +| 方法 | 路径 | 描述 | +| ------ | ----------------------------------------------------------------- | --------------------------------- | +| GET | `/knowledge-bases/:id/faq/entries` | 获取 FAQ 条目列表 | +| GET | `/knowledge-bases/:id/faq/entries/export` | 导出 FAQ 条目(CSV) | +| GET | `/knowledge-bases/:id/faq/entries/:entry_id` | 获取单个 FAQ 条目(按 seq_id) | +| POST | `/knowledge-bases/:id/faq/entries` | 批量 Upsert FAQ 条目(异步) | +| POST | `/knowledge-bases/:id/faq/entry` | 同步创建单个 FAQ 条目 | +| PUT | `/knowledge-bases/:id/faq/entries/:entry_id` | 更新单个 FAQ 条目 | +| POST | `/knowledge-bases/:id/faq/entries/:entry_id/similar-questions` | 为 FAQ 条目追加相似问 | +| PUT | `/knowledge-bases/:id/faq/entries/fields` | 批量更新字段(启用/推荐/标签) | +| PUT | `/knowledge-bases/:id/faq/entries/tags` | 批量更新标签 | +| DELETE | `/knowledge-bases/:id/faq/entries` | 批量删除 FAQ 条目 | +| POST | `/knowledge-bases/:id/faq/search` | FAQ 混合搜索 | +| PUT | `/knowledge-bases/:id/faq/import/last-result/display` | 更新上次导入结果卡片显示状态 | +| GET | `/faq/import/progress/:task_id` | 查询 FAQ 导入任务进度(公共) | + +> **路径参数说明**:`:entry_id` 始终是 FAQ 条目的 `seq_id`(整数),不是字符串形式的 ID。同理,批量接口中的 `by_id` / `by_tag` / `exclude_ids` / `ids` 字段均为 `seq_id` 列表(整数)。 + +## GET `/knowledge-bases/:id/faq/entries` - 获取 FAQ 条目列表 + +支持分页、按标签过滤、关键字搜索与排序。 **查询参数**: -- `page`: 页码(默认 1) -- `page_size`: 每页条数(默认 20) -- `tag_id`: 按标签ID筛选(可选) -- `keyword`: 关键字搜索(可选) -- `search_field`: 搜索字段(可选),可选值: - - `standard_question`: 只搜索标准问题 - - `similar_questions`: 只搜索相似问法 - - `answers`: 只搜索答案 - - 留空或不传:搜索全部字段 -- `sort_order`: 排序方式(可选),`asc` 表示按更新时间正序,默认按更新时间倒序 + +| 参数 | 类型 | 必填 | 说明 | +| ------------ | ------ | ---- | --------------------------------------------------------------------------------------------- | +| page | int | 否 | 页码,默认 1 | +| page_size | int | 否 | 每页数量,默认 20 | +| tag_id | int | 否 | 按标签 `seq_id` 过滤 | +| keyword | string | 否 | 关键字搜索 | +| search_field | string | 否 | 搜索字段:`standard_question` / `similar_questions` / `answers`,留空则全字段搜索 | +| sort_order | string | 否 | 排序方式,`asc` 表示按更新时间正序,默认按更新时间倒序 | **请求**: ```curl -# 搜索全部字段 +# 全字段搜索 curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries?page=1&page_size=10&keyword=密码' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' -# 只搜索标准问题 +# 仅搜索标准问 curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries?keyword=密码&search_field=standard_question' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' - -# 只搜索相似问法 -curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries?keyword=忘记&search_field=similar_questions' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' - -# 只搜索答案 -curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries?keyword=点击&search_field=answers' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -63,16 +62,19 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/en "page_size": 10, "data": [ { - "id": "faq-00000001", + "id": 1, "chunk_id": "chunk-00000001", "knowledge_id": "knowledge-00000001", "knowledge_base_id": "kb-00000001", - "tag_id": "tag-00000001", + "tag_id": 12, + "tag_name": "账户", "is_enabled": true, + "is_recommended": false, "standard_question": "如何重置密码?", "similar_questions": ["忘记密码怎么办", "密码找回"], "negative_questions": ["如何修改用户名"], "answers": ["您可以通过点击登录页面的'忘记密码'链接来重置密码。"], + "answer_strategy": "all", "index_mode": "hybrid", "chunk_type": "faq", "created_at": "2025-08-12T10:00:00+08:00", @@ -84,18 +86,94 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/en } ``` -## POST `/knowledge-bases/:id/faq/entries` - 批量导入FAQ条目 +## GET `/knowledge-bases/:id/faq/entries/export` - 导出 FAQ 条目 -**请求参数**: -- `mode`: 导入模式,`append`(追加)或 `replace`(替换) -- `entries`: FAQ条目数组 -- `knowledge_id`: 关联的知识ID(可选) +将知识库下的所有 FAQ 条目导出为 CSV(UTF-8 带 BOM,Excel 兼容)。 + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries/export' \ +--header 'X-API-Key: sk-xxxxx' \ +--output faq_export.csv +``` + +**响应**: `Content-Type: text/csv; charset=utf-8`,附带文件名 `faq_export.csv`。 + +## GET `/knowledge-bases/:id/faq/entries/:entry_id` - 获取单个 FAQ 条目 + +根据 `seq_id`(整数)获取单个 FAQ 条目详情。 + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries/1' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: + +```json +{ + "data": { + "id": 1, + "chunk_id": "chunk-00000001", + "knowledge_id": "knowledge-00000001", + "knowledge_base_id": "kb-00000001", + "tag_id": 12, + "tag_name": "账户", + "is_enabled": true, + "is_recommended": false, + "standard_question": "如何重置密码?", + "similar_questions": ["忘记密码怎么办", "密码找回"], + "negative_questions": [], + "answers": ["您可以通过点击登录页面的'忘记密码'链接来重置密码。"], + "answer_strategy": "all", + "index_mode": "hybrid", + "chunk_type": "faq", + "created_at": "2025-08-12T10:00:00+08:00", + "updated_at": "2025-08-12T10:00:00+08:00" + }, + "success": true +} +``` + +## POST `/knowledge-bases/:id/faq/entries` - 批量 Upsert FAQ 条目(异步) + +**异步**批量导入或更新 FAQ 条目。接口立即返回 `task_id`,调用方需通过 `GET /faq/import/progress/:task_id` 查询进度与结果。 + +支持 `dry_run=true`:异步执行仅校验(格式 / 批内重复 / 与库内重复 / 内容安全),不实际写入。 + +**请求体(`types.FAQBatchUpsertPayload`)**: + +| 字段 | 类型 | 必填 | 说明 | +| ------------ | -------------------------- | ---- | ----------------------------------------------------------------------------------- | +| entries | `[]FAQEntryPayload` | 是 | FAQ 条目数组 | +| mode | string | 是 | `append` 或 `replace`(替换会清空已有条目) | +| knowledge_id | string | 否 | 关联的 FAQ Knowledge ID(不传则使用知识库默认 FAQ knowledge) | +| task_id | string | 否 | 任务 ID,不传则自动生成 UUID | +| dry_run | boolean | 否 | 仅验证不导入 | + +`FAQEntryPayload` 字段: + +| 字段 | 类型 | 必填 | 说明 | +| ------------------- | --------- | ---- | ------------------------------------------------------------- | +| id | int64 | 否 | 指定 `seq_id`(数据迁移场景,需小于自增起始值 100000000) | +| standard_question | string | 是 | 标准问 | +| similar_questions | string[] | 否 | 相似问列表 | +| negative_questions | string[] | 否 | 反例问题列表 | +| answers | string[] | 否 | 答案列表 | +| answer_strategy | string | 否 | 答案返回策略:`all` 或 `random` | +| tag_id | int64 | 否 | 标签 `seq_id` | +| tag_name | string | 否 | 标签名(用于按名匹配标签) | +| is_enabled | boolean | 否 | 是否启用 | +| is_recommended | boolean | 否 | 是否推荐 | **请求**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "mode": "append", @@ -104,7 +182,7 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/en "standard_question": "如何联系客服?", "similar_questions": ["客服电话", "在线客服"], "answers": ["您可以通过拨打400-xxx-xxxx联系我们的客服。"], - "tag_id": "tag-00000001" + "tag_id": 1 }, { "standard_question": "退款政策是什么?", @@ -118,38 +196,30 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/en ```json { - "data": { - "task_id": "task-00000001" - }, + "data": { "task_id": "task-00000001" }, "success": true } ``` -注:批量导入为异步操作,返回任务ID用于追踪进度。 +> 用 `GET /faq/import/progress/:task_id` 查询任务最终状态。 -## POST `/knowledge-bases/:id/faq/entry` - 创建单个FAQ条目 +## POST `/knowledge-bases/:id/faq/entry` - 同步创建单个 FAQ 条目 -同步创建单个FAQ条目,适用于单条录入场景。会自动检查标准问和相似问是否与已有FAQ重复。 +**同步**创建单条 FAQ 条目,会即时校验标准问/相似问与库内已有条目的重复。 -**请求参数**: -- `standard_question`: 标准问(必填) -- `similar_questions`: 相似问数组(可选) -- `negative_questions`: 反例问题数组(可选) -- `answers`: 答案数组(必填) -- `tag_id`: 标签ID(可选) -- `is_enabled`: 是否启用(可选,默认true) +**请求体**: 同上 `FAQEntryPayload`(`standard_question` 必填)。 **请求**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entry' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "standard_question": "如何联系客服?", "similar_questions": ["客服电话", "在线客服"], "answers": ["您可以通过拨打400-xxx-xxxx联系我们的客服。"], - "tag_id": "tag-00000001", + "tag_id": 1, "is_enabled": true }' ``` @@ -159,16 +229,19 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/en ```json { "data": { - "id": "faq-00000001", + "id": 1, "chunk_id": "chunk-00000001", "knowledge_id": "knowledge-00000001", "knowledge_base_id": "kb-00000001", - "tag_id": "tag-00000001", + "tag_id": 1, + "tag_name": "客服", "is_enabled": true, + "is_recommended": false, "standard_question": "如何联系客服?", "similar_questions": ["客服电话", "在线客服"], "negative_questions": [], "answers": ["您可以通过拨打400-xxx-xxxx联系我们的客服。"], + "answer_strategy": "all", "index_mode": "hybrid", "chunk_type": "faq", "created_at": "2025-08-12T10:00:00+08:00", @@ -190,13 +263,15 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/en } ``` -## PUT `/knowledge-bases/:id/faq/entries/:entry_id` - 更新单个FAQ条目 +## PUT `/knowledge-bases/:id/faq/entries/:entry_id` - 更新单个 FAQ 条目 + +按 `seq_id` 更新单条 FAQ 条目,请求体同 `FAQEntryPayload`。 **请求**: ```curl -curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries/faq-00000001' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries/1' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "standard_question": "如何重置账户密码?", @@ -206,107 +281,61 @@ curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-0 }' ``` -**响应**: +**响应**: 返回更新后的 FAQ 条目,结构同创建接口。 -```json -{ - "success": true -} -``` +## POST `/knowledge-bases/:id/faq/entries/:entry_id/similar-questions` - 追加相似问 -## GET `/knowledge-bases/:id/faq/entries/:entry_id` - 获取单个FAQ条目 +向指定 FAQ 条目(`seq_id`)追加相似问。 -根据 seq_id 获取单个 FAQ 条目的详细信息。 +**请求体**: -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries/1' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' -``` - -**响应**: - -```json -{ - "data": { - "id": "faq-00000001", - "seq_id": 1, - "chunk_id": "chunk-00000001", - "knowledge_id": "knowledge-00000001", - "knowledge_base_id": "kb-00000001", - "tag_id": "tag-00000001", - "is_enabled": true, - "standard_question": "如何重置密码?", - "similar_questions": ["忘记密码怎么办", "密码找回"], - "negative_questions": [], - "answers": ["您可以通过点击登录页面的'忘记密码'链接来重置密码。"], - "index_mode": "hybrid", - "chunk_type": "faq", - "created_at": "2025-08-12T10:00:00+08:00", - "updated_at": "2025-08-12T10:00:00+08:00" - }, - "success": true -} -``` - -## POST `/knowledge-bases/:id/faq/entries/:entry_id/similar-questions` - 添加相似问题 - -为指定的 FAQ 条目追加相似问法。 - -**请求参数**: -- `similar_questions`: 要追加的相似问题数组(必填) +| 字段 | 类型 | 必填 | 说明 | +| ----------------- | -------- | ---- | --------------------- | +| similar_questions | string[] | 是 | 要追加的相似问数组 | **请求**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries/1/similar-questions' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "similar_questions": ["怎样修改密码", "密码重置方法"] }' ``` -**响应**: +**响应**: 返回追加后的完整 FAQ 条目。 -```json -{ - "data": { - "id": "faq-00000001", - "seq_id": 1, - "standard_question": "如何重置密码?", - "similar_questions": ["忘记密码怎么办", "密码找回", "怎样修改密码", "密码重置方法"], - "answers": ["您可以通过点击登录页面的'忘记密码'链接来重置密码。"], - "is_enabled": true, - "chunk_type": "faq", - "created_at": "2025-08-12T10:00:00+08:00", - "updated_at": "2025-08-12T11:00:00+08:00" - }, - "success": true -} -``` +## PUT `/knowledge-bases/:id/faq/entries/fields` - 批量更新字段 -## PUT `/knowledge-bases/:id/faq/entries/fields` - 批量更新FAQ字段 +**统一**的批量字段更新接口,支持同时更新 `is_enabled` / `is_recommended` / `tag_id`,并支持两种作用域: -支持按条目ID或按标签ID批量更新 FAQ 条目的多个字段(启用状态、推荐状态、标签等)。 +- **按条目 ID** (`by_id`):键为条目 `seq_id`,值为该条目要更新的字段。 +- **按标签 ID** (`by_tag`):键为标签 `seq_id`,对该标签下的所有条目应用相同的字段更新;可配合 `exclude_ids` 排除部分条目。 -**请求参数**: -- `by_id`: 按条目 seq_id 更新(可选),键为 seq_id,值为要更新的字段 -- `by_tag`: 按标签 seq_id 更新(可选),键为 tag_seq_id,值为要更新的字段 -- `exclude_ids`: 排除的条目 seq_id 列表(与 by_tag 配合使用,可选) +`by_id` 和 `by_tag` 至少传一项;二者可同时使用。 -每个更新对象支持的字段: -- `is_enabled`: 是否启用(可选) -- `is_recommended`: 是否推荐(可选) -- `tag_id`: 标签ID(可选) +**请求体(`types.FAQEntryFieldsBatchUpdate`)**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------- | --------------------------------- | ---- | ------------------------------------------ | +| by_id | `map[int64]FAQEntryFieldsUpdate` | 否 | 按条目 `seq_id` 更新 | +| by_tag | `map[int64]FAQEntryFieldsUpdate` | 否 | 按标签 `seq_id` 对该标签下所有条目更新 | +| exclude_ids | `int64[]` | 否 | 与 `by_tag` 配合使用,排除指定条目 `seq_id` | + +`FAQEntryFieldsUpdate` 字段(全部可选,仅传入的字段会被更新): + +| 字段 | 类型 | 说明 | +| -------------- | ------- | -------------- | +| is_enabled | boolean | 是否启用 | +| is_recommended | boolean | 是否推荐 | +| tag_id | int64 | 标签 `seq_id` | **请求**: ```curl curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries/fields' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "by_id": { @@ -314,7 +343,7 @@ curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-0 "2": {"is_enabled": false} }, "by_tag": { - "100": {"is_enabled": true} + "100": {"is_recommended": true} }, "exclude_ids": [3, 4] }' @@ -323,76 +352,92 @@ curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-0 **响应**: ```json -{ - "success": true -} +{ "success": true } ``` -## PUT `/knowledge-bases/:id/faq/entries/tags` - 批量更新FAQ标签 +## PUT `/knowledge-bases/:id/faq/entries/tags` - 批量更新标签 + +仅更新标签关联。键为条目 `seq_id`,值为目标标签 `seq_id`;值传 `null` 表示清除标签。 + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ------- | --------------------- | ---- | -------------------------------------------- | +| updates | `map[int64]int64?` | 是 | 键:条目 `seq_id`;值:标签 `seq_id` 或 `null` | **请求**: ```curl curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries/tags' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "updates": { - "faq-00000001": "tag-00000001", - "faq-00000002": "tag-00000002", - "faq-00000003": null + "1": 10, + "2": 11, + "3": null } }' ``` -注:设置为 `null` 可清除标签关联。 - **响应**: ```json -{ - "success": true -} +{ "success": true } ``` -## DELETE `/knowledge-bases/:id/faq/entries` - 批量删除FAQ条目 +## DELETE `/knowledge-bases/:id/faq/entries` - 批量删除 + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ---- | --------- | ---- | --------------------------------- | +| ids | `int64[]` | 是 | 要删除的 FAQ 条目 `seq_id` 列表 | **请求**: ```curl curl --location --request DELETE 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ - "ids": ["faq-00000001", "faq-00000002"] + "ids": [1, 2, 3] }' ``` **响应**: ```json -{ - "success": true -} +{ "success": true } ``` -## POST `/knowledge-bases/:id/faq/search` - 混合搜索FAQ +## POST `/knowledge-bases/:id/faq/search` - FAQ 混合搜索 -**请求参数**: -- `query_text`: 搜索查询文本 -- `vector_threshold`: 向量相似度阈值(0-1) -- `match_count`: 返回结果数量(最大200) +向量 + 关键字混合检索,支持两级优先级标签召回。 + +**请求体(`types.FAQSearchRequest`)**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------------------- | --------- | ---- | ----------------------------------------------------------------------------------- | +| query_text | string | 是 | 搜索文本 | +| vector_threshold | float | 否 | 向量相似度阈值(0–1) | +| match_count | int | 否 | 返回数量,默认 10,最大 200 | +| first_priority_tag_ids | `int64[]` | 否 | 第一优先级标签 `seq_id` 列表(最高优先召回范围) | +| second_priority_tag_ids | `int64[]` | 否 | 第二优先级标签 `seq_id` 列表 | +| only_recommended | boolean | 否 | 是否仅返回 `is_recommended=true` 的条目 | **请求**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/search' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "query_text": "如何重置密码", "vector_threshold": 0.5, - "match_count": 10 + "match_count": 10, + "first_priority_tag_ids": [12], + "only_recommended": false }' ``` @@ -402,18 +447,22 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/se { "data": [ { - "id": "faq-00000001", + "id": 1, "chunk_id": "chunk-00000001", "knowledge_id": "knowledge-00000001", "knowledge_base_id": "kb-00000001", - "tag_id": "tag-00000001", + "tag_id": 12, + "tag_name": "账户", "is_enabled": true, + "is_recommended": false, "standard_question": "如何重置密码?", "similar_questions": ["忘记密码怎么办", "密码找回"], "answers": ["您可以通过点击登录页面的'忘记密码'链接来重置密码。"], + "answer_strategy": "all", "chunk_type": "faq", "score": 0.95, "match_type": "vector", + "matched_question": "忘记密码怎么办", "created_at": "2025-08-12T10:00:00+08:00", "updated_at": "2025-08-12T10:00:00+08:00" } @@ -422,81 +471,92 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/se } ``` -## GET `/knowledge-bases/:id/faq/entries/export` - 导出FAQ条目 +## PUT `/knowledge-bases/:id/faq/import/last-result/display` - 更新上次导入结果显示状态 -将知识库下的所有 FAQ 条目导出为 CSV 文件。 +控制上次导入完成后,前端结果卡片的显示/隐藏。 -**请求**: +**请求体**: -```curl -curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/entries/export' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---output faq_export.csv -``` - -**响应**: - -CSV 文件下载(Content-Type: text/csv) - -## GET `/faq/import/progress/:task_id` - 获取FAQ导入进度 - -查询异步 FAQ 导入任务的执行进度。任务 ID 由批量导入接口返回。 - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/faq/import/progress/task-00000001' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' -``` - -**响应**: - -```json -{ - "data": { - "task_id": "task-00000001", - "status": "completed", - "total": 100, - "success_count": 98, - "failed_count": 2, - "failed_entries": [ - { - "index": 5, - "standard_question": "重复的问题", - "error": "标准问与已有FAQ重复" - } - ], - "success_entries": [] - }, - "success": true -} -``` - -注:`status` 可能的值为 `pending`、`processing`、`completed`、`failed`。 - -## PUT `/knowledge-bases/:id/faq/import/last-result/display` - 更新导入结果显示状态 - -更新上一次 FAQ 导入结果的显示状态,用于控制前端是否展示导入结果提示。 - -**请求参数**: -- `display_status`: 显示状态(如 `"dismissed"`) +| 字段 | 类型 | 必填 | 说明 | +| -------------- | ------ | ---- | --------------------- | +| display_status | string | 是 | `open` 或 `close` | **请求**: ```curl curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/faq/import/last-result/display' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ - "display_status": "dismissed" + "display_status": "close" }' ``` **响应**: +```json +{ "success": true } +``` + +## GET `/faq/import/progress/:task_id` - 查询 FAQ 导入进度 + +> **注意**:此接口**不在** `/knowledge-bases/:id/faq` 分组下,路径直接以 `/faq/import/progress/:task_id` 开头。任务 ID 由 `POST /knowledge-bases/:id/faq/entries` 返回。 + +**路径参数**: + +| 参数 | 类型 | 说明 | +| ------- | ------ | -------------- | +| task_id | string | 导入任务的 ID | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/faq/import/progress/task-00000001' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**(节选关键字段): + ```json { + "data": { + "task_id": "task-00000001", + "kb_id": "kb-00000001", + "knowledge_id": "knowledge-00000001", + "status": "completed", + "progress": 100, + "total": 100, + "processed": 100, + "success_count": 95, + "failed_count": 3, + "partial_failed_count": 2, + "skipped_count": 0, + "failed_entries": [ + { + "index": 5, + "reason": "标准问与已有FAQ重复", + "standard_question": "重复的问题" + } + ], + "success_entries": [ + { "index": 0, "seq_id": 101, "standard_question": "如何联系客服?" } + ], + "message": "", + "error": "", + "created_at": 1736582400, + "updated_at": 1736582460, + "dry_run": false, + "import_mode": "append", + "imported_at": "2025-08-12T10:01:00+08:00", + "display_status": "open", + "processing_time": 60000 + }, "success": true } ``` + +`status` 可能取值:`pending` / `processing` / `completed` / `failed`。 + +当失败条目过多时,`failed_entries` 可能不直接返回,而通过 `failed_entries_url` 提供 CSV 下载地址。 + +`dry_run=true` 模式下的任务同样通过此接口查询,`success_entries` 中的 `seq_id` 不会真正写入。 diff --git a/docs/api/knowledge-base.md b/docs/api/knowledge-base.md index ac901a0fc..20dd2e622 100644 --- a/docs/api/knowledge-base.md +++ b/docs/api/knowledge-base.md @@ -7,29 +7,49 @@ - 知识库类型 `type` 为 `document`(文档)或 `faq`(FAQ),默认 `document`。 - JSON 中对象存储相关字段:**`storage_config`** 为序列化字段名(对应数据库列 `cos_config`,兼容旧数据)。旧客户端若仍发送或接收 `cos_config`,服务端会兼容解析;新集成请使用 **`storage_config`**。 - **`storage_provider_config`** 为新版存储提供者选择(如 `{"provider": "local"}`),与租户级存储引擎凭证配合使用;无配置时可为 `null`。 +- 嵌套配置对象:`chunking_config`、`image_processing_config`、`vlm_config`、`asr_config`、`extract_config`、`faq_config`、`question_generation_config`。其中 `extract_config`、`faq_config`、`question_generation_config` 允许为 `null`。 - -| 方法 | 路径 | 描述 | -| ------ | ------------------------------------ | ------------------------ | -| POST | `/knowledge-bases` | 创建知识库 | -| GET | `/knowledge-bases` | 获取知识库列表 | -| GET | `/knowledge-bases/:id` | 获取知识库详情 | -| PUT | `/knowledge-bases/:id` | 更新知识库 | -| DELETE | `/knowledge-bases/:id` | 删除知识库 | -| POST | `/knowledge-bases/copy` | 拷贝知识库 | -| GET | `/knowledge-bases/copy/progress/:task_id` | 获取拷贝进度 | -| GET | `/knowledge-bases/:id/hybrid-search` | 混合搜索(向量+关键词) | -| PUT | `/knowledge-bases/:id/pin` | 置顶/取消置顶知识库 | -| GET | `/knowledge-bases/:id/move-targets` | 获取可迁移目标知识库列表 | +| 方法 | 路径 | 描述 | +| ------ | ----------------------------------------- | ------------------------ | +| POST | `/knowledge-bases` | 创建知识库 | +| GET | `/knowledge-bases` | 获取知识库列表 | +| GET | `/knowledge-bases/:id` | 获取知识库详情 | +| PUT | `/knowledge-bases/:id` | 更新知识库 | +| DELETE | `/knowledge-bases/:id` | 删除知识库 | +| PUT | `/knowledge-bases/:id/pin` | 置顶/取消置顶知识库 | +| GET | `/knowledge-bases/:id/hybrid-search` | 混合搜索(向量+关键词) | +| POST | `/knowledge-bases/copy` | 拷贝知识库(异步任务) | +| GET | `/knowledge-bases/copy/progress/:task_id` | 获取拷贝进度 | +| GET | `/knowledge-bases/:id/move-targets` | 获取可迁移目标知识库列表 | ## POST `/knowledge-bases` - 创建知识库 +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------------------------- | ------- | ---- | --------------------------------------------------------------- | +| name | string | 是 | 知识库名称 | +| description | string | 否 | 知识库描述 | +| type | string | 否 | 知识库类型:`document`(默认)或 `faq` | +| is_temporary | boolean | 否 | 是否为临时知识库(默认 `false`,临时库通常不在 UI 列表中显示) | +| chunking_config | object | 否 | 分块配置(见下方示例) | +| image_processing_config | object | 否 | 图片处理配置 | +| embedding_model_id | string | 否 | Embedding 模型 ID | +| summary_model_id | string | 否 | 摘要模型 ID | +| vlm_config | object | 否 | VLM(视觉模型)配置 | +| asr_config | object | 否 | ASR(语音识别)配置 | +| storage_provider_config | object | 否 | 存储提供者选择,如 `{"provider": "local"}` | +| storage_config | object | 否 | 旧版 COS 存储凭证(兼容字段,新集成留空即可) | +| extract_config | object | 否 | 图谱抽取配置;`enabled=true` 时需提供 `text`/`tags`/`nodes`/`relations` | +| faq_config | object | 否 | FAQ 配置(仅 FAQ 类型知识库需要) | +| question_generation_config | object | 否 | 问题生成配置 | + **请求**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases' \ --header 'Content-Type: application/json' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --data '{ "name": "weknora", "description": "weknora description", @@ -160,180 +180,74 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases' \ ## GET `/knowledge-bases` - 获取知识库列表 +返回当前租户拥有的全部知识库。当传入 `agent_id` 时,校验调用者对该共享智能体的访问权限后,返回该智能体配置可见的知识库范围(用于 `@` 提及)。 + +**Query 参数**: + +| 字段 | 类型 | 必填 | 说明 | +| -------- | ------ | ---- | ---------------------------------------------------------- | +| agent_id | string | 否 | 共享智能体 ID;传入时按智能体配置(`all` / `selected` / `none`)过滤可见知识库 | + **请求**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases' \ --header 'Content-Type: application/json' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' +--header 'X-API-Key: sk-xxxxx' ``` -**响应**: - -```json -{ - "data": [ - { - "id": "kb-00000001", - "name": "Default Knowledge Base", - "description": "System Default Knowledge Base", - "type": "document", - "is_temporary": false, - "tenant_id": 1, - "chunking_config": { - "chunk_size": 1000, - "chunk_overlap": 200, - "separators": [ - "\n\n", - "\n", - "。", - "!", - "?", - ";", - ";" - ], - "enable_multimodal": true, - "parser_engine_rules": [], - "enable_parent_child": false, - "parent_chunk_size": 4096, - "child_chunk_size": 384 - }, - "image_processing_config": { - "model_id": "" - }, - "embedding_model_id": "dff7bc94-7885-4dd1-bfd5-bd96e4df2fc3", - "summary_model_id": "8aea788c-bb30-4898-809e-e40c14ffb48c", - "vlm_config": { - "enabled": true, - "model_id": "f2083ad7-63e3-486d-a610-e6c56e58d72e" - }, - "asr_config": { - "enabled": false, - "model_id": "", - "language": "" - }, - "storage_provider_config": { - "provider": "local" - }, - "storage_config": { - "secret_id": "", - "secret_key": "", - "region": "", - "bucket_name": "", - "app_id": "", - "path_prefix": "" - }, - "extract_config": null, - "faq_config": null, - "question_generation_config": null, - "is_pinned": false, - "pinned_at": null, - "knowledge_count": 12, - "chunk_count": 340, - "processing_count": 0, - "created_at": "2025-08-11T20:10:41.817794+08:00", - "updated_at": "2025-08-12T11:23:00.593097+08:00", - "deleted_at": null - } - ], - "success": true -} -``` +**响应**: `data` 为数组,每个元素的字段结构同 `POST /knowledge-bases` 响应,并额外携带 `knowledge_count` / `chunk_count` / `processing_count` / `share_count` / `is_pinned` / `pinned_at` 这些聚合与状态字段。 ## GET `/knowledge-bases/:id` - 获取知识库详情 +根据 ID 获取知识库详情。当通过共享智能体访问时,可传 `agent_id` 进行权限校验;此时返回对象会附加 `my_permission` 字段以指示当前用户对该知识库的角色(如 `viewer`)。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | --------- | +| id | string | 知识库 ID | + +**Query 参数**: + +| 字段 | 类型 | 必填 | 说明 | +| -------- | ------ | ---- | ------------------------------------------ | +| agent_id | string | 否 | 共享智能体 ID(用于校验该智能体是否有权访问) | + **请求**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001' \ --header 'Content-Type: application/json' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' +--header 'X-API-Key: sk-xxxxx' ``` -**响应**: - -```json -{ - "data": { - "id": "kb-00000001", - "name": "Default Knowledge Base", - "description": "System Default Knowledge Base", - "type": "document", - "is_temporary": false, - "tenant_id": 1, - "chunking_config": { - "chunk_size": 1000, - "chunk_overlap": 200, - "separators": [ - "\n\n", - "\n", - "。", - "!", - "?", - ";", - ";" - ], - "enable_multimodal": true, - "parser_engine_rules": [], - "enable_parent_child": false, - "parent_chunk_size": 4096, - "child_chunk_size": 384 - }, - "image_processing_config": { - "model_id": "" - }, - "embedding_model_id": "dff7bc94-7885-4dd1-bfd5-bd96e4df2fc3", - "summary_model_id": "8aea788c-bb30-4898-809e-e40c14ffb48c", - "vlm_config": { - "enabled": true, - "model_id": "f2083ad7-63e3-486d-a610-e6c56e58d72e" - }, - "asr_config": { - "enabled": false, - "model_id": "", - "language": "" - }, - "storage_provider_config": { - "provider": "local" - }, - "storage_config": { - "secret_id": "", - "secret_key": "", - "region": "", - "bucket_name": "", - "app_id": "", - "path_prefix": "" - }, - "extract_config": { - "enabled": false, - "text": "", - "tags": [], - "nodes": [], - "relations": [] - }, - "faq_config": null, - "question_generation_config": null, - "is_pinned": false, - "pinned_at": null, - "knowledge_count": 12, - "chunk_count": 340, - "processing_count": 0, - "created_at": "2025-08-11T20:10:41.817794+08:00", - "updated_at": "2025-08-12T11:23:00.593097+08:00", - "deleted_at": null - }, - "success": true -} -``` +**响应**: 字段结构同 `POST /knowledge-bases` 响应,并附 `is_pinned` / `pinned_at` / `knowledge_count` / `chunk_count` / `processing_count` 状态字段。通过共享智能体访问时还会附加 `my_permission`。 ## PUT `/knowledge-bases/:id` - 更新知识库 +仅知识库 owner(admin)或具备 `editor` 权限的用户可调用。注意:**`vector_store_id` 在创建后不可修改**,更新接口不接收该字段。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | --------- | +| id | string | 知识库 ID | + +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------- | ------ | ---- | ------------------------------------------------------------- | +| name | string | 是 | 知识库名称 | +| description | string | 否 | 知识库描述 | +| config | object | 否 | 更新配置;包含 `chunking_config` / `image_processing_config` / `faq_config` / `wiki_config` / `indexing_strategy` | + **请求**: ```curl curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/b5829e4a-3845-4624-a7fb-ea3b35e843b0' \ --header 'Content-Type: application/json' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --data '{ "name": "weknora new", "description": "weknora description new", @@ -368,89 +282,24 @@ curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/b582 }' ``` -**响应**: - -```json -{ - "data": { - "id": "b5829e4a-3845-4624-a7fb-ea3b35e843b0", - "name": "weknora new", - "description": "weknora description new", - "type": "document", - "is_temporary": false, - "tenant_id": 1, - "chunking_config": { - "chunk_size": 1000, - "chunk_overlap": 200, - "separators": [ - "\n\n", - "\n", - "。", - "!", - "?", - ";", - ";" - ], - "enable_multimodal": true, - "parser_engine_rules": [ - { - "file_types": [".md", ".txt"], - "engine": "builtin" - } - ], - "enable_parent_child": true, - "parent_chunk_size": 4096, - "child_chunk_size": 384 - }, - "image_processing_config": { - "model_id": "" - }, - "embedding_model_id": "dff7bc94-7885-4dd1-bfd5-bd96e4df2fc3", - "summary_model_id": "8aea788c-bb30-4898-809e-e40c14ffb48c", - "vlm_config": { - "enabled": true, - "model_id": "f2083ad7-63e3-486d-a610-e6c56e58d72e" - }, - "asr_config": { - "enabled": false, - "model_id": "", - "language": "" - }, - "storage_provider_config": { - "provider": "local" - }, - "storage_config": { - "secret_id": "", - "secret_key": "", - "region": "", - "bucket_name": "", - "app_id": "", - "path_prefix": "" - }, - "extract_config": null, - "faq_config": null, - "question_generation_config": null, - "is_pinned": false, - "pinned_at": null, - "knowledge_count": 3, - "chunk_count": 48, - "processing_count": 1, - "created_at": "2025-08-12T11:30:09.206238+08:00", - "updated_at": "2025-08-12T11:36:09.083577609+08:00", - "deleted_at": null - }, - "success": true -} -``` +**响应**: 字段结构同 `POST /knowledge-bases` 响应(返回更新后的完整知识库对象)。 ## DELETE `/knowledge-bases/:id` - 删除知识库 +仅知识库 owner(与 owning tenant 匹配的 admin)可调用,删除将级联清理知识库下所有知识与切片。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | --------- | +| id | string | 知识库 ID | + **请求**: ```curl curl --location --request DELETE 'http://localhost:8080/api/v1/knowledge-bases/b5829e4a-3845-4624-a7fb-ea3b35e843b0' \ --header 'Content-Type: application/json' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -462,87 +311,59 @@ curl --location --request DELETE 'http://localhost:8080/api/v1/knowledge-bases/b } ``` -## POST `/knowledge-bases/copy` - 拷贝知识库 +## PUT `/knowledge-bases/:id/pin` - 置顶/取消置顶知识库 -异步拷贝一个知识库,包括知识库配置和所有知识内容。返回任务ID用于查询拷贝进度。 +切换知识库的置顶状态。无需请求体,每次调用会自动反转当前 `is_pinned`。置顶时会同步写入 `pinned_at` 时间戳。 -**请求参数**: -- `source_id`: 源知识库ID(必填) -- `name`: 新知识库名称(可选,默认使用原名称加"(副本)"后缀) +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | --------- | +| id | string | 知识库 ID | **请求**: ```curl -curl --location 'http://localhost:8080/api/v1/knowledge-bases/copy' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' \ ---data '{ - "source_id": "kb-00000001", - "name": "知识库副本" -}' -``` - -**响应**: - -```json -{ - "data": { - "task_id": "task-copy-00000001", - "target_id": "kb-00000002" - }, - "success": true -} -``` - -## GET `/knowledge-bases/copy/progress/:task_id` - 获取拷贝进度 - -查询知识库拷贝任务的执行进度。 - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/knowledge-bases/copy/progress/task-copy-00000001' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/pin' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' ``` -**响应**: - -```json -{ - "data": { - "task_id": "task-copy-00000001", - "status": "completed", - "total": 10, - "finished": 10, - "source_id": "kb-00000001", - "target_id": "kb-00000002" - }, - "success": true -} -``` - -注:`status` 可能的值为 `pending`、`processing`、`completed`、`failed`。 +**响应**: 字段结构同 `POST /knowledge-bases` 响应,本接口操作后 `is_pinned` 翻转、`pinned_at` 同步更新。 ## GET `/knowledge-bases/:id/hybrid-search` - 混合搜索 -执行向量搜索和关键词搜索的混合检索。 +在指定知识库内执行向量召回 + 关键词召回的混合检索。 -**注意**:此接口使用 GET 方法但需要 JSON 请求体。 +**注意**:此接口使用 `GET` 方法但 **需要 JSON 请求体**(`SearchParams`),并非通过 query string 传参。 -**请求参数**: -- `query_text`: 搜索查询文本(必填) -- `vector_threshold`: 向量相似度阈值(0-1,可选) -- `keyword_threshold`: 关键词匹配阈值(可选) -- `match_count`: 返回结果数量(可选) -- `disable_keywords_match`: 是否禁用关键词匹配(可选) -- `disable_vector_match`: 是否禁用向量匹配(可选) +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | --------- | +| id | string | 知识库 ID | + +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ------------------------ | -------- | ---- | ---------------------------------------------------------------- | +| query_text | string | 是 | 查询文本 | +| vector_threshold | number | 否 | 向量相似度阈值(0-1) | +| keyword_threshold | number | 否 | 关键词匹配阈值 | +| match_count | integer | 否 | 返回结果数量上限 | +| disable_keywords_match | boolean | 否 | 关闭关键词召回 | +| disable_vector_match | boolean | 否 | 关闭向量召回 | +| knowledge_ids | string[] | 否 | 仅在指定的知识 ID 范围内召回 | +| tag_ids | string[] | 否 | 标签过滤(FAQ 类型常用于优先级过滤) | +| only_recommended | boolean | 否 | 仅返回标记为推荐的内容 | +| knowledge_base_ids | string[] | 否 | 跨知识库召回(需共享相同 embedding 模型),优先级高于路径中的 `:id` | +| skip_context_enrichment | boolean | 否 | 跳过父子片段/相邻片段的上下文补全(chat 流程使用) | **请求**: ```curl curl --location --request GET 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/hybrid-search' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "query_text": "如何使用知识库", @@ -577,15 +398,76 @@ curl --location --request GET 'http://localhost:8080/api/v1/knowledge-bases/kb-0 } ``` -## PUT `/knowledge-bases/:id/pin` - 置顶/取消置顶知识库 +## POST `/knowledge-bases/copy` - 拷贝知识库 -切换知识库的置顶状态。无需请求体,每次调用会自动切换当前置顶状态。 +异步拷贝整个知识库(配置 + 全部知识内容)。请求会被入队到 Asynq 后台任务(队列 `default`,最多重试 3 次),并立即返回 `task_id` 供轮询进度。 + +**约束**:源知识库 `source_id` 必须属于调用者所在租户;若指定 `target_id`,目标知识库同样必须属于调用者租户,否则返回 `403 Forbidden`。 + +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ---------- | ------ | ---- | ------------------------------------------------------------- | +| source_id | string | 是 | 源知识库 ID(必须属于当前租户) | +| target_id | string | 否 | 目标知识库 ID(若复用已存在知识库;同样必须属于当前租户) | +| task_id | string | 否 | 自定义任务 ID;不传则由服务端生成(基于租户、源 ID、时间戳) | **请求**: ```curl -curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/pin' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +curl --location 'http://localhost:8080/api/v1/knowledge-bases/copy' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "source_id": "kb-00000001" +}' +``` + +**响应**: + +```json +{ + "data": { + "task_id": "kb_clone_1_kb-00000001_1736582400", + "source_id": "kb-00000001", + "target_id": "", + "message": "Knowledge base copy task started" + }, + "success": true +} +``` + +## GET `/knowledge-bases/copy/progress/:task_id` - 获取拷贝进度 + +查询拷贝任务的当前状态与进度(数据由 worker 写入 Redis)。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ------- | ------ | ------------------------------------------------- | +| task_id | string | 由 `POST /knowledge-bases/copy` 返回的任务 ID | + +**响应字段(`data`)**: + +| 字段 | 类型 | 说明 | +| ---------- | ------- | ------------------------------------------------------------ | +| task_id | string | 任务 ID | +| source_id | string | 源知识库 ID | +| target_id | string | 目标知识库 ID(任务开始后填入) | +| status | string | `pending` / `processing` / `completed` / `failed` | +| progress | integer | 进度百分比 0–100 | +| total | integer | 计划拷贝的知识总数 | +| processed | integer | 已处理的知识数 | +| message | string | 当前状态描述 | +| error | string | 失败时的错误信息 | +| created_at | integer | 任务创建时间(Unix 秒) | +| updated_at | integer | 最后更新时间(Unix 秒) | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/knowledge-bases/copy/progress/kb_clone_1_kb-00000001_1736582400' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' ``` @@ -594,58 +476,17 @@ curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-0 ```json { "data": { - "id": "kb-00000001", - "name": "Default Knowledge Base", - "description": "System Default Knowledge Base", - "type": "document", - "is_temporary": false, - "tenant_id": 1, - "chunking_config": { - "chunk_size": 1000, - "chunk_overlap": 200, - "separators": ["\n\n", "\n", "。", "!", "?", ";", ";"], - "enable_multimodal": true, - "parser_engine_rules": [], - "enable_parent_child": false, - "parent_chunk_size": 4096, - "child_chunk_size": 384 - }, - "image_processing_config": { - "model_id": "" - }, - "embedding_model_id": "dff7bc94-7885-4dd1-bfd5-bd96e4df2fc3", - "summary_model_id": "8aea788c-bb30-4898-809e-e40c14ffb48c", - "vlm_config": { - "enabled": true, - "model_id": "f2083ad7-63e3-486d-a610-e6c56e58d72e" - }, - "asr_config": { - "enabled": false, - "model_id": "", - "language": "" - }, - "storage_provider_config": { - "provider": "local" - }, - "storage_config": { - "secret_id": "", - "secret_key": "", - "region": "", - "bucket_name": "", - "app_id": "", - "path_prefix": "" - }, - "extract_config": null, - "faq_config": null, - "question_generation_config": null, - "is_pinned": true, - "pinned_at": "2025-08-12T15:00:00.000000+08:00", - "knowledge_count": 12, - "chunk_count": 340, - "processing_count": 0, - "created_at": "2025-08-11T20:10:41.817794+08:00", - "updated_at": "2025-08-12T15:00:00.000000+08:00", - "deleted_at": null + "task_id": "kb_clone_1_kb-00000001_1736582400", + "source_id": "kb-00000001", + "target_id": "kb-00000002", + "status": "completed", + "progress": 100, + "total": 10, + "processed": 10, + "message": "Task completed successfully", + "error": "", + "created_at": 1736582400, + "updated_at": 1736582460 }, "success": true } @@ -653,13 +494,25 @@ curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-0 ## GET `/knowledge-bases/:id/move-targets` - 获取可迁移目标知识库列表 -获取当前知识库可以迁移知识到的目标知识库列表。返回结果会排除当前知识库本身。 +返回当前知识库的内容**可以迁移到**的目标知识库列表。筛选规则: + +- 与源知识库 `type` 相同 +- 与源知识库 `embedding_model_id` 相同 +- 非临时知识库(`is_temporary = false`) +- 不包含源知识库自身 +- 仅同租户的知识库 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | ------------- | +| id | string | 源知识库 ID | **请求**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/move-targets' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' ``` diff --git a/docs/api/knowledge-search.md b/docs/api/knowledge-search.md index 2ca7afc75..13b6cb5ba 100644 --- a/docs/api/knowledge-search.md +++ b/docs/api/knowledge-search.md @@ -10,18 +10,23 @@ 在知识库中搜索相关内容(不使用 LLM 总结),直接返回检索结果。 -**请求参数**: -- `query`: 搜索查询文本(必填) -- `knowledge_base_id`: 单个知识库ID(向后兼容) -- `knowledge_base_ids`: 知识库ID列表(支持多知识库搜索) -- `knowledge_ids`: 指定知识(文件)ID列表 +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ------------------- | -------- | ---- | ---------------------------------------------------------- | +| query | string | 是 | 搜索查询文本 | +| knowledge_base_id | string | 否 | 单个知识库 ID(向后兼容);与 `knowledge_base_ids` 互斥 | +| knowledge_base_ids | string[] | 否 | 多个知识库 ID 列表,跨知识库搜索 | +| knowledge_ids | string[] | 否 | 进一步限定到指定知识(文件);不传则在整库范围内搜索 | + +> 必须指定 `knowledge_base_id` 或 `knowledge_base_ids` 中的至少一个。 **请求**: ```curl # 搜索单个知识库 curl --location 'http://localhost:8080/api/v1/knowledge-search' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "query": "如何使用知识库", @@ -30,7 +35,7 @@ curl --location 'http://localhost:8080/api/v1/knowledge-search' \ # 搜索多个知识库 curl --location 'http://localhost:8080/api/v1/knowledge-search' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "query": "如何使用知识库", @@ -39,7 +44,7 @@ curl --location 'http://localhost:8080/api/v1/knowledge-search' \ # 搜索指定文件 curl --location 'http://localhost:8080/api/v1/knowledge-search' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "query": "如何使用知识库", @@ -72,3 +77,21 @@ curl --location 'http://localhost:8080/api/v1/knowledge-search' \ "success": true } ``` + +**响应字段说明(data[])**: + +| 字段 | 类型 | 说明 | +| ------------------ | ------- | ------------------------------------- | +| id | string | 分块 ID | +| content | string | 命中的分块文本 | +| knowledge_id | string | 该分块所属的知识 ID | +| chunk_index | int | 分块在知识中的序号 | +| knowledge_title | string | 来源知识标题 | +| start_at / end_at | int | 分块在源文档中的字符偏移 | +| seq | int | 命中排序号 | +| score | number | 相似度(rerank 后归一化后的最终得分) | +| chunk_type | string | 分块类型(`text` / `image` / ...) | +| image_info | string | 图像分块的额外信息(JSON 字符串) | +| metadata | object | 自定义元数据 | +| knowledge_filename | string | 来源文件名 | +| knowledge_source | string | 来源类型(`file` / `url` / `manual`) | diff --git a/docs/api/knowledge.md b/docs/api/knowledge.md index 50f639b1d..5ecd2d57c 100644 --- a/docs/api/knowledge.md +++ b/docs/api/knowledge.md @@ -2,45 +2,70 @@ [返回目录](./README.md) -| 方法 | 路径 | 描述 | -| ------ | ------------------------------------- | ------------------------ | -| POST | `/knowledge-bases/:id/knowledge/file` | 从文件创建知识 | -| POST | `/knowledge-bases/:id/knowledge/url` | 从 URL 创建知识 | -| POST | `/knowledge-bases/:id/knowledge/manual` | 创建手工 Markdown 知识 | -| GET | `/knowledge-bases/:id/knowledge` | 获取知识库下的知识列表 | -| GET | `/knowledge/:id` | 获取知识详情 | -| DELETE | `/knowledge/:id` | 删除知识 | -| GET | `/knowledge/:id/download` | 下载知识文件 | -| PUT | `/knowledge/:id` | 更新知识 | -| PUT | `/knowledge/manual/:id` | 更新手工 Markdown 知识 | -| PUT | `/knowledge/image/:id/:chunk_id` | 更新图像分块信息 | -| PUT | `/knowledge/tags` | 批量更新知识标签 | -| GET | `/knowledge/batch` | 批量获取知识 | -| POST | `/knowledge/:id/reparse` | 重新解析知识 | -| GET | `/knowledge/search` | 搜索/过滤知识条目 | -| POST | `/knowledge/move` | 迁移知识到另一个知识库 | -| GET | `/knowledge/move/progress/:task_id` | 获取知识迁移进度 | -| GET | `/knowledge/:id/preview` | 预览知识文件 | +知识(Knowledge)是知识库下的一条可检索内容(来自文件、URL 或手工录入的 Markdown)。本文档涵盖知识的创建、查询、更新、删除、迁移与文件预览/下载等接口。 -## POST `/knowledge-bases/:id/knowledge/file` - 从文件创建知识 +| 方法 | 路径 | 描述 | +| ------ | ------------------------------------------ | ------------------------------------------ | +| POST | `/knowledge-bases/:id/knowledge/file` | 上传文件创建知识(multipart) | +| POST | `/knowledge-bases/:id/knowledge/url` | 从 URL 创建知识(网页抓取或文件下载) | +| POST | `/knowledge-bases/:id/knowledge/manual` | 创建手工 Markdown 知识 | +| GET | `/knowledge-bases/:id/knowledge` | 列出知识库下的知识(支持分页/筛选) | +| DELETE | `/knowledge-bases/:id/knowledge` | 清空知识库下的所有知识(异步任务) | +| GET | `/knowledge/batch` | 按 ID 列表批量获取知识 | +| GET | `/knowledge/:id` | 获取知识详情 | +| PUT | `/knowledge/:id` | 更新知识(标题/描述/标签等) | +| DELETE | `/knowledge/:id` | 删除单条知识 | +| PUT | `/knowledge/manual/:id` | 更新手工 Markdown 知识 | +| POST | `/knowledge/:id/reparse` | 重新解析知识(异步) | +| GET | `/knowledge/:id/download` | 下载原始文件(attachment) | +| GET | `/knowledge/:id/preview` | 内联预览文件(按扩展名设置 Content-Type) | +| PUT | `/knowledge/image/:id/:chunk_id` | 更新分块图像信息 | +| PUT | `/knowledge/tags` | 批量更新知识标签 | +| GET | `/knowledge/search` | 跨知识库搜索/过滤知识 | +| POST | `/knowledge/batch-delete` | 同一知识库内批量删除知识(异步任务) | +| POST | `/knowledge/move` | 迁移知识到另一知识库(异步任务) | +| GET | `/knowledge/move/progress/:task_id` | 查询知识迁移任务进度 | -**表单参数**: -- `file`: 上传的文件(必填) -- `metadata`: JSON 格式的元数据(可选) -- `enable_multimodel`: 是否启用多模态处理(可选,true/false) -- `fileName`: 自定义文件名,用于文件夹上传时保留路径(可选) +> **公共说明**: +> - 路径中的 `:id`(知识库路径下)为**知识库 ID**,`/knowledge/:id` 中的 `:id` 为**知识 ID**。 +> - 所有写操作(创建、更新、删除、迁移、重新解析)需要当前用户在知识库所属组织内具有 `editor` 或 `admin` 权限;清空知识库内容仅 KB **所有者**(admin 且租户匹配)可操作。 +> - 关键状态字段:`parse_status` 取值 `pending` / `processing` / `completed` / `failed`;`enable_status` 取值 `enabled` / `disabled`。 + +## POST `/knowledge-bases/:id/knowledge/file` - 上传文件创建知识 + +通过 `multipart/form-data` 上传文件创建知识条目。文件大小受 `MAX_FILE_SIZE_MB` 环境变量限制。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | --------- | +| id | string | 知识库 ID | + +**表单字段**: + +| 字段 | 类型 | 必填 | 说明 | +| ------------------- | ------- | ---- | -------------------------------------------------------------------- | +| `file` | file | 是 | 待上传的文件 | +| `fileName` | string | 否 | 自定义文件名,用于"文件夹上传"时保留相对路径(如 `docs/intro.md`) | +| `metadata` | string | 否 | JSON 字符串,会被反序列化为 `map[string]string` | +| `enable_multimodel` | string | 否 | `"true"` / `"false"`,是否启用图文多模态解析 | +| `tag_id` | string | 否 | 标签 ID;传 `__untagged__` 或空字符串表示未分类 | +| `channel` | string | 否 | 来源渠道标识(写入 `channel` 字段,默认 `web`) | **请求**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/knowledge/file' \ ---header 'Content-Type: application/json' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --form 'file=@"/Users/xxxx/tests/彗星.txt"' \ ---form 'enable_multimodel="true"' +--form 'enable_multimodel="true"' \ +--form 'tag_id="tag-00000001"' \ +--form 'metadata="{\"source\":\"manual_upload\"}"' ``` -**响应**: +> 注意:使用 `-F`/`--form` 时 curl 会自动设置 `Content-Type: multipart/form-data; boundary=...`,不要再手动加 `--header 'Content-Type: application/json'`,否则请求体会被错误解析。 + +**响应**(创建成功,`parse_status=processing` 表示解析任务已入队): ```json { @@ -53,7 +78,7 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/knowle "description": "", "source": "", "channel": "web", - "tag_id": "", + "tag_id": "tag-00000001", "summary_status": "none", "parse_status": "processing", "enable_status": "disabled", @@ -75,21 +100,55 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/knowle } ``` +文件重复时返回 409 与已存在知识的引用;超过大小限制返回 400 `文件大小不能超过 N MB`。 + ## POST `/knowledge-bases/:id/knowledge/url` - 从 URL 创建知识 -**请求**: +可创建**网页知识**或**远程文件知识**。后端根据下列规则自动判定: + +- 当 `file_name` / `file_type` 任一被显式提供,或 URL 路径含已知文件扩展名时,按"文件下载模式"处理(拉取远端文件保存); +- 否则按"网页抓取模式"处理。 + +URL 会经过 SSRF 安全校验,禁止指向内网/回环地址。 + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ------------------- | ------- | ---- | ------------------------------------------------- | +| `url` | string | 是 | 目标 URL | +| `file_name` | string | 否 | 显式指定文件名,强制走文件下载模式 | +| `file_type` | string | 否 | 显式指定文件类型(如 `pdf`、`docx`) | +| `enable_multimodel` | boolean | 否 | 是否启用多模态解析 | +| `title` | string | 否 | 自定义标题 | +| `tag_id` | string | 否 | 标签 ID | +| `channel` | string | 否 | 来源渠道标识 | + +**请求(网页模式)**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/knowledge/url' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ - "url":"https://github.com/Tencent/WeKnora", - "enable_multimodel":true + "url": "https://github.com/Tencent/WeKnora", + "enable_multimodel": true }' ``` -**响应**: +**请求(远程文件模式)**: + +```curl +curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/knowledge/url' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "url": "https://example.com/papers/whitepaper.pdf", + "file_name": "whitepaper.pdf", + "file_type": "pdf" +}' +``` + +**响应**(HTTP 201): ```json { @@ -124,259 +183,30 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/knowle } ``` -## GET `/knowledge-bases/:id/knowledge` - 获取知识库下的知识列表 - -**查询参数**: -- `page`: 页码(默认 1) -- `page_size`: 每页条数(默认 20) -- `tag_id`: 按标签ID筛选(可选) - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/knowledge?page_size=1&page=1&tag_id=tag-00000001' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' -``` - -**响应**: - -```json -{ - "data": [ - { - "id": "9c8af585-ae15-44ce-8f73-45ad18394651", - "tenant_id": 1, - "knowledge_base_id": "kb-00000001", - "type": "url", - "title": "", - "description": "", - "source": "https://github.com/Tencent/WeKnora", - "channel": "web", - "tag_id": "", - "summary_status": "none", - "parse_status": "pending", - "enable_status": "disabled", - "embedding_model_id": "dff7bc94-7885-4dd1-bfd5-bd96e4df2fc3", - "file_name": "", - "file_type": "", - "file_size": 0, - "file_hash": "", - "file_path": "", - "storage_size": 0, - "metadata": null, - "created_at": "2025-08-12T11:55:05.709266+08:00", - "updated_at": "2025-08-12T11:55:05.709266+08:00", - "processed_at": null, - "error_message": "", - "deleted_at": null - } - ], - "page": 1, - "page_size": 1, - "success": true, - "total": 2 -} -``` - -注:parse_status 包含 `pending/processing/failed/completed` 四种状态 - -## GET `/knowledge/:id` - 获取知识详情 - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/knowledge/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' -``` - -**响应**: - -```json -{ - "data": { - "id": "4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5", - "tenant_id": 1, - "knowledge_base_id": "kb-00000001", - "type": "file", - "title": "彗星.txt", - "description": "彗星是由冰和尘埃构成的太阳系小天体,接近太阳时会形成彗发和彗尾。其轨道周期差异大,来源包括柯伊伯带和奥尔特云。彗星与小行星的区别逐渐模糊,部分彗星已失去挥发物质,类似小行星。截至2019年,已知彗星超6600颗,数量庞大。彗星在古代被视为凶兆,现代研究揭示其复杂结构与起源。", - "source": "", - "channel": "web", - "tag_id": "", - "summary_status": "none", - "parse_status": "completed", - "enable_status": "enabled", - "embedding_model_id": "dff7bc94-7885-4dd1-bfd5-bd96e4df2fc3", - "file_name": "彗星.txt", - "file_type": "txt", - "file_size": 7710, - "file_hash": "d69476ddbba45223a5e97e786539952c", - "file_path": "data/files/1/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/1754970756171067621.txt", - "storage_size": 33689, - "metadata": null, - "created_at": "2025-08-12T11:52:36.168632+08:00", - "updated_at": "2025-08-12T11:52:53.376871+08:00", - "processed_at": "2025-08-12T11:52:53.376573+08:00", - "error_message": "", - "deleted_at": null - }, - "success": true -} -``` - -## GET `/knowledge/batch` - 批量获取知识 - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/knowledge/batch?ids=9c8af585-ae15-44ce-8f73-45ad18394651&ids=4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' -``` - -**响应**: - -```json -{ - "data": [ - { - "id": "9c8af585-ae15-44ce-8f73-45ad18394651", - "tenant_id": 1, - "knowledge_base_id": "kb-00000001", - "type": "url", - "title": "", - "description": "", - "source": "https://github.com/Tencent/WeKnora", - "channel": "web", - "tag_id": "", - "summary_status": "none", - "parse_status": "pending", - "enable_status": "disabled", - "embedding_model_id": "dff7bc94-7885-4dd1-bfd5-bd96e4df2fc3", - "file_name": "", - "file_type": "", - "file_size": 0, - "file_hash": "", - "file_path": "", - "storage_size": 0, - "metadata": null, - "created_at": "2025-08-12T11:55:05.709266+08:00", - "updated_at": "2025-08-12T11:55:05.709266+08:00", - "processed_at": null, - "error_message": "", - "deleted_at": null - }, - { - "id": "4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5", - "tenant_id": 1, - "knowledge_base_id": "kb-00000001", - "type": "file", - "title": "彗星.txt", - "description": "彗星是由冰和尘埃构成的太阳系小天体,接近太阳时会形成彗发和彗尾。其轨道周期差异大,来源包括柯伊伯带和奥尔特云。彗星与小行星的区别逐渐模糊,部分彗星已失去挥发物质,类似小行星。截至2019年,已知彗星超6600颗,数量庞大。彗星在古代被视为凶兆,现代研究揭示其复杂结构与起源。", - "source": "", - "channel": "web", - "tag_id": "", - "summary_status": "none", - "parse_status": "completed", - "enable_status": "enabled", - "embedding_model_id": "dff7bc94-7885-4dd1-bfd5-bd96e4df2fc3", - "file_name": "彗星.txt", - "file_type": "txt", - "file_size": 7710, - "file_hash": "d69476ddbba45223a5e97e786539952c", - "file_path": "data/files/1/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/1754970756171067621.txt", - "storage_size": 33689, - "metadata": null, - "created_at": "2025-08-12T11:52:36.168632+08:00", - "updated_at": "2025-08-12T11:52:53.376871+08:00", - "processed_at": "2025-08-12T11:52:53.376573+08:00", - "error_message": "", - "deleted_at": null - } - ], - "success": true -} -``` - -## DELETE `/knowledge/:id` - 删除知识 - -**请求**: - -```curl -curl --location --request DELETE 'http://localhost:8080/api/v1/knowledge/9c8af585-ae15-44ce-8f73-45ad18394651' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' -``` - -**响应**: - -```json -{ - "message": "Deleted successfully", - "success": true -} -``` - -## GET `/knowledge/:id/download` - 下载知识文件 - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/knowledge/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/download' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' -``` - -**响应**: - -``` -attachment -``` - -## PUT `/knowledge/:id` - 更新知识 - -**请求**: - -```curl -curl --location --request PUT 'http://localhost:8080/api/v1/knowledge/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' \ ---data '{ - "title": "更新的标题", - "description": "更新的描述", - "tag_id": "tag-00000001" -}' -``` - -**响应**: - -```json -{ - "message": "Updated successfully", - "success": true -} -``` - ## POST `/knowledge-bases/:id/knowledge/manual` - 创建手工 Markdown 知识 -创建手工 Markdown 知识条目,适用于直接编写内容而非上传文件的场景。 +适用于直接编写 Markdown 内容(无源文件)的场景。 -**请求参数**: -- `title`: 知识标题(必填) -- `content`: Markdown 内容(必填) -- `tag_id`: 标签ID(可选) +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| --------- | ------ | ---- | --------------------------------------------------- | +| `title` | string | 是 | 标题 | +| `content` | string | 是 | Markdown 正文 | +| `status` | string | 否 | 草稿/发布等业务状态(草稿不会触发解析) | +| `tag_id` | string | 否 | 标签 ID | +| `channel` | string | 否 | 来源渠道标识 | **请求**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/knowledge/manual' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "title": "产品使用指南", "content": "# 产品使用指南\n\n## 快速入门\n\n这是一份产品使用指南...", + "status": "published", "tag_id": "tag-00000001" }' ``` @@ -394,7 +224,7 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/knowle "description": "", "source": "", "channel": "web", - "tag_id": "", + "tag_id": "tag-00000001", "summary_status": "none", "parse_status": "processing", "enable_status": "disabled", @@ -416,17 +246,259 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/knowle } ``` +## GET `/knowledge-bases/:id/knowledge` - 列出知识库下的知识 + +支持分页与按标签/关键词/文件类型筛选。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | --------- | +| id | string | 知识库 ID | + +**查询参数**: + +| 字段 | 类型 | 默认 | 说明 | +| ----------- | ------- | ---- | ----------------------------------- | +| `page` | integer | 1 | 页码(从 1 开始) | +| `page_size` | integer | 20 | 每页条数 | +| `tag_id` | string | - | 按标签 ID 过滤 | +| `keyword` | string | - | 按标题/内容关键词过滤 | +| `file_type` | string | - | 按单个文件扩展名过滤(如 `pdf`) | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/knowledge?page=1&page_size=1&tag_id=tag-00000001' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: + +```json +{ + "data": [ + { + "id": "9c8af585-ae15-44ce-8f73-45ad18394651", + "tenant_id": 1, + "knowledge_base_id": "kb-00000001", + "type": "url", + "title": "", + "description": "", + "source": "https://github.com/Tencent/WeKnora", + "channel": "web", + "tag_id": "tag-00000001", + "summary_status": "none", + "parse_status": "pending", + "enable_status": "disabled", + "embedding_model_id": "dff7bc94-7885-4dd1-bfd5-bd96e4df2fc3", + "file_name": "", + "file_type": "", + "file_size": 0, + "file_hash": "", + "file_path": "", + "storage_size": 0, + "metadata": null, + "created_at": "2025-08-12T11:55:05.709266+08:00", + "updated_at": "2025-08-12T11:55:05.709266+08:00", + "processed_at": null, + "error_message": "", + "deleted_at": null + } + ], + "page": 1, + "page_size": 1, + "total": 2, + "success": true +} +``` + +## DELETE `/knowledge-bases/:id/knowledge` - 清空知识库下的所有知识 + +异步提交"清空任务",删除该知识库下的全部知识条目;知识库本身保留。**仅 KB 所有者(admin 且租户匹配)可操作**。 + +**请求**: + +```curl +curl --location --request DELETE 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/knowledge' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**(已入队): + +```json +{ + "success": true, + "message": "Knowledge base contents clear task submitted", + "data": { "deleted_count": 42 } +} +``` + +知识库已为空时: + +```json +{ + "success": true, + "message": "Knowledge base is already empty", + "data": { "deleted_count": 0 } +} +``` + +## GET `/knowledge/batch` - 批量获取知识 + +按 ID 列表一次性获取多条知识详情,常用于刷新页面后恢复选中列表。 + +**查询参数**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------- | -------- | ---- | --------------------------------------------------------------------- | +| `ids` | string[] | 是 | 知识 ID,重复 `ids=...` 传多个 | +| `kb_id` | string | 否 | 限定知识库范围;共享知识库场景下用于按 KB 校验权限并解析有效租户 | +| `agent_id` | string | 否 | 共享 Agent ID;按 Agent 所属租户拉取,常用于共享场景刷新后的文件回填 | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/knowledge/batch?ids=9c8af585-ae15-44ce-8f73-45ad18394651&ids=4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: + +```json +{ + "data": [ + { + "id": "9c8af585-ae15-44ce-8f73-45ad18394651", + "tenant_id": 1, + "knowledge_base_id": "kb-00000001", + "type": "url", + "title": "", + "source": "https://github.com/Tencent/WeKnora", + "parse_status": "pending", + "enable_status": "disabled", + "created_at": "2025-08-12T11:55:05.709266+08:00", + "updated_at": "2025-08-12T11:55:05.709266+08:00" + }, + { + "id": "4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5", + "tenant_id": 1, + "knowledge_base_id": "kb-00000001", + "type": "file", + "title": "彗星.txt", + "file_name": "彗星.txt", + "file_type": "txt", + "file_size": 7710, + "parse_status": "completed", + "enable_status": "enabled", + "created_at": "2025-08-12T11:52:36.168632+08:00", + "updated_at": "2025-08-12T11:52:53.376871+08:00" + } + ], + "success": true +} +``` + +> 上述响应字段省略了空值与一些大字段,实际返回的是完整 `Knowledge` 对象。 + +## GET `/knowledge/:id` - 获取知识详情 + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/knowledge/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: + +```json +{ + "data": { + "id": "4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5", + "tenant_id": 1, + "knowledge_base_id": "kb-00000001", + "type": "file", + "title": "彗星.txt", + "description": "彗星是由冰和尘埃构成的太阳系小天体,接近太阳时会形成彗发和彗尾。", + "source": "", + "channel": "web", + "tag_id": "tag-00000001", + "summary_status": "completed", + "parse_status": "completed", + "enable_status": "enabled", + "embedding_model_id": "dff7bc94-7885-4dd1-bfd5-bd96e4df2fc3", + "file_name": "彗星.txt", + "file_type": "txt", + "file_size": 7710, + "file_hash": "d69476ddbba45223a5e97e786539952c", + "file_path": "data/files/1/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/1754970756171067621.txt", + "storage_size": 33689, + "metadata": null, + "created_at": "2025-08-12T11:52:36.168632+08:00", + "updated_at": "2025-08-12T11:52:53.376871+08:00", + "processed_at": "2025-08-12T11:52:53.376573+08:00", + "error_message": "", + "deleted_at": null + }, + "success": true +} +``` + +## PUT `/knowledge/:id` - 更新知识 + +更新知识条目的元信息(标题/描述/标签等)。请求体为 `Knowledge` 结构,仅服务侧白名单字段会被实际更新。 + +**请求**: + +```curl +curl --location --request PUT 'http://localhost:8080/api/v1/knowledge/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "title": "彗星 - 天文百科", + "description": "彗星条目,已校对", + "tag_id": "tag-00000001", + "enable_status": "enabled" +}' +``` + +**响应**: + +```json +{ + "success": true, + "message": "Knowledge chunk updated successfully" +} +``` + +## DELETE `/knowledge/:id` - 删除单条知识 + +**请求**: + +```curl +curl --location --request DELETE 'http://localhost:8080/api/v1/knowledge/9c8af585-ae15-44ce-8f73-45ad18394651' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: + +```json +{ + "success": true, + "message": "Deleted successfully" +} +``` + ## PUT `/knowledge/manual/:id` - 更新手工 Markdown 知识 -**请求参数**: -- `title`: 新标题(可选) -- `content`: 新 Markdown 内容(可选) +**请求体**:同 `POST /knowledge-bases/:id/knowledge/manual`,字段全部可选。 **请求**: ```curl curl --location --request PUT 'http://localhost:8080/api/v1/knowledge/manual/5a3b2c1d-0e9f-4a8b-7c6d-5e4f3a2b1c0d' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "title": "产品使用指南 V2", @@ -445,6 +517,7 @@ curl --location --request PUT 'http://localhost:8080/api/v1/knowledge/manual/5a3 "type": "manual", "title": "产品使用指南 V2", "parse_status": "processing", + "enable_status": "enabled", "created_at": "2025-08-12T12:00:00.000000+08:00", "updated_at": "2025-08-12T12:30:00.000000+08:00" }, @@ -452,81 +525,23 @@ curl --location --request PUT 'http://localhost:8080/api/v1/knowledge/manual/5a3 } ``` -## PUT `/knowledge/image/:id/:chunk_id` - 更新图像分块信息 - -更新知识条目中指定分块的图像描述信息。 - -**请求参数**: -- `image_info`: 图像信息(JSON 格式字符串) - -**请求**: - -```curl -curl --location --request PUT 'http://localhost:8080/api/v1/knowledge/image/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' \ ---data '{ - "image_info": "{\"description\": \"产品架构图\", \"alt_text\": \"WeKnora 系统架构\"}" -}' -``` - -**响应**: - -```json -{ - "message": "Updated successfully", - "success": true -} -``` - -## PUT `/knowledge/tags` - 批量更新知识标签 - -批量更新多个知识条目的标签关联。 - -**请求参数**: -- `updates`: 知识ID到标签ID的映射(设为 `null` 可清除标签) - -**请求**: - -```curl -curl --location --request PUT 'http://localhost:8080/api/v1/knowledge/tags' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' \ ---data '{ - "updates": { - "4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5": "tag-00000001", - "9c8af585-ae15-44ce-8f73-45ad18394651": null - } -}' -``` - -注:设置为 `null` 可清除标签关联。 - -**响应**: - -```json -{ - "success": true -} -``` - ## POST `/knowledge/:id/reparse` - 重新解析知识 -触发知识的异步重新解析。此操作会删除现有的文档内容,然后使用最新的解析配置重新解析知识。 -适用于解析配置更新后需要刷新内容,或者原始解析失败需要重试的场景。 +异步重新解析:删除现有分块/向量并按最新配置重新解析。常用于解析配置变更或上次解析失败重试的场景。 **请求**: ```curl curl --location --request POST 'http://localhost:8080/api/v1/knowledge/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/reparse' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: ```json { + "success": true, + "message": "Knowledge reparse task submitted", "data": { "id": "4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5", "tenant_id": 1, @@ -537,75 +552,249 @@ curl --location --request POST 'http://localhost:8080/api/v1/knowledge/4c4e7c1a- "enable_status": "enabled", "created_at": "2025-08-12T11:52:36.168632+08:00", "updated_at": "2025-08-12T13:00:00.000000+08:00" - }, - "success": true + } } ``` -注:重新解析为异步操作,返回后 `parse_status` 将变为 `pending`,随后进入 `processing` 状态。 +调用后 `parse_status` 会先变为 `pending`,再由后台 worker 转为 `processing` → `completed`/`failed`。 -## GET `/knowledge/search` - 搜索/过滤知识条目 +## GET `/knowledge/:id/download` - 下载原始文件 -按关键词搜索和过滤知识条目,支持按文件类型和 Agent ID 筛选。 +以 `attachment` 方式下载知识对应的原始文件。 -**查询参数**: -- `keyword`: 搜索关键词(可选) -- `offset`: 偏移量(默认 0) -- `limit`: 返回数量(默认 20) -- `file_types`: 文件类型过滤,多个类型用逗号分隔(可选) -- `agent_id`: 按 Agent ID 筛选(可选) +**响应头**: + +``` +Content-Type: application/octet-stream +Content-Disposition: attachment; filename="彗星.txt" +``` **请求**: ```curl -curl --location 'http://localhost:8080/api/v1/knowledge/search?keyword=%E5%BD%97%E6%98%9F&offset=0&limit=10&file_types=txt,pdf' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' +curl --location -OJ 'http://localhost:8080/api/v1/knowledge/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/download' \ +--header 'X-API-Key: sk-xxxxx' +``` + +响应体为文件二进制流。 + +## GET `/knowledge/:id/preview` - 内联预览文件 + +返回原始文件用于浏览器**内嵌预览**: + +- `Content-Type` 按文件扩展名映射(`.pdf` → `application/pdf`,`.png` → `image/png`,`.txt`/`.md`/`.json` 等 → 对应文本 MIME 并带 `charset=utf-8`,未知扩展名回落到 `application/octet-stream`)。 +- `Content-Disposition: inline; filename="<原文件名>"`,浏览器会内嵌渲染而非下载。 +- `Cache-Control: private, max-age=3600`。 + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/knowledge/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/preview' \ +--header 'X-API-Key: sk-xxxxx' \ +-D - +``` + +**响应头**示例: + +``` +HTTP/1.1 200 OK +Content-Type: text/plain; charset=utf-8 +Content-Disposition: inline; filename="彗星.txt" +Cache-Control: private, max-age=3600 +``` + +响应体为文件内容(按 `Content-Type` 解读)。 + +## PUT `/knowledge/image/:id/:chunk_id` - 更新分块图像信息 + +为指定知识下的某个图像分块更新描述/替代文本等元信息。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---------- | ------ | -------- | +| `id` | string | 知识 ID | +| `chunk_id` | string | 分块 ID | + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ------------ | ------ | ---- | ------------------------------------ | +| `image_info` | string | 是 | 图像信息(业务侧 JSON 字符串) | + +**请求**: + +```curl +curl --location --request PUT 'http://localhost:8080/api/v1/knowledge/image/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "image_info": "{\"description\":\"产品架构图\",\"alt_text\":\"WeKnora 系统架构\"}" +}' ``` **响应**: ```json { - "data": { - "data": [ - { - "id": "4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5", - "tenant_id": 1, - "knowledge_base_id": "kb-00000001", - "type": "file", - "title": "彗星.txt", - "description": "彗星是由冰和尘埃构成的太阳系小天体...", - "file_name": "彗星.txt", - "file_type": "txt", - "file_size": 7710, - "parse_status": "completed", - "enable_status": "enabled", - "created_at": "2025-08-12T11:52:36.168632+08:00", - "updated_at": "2025-08-12T11:52:53.376871+08:00" - } - ], - "has_more": false - }, - "success": true + "success": true, + "message": "Knowledge chunk image updated successfully" } ``` -## POST `/knowledge/move` - 迁移知识到另一个知识库 +## PUT `/knowledge/tags` - 批量更新知识标签 -将知识条目从一个知识库迁移到另一个知识库。此操作为异步任务,返回任务ID用于查询迁移进度。 +批量为多条知识设置/清除标签。 -**请求参数**: -- `knowledge_ids`: 待迁移的知识ID列表(必填) -- `source_kb_id`: 源知识库ID(必填) -- `target_kb_id`: 目标知识库ID(必填) -- `mode`: 迁移模式,`reuse_vectors` 复用向量数据,`reparse` 重新解析(必填) +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| --------- | -------------------------- | ---- | -------------------------------------------------------------------------- | +| `updates` | object | 是 | 知识 ID → 标签 ID 的映射;值为 `null` 表示清除该条知识的标签 | +| `kb_id` | string | 否 | 限定知识库范围;指定时按该 KB 校验编辑权限(共享 KB 场景必填) | + +未传 `kb_id` 时,服务会从 `updates` 中取首个 knowledge ID 推断其所属知识库并据此鉴权。 + +**请求**: + +```curl +curl --location --request PUT 'http://localhost:8080/api/v1/knowledge/tags' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "kb_id": "kb-00000001", + "updates": { + "4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5": "tag-00000001", + "9c8af585-ae15-44ce-8f73-45ad18394651": null + } +}' +``` + +**响应**: + +```json +{ "success": true } +``` + +## GET `/knowledge/search` - 跨知识库搜索/过滤知识 + +按关键词在当前租户(含已共享给当前租户)的知识中检索;可按文件类型过滤;指定 `agent_id` 时按共享 Agent 所配置的知识库范围检索。 + +**查询参数**: + +| 字段 | 类型 | 默认 | 说明 | +| ------------ | ------- | ---- | --------------------------------------------------------------------- | +| `keyword` | string | - | 关键词(可选) | +| `offset` | integer | 0 | 偏移量 | +| `limit` | integer | 20 | 返回条数 | +| `file_types` | string | - | 逗号分隔的扩展名列表,例如 `txt,pdf,docx` | +| `agent_id` | string | - | 共享 Agent ID;按该 Agent 的 KB 选择模式(`all`/`selected`/`none`)限定范围 | + +**请求**: + +```curl +curl --location --get 'http://localhost:8080/api/v1/knowledge/search' \ +--header 'X-API-Key: sk-xxxxx' \ +--data-urlencode 'keyword=彗星' \ +--data-urlencode 'offset=0' \ +--data-urlencode 'limit=10' \ +--data-urlencode 'file_types=txt,pdf' +``` + +**响应**: + +```json +{ + "success": true, + "data": [ + { + "id": "4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5", + "tenant_id": 1, + "knowledge_base_id": "kb-00000001", + "type": "file", + "title": "彗星.txt", + "description": "彗星是由冰和尘埃构成的太阳系小天体...", + "file_name": "彗星.txt", + "file_type": "txt", + "file_size": 7710, + "parse_status": "completed", + "enable_status": "enabled", + "created_at": "2025-08-12T11:52:36.168632+08:00", + "updated_at": "2025-08-12T11:52:53.376871+08:00" + } + ], + "has_more": false +} +``` + +> 注意:与其他列表接口不同,此处的 `data` 是**数组**而非 `{data, has_more}` 嵌套对象;`has_more` 与 `data` 同级。 + +`agent_id=...&` 且该 Agent 的 KB 选择模式为 `none` 时,将直接返回 `data: []` 与 `has_more: false`。 + +## POST `/knowledge/batch-delete` - 同一知识库内批量删除 + +按 ID 列表在单个知识库内批量删除知识(异步任务)。单次最多 200 个 ID;服务侧会校验所有 ID 属于同一 `kb_id`。 + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ------- | -------- | ---- | --------------------------------- | +| `kb_id` | string | 是 | 目标知识库 ID | +| `ids` | string[] | 是 | 待删除的知识 ID 列表(≤ 200) | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/knowledge/batch-delete' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "kb_id": "kb-00000001", + "ids": [ + "4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5", + "9c8af585-ae15-44ce-8f73-45ad18394651" + ] +}' +``` + +**响应**: + +```json +{ + "success": true, + "message": "Batch delete task submitted", + "data": { + "task_id": "kg_delete_1_kb-00000001_xxxx", + "deleted_count": 2 + } +} +``` + +任一 ID 不属于 `kb_id` 或不存在时,返回 400 并整批拒绝。 + +## POST `/knowledge/move` - 迁移知识到另一知识库 + +将一条或多条**处于 `completed` 状态**的知识从源 KB 迁到目标 KB(异步)。约束: + +- 源/目标 KB 必须属于当前租户; +- 源/目标必须为同一 KB 类型且**使用相同的 Embedding 模型**; +- 源 KB ≠ 目标 KB; +- 仅 `parse_status=completed` 的知识可迁移。 + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| --------------- | -------- | ---- | ------------------------------------------------------------------------------- | +| `knowledge_ids` | string[] | 是 | 待迁移的知识 ID 列表(至少 1 个) | +| `source_kb_id` | string | 是 | 源知识库 ID | +| `target_kb_id` | string | 是 | 目标知识库 ID | +| `mode` | string | 是 | 迁移模式:`reuse_vectors`(复用向量数据,零成本) / `reparse`(在目标库重新解析) | **请求**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge/move' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "knowledge_ids": ["4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5"], @@ -619,64 +808,48 @@ curl --location 'http://localhost:8080/api/v1/knowledge/move' \ ```json { + "success": true, "data": { - "task_id": "task-move-00000001", + "task_id": "kg_move_1_kb-00000001_xxxx", "source_kb_id": "kb-00000001", "target_kb_id": "kb-00000002", "knowledge_count": 1, - "message": "知识迁移任务已创建" - }, - "success": true + "message": "Knowledge move task started" + } } ``` -## GET `/knowledge/move/progress/:task_id` - 获取知识迁移进度 +获取 `task_id` 后通过下一个接口轮询进度。 -查询知识迁移任务的执行进度。 +## GET `/knowledge/move/progress/:task_id` - 查询迁移进度 **请求**: ```curl -curl --location 'http://localhost:8080/api/v1/knowledge/move/progress/task-move-00000001' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ ---header 'Content-Type: application/json' +curl --location 'http://localhost:8080/api/v1/knowledge/move/progress/kg_move_1_kb-00000001_xxxx' \ +--header 'X-API-Key: sk-xxxxx' ``` **响应**: ```json { + "success": true, "data": { - "task_id": "task-move-00000001", + "task_id": "kg_move_1_kb-00000001_xxxx", + "source_kb_id": "kb-00000001", + "target_kb_id": "kb-00000002", "status": "completed", "progress": 100, "total": 1, "processed": 1, + "failed": 0, "message": "迁移完成", - "error": "" - }, - "success": true + "error": "", + "created_at": 1731312000, + "updated_at": 1731312045 + } } ``` -注:`status` 可能的值为 `pending`、`processing`、`completed`、`failed`。 - -## GET `/knowledge/:id/preview` - 预览知识文件 - -在浏览器中内联预览知识文件内容。响应会设置相应的 `Content-Type` 和 `Content-Disposition` 头,用于浏览器端直接展示文件。 - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/knowledge/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/preview' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' -``` - -**响应**: - -``` -Content-Type: text/plain; charset=utf-8 -Content-Disposition: inline; filename="彗星.txt" - -(文件内容) -``` +`status` 取值:`pending` / `processing` / `completed` / `failed`;`progress` 为 0-100 的整数百分比;`created_at` / `updated_at` 为 Unix 秒时间戳。 diff --git a/docs/api/mcp-service.md b/docs/api/mcp-service.md index cb1ed3f81..46b539bf2 100644 --- a/docs/api/mcp-service.md +++ b/docs/api/mcp-service.md @@ -2,29 +2,38 @@ [返回目录](./README.md) -| 方法 | 路径 | 描述 | -| ------ | --------------------------------- | ---------------------- | -| POST | `/mcp-services` | 创建 MCP 服务 | -| GET | `/mcp-services` | 获取 MCP 服务列表 | -| GET | `/mcp-services/:id` | 获取 MCP 服务详情 | -| PUT | `/mcp-services/:id` | 更新 MCP 服务 | -| DELETE | `/mcp-services/:id` | 删除 MCP 服务 | -| POST | `/mcp-services/:id/test` | 测试 MCP 服务连接 | -| GET | `/mcp-services/:id/tools` | 获取 MCP 服务工具列表 | -| GET | `/mcp-services/:id/resources` | 获取 MCP 服务资源列表 | +MCP(Model Context Protocol)服务管理接口,提供 MCP 服务的 CRUD、连通性测试、工具/资源发现,以及工具人工审批策略配置。 + +| 方法 | 路径 | 描述 | +| ------ | ------------------------------------------------- | --------------------------------------------- | +| POST | `/mcp-services` | 创建 MCP 服务 | +| GET | `/mcp-services` | 获取当前租户的 MCP 服务列表 | +| GET | `/mcp-services/:id` | 获取 MCP 服务详情 | +| PUT | `/mcp-services/:id` | 更新 MCP 服务(部分字段更新) | +| DELETE | `/mcp-services/:id` | 删除 MCP 服务 | +| POST | `/mcp-services/:id/test` | 测试 MCP 服务连通性 | +| GET | `/mcp-services/:id/tools` | 获取 MCP 服务工具列表 | +| GET | `/mcp-services/:id/resources` | 获取 MCP 服务资源列表 | +| GET | `/mcp-services/:id/tool-approvals` | 列出该服务下各工具的人工审批策略 | +| PUT | `/mcp-services/:id/tool-approvals/:tool_name` | 设置/更新某工具的人工审批策略 | +| POST | `/agent/tool-approvals/:pending_id` | 处理 Agent 工具调用待审批请求 | ## POST `/mcp-services` - 创建 MCP 服务 **请求参数**: -- `name`: 服务名称(必填) -- `description`: 服务描述(可选) -- `transport_type`: 传输类型,可选值:`sse`、`http-streamable`、`stdio`(必填) -- `url`: 服务地址,当 transport_type 为 `sse` 或 `http-streamable` 时必填 -- `headers`: 自定义请求头(可选) -- `auth_config`: 认证配置(可选),包含 `api_key`、`token`、`custom_headers` -- `advanced_config`: 高级配置(可选),包含 `timeout`、`retry_count`、`retry_delay` -- `stdio_config`: stdio 传输配置(可选),包含 `command`、`args` -- `env_vars`: 环境变量(可选) + +| 字段 | 类型 | 必填 | 说明 | +| ---------------- | ------- | ---- | --------------------------------------------------------------------------------------------- | +| name | string | 是 | 服务名称 | +| description | string | 否 | 服务描述 | +| transport_type | string | 是 | 传输类型,可选:`sse`、`http-streamable`、`stdio` | +| url | string | 条件 | 服务地址;当 `transport_type` 为 `sse` / `http-streamable` 时必填(受 SSRF 安全校验约束) | +| headers | object | 否 | 自定义请求头 | +| auth_config | object | 否 | 认证配置,支持 `api_key`、`token` | +| advanced_config | object | 否 | 高级配置,支持 `timeout`、`retry_count`、`retry_delay` | +| stdio_config | object | 条件 | stdio 传输配置,包含 `command`、`args`;当 `transport_type` 为 `stdio` 时必填 | +| env_vars | object | 否 | 环境变量(stdio 场景常用) | +| enabled | boolean | 否 | 是否启用 | **请求**: @@ -82,7 +91,7 @@ curl --location 'http://localhost:8080/api/v1/mcp-services' \ } ``` -**创建 stdio 类型的 MCP 服务**: +**创建 stdio 类型的 MCP 服务示例**: ```curl curl --location 'http://localhost:8080/api/v1/mcp-services' \ @@ -104,6 +113,8 @@ curl --location 'http://localhost:8080/api/v1/mcp-services' \ ## GET `/mcp-services` - 获取 MCP 服务列表 +返回当前租户已配置的所有 MCP 服务。 + **请求**: ```curl @@ -166,6 +177,14 @@ curl --location 'http://localhost:8080/api/v1/mcp-services' \ ## GET `/mcp-services/:id` - 获取 MCP 服务详情 +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | -------------- | +| id | string | MCP 服务 ID | + +> 注:内置(`is_builtin: true`)服务在响应中会隐藏敏感凭证字段。 + **请求**: ```curl @@ -205,6 +224,8 @@ curl --location 'http://localhost:8080/api/v1/mcp-services/mcp-00000001' \ ## PUT `/mcp-services/:id` - 更新 MCP 服务 +支持部分字段更新,可传入下列任意子集:`name`、`description`、`enabled`、`transport_type`、`url`、`stdio_config`、`env_vars`、`headers`、`auth_config`、`advanced_config`。其中 `url` 若提供,会再次执行 SSRF 安全校验。 + **请求**: ```curl @@ -261,11 +282,14 @@ curl --location --request DELETE 'http://localhost:8080/api/v1/mcp-services/mcp- ```json { - "success": true + "success": true, + "message": "MCP service deleted successfully" } ``` -## POST `/mcp-services/:id/test` - 测试 MCP 服务连接 +## POST `/mcp-services/:id/test` - 测试 MCP 服务连通性 + +后端会以已保存配置建立一次 MCP 连接,返回连接结果及探测到的工具/资源列表。连接失败时 HTTP 仍为 200,但 `data.success` 为 `false`,错误原因在 `data.message` 中。 **请求**: @@ -394,3 +418,139 @@ curl --location 'http://localhost:8080/api/v1/mcp-services/mcp-00000001/resource "success": true } ``` + +## GET `/mcp-services/:id/tool-approvals` - 列出工具人工审批策略 + +返回该 MCP 服务下各工具持久化的 `require_approval` 标记。仅返回数据库中已显式配置过的工具记录;未出现在列表中的工具默认无需审批。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | ----------- | +| id | string | MCP 服务 ID | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/mcp-services/mcp-00000001/tool-approvals' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: + +```json +{ + "data": [ + { + "tool_name": "delete_file", + "require_approval": true, + "updated_at": "2025-09-20T15:30:00+08:00" + }, + { + "tool_name": "get_weather", + "require_approval": false, + "updated_at": "2025-09-20T15:31:00+08:00" + } + ], + "success": true +} +``` + +## PUT `/mcp-services/:id/tool-approvals/:tool_name` - 设置工具人工审批策略 + +为指定 MCP 服务下的某个工具设置/更新人工审批要求。当 `require_approval` 为 `true` 时,Agent 在调用该工具前会阻塞并产生一条待审批记录,需要前端调用 `POST /agent/tool-approvals/:pending_id` 完成审批。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---------- | ------ | ------------------------------------------------------------------- | +| id | string | MCP 服务 ID | +| tool_name | string | 工具名(由 Gin 自动 URL 解码,调用方需对名称中的 `%`、`/` 做 URL 编码) | + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------------- | ------- | ---- | ----------------------------------- | +| require_approval | boolean | 是 | 是否要求人工审批后才能执行该工具 | + +**请求**: + +```curl +curl --location --request PUT 'http://localhost:8080/api/v1/mcp-services/mcp-00000001/tool-approvals/delete_file' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "require_approval": true +}' +``` + +**响应**: + +```json +{ + "success": true +} +``` + +## POST `/agent/tool-approvals/:pending_id` - 处理待审批工具调用 + +用于 Agent 在执行过程中阻塞等待人工审批的场景:当 Agent 命中一个 `require_approval = true` 的工具时会生成一条 `pending_id`,前端拿到这个 ID 后调用此接口将审批结果回传给 Agent,Agent 才会继续执行(或终止)。 + +**鉴权要求**:请求上下文中必须有已认证用户(`user_id`),且该用户必须是当前 pending 会话的所有者;租户与用户两层都会做 fail-close 校验。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ----------- | ------ | ------------------- | +| pending_id | string | 待审批记录 ID | + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| -------------- | ------ | ---- | ----------------------------------------------------------------------------------------------------------------- | +| decision | string | 是 | 审批结论,必须为 `approve` 或 `reject` | +| modified_args | object | 否 | 仅在 `approve` 时生效,允许人工修改本次工具调用的参数;必须是非 null 的 JSON 对象,否则返回 400 | +| reason | string | 否 | 审批理由(任意,便于审计) | + +**请求(通过)**: + +```curl +curl --location --request POST 'http://localhost:8080/api/v1/agent/tool-approvals/pending-abcdef123456' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "decision": "approve", + "modified_args": { + "path": "/tmp/safe-target.txt" + }, + "reason": "已确认目标路径安全" +}' +``` + +**请求(驳回)**: + +```curl +curl --location --request POST 'http://localhost:8080/api/v1/agent/tool-approvals/pending-abcdef123456' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "decision": "reject", + "reason": "目标路径在受保护目录" +}' +``` + +**响应**: + +```json +{ + "success": true +} +``` + +**错误码说明**: + +| HTTP | 触发条件 | +| ---- | --------------------------------------------------------------------------------------- | +| 400 | `decision` 不是 `approve`/`reject`;或 `modified_args` 是 `null`/非对象;或租户/用户错配 | +| 401 | 上下文缺失认证用户(中间件未注入 `user_id`) | +| 404 | `pending_id` 不存在或已完成(超时/取消已先一步消费) | diff --git a/docs/api/message.md b/docs/api/message.md index a8a7e8bd3..5d8971863 100644 --- a/docs/api/message.md +++ b/docs/api/message.md @@ -20,7 +20,7 @@ ```curl curl --location --request GET 'http://localhost:8080/api/v1/messages/ceb9babb-1e30-41d7-817d-fd584954304b/load?limit=3&before_time=2030-08-12T14%3A35%3A42.123456789Z' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "query": "彗尾的形状" @@ -177,7 +177,7 @@ curl --location --request GET 'http://localhost:8080/api/v1/messages/ceb9babb-1e ```curl curl --location --request DELETE 'http://localhost:8080/api/v1/messages/ceb9babb-1e30-41d7-817d-fd584954304b/9bcafbcf-a758-40af-a9a3-c4d8e0f49439' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' ``` @@ -204,7 +204,7 @@ curl --location --request DELETE 'http://localhost:8080/api/v1/messages/ceb9babb ```curl curl --location 'http://localhost:8080/api/v1/messages/search' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "query": "彗星的结构", @@ -245,7 +245,7 @@ curl --location 'http://localhost:8080/api/v1/messages/search' \ ```curl curl --location 'http://localhost:8080/api/v1/messages/chat-history-stats' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' ``` diff --git a/docs/api/model.md b/docs/api/model.md index b5ab8e1d2..eefcce575 100644 --- a/docs/api/model.md +++ b/docs/api/model.md @@ -2,24 +2,26 @@ [返回目录](./README.md) -| 方法 | 路径 | 描述 | -| ------ | ----------------------- | --------------------- | -| POST | `/models` | 创建模型 | -| GET | `/models` | 获取模型列表 | -| GET | `/models/:id` | 获取模型详情 | -| PUT | `/models/:id` | 更新模型 | -| DELETE | `/models/:id` | 删除模型 | -| GET | `/models/providers` | 获取模型服务商列表 | +模型管理接口用于维护当前租户下可用的 LLM / Embedding / Rerank / VLLM / ASR 模型配置。 + +| 方法 | 路径 | 描述 | +| ------ | ------------------- | --------------------- | +| GET | `/models/providers` | 获取模型服务商列表 | +| POST | `/models` | 创建模型 | +| GET | `/models` | 获取模型列表 | +| GET | `/models/:id` | 获取模型详情 | +| PUT | `/models/:id` | 更新模型 | +| DELETE | `/models/:id` | 删除模型 | ## 服务商支持 (Provider Support) -WeKnora 支持多种主流 AI 模型服务商,在创建模型时可通过 `provider` 字段指定服务商类型以获得更好的兼容性。 +WeKnora 支持多种主流 AI 模型服务商,在创建模型时可通过 `parameters.provider` 字段指定服务商类型以获得更好的兼容性。 ### 支持的服务商列表 | 服务商标识 | 名称 | 支持的模型类型 | | -------------- | ---------------------------- | ------------------------------- | -| `generic` | 自定义 (OpenAI兼容接口) | Chat, Embedding, Rerank, VLLM | +| `generic` | 自定义 (OpenAI 兼容接口) | Chat, Embedding, Rerank, VLLM | | `openai` | OpenAI | Chat, Embedding, Rerank, VLLM | | `aliyun` | 阿里云 DashScope | Chat, Embedding, Rerank, VLLM | | `zhipu` | 智谱 BigModel | Chat, Embedding, Rerank, VLLM | @@ -39,15 +41,17 @@ WeKnora 支持多种主流 AI 模型服务商,在创建模型时可通过 `pro | `longcat` | LongCat AI | Chat | | `gpustack` | GPUStack | Chat, Embedding, Rerank, VLLM | +> 实际可用的服务商以 `GET /models/providers` 返回为准。 + ## GET `/models/providers` - 获取模型服务商列表 -根据模型类型获取支持的服务商列表及配置信息。 +根据模型类型获取支持的服务商列表及配置信息(系统级元数据,与租户无关)。 -**请求参数**: +**查询参数**: -| 参数 | 类型 | 必填 | 描述 | -| ---------- | ------ | ---- | ---------------------------------------------- | -| model_type | string | 否 | 模型类型:`chat`, `embedding`, `rerank`, `vllm` | +| 字段 | 类型 | 必填 | 说明 | +| ---------- | ------ | ---- | --------------------------------------------------- | +| model_type | string | 否 | 模型类型,可选值:`chat` / `embedding` / `rerank` / `vllm` / `asr`;省略则返回全部 | **请求**: @@ -95,6 +99,20 @@ curl --location 'http://localhost:8080/api/v1/models/providers?model_type=embedd ## POST `/models` - 创建模型 +为当前租户创建一个新的模型配置。 + +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------- | ------ | ---- | --------------------------------------------------------------- | +| name | string | 是 | 模型名称(远程模型对应服务商的 model id,本地模型为 Ollama tag)| +| type | string | 是 | 模型类型,可选值:`KnowledgeQA` / `Embedding` / `Rerank` / `VLLM` / `ASR` | +| source | string | 是 | 模型来源,可选值:`local` / `remote` | +| description | string | 否 | 模型描述 | +| parameters | object | 是 | 模型参数,详见下方 [Parameters](#parameters-模型参数) | + +> 当 `parameters.base_url` 不为空时,后端会执行 SSRF 校验,校验失败将返回 400。 + ### 创建对话模型(KnowledgeQA) **本地 Ollama 模型**: @@ -295,6 +313,8 @@ curl --location 'http://localhost:8080/api/v1/models' \ ## GET `/models` - 获取模型列表 +返回当前租户下的所有模型。内置模型(`is_builtin = true`)的 `base_url` 与 `api_key` 会被清空以隐藏敏感信息。 + **请求**: ```curl @@ -303,62 +323,16 @@ curl --location 'http://localhost:8080/api/v1/models' \ --header 'X-API-Key: your_api_key' ``` -**响应**: - -```json -{ - "success": true, - "data": [ - { - "id": "dff7bc94-7885-4dd1-bfd5-bd96e4df2fc3", - "tenant_id": 1, - "name": "text-embedding-v3", - "type": "Embedding", - "source": "remote", - "description": "阿里云通义千问 Embedding 模型", - "parameters": { - "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", - "api_key": "sk-***", - "provider": "aliyun", - "embedding_parameters": { - "dimension": 1024, - "truncate_prompt_tokens": 0 - } - }, - "is_default": true, - "status": "active", - "created_at": "2025-08-11T20:10:41.813832+08:00", - "updated_at": "2025-08-11T20:10:41.822354+08:00", - "deleted_at": null - }, - { - "id": "8aea788c-bb30-4898-809e-e40c14ffb48c", - "tenant_id": 1, - "name": "qwen-plus", - "type": "KnowledgeQA", - "source": "remote", - "description": "阿里云 Qwen 大模型", - "parameters": { - "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", - "api_key": "sk-***", - "provider": "aliyun", - "embedding_parameters": { - "dimension": 0, - "truncate_prompt_tokens": 0 - } - }, - "is_default": true, - "status": "active", - "created_at": "2025-08-11T20:10:41.811761+08:00", - "updated_at": "2025-08-11T20:10:41.825381+08:00", - "deleted_at": null - } - ] -} -``` +**响应**: `data` 为数组,每个元素的字段结构同 `POST /models` 响应。内置模型的 `base_url` 与 `api_key` 字段为空字符串。 ## GET `/models/:id` - 获取模型详情 +**路径参数**: + +| 字段 | 类型 | 必填 | 说明 | +| ---- | ------ | ---- | -------- | +| id | string | 是 | 模型 ID | + **请求**: ```curl @@ -367,38 +341,28 @@ curl --location 'http://localhost:8080/api/v1/models/dff7bc94-7885-4dd1-bfd5-bd9 --header 'X-API-Key: your_api_key' ``` -**响应**: - -```json -{ - "success": true, - "data": { - "id": "dff7bc94-7885-4dd1-bfd5-bd96e4df2fc3", - "tenant_id": 1, - "name": "text-embedding-v3", - "type": "Embedding", - "source": "remote", - "description": "阿里云通义千问 Embedding 模型", - "parameters": { - "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", - "api_key": "sk-***", - "provider": "aliyun", - "embedding_parameters": { - "dimension": 1024, - "truncate_prompt_tokens": 0 - } - }, - "is_default": true, - "status": "active", - "created_at": "2025-08-11T20:10:41.813832+08:00", - "updated_at": "2025-08-11T20:10:41.822354+08:00", - "deleted_at": null - } -} -``` +**响应**: 字段结构同 `POST /models` 响应。404 表示模型不存在。 ## PUT `/models/:id` - 更新模型 +**路径参数**: + +| 字段 | 类型 | 必填 | 说明 | +| ---- | ------ | ---- | -------- | +| id | string | 是 | 模型 ID | + +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------- | ------ | ---- | --------------------------------------------------------------- | +| name | string | 否 | 模型名称(为空字符串时保留原值) | +| description | string | 否 | 模型描述(始终覆盖,传空字符串会清空) | +| type | string | 否 | 模型类型,取值同创建接口 | +| source | string | 否 | 模型来源,取值同创建接口 | +| parameters | object | 否 | 模型参数;`parameter_size` 由后端管理,请求中无需提供;`extra_config` 为空时会沿用旧值 | + +> 同样会对 `parameters.base_url` 做 SSRF 校验,失败时返回 400。 + **请求**: ```curl @@ -407,6 +371,8 @@ curl --location --request PUT 'http://localhost:8080/api/v1/models/8fdc464d-8eaa --header 'X-API-Key: your_api_key' \ --data '{ "name": "gte-rerank-v2", + "type": "Rerank", + "source": "remote", "description": "阿里云 GTE Rerank 模型 V2", "parameters": { "base_url": "https://dashscope.aliyuncs.com/api/v1/services/rerank/text-rerank/text-rerank", @@ -416,38 +382,16 @@ curl --location --request PUT 'http://localhost:8080/api/v1/models/8fdc464d-8eaa }' ``` -**响应**: - -```json -{ - "success": true, - "data": { - "id": "8fdc464d-8eaa-44d4-a85b-094b28af5330", - "tenant_id": 1, - "name": "gte-rerank-v2", - "type": "Rerank", - "source": "remote", - "description": "阿里云 GTE Rerank 模型 V2", - "parameters": { - "base_url": "https://dashscope.aliyuncs.com/api/v1/services/rerank/text-rerank/text-rerank", - "api_key": "sk-***", - "provider": "aliyun", - "embedding_parameters": { - "dimension": 0, - "truncate_prompt_tokens": 0 - } - }, - "is_default": false, - "status": "active", - "created_at": "2025-08-12T10:57:39.512681+08:00", - "updated_at": "2025-08-12T11:00:27.271678+08:00", - "deleted_at": null - } -} -``` +**响应**: 字段结构同 `POST /models` 响应,返回更新后的完整模型对象。 ## DELETE `/models/:id` - 删除模型 +**路径参数**: + +| 字段 | 类型 | 必填 | 说明 | +| ---- | ------ | ---- | -------- | +| id | string | 是 | 模型 ID | + **请求**: ```curl @@ -465,37 +409,46 @@ curl --location --request DELETE 'http://localhost:8080/api/v1/models/8fdc464d-8 } ``` +404 表示模型不存在。 + ## 参数说明 ### ModelType (模型类型) -| 值 | 说明 | 用途 | -| ------------ | ------------ | ------------------------------ | -| KnowledgeQA | 对话模型 | 知识库问答、对话生成 | -| Embedding | 嵌入模型 | 文本向量化、知识库检索 | -| Rerank | 排序模型 | 检索结果重排序、相关性优化 | -| VLLM | 视觉语言模型 | 多模态分析、图文理解 | +| 值 | 前端别名 | 说明 | 用途 | +| ----------- | ----------- | ------------ | ------------------------------ | +| KnowledgeQA | `chat` | 对话模型 | 知识库问答、对话生成 | +| Embedding | `embedding` | 嵌入模型 | 文本向量化、知识库检索 | +| Rerank | `rerank` | 排序模型 | 检索结果重排序、相关性优化 | +| VLLM | `vllm` | 视觉语言模型 | 多模态分析、图文理解 | +| ASR | `asr` | 语音识别模型 | 音频转写 | + +> 创建/更新接口请求体的 `type` 字段使用第一列的后端枚举值(如 `KnowledgeQA`);`GET /models/providers?model_type=` 查询参数使用第二列的前端别名(如 `chat`)。 ### ModelSource (模型来源) -| 值 | 说明 | 配置要求 | -| -------- | ---------- | ------------------------------ | -| local | 本地模型 | 需要已安装 Ollama 并拉取模型 | +| 值 | 说明 | 配置要求 | +| -------- | ---------- | -------------------------------- | +| local | 本地模型 | 需要已安装 Ollama 并拉取模型 | | remote | 远程 API | 需要提供 `base_url` 和 `api_key` | ### Parameters (模型参数) -| 字段 | 类型 | 说明 | -| -------------------- | ------ | -------------------------------------------- | -| base_url | string | API 服务地址(远程模型必填) | -| api_key | string | API 密钥(远程模型必填) | -| provider | string | 服务商标识(可选,用于选择特定的 API 适配器)| -| embedding_parameters | object | Embedding 模型专用参数 | -| extra_config | object | 服务商特定的额外配置 | +| 字段 | 类型 | 必填 | 说明 | +| -------------------- | ----------------- | ---- | ---------------------------------------------------------- | +| base_url | string | 否 | API 服务地址;远程模型必填,会经过 SSRF 校验 | +| api_key | string | 否 | API 密钥;远程模型必填,存储时使用 AES-256 加密 | +| provider | string | 否 | 服务商标识(见上方支持列表),用于选择特定的 API 适配器 | +| interface_type | string | 否 | 接口风格标识(OpenAI 兼容请留空) | +| embedding_parameters | object | 否 | Embedding 模型专用参数,见下方 | +| parameter_size | string | 否 | 模型参数规模(如 `7B`/`13B`/`70B`),通常由后端写入 | +| extra_config | object | 否 | 服务商特定的额外配置 | +| custom_headers | object | 否 | 调用上游 API 时附加的自定义 HTTP 头;保留头会被忽略 | +| supports_vision | bool | 否 | 模型是否支持图像/多模态输入 | ### EmbeddingParameters (嵌入参数) -| 字段 | 类型 | 说明 | -| ---------------------- | ---- | -------------------------- | -| dimension | int | 向量维度(如:768, 1024) | -| truncate_prompt_tokens | int | 截断 Token 数(0 表示不截断)| +| 字段 | 类型 | 必填 | 说明 | +| ---------------------- | ---- | ---- | ------------------------------- | +| dimension | int | 否 | 向量维度(如 768、1024) | +| truncate_prompt_tokens | int | 否 | 截断 Token 数(0 表示不截断) | diff --git a/docs/api/organization.md b/docs/api/organization.md index f6e4c41ae..e42635841 100644 --- a/docs/api/organization.md +++ b/docs/api/organization.md @@ -2,75 +2,104 @@ [返回目录](./README.md) -## 组织 CRUD +组织(Organization,又称"空间")是 WeKnora 的多租户协作单元。一个用户可以创建/加入多个组织,并以 owner / admin / editor / viewer 的角色参与;知识库与智能体可以共享到组织内,组织成员根据角色获得相应的访问权限。 -| 方法 | 路径 | 描述 | -| ------ | ------------------------- | ---------------- | -| POST | `/organizations` | 创建组织 | -| GET | `/organizations` | 获取我的组织列表 | -| GET | `/organizations/:id` | 获取组织详情 | -| PUT | `/organizations/:id` | 更新组织 | -| DELETE | `/organizations/:id` | 删除组织 | +本页文档覆盖以下六类接口: -## 成员管理 +- 组织管理:组织 CRUD、邀请码、搜索、加入与离开 +- 成员管理:成员列表、角色变更、移除、邀请 +- 加入请求:申请列表、审核 +- 知识库共享:将知识库共享到组织 / 取消共享 / 修改权限 +- 智能体共享:将智能体共享到组织 / 取消共享 +- 我的共享视图:当前用户可访问的所有共享知识库 / 智能体 -| 方法 | 路径 | 描述 | -| ------ | --------------------------------------------- | ------------------ | -| POST | `/organizations/join` | 通过邀请码加入组织 | -| POST | `/organizations/join-request` | 提交加入申请 | -| GET | `/organizations/search` | 搜索组织 | -| POST | `/organizations/join-by-id` | 通过组织ID加入 | -| GET | `/organizations/preview/:invite_code` | 预览组织信息 | -| POST | `/organizations/:id/leave` | 离开组织 | -| POST | `/organizations/:id/request-upgrade` | 请求角色升级 | -| POST | `/organizations/:id/invite-code` | 生成邀请码 | -| GET | `/organizations/:id/search-users` | 搜索可邀请用户 | -| POST | `/organizations/:id/invite` | 邀请成员 | -| GET | `/organizations/:id/members` | 获取成员列表 | -| PUT | `/organizations/:id/members/:user_id` | 更新成员角色 | -| DELETE | `/organizations/:id/members/:user_id` | 移除成员 | +公共说明: +- 所有路径前缀为 `/api/v1` +- 鉴权头:`X-API-Key: sk-xxxxx`(或 `Authorization: Bearer ...`) +- 错误响应统一为 `{ "success": false, "error": "..." }`,HTTP 状态码遵循 RESTful 语义 +- 角色 (`OrgMemberRole`) 取值:`owner` / `admin` / `editor` / `viewer` +- 共享权限 (`permission`) 取值:`viewer` / `editor`(创建时通常只允许这两个) -## 加入请求 +## 路由总览 -| 方法 | 路径 | 描述 | -| ---- | ------------------------------------------------------- | ---------------- | -| GET | `/organizations/:id/join-requests` | 获取加入请求列表 | -| PUT | `/organizations/:id/join-requests/:request_id/review` | 审核加入请求 | +### 组织管理 -## 知识库共享 +| 方法 | 路径 | 描述 | +| ------ | --------------------------------------------- | ----------------------------------- | +| POST | `/organizations` | 创建组织 | +| GET | `/organizations` | 获取我的组织列表(含资源数量) | +| GET | `/organizations/preview/:code` | 通过邀请码预览组织(不加入) | +| POST | `/organizations/join` | 通过邀请码加入组织 | +| POST | `/organizations/join-request` | 提交加入申请(针对需要审核的组织) | +| GET | `/organizations/search` | 搜索可加入的可被搜索的组织 | +| POST | `/organizations/join-by-id` | 通过组织 ID 加入可被搜索的组织 | +| GET | `/organizations/:id` | 获取组织详情 | +| PUT | `/organizations/:id` | 更新组织 | +| DELETE | `/organizations/:id` | 删除组织 | +| POST | `/organizations/:id/leave` | 离开组织 | +| POST | `/organizations/:id/request-upgrade` | 现有成员申请角色升级 | +| POST | `/organizations/:id/invite-code` | 重新生成邀请码 | -| 方法 | 路径 | 描述 | -| ------ | --------------------------------------------- | ---------------- | -| POST | `/knowledge-bases/:id/shares` | 共享知识库 | -| GET | `/knowledge-bases/:id/shares` | 获取知识库共享列表 | -| PUT | `/knowledge-bases/:id/shares/:share_id` | 更新共享权限 | -| DELETE | `/knowledge-bases/:id/shares/:share_id` | 取消知识库共享 | +### 成员管理 -## 智能体共享 +| 方法 | 路径 | 描述 | +| ------ | --------------------------------------------- | --------------------------------- | +| GET | `/organizations/:id/search-users` | 搜索可邀请的用户(仅管理员) | +| POST | `/organizations/:id/invite` | 直接添加用户为成员(仅管理员) | +| GET | `/organizations/:id/members` | 获取成员列表 | +| PUT | `/organizations/:id/members/:user_id` | 更新成员角色 | +| DELETE | `/organizations/:id/members/:user_id` | 移除成员 | -| 方法 | 路径 | 描述 | -| ------ | --------------------------------------- | ---------------- | -| POST | `/agents/:id/shares` | 共享智能体 | -| GET | `/agents/:id/shares` | 获取智能体共享列表 | -| DELETE | `/agents/:id/shares/:share_id` | 取消智能体共享 | +### 加入请求 -## 共享资源 +| 方法 | 路径 | 描述 | +| ---- | ------------------------------------------------------- | -------------------------- | +| GET | `/organizations/:id/join-requests` | 获取待审核加入申请列表 | +| PUT | `/organizations/:id/join-requests/:request_id/review` | 审核加入申请(仅管理员) | -| 方法 | 路径 | 描述 | -| ---- | --------------------------- | ------------------ | -| GET | `/shared-knowledge-bases` | 获取共享知识库列表 | -| GET | `/shared-agents` | 获取共享智能体列表 | +### 知识库共享 + +| 方法 | 路径 | 描述 | +| ------ | --------------------------------------------- | ------------------------------------- | +| POST | `/knowledge-bases/:id/shares` | 将知识库共享到组织 | +| GET | `/knowledge-bases/:id/shares` | 获取该知识库的共享列表 | +| PUT | `/knowledge-bases/:id/shares/:share_id` | 更新共享权限 | +| DELETE | `/knowledge-bases/:id/shares/:share_id` | 取消共享 | +| GET | `/organizations/:id/shares` | 获取组织下被共享进来的知识库列表 | +| GET | `/organizations/:id/shared-knowledge-bases` | 组织内全部知识库(含我共享的,空间视图) | + +### 智能体共享 + +| 方法 | 路径 | 描述 | +| ------ | --------------------------------------------- | ------------------------------------- | +| POST | `/agents/:id/shares` | 将智能体共享到组织 | +| GET | `/agents/:id/shares` | 获取该智能体的共享列表 | +| DELETE | `/agents/:id/shares/:share_id` | 取消共享 | +| GET | `/organizations/:id/agent-shares` | 获取组织下被共享进来的智能体列表 | +| GET | `/organizations/:id/shared-agents` | 组织内全部智能体(含我共享的,空间视图) | + +### 我的共享视图 + +| 方法 | 路径 | 描述 | +| ---- | ----------------------------- | --------------------------------------- | +| GET | `/shared-knowledge-bases` | 获取所有共享给我的知识库(跨组织) | +| GET | `/shared-agents` | 获取所有共享给我的智能体(跨组织) | --- -## POST `/organizations` - 创建组织 +## 组织管理 -**请求参数**: -- `name`: 组织名称(必填) -- `description`: 组织描述(可选) -- `avatar`: 组织头像 URL(可选) -- `invite_code_validity_days`: 邀请码有效天数(可选) -- `member_limit`: 成员上限(可选) +### POST `/organizations` - 创建组织 + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| --------------------------- | ------- | ---- | ------------------------------------------------- | +| name | string | 是 | 组织名称(1-255 字符) | +| description | string | 否 | 组织描述(最多 1000 字符) | +| avatar | string | 否 | 头像 URL(最多 512 字符) | +| invite_code_validity_days | int | 否 | 邀请码有效天数:`0`=永久,`1` / `7` / `30`,默认 7 | +| member_limit | int | 否 | 成员上限,`0`=不限,默认 50 | **请求**: @@ -86,7 +115,7 @@ curl --location 'http://localhost:8080/api/v1/organizations' \ }' ``` -**响应**: +**响应**(`201 Created`): ```json { @@ -115,14 +144,15 @@ curl --location 'http://localhost:8080/api/v1/organizations' \ } ``` -## GET `/organizations` - 获取我的组织列表 +### GET `/organizations` - 获取我的组织列表 + +返回当前用户所属的全部组织;`resource_counts` 字段附带每个空间内的知识库数与智能体数,供列表页侧栏直接渲染。 **请求**: ```curl curl --location 'http://localhost:8080/api/v1/organizations' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -135,8 +165,9 @@ curl --location 'http://localhost:8080/api/v1/organizations' \ "id": "org-00000001", "name": "AI 技术团队", "description": "专注于 AI 技术研究与知识管理", - "avatar": "", "owner_id": "user-00000001", + "invite_code": "ABC123XY", + "invite_code_expires_at": "2025-08-19T10:00:00+08:00", "invite_code_validity_days": 7, "require_approval": false, "searchable": false, @@ -151,20 +182,199 @@ curl --location 'http://localhost:8080/api/v1/organizations' \ "created_at": "2025-08-12T10:00:00+08:00", "updated_at": "2025-08-12T10:00:00+08:00" } - ] + ], + "total": 1, + "resource_counts": { + "knowledge_bases": { "by_organization": { "org-00000001": 5 } }, + "agents": { "by_organization": { "org-00000001": 2 } } + } }, "success": true } ``` -## GET `/organizations/:id` - 获取组织详情 +### GET `/organizations/preview/:code` - 通过邀请码预览组织 + +不需要事先成为成员,可用于"加入前预览"页面。`:code` 为邀请码字符串。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | ------ | +| code | string | 邀请码 | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/organizations/preview/ABC123XY' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: + +```json +{ + "data": { + "id": "org-00000001", + "name": "AI 技术团队", + "description": "专注于 AI 技术研究与知识管理", + "avatar": "", + "member_count": 3, + "share_count": 2, + "agent_share_count": 1, + "is_already_member": false, + "require_approval": true, + "created_at": "2025-08-12T10:00:00+08:00" + }, + "success": true +} +``` + +### POST `/organizations/join` - 通过邀请码加入组织 + +仅适用于 `require_approval: false` 的组织。需要审核的组织请改用 `/organizations/join-request`。 + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------- | ------ | ---- | --------------------- | +| invite_code | string | 是 | 邀请码(8-32 字符) | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/organizations/join' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "invite_code": "ABC123XY" +}' +``` + +**响应**: 返回加入后的组织信息(结构同 `GET /organizations/:id`)。 + +### POST `/organizations/join-request` - 提交加入申请 + +当组织开启审核(`require_approval: true`)时使用。 + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------- | ------ | ---- | ------------------------------------------- | +| invite_code | string | 是 | 邀请码(8-32 字符) | +| message | string | 否 | 申请留言(最多 500 字符) | +| role | string | 否 | 期望角色:`viewer` / `editor` / `admin` | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/organizations/join-request' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "invite_code": "ABC123XY", + "message": "希望加入团队参与知识库建设", + "role": "editor" +}' +``` + +**响应**: + +```json +{ + "data": { + "id": "jr-00000001", + "user_id": "user-00000002", + "request_type": "join", + "requested_role": "editor", + "status": "pending", + "created_at": "2025-08-14T10:00:00+08:00" + }, + "success": true +} +``` + +### GET `/organizations/search` - 搜索可加入的组织 + +返回设置了 `searchable: true` 的组织。仅返回元数据,不返回邀请码。 + +**查询参数**: + +| 字段 | 类型 | 必填 | 说明 | +| ----- | ------ | ---- | --------------------------------- | +| q | string | 否 | 搜索关键词(名称或描述模糊匹配) | +| limit | int | 否 | 返回数量(1-100,默认 20) | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/organizations/search?q=AI&limit=10' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: + +```json +{ + "data": [ + { + "id": "org-00000001", + "name": "AI 技术团队", + "description": "专注于 AI 技术研究与知识管理", + "avatar": "", + "member_count": 3, + "member_limit": 50, + "share_count": 2, + "agent_share_count": 1, + "is_already_member": false, + "require_approval": true + } + ], + "total": 1, + "success": true +} +``` + +### POST `/organizations/join-by-id` - 通过组织 ID 加入 + +用于"搜索可加入空间"流程,无需邀请码;目标组织必须 `searchable: true`。如果组织开启了审核,会创建加入申请;否则直接加入。 + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ---------------- | ------ | ---- | ------------------------------------------ | +| organization_id | string | 是 | 目标组织 ID | +| message | string | 否 | 申请留言(最多 500 字符) | +| role | string | 否 | 期望角色:`viewer` / `editor` / `admin` | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/organizations/join-by-id' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "organization_id": "org-00000001", + "message": "希望加入贵团队", + "role": "viewer" +}' +``` + +**响应**: 返回加入后的组织信息(结构同 `GET /organizations/:id`)。 + +### GET `/organizations/:id` - 获取组织详情 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | ------- | +| id | string | 组织 ID | **请求**: ```curl curl --location 'http://localhost:8080/api/v1/organizations/org-00000001' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -197,16 +407,21 @@ curl --location 'http://localhost:8080/api/v1/organizations/org-00000001' \ } ``` -## PUT `/organizations/:id` - 更新组织 +`invite_code` 与 `invite_code_expires_at` 仅当前用户为 owner 或 admin 时返回。 -**请求参数**(均为可选): -- `name`: 组织名称 -- `description`: 组织描述 -- `avatar`: 组织头像 URL -- `require_approval`: 是否需要审核加入 -- `searchable`: 是否可被搜索 -- `invite_code_validity_days`: 邀请码有效天数 -- `member_limit`: 成员上限 +### PUT `/organizations/:id` - 更新组织 + +**请求体**(全部字段均为可选,传 `null` 等同于不更新): + +| 字段 | 类型 | 说明 | +| --------------------------- | ------- | --------------------------------------------- | +| name | string | 组织名称(1-255 字符) | +| description | string | 组织描述(最多 1000 字符) | +| avatar | string | 头像 URL(最多 512 字符) | +| require_approval | bool | 加入是否需要审核 | +| searchable | bool | 是否在 `/organizations/search` 中可被发现 | +| invite_code_validity_days | int | 邀请码有效天数(0=永久,1/7/30) | +| member_limit | int | 成员上限(0=不限) | **请求**: @@ -221,244 +436,52 @@ curl --location --request PUT 'http://localhost:8080/api/v1/organizations/org-00 }' ``` -**响应**: +**响应**: 返回更新后的组织信息(结构同 `GET /organizations/:id`)。 -```json -{ - "data": { - "id": "org-00000001", - "name": "AI 技术团队", - "description": "专注于 AI 技术研究与知识管理(更新)", - "avatar": "", - "owner_id": "user-00000001", - "invite_code_validity_days": 7, - "require_approval": true, - "searchable": true, - "member_limit": 50, - "member_count": 3, - "share_count": 2, - "agent_share_count": 1, - "pending_join_request_count": 0, - "is_owner": true, - "my_role": "owner", - "has_pending_upgrade": false, - "created_at": "2025-08-12T10:00:00+08:00", - "updated_at": "2025-08-12T12:00:00+08:00" - }, - "success": true -} -``` +### DELETE `/organizations/:id` - 删除组织 -## DELETE `/organizations/:id` - 删除组织 +仅 owner 可删除;删除会同时清理成员关系与共享记录。 **请求**: ```curl curl --location --request DELETE 'http://localhost:8080/api/v1/organizations/org-00000001' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: ```json -{ - "success": true -} +{ "success": true } ``` ---- +### POST `/organizations/:id/leave` - 离开组织 -## POST `/organizations/join` - 通过邀请码加入组织 - -**请求参数**: -- `invite_code`: 邀请码(必填) - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/organizations/join' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' \ ---data '{ - "invite_code": "ABC123XY" -}' -``` - -**响应**: - -```json -{ - "success": true -} -``` - -## POST `/organizations/join-request` - 提交加入申请 - -当组织开启了审核加入(`require_approval: true`)时使用。 - -**请求参数**: -- `invite_code`: 邀请码(必填) -- `message`: 申请留言(可选) -- `role`: 申请角色(可选) - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/organizations/join-request' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' \ ---data '{ - "invite_code": "ABC123XY", - "message": "希望加入团队参与知识库建设", - "role": "editor" -}' -``` - -**响应**: - -```json -{ - "success": true -} -``` - -## GET `/organizations/search` - 搜索组织 - -**查询参数**: -- `keyword`: 搜索关键字(可选) -- `page`: 页码(默认 1) -- `page_size`: 每页条数(默认 20) - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/organizations/search?keyword=AI&page=1&page_size=10' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' -``` - -**响应**: - -```json -{ - "data": { - "organizations": [ - { - "id": "org-00000001", - "name": "AI 技术团队", - "description": "专注于 AI 技术研究与知识管理", - "avatar": "", - "owner_id": "user-00000001", - "invite_code_validity_days": 7, - "require_approval": true, - "searchable": true, - "member_limit": 50, - "member_count": 3, - "share_count": 2, - "agent_share_count": 1, - "pending_join_request_count": 0, - "is_owner": false, - "my_role": "", - "has_pending_upgrade": false, - "created_at": "2025-08-12T10:00:00+08:00", - "updated_at": "2025-08-12T10:00:00+08:00" - } - ] - }, - "success": true -} -``` - -## POST `/organizations/join-by-id` - 通过组织ID加入 - -**请求参数**: -- `organization_id`: 组织 ID(必填) -- `message`: 申请留言(可选) -- `role`: 申请角色(可选) - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/organizations/join-by-id' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' \ ---data '{ - "organization_id": "org-00000001", - "message": "希望加入贵团队", - "role": "viewer" -}' -``` - -**响应**: - -```json -{ - "success": true -} -``` - -## GET `/organizations/preview/:invite_code` - 预览组织信息 - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/organizations/preview/ABC123XY' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' -``` - -**响应**: - -```json -{ - "data": { - "id": "org-00000001", - "name": "AI 技术团队", - "description": "专注于 AI 技术研究与知识管理", - "avatar": "", - "owner_id": "user-00000001", - "invite_code_validity_days": 7, - "require_approval": true, - "searchable": true, - "member_limit": 50, - "member_count": 3, - "share_count": 0, - "agent_share_count": 0, - "pending_join_request_count": 0, - "is_owner": false, - "my_role": "", - "has_pending_upgrade": false, - "created_at": "2025-08-12T10:00:00+08:00", - "updated_at": "2025-08-12T10:00:00+08:00" - }, - "success": true -} -``` - -## POST `/organizations/:id/leave` - 离开组织 +owner 不能离开自己的组织,需先转让所有权或删除组织。 **请求**: ```curl curl --location --request POST 'http://localhost:8080/api/v1/organizations/org-00000001/leave' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: ```json -{ - "success": true -} +{ "success": true, "message": "Left organization successfully" } ``` -## POST `/organizations/:id/request-upgrade` - 请求角色升级 +### POST `/organizations/:id/request-upgrade` - 申请角色升级 -**请求参数**: -- `requested_role`: 期望角色(必填) -- `message`: 申请理由(可选) +由已加入的成员主动发起,等待管理员审核(出现在 `/organizations/:id/join-requests` 中的 `request_type: "upgrade"`)。 + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| -------------- | ------ | ---- | ---------------------------------------------------- | +| requested_role | string | 是 | 期望角色:`viewer` / `editor` / `admin` | +| message | string | 否 | 申请理由(最多 500 字符) | **请求**: @@ -476,42 +499,58 @@ curl --location --request POST 'http://localhost:8080/api/v1/organizations/org-0 ```json { + "data": { + "id": "jr-00000002", + "request_type": "upgrade", + "prev_role": "editor", + "requested_role": "admin", + "status": "pending", + "created_at": "2025-08-14T11:00:00+08:00" + }, "success": true } ``` -## POST `/organizations/:id/invite-code` - 生成邀请码 +### POST `/organizations/:id/invite-code` - 重新生成邀请码 + +仅 owner / admin 可调用,会让旧邀请码失效。 **请求**: ```curl curl --location --request POST 'http://localhost:8080/api/v1/organizations/org-00000001/invite-code' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: ```json { - "data": { - "invite_code": "NEW1CODE" - }, + "data": { "invite_code": "NEW1CODE" }, "success": true } ``` -## GET `/organizations/:id/search-users` - 搜索可邀请用户 +--- + +## 成员管理 + +### GET `/organizations/:id/search-users` - 搜索可邀请的用户 + +仅 owner / admin 可调用。匹配用户名或邮箱,自动排除已在该组织内的用户。 **查询参数**: -- `keyword`: 用户名或邮箱关键字(可选) + +| 字段 | 类型 | 必填 | 说明 | +| ----- | ------ | ---- | ----------------------------- | +| q | string | 是 | 关键词(用户名或邮箱) | +| limit | int | 否 | 返回数量上限,默认 10 | **请求**: ```curl -curl --location 'http://localhost:8080/api/v1/organizations/org-00000001/search-users?keyword=zhang' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +curl --location 'http://localhost:8080/api/v1/organizations/org-00000001/search-users?q=zhang&limit=10' \ +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -522,23 +561,24 @@ curl --location 'http://localhost:8080/api/v1/organizations/org-00000001/search- { "id": "user-00000002", "username": "zhangsan", - "email": "zhangsan@example.com" - }, - { - "id": "user-00000003", - "username": "zhangwei", - "email": "zhangwei@example.com" + "email": "zhangsan@example.com", + "avatar": "" } ], "success": true } ``` -## POST `/organizations/:id/invite` - 邀请成员 +### POST `/organizations/:id/invite` - 直接邀请用户 -**请求参数**: -- `user_id`: 用户 ID(必填) -- `role`: 角色(必填) +仅 owner / admin 可调用,被邀请用户直接成为成员(无需审核)。 + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ------- | ------ | ---- | ------------------------------------------- | +| user_id | string | 是 | 被邀请用户的 ID | +| role | string | 是 | 角色:`viewer` / `editor` / `admin` | **请求**: @@ -555,19 +595,16 @@ curl --location 'http://localhost:8080/api/v1/organizations/org-00000001/invite' **响应**: ```json -{ - "success": true -} +{ "success": true, "message": "Member added successfully" } ``` -## GET `/organizations/:id/members` - 获取成员列表 +### GET `/organizations/:id/members` - 获取成员列表 **请求**: ```curl curl --location 'http://localhost:8080/api/v1/organizations/org-00000001/members' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -596,16 +633,27 @@ curl --location 'http://localhost:8080/api/v1/organizations/org-00000001/members "tenant_id": 2, "joined_at": "2025-08-13T09:00:00+08:00" } - ] + ], + "total": 2 }, "success": true } ``` -## PUT `/organizations/:id/members/:user_id` - 更新成员角色 +### PUT `/organizations/:id/members/:user_id` - 更新成员角色 -**请求参数**: -- `role`: 新角色(必填) +**路径参数**: + +| 字段 | 类型 | 说明 | +| -------- | ------ | -------------- | +| id | string | 组织 ID | +| user_id | string | 目标成员的用户 ID | + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ---- | ------ | ---- | ----------------------------------- | +| role | string | 是 | `viewer` / `editor` / `admin` | **请求**: @@ -613,47 +661,45 @@ curl --location 'http://localhost:8080/api/v1/organizations/org-00000001/members curl --location --request PUT 'http://localhost:8080/api/v1/organizations/org-00000001/members/user-00000002' \ --header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ ---data '{ - "role": "admin" -}' +--data '{ "role": "admin" }' ``` **响应**: ```json -{ - "success": true -} +{ "success": true } ``` -## DELETE `/organizations/:id/members/:user_id` - 移除成员 +### DELETE `/organizations/:id/members/:user_id` - 移除成员 + +仅 owner / admin 可调用,不能移除 owner。 **请求**: ```curl curl --location --request DELETE 'http://localhost:8080/api/v1/organizations/org-00000001/members/user-00000002' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: ```json -{ - "success": true -} +{ "success": true } ``` --- -## GET `/organizations/:id/join-requests` - 获取加入请求列表 +## 加入请求 + +### GET `/organizations/:id/join-requests` - 获取待审核申请列表 + +仅 owner / admin 可调用。仅返回 `status: pending` 的记录;`request_type` 区分新加入申请(`join`)与升级申请(`upgrade`)。 **请求**: ```curl curl --location 'http://localhost:8080/api/v1/organizations/org-00000001/join-requests' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -673,19 +719,44 @@ curl --location 'http://localhost:8080/api/v1/organizations/org-00000001/join-re "requested_role": "editor", "status": "pending", "created_at": "2025-08-14T10:00:00+08:00" + }, + { + "id": "jr-00000002", + "user_id": "user-00000002", + "username": "zhangsan", + "email": "zhangsan@example.com", + "message": "需要管理员权限来管理知识库共享", + "request_type": "upgrade", + "prev_role": "editor", + "requested_role": "admin", + "status": "pending", + "created_at": "2025-08-14T11:00:00+08:00" } - ] + ], + "total": 2 }, "success": true } ``` -## PUT `/organizations/:id/join-requests/:request_id/review` - 审核加入请求 +### PUT `/organizations/:id/join-requests/:request_id/review` - 审核加入申请 -**请求参数**: -- `approved`: 是否批准(必填,布尔值) -- `message`: 审核留言(可选) -- `role`: 分配角色(可选,批准时生效) +仅 owner / admin 可调用。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---------- | ------ | -------- | +| id | string | 组织 ID | +| request_id | string | 申请 ID | + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| -------- | ------ | ---- | --------------------------------------------------------------------- | +| approved | bool | 是 | 是否通过 | +| message | string | 否 | 审核留言(最多 500 字符) | +| role | string | 否 | 通过时强制分配的角色(缺省按申请者请求的角色),仅 `viewer/editor/admin` | **请求**: @@ -703,18 +774,23 @@ curl --location --request PUT 'http://localhost:8080/api/v1/organizations/org-00 **响应**: ```json -{ - "success": true -} +{ "success": true, "message": "Review completed" } ``` --- -## POST `/knowledge-bases/:id/shares` - 共享知识库 +## 知识库共享 -**请求参数**: -- `organization_id`: 目标组织 ID(必填) -- `permission`: 权限级别(必填) +### POST `/knowledge-bases/:id/shares` - 共享知识库到组织 + +**路径参数**: `id` = 知识库 ID + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ---------------- | ------ | ---- | ------------------------------- | +| organization_id | string | 是 | 目标组织 ID | +| permission | string | 是 | 共享权限:`viewer` / `editor` | **请求**: @@ -724,40 +800,36 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/shares --header 'Content-Type: application/json' \ --data '{ "organization_id": "org-00000001", - "permission": "read" + "permission": "viewer" }' ``` -**响应**: +**响应**(`201 Created`): ```json { "data": { "id": "kbs-00000001", "knowledge_base_id": "kb-00000001", - "knowledge_base_name": "技术文档库", "organization_id": "org-00000001", - "organization_name": "AI 技术团队", "shared_by_user_id": "user-00000001", - "shared_by_username": "admin", "source_tenant_id": 1, - "permission": "read", - "my_role_in_org": "owner", - "my_permission": "read", + "permission": "viewer", "created_at": "2025-08-15T10:00:00+08:00" }, "success": true } ``` -## GET `/knowledge-bases/:id/shares` - 获取知识库共享列表 +### GET `/knowledge-bases/:id/shares` - 获取知识库共享列表 + +返回该知识库被共享到的所有组织。 **请求**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/shares' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -770,26 +842,40 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/shares "id": "kbs-00000001", "knowledge_base_id": "kb-00000001", "knowledge_base_name": "技术文档库", + "knowledge_base_type": "document", + "knowledge_count": 12, + "chunk_count": 0, "organization_id": "org-00000001", "organization_name": "AI 技术团队", "shared_by_user_id": "user-00000001", "shared_by_username": "admin", "source_tenant_id": 1, - "permission": "read", + "permission": "viewer", "my_role_in_org": "owner", - "my_permission": "read", + "my_permission": "viewer", "created_at": "2025-08-15T10:00:00+08:00" } - ] + ], + "total": 1 }, "success": true } ``` -## PUT `/knowledge-bases/:id/shares/:share_id` - 更新共享权限 +### PUT `/knowledge-bases/:id/shares/:share_id` - 更新共享权限 -**请求参数**: -- `permission`: 新权限级别(必填) +**路径参数**: + +| 字段 | 类型 | 说明 | +| -------- | ------ | -------------- | +| id | string | 知识库 ID | +| share_id | string | 共享记录 ID | + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ---------- | ------ | ---- | ----------------------------- | +| permission | string | 是 | 新权限:`viewer` / `editor` | **请求**: @@ -797,44 +883,106 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/shares curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/shares/kbs-00000001' \ --header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ ---data '{ - "permission": "write" -}' +--data '{ "permission": "editor" }' ``` **响应**: ```json -{ - "success": true -} +{ "success": true } ``` -## DELETE `/knowledge-bases/:id/shares/:share_id` - 取消知识库共享 +### DELETE `/knowledge-bases/:id/shares/:share_id` - 取消知识库共享 **请求**: ```curl curl --location --request DELETE 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/shares/kbs-00000001' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: + +```json +{ "success": true } +``` + +### GET `/organizations/:id/shares` - 获取组织下被共享进来的知识库 + +仅本组织成员可查看。`my_permission = min(permission, my_role_in_org)`,即当前用户的有效权限。 + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/organizations/org-00000001/shares' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: 结构同 `GET /knowledge-bases/:id/shares`。 + +### GET `/organizations/:id/shared-knowledge-bases` - 组织内知识库空间视图 + +供"切换到某个空间后"的知识库列表页使用。返回该组织中所有当前用户可见的知识库,包含: + +1. 通过 `POST /knowledge-bases/:id/shares` 直接共享进来的知识库 +2. 通过共享智能体(`agents/:id/shares`)携带进来的知识库(只读,`source_from_agent` 字段标识来源) +3. 当前用户自己共享出去的知识库(`is_mine: true`) + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/organizations/org-00000001/shared-knowledge-bases' \ +--header 'X-API-Key: sk-xxxxx' ``` **响应**: ```json { + "data": [ + { + "knowledge_base": { + "id": "kb-00000001", + "name": "技术文档库", + "type": "document" + }, + "share_id": "kbs-00000001", + "organization_id": "org-00000001", + "org_name": "AI 技术团队", + "permission": "viewer", + "source_tenant_id": 1, + "shared_at": "2025-08-15T10:00:00+08:00", + "is_mine": false, + "source_from_agent": { + "agent_id": "agent-00000005", + "agent_name": "智能客服助手", + "kb_selection_mode": "selected" + } + } + ], + "total": 1, "success": true } ``` +`source_from_agent` 仅在该 KB 是通过共享智能体引入时出现;直接共享的 KB 不含该字段。 + --- -## POST `/agents/:id/shares` - 共享智能体 +## 智能体共享 -**请求参数**: -- `organization_id`: 目标组织 ID(必填) -- `permission`: 权限级别(必填) +### POST `/agents/:id/shares` - 共享智能体到组织 + +仅当前用户在目标组织中具有 editor 或 admin 角色时允许;智能体必须已完成配置(必填的对话模型、若启用了 knowledge_search 工具需配置重排模型等)。 + +**路径参数**: `id` = 智能体 ID + +**请求体**: + +| 字段 | 类型 | 必填 | 说明 | +| ---------------- | ------ | ---- | ------------------------------- | +| organization_id | string | 是 | 目标组织 ID | +| permission | string | 是 | 共享权限:`viewer` / `editor` | **请求**: @@ -844,38 +992,85 @@ curl --location 'http://localhost:8080/api/v1/agents/agent-00000001/shares' \ --header 'Content-Type: application/json' \ --data '{ "organization_id": "org-00000001", - "permission": "read" + "permission": "viewer" }' ``` +**响应**(`201 Created`): + +```json +{ + "data": { + "id": "as-00000001", + "agent_id": "agent-00000001", + "organization_id": "org-00000001", + "shared_by_user_id": "user-00000001", + "source_tenant_id": 1, + "permission": "viewer", + "created_at": "2025-08-15T11:00:00+08:00" + }, + "success": true +} +``` + +### GET `/agents/:id/shares` - 获取智能体共享列表 + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/agents/agent-00000001/shares' \ +--header 'X-API-Key: sk-xxxxx' +``` + **响应**: ```json { "data": { - "id": "as-00000001", - "agent_id": "agent-00000001", - "agent_name": "智能客服助手", - "organization_id": "org-00000001", - "organization_name": "AI 技术团队", - "shared_by_user_id": "user-00000001", - "shared_by_username": "admin", - "source_tenant_id": 1, - "permission": "read", - "created_at": "2025-08-15T11:00:00+08:00" + "shares": [ + { + "id": "as-00000001", + "agent_id": "agent-00000001", + "organization_id": "org-00000001", + "organization_name": "AI 技术团队", + "shared_by_user_id": "user-00000001", + "source_tenant_id": 1, + "permission": "viewer", + "created_at": "2025-08-15T11:00:00+08:00" + } + ], + "total": 1 }, "success": true } ``` -## GET `/agents/:id/shares` - 获取智能体共享列表 +### DELETE `/agents/:id/shares/:share_id` - 取消智能体共享 + +只有共享者本人或拥有相应权限的管理员可以取消。 **请求**: ```curl -curl --location 'http://localhost:8080/api/v1/agents/agent-00000001/shares' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +curl --location --request DELETE 'http://localhost:8080/api/v1/agents/agent-00000001/shares/as-00000001' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: + +```json +{ "success": true, "message": "Share removed successfully" } +``` + +### GET `/organizations/:id/agent-shares` - 获取组织下被共享进来的智能体 + +仅本组织成员可查看。返回结构在 `AgentShareResponse` 基础上额外补充 `my_role_in_org` / `my_permission`,并附带智能体的能力范围摘要(`scope_kb` / `scope_web_search` / `scope_mcp` 等)。 + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/organizations/org-00000001/agent-shares' \ +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -888,50 +1083,79 @@ curl --location 'http://localhost:8080/api/v1/agents/agent-00000001/shares' \ "id": "as-00000001", "agent_id": "agent-00000001", "agent_name": "智能客服助手", + "agent_avatar": "🤖", "organization_id": "org-00000001", "organization_name": "AI 技术团队", "shared_by_user_id": "user-00000001", "shared_by_username": "admin", "source_tenant_id": 1, - "permission": "read", - "created_at": "2025-08-15T11:00:00+08:00" + "permission": "viewer", + "my_role_in_org": "editor", + "my_permission": "viewer", + "created_at": "2025-08-15T11:00:00+08:00", + "scope_kb": "selected", + "scope_kb_count": 2, + "scope_web_search": true, + "scope_mcp": "none" } - ] + ], + "total": 1 }, "success": true } ``` -## DELETE `/agents/:id/shares/:share_id` - 取消智能体共享 +### GET `/organizations/:id/shared-agents` - 组织内智能体空间视图 + +供"切换到某个空间后"的智能体列表页使用。返回该组织中所有当前用户可见的智能体,包含他人共享与本人共享(`is_mine: true`)。 **请求**: ```curl -curl --location --request DELETE 'http://localhost:8080/api/v1/agents/agent-00000001/shares/as-00000001' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +curl --location 'http://localhost:8080/api/v1/organizations/org-00000001/shared-agents' \ +--header 'X-API-Key: sk-xxxxx' ``` **响应**: ```json { + "data": [ + { + "agent": { + "id": "agent-00000001", + "name": "智能客服助手" + }, + "share_id": "as-00000001", + "organization_id": "org-00000001", + "org_name": "AI 技术团队", + "permission": "viewer", + "source_tenant_id": 1, + "shared_at": "2025-08-15T11:00:00+08:00", + "shared_by_user_id": "user-00000001", + "shared_by_username": "admin", + "disabled_by_me": false, + "is_mine": false + } + ], + "total": 1, "success": true } ``` --- -## GET `/shared-knowledge-bases` - 获取共享知识库列表 +## 我的共享视图 -获取当前用户通过组织共享获得的所有知识库。 +### GET `/shared-knowledge-bases` - 获取共享给我的知识库(跨组织) + +返回当前用户通过所有组织(不限制 `:id`)共享获得的知识库列表,供"全部知识库"视图使用。 **请求**: ```curl curl --location 'http://localhost:8080/api/v1/shared-knowledge-bases' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -940,28 +1164,32 @@ curl --location 'http://localhost:8080/api/v1/shared-knowledge-bases' \ { "data": [ { + "knowledge_base": { + "id": "kb-00000001", + "name": "技术文档库" + }, "share_id": "kbs-00000001", "organization_id": "org-00000001", "org_name": "AI 技术团队", - "permission": "read", + "permission": "viewer", "source_tenant_id": 1, "shared_at": "2025-08-15T10:00:00+08:00" } ], + "total": 1, "success": true } ``` -## GET `/shared-agents` - 获取共享智能体列表 +### GET `/shared-agents` - 获取共享给我的智能体(跨组织) -获取当前用户通过组织共享获得的所有智能体。 +返回当前用户通过所有组织共享获得的智能体列表,供"全部智能体"视图使用。`disabled_by_me: true` 表示当前租户已在对话下拉中隐藏该智能体。 **请求**: ```curl curl --location 'http://localhost:8080/api/v1/shared-agents' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' +--header 'X-API-Key: sk-xxxxx' ``` **响应**: @@ -970,14 +1198,23 @@ curl --location 'http://localhost:8080/api/v1/shared-agents' \ { "data": [ { + "agent": { + "id": "agent-00000001", + "name": "智能客服助手" + }, "share_id": "as-00000001", "organization_id": "org-00000001", "org_name": "AI 技术团队", - "permission": "read", + "permission": "viewer", "source_tenant_id": 1, - "shared_at": "2025-08-15T11:00:00+08:00" + "shared_at": "2025-08-15T11:00:00+08:00", + "shared_by_user_id": "user-00000001", + "shared_by_username": "admin", + "disabled_by_me": false } ], + "total": 1, "success": true } ``` + diff --git a/docs/api/session.md b/docs/api/session.md index 3123a5539..86f871d27 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -2,20 +2,24 @@ [返回目录](./README.md) -| 方法 | 路径 | 描述 | -| ------ | --------------------------------------- | --------------------- | -| POST | `/sessions` | 创建会话 | -| GET | `/sessions/:id` | 获取会话详情 | -| GET | `/sessions` | 获取租户的会话列表 | -| PUT | `/sessions/:id` | 更新会话 | -| DELETE | `/sessions/:id` | 删除会话 | -| DELETE | `/sessions/:id/messages` | 清空会话消息 | -| DELETE | `/sessions/batch` | 批量删除会话 | -| POST | `/sessions/:session_id/generate_title` | 生成会话标题 | -| POST | `/sessions/:session_id/stop` | 停止生成 | -| GET | `/sessions/continue-stream/:session_id` | 继续未完成的流式响应 | +会话(Session)是纯粹的对话容器,仅存储基础信息(标题、描述、置顶状态等)。所有与知识库、模型、检索策略相关的配置均在查询时由 Custom Agent 提供,不再存储在会话中。 -> **说明**:会话(Session)是纯粹的对话容器,仅存储基础信息(标题、描述)。所有与知识库、模型、检索策略相关的配置均在查询时由 Custom Agent 提供,不再存储在会话中。 +| 方法 | 路径 | 描述 | +| ------ | ------------------------------------------ | ----------------------------- | +| POST | `/sessions` | 创建会话 | +| DELETE | `/sessions/batch` | 批量删除会话 | +| GET | `/sessions/:id` | 获取会话详情 | +| GET | `/sessions` | 获取当前租户的会话列表 | +| PUT | `/sessions/:id` | 更新会话 | +| DELETE | `/sessions/:id` | 删除会话 | +| DELETE | `/sessions/:id/messages` | 清空会话消息 | +| POST | `/sessions/:session_id/generate_title` | 生成会话标题 | +| POST | `/sessions/:session_id/stop` | 停止生成 | +| POST | `/sessions/:session_id/pin` | 置顶会话 | +| DELETE | `/sessions/:id/pin` | 取消置顶会话 | +| GET | `/sessions/continue-stream/:session_id` | 继续未完成的流式响应 | + +> **路由命名说明**:置顶接口的 POST 与 DELETE 使用了不同的路径参数名(POST 用 `:session_id`,DELETE 用 `:id`)。这是由于 gin 路由器为每个 HTTP 方法维护独立的 radix tree,且既有树中的通配符命名不同,必须保留以避免注册时的 `wildcard conflicts` panic。两者语义上都指会话 ID。 ## POST `/sessions` - 创建会话 @@ -33,122 +37,6 @@ curl --location 'http://localhost:8080/api/v1/sessions' \ **请求参数**: -| 字段 | 类型 | 必填 | 描述 | -| ------------- | ------ | ---- | ---------- | -| `title` | string | 否 | 会话标题 | -| `description` | string | 否 | 会话描述 | - -**响应**: - -```json -{ - "success": true, - "data": { - "id": "411d6b70-9a85-4d03-bb74-aab0fd8bd12f", - "title": "我的新对话", - "description": "关于 AI 的讨论", - "tenant_id": 1, - "created_at": "2026-03-27T12:26:19.611616+08:00", - "updated_at": "2026-03-27T12:26:19.611616+08:00", - "deleted_at": null - } -} -``` - -## GET `/sessions/:id` - 获取会话详情 - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/sessions/ceb9babb-1e30-41d7-817d-fd584954304b' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' -``` - -**路径参数**: - -| 字段 | 类型 | 必填 | 描述 | -| ---- | ------ | ---- | ------ | -| `id` | string | 是 | 会话ID | - -**响应**: - -```json -{ - "success": true, - "data": { - "id": "ceb9babb-1e30-41d7-817d-fd584954304b", - "title": "模型优化策略", - "description": "", - "tenant_id": 1, - "created_at": "2026-03-27T10:24:38.308596+08:00", - "updated_at": "2026-03-27T10:25:41.317761+08:00", - "deleted_at": null - } -} -``` - -## GET `/sessions?page=&page_size=` - 获取租户的会话列表 - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/sessions?page=1&page_size=10' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' -``` - -**查询参数**: - -| 字段 | 类型 | 必填 | 描述 | -| ----------- | ---- | ---- | -------------------- | -| `page` | int | 否 | 页码(默认 1) | -| `page_size` | int | 否 | 每页数量(默认 10) | - -**响应**: - -```json -{ - "success": true, - "data": [ - { - "id": "411d6b70-9a85-4d03-bb74-aab0fd8bd12f", - "title": "我的新对话", - "description": "", - "tenant_id": 1, - "created_at": "2026-03-27T12:26:19.611616+08:00", - "updated_at": "2026-03-27T12:26:19.611616+08:00", - "deleted_at": null - } - ], - "total": 1, - "page": 1, - "page_size": 10 -} -``` - -## PUT `/sessions/:id` - 更新会话 - -**请求**: - -```curl -curl --location --request PUT 'http://localhost:8080/api/v1/sessions/411d6b70-9a85-4d03-bb74-aab0fd8bd12f' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' \ ---data '{ - "title": "WeKnora 技术讨论", - "description": "关于 WeKnora 架构的讨论" -}' -``` - -**路径参数**: - -| 字段 | 类型 | 必填 | 描述 | -| ---- | ------ | ---- | ------ | -| `id` | string | 是 | 会话ID | - -**请求参数**: - | 字段 | 类型 | 必填 | 描述 | | ------------- | ------ | ---- | -------- | | `title` | string | 否 | 会话标题 | @@ -161,67 +49,19 @@ curl --location --request PUT 'http://localhost:8080/api/v1/sessions/411d6b70-9a "success": true, "data": { "id": "411d6b70-9a85-4d03-bb74-aab0fd8bd12f", - "title": "WeKnora 技术讨论", - "description": "关于 WeKnora 架构的讨论", + "title": "我的新对话", + "description": "关于 AI 的讨论", "tenant_id": 1, + "user_id": "u-001", + "is_pinned": false, "created_at": "2026-03-27T12:26:19.611616+08:00", - "updated_at": "2026-03-27T14:20:56.738424+08:00", + "updated_at": "2026-03-27T12:26:19.611616+08:00", "deleted_at": null } } ``` -## DELETE `/sessions/:id` - 删除会话 - -**请求**: - -```curl -curl --location --request DELETE 'http://localhost:8080/api/v1/sessions/411d6b70-9a85-4d03-bb74-aab0fd8bd12f' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' -``` - -**路径参数**: - -| 字段 | 类型 | 必填 | 描述 | -| ---- | ------ | ---- | ------ | -| `id` | string | 是 | 会话ID | - -**响应**: - -```json -{ - "success": true, - "message": "Session deleted successfully" -} -``` - -## DELETE `/sessions/:id/messages` - 清空会话消息 - -删除会话中的所有消息,同时清除 LLM 上下文和聊天历史知识库条目。会话本身保留。 - -**请求**: - -```curl -curl --location --request DELETE 'http://localhost:8080/api/v1/sessions/ceb9babb-1e30-41d7-817d-fd584954304b/messages' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' -``` - -**路径参数**: - -| 字段 | 类型 | 必填 | 描述 | -| ---- | ------ | ---- | ------ | -| `id` | string | 是 | 会话ID | - -**响应**: - -```json -{ - "success": true, - "message": "Session messages cleared successfully" -} -``` +> 通过 API-Key 调用时 `user_id` 可能为空,此时会话以租户级可见。 ## DELETE `/sessions/batch` - 批量删除会话 @@ -254,10 +94,10 @@ curl --location --request DELETE 'http://localhost:8080/api/v1/sessions/batch' \ **请求参数**: -| 字段 | 类型 | 必填 | 描述 | -| ------------ | -------- | ---- | -------------------------------------------------------- | -| `ids` | string[] | 否 | 要删除的会话 ID 列表(`delete_all` 为 false 时必填) | -| `delete_all` | bool | 否 | 设为 `true` 时删除当前租户的所有会话,忽略 `ids` 字段 | +| 字段 | 类型 | 必填 | 描述 | +| ------------ | -------- | ---- | ---------------------------------------------------------- | +| `ids` | string[] | 否 | 要删除的会话 ID 列表(`delete_all` 为 `false` 时必填) | +| `delete_all` | bool | 否 | 设为 `true` 时删除当前租户的所有会话,忽略 `ids` 字段 | **响应**: @@ -268,6 +108,198 @@ curl --location --request DELETE 'http://localhost:8080/api/v1/sessions/batch' \ } ``` +`delete_all=true` 时 `message` 为 `"All sessions deleted successfully"`。 + +## GET `/sessions/:id` - 获取会话详情 + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/sessions/ceb9babb-1e30-41d7-817d-fd584954304b' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' +``` + +**路径参数**: + +| 字段 | 类型 | 必填 | 描述 | +| ---- | ------ | ---- | ------- | +| `id` | string | 是 | 会话 ID | + +**响应**: + +```json +{ + "success": true, + "data": { + "id": "ceb9babb-1e30-41d7-817d-fd584954304b", + "title": "模型优化策略", + "description": "", + "tenant_id": 1, + "user_id": "u-001", + "is_pinned": true, + "pinned_at": "2026-04-01T09:12:33.123456+08:00", + "created_at": "2026-03-27T10:24:38.308596+08:00", + "updated_at": "2026-03-27T10:25:41.317761+08:00", + "deleted_at": null + } +} +``` + +会话不存在时返回 `404`。 + +## GET `/sessions` - 获取当前租户的会话列表 + +获取当前租户的会话列表,支持分页、关键字搜索、按来源 / Agent 过滤。 + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/sessions?page=1&page_size=10&keyword=AI&source=web' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' +``` + +**查询参数**: + +| 字段 | 类型 | 必填 | 描述 | +| ----------- | ------ | ---- | ----------------------------------------------------------------- | +| `page` | int | 否 | 页码(默认 1) | +| `page_size` | int | 否 | 每页数量(默认 10) | +| `keyword` | string | 否 | 按标题模糊匹配(ILIKE `%keyword%`) | +| `source` | string | 否 | 来源过滤:`web`(无 IM 映射)或 IM 平台名,如 `feishu`、`wechat`、`slack` | +| `agent_id` | string | 否 | 按 Agent 过滤(仅对 IM 会话生效) | + +**响应**: + +```json +{ + "success": true, + "data": [ + { + "id": "411d6b70-9a85-4d03-bb74-aab0fd8bd12f", + "title": "我的新对话", + "description": "", + "tenant_id": 1, + "user_id": "u-001", + "is_pinned": true, + "pinned_at": "2026-04-01T09:12:33.123456+08:00", + "created_at": "2026-03-27T12:26:19.611616+08:00", + "updated_at": "2026-03-27T12:26:19.611616+08:00", + "deleted_at": null, + "im_platform": "feishu", + "im_chat_id": "oc_xxx", + "im_agent_id": "agent-001" + } + ], + "total": 1, + "page": 1, + "page_size": 10 +} +``` + +> 列表项始终包含置顶状态字段,IM 来源相关字段(`im_platform`、`im_chat_id`、`im_thread_id`、`im_user_id`、`im_agent_id`、`im_channel_id`)仅对 IM 创建的会话填充,Web 会话省略。 + +## PUT `/sessions/:id` - 更新会话 + +**请求**: + +```curl +curl --location --request PUT 'http://localhost:8080/api/v1/sessions/411d6b70-9a85-4d03-bb74-aab0fd8bd12f' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "title": "WeKnora 技术讨论", + "description": "关于 WeKnora 架构的讨论" +}' +``` + +**路径参数**: + +| 字段 | 类型 | 必填 | 描述 | +| ---- | ------ | ---- | ------- | +| `id` | string | 是 | 会话 ID | + +**请求参数**: + +| 字段 | 类型 | 必填 | 描述 | +| ------------- | ------ | ---- | -------- | +| `title` | string | 否 | 会话标题 | +| `description` | string | 否 | 会话描述 | + +**响应**: + +```json +{ + "success": true, + "data": { + "id": "411d6b70-9a85-4d03-bb74-aab0fd8bd12f", + "title": "WeKnora 技术讨论", + "description": "关于 WeKnora 架构的讨论", + "tenant_id": 1, + "user_id": "u-001", + "is_pinned": false, + "created_at": "2026-03-27T12:26:19.611616+08:00", + "updated_at": "2026-03-27T14:20:56.738424+08:00", + "deleted_at": null + } +} +``` + +会话不存在时返回 `404`。 + +## DELETE `/sessions/:id` - 删除会话 + +**请求**: + +```curl +curl --location --request DELETE 'http://localhost:8080/api/v1/sessions/411d6b70-9a85-4d03-bb74-aab0fd8bd12f' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' +``` + +**路径参数**: + +| 字段 | 类型 | 必填 | 描述 | +| ---- | ------ | ---- | ------- | +| `id` | string | 是 | 会话 ID | + +**响应**: + +```json +{ + "success": true, + "message": "Session deleted successfully" +} +``` + +## DELETE `/sessions/:id/messages` - 清空会话消息 + +删除会话中的所有消息,同时清除 LLM 上下文和聊天历史知识库条目。会话本身保留。 + +**请求**: + +```curl +curl --location --request DELETE 'http://localhost:8080/api/v1/sessions/ceb9babb-1e30-41d7-817d-fd584954304b/messages' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' +``` + +**路径参数**: + +| 字段 | 类型 | 必填 | 描述 | +| ---- | ------ | ---- | ------- | +| `id` | string | 是 | 会话 ID | + +**响应**: + +```json +{ + "success": true, + "message": "Session messages cleared successfully" +} +``` + ## POST `/sessions/:session_id/generate_title` - 生成会话标题 根据消息内容自动生成会话标题。 @@ -294,9 +326,9 @@ curl --location 'http://localhost:8080/api/v1/sessions/ceb9babb-1e30-41d7-817d-f **路径参数**: -| 字段 | 类型 | 必填 | 描述 | -| ------------ | ------ | ---- | ------ | -| `session_id` | string | 是 | 会话ID | +| 字段 | 类型 | 必填 | 描述 | +| ------------ | ------ | ---- | ------- | +| `session_id` | string | 是 | 会话 ID | **请求参数**: @@ -315,7 +347,7 @@ curl --location 'http://localhost:8080/api/v1/sessions/ceb9babb-1e30-41d7-817d-f ## POST `/sessions/:session_id/stop` - 停止生成 -停止当前正在进行的生成任务。 +停止当前正在进行的助手回复生成任务。后端会向流中追加一条 `stop` 事件,由活跃的 SSE 处理协程感知并发起取消。 **请求**: @@ -330,15 +362,15 @@ curl --location 'http://localhost:8080/api/v1/sessions/7c966c74-610e-4516-8d5b-0 **路径参数**: -| 字段 | 类型 | 必填 | 描述 | -| ------------ | ------ | ---- | ------ | -| `session_id` | string | 是 | 会话ID | +| 字段 | 类型 | 必填 | 描述 | +| ------------ | ------ | ---- | ------- | +| `session_id` | string | 是 | 会话 ID | **请求参数**: -| 字段 | 类型 | 必填 | 描述 | -| ------------ | ------ | ---- | ------------------------ | -| `message_id` | string | 是 | 要停止生成的助手消息 ID | +| 字段 | 类型 | 必填 | 描述 | +| ------------ | ------ | ---- | ----------------------- | +| `message_id` | string | 是 | 要停止生成的助手消息 ID | **响应**: @@ -349,14 +381,87 @@ curl --location 'http://localhost:8080/api/v1/sessions/7c966c74-610e-4516-8d5b-0 } ``` +若消息已完成(无需停止): + +```json +{ + "success": true, + "message": "Message already completed" +} +``` + +> 消息不属于当前会话返回 `403`;消息或会话不存在返回 `404`。 + +## POST `/sessions/:session_id/pin` - 置顶会话 + +将指定会话置顶(用户维度)。 + +**请求**: + +```curl +curl --location --request POST 'http://localhost:8080/api/v1/sessions/ceb9babb-1e30-41d7-817d-fd584954304b/pin' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**路径参数**: + +| 字段 | 类型 | 必填 | 描述 | +| ------------ | ------ | ---- | ------- | +| `session_id` | string | 是 | 会话 ID | + +**响应**: + +```json +{ + "success": true, + "is_pinned": true +} +``` + +会话不存在或对当前用户不可见时返回 `404`。 + +## DELETE `/sessions/:id/pin` - 取消置顶会话 + +取消指定会话的置顶。 + +**请求**: + +```curl +curl --location --request DELETE 'http://localhost:8080/api/v1/sessions/ceb9babb-1e30-41d7-817d-fd584954304b/pin' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**路径参数**: + +| 字段 | 类型 | 必填 | 描述 | +| ---- | ------ | ---- | ------- | +| `id` | string | 是 | 会话 ID | + +**响应**: + +```json +{ + "success": true, + "is_pinned": false +} +``` + +> 同上,`POST /pin` 与 `DELETE /pin` 路径参数名不同,但语义上都是会话 ID。 + ## GET `/sessions/continue-stream/:session_id` - 继续未完成的流式响应 -重新连接正在进行的流式响应,先回放已有事件,再继续接收新事件。 +用于在 SSE 连接断开后重新连接正在进行的流式响应:先回放该消息已产生的所有事件,再继续推送后续事件,直至 `complete`。 + +**路径参数**: + +| 字段 | 类型 | 必填 | 描述 | +| ------------ | ------ | ---- | ------- | +| `session_id` | string | 是 | 会话 ID | **查询参数**: -| 字段 | 类型 | 必填 | 描述 | -| ------------ | ------ | ---- | ------------------------------------------------------------------------- | +| 字段 | 类型 | 必填 | 描述 | +| ------------ | ------ | ---- | ------------------------------------------------------------------------------- | | `message_id` | string | 是 | 从 `/messages/:session_id/load` 接口中获取的 `is_completed` 为 `false` 的消息 ID | **请求**: @@ -369,4 +474,4 @@ curl --location 'http://localhost:8080/api/v1/sessions/continue-stream/ceb9babb- **响应格式**: -服务器端事件流(Server-Sent Events),与 `/knowledge-chat/:session_id` 返回结果一致。 +服务器端事件流(Server-Sent Events),事件结构与 `/knowledge-chat/:session_id`、`/agent-chat/:session_id` 返回结果一致。若该消息当前在流中已无事件返回 `404 No stream events found`;若消息记录不存在返回 `404 Incomplete message not found`。 diff --git a/docs/api/system.md b/docs/api/system.md index dc1b1cddc..3d3fc4784 100644 --- a/docs/api/system.md +++ b/docs/api/system.md @@ -10,7 +10,6 @@ | POST | `/system/docreader/reconnect` | 重连文档解析服务 | | GET | `/system/storage-engine-status` | 获取存储引擎状态 | | POST | `/system/storage-engine-check` | 检查存储引擎连通性 | -| GET | `/system/minio/buckets` | 获取 MinIO 桶列表 | ## GET `/system/info` - 获取系统信息 @@ -201,34 +200,4 @@ curl --location 'http://localhost:8080/api/v1/system/storage-engine-check' \ } ``` -## GET `/system/minio/buckets` - 获取 MinIO 桶列表 -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/system/minio/buckets' \ ---header 'X-API-Key: sk-xxxxx' \ ---header 'Content-Type: application/json' -``` - -**响应**: - -```json -{ - "data": { - "buckets": [ - { - "name": "weknora", - "policy": "read-write", - "created_at": "2025-08-01T10:00:00+08:00" - }, - { - "name": "weknora-backup", - "policy": "read-only", - "created_at": "2025-08-05T14:00:00+08:00" - } - ] - }, - "success": true -} -``` diff --git a/docs/api/tag.md b/docs/api/tag.md index 43c2c0fcc..53f84bc22 100644 --- a/docs/api/tag.md +++ b/docs/api/tag.md @@ -20,7 +20,7 @@ ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/tags?page=1&page_size=10' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' ``` @@ -65,11 +65,25 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/tags?p ## POST `/knowledge-bases/:id/tags` - 创建标签 +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | ----------- | +| id | string | 知识库 ID | + +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ---------- | ------ | ---- | ------------------------ | +| name | string | 是 | 标签名(同库内唯一) | +| color | string | 否 | 标签颜色(CSS 颜色字符串) | +| sort_order | int | 否 | 排序值(数值越小越靠前) | + **请求**: ```curl curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/tags' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "name": "产品手册", @@ -98,11 +112,20 @@ curl --location 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/tags' ## PUT `/knowledge-bases/:id/tags/:tag_id` - 更新标签 +**路径参数**: + +| 字段 | 类型 | 说明 | +| ------ | ------ | ----------- | +| id | string | 知识库 ID | +| tag_id | string | 标签 ID | + +**参数说明(请求体)**: 同创建接口,所有字段均可选;未传则保留原值。 + **请求**: ```curl curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/tags/tag-00000003' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ "name": "产品手册更新", @@ -130,14 +153,24 @@ curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-0 ## DELETE `/knowledge-bases/:id/tags/:tag_id` - 删除标签 +**路径参数**: + +| 字段 | 类型 | 说明 | +| ------ | ------ | -------- | +| id | string | 知识库 ID | +| tag_id | string | 标签 ID | + **查询参数**: -- `force`: 设置为 `true` 时强制删除(即使标签被引用) + +| 字段 | 类型 | 默认 | 说明 | +| ----- | ------- | ----- | --------------------------------------------- | +| force | boolean | false | 设置为 `true` 时强制删除(即使标签被引用) | **请求**: ```curl curl --location --request DELETE 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/tags/tag-00000003?force=true' \ ---header 'X-API-Key: sk-vQHV2NZI_LK5W7wHQvH3yGYExX8YnhaHwZipUYbiZKCYJbBQ' \ +--header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' ``` diff --git a/docs/api/tenant.md b/docs/api/tenant.md index fd8b912bb..91e8ddadb 100644 --- a/docs/api/tenant.md +++ b/docs/api/tenant.md @@ -2,249 +2,23 @@ [返回目录](./README.md) -| 方法 | 路径 | 描述 | -| ------ | -------------- | --------------------- | -| POST | `/tenants` | 创建新租户 | -| GET | `/tenants/:id` | 获取指定租户信息 | -| PUT | `/tenants/:id` | 更新租户信息 | -| DELETE | `/tenants/:id` | 删除租户 | -| GET | `/tenants` | 获取租户列表 | -| GET | `/tenants/all` | 获取所有租户列表(需跨租户权限) | -| GET | `/tenants/search` | 搜索租户(需跨租户权限) | -| GET | `/tenants/kv/:key` | 获取租户KV配置 | -| PUT | `/tenants/kv/:key` | 更新租户KV配置 | +包含两组接口: +- 租户 CRUD(`/tenants`、`/tenants/:id`、`/tenants/:id/api-key`):当前认证用户对自己所属租户进行管理;跨租户访问需要管理员权限。 +- 跨租户接口(`/tenants/all`、`/tenants/search`):**需要服务端启用 `EnableCrossTenantAccess` 且当前用户具备 `CanAccessAllTenants` 权限**,否则返回 403。 +- 租户 KV 配置(`/tenants/kv/:key`):当前租户级别的通用配置项,**`tenant_id` 从认证上下文中获取,不在 URL 中传入**。 -## POST `/tenants` - 创建新租户 - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/tenants' \ ---header 'Content-Type: application/json' \ ---data '{ - "name": "weknora", - "description": "weknora tenants", - "business": "wechat", - "retriever_engines": { - "engines": [ - { - "retriever_type": "keywords", - "retriever_engine_type": "postgres" - }, - { - "retriever_type": "vector", - "retriever_engine_type": "postgres" - } - ] - } -}' -``` - -**响应**: - -```json -{ - "data": { - "id": 10000, - "name": "weknora", - "description": "weknora tenants", - "api_key": "sk-aaLRAgvCRJcmtiL2vLMeB1FB5UV0Q-qB7DlTE1pJ9KA93XZG", - "status": "active", - "retriever_engines": { - "engines": [ - { - "retriever_engine_type": "postgres", - "retriever_type": "keywords" - }, - { - "retriever_engine_type": "postgres", - "retriever_type": "vector" - } - ] - }, - "business": "wechat", - "storage_quota": 10737418240, - "storage_used": 0, - "created_at": "2025-08-11T20:37:28.396980093+08:00", - "updated_at": "2025-08-11T20:37:28.396980301+08:00", - "deleted_at": null - }, - "success": true -} -``` - -## GET `/tenants/:id` - 获取指定租户信息 - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/tenants/10000' \ ---header 'Content-Type: application/json' \ ---header 'X-API-Key: sk-aaLRAgvCRJcmtiL2vLMeB1FB5UV0Q-qB7DlTE1pJ9KA93XZG' -``` - -**响应**: - -```json -{ - "data": { - "id": 10000, - "name": "weknora", - "description": "weknora tenants", - "api_key": "sk-aaLRAgvCRJcmtiL2vLMeB1FB5UV0Q-qB7DlTE1pJ9KA93XZG", - "status": "active", - "retriever_engines": { - "engines": [ - { - "retriever_engine_type": "postgres", - "retriever_type": "keywords" - }, - { - "retriever_engine_type": "postgres", - "retriever_type": "vector" - } - ] - }, - "business": "wechat", - "storage_quota": 10737418240, - "storage_used": 0, - "created_at": "2025-08-11T20:37:28.39698+08:00", - "updated_at": "2025-08-11T20:37:28.405693+08:00", - "deleted_at": null - }, - "success": true -} -``` - -## PUT `/tenants/:id` - 更新租户信息 - -注意 API Key 会变更 - -**请求**: - -```curl -curl --location --request PUT 'http://localhost:8080/api/v1/tenants/10000' \ ---header 'Content-Type: application/json' \ ---header 'X-API-Key: sk-KREi84yPtahKxMtIMOW-Cxx2dxb9xROpUuDSpi3vbiC1QVDe' \ ---data '{ - "name": "weknora new", - "description": "weknora tenants new", - "status": "active", - "retriever_engines": { - "engines": [ - { - "retriever_engine_type": "postgres", - "retriever_type": "keywords" - }, - { - "retriever_engine_type": "postgres", - "retriever_type": "vector" - } - ] - }, - "business": "wechat", - "storage_quota": 10737418240 -}' -``` - -**响应**: - -```json -{ - "data": { - "id": 10000, - "name": "weknora new", - "description": "weknora tenants new", - "api_key": "sk-IKtd9JGV4-aPGQ6RiL8YJu9Vzb3-ae4lgFkjFJZmhvUn2mLu", - "status": "active", - "retriever_engines": { - "engines": [ - { - "retriever_engine_type": "postgres", - "retriever_type": "keywords" - }, - { - "retriever_engine_type": "postgres", - "retriever_type": "vector" - } - ] - }, - "business": "wechat", - "storage_quota": 10737418240, - "storage_used": 0, - "created_at": "0001-01-01T00:00:00Z", - "updated_at": "2025-08-11T20:49:02.13421034+08:00", - "deleted_at": null - }, - "success": true -} -``` - -## DELETE `/tenants/:id` - 删除租户 - -**请求**: - -```curl -curl --location --request DELETE 'http://localhost:8080/api/v1/tenants/10000' \ ---header 'Content-Type: application/json' \ ---header 'X-API-Key: sk-IKtd9JGV4-aPGQ6RiL8YJu9Vzb3-ae4lgFkjFJZmhvUn2mLu' -``` - -**响应**: - -```json -{ - "message": "Tenant deleted successfully", - "success": true -} -``` - -## GET `/tenants` - 获取租户列表 - -**请求**: - -```curl -curl --location 'http://localhost:8080/api/v1/tenants' \ ---header 'Content-Type: application/json' \ ---header 'X-API-Key: sk-An7_t_izCKFIJ4iht9Xjcjnj_MC48ILvwezEDki9ScfIa7KA' -``` - -**响应**: - -```json -{ - "data": { - "items": [ - { - "id": 10002, - "name": "weknora", - "description": "weknora tenants", - "api_key": "sk-An7_t_izCKFIJ4iht9Xjcjnj_MC48ILvwezEDki9ScfIa7KA", - "status": "active", - "retriever_engines": { - "engines": [ - { - "retriever_engine_type": "postgres", - "retriever_type": "keywords" - }, - { - "retriever_engine_type": "postgres", - "retriever_type": "vector" - } - ] - }, - "business": "wechat", - "storage_quota": 10737418240, - "storage_used": 0, - "created_at": "2025-08-11T20:52:58.05679+08:00", - "updated_at": "2025-08-11T20:52:58.060495+08:00", - "deleted_at": null - } - ] - }, - "success": true -} -``` +| 方法 | 路径 | 描述 | +| ------ | -------------------------- | ------------------------------------------------- | +| GET | `/tenants/all` | 获取所有租户列表(需跨租户权限) | +| GET | `/tenants/search` | 分页搜索租户(需跨租户权限) | +| POST | `/tenants` | 创建新租户 | +| GET | `/tenants/:id` | 获取指定租户信息 | +| PUT | `/tenants/:id` | 更新租户信息 | +| DELETE | `/tenants/:id` | 删除租户 | +| POST | `/tenants/:id/api-key` | 重置租户 API Key | +| GET | `/tenants` | 获取当前用户可见的租户列表 | +| GET | `/tenants/kv/:key` | 获取当前租户的 KV 配置(tenant 由认证上下文确定) | +| PUT | `/tenants/kv/:key` | 更新当前租户的 KV 配置(tenant 由认证上下文确定) | ## GET `/tenants/all` - 获取所有租户列表 @@ -330,56 +104,41 @@ curl --location 'http://localhost:8080/api/v1/tenants/search?keyword=weknora&pag } ``` -## GET `/tenants/kv/:key` - 获取租户KV配置 +## POST `/tenants` - 创建新租户 -获取指定键名的租户配置项。 +创建一个新的租户,服务端会自动生成租户 ID 与 API Key。 -**支持的 key 值**: -- `agent-config`: Agent 配置 -- `web-search-config`: 网页搜索配置 -- `conversation-config`: 对话配置 -- `prompt-templates`: 提示词模板 -- `parser-engine-config`: 解析引擎配置 -- `storage-engine-config`: 存储引擎配置 -- `chat-history-config`: 聊天历史配置 -- `retrieval-config`: 检索配置 +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------------- | ------ | ---- | ------------------------------------------------------ | +| name | string | 是 | 租户名称 | +| description | string | 否 | 租户描述 | +| business | string | 否 | 业务标识(如 `wechat`) | +| retriever_engines | object | 否 | 检索引擎组合配置(`engines` 数组:每项含 `retriever_type` 与 `retriever_engine_type`) | +| storage_quota | int | 否 | 存储配额(字节) | **请求**: ```curl -curl --location 'http://localhost:8080/api/v1/tenants/kv/agent-config' \ +curl --location 'http://localhost:8080/api/v1/tenants' \ --header 'Content-Type: application/json' \ ---header 'X-API-Key: sk-An7_t_izCKFIJ4iht9Xjcjnj_MC48ILvwezEDki9ScfIa7KA' -``` - -**响应**: - -```json -{ - "data": { - "key": "agent-config", - "value": { - "enabled": true, - "max_iterations": 10 - } - }, - "success": true -} -``` - -## PUT `/tenants/kv/:key` - 更新租户KV配置 - -更新指定键名的租户配置项。请求体内容根据不同的 key 值而有所不同。 - -**请求**: - -```curl -curl --location --request PUT 'http://localhost:8080/api/v1/tenants/kv/agent-config' \ ---header 'Content-Type: application/json' \ ---header 'X-API-Key: sk-An7_t_izCKFIJ4iht9Xjcjnj_MC48ILvwezEDki9ScfIa7KA' \ --data '{ - "enabled": true, - "max_iterations": 20 + "name": "weknora", + "description": "weknora tenants", + "business": "wechat", + "retriever_engines": { + "engines": [ + { + "retriever_type": "keywords", + "retriever_engine_type": "postgres" + }, + { + "retriever_type": "vector", + "retriever_engine_type": "postgres" + } + ] + } }' ``` @@ -388,12 +147,364 @@ curl --location --request PUT 'http://localhost:8080/api/v1/tenants/kv/agent-con ```json { "data": { - "key": "agent-config", - "value": { - "enabled": true, - "max_iterations": 20 - } + "id": 10000, + "name": "weknora", + "description": "weknora tenants", + "api_key": "sk-aaLRAgvCRJcmtiL2vLMeB1FB5UV0Q-qB7DlTE1pJ9KA93XZG", + "status": "active", + "retriever_engines": { + "engines": [ + { + "retriever_engine_type": "postgres", + "retriever_type": "keywords" + }, + { + "retriever_engine_type": "postgres", + "retriever_type": "vector" + } + ] + }, + "business": "wechat", + "storage_quota": 10737418240, + "storage_used": 0, + "created_at": "2025-08-11T20:37:28.396980093+08:00", + "updated_at": "2025-08-11T20:37:28.396980301+08:00", + "deleted_at": null }, "success": true } ``` + +## GET `/tenants/:id` - 获取指定租户信息 + +获取指定 ID 的租户详情。只能访问自己所属租户;访问其他租户需要跨租户权限,否则返回 403。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ---- | ------- | +| id | int | 租户 ID | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/tenants/10000' \ +--header 'X-API-Key: sk-aaLRAgvCRJcmtiL2vLMeB1FB5UV0Q-qB7DlTE1pJ9KA93XZG' \ +--header 'Content-Type: application/json' +``` + +**响应**: + +```json +{ + "data": { + "id": 10000, + "name": "weknora", + "description": "weknora tenants", + "api_key": "sk-aaLRAgvCRJcmtiL2vLMeB1FB5UV0Q-qB7DlTE1pJ9KA93XZG", + "status": "active", + "retriever_engines": { + "engines": [ + { + "retriever_engine_type": "postgres", + "retriever_type": "keywords" + }, + { + "retriever_engine_type": "postgres", + "retriever_type": "vector" + } + ] + }, + "business": "wechat", + "storage_quota": 10737418240, + "storage_used": 0, + "created_at": "2025-08-11T20:37:28.39698+08:00", + "updated_at": "2025-08-11T20:37:28.405693+08:00", + "deleted_at": null + }, + "success": true +} +``` + +## PUT `/tenants/:id` - 更新租户信息 + +更新指定租户的基础信息。访问规则同 `GET /tenants/:id`。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ---- | ------- | +| id | int | 租户 ID | + +**参数说明(请求体)**: 与 `POST /tenants` 相同字段;未传字段保持原值。 + +**请求**: + +```curl +curl --location --request PUT 'http://localhost:8080/api/v1/tenants/10000' \ +--header 'X-API-Key: sk-aaLRAgvCRJcmtiL2vLMeB1FB5UV0Q-qB7DlTE1pJ9KA93XZG' \ +--header 'Content-Type: application/json' \ +--data '{ + "name": "weknora new", + "description": "weknora tenants new", + "status": "active", + "retriever_engines": { + "engines": [ + { + "retriever_engine_type": "postgres", + "retriever_type": "keywords" + }, + { + "retriever_engine_type": "postgres", + "retriever_type": "vector" + } + ] + }, + "business": "wechat", + "storage_quota": 10737418240 +}' +``` + +**响应**: + +```json +{ + "data": { + "id": 10000, + "name": "weknora new", + "description": "weknora tenants new", + "api_key": "sk-aaLRAgvCRJcmtiL2vLMeB1FB5UV0Q-qB7DlTE1pJ9KA93XZG", + "status": "active", + "retriever_engines": { + "engines": [ + { + "retriever_engine_type": "postgres", + "retriever_type": "keywords" + }, + { + "retriever_engine_type": "postgres", + "retriever_type": "vector" + } + ] + }, + "business": "wechat", + "storage_quota": 10737418240, + "storage_used": 0, + "created_at": "2025-08-11T20:37:28.39698+08:00", + "updated_at": "2025-08-11T20:49:02.13421034+08:00", + "deleted_at": null + }, + "success": true +} +``` + +## DELETE `/tenants/:id` - 删除租户 + +删除指定租户。访问规则同 `GET /tenants/:id`。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ---- | ------- | +| id | int | 租户 ID | + +**请求**: + +```curl +curl --location --request DELETE 'http://localhost:8080/api/v1/tenants/10000' \ +--header 'X-API-Key: sk-aaLRAgvCRJcmtiL2vLMeB1FB5UV0Q-qB7DlTE1pJ9KA93XZG' \ +--header 'Content-Type: application/json' +``` + +**响应**: + +```json +{ + "message": "Tenant deleted successfully", + "success": true +} +``` + +## POST `/tenants/:id/api-key` - 重置租户 API Key + +为指定租户生成新的 API Key,旧 Key 立即失效。访问规则同 `GET /tenants/:id`。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ---- | ------- | +| id | int | 租户 ID | + +**请求**: + +```curl +curl --location --request POST 'http://localhost:8080/api/v1/tenants/10000/api-key' \ +--header 'X-API-Key: sk-aaLRAgvCRJcmtiL2vLMeB1FB5UV0Q-qB7DlTE1pJ9KA93XZG' \ +--header 'Content-Type: application/json' +``` + +**响应**: + +```json +{ + "data": { + "api_key": "sk-IKtd9JGV4-aPGQ6RiL8YJu9Vzb3-ae4lgFkjFJZmhvUn2mLu" + }, + "success": true +} +``` + +## GET `/tenants` - 获取租户列表 + +返回当前认证上下文对应的租户(普通用户为单条;管理员仍只返回自身租户)。 + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/tenants' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' +``` + +**响应**: + +```json +{ + "data": { + "items": [ + { + "id": 10002, + "name": "weknora", + "description": "weknora tenants", + "api_key": "sk-An7_t_izCKFIJ4iht9Xjcjnj_MC48ILvwezEDki9ScfIa7KA", + "status": "active", + "retriever_engines": { + "engines": [ + { + "retriever_engine_type": "postgres", + "retriever_type": "keywords" + }, + { + "retriever_engine_type": "postgres", + "retriever_type": "vector" + } + ] + }, + "business": "wechat", + "storage_quota": 10737418240, + "storage_used": 0, + "created_at": "2025-08-11T20:52:58.05679+08:00", + "updated_at": "2025-08-11T20:52:58.060495+08:00", + "deleted_at": null + } + ] + }, + "success": true +} +``` + +## GET `/tenants/kv/:key` - 获取租户 KV 配置 + +获取当前租户的 KV 配置项。**租户 ID 从认证上下文中获取**(即由 `X-API-Key` / Bearer Token 对应的租户决定),URL 中不需要也不接受 tenant_id。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | ---------------------------------------------- | +| key | string | 配置键名(见下方支持的 key 列表,不支持的键返回 400) | + +**支持的 key 值**: + +| key | 说明 | +| ---------------------- | ----------------------------- | +| `agent-config` | Agent 配置(最大迭代次数、温度、System Prompt、可用工具等) | +| `web-search-config` | 网页搜索配置 | +| `conversation-config` | 普通模式会话/对话配置 | +| `prompt-templates` | 系统提示词模板(只读,按用户语言本地化) | +| `parser-engine-config` | 解析引擎配置(如 MinerU) | +| `storage-engine-config`| 存储引擎配置(Local/MinIO/COS) | +| `chat-history-config` | 聊天历史索引配置 | +| `retrieval-config` | 全局检索配置 | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/tenants/kv/agent-config' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' +``` + +**响应(以 `agent-config` 为例)**: + +```json +{ + "data": { + "max_iterations": 10, + "allowed_tools": ["knowledge_search", "web_search"], + "temperature": 0.3, + "system_prompt": "...", + "use_custom_system_prompt": false, + "available_tools": [ + { "name": "knowledge_search", "label": "知识库检索", "description": "..." } + ], + "available_placeholders": [ + { "name": "web_search_status", "label": "联网搜索状态", "description": "..." } + ] + }, + "success": true +} +``` + +失败时(不支持的键): + +```json +{ "success": false, "error": "unsupported key" } +``` + +## PUT `/tenants/kv/:key` - 更新租户 KV 配置 + +更新当前租户的 KV 配置项。**租户 ID 从认证上下文中获取**,请求体结构按 `key` 不同而异。`prompt-templates` 为只读,不支持 PUT。 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | ----------------------------- | +| key | string | 配置键名(见 GET 接口的支持列表,`prompt-templates` 除外) | + +**请求(以 `agent-config` 为例)**: + +```curl +curl --location --request PUT 'http://localhost:8080/api/v1/tenants/kv/agent-config' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "max_iterations": 20, + "temperature": 0.3, + "system_prompt": "" +}' +``` + +**响应**: + +```json +{ + "data": { + "max_iterations": 20, + "allowed_tools": ["knowledge_search", "web_search"], + "temperature": 0.3, + "system_prompt": "", + "use_custom_system_prompt": false + }, + "message": "Agent configuration updated successfully", + "success": true +} +``` + +**约束**: + +- `agent-config`: `max_iterations` 取值范围 `(0, 30]`;`temperature` 取值范围 `[0, 2]`。 +- `web-search-config`: `max_results` 取值范围 `[1, 50]`。 +- `conversation-config`: 包含多项阈值校验(如 `keyword_threshold` / `vector_threshold` ∈ `[0, 1]`,`rerank_threshold` ∈ `[-10, 10]`,`temperature` ∈ `[0, 2]`,`max_completion_tokens` ∈ `[1, 100000]` 等)。 +- `retrieval-config`: `embedding_top_k` / `rerank_top_k` ∈ `[0, 200]`;阈值范围同上。 +- `storage-engine-config`: `default_provider` 必须在 `STORAGE_ALLOW_LIST` 允许的列表内。 +- `chat-history-config`: 启用且设置了 `embedding_model_id` 而尚未关联知识库时,会自动创建一个隐藏知识库并将其 ID 写入配置。 diff --git a/docs/api/vector-store.md b/docs/api/vector-store.md index 2ad6ef0e6..08029afef 100644 --- a/docs/api/vector-store.md +++ b/docs/api/vector-store.md @@ -2,24 +2,22 @@ [返回目录](./README.md) -向量存储(VectorStore)API 用于管理向量数据库连接配置。支持 Elasticsearch、PostgreSQL、Qdrant、Milvus、Weaviate、Tencent VectorDB、SQLite 七种引擎类型。 +向量存储(VectorStore)API 用于管理租户的向量数据库连接配置,支持 Elasticsearch、PostgreSQL、Qdrant、Milvus、Weaviate、Tencent VectorDB、SQLite 等引擎。接口同时管理用户在 DB 中创建的配置(`source: "user"`)以及通过 `RETRIEVE_DRIVER` 环境变量配置的虚拟存储(`source: "env"`,只读)。 -| 方法 | 路径 | 描述 | -| ------ | ---------------------------- | ------------------------------ | -| GET | `/vector-stores/types` | 获取支持的引擎类型及配置字段 | -| POST | `/vector-stores/test` | 使用原始凭据测试连接(不保存) | -| POST | `/vector-stores` | 创建向量存储 | -| GET | `/vector-stores` | 获取向量存储列表 | -| GET | `/vector-stores/:id` | 获取向量存储详情 | -| PUT | `/vector-stores/:id` | 更新向量存储(仅名称可修改) | -| DELETE | `/vector-stores/:id` | 删除向量存储(软删除) | -| POST | `/vector-stores/:id/test` | 测试已保存的向量存储连接 | - ---- +| 方法 | 路径 | 描述 | +| ------ | ---------------------------- | -------------------------------- | +| GET | `/vector-stores/types` | 获取支持的引擎类型及字段元数据 | +| POST | `/vector-stores/test` | 使用原始凭据测试连接(不落库) | +| POST | `/vector-stores` | 创建向量存储 | +| GET | `/vector-stores` | 获取向量存储列表 | +| GET | `/vector-stores/:id` | 获取向量存储详情 | +| PUT | `/vector-stores/:id` | 更新向量存储(仅名称可改) | +| DELETE | `/vector-stores/:id` | 删除向量存储(软删除) | +| POST | `/vector-stores/:id/test` | 测试已保存或环境变量存储的连通性 | ## GET `/vector-stores/types` - 获取支持的引擎类型 -返回所有支持的引擎类型,包含连接配置和索引配置的字段定义,可用于前端表单生成。 +返回所有支持的引擎类型及其连接配置字段、索引配置字段的定义,可用于前端动态表单生成。系统级元数据,无需鉴权感知,但仍需 `X-API-Key`。 **请求**: @@ -38,40 +36,45 @@ curl --location 'http://localhost:8080/api/v1/vector-stores/types' \ "type": "elasticsearch", "display_name": "Elasticsearch (Keywords + Vector)", "connection_fields": [ - {"name": "addr", "type": "string", "required": true, "description": "Elasticsearch URL (e.g., http://localhost:9200)"}, - {"name": "username", "type": "string", "required": false}, - {"name": "password", "type": "string", "required": false, "sensitive": true} + { "name": "addr", "type": "string", "required": true, "description": "Elasticsearch URL (e.g., http://localhost:9200)" }, + { "name": "username", "type": "string", "required": false }, + { "name": "password", "type": "string", "required": false, "sensitive": true } ], "index_fields": [ - {"name": "index_name", "type": "string", "required": false, "default": "xwrag_default"}, - {"name": "number_of_shards", "type": "number", "required": false}, - {"name": "number_of_replicas", "type": "number", "required": false} + { "name": "index_name", "type": "string", "required": false, "default": "xwrag_default" }, + { "name": "number_of_shards", "type": "number", "required": false }, + { "name": "number_of_replicas", "type": "number", "required": false } ] }, { "type": "postgres", "display_name": "PostgreSQL (Keywords + Vector)", "connection_fields": [ - {"name": "use_default_connection", "type": "boolean", "required": false, "default": true, "description": "Use the application's default database connection"}, - {"name": "addr", "type": "string", "required": false, "description": "PostgreSQL connection string (required if use_default_connection is false)"}, - {"name": "username", "type": "string", "required": false}, - {"name": "password", "type": "string", "required": false, "sensitive": true} + { "name": "use_default_connection", "type": "boolean", "required": false, "default": true, "description": "Use the application's default database connection" }, + { "name": "addr", "type": "string", "required": false, "description": "PostgreSQL connection string (required if use_default_connection is false)" }, + { "name": "username", "type": "string", "required": false }, + { "name": "password", "type": "string", "required": false, "sensitive": true } ] } ] } ``` ---- +## POST `/vector-stores/test` - 使用原始凭据测试连接 -## POST `/vector-stores/test` - 测试原始凭据连接 +用前端表单中尚未保存的凭据执行一次连通性测试,不会写入数据库。成功时返回自动检测到的服务器版本(如 ES 版本号);某些引擎(如 Milvus、SQLite)无法检测版本,`version` 会返回空字符串。 -使用提供的凭据执行连接测试,不保存任何数据。成功时返回自动检测到的服务器版本。 +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------------- | ------ | ---- | ------------------------------------------------------------- | +| engine_type | string | 是 | 引擎类型,取自 `/vector-stores/types` 的 `type` | +| connection_config | object | 是 | 该引擎对应的连接配置字段(与 `connection_fields` 对应) | **请求**: ```curl -curl --location 'http://localhost:8080/api/v1/vector-stores/test' \ +curl --location --request POST 'http://localhost:8080/api/v1/vector-stores/test' \ --header 'X-API-Key: sk-xxxxx' \ --header 'Content-Type: application/json' \ --data '{ @@ -93,8 +96,6 @@ curl --location 'http://localhost:8080/api/v1/vector-stores/test' \ } ``` -> `version` 字段包含自动检测到的服务器版本。如果无法检测(如 Milvus、SQLite),则为空字符串。 - **响应(失败)**: ```json @@ -104,13 +105,22 @@ curl --location 'http://localhost:8080/api/v1/vector-stores/test' \ } ``` ---- +> 注意:测试失败时 HTTP 状态码仍为 `200`,错误信息通过 `success: false` + `error` 字段返回。 ## POST `/vector-stores` - 创建向量存储 -创建一个新的向量存储配置。同一 endpoint + index 组合不允许重复注册(包括环境变量配置的存储)。 +为当前租户创建一个新的向量存储配置。同一 endpoint + index 组合在租户内不允许重复(与环境变量配置的存储也会冲突)。 -Tencent VectorDB 使用 `engine_type: "tencent_vectordb"`。`connection_config` 中 `addr`、`username`、`api_key` 为必填,`database` 可选;`index_config.collection_name` 表示集合名前缀,实际集合会按向量维度追加后缀,例如 `weknora_embeddings_768`。Tencent VectorDB 适配器同时支持向量检索和基于 BM25 sparse vector 的关键词检索;旧版本已创建且没有 `sparse_vector` 索引的集合,需要重建并重新导入数据后才能启用关键词检索。 +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------------- | ------ | ---- | --------------------------------------------------------------- | +| name | string | 是 | 存储显示名(租户内友好名) | +| engine_type | string | 是 | 引擎类型,取自 `/vector-stores/types` | +| connection_config | object | 是 | 连接配置(与所选引擎的 `connection_fields` 对应) | +| index_config | object | 否 | 索引配置(与所选引擎的 `index_fields` 对应) | + +> Tencent VectorDB 使用 `engine_type: "tencent_vectordb"`。`connection_config` 中 `addr`、`username`、`api_key` 必填,`database` 可选;`index_config.collection_name` 表示集合名前缀,实际集合会按向量维度追加后缀(例如 `weknora_embeddings_768`)。该适配器同时支持向量检索和基于 BM25 sparse vector 的关键词检索;旧版本已创建且没有 `sparse_vector` 索引的集合需要重建并重新导入数据后才能启用关键词检索。 **请求**: @@ -178,14 +188,11 @@ curl --location 'http://localhost:8080/api/v1/vector-stores' \ } ``` -> **注意**: 响应中的敏感字段(`password`、`api_key`)会被掩码为 `"***"`。 -> `connection_config.version` 字段在连接测试成功后自动填充(如 `"7.10.1"`),创建时为空。 - ---- +> 响应中的敏感字段(`password`、`api_key` 等)会被掩码为 `"***"`。`connection_config.version` 字段在连接测试成功后才会自动填充,创建时为空。 ## GET `/vector-stores` - 获取向量存储列表 -返回当前租户的所有向量存储,包含环境变量配置的存储(`source: "env"`)和用户创建的存储(`source: "user"`)。环境变量存储排列在前。 +返回当前租户的所有向量存储,包含 `RETRIEVE_DRIVER` 环境变量配置的虚拟存储(`source: "env"`、`readonly: true`)和用户在 DB 中创建的存储(`source: "user"`、`readonly: false`)。环境变量存储排列在前。 **请求**: @@ -226,11 +233,15 @@ curl --location 'http://localhost:8080/api/v1/vector-stores' \ } ``` ---- - ## GET `/vector-stores/:id` - 获取向量存储详情 -根据 ID 获取单个向量存储详情。支持 `__env_*` 格式的环境变量存储 ID。 +根据 ID 获取单个向量存储。支持 DB 存储 UUID 和 `__env_*` 形式的环境变量存储 ID(例如 `__env_postgres__`)。 + +**路径参数**: + +| 字段 | 类型 | 必填 | 说明 | +| ---- | ------ | ---- | --------------------------------------------------- | +| id | string | 是 | 向量存储 ID(DB UUID 或 `__env_{driver}__`) | **请求**: @@ -265,11 +276,21 @@ curl --location 'http://localhost:8080/api/v1/vector-stores/550e8400-e29b-41d4-a } ``` ---- - ## PUT `/vector-stores/:id` - 更新向量存储 -更新向量存储的名称。`engine_type`、`connection_config`、`index_config` 创建后不可变更。环境变量存储不可修改(返回 400)。 +仅支持更新 `name`。`engine_type`、`connection_config`、`index_config` 创建后不可变更;环境变量存储不可修改(返回 `400`)。 + +**路径参数**: + +| 字段 | 类型 | 必填 | 说明 | +| ---- | ------ | ---- | -------------- | +| id | string | 是 | 向量存储 ID | + +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ---- | ------ | ---- | ----------------- | +| name | string | 是 | 新的存储显示名 | **请求**: @@ -307,11 +328,15 @@ curl --location --request PUT 'http://localhost:8080/api/v1/vector-stores/550e84 } ``` ---- - ## DELETE `/vector-stores/:id` - 删除向量存储 -软删除向量存储。环境变量存储不可删除(返回 400)。 +对 DB 中的存储执行软删除。环境变量存储不可删除(返回 `400`)。 + +**路径参数**: + +| 字段 | 类型 | 必填 | 说明 | +| ---- | ------ | ---- | -------------- | +| id | string | 是 | 向量存储 ID | **请求**: @@ -328,11 +353,15 @@ curl --location --request DELETE 'http://localhost:8080/api/v1/vector-stores/550 } ``` ---- +## POST `/vector-stores/:id/test` - 测试已保存或环境变量存储的连接 -## POST `/vector-stores/:id/test` - 测试已保存的向量存储连接 +对已保存的 DB 存储或环境变量虚拟存储执行一次连接测试。成功时返回检测到的服务器版本;对 DB 存储,检测到的版本会被自动写回 `connection_config.version`,环境变量存储不会更新。 -对已保存的向量存储或环境变量存储执行连接测试。成功时返回检测到的服务器版本,并自动更新存储记录中的 `connection_config.version` 字段。 +**路径参数**: + +| 字段 | 类型 | 必填 | 说明 | +| ---- | ------ | ---- | --------------------------------------------------- | +| id | string | 是 | 向量存储 ID(DB UUID 或 `__env_{driver}__`) | **请求**: @@ -350,8 +379,6 @@ curl --location --request POST 'http://localhost:8080/api/v1/vector-stores/550e8 } ``` -> 对于已保存的存储(非环境变量存储),检测到的版本会自动保存到 `connection_config.version` 中。 - **响应(失败)**: ```json @@ -361,24 +388,24 @@ curl --location --request POST 'http://localhost:8080/api/v1/vector-stores/550e8 } ``` ---- +> 与 `/vector-stores/test` 一致,测试失败时 HTTP 状态码仍为 `200`,错误通过 `success: false` + `error` 返回。 ## 环境变量存储 -通过 `RETRIEVE_DRIVER` 环境变量配置的向量存储以虚拟条目形式出现在列表中。这些条目的特征: +通过 `RETRIEVE_DRIVER` 环境变量配置的向量存储以虚拟条目形式出现在列表和详情中。这些条目的特征: -- **ID 格式**: `__env_{driver}__`(如 `__env_postgres__`、`__env_elasticsearch_v8__`) -- **source**: `"env"` -- **readonly**: `true` -- **不可修改/删除**: PUT 和 DELETE 返回 400 -- **可测试连接**: POST `/:id/test` 正常工作 +- **ID 格式**:`__env_{driver}__`(如 `__env_postgres__`、`__env_elasticsearch_v8__`) +- **source**:`"env"` +- **readonly**:`true` +- **不可修改/删除**:`PUT` 和 `DELETE` 返回 `400` +- **可测试连通性**:`POST /vector-stores/:id/test` 正常工作 ## 错误码 -| HTTP 状态码 | 含义 | -|-------------|------| -| 400 | 请求参数错误、验证失败、尝试修改环境变量存储 | -| 401 | 未认证 | -| 404 | 向量存储不存在 | -| 409 | 同一 endpoint + index 组合已存在 | -| 500 | 内部服务器错误 | +| HTTP 状态码 | 含义 | +| ----------- | --------------------------------------------------- | +| 400 | 请求参数错误、校验失败、尝试修改环境变量存储 | +| 401 | 未认证(缺少租户上下文或 API Key) | +| 404 | 向量存储不存在 | +| 409 | 同一 endpoint + index 组合已存在 | +| 500 | 内部服务器错误 | diff --git a/docs/api/web-search.md b/docs/api/web-search.md index c05ba5781..38c336cc3 100644 --- a/docs/api/web-search.md +++ b/docs/api/web-search.md @@ -2,13 +2,25 @@ [返回目录](./README.md) -| 方法 | 路径 | 描述 | -| ---- | ------------------------ | ---------------------- | -| GET | `/web-search/providers` | 获取网络搜索服务商列表 | +包含两组接口: +- `/web-search/providers`:返回**可用的 provider 类型**(只读元数据) +- `/web-search-providers/*`:当前租户**自定义保存**的 provider CRUD 与连通性测试 -## GET `/web-search/providers` - 获取网络搜索服务商列表 +| 方法 | 路径 | 描述 | +| ------ | ------------------------------------- | ----------------------------------- | +| GET | `/web-search/providers` | 获取网络搜索服务商类型列表 | +| GET | `/web-search-providers/types` | 获取 Provider 类型元数据(含参数定义) | +| POST | `/web-search-providers/test` | 使用原始凭证测试连通性(不落库) | +| POST | `/web-search-providers` | 创建租户级 Provider 配置 | +| GET | `/web-search-providers` | 获取当前租户已保存的 Provider 列表 | +| GET | `/web-search-providers/:id` | 获取指定 Provider 详情 | +| PUT | `/web-search-providers/:id` | 更新 Provider | +| DELETE | `/web-search-providers/:id` | 删除 Provider | +| POST | `/web-search-providers/:id/test` | 使用已保存凭证测试连通性 | -获取系统中可用的网络搜索服务商列表。 +## GET `/web-search/providers` - 获取网络搜索服务商类型列表 + +获取系统中可用的网络搜索服务商列表(系统级元数据,与租户无关)。 **请求**: @@ -45,3 +57,201 @@ curl --location 'http://localhost:8080/api/v1/web-search/providers' \ "success": true } ``` + +## GET `/web-search-providers/types` - 获取 Provider 类型元数据 + +返回 UI 表单需要的所有 provider 类型及参数定义(每种 provider 需要哪些字段、类型、是否必填)。 + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/web-search-providers/types' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: + +```json +{ + "data": [ + { + "provider": "google", + "label": "Google Search", + "description": "...", + "parameter_schema": [ + { "name": "api_key", "label": "API Key", "type": "string", "required": true }, + { "name": "cx", "label": "Search Engine ID", "type": "string", "required": true } + ] + } + ], + "success": true +} +``` + +## POST `/web-search-providers/test` - 使用原始凭证测试连通性 + +前端表单"测试连接"按钮使用:用尚未保存的凭证发起一次样本搜索。 + +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ---------- | ------ | ---- | --------------------------------- | +| provider | string | 是 | provider 类型(如 `google`、`bing`) | +| parameters | object | 是 | 该 provider 所需凭证与参数(与 `/types` 中 `parameter_schema` 对应) | + +**请求**: + +```curl +curl --location --request POST 'http://localhost:8080/api/v1/web-search-providers/test' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "provider": "google", + "parameters": { + "api_key": "AIza...", + "cx": "0123456789:abcdefg" + } +}' +``` + +**响应**: + +```json +{ "success": true } +``` + +失败时: + +```json +{ "success": false, "error": "google api: 403 forbidden" } +``` + +## POST `/web-search-providers` - 创建 Provider + +**参数说明(请求体)**: + +| 字段 | 类型 | 必填 | 说明 | +| ----------- | ------- | ---- | ------------------------------------------ | +| name | string | 是 | Provider 显示名(在租户内唯一友好名) | +| provider | string | 是 | Provider 类型(来自 `/web-search-providers/types`) | +| description | string | 否 | 备注 | +| parameters | object | 否 | 凭证与参数 | +| is_default | boolean | 否 | 是否设为当前租户默认 Provider | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/web-search-providers' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "name": "公司 Google CSE", + "provider": "google", + "description": "用于内网搜索", + "parameters": { + "api_key": "AIza...", + "cx": "0123456789:abcdefg" + }, + "is_default": true +}' +``` + +**响应**: + +```json +{ + "data": { + "id": "wsp-...", + "tenant_id": 1, + "name": "公司 Google CSE", + "provider": "google", + "is_default": true, + "parameters": { "api_key": "***", "cx": "0123456789:abcdefg" } + }, + "success": true +} +``` + +## GET `/web-search-providers` - 获取 Provider 列表 + +返回当前租户已保存的所有 Provider。 + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/web-search-providers' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: + +```json +{ + "data": [ + { "id": "wsp-001", "name": "公司 Google CSE", "provider": "google", "is_default": true } + ], + "success": true +} +``` + +## GET `/web-search-providers/:id` - 获取 Provider 详情 + +**路径参数**: + +| 字段 | 类型 | 说明 | +| ---- | ------ | ----------- | +| id | string | Provider ID | + +**请求**: + +```curl +curl --location 'http://localhost:8080/api/v1/web-search-providers/wsp-001' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: 同创建接口。404 表示不存在。 + +## PUT `/web-search-providers/:id` - 更新 Provider + +**说明**:`provider` 字段(类型)创建后不可修改,仅支持更新 `name` / `description` / `parameters` / `is_default`。 + +**参数说明(请求体)**: 同创建接口,但不包含 `provider`。 + +**请求**: + +```curl +curl --location --request PUT 'http://localhost:8080/api/v1/web-search-providers/wsp-001' \ +--header 'X-API-Key: sk-xxxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "name": "公司 Google CSE (v2)", + "parameters": { "api_key": "NEW...", "cx": "0123456789:abcdefg" }, + "is_default": false +}' +``` + +**响应**: `{ "data": {...更新后实体...}, "success": true }` + +## DELETE `/web-search-providers/:id` - 删除 Provider + +**请求**: + +```curl +curl --location --request DELETE 'http://localhost:8080/api/v1/web-search-providers/wsp-001' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: `{ "success": true }` + +## POST `/web-search-providers/:id/test` - 测试已保存的 Provider + +使用数据库中已保存的凭证发起一次样本搜索。 + +**请求**: + +```curl +curl --location --request POST 'http://localhost:8080/api/v1/web-search-providers/wsp-001/test' \ +--header 'X-API-Key: sk-xxxxx' +``` + +**响应**: 同 `POST /web-search-providers/test`。