From a6291e00e10fc24a768d8088aa162be174082e50 Mon Sep 17 00:00:00 2001 From: "zeyu.fz" Date: Sat, 22 Aug 2026 11:21:22 +0800 Subject: [PATCH] =?UTF-8?q?docs(knowledge):=20=E5=BC=BA=E5=88=B6=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=9F=A5=E8=AF=86=E5=BA=93=E6=8F=8F=E8=BF=B0=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 升级知识库创建命令,`--description` 参数变为必填,描述知识库内容和用途 - 更新所有相关文档示例,统一加入 `--description` 参数和示例文本 - CLI 校验增强,缺失或超长的描述参数本地报错,避免服务端拒绝 - 优化服务创建命令,推荐填写描述以帮助 agent 选择合适服务 - 多个测试用例添加对描述参数的验证和断言 - 知识库和集合列表中描述信息作为区分同类项目的辅助信息显式展示 - 其他细节调整包括命令帮助及参数说明内容的更新 --- docs/knowledge/kb.md | 10 +- docs/knowledge/knowledge-cli-guide.md | 4 +- docs/knowledge/service.md | 4 +- .../commands/knowledge/collection-create.ts | 5 +- .../src/commands/knowledge/kb-create.ts | 28 ++++- .../src/commands/knowledge/retrieve.ts | 7 ++ .../src/commands/knowledge/service-create.ts | 12 +- .../src/commands/knowledge/service-list.ts | 4 +- .../src/commands/knowledge/service-update.ts | 9 ++ .../e2e/knowledge/journeys/journey-helpers.ts | 2 + .../knowledge-chunk-category-file.e2e.test.ts | 9 +- .../knowledge-doc-status.e2e.test.ts | 2 + .../knowledge/knowledge-kb-create.e2e.test.ts | 50 ++++++++ .../knowledge/knowledge-kb-delete.e2e.test.ts | 2 + .../knowledge/knowledge-service.e2e.test.ts | 41 ++++++- .../tests/e2e/knowledge/knowledge.e2e.test.ts | 7 +- .../tests/e2e/knowledge/verified-models.ts | 36 ++++++ packages/core/src/types/knowledge-admin.ts | 11 ++ packages/kscli/README.md | 1 + packages/kscli/README.zh.md | 1 + skills/bailian-cli/reference/knowledge.md | 109 +++++++++--------- 21 files changed, 275 insertions(+), 79 deletions(-) create mode 100644 packages/commands/tests/e2e/knowledge/verified-models.ts diff --git a/docs/knowledge/kb.md b/docs/knowledge/kb.md index 25fcf5e..25a9b35 100644 --- a/docs/knowledge/kb.md +++ b/docs/knowledge/kb.md @@ -128,7 +128,7 @@ bl knowledge info --index-id idx-xxx --workspace-id ws-xxx **用法** ```bash -bl knowledge create --name (--doc-id | --category-id ) [flags] +bl knowledge create --name --description (--doc-id | --category-id ) [flags] ``` **参数** @@ -136,6 +136,7 @@ bl knowledge create --name (--doc-id | --category-id ) [flags] | 参数 | 类型 | 必填 | 说明 | | --------------------------- | ------ | ---- | -------------------------------------------------------- | | `--name ` | string | 是 | 知识库名称(1-20 字符,工作区内唯一) | +| `--description ` | string | 是 | 知识库装了什么内容、给谁用(1-200 字符) | | `--doc-id ` | array | 否¹ | 数据中心文件 ID(可重复);与 `--category-id` 互斥 | | `--category-id ` | array | 否¹ | 按分类导入该分类下所有文件(可重复);与 `--doc-id` 互斥 | | `--embedding-model ` | string | 否 | 向量模型名称(默认:`text-embedding-v4`) | @@ -148,6 +149,7 @@ bl knowledge create --name (--doc-id | --category-id ) [flags] **参数约束** - `--name` 长度 1-20 字符 +- `--description` 长度 1-200 字符,缺失或超长会在本地被拦截 - `--doc-id` 和 `--category-id` 互斥,必须提供其一 **输出** @@ -176,13 +178,13 @@ json 模式:返回 API 原始响应,包含 `pipelineId`(知识库 ID)和 ```bash # 从指定文件创建知识库 -bl knowledge create --name demo --doc-id file-xxx --workspace-id ws-xxx +bl knowledge create --name demo --description '产品文档' --doc-id file-xxx --workspace-id ws-xxx # 从分类导入并等待导入完成 -bl knowledge create --name demo --category-id cate-xxx --wait +bl knowledge create --name demo --description '产品文档' --category-id cate-xxx --wait # 指定向量模型和切片大小 -bl knowledge create --name my-kb --doc-id file-a --doc-id file-b --embedding-model text-embedding-v4 --chunk-size 400 --workspace-id ws-xxx +bl knowledge create --name my-kb --description '产品文档 v2' --doc-id file-a --doc-id file-b --embedding-model text-embedding-v4 --chunk-size 400 --workspace-id ws-xxx ``` --- diff --git a/docs/knowledge/knowledge-cli-guide.md b/docs/knowledge/knowledge-cli-guide.md index 04ae639..5080938 100644 --- a/docs/knowledge/knowledge-cli-guide.md +++ b/docs/knowledge/knowledge-cli-guide.md @@ -153,7 +153,7 @@ bl knowledge doc upload --file ./docs/intro.md --workspace-id ws-xxx # → 返回 file-id # 2. 用文件创建知识库 -bl knowledge create --name my-kb --doc-id file-xxx --workspace-id ws-xxx --wait +bl knowledge create --name my-kb --description '产品文档' --doc-id file-xxx --workspace-id ws-xxx --wait # → 返回 index-id (pipelineId) 和导入任务状态 # 3. 创建检索服务(search 场景) @@ -245,7 +245,7 @@ bl knowledge doc import-oss \ # → 返回各文件的 fileId # 2. 创建知识库并导入这些文件 -bl knowledge create --name oss-kb --doc-id file-a --doc-id file-b --workspace-id ws-xxx --wait +bl knowledge create --name oss-kb --description 'OSS 导入文档' --doc-id file-a --doc-id file-b --workspace-id ws-xxx --wait # 3. 检索 bl knowledge search --query "相关内容" --agent-id aid-xxx --workspace-id ws-xxx diff --git a/docs/knowledge/service.md b/docs/knowledge/service.md index d0d0637..a4cb2d6 100644 --- a/docs/knowledge/service.md +++ b/docs/knowledge/service.md @@ -142,14 +142,14 @@ bl knowledge service create --name --scene [flags] | ------------------------ | ------ | ---- | ------------------------------------------------- | | `--name ` | string | 是 | 服务名称(最多 200 字符,同一场景下工作区内唯一) | | `--scene ` | string | 是 | 服务场景:`chat`(Q&A)或 `search`(检索) | -| `--description ` | string | 否 | 服务描述(最多 1000 字符) | +| `--description ` | string | 建议 | 这个服务能回答什么、给谁用(最多 1000 字符) | | `--index-id ` | string | 否 | 绑定此知识库;其他配置使用服务端默认值 | **参数约束** - `--name` 最多 200 字符 - `--scene` 只能是 `chat` 或 `search` -- `--description` 最多 1000 字符 +- `--description` 最多 1000 字符;建议填写 —— agent 靠它判断该调用哪个服务 **输出** diff --git a/packages/commands/src/commands/knowledge/collection-create.ts b/packages/commands/src/commands/knowledge/collection-create.ts index 2db8031..818e620 100644 --- a/packages/commands/src/commands/knowledge/collection-create.ts +++ b/packages/commands/src/commands/knowledge/collection-create.ts @@ -20,8 +20,9 @@ const COLLECTION_CREATE_FLAGS = { type: "string", valueHint: "", description: { - "en-US": "Collection description (required by the server)", - "zh-CN": "数据集合描述(服务端必填)", + "en-US": + "What this collection holds and what it is for — tells collections apart in the list", + "zh-CN": "数据集合装了什么内容、给谁用,用于在列表中区分同类集合", }, required: true, }, diff --git a/packages/commands/src/commands/knowledge/kb-create.ts b/packages/commands/src/commands/knowledge/kb-create.ts index 25aa960..794f3ec 100644 --- a/packages/commands/src/commands/knowledge/kb-create.ts +++ b/packages/commands/src/commands/knowledge/kb-create.ts @@ -29,6 +29,16 @@ const KB_CREATE_FLAGS = { }, required: true, }, + description: { + type: "string", + valueHint: "", + description: { + "en-US": + "What this knowledge base holds and what it is for — tells bases apart in the workspace list (1-200 chars)", + "zh-CN": "知识库装了什么内容、给谁用,用于在 Workspace 列表中区分同类知识库(1–200 个字符)", + }, + required: true, + }, docId: { type: "array", valueHint: "", @@ -107,7 +117,7 @@ export default defineCommand({ "zh-CN": "创建知识库并导入数据中心文件或类目", }, auth: "apiKey", - usageArgs: "--name (--doc-id | --category-id ) [flags]", + usageArgs: "--name --description (--doc-id | --category-id ) [flags]", flags: KB_CREATE_FLAGS, notes: [ { @@ -126,11 +136,20 @@ export default defineCommand({ }, ], exampleArgs: [ - "--name demo --doc-id file-xxx --workspace-id ws-xxx", - "--name demo --category-id cate-xxx --wait", + { + "en-US": "--name demo --description 'product docs' --doc-id file-xxx --workspace-id ws-xxx", + "zh-CN": "--name demo --description '产品文档' --doc-id file-xxx --workspace-id ws-xxx", + }, + { + "en-US": "--name demo --description 'product docs' --category-id cate-xxx --wait", + "zh-CN": "--name demo --description '产品文档' --category-id cate-xxx --wait", + }, ], validate(flags) { if (flags.name.length < 1 || flags.name.length > 20) return "--name must be 1-20 characters"; + if (flags.description.length < 1 || flags.description.length > 200) { + return "--description must be 1-200 characters"; + } const hasDocIds = !!flags.docId?.length; const hasCategoryIds = !!flags.categoryId?.length; if (hasDocIds && hasCategoryIds) return "Use either --doc-id or --category-id, not both"; @@ -147,6 +166,9 @@ export default defineCommand({ // Note: the public docs' example uses sinkType DEFAULT, but BUILT_IN is what works against the live API. const body = { name: flags.name, + // The server enforces description as a required 1-200 char field (the public + // API docs still list it as absent from CreateIndexV2Request.required). + description: flags.description, structureType: "unstructured", sinkType: "BUILT_IN", embeddingModelName: flags.embeddingModel ?? "text-embedding-v4", diff --git a/packages/commands/src/commands/knowledge/retrieve.ts b/packages/commands/src/commands/knowledge/retrieve.ts index 26c8a5b..d8dae1a 100644 --- a/packages/commands/src/commands/knowledge/retrieve.ts +++ b/packages/commands/src/commands/knowledge/retrieve.ts @@ -82,6 +82,13 @@ export default defineCommand({ auth: "apiKey", usageArgs: "--index-id --query [flags]", flags: RETRIEVE_FLAGS, + notes: [ + { + "en-US": + "--rerank-model requires the target knowledge base to already have a rerank model configured; otherwise every value is rejected.", + "zh-CN": "--rerank-model 要求目标知识库已配置重排序模型,否则任何取值都会被拒绝。", + }, + ], exampleArgs: [ { "en-US": '--index-id idx_xxx --query "How to use Alibaba Cloud Bailian"', diff --git a/packages/commands/src/commands/knowledge/service-create.ts b/packages/commands/src/commands/knowledge/service-create.ts index 0609b11..d0947d2 100644 --- a/packages/commands/src/commands/knowledge/service-create.ts +++ b/packages/commands/src/commands/knowledge/service-create.ts @@ -32,8 +32,10 @@ const SERVICE_CREATE_FLAGS = { type: "string", valueHint: "", description: { - "en-US": "Service description (up to 1000 chars)", - "zh-CN": "服务描述(最多 1000 个字符)", + "en-US": + "What this service answers and who it serves — recommended: agents read it to pick the right service (up to 1000 chars)", + "zh-CN": + "这个服务能回答什么、给谁用 —— 建议填写:agent 靠它判断该调用哪个服务(最多 1000 个字符)", }, }, indexId: { @@ -71,7 +73,11 @@ export default defineCommand({ }, ], exampleArgs: [ - "--name my-qa --scene chat --workspace-id ws-xxx", + { + "en-US": + "--name my-qa --scene chat --description 'answers product FAQs' --workspace-id ws-xxx", + "zh-CN": "--name my-qa --scene chat --description '回答产品常见问题' --workspace-id ws-xxx", + }, "--name my-search --scene search --index-id idx-xxx", ], validate(flags) { diff --git a/packages/commands/src/commands/knowledge/service-list.ts b/packages/commands/src/commands/knowledge/service-list.ts index 32fdfa4..a3c346a 100644 --- a/packages/commands/src/commands/knowledge/service-list.ts +++ b/packages/commands/src/commands/knowledge/service-list.ts @@ -14,8 +14,8 @@ const SERVICE_LIST_FLAGS = { type: "string", valueHint: "", description: { - "en-US": "Service scene: chat (Q&A) or search (retrieval). Required by the server", - "zh-CN": "服务场景:chat(问答)或 search(检索),服务端必填", + "en-US": "Service scene: chat (Q&A) or search (retrieval)", + "zh-CN": "服务场景:chat(问答)或 search(检索)", }, required: true, }, diff --git a/packages/commands/src/commands/knowledge/service-update.ts b/packages/commands/src/commands/knowledge/service-update.ts index bc41d2b..e1ff67d 100644 --- a/packages/commands/src/commands/knowledge/service-update.ts +++ b/packages/commands/src/commands/knowledge/service-update.ts @@ -201,6 +201,15 @@ const KNOWN_CONFIG_KEYS = new Set([ "session_file_max_parse_length", "enable_kb_router", "kb_router_model", + "user_system_prompt", + "anti_leak_prompt", + "refusal_prompt", + "credibility_prompt", + "enable_thinking", + "enable_temperature", + "enable_credibility", + "enable_max_completion_tokens", + "session_file_parse_mode", "rerank_top_n", "hybrid_rerank", "kb_search_configs", diff --git a/packages/commands/tests/e2e/knowledge/journeys/journey-helpers.ts b/packages/commands/tests/e2e/knowledge/journeys/journey-helpers.ts index 192b410..9ec77a2 100644 --- a/packages/commands/tests/e2e/knowledge/journeys/journey-helpers.ts +++ b/packages/commands/tests/e2e/knowledge/journeys/journey-helpers.ts @@ -323,6 +323,8 @@ export async function createKbWithDocs( "create", "--name", kbName, + "--description", + `journey ${journeyId} fixture knowledge base (safe to delete)`, ...fileIds.flatMap((fileId) => ["--doc-id", fileId]), "--workspace-id", workspaceId, diff --git a/packages/commands/tests/e2e/knowledge/knowledge-chunk-category-file.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-chunk-category-file.e2e.test.ts index fc00111..ef3e4a6 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-chunk-category-file.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-chunk-category-file.e2e.test.ts @@ -18,6 +18,7 @@ import { KNOWLEDGE_KB_DELETE_ROUTES, } from "../topic-routes.ts"; import { deleteKbWithRetry, pollUntil } from "./journeys/journey-helpers.ts"; +import { VERIFIED_RERANK_MODEL } from "./verified-models.ts"; interface DryRunBody { endpoint?: string; @@ -1058,6 +1059,8 @@ describe.skipIf(!isKbAdminE2EReady())( "create", "--name", `e2e-cate-kb-${Date.now() % 100000000}`.slice(0, 20), + "--description", + "e2e fixture knowledge base (safe to delete)", "--category-id", categoryId, "--workspace-id", @@ -1158,6 +1161,8 @@ describe.skipIf(!isKbAdminE2EReady())( "create", "--name", `e2e-ck-${Date.now() % 100000000}`, + "--description", + "e2e fixture knowledge base (safe to delete)", "--doc-id", fileId, "--workspace-id", @@ -1319,7 +1324,7 @@ describe.skipIf(!isKbAdminE2EReady())( "chunk chain fixture", "--rerank", "--rerank-model", - "qwen3-rerank-hybrid", + VERIFIED_RERANK_MODEL, "--rerank-mode", "similar", "--rerank-top-n", @@ -1701,6 +1706,8 @@ describe.skipIf(!isKbAdminE2EReady())("e2e: chunk/category/file 参数补全 (li "create", "--name", `e2e-pk-${Date.now() % 100000000}`.slice(0, 20), + "--description", + "e2e fixture knowledge base (safe to delete)", "--doc-id", fileId, "--workspace-id", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-doc-status.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-doc-status.e2e.test.ts index 31517eb..6b98c4b 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-doc-status.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-doc-status.e2e.test.ts @@ -148,6 +148,8 @@ describe.skipIf(!isKbAdminE2EReady())("e2e: knowledge doc status (live, 自清 "create", "--name", `e2e-st-${Date.now() % 100000000}`, + "--description", + "e2e fixture knowledge base (safe to delete)", "--doc-id", fileIdA, "--workspace-id", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-kb-create.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-kb-create.e2e.test.ts index 651ee64..af144b4 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-kb-create.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-kb-create.e2e.test.ts @@ -5,6 +5,7 @@ import { KNOWLEDGE_KB_CREATE_ROUTES } from "../topic-routes.ts"; interface DryRunBody { endpoint?: string; request?: { + description?: string; sourceType?: string; sinkType?: string; docIds?: string[]; @@ -23,6 +24,7 @@ describe("e2e: knowledge kb create", () => { ]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/--name/i); + expect(stderr).toMatch(/--description/i); expect(stderr).toMatch(/--doc-id/i); expect(stderr).toMatch(/--category-id/i); expect(stderr).toMatch(/--embedding-model/i); @@ -33,6 +35,40 @@ describe("e2e: knowledge kb create", () => { const { exitCode } = await runCommandE2e(KNOWLEDGE_KB_CREATE_ROUTES, [ "knowledge", "create", + "--description", + "demo base", + "--doc-id", + "file_test", + "--workspace-id", + "ws_test", + ]); + expect(exitCode).toBe(2); + }); + + // The server rejects a missing description with HTTP 400 (Index.InvalidParameter); + // the CLI must stop it locally instead. + test("缺 --description 报 USAGE (2)", async () => { + const { exitCode } = await runCommandE2e(KNOWLEDGE_KB_CREATE_ROUTES, [ + "knowledge", + "create", + "--name", + "demo", + "--doc-id", + "file_test", + "--workspace-id", + "ws_test", + ]); + expect(exitCode).toBe(2); + }); + + test("--description 201 字符报 USAGE (2)", async () => { + const { exitCode } = await runCommandE2e(KNOWLEDGE_KB_CREATE_ROUTES, [ + "knowledge", + "create", + "--name", + "demo", + "--description", + "x".repeat(201), "--doc-id", "file_test", "--workspace-id", @@ -47,6 +83,8 @@ describe("e2e: knowledge kb create", () => { "create", "--name", "demo", + "--description", + "demo base", "--workspace-id", "ws_test", ]); @@ -59,6 +97,8 @@ describe("e2e: knowledge kb create", () => { "create", "--name", "demo", + "--description", + "demo base", "--doc-id", "file_test", "--category-id", @@ -75,6 +115,8 @@ describe("e2e: knowledge kb create", () => { "create", "--name", "x".repeat(21), + "--description", + "demo base", "--doc-id", "file_test", "--workspace-id", @@ -89,6 +131,8 @@ describe("e2e: knowledge kb create", () => { "create", "--name", "demo", + "--description", + "demo base", "--doc-id", "file_test", "--workspace-id", @@ -103,6 +147,8 @@ describe("e2e: knowledge kb create", () => { expect(data.request?.sourceType).toBe("DATA_CENTER_FILE"); expect(data.request?.docIds).toEqual(["file_test"]); expect(data.request?.sinkType).toBe("BUILT_IN"); + // description is a server-required field — it must reach the request body verbatim + expect(data.request?.description).toBe("demo base"); // Defaults are part of the contract — the server applies no fallback of its own expect(data.request?.embeddingModelName).toBe("text-embedding-v4"); expect(data.request?.chunkSize).toBe(600); @@ -114,6 +160,8 @@ describe("e2e: knowledge kb create", () => { "create", "--name", "demo", + "--description", + "demo base", "--doc-id", "file_test", "--embedding-model", @@ -138,6 +186,8 @@ describe("e2e: knowledge kb create", () => { "create", "--name", "demo", + "--description", + "demo base", "--category-id", "cate_test", "--workspace-id", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-kb-delete.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-kb-delete.e2e.test.ts index d19bce9..04629e7 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-kb-delete.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-kb-delete.e2e.test.ts @@ -101,6 +101,8 @@ describe.skipIf(!isKbAdminE2EReady())("e2e: knowledge kb 写链路 (live, 自清 "create", "--name", kbName, + "--description", + "e2e fixture knowledge base (safe to delete)", "--doc-id", fileId, "--workspace-id", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-service.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-service.e2e.test.ts index 1d35d57..5f60768 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-service.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-service.e2e.test.ts @@ -7,6 +7,7 @@ import { join } from "node:path"; import { describe, expect, test } from "vite-plus/test"; import { isKbAdminE2EReady, parseStdoutJson, runCommandE2e } from "../helpers.ts"; import { KNOWLEDGE_SERVICE_ROUTES } from "../topic-routes.ts"; +import { pickDifferentAgentModel } from "./verified-models.ts"; interface DryRunBody { endpoint?: string; @@ -780,6 +781,33 @@ describe.skipIf(!isKbAdminE2EReady())("e2e: knowledge service 参数全覆盖 (l expect(listIndexIdRun.exitCode, listIndexIdRun.stderr).toBe(0); // ── P0 组1: update --name --version-desc --model → get 读回 3 个 scalar ── + // --model 取值自适应:先读回草稿当前模型,再挑一个不同的已验证模型写入, + // 断言才能证明「值真的变了」而不是把默认值原样写回 + const baselineGetRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [ + "knowledge", + "service", + "get", + "--agent-id", + agentId, + "--agent-version", + "beta", + "--workspace-id", + workspaceId, + "--output", + "json", + ]); + expect(baselineGetRun.exitCode, baselineGetRun.stderr).toBe(0); + const baselineModel = parseStdoutJson<{ + data?: { agent_details?: Array<{ agent_config?: { agent_model?: string } }> }; + }>(baselineGetRun.stdout).data?.agent_details?.[0]?.agent_config?.agent_model; + const targetModel = pickDifferentAgentModel(baselineModel); + if (targetModel === undefined) { + // 白名单缩到只剩当前模型 —— 跳过模型断言而不是断言一个空操作 + process.stderr.write( + `skip --model assertion: no verified model differs from ${baselineModel}\n`, + ); + } + const newName = `${serviceName}-renamed`; const updateScalarRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [ "knowledge", @@ -791,8 +819,7 @@ describe.skipIf(!isKbAdminE2EReady())("e2e: knowledge service 参数全覆盖 (l newName, "--version-desc", "beta-v1", - "--model", - "qwen-plus", + ...(targetModel === undefined ? [] : ["--model", targetModel]), "--workspace-id", workspaceId, ]); @@ -816,14 +843,18 @@ describe.skipIf(!isKbAdminE2EReady())("e2e: knowledge service 参数全覆盖 (l data?: { agent_name?: string; agent_details?: Array<{ - agent_version_desc?: string; + agent_version_desc?: string | null; agent_config?: { agent_model?: string }; }>; }; }>(scalarGetRun.stdout); expect(scalarGetData.data?.agent_name).toBe(newName); - expect(scalarGetData.data?.agent_details?.[0]?.agent_config?.agent_model).toBe("qwen-plus"); - expect(scalarGetData.data?.agent_details?.[0]?.agent_version_desc).toBe("beta-v1"); + if (targetModel !== undefined) { + expect(scalarGetData.data?.agent_details?.[0]?.agent_config?.agent_model).toBe(targetModel); + } + // --version-desc 在 beta 草稿上是服务端空操作:update 返回 200 但存为 null。 + // 版本说明只在 deploy 时传入、或对已发布版本 update 才能落库(下方 P0 组4 覆盖)。 + expect(scalarGetData.data?.agent_details?.[0]?.agent_version_desc).toBeNull(); // ── P0 组2: update --policy turbo → get 读回 ── const updateTurboRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [ diff --git a/packages/commands/tests/e2e/knowledge/knowledge.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge.e2e.test.ts index 160a4ed..682d526 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge.e2e.test.ts @@ -6,6 +6,7 @@ import { runCommandE2e, } from "../helpers.ts"; import { KNOWLEDGE_ROUTES } from "../topic-routes.ts"; +import { VERIFIED_RERANK_MODEL } from "./verified-models.ts"; // ---- Types ---- @@ -168,7 +169,7 @@ describe("e2e: knowledge retrieve dry-run", () => { "hello", "--rerank", "--rerank-model", - "qwen3-rerank-hybrid", + VERIFIED_RERANK_MODEL, "--rerank-mode", "custom", "--rerank-instruct", @@ -187,7 +188,7 @@ describe("e2e: knowledge retrieve dry-run", () => { expect(data.request?.enable_reranking).toBe(true); expect(data.request?.dense_similarity_top_k).toBe(100); expect(data.request?.sparse_similarity_top_k).toBe(50); - expect(data.request?.rerank?.[0]?.model_name).toBe("qwen3-rerank-hybrid"); + expect(data.request?.rerank?.[0]?.model_name).toBe(VERIFIED_RERANK_MODEL); expect(data.request?.rerank?.[0]?.rerank_mode).toBe("custom"); expect(data.request?.rerank?.[0]?.rerank_instruct).toBe("按相关性排序"); }); @@ -220,7 +221,7 @@ describe.skipIf(!isKbAdminE2EReady())("e2e: knowledge retrieve 参数补全 (liv "e2e test", "--rerank", "--rerank-model", - "qwen3-rerank-hybrid", + VERIFIED_RERANK_MODEL, "--rerank-mode", "custom", "--rerank-instruct", diff --git a/packages/commands/tests/e2e/knowledge/verified-models.ts b/packages/commands/tests/e2e/knowledge/verified-models.ts new file mode 100644 index 0000000..589f206 --- /dev/null +++ b/packages/commands/tests/e2e/knowledge/verified-models.ts @@ -0,0 +1,36 @@ +// Model values verified against the live API, shared by the knowledge e2e cases so a +// server-side model rotation only needs one edit here. +// +// Deliberately test-only: the command layer must NOT mirror these lists into local +// validation. Model catalogs churn with server-side launches/deprecations, so a +// hardcoded allowlist in the CLI would block valid models until users upgrade. + +/** + * Accepted values for `agent_config.agent_model` (service create/update). + * Everything else — including qwen-plus, qwen-max, qwen3-max, qwen3.7-flash, + * qwen3.7-max and deepseek-v3 — is rejected with + * HTTP 401 InvalidParameter "agent model not allowed: ". + * The first entry is also the server-side default for a freshly created service. + */ +export const VERIFIED_AGENT_MODELS = ["qwen3.6-plus", "qwen3.7-plus"] as const; + +/** + * Accepted value for `rerank[].model_name` on index/retrieve. + * Gotcha: the server only branches on the `-hybrid` suffix — the model prefix is + * ignored (`qwen3-rerank` and `gte-rerank` score identically to omitting the field, + * `qwen3-rerank-hybrid` and `gte-rerank-hybrid` score identically to each other), so + * the effective scoring model is the index's own `rerankModelName`. Unknown names and + * indexes without `rerankModelName` both fail with + * HTTP 500 Index.IndexRerankError "index rerank config() error.". + */ +export const VERIFIED_RERANK_MODEL = "qwen3-rerank-hybrid"; + +/** + * Pick a verified model that differs from the current one, so a write → read-back + * assertion proves the value actually changed instead of re-writing the default. + * Returns undefined when the allowlist has shrunk to the model already in use — the + * caller must then skip the model assertion instead of asserting a no-op. + */ +export function pickDifferentAgentModel(currentModel: string | undefined): string | undefined { + return VERIFIED_AGENT_MODELS.find((model) => model !== currentModel); +} diff --git a/packages/core/src/types/knowledge-admin.ts b/packages/core/src/types/knowledge-admin.ts index 3210782..3ca4e8f 100644 --- a/packages/core/src/types/knowledge-admin.ts +++ b/packages/core/src/types/knowledge-admin.ts @@ -186,6 +186,17 @@ export interface RagAgentConfig { session_file_max_parse_length?: number; enable_kb_router?: string; kb_router_model?: string; + // Fields the server returns in the beta draft config; typed so read-merge-write + // round-trips them without a passthrough warning. Observed value kinds, not docs. + user_system_prompt?: string; + anti_leak_prompt?: string; + refusal_prompt?: string; + credibility_prompt?: string; + enable_thinking?: boolean; + enable_temperature?: boolean; + enable_credibility?: boolean; + enable_max_completion_tokens?: boolean; + session_file_parse_mode?: string; rerank_top_n?: number; hybrid_rerank?: Record; kb_search_configs?: Array>; diff --git a/packages/kscli/README.md b/packages/kscli/README.md index 76e4c8e..67a2e00 100644 --- a/packages/kscli/README.md +++ b/packages/kscli/README.md @@ -42,6 +42,7 @@ npm install -g knowledge-studio-cli # 1. Create a knowledge base kscli kb create \ --name "my-kb" \ + --description "my product docs knowledge base" \ --embedding-model text-embedding-v3 \ --workspace-id diff --git a/packages/kscli/README.zh.md b/packages/kscli/README.zh.md index 5e0a44c..d485a80 100644 --- a/packages/kscli/README.zh.md +++ b/packages/kscli/README.zh.md @@ -42,6 +42,7 @@ npm install -g knowledge-studio-cli # 1. 创建知识库 kscli kb create \ --name "my-kb" \ + --description "我的产品文档知识库" \ --embedding-model text-embedding-v3 \ --workspace-id diff --git a/skills/bailian-cli/reference/knowledge.md b/skills/bailian-cli/reference/knowledge.md index 6c02e30..a5e8c4f 100644 --- a/skills/bailian-cli/reference/knowledge.md +++ b/skills/bailian-cli/reference/knowledge.md @@ -362,16 +362,16 @@ bl knowledge chunk update --index-id idx-xxx --chunk-id chunk-xxx --doc-id file- #### Flags -| Flag | Type | Required | Description | -| ---------------------- | ------ | -------- | --------------------------------------------------------------- | -| `--name ` | string | yes | Collection name | -| `--description ` | string | yes | Collection description (required by the server) | -| `--store-type ` | string | no | Storage: platform (managed) or custom (your own OSS bucket) | -| `--oss-region ` | string | no | OSS region id (required with --store-type custom) | -| `--oss-bucket ` | string | no | OSS bucket name (required with --store-type custom) | -| `--workspace-id ` | string | no | Workspace ID for API endpoint URL (or set BAILIAN_WORKSPACE_ID) | -| `--api-key ` | string | no | API key | -| `--base-url ` | string | no | API base URL | +| Flag | Type | Required | Description | +| ---------------------- | ------ | -------- | ----------------------------------------------------------------------------------- | +| `--name ` | string | yes | Collection name | +| `--description ` | string | yes | What this collection holds and what it is for — tells collections apart in the list | +| `--store-type ` | string | no | Storage: platform (managed) or custom (your own OSS bucket) | +| `--oss-region ` | string | no | OSS region id (required with --store-type custom) | +| `--oss-bucket ` | string | no | OSS bucket name (required with --store-type custom) | +| `--workspace-id ` | string | no | Workspace ID for API endpoint URL (or set BAILIAN_WORKSPACE_ID) | +| `--api-key ` | string | no | API key | +| `--base-url ` | string | no | API base URL | #### Notes @@ -420,27 +420,28 @@ bl knowledge collection get --name my-collection ### `bl knowledge create` -| Field | Value | -| ------------------ | --------------------------------------------------------------------------------- | -| **Name** | `knowledge create` | -| **Description** | Create a knowledge base and import data-center files or categories | -| **Authentication** | API Key | -| **Usage** | `bl knowledge create --name (--doc-id \| --category-id ) [flags]` | +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------------------------------ | +| **Name** | `knowledge create` | +| **Description** | Create a knowledge base and import data-center files or categories | +| **Authentication** | API Key | +| **Usage** | `bl knowledge create --name --description (--doc-id \| --category-id ) [flags]` | #### Flags -| Flag | Type | Required | Description | -| --------------------------- | ------ | -------- | ------------------------------------------------------------------------------------ | -| `--name ` | string | yes | Knowledge base name (1-20 chars, unique in workspace) | -| `--doc-id ` | array | no | Data-center file id to import (repeatable); mutually exclusive with --category-id | -| `--category-id ` | array | no | Import every file under this category (repeatable); mutually exclusive with --doc-id | -| `--embedding-model ` | string | no | Embedding model name (default: text-embedding-v4) | -| `--chunk-size ` | number | no | Chunk size in characters (default: 600, recommended 300-800) | -| `--wait` | switch | no | Poll the initial import job to a terminal state | -| `--poll-interval ` | number | no | Polling interval when waiting (default: 5) | -| `--workspace-id ` | string | no | Workspace ID for API endpoint URL (or set BAILIAN_WORKSPACE_ID) | -| `--api-key ` | string | no | API key | -| `--base-url ` | string | no | API base URL | +| Flag | Type | Required | Description | +| --------------------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------- | +| `--name ` | string | yes | Knowledge base name (1-20 chars, unique in workspace) | +| `--description ` | string | yes | What this knowledge base holds and what it is for — tells bases apart in the workspace list (1-200 chars) | +| `--doc-id ` | array | no | Data-center file id to import (repeatable); mutually exclusive with --category-id | +| `--category-id ` | array | no | Import every file under this category (repeatable); mutually exclusive with --doc-id | +| `--embedding-model ` | string | no | Embedding model name (default: text-embedding-v4) | +| `--chunk-size ` | number | no | Chunk size in characters (default: 600, recommended 300-800) | +| `--wait` | switch | no | Poll the initial import job to a terminal state | +| `--poll-interval ` | number | no | Polling interval when waiting (default: 5) | +| `--workspace-id ` | string | no | Workspace ID for API endpoint URL (or set BAILIAN_WORKSPACE_ID) | +| `--api-key ` | string | no | API key | +| `--base-url ` | string | no | API base URL | #### Notes @@ -451,11 +452,11 @@ bl knowledge collection get --name my-collection #### Examples ```bash -bl knowledge create --name demo --doc-id file-xxx --workspace-id ws-xxx +bl knowledge create --name demo --description 'product docs' --doc-id file-xxx --workspace-id ws-xxx ``` ```bash -bl knowledge create --name demo --category-id cate-xxx --wait +bl knowledge create --name demo --description 'product docs' --category-id cate-xxx --wait ``` ### `bl knowledge delete` @@ -911,6 +912,10 @@ bl knowledge list --name demo --page-number 2 --page-size 50 | `--api-key ` | string | no | API key | | `--base-url ` | string | no | API base URL | +#### Notes + +- --rerank-model requires the target knowledge base to already have a rerank model configured; otherwise every value is rejected. + #### Examples ```bash @@ -999,15 +1004,15 @@ bl knowledge service copy --agent-id aid-xxx --workspace-id ws-xxx #### Flags -| Flag | Type | Required | Description | -| ---------------------- | ------ | -------- | ----------------------------------------------------------------- | -| `--name ` | string | yes | Service name (up to 200 chars, unique per scene in the workspace) | -| `--scene ` | string | yes | Service scene: chat (Q&A) or search (retrieval) | -| `--description ` | string | no | Service description (up to 1000 chars) | -| `--index-id ` | string | no | Bind this knowledge base; other settings use server defaults | -| `--workspace-id ` | string | no | Workspace ID for API endpoint URL (or set BAILIAN_WORKSPACE_ID) | -| `--api-key ` | string | no | API key | -| `--base-url ` | string | no | API base URL | +| Flag | Type | Required | Description | +| ---------------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------- | +| `--name ` | string | yes | Service name (up to 200 chars, unique per scene in the workspace) | +| `--scene ` | string | yes | Service scene: chat (Q&A) or search (retrieval) | +| `--description ` | string | no | What this service answers and who it serves — recommended: agents read it to pick the right service (up to 1000 chars) | +| `--index-id ` | string | no | Bind this knowledge base; other settings use server defaults | +| `--workspace-id ` | string | no | Workspace ID for API endpoint URL (or set BAILIAN_WORKSPACE_ID) | +| `--api-key ` | string | no | API key | +| `--base-url ` | string | no | API base URL | #### Notes @@ -1018,7 +1023,7 @@ bl knowledge service copy --agent-id aid-xxx --workspace-id ws-xxx #### Examples ```bash -bl knowledge service create --name my-qa --scene chat --workspace-id ws-xxx +bl knowledge service create --name my-qa --scene chat --description 'answers product FAQs' --workspace-id ws-xxx ``` ```bash @@ -1141,18 +1146,18 @@ bl knowledge service get --agent-id aid-xxx --agent-version beta #### Flags -| Flag | Type | Required | Description | -| --------------------- | ------ | -------- | ----------------------------------------------------------------------- | -| `--scene ` | string | yes | Service scene: chat (Q&A) or search (retrieval). Required by the server | -| `--status ` | string | no | Filter by status: draft, deployed (includes edited) or deleted | -| `--name ` | string | no | Filter by service name (fuzzy match) | -| `--agent-id ` | string | no | Filter by exact agent ID | -| `--index-id ` | string | no | Filter by exact linked knowledge base (pipeline) ID | -| `--page-number ` | number | no | Page number (default: 1) | -| `--page-size ` | number | no | Page size per request | -| `--workspace-id ` | string | no | Workspace ID for API endpoint URL (or set BAILIAN_WORKSPACE_ID) | -| `--api-key ` | string | no | API key | -| `--base-url ` | string | no | API base URL | +| Flag | Type | Required | Description | +| --------------------- | ------ | -------- | --------------------------------------------------------------- | +| `--scene ` | string | yes | Service scene: chat (Q&A) or search (retrieval) | +| `--status ` | string | no | Filter by status: draft, deployed (includes edited) or deleted | +| `--name ` | string | no | Filter by service name (fuzzy match) | +| `--agent-id ` | string | no | Filter by exact agent ID | +| `--index-id ` | string | no | Filter by exact linked knowledge base (pipeline) ID | +| `--page-number ` | number | no | Page number (default: 1) | +| `--page-size ` | number | no | Page size per request | +| `--workspace-id ` | string | no | Workspace ID for API endpoint URL (or set BAILIAN_WORKSPACE_ID) | +| `--api-key ` | string | no | API key | +| `--base-url ` | string | no | API base URL | #### Notes