siluzan-cso-cli 1.1.18-beta.4 → 1.1.18-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -54,7 +54,7 @@ siluzan-cso init -d /path/to/skills # 写入自定义目录
54
54
  siluzan-cso init --force # 强制覆盖已存在文件
55
55
  ```
56
56
 
57
- > **注意**:当前为测试版(1.1.18-beta.4),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-cso-cli`。
57
+ > **注意**:当前为测试版(1.1.18-beta.5),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-cso-cli`。
58
58
 
59
59
  | 助手 | 建议 `--ai` |
60
60
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -1948,6 +1948,7 @@ var import_semver = __toESM(require_semver2(), 1);
1948
1948
  import * as fs2 from "fs";
1949
1949
  import * as path2 from "path";
1950
1950
  import { fileURLToPath } from "url";
1951
+ import { spawn } from "child_process";
1951
1952
  import Table from "cli-table3";
1952
1953
  var SILUZAN_DIR = path.join(os.homedir(), ".siluzan");
1953
1954
  var CONFIG_FILE = path.join(SILUZAN_DIR, "config.json");
@@ -2226,11 +2227,7 @@ function rawRequest(url, options) {
2226
2227
  error: "timeout"
2227
2228
  });
2228
2229
  }
2229
- reject(
2230
- new Error(
2231
- `\u8BF7\u6C42\u8D85\u65F6\uFF08${(timeoutMs / 1e3).toFixed(0)} \u79D2\uFF09\uFF1A${method} ${url}`
2232
- )
2233
- );
2230
+ reject(new Error(`\u8BF7\u6C42\u8D85\u65F6\uFF08${(timeoutMs / 1e3).toFixed(0)} \u79D2\uFF09\uFF1A${method} ${url}`));
2234
2231
  });
2235
2232
  req.on("error", (err) => {
2236
2233
  if (perfOn) {
@@ -2341,6 +2338,29 @@ async function fetchNpmVersion(pkgName, tag, timeoutMs = 4e3) {
2341
2338
  return null;
2342
2339
  }
2343
2340
  }
2341
+ async function defaultRunMinRequiredGlobalInstall(ctx) {
2342
+ if (process.env.SILUZAN_SKIP_AUTO_GLOBAL_INSTALL === "1") {
2343
+ return { ok: false, stderr: "SILUZAN_SKIP_AUTO_GLOBAL_INSTALL=1" };
2344
+ }
2345
+ const spec = `${ctx.pkgName}@${ctx.tag}`;
2346
+ return await new Promise((resolve32) => {
2347
+ const child = spawn("npm", ["install", "-g", spec, "--no-fund", "--no-audit"], {
2348
+ stdio: "inherit",
2349
+ shell: true
2350
+ });
2351
+ child.on("error", (err) => {
2352
+ resolve32({ ok: false, stderr: err instanceof Error ? err.message : String(err) });
2353
+ });
2354
+ child.on("close", (code, signal) => {
2355
+ if (code === 0) {
2356
+ resolve32({ ok: true });
2357
+ } else {
2358
+ const sig = signal ? ` signal=${signal}` : "";
2359
+ resolve32({ ok: false, stderr: `exit code ${code}${sig}` });
2360
+ }
2361
+ });
2362
+ });
2363
+ }
2344
2364
  function createVersionNotifier(opts) {
2345
2365
  const {
2346
2366
  pkgName,
@@ -2349,27 +2369,33 @@ function createVersionNotifier(opts) {
2349
2369
  resolveTag,
2350
2370
  forceUpdateExtra = "",
2351
2371
  updateAvailableExtra = "",
2372
+ runMinRequiredGlobalInstall = defaultRunMinRequiredGlobalInstall,
2352
2373
  getCurrentVersion: getCurrentVersion22,
2353
2374
  mergeWriteConfig,
2354
2375
  readConfigRaw
2355
2376
  } = opts;
2356
- const KEY_LAST_CHECK = `${cachePrefix}LastVersionCheck`;
2357
2377
  const KEY_LATEST_STABLE = `${cachePrefix}LatestStable`;
2358
2378
  const KEY_LATEST_BETA = `${cachePrefix}LatestBeta`;
2359
2379
  const KEY_MIN_STABLE = `${cachePrefix}MinRequiredStable`;
2360
2380
  const KEY_MIN_BETA = `${cachePrefix}MinRequiredBeta`;
2361
2381
  const KEY_LAST_NOTIFIED = `${cachePrefix}LastNotified`;
2382
+ const KEY_FETCH_AT_MAIN = `${cachePrefix}VersionFetchAtMain`;
2383
+ const KEY_FETCH_AT_MIN = `${cachePrefix}VersionFetchAtMin`;
2362
2384
  const HOURS_24 = 24 * 60 * 60 * 1e3;
2363
- async function fetchVersionByTag(tag, cacheKey, cfg) {
2364
- const lastCheck = cfg[KEY_LAST_CHECK];
2365
- if (typeof lastCheck === "string" && cacheKey in cfg) {
2366
- const lastMs = new Date(lastCheck).getTime();
2367
- if (Date.now() - lastMs < HOURS_24) {
2385
+ const TTL_MAIN_TAG_MS = 60 * 60 * 1e3;
2386
+ const TTL_MIN_REQUIRED_MS = HOURS_24;
2387
+ async function fetchVersionByTag(tag, cacheKey, fetchAtKey, cfg, maxAgeMs) {
2388
+ const lastAt = cfg[fetchAtKey];
2389
+ if (typeof lastAt === "string" && cacheKey in cfg) {
2390
+ const lastMs = new Date(lastAt).getTime();
2391
+ if (Date.now() - lastMs < maxAgeMs) {
2368
2392
  const v = cfg[cacheKey];
2369
- return typeof v === "string" && v ? v : null;
2393
+ const sv = typeof v === "string" && v ? v : null;
2394
+ return { version: sv, hitNetwork: false };
2370
2395
  }
2371
2396
  }
2372
- return fetchNpmVersion(pkgName, tag);
2397
+ const version = await fetchNpmVersion(pkgName, tag);
2398
+ return { version, hitNetwork: true };
2373
2399
  }
2374
2400
  async function notifyIfOutdated2() {
2375
2401
  try {
@@ -2380,15 +2406,20 @@ function createVersionNotifier(opts) {
2380
2406
  const minCacheKey = isBeta ? KEY_MIN_BETA : KEY_MIN_STABLE;
2381
2407
  const minTag = npmMinRequiredTagForBuildEnv(isBeta ? "test" : "production");
2382
2408
  const cfg = readConfigRaw();
2383
- const [latest, minRequired] = await Promise.all([
2384
- fetchVersionByTag(tag, latestCacheKey, cfg),
2385
- fetchVersionByTag(minTag, minCacheKey, cfg)
2409
+ const [mainRes, minRes] = await Promise.all([
2410
+ fetchVersionByTag(tag, latestCacheKey, KEY_FETCH_AT_MAIN, cfg, TTL_MAIN_TAG_MS),
2411
+ fetchVersionByTag(minTag, minCacheKey, KEY_FETCH_AT_MIN, cfg, TTL_MIN_REQUIRED_MS)
2386
2412
  ]);
2387
- await mergeWriteConfig({
2388
- [KEY_LAST_CHECK]: (/* @__PURE__ */ new Date()).toISOString(),
2413
+ const latest = mainRes.version;
2414
+ const minRequired = minRes.version;
2415
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
2416
+ const cacheUpdates = {
2389
2417
  [latestCacheKey]: latest ?? "",
2390
2418
  [minCacheKey]: minRequired ?? ""
2391
- });
2419
+ };
2420
+ if (mainRes.hitNetwork) cacheUpdates[KEY_FETCH_AT_MAIN] = nowIso;
2421
+ if (minRes.hitNetwork) cacheUpdates[KEY_FETCH_AT_MIN] = nowIso;
2422
+ await mergeWriteConfig(cacheUpdates);
2392
2423
  const lastNotified = typeof cfg[KEY_LAST_NOTIFIED] === "string" ? new Date(cfg[KEY_LAST_NOTIFIED]).getTime() : 0;
2393
2424
  if (Date.now() - lastNotified < HOURS_24) return;
2394
2425
  const tagLabel = isBeta ? "\uFF08\u6D4B\u8BD5\u7248\uFF09" : "\uFF08\u6B63\u5F0F\u7248\uFF09";
@@ -2396,11 +2427,26 @@ function createVersionNotifier(opts) {
2396
2427
  await mergeWriteConfig({ [KEY_LAST_NOTIFIED]: (/* @__PURE__ */ new Date()).toISOString() });
2397
2428
  console.log(
2398
2429
  `
2399
- [SILUZAN_FORCE_UPDATE] \u5F53\u524D\u7248\u672C ${current} \u5DF2\u8FC7\u671F\uFF0C\u6700\u4F4E\u8981\u6C42\u7248\u672C ${minRequired}${tagLabel}
2400
- \u26D4 \u5FC5\u987B\u5148\u5347\u7EA7\u624D\u80FD\u7EE7\u7EED\u4F7F\u7528\uFF0CAgent \u8BF7\u7ACB\u5373\u6267\u884C\uFF1A${binName} update
2401
- \u6216\u624B\u52A8\uFF1Anpm install -g ${pkgName}@${tag}
2430
+ [SILUZAN_FORCE_UPDATE] \u5F53\u524D\u7248\u672C ${current} \u5DF2\u4F4E\u4E8E\u6700\u4F4E\u8981\u6C42 ${minRequired}${tagLabel}
2431
+ \u26D4 \u6B63\u5728\u81EA\u52A8\u6267\u884C\uFF1Anpm install -g ${pkgName}@${tag}
2402
2432
  ` + (forceUpdateExtra ? forceUpdateExtra + "\n" : "")
2403
2433
  );
2434
+ const installResult = await runMinRequiredGlobalInstall({ pkgName, tag });
2435
+ if (installResult.ok) {
2436
+ console.log(
2437
+ `
2438
+ [SILUZAN_AUTO_GLOBAL_INSTALL_OK] \u5168\u5C40\u5B89\u88C5\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C\u672C\u547D\u4EE4\u4EE5\u52A0\u8F7D\u65B0\u7248\u672C ${pkgName}@${tag}\u3002
2439
+ `
2440
+ );
2441
+ } else {
2442
+ console.log(
2443
+ `
2444
+ [SILUZAN_AUTO_GLOBAL_INSTALL_FAILED] \u81EA\u52A8\u5168\u5C40\u5B89\u88C5\u5931\u8D25\uFF1A${installResult.stderr ?? "unknown"}
2445
+ \u8BF7\u624B\u52A8\u6267\u884C\uFF1Anpm install -g ${pkgName}@${tag}
2446
+ \u6216\uFF1A${binName} update
2447
+ ` + (forceUpdateExtra ? forceUpdateExtra + "\n" : "")
2448
+ );
2449
+ }
2404
2450
  return;
2405
2451
  }
2406
2452
  if (latest && isNewer(current, latest)) {
@@ -2489,16 +2535,11 @@ function printAuthMissingHelp(binName) {
2489
2535
  `
2490
2536
  \u274C \u672A\u627E\u5230\u8BA4\u8BC1\u51ED\u636E\u3002\u8BF7\u9009\u62E9\u4EE5\u4E0B\u4EFB\u610F\u4E00\u79CD\u65B9\u5F0F\uFF1A
2491
2537
 
2492
- \u65B9\u5F0F\u4E00\uFF08\u63A8\u8350\uFF09\uFF1AAPI Key
2493
- \u5728\u4E1D\u8DEF\u8D5E\u300C\u8BBE\u7F6E \u2192 API Key \u7BA1\u7406\u300D\u521B\u5EFA API Key\uFF0C\u7136\u540E\u8FD0\u884C\uFF1A
2494
- ${binName} login --api-key <YOUR_API_KEY>
2495
-
2496
- \u65B9\u5F0F\u4E8C\uFF1A\u73AF\u5883\u53D8\u91CF\uFF08CI/CD \u63A8\u8350\uFF09
2497
- export SILUZAN_API_KEY=<YOUR_API_KEY>
2498
- # \u6216 export SILUZAN_AUTH_TOKEN=<YOUR_TOKEN>
2499
-
2500
- \u65B9\u5F0F\u4E09\uFF1AJWT Token
2501
- ${binName} login
2538
+ \u8BF7\u4F7F\u7528\u624B\u673A\u53F7\u91CD\u65B0\u767B\u5F55
2539
+ ${binName} send-login-code --phone <YOUR_PHONE>
2540
+ `,
2541
+ `\u7136\u540E\u4F7F\u7528\u6536\u5230\u7684\u9A8C\u8BC1\u7801\u5B8C\u6210\u767B\u5F55
2542
+ ${binName} login --phone <YOUR_PHONE> --code <YOUR_CODE>
2502
2543
  `
2503
2544
  );
2504
2545
  process.exit(1);
@@ -3013,7 +3054,7 @@ import * as readline from "readline";
3013
3054
  import * as os3 from "os";
3014
3055
  function parseAllowedServices(raw) {
3015
3056
  const allowed = ["CSO", "TSO", "CUT"];
3016
- if (!raw) return ["CSO", "CUT"];
3057
+ if (!raw) return ["CSO", "TSO", "CUT"];
3017
3058
  const parts = raw.split(",").map((s) => s.trim().toUpperCase()).filter(Boolean);
3018
3059
  const result = [];
3019
3060
  for (const p of parts) {
@@ -3085,9 +3126,6 @@ async function runPhoneLogin(opts) {
3085
3126
  console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n");
3086
3127
  console.log(` \u624B\u673A\u53F7 : ${phone}`);
3087
3128
  console.log(` \u9A8C\u8BC1\u7801 : ${code}`);
3088
- console.log(` SSO : ${ssoBaseUrl}`);
3089
- console.log(` CSO API : ${csoBaseUrl}`);
3090
- console.log(` \u670D\u52A1\u8303\u56F4 : ${allowedServices.join(", ")}`);
3091
3129
  if (opts.expiresAt) {
3092
3130
  console.log(` \u8FC7\u671F\u65F6\u95F4 : ${opts.expiresAt}`);
3093
3131
  } else {
@@ -3139,12 +3177,8 @@ async function runPhoneLogin(opts) {
3139
3177
  `
3140
3178
  );
3141
3179
  } else if (/验证码错误|验证码/.test(msg)) {
3142
- console.error(
3143
- `
3144
- \u9A8C\u8BC1\u7801\u53EF\u80FD\u5DF2\u8FC7\u671F\u6216\u8F93\u9519\u3002\u8BF7\u91CD\u65B0\u53D1\u7801\u540E\u518D\u8BD5\uFF1A
3145
- siluzan-cso send-login-code --phone ${phone}
3146
- `
3147
- );
3180
+ console.error(` siluzan-cso send-login-code --phone ${phone}
3181
+ `);
3148
3182
  }
3149
3183
  process.exit(1);
3150
3184
  }
@@ -3688,7 +3722,11 @@ var register = defineCommand({
3688
3722
  description: "\u5217\u51FA\u516C\u53F8\u6240\u6709\u6210\u5458\uFF0C\u663E\u793A\u7528\u6237ID\u4F9B list-accounts --owner \u4F7F\u7528",
3689
3723
  options: [
3690
3724
  { flags: "-q, --query <keyword>", description: "\u6309\u59D3\u540D\u6216\u624B\u673A\u53F7\u6A21\u7CCA\u641C\u7D22" },
3691
- { flags: "--unicode", description: "\u4F7F\u7528 Unicode \u7EBF\u6846\u8868\u683C\uFF08\u9ED8\u8BA4 ASCII +-|\uFF09", defaultValue: false }
3725
+ {
3726
+ flags: "--unicode",
3727
+ description: "\u4F7F\u7528 Unicode \u7EBF\u6846\u8868\u683C\uFF08\u9ED8\u8BA4 ASCII +-|\uFF09",
3728
+ defaultValue: false
3729
+ }
3692
3730
  ],
3693
3731
  handler: async (opts) => {
3694
3732
  await runListMembers({
@@ -5988,6 +6026,9 @@ function formatRagResultsMarkdown(query, items, subQueries) {
5988
6026
  });
5989
6027
  return parts.join("");
5990
6028
  }
6029
+ function normalizeQueryPartition(p) {
6030
+ return p === "wiki" ? "wiki" : "default";
6031
+ }
5991
6032
  function buildQueryKnowledgesSearchParams(options) {
5992
6033
  const p = new URLSearchParams();
5993
6034
  p.append("queryTxt", options.queryTxt);
@@ -6006,6 +6047,7 @@ function buildQueryKnowledgesSearchParams(options) {
6006
6047
  for (const c of options.categories) {
6007
6048
  p.append("category", c);
6008
6049
  }
6050
+ p.append("partition", options.partition);
6009
6051
  return p.toString();
6010
6052
  }
6011
6053
  async function runRagQuery(options) {
@@ -6042,6 +6084,12 @@ async function runRagQuery(options) {
6042
6084
  }
6043
6085
  const tagList = resolveTags(options.tags);
6044
6086
  const categories = splitCsv(options.category);
6087
+ const rawPartition = options.partition?.trim();
6088
+ if (rawPartition && rawPartition !== "wiki" && rawPartition !== "default") {
6089
+ console.error("\n\u274C partition \u53EA\u80FD\u4E3A wiki \u6216 default\u3002");
6090
+ process.exit(1);
6091
+ }
6092
+ const partition = normalizeQueryPartition(rawPartition);
6045
6093
  const baseSearch = {
6046
6094
  belongTo,
6047
6095
  belongToId,
@@ -6049,7 +6097,8 @@ async function runRagQuery(options) {
6049
6097
  sourceIds,
6050
6098
  folderIds,
6051
6099
  tags: tagList,
6052
- categories
6100
+ categories,
6101
+ partition
6053
6102
  };
6054
6103
  const verbose = Boolean(options.verbose);
6055
6104
  async function fetchKnowledgesOne(queryPart) {
@@ -6101,7 +6150,8 @@ async function runRagQuery(options) {
6101
6150
  belongToId,
6102
6151
  tags: tagList,
6103
6152
  topK: clamped,
6104
- output
6153
+ output,
6154
+ partition
6105
6155
  },
6106
6156
  null,
6107
6157
  2
@@ -6194,8 +6244,10 @@ async function runRagList(options) {
6194
6244
  if (builtinRes.ok) {
6195
6245
  folders = mergeFoldersByIdOrder(rootRes.folders, builtinRes.folders);
6196
6246
  } else if (verbose) {
6197
- console.error(`
6198
- \u26A0\uFE0F \u83B7\u53D6\u77E5\u8BC6\u5E93\u5217\u8868\u5931\u8D25\uFF08\u5185\u7F6E\u7236\u7EA7\uFF09\uFF1A${builtinRes.reason}\uFF08\u5DF2\u4EC5\u7528\u6839\u8282\u70B9\u6570\u636E\u5408\u5E76\u5C55\u793A\uFF09`);
6247
+ console.error(
6248
+ `
6249
+ \u26A0\uFE0F \u83B7\u53D6\u77E5\u8BC6\u5E93\u5217\u8868\u5931\u8D25\uFF08\u5185\u7F6E\u7236\u7EA7\uFF09\uFF1A${builtinRes.reason}\uFF08\u5DF2\u4EC5\u7528\u6839\u8282\u70B9\u6570\u636E\u5408\u5E76\u5C55\u793A\uFF09`
6250
+ );
6199
6251
  }
6200
6252
  if (options.ragOnly) {
6201
6253
  folders = folders.filter((f) => f.ragStatus === true);
@@ -6490,7 +6542,11 @@ function registerCsoCommands(program2) {
6490
6542
  });
6491
6543
  }
6492
6544
  );
6493
- ragCmd.command("query").description("\u6309\u5173\u952E\u8BCD\u68C0\u7D22\u5DF2\u7EB3\u5165\u5411\u91CF\u7684\u77E5\u8BC6\u7247\u6BB5\uFF08GET cutapi/v1/material/queryknowledges\uFF09").requiredOption("-q, --query <text>", "\u68C0\u7D22\u5173\u952E\u8BCD\u6216\u95EE\u53E5").option("--belong-to-id <id>", "\u4F01\u4E1A ID\uFF1B\u4E0D\u4F20\u5219\u4ECE GET /query/account/me \u53D6 companyId").option("--source-id <ids>", "\u7D20\u6750\u6587\u4EF6 ID\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u53EF\u9009\uFF0Cqueryknowledges \u7684 sourceid\uFF09").option("--folder-id <ids>", "\u6587\u4EF6\u5939 ID\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u53EF\u9009\uFF0Ccomid\uFF09").option("--top-k <n>", "\u8FD4\u56DE\u6761\u6570\uFF0C3\u201330\uFF08\u9ED8\u8BA4 7\uFF09", "7").option("--belong-to <n>", "\u5F52\u5C5E\u7C7B\u578B\uFF08\u9ED8\u8BA4 0\uFF0C\u4F01\u4E1A\uFF09", "0").option("--tags <tags>", "\u6807\u7B7E\u8FC7\u6EE4\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u4E0D\u4F20\u5219\u4E0D\u8FC7\u6EE4\u6807\u7B7E\uFF08\u5168\u91CF\u68C0\u7D22\uFF09").option("--category <categories>", "\u5206\u7C7B\u8FC7\u6EE4\uFF0C\u9017\u53F7\u5206\u9694").option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option("--json", "\u8F93\u51FA\u5B8C\u6574 JSON", false).option("--unicode", "\u4F7F\u7528 Unicode \u7EBF\u6846\u8868\u683C\uFF08\u9ED8\u8BA4 ASCII +-|\uFF09", false).option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
6545
+ ragCmd.command("query").description("\u6309\u5173\u952E\u8BCD\u68C0\u7D22\u5DF2\u7EB3\u5165\u5411\u91CF\u7684\u77E5\u8BC6\u7247\u6BB5\uFF08GET cutapi/v1/material/queryknowledges\uFF09").requiredOption("-q, --query <text>", "\u68C0\u7D22\u5173\u952E\u8BCD\u6216\u95EE\u53E5").option("--belong-to-id <id>", "\u4F01\u4E1A ID\uFF1B\u4E0D\u4F20\u5219\u4ECE GET /query/account/me \u53D6 companyId").option("--source-id <ids>", "\u7D20\u6750\u6587\u4EF6 ID\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u53EF\u9009\uFF0Cqueryknowledges \u7684 sourceid\uFF09").option("--folder-id <ids>", "\u6587\u4EF6\u5939 ID\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u53EF\u9009\uFF0Ccomid\uFF09").option("--top-k <n>", "\u8FD4\u56DE\u6761\u6570\uFF0C3\u201330\uFF08\u9ED8\u8BA4 7\uFF09", "7").option("--belong-to <n>", "\u5F52\u5C5E\u7C7B\u578B\uFF08\u9ED8\u8BA4 0\uFF0C\u4F01\u4E1A\uFF09", "0").option("--tags <tags>", "\u6807\u7B7E\u8FC7\u6EE4\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u4E0D\u4F20\u5219\u4E0D\u8FC7\u6EE4\u6807\u7B7E\uFF08\u5168\u91CF\u68C0\u7D22\uFF09").option("--category <categories>", "\u5206\u7C7B\u8FC7\u6EE4\uFF0C\u9017\u53F7\u5206\u9694").option(
6546
+ "--partition <wiki|default>",
6547
+ "\u68C0\u7D22\u5206\u533A\uFF08queryknowledges \u7684 partition\uFF0C\u9ED8\u8BA4 default\uFF09",
6548
+ "default"
6549
+ ).option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option("--json", "\u8F93\u51FA\u5B8C\u6574 JSON", false).option("--unicode", "\u4F7F\u7528 Unicode \u7EBF\u6846\u8868\u683C\uFF08\u9ED8\u8BA4 ASCII +-|\uFF09", false).option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
6494
6550
  async (opts) => {
6495
6551
  await runRagQuery({
6496
6552
  query: opts.query,
@@ -6501,6 +6557,7 @@ function registerCsoCommands(program2) {
6501
6557
  belongTo: opts.belongTo !== void 0 ? parseInt(opts.belongTo, 10) : void 0,
6502
6558
  tags: opts.tags,
6503
6559
  category: opts.category,
6560
+ partition: opts.partition,
6504
6561
  token: opts.token,
6505
6562
  json: opts.json,
6506
6563
  unicode: opts.unicode,
@@ -6508,9 +6565,7 @@ function registerCsoCommands(program2) {
6508
6565
  });
6509
6566
  }
6510
6567
  );
6511
- const REGISTRARS = [
6512
- register
6513
- ];
6568
+ const REGISTRARS = [register];
6514
6569
  for (const reg of REGISTRARS) reg(program2);
6515
6570
  program2.command("upload").description("\u4E0A\u4F20\u672C\u5730\u89C6\u9891\u6216\u56FE\u7247\u5230 Siluzan \u7D20\u6750\u5E93\uFF0C\u8F93\u51FA\u53EF\u76F4\u63A5\u586B\u5165 publish-config.json \u7684\u5B57\u6BB5").requiredOption("-f, --file <path>", "\u672C\u5730\u6587\u4EF6\u8DEF\u5F84").option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option("--kind <video|image>", "\u624B\u52A8\u6307\u5B9A\u6587\u4EF6\u7C7B\u578B\uFF08\u9ED8\u8BA4\u6309\u6269\u5C55\u540D\u81EA\u52A8\u5224\u65AD\uFF09").option("--cover <path>", "\u5C01\u9762\u56FE\u7247\u8DEF\u5F84\uFF08\u4E0A\u4F20\u89C6\u9891\u65F6\u5FC5\u987B\u63D0\u4F9B\uFF09").option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F\u53CA\u5B8C\u6574 URL\uFF08\u9ED8\u8BA4\u9690\u85CF\u542B\u7B7E\u540D URL\uFF09", false).action(
6516
6571
  async (opts) => {
@@ -6539,7 +6594,9 @@ function registerCsoCommands(program2) {
6539
6594
  const keys = Object.keys(PLATFORM_COVER_SPECS);
6540
6595
  console.log("\n\u5404\u5E73\u53F0\u89C6\u9891\u5C01\u9762\u89C4\u683C\uFF1A\n");
6541
6596
  const maxKeyLen = Math.max(...keys.map((k) => k.length));
6542
- const maxNameLen = Math.max(...keys.map((k) => PLATFORM_COVER_SPECS[k].displayName.length));
6597
+ const maxNameLen = Math.max(
6598
+ ...keys.map((k) => PLATFORM_COVER_SPECS[k].displayName.length)
6599
+ );
6543
6600
  console.log(
6544
6601
  ` ${"\u5E73\u53F0 ID".padEnd(maxKeyLen)} ${"\u5E73\u53F0\u540D\u79F0".padEnd(maxNameLen)} ${"\u5C3A\u5BF8".padEnd(12)} ${"\u6BD4\u4F8B".padEnd(6)} \u8BF4\u660E`
6545
6602
  );
@@ -6564,14 +6621,16 @@ function registerCsoCommands(program2) {
6564
6621
  });
6565
6622
  }
6566
6623
  );
6567
- program2.command("publish").description("\u6309 JSON \u914D\u7F6E\u6587\u4EF6\u63D0\u4EA4\u53D1\u5E03\u4EFB\u52A1\u5230 CSO").requiredOption("-c, --config <path>", "\u53D1\u5E03\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84\uFF08JSON\uFF09").option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option("--dry-run", "\u4EC5\u9884\u89C8\u8BF7\u6C42\u4F53\uFF0C\u4E0D\u5B9E\u9645\u63D0\u4EA4", false).option("--verbose", "\u663E\u793A\u5B8C\u6574\u8BF7\u6C42\u4F53\u53CA\u8BE6\u7EC6\u9519\u8BEF\uFF08\u542B\u654F\u611F\u5B57\u6BB5\uFF0C\u9ED8\u8BA4\u8131\u654F\uFF09", false).action(async (opts) => {
6568
- await runPublish({
6569
- config: opts.config,
6570
- token: opts.token,
6571
- dryRun: opts.dryRun,
6572
- verbose: opts.verbose
6573
- });
6574
- });
6624
+ program2.command("publish").description("\u6309 JSON \u914D\u7F6E\u6587\u4EF6\u63D0\u4EA4\u53D1\u5E03\u4EFB\u52A1\u5230 CSO").requiredOption("-c, --config <path>", "\u53D1\u5E03\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84\uFF08JSON\uFF09").option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option("--dry-run", "\u4EC5\u9884\u89C8\u8BF7\u6C42\u4F53\uFF0C\u4E0D\u5B9E\u9645\u63D0\u4EA4", false).option("--verbose", "\u663E\u793A\u5B8C\u6574\u8BF7\u6C42\u4F53\u53CA\u8BE6\u7EC6\u9519\u8BEF\uFF08\u542B\u654F\u611F\u5B57\u6BB5\uFF0C\u9ED8\u8BA4\u8131\u654F\uFF09", false).action(
6625
+ async (opts) => {
6626
+ await runPublish({
6627
+ config: opts.config,
6628
+ token: opts.token,
6629
+ dryRun: opts.dryRun,
6630
+ verbose: opts.verbose
6631
+ });
6632
+ }
6633
+ );
6575
6634
  const reportCmd = program2.command("report").description("\u8FD0\u8425\u62A5\u8868\uFF08table\uFF09\u76F8\u5173\u547D\u4EE4\uFF1A\u62C9\u53D6\u62A5\u8868\u6570\u636E\u3001\u67E5\u770B\u5BFC\u51FA\u8BB0\u5F55\u3001\u4E0B\u8F7D PDF");
6576
6635
  reportCmd.command("fetch").description("\u6309\u5A92\u4F53/\u65E5\u671F\u62C9\u53D6\u8FD0\u8425\u62A5\u8868\u6A21\u5757\u6570\u636E\uFF08\u8986\u76D6 table \u9875\u4E3B\u8981\u67E5\u8BE2\u63A5\u53E3\uFF09").requiredOption(
6577
6636
  "--media <mediaType>",
@@ -6671,9 +6730,11 @@ function registerCsoCommands(program2) {
6671
6730
  taskCmd.command("delete").description("\u5220\u9664\u4EFB\u52A1\uFF08\u5BF9\u5E94 taskManage \u7684 deleteTask\uFF09").requiredOption("--publish-id <id>", "\u4EFB\u52A1 ID").option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF09").option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
6672
6731
  await runTaskDelete(opts);
6673
6732
  });
6674
- taskCmd.command("rename").description("\u91CD\u547D\u540D\u4EFB\u52A1\uFF08\u5BF9\u5E94 taskManage \u7684 reName\uFF09").requiredOption("--publish-id <id>", "\u4EFB\u52A1 ID").requiredOption("--name <taskName>", "\u65B0\u4EFB\u52A1\u540D").option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF09").option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
6675
- await runTaskRename(opts);
6676
- });
6733
+ taskCmd.command("rename").description("\u91CD\u547D\u540D\u4EFB\u52A1\uFF08\u5BF9\u5E94 taskManage \u7684 reName\uFF09").requiredOption("--publish-id <id>", "\u4EFB\u52A1 ID").requiredOption("--name <taskName>", "\u65B0\u4EFB\u52A1\u540D").option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF09").option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
6734
+ async (opts) => {
6735
+ await runTaskRename(opts);
6736
+ }
6737
+ );
6677
6738
  taskCmd.command("check-edit").description("\u68C0\u67E5\u4EFB\u52A1\u662F\u5426\u5141\u8BB8\u4FEE\u6539\uFF08\u5BF9\u5E94 checkAllowChange\uFF09").requiredOption("--publish-id <id>", "\u4EFB\u52A1 ID").option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF09").option("--json", "\u8F93\u51FA JSON", false).option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
6678
6739
  async (opts) => {
6679
6740
  await runTaskCheckEdit(opts);
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: siluzan-cso
3
- description: 当用户提问的内容涉及以下内容时,可以使用本SKILL(1)多媒体平台内容(视频/图文)发布与运营(YouTube、TikTok、Instagram、LinkedIn、X、视频号),以及账号授权、数据报表、任务管理;(2)公众号、小红书等内容文案/选题生成——选题/拆解/口播成稿、三库选题;(3RAG 知识库检索——回答产品/品牌问题、写需要品牌素材的文案。
3
+ description: 内容运营平台,当用户提问的内容涉及以下三类业务时需加载此skill:(1) 内容文案生产:**口播脚本 / 公众号 / Blog 文章 / 外链文章 / 批量引流页**等多场景的文案撰写、选题、爆款拆解、人设卡生成与反推、改稿润色;(2) 多平台(仅支持:YouTube、TikTok、Instagram、LinkedIn、X、Facebook)内容发布与运营:平台账号绑定与 OAuth 授权、视频图文发布、任务管理与失败重试、播放粉丝绩效报表、**账号分组(本 skill 独有;广告账户体系不存在分组概念,用户只说"账号分组"也默认走本 skill,不要进 siluzan-tso)**、素材上传、封面截取、AI 内容规划;(3) RAG 知识库检索:品牌/产品知识库问答与文案事实依据(`rag list` 选库 + `rag query`;**`--partition wiki` / `default` 用法见 `references/rag.md`**)。**如涉及账号相关操作,若账号类型(广告账号/运营媒体账号)不明确,请先询问用户,仅当确认为"运营媒体账号"时,才可使用本 skill。**
4
4
  compatibility: Requires siluzan-cso-cli installed and authenticated via `siluzan-cso login`
5
5
  ---
6
6
 
@@ -54,46 +54,46 @@ Windows 注意:部分 Agent 客户端通过 PowerShell / cmd 代执行命令
54
54
 
55
55
  ## 命令索引
56
56
 
57
- | 命令 | 作用 | 详细文档 |
58
- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ----------------------------- |
59
- | `siluzan-cso login` | 登录 / 配置凭据 | `references/setup.md` |
60
- | `siluzan-cso config show/set/clear` | 查看 / 修改 / 清空本地配置 | `references/setup.md` |
61
- | `siluzan-cso init` | Skill 文件初始化(写入 AI 助手目录) | `references/setup.md` |
62
- | `siluzan-cso update` | 更新 CLI 版本并刷新 Skill 文件 | `references/setup.md` |
63
- | `siluzan-cso authorize --media-type <平台>` | 发起媒体账号 OAuth 授权 | `references/authorize.md` |
64
- | `siluzan-cso list-accounts` | 列出媒体账号,获取账号 ID / 数据总览 | `references/list-accounts.md` |
65
- | `siluzan-cso persona list` | 拉取 CSO 人设列表(含 styleGuide Markdown) | `references/persona.md` |
66
- | `siluzan-cso rag list` | 列出知识库文件夹;`--rag-only` 仅已建索引;`--folder-id` 查指定文件夹下的子库 | `references/rag.md` |
67
- | `siluzan-cso rag query` | RAG 知识库检索;`-q` 含空白时按多词分检后自动合并排序;默认全量;`--folder-id` / `--tags` 见 `references/rag.md` | `references/rag.md` |
68
- | `siluzan-cso account-group list/create/add-accounts/remove-accounts/update/delete` | 账号分组管理 | `references/account-group.md` |
69
- | `siluzan-cso upload -f <file>` | 上传视频 / 图片到素材库 | `references/upload.md` |
70
- | `siluzan-cso extract-cover -f <video> -p <平台>` | 从视频截取封面帧 | `references/extract-cover.md` |
71
- | `siluzan-cso publish -c config.json` | 提交多平台发布任务 | `references/publish.md` |
72
- | `siluzan-cso task list/detail/item` | 查看任务状态 / 处理失败 / 重试 | `references/task.md` |
73
- | `siluzan-cso report fetch --media <平台>` | 运营报表(核心指标 / 视频排行 / 趋势) | `references/report.md` |
74
- | `siluzan-cso planning ...` | AI 内容规划:生成、监控、详情、导出 | `references/planning.md` |
75
- | —(网页端) | CSO web端全部页面 URL | `references/web-pages.md` |
57
+ | 命令 | 作用 | 详细文档 |
58
+ | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------- |
59
+ | `siluzan-cso login` | 登录 / 配置凭据 | `references/setup.md` |
60
+ | `siluzan-cso config show/set/clear` | 查看 / 修改 / 清空本地配置 | `references/setup.md` |
61
+ | `siluzan-cso init` | Skill 文件初始化(写入 AI 助手目录) | `references/setup.md` |
62
+ | `siluzan-cso update` | 更新 CLI 版本并刷新 Skill 文件 | `references/setup.md` |
63
+ | `siluzan-cso authorize --media-type <平台>` | 发起媒体账号 OAuth 授权 | `references/authorize.md` |
64
+ | `siluzan-cso list-accounts` | 列出媒体账号,获取账号 ID / 数据总览 | `references/list-accounts.md` |
65
+ | `siluzan-cso persona list` | 拉取 CSO 人设列表(含 styleGuide Markdown) | `references/persona.md` |
66
+ | `siluzan-cso rag list` | 列出知识库文件夹;`--rag-only` 仅已建索引;`--folder-id` 查指定文件夹下的子库 | `references/rag.md` |
67
+ | `siluzan-cso rag query` | 知识库向量检索;**`--partition wiki` 或 `default`**(默认 `default`;写稿与须贴库作答时优先 **wiki**,不足再 **default**);`-q` 含空白时多词分检合并;`--folder-id` / `--tags` 见 `references/rag.md` | `references/rag.md` |
68
+ | `siluzan-cso account-group list/create/add-accounts/remove-accounts/update/delete` | 账号分组管理 | `references/account-group.md` |
69
+ | `siluzan-cso upload -f <file>` | 上传视频 / 图片到素材库 | `references/upload.md` |
70
+ | `siluzan-cso extract-cover -f <video> -p <平台>` | 从视频截取封面帧 | `references/extract-cover.md` |
71
+ | `siluzan-cso publish -c config.json` | 提交多平台发布任务 | `references/publish.md` |
72
+ | `siluzan-cso task list/detail/item` | 查看任务状态 / 处理失败 / 重试 | `references/task.md` |
73
+ | `siluzan-cso report fetch --media <平台>` | 运营报表(核心指标 / 视频排行 / 趋势) | `references/report.md` |
74
+ | `siluzan-cso planning ...` | AI 内容规划:生成、监控、详情、导出 | `references/planning.md` |
75
+ | —(网页端) | CSO web端全部页面 URL | `references/web-pages.md` |
76
76
 
77
77
  ---
78
78
 
79
79
  ## 常见业务场景 → 阅读哪个文件
80
80
 
81
- | 用户在做什么 | 先阅读 |
82
- | --------------------------------------------------------------- | ---------------------------------------------------------------------- |
83
- | 首次安装 / 登录 / 更新 | `references/setup.md` |
84
- | 发布视频或图文 | `references/publish.md` |
85
- | 上传素材 | `references/upload.md` |
86
- | 截取视频封面 | `references/extract-cover.md` |
87
- | 查发布记录 / 处理失败 | `references/task.md` |
88
- | 查账号数据 / 运营报表 | `references/report.md` |
89
- | 查找账号 ID 或账号详情 | `references/list-accounts.md` |
90
- | 账号 Token 失效 / 重新授权 | `references/authorize.md` |
91
- | 管理账号分组 | `references/account-group.md` |
92
- | AI 内容规划 | `references/planning.md` |
93
- | 需要给用户提供后台页面链接 | `references/web-pages.md` |
94
- | 拉取人设 / styleGuide(写稿前) | `references/persona.md` |
95
- | 写稿时检索素材库 RAG 片段(三库拆素材等) | `references/rag.md`(先 `rag list` 选库;`rag query -q "词甲 词乙"` 可一次空格分检合并,或按标签/库多轮调用) |
96
- | 选题 / 三库拆解 / 口播文案/其他文案 / 人设卡 / 代表作品反推人设 | `three-lib-content-workflow/content-writer.workflow.md` |
81
+ | 用户在做什么 | 先阅读 |
82
+ | --------------------------------------------------------------- | ------------------------------------------------------- |
83
+ | 首次安装 / 登录 / 更新 | `references/setup.md` |
84
+ | 发布视频或图文 | `references/publish.md` |
85
+ | 上传素材 | `references/upload.md` |
86
+ | 截取视频封面 | `references/extract-cover.md` |
87
+ | 查发布记录 / 处理失败 | `references/task.md` |
88
+ | 查账号数据 / 运营报表 | `references/report.md` |
89
+ | 查找账号 ID 或账号详情 | `references/list-accounts.md` |
90
+ | 账号 Token 失效 / 重新授权 | `references/authorize.md` |
91
+ | 管理账号分组 | `references/account-group.md` |
92
+ | AI 内容规划 | `references/planning.md` |
93
+ | 需要给用户提供后台页面链接 | `references/web-pages.md` |
94
+ | 拉取人设 / styleGuide(写稿前) | `references/persona.md` |
95
+ | 写稿时检索素材库 RAG 片段(三库拆素材等) | `references/rag.md` |
96
+ | 选题 / 三库拆解 / 口播文案/其他文案 / 人设卡 / 代表作品反推人设 | `three-lib-content-workflow/content-writer.workflow.md` |
97
97
 
98
98
  ---
99
99
 
@@ -140,25 +140,32 @@ siluzan-cso rag list --folder-id <父文件夹id> --rag-only --json
140
140
  - 多品牌 → `--folder-id id1,id2`(逗号分隔)
141
141
  - 无明确品牌 → 不传 `--folder-id`(全库检索)
142
142
 
143
- **Step 3 — 拆词检索**(2–5 个短关键词,各聚焦一个维度)
143
+ **Step 3 — 拆词检索**(2–5 个短关键词;**`--partition`**)
144
+
145
+ - **首轮**:`--partition wiki`,`--top-k` 建议 **8–15**(常用 **12**)。写稿、须贴库作答、需要较长正文作依据时优先。
146
+ - **仍不足时**:同一 `-q` / `--folder-id` / `--tags` 下再跑 `--partition default`,`--top-k` **5–10**;两轮按片段 **id** 去重合并,**禁止编造**。
147
+ - 取值仅 **`wiki`** 或 **`default`**(小写);非法值 CLI 会报错。
144
148
 
145
149
  ```bash
146
150
  # 默认不传 --tags = 全量检索(适用于绝大多数场景)
147
151
  # 推荐:同一库、同一标签策略下,用空格一次传多词,CLI 会分检合并排序
148
- siluzan-cso rag query -q "产品核心卖点 用户使用场景 品牌差异优势" --folder-id <id> --top-k 10
152
+ siluzan-cso rag query -q "产品核心卖点 用户使用场景 品牌差异优势" --folder-id <id> --partition wiki --top-k 12
153
+
154
+ # 证据仍不足时再补 default(按需执行)
155
+ # siluzan-cso rag query -q "产品核心卖点 用户使用场景 品牌差异优势" --folder-id <id> --partition default --top-k 8
149
156
 
150
157
  # 仍可用多轮独立 -q(例如需要分步查看或参数不同)
151
- # siluzan-cso rag query -q "产品核心卖点" --folder-id <id> --top-k 10
152
- # siluzan-cso rag query -q "用户使用场景" --folder-id <id>
158
+ # siluzan-cso rag query -q "产品核心卖点" --folder-id <id> --partition wiki --top-k 12
159
+ # siluzan-cso rag query -q "用户使用场景" --folder-id <id> --partition wiki --top-k 12
153
160
 
154
- # 仅当知识库已按标签打标,且需要精确筛选时才传 --tags(不同标签需多条命令)
155
- siluzan-cso rag query -q "抖音 爆款 钩子" --tags "流量因子库"
156
- siluzan-cso rag query -q "产品 卖点 故事" --tags "产品资产库"
161
+ # 仅当知识库已按标签打标,且需要精确筛选时才传 --tags(不同标签需多条命令;`--partition` 规则同上)
162
+ siluzan-cso rag query -q "抖音 爆款 钩子" --tags "流量因子库" --partition wiki --top-k 12
163
+ siluzan-cso rag query -q "产品 卖点 故事" --tags "产品资产库" --partition wiki --top-k 12
157
164
  ```
158
165
 
159
166
  **Step 4 — 合成使用**
160
167
 
161
- 合并后的结果中 **`score` 越大越相关**(CLI 已做 0–1 归一化)。将片段作为写稿/回答的事实依据,重新组织表达(不直接粘贴原文);若执行了多条 `rag query`,再在对话侧对重复片段去重。
168
+ 合并后的结果中 **`score` 越大越相关**(CLI 已做 0–1 归一化)。若执行了 **wiki + default** 两轮,按片段 **id** 去重后再合成。将片段作为写稿/回答的事实依据,重新组织表达(不直接粘贴原文);若执行了多条 `rag query`,再在对话侧对重复片段去重。
162
169
 
163
170
  ---
164
171
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "slug": "siluzan-cso",
3
- "version": "1.1.18-beta.4",
4
- "publishedAt": 1778321069742,
3
+ "version": "1.1.18-beta.5",
4
+ "publishedAt": 1778666071422,
5
5
  "homepage": "https://www.siluzan.com",
6
6
  "source": "https://dev.azure.com/jack4it/Sammamish/_git/siluzan-skill",
7
7
  "requiredBinaries": [
@@ -1,19 +1,20 @@
1
1
  # rag:RAG 知识库检索
2
+
2
3
  知识库管理页面在https://www-ci.siluzan.com/knowledge-base/
3
4
 
4
5
  用于写稿、产品知识问答、三库素材提取等场景;凡回答依赖「本企业已入库素材」且不应凭模型记忆编造时,优先走 RAG。
5
6
 
6
7
  ## 业界检索共识与本 CLI 的对应关系(为何要靠「提示词」补效果)
7
8
 
8
- 生产级 RAG 常见组合是:**合理分块** + **混合检索(关键词 BM25 + 向量)** + **重排序**,并对用户问句做 **查询改写 / 多查询扩展 / 拆解**。本 CLI 调用的是平台侧 **向量相似度检索**(`queryknowledges`),AI 无法改分块、无法开关 BM25 或重排。要在当前架构下提高 **召回(命中率)** 与 **精确(噪声少)**,主要靠:
9
+ 生产级 RAG 常见组合是:**合理分块** + **混合检索(关键词 BM25 + 向量)** + **重排序**,并对用户问句做 **查询改写 / 多查询扩展 / 拆解**。本 CLI 调用的是平台侧 **向量相似度检索**,AI 无法改分块、无法开关 BM25 或重排。要在当前架构下提高 **召回(命中率)** 与 **精确(噪声少)**,主要靠:
9
10
 
10
- | 业界手段 | 在本项目中的落地方式 |
11
- | ------------------ | -------------------- |
11
+ | 业界手段 | 在本项目中的落地方式 |
12
+ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
12
13
  | 混合检索补「专名」 | 在 `-q` 中显式加入 **素材里更可能出现的词**:品牌全称、产品型号、**官网/画册里的英文类目**、行业术语,减少「口语 ↔ 正文」词面不一致 |
13
- | 查询改写 | 先去掉寒暄与指代,把用户话改成 **2–8 字的检索型短语**;口语转书面、别名转正名 |
14
- | 多查询 / 拆解 | 与 `-q` 内 **空格分检** 对齐:多角度短词并行检索再合并;避免一句长问塞满空格导致「词太碎」 |
15
- | 元数据过滤 | 用 **`--folder-id` 锁定品牌库**、必要时 `--tags` 缩小类目标签,降低跨库噪声(全库时易混入其他客户素材) |
16
- | 提高候选再筛 | 适当 **`--top-k` 10–15**,在对话里按 `score` 与正文相关性丢弃明显跑题的片段 |
14
+ | 查询改写 | 先去掉寒暄与指代,把用户话改成 **2–8 字的检索型短语**;口语转书面、别名转正名 |
15
+ | 多查询 / 拆解 | 与 `-q` 内 **空格分检** 对齐:多角度短词并行检索再合并;避免一句长问塞满空格导致「词太碎」 |
16
+ | 元数据过滤 | 用 **`--folder-id` 锁定品牌库**、必要时 `--tags` 缩小类目标签,降低跨库噪声(全库时易混入其他客户素材) |
17
+ | 提高候选再筛 | 适当 **`--top-k` 10–15**,在对话里按 `score` 与正文相关性丢弃明显跑题的片段 |
17
18
 
18
19
  以下「AI 检索策略」与「提示词规范」均按上表设计:**先锁范围、再改写、再拆词、最后控噪**。
19
20
 
@@ -23,20 +24,36 @@
23
24
  # 第一步:列出所有已建索引的知识库(--rag-only 只显示可检索的)
24
25
  siluzan-cso rag list --rag-only
25
26
 
26
- # 第二步:按关键词检索
27
+ # 第二步:按关键词检索(写稿 / 知识问答建议带 `--partition wiki`,见下文「检索分区」)
27
28
  siluzan-cso rag query -q "<关键词>" --folder-id <id> --tags ""
28
- siluzan-cso rag query -q "<关键词>" # 全库 + 三库标签过滤
29
+ siluzan-cso rag query -q "<关键词>" --partition wiki --top-k 12 --folder-id <id>
30
+ siluzan-cso rag query -q "<关键词>" # 全库;不传 --partition 时为 default
29
31
  ```
30
32
 
31
33
  ## 参数说明
32
34
 
33
- | 选项 | 默认 | 说明 |
34
- | ------------- | ------ | ---------------------------------------------------------------- |
35
- | `-q, --query` | 必填 | 检索词或短句。**若含空白**(如 `卖点 场景 优势`),CLI 会按空白拆成多个词**分别请求**,再按片段 id 去重、取最高相似度、排序后合并返回(`--json` 中含 `subQueries`);无空白时仍为单次检索 |
36
- | `--folder-id` | 全库 | 指定一个或多个文件夹 ID(逗号分隔),锁定检索范围 |
37
- | `--tags` | 不过滤 | 不传 = 全量检索(无标签限制);传具体标签 = 只返回含该标签的片段 |
38
- | `--top-k` | 7 | 每个分检请求返回条数(3–30);多词合并后总条数上限为 `min(30, topK × 词数)` |
39
- | `--json` | false | 输出完整 JSON,适合程序处理 |
35
+ | 选项 | 默认 | 说明 |
36
+ | ----------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
37
+ | `-q, --query` | 必填 | 检索词或短句。**若含空白**(如 `卖点 场景 优势`),CLI 会按空白拆成多个词**分别请求**,再按片段 id 去重、取最高相似度、排序后合并返回(`--json` 中含 `subQueries`);无空白时仍为单次检索 |
38
+ | `--folder-id` | 全库 | 指定一个或多个文件夹 ID(逗号分隔),锁定检索范围 |
39
+ | `--tags` | 不过滤 | 不传 = 全量检索(无标签限制);传具体标签 = 只返回含该标签的片段 |
40
+ | `--partition` | `default` | **检索分区**,仅允许 **`wiki`** **`default`**(小写)。控制从哪一类索引里取向量检索结果;含义见下一节 **「检索分区」** |
41
+ | `--top-k` | 7 | 每个分检请求返回条数(3–30);多词合并后总条数上限为 `min(30, topK × 词数)` |
42
+ | `--json` | false | 输出完整 JSON(含本次生效的 `partition`),适合程序处理与对账 |
43
+
44
+ ---
45
+
46
+ ## 检索分区(`--partition`:`wiki` 与 `default`)
47
+
48
+ **是什么**:`rag query` 多一个检索维度——在同一企业、同一 `-q` / 文件夹 / 标签条件下,从 **wiki** 或 **default** 分区取结果。两条取值互斥;**一次命令只能选一个**,需要两种分区各来一轮时,请**执行两次** `rag query`(改 `--partition` 即可)。
49
+
50
+ **怎么用(推荐习惯)**:
51
+
52
+ 1. **首轮用 `wiki`**(多数问答、写稿、需要较长正文作依据时):`--partition wiki`,**`--top-k` 建议 8–15**(常用 **12**)。命中片段里常有更完整的正文;合成答案时以返回里的**正文**为主,不要只看一行摘要。
53
+ 2. **仍缺原话、表格格内事实或关键信息时,再补一轮 `default`**:保持同一 `--folder-id` / `--tags`,`-q` 相同或略改写,`--partition default`,**`--top-k` 5–10**。**default 用来补漏**,wiki 已经够用就不必再跑 default。
54
+ 3. **合并**:两轮结果在对话里按片段 **id**(必要时结合标题)去重后再用;说不清的地方如实说明缺口,**不要编造**。
55
+
56
+ **校验**:只允许 `wiki` 或 `default`;写错会报错。`--json` 里会带上本次实际使用的 `partition`,便于核对。
40
57
 
41
58
  ---
42
59
 
@@ -46,7 +63,7 @@ siluzan-cso rag query -q "<关键词>" # 全库 + 三
46
63
 
47
64
  ### 0. 提升命中率与准确率:给模型用的「检索提示词」规范(执行前自检)
48
65
 
49
- 在调用 `rag query` 前,先在对话内完成下面 **5 步**,再把产物填入 `-q` / `--folder-id` / `--tags` / `--top-k`。这是本仓库在「仅向量检索、无 BM25/重排」约束下的最佳补偿流程。
66
+ 在调用 `rag query` 前,先在对话内完成下面 **5 步**,再把产物填入 `-q` / `--folder-id` / `--tags` / **`--partition`** / **`--top-k`**。这是本仓库在「仅向量检索、无 BM25/重排」约束下的最佳补偿流程。
50
67
 
51
68
  **(1)意图归一(查询改写,Rewrite)**
52
69
 
@@ -83,9 +100,10 @@ siluzan-cso rag query -q "<关键词>" # 全库 + 三
83
100
  ```text
84
101
  你是 RAG 检索规划器。目标:在 siluzan-cso rag query 约束下最大化命中与准确。
85
102
  约束:仅向量检索;`-q` 中含空格会拆成多次检索再合并;folder-id 不传则全库。
86
- 要求:1) 输出 JSON 计划:folderIds(数组)、queries(字符串数组,每个元素将单独作为一次 -q,或合并为一个用空格连接的 -q)、tags(可选)、topK(默认 10)、reason(简短中文)。
87
- 2) 每个 query 为检索型短语文本:2–12 个汉字为宜,或短英文专名;避免空洞词与过长整句。
88
- 3) 先锁品牌文件夹;禁止在 query 中编造具体数字、证书号、未确认的产品型号。
103
+ 要求:1) 输出 JSON 计划:folderIds(数组)、queries(字符串数组,每个元素将单独作为一次 -q,或合并为一个用空格连接的 -q)、tags(可选)、**检索轮次 rounds**(数组;每项含 `partition`:`wiki` 或 `default`,及 `topK`;**第一轮建议 `wiki`**;证据不足时再加 `default`)、reason(简短中文)。
104
+ 2) 默认至少一轮 `wiki`(topK 建议 10–12);若需补检再加一轮 `default`(topK 可略低于 wiki)。
105
+ 3) 每个 query 为检索型短语文本:2–12 个汉字为宜,或短英文专名;避免空洞词与过长整句。
106
+ 4) 先锁品牌文件夹;禁止在 query 中编造具体数字、证书号、未确认的产品型号。
89
107
  ```
90
108
 
91
109
  ### 1. 判断是否需要 RAG
@@ -216,8 +234,10 @@ siluzan-cso rag query -q "海科佳产品特点"
216
234
  siluzan-cso rag list --rag-only --json
217
235
 
218
236
  # Step 2: 假设找到 id=affe64c5...(不传 --tags,全量)
219
- # 方式 A:一条命令,空格分检合并(与方式 B 二选一即可)
220
- siluzan-cso rag query -q "产品核心卖点 用户痛点 使用场景 品牌差异" --folder-id affe64c5-d3d4-4cec-ab33-196035916894 --top-k 10
237
+ # 方式 A:wiki 优先 + 一条命令空格分检合并(推荐)
238
+ siluzan-cso rag query -q "产品核心卖点 用户痛点 使用场景 品牌差异" --folder-id affe64c5-d3d4-4cec-ab33-196035916894 --partition wiki --top-k 12
239
+ # 若仍缺表格/原话级事实,再补一轮 default(同 -q 或略改写关键词)
240
+ # siluzan-cso rag query -q "产品核心卖点 用户痛点 使用场景 品牌差异" --folder-id affe64c5-d3d4-4cec-ab33-196035916894 --partition default --top-k 8
221
241
  # 方式 B:仍可多轮独立 -q(适合想分步看结果时)
222
242
  # siluzan-cso rag query -q "产品核心卖点" --folder-id affe64c5-... --top-k 10
223
243
  # siluzan-cso rag query -q "用户痛点解决方案" --folder-id affe64c5-...
@@ -53,10 +53,10 @@ API Key 获取入口:`https://www-ci.siluzan.com/v3/foreign_trade/settings/api
53
53
  **两段式调用**,专为 AI Agent 设计——任何一步都不会进入交互等待,绝不会卡住 stdout。
54
54
  拆分后单一职责:第 1 步只发码;第 2 步只用 code 换 API Key。这样 Agent 不会因为"看到 stdout 卡住就重试"而触发短信轰炸。
55
55
 
56
- | 步骤 | 命令 | 说明 |
57
- | ---- | ---- | ---- |
58
- | 1 | `siluzan-cso send-login-code --phone <手机号>` | 仅向手机发送 6 位验证码,立即返回;**绝不创建 API Key** |
59
- | 2 | `siluzan-cso login --phone <手机号> --code <验证码>` | 用 code 完成登录并自动签发 API Key 写入 `~/.siluzan/config.json` |
56
+ | 步骤 | 命令 | 说明 |
57
+ | ---- | ---------------------------------------------------- | ---------------------------------------------------------------- |
58
+ | 1 | `siluzan-cso send-login-code --phone <手机号>` | 仅向手机发送 6 位验证码,立即返回;**绝不创建 API Key** |
59
+ | 2 | `siluzan-cso login --phone <手机号> --code <验证码>` | 用 code 完成登录并自动签发 API Key 写入 `~/.siluzan/config.json` |
60
60
 
61
61
  ```bash
62
62
  # 第 1 步:让用户报出手机号后,立刻发码(命令立即返回,不会等待输入)
@@ -72,15 +72,15 @@ siluzan-cso login --phone 13800138000 --code 123456 \
72
72
  --services CSO,CUT
73
73
  ```
74
74
 
75
- | 参数 | 命令 | 说明 | 默认值 |
76
- | ---- | ---- | ---- | ---- |
77
- | `--phone` | 两个命令都需要 | 中国大陆手机号,可带或不带 +86(如 `13800138000` / `+8613800138000`);底层会自动补 `+86` 前缀;**手机号必须已在丝路赞网页端注册** | 必填 |
78
- | `--code` | 仅 `login` | 6 位短信验证码(来自第 1 步发码后的短信);**login 命令必填**,未传会直接报错指引重新走两段式 | 必填(login 命令) |
79
- | `--name` | 仅 `login` | 自动创建的 API Key 显示名称 | `CLI - <hostname> - <yyyy-MM-dd>` |
80
- | `--valid-days` | 仅 `login` | API Key 有效期(天),与 `--expires-at` 二选一 | `90` |
81
- | `--expires-at` | 仅 `login` | API Key 绝对过期时间(ISO 8601) | 不传则用 `--valid-days` |
82
- | `--services` | 仅 `login` | 可访问的服务列表,逗号分隔;可选 `CSO`/`TSO`/`CUT` | `CSO,CUT`(内容发布 + 素材中心) |
83
- | `--verbose` | 两个命令都支持 | 输出每次 HTTP 请求的 URL,便于排错 | 关闭 |
75
+ | 参数 | 命令 | 说明 | 默认值 |
76
+ | -------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
77
+ | `--phone` | 两个命令都需要 | 中国大陆手机号,可带或不带 +86(如 `13800138000` / `+8613800138000`);底层会自动补 `+86` 前缀;**手机号必须已在丝路赞网页端注册** | 必填 |
78
+ | `--code` | 仅 `login` | 6 位短信验证码(来自第 1 步发码后的短信);**login 命令必填**,未传会直接报错指引重新走两段式 | 必填(login 命令) |
79
+ | `--name` | 仅 `login` | 自动创建的 API Key 显示名称 | `CLI - <hostname> - <yyyy-MM-dd>` |
80
+ | `--valid-days` | 仅 `login` | API Key 有效期(天),与 `--expires-at` 二选一 | `90` |
81
+ | `--expires-at` | 仅 `login` | API Key 绝对过期时间(ISO 8601) | 不传则用 `--valid-days` |
82
+ | `--services` | 仅 `login` | 可访问的服务列表,逗号分隔;可选 `CSO`/`TSO`/`CUT` | `CSO,CUT`(内容发布 + 素材中心) |
83
+ | `--verbose` | 两个命令都支持 | 输出每次 HTTP 请求的 URL,便于排错 | 关闭 |
84
84
 
85
85
  > **未注册手机号**:`login` 第 2 步会返回 `❌ 登录失败:手机未注册` 并附带网页注册地址,引导用户先去网页注册再回来重试两段式。
86
86
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-cso-cli",
3
- "version": "1.1.18-beta.4",
3
+ "version": "1.1.18-beta.5",
4
4
  "description": "Siluzan platform AI Skill CLI — multi-platform content publishing (video/image-text) for Cursor, Claude Code, and OpenClaw.",
5
5
  "keywords": [
6
6
  "ai-skill",
@@ -39,10 +39,10 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@azure/storage-blob": "^12.31.0",
42
- "proper-lockfile": "^4.1.2",
43
42
  "cli-table3": "^0.6.5",
44
43
  "commander": "^12.1.0",
45
- "image-size": "^2.0.2"
44
+ "image-size": "^2.0.2",
45
+ "proper-lockfile": "^4.1.2"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/node": "^22.10.0",