koishi-plugin-image-prompt 1.1.1 → 1.2.0

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/lib/index.d.ts CHANGED
@@ -9,7 +9,7 @@ export declare const inject: {
9
9
  required: string[];
10
10
  optional: string[];
11
11
  };
12
- export declare const usage = "\n---\n\n\u6B64\u63D2\u4EF6\u76F4\u63A5\u8C03\u7528 OpenAI \u517C\u5BB9\u7684 Chat Completions \u63A5\u53E3\u751F\u6210\u56FE\u7247\n\n\u8BF7\u5728\u63D2\u4EF6\u8BBE\u7F6E\u4E2D\u586B\u5199\uFF1A\n\n- API \u670D\u52A1\u5668\u5730\u5740\uFF08baseUrl\uFF09\n- \u4F7F\u7528\u7684\u6A21\u578B\uFF08model\uFF09\n- API \u5BC6\u94A5\uFF08apiKey\uFF09\n\n\u3010AI \u9009\u62E9\u53C2\u8003\u56FE\u7247\u3011\n\n1. \u5728\u300C\u53C2\u8003\u56FE\u7247\u7EC4\u300D\u4E2D\u6CE8\u518C\u5206\u7EC4\uFF1A\u6BCF\u7EC4\u586B\u5199\u82E5\u5E72\u5F20\u300C\u56FE\u7247\u94FE\u63A5 + \u63CF\u8FF0\u300D\uFF08\u63CF\u8FF0\u7528\u4E8E\u8BA9 AI \u5224\u65AD\u8BE5\u56FE\u7684\u7528\u9014\uFF09\u3002\n2. \u5728\u6307\u4EE4\u914D\u7F6E\u7684\u300C\u5F15\u7528\u7684\u53C2\u8003\u56FE\u7247\u7EC4\u540D\u79F0\u300D\u4E2D\u586B\u5165\u7EC4\u540D\uFF08\u53EF\u586B\u591A\u4E2A\uFF09\uFF0C\u8BE5\u6307\u4EE4\u6267\u884C\u65F6\u4F1A\u628A\u7EC4\u5185\u56FE\u7247\u5168\u90E8\u4EA4\u7ED9 AI \u6311\u9009\u3002\n3. \u300CAI \u9009\u56FE\u8BBE\u7F6E\u300D\u53EF\u4FEE\u6539\u5BF9\u8BDD\u6A21\u578B\uFF08\u9ED8\u8BA4 Qwen/Qwen2.5-7B-Instruct\uFF09\u3001\u9009\u62E9\u63D0\u793A\u8BCD\u6A21\u677F\u3001\u8D85\u65F6\u4E0E\u91CD\u8BD5\u7B49\uFF1B\n \u63A5\u53E3\u5730\u5740/\u5BC6\u94A5\u7559\u7A7A\u65F6\u590D\u7528\u7ED8\u56FE\u63A5\u53E3\u7684\u914D\u7F6E\u3002\n4. \u5F53 AI \u5224\u5B9A\u5019\u9009\u56FE\u7247\u91CC\u6CA1\u6709\u5408\u9002\u7684\u53C2\u8003\u56FE\u65F6\uFF0C\u4F1A\u6309\u914D\u7F6E\u8BE2\u95EE\u7528\u6237\u8865\u5145\u53D1\u9001\u56FE\u7247\uFF08\u7528\u6237\u53D1\u9001\u540E\u4F1A\u88AB\u76F4\u63A5\u4F7F\u7528\uFF09\u3002\n \u82E5\u9009\u56FE\u6A21\u578B\u652F\u6301\u8BC6\u522B\u56FE\u7247\uFF08\u591A\u6A21\u6001\uFF09\uFF0C\u53EF\u52FE\u9009\u300C\u9009\u56FE\u6A21\u578B\u652F\u6301\u8BC6\u522B\u56FE\u7247\u300D\uFF0C\u63D2\u4EF6\u4F1A\u628A\u5019\u9009\u56FE\u7247\u672C\u8EAB\u53D1\u7ED9\u6A21\u578B\uFF0C\n \u6A21\u578B\u5BF9\u7740\u771F\u5B9E\u56FE\u7247\u6311\u9009\uFF1B\u5B83\u7ED9\u51FA\u7684\u5173\u952E\u89C6\u89C9\u7279\u5F81\u8FD8\u4F1A\u5E76\u5165\u7ED8\u56FE\u63D0\u793A\u8BCD\uFF0C\u8BA9\u51FA\u56FE\u66F4\u8FD8\u539F\u53C2\u8003\u56FE\u3002\n5. \u300C\u542F\u7528 AI \u667A\u80FD\u9009\u62E9\u53C2\u8003\u56FE\u7247\u300D\u9ED8\u8BA4\u5173\u95ED\uFF0C\u9700\u624B\u52A8\u5F00\u542F\uFF1B\u300CAI \u9009\u56FE\u5931\u8D25\u65F6\u56DE\u9000\u4E3A\u4F7F\u7528\u5019\u9009\u6C60\u5185\u5168\u90E8\u56FE\u7247\u300D\u9ED8\u8BA4\u5173\u95ED\uFF0C\n \u5931\u8D25\u65F6\u672C\u6B21\u4E0D\u4F7F\u7528\u53C2\u8003\u56FE\u7247\uFF08\u5F00\u542F\u5219\u6539\u7528\u5019\u9009\u6C60\u5185\u5168\u90E8\u56FE\u7247\uFF09\u3002\n6. \u53C2\u8003\u56FE\u8F83\u591A\u65F6\uFF08\u8D85\u8FC7 12 \u5F20\uFF09\u81EA\u52A8\u8D70\u4E24\u7EA7\u68C0\u7D22\uFF1A\u5148\u8BA9\u6A21\u578B\u4EA7\u51FA\u68C0\u7D22\u5173\u952E\u8BCD\u3001\u672C\u5730\u5339\u914D\u53EC\u56DE\uFF0C\u518D\u5BF9\u53EC\u56DE\u7ED3\u679C\u7CBE\u6392\uFF1B\n \u8BC6\u56FE\u6A21\u5F0F\u4E0B\u4E5F\u53EA\u53D1\u9001\u53EC\u56DE\u7684\u8FD9\u51E0\u5F20\u56FE\u7247\uFF0C\u907F\u514D\u6BCF\u6B21\u90FD\u628A\u6574\u4E2A\u56FE\u5E93\u53D1\u7ED9\u6A21\u578B\u3002\n7. \u63CF\u8FF0\u53EF\u4EE5\u7528\u300C\u751F\u6210\u63CF\u8FF0\u300D\u6307\u4EE4\u8BA9\u6A21\u578B\u770B\u56FE\u81EA\u52A8\u751F\u6210\u5E76\u5199\u56DE\u914D\u7F6E\uFF08\u9700\u8981\u652F\u6301\u56FE\u7247\u8F93\u5165\u7684\u6A21\u578B\uFF09\u3002\n8. \u751F\u6210\u7ED3\u679C\u53EF\u81EA\u52A8\u5165\u5E93\uFF08\u9ED8\u8BA4\u5173\u95ED\uFF09\uFF0C\u4E0B\u6B21\u80FD\u88AB\u81EA\u5DF1\u68C0\u7D22\u5230\u5E76\u590D\u7528\uFF0C\u5F62\u6210\u95ED\u73AF\uFF1B\n \u5F00\u542F\u300C\u540E\u53F0\u7ED8\u56FE\u300D\u540E\u51FA\u56FE\u4E0D\u518D\u963B\u585E\uFF0C\u5148\u56DE\u300C\u6B63\u5728\u753B\u300D\uFF0C\u753B\u597D\u4E3B\u52A8\u63A8\u9001\u3002\n\n\u63D0\u793A\u8BCD\u6A21\u677F\u53EF\u7528\u5360\u4F4D\u7B26\uFF1A{candidates} \u5019\u9009\u56FE\u7247\u5217\u8868\u3001{userInput} \u7528\u6237\u9644\u52A0\u9700\u6C42\u3001{max} \u6700\u591A\u9009\u62E9\u6570\u91CF\u3001{command} \u6307\u4EE4\u540D\u3001{prompt} \u6307\u4EE4\u63D0\u793A\u8BCD\n\n---\n\u6B64\u9879\u76EE\u6240\u9700\u7684koishi\u670D\u52A1\uFF1A\u5FC5\u9700 'http', 'logger', 'i18n'\uFF1B\u53EF\u9009 'puppeteer'\uFF08\u4EC5\u300C\u6587\u5B57\u6E32\u67D3\u53C2\u8003\u56FE\u300D\u9700\u8981\uFF09\n\n---\n";
12
+ export declare const usage = "\n---\n\n\u6B64\u63D2\u4EF6\u76F4\u63A5\u8C03\u7528 OpenAI \u517C\u5BB9\u7684 Chat Completions \u63A5\u53E3\u751F\u6210\u56FE\u7247\n\n\u8BF7\u5728\u63D2\u4EF6\u8BBE\u7F6E\u4E2D\u586B\u5199\uFF1A\n\n- API \u670D\u52A1\u5668\u5730\u5740\uFF08baseUrl\uFF09\n- \u4F7F\u7528\u7684\u6A21\u578B\uFF08model\uFF09\n- API \u5BC6\u94A5\uFF08apiKey\uFF09\n\n\u3010AI \u9009\u62E9\u53C2\u8003\u56FE\u7247\u3011\n\n1. \u5728\u300C\u53C2\u8003\u56FE\u7247\u7EC4\u300D\u4E2D\u6CE8\u518C\u5206\u7EC4\uFF1A\u6BCF\u7EC4\u586B\u5199\u82E5\u5E72\u5F20\u300C\u56FE\u7247\u94FE\u63A5 + \u63CF\u8FF0\u300D\uFF08\u63CF\u8FF0\u7528\u4E8E\u8BA9 AI \u5224\u65AD\u8BE5\u56FE\u7684\u7528\u9014\uFF09\u3002\n **\u63CF\u8FF0\u4E00\u5B9A\u8981\u5199\u5177\u4F53**\uFF08\u53D1\u8272\u53D1\u578B\u3001\u670D\u88C5\u3001\u52A8\u4F5C\u3001\u753B\u98CE\uFF09\u2014\u2014 \u6253\u5206\u5236\u4E0B\u6CA1\u63CF\u8FF0\u7684\u56FE\u6C38\u8FDC\u9009\u4E0D\u4E0A\u3002\n2. \u5728\u6307\u4EE4\u914D\u7F6E\u7684\u300C\u5F15\u7528\u7684\u53C2\u8003\u56FE\u7247\u7EC4\u540D\u79F0\u300D\u4E2D\u586B\u5165\u7EC4\u540D\uFF08\u53EF\u586B\u591A\u4E2A\uFF09\uFF0C\u8BE5\u6307\u4EE4\u6267\u884C\u65F6\u4F1A\u628A\u7EC4\u5185\u56FE\u7247\u5168\u90E8\u4EA4\u7ED9 AI \u6311\u9009\u3002\n3. \u300CAI \u9009\u56FE\u8BBE\u7F6E\u300D\u53EF\u4FEE\u6539\u5BF9\u8BDD\u6A21\u578B\uFF08\u9ED8\u8BA4 Qwen/Qwen2.5-7B-Instruct\uFF09\u3001\u9009\u62E9\u6A21\u5F0F\u3001\u63D0\u793A\u8BCD\u6A21\u677F\u3001\u8D85\u65F6\u4E0E\u91CD\u8BD5\u7B49\uFF1B\n \u63A5\u53E3\u5730\u5740/\u5BC6\u94A5\u7559\u7A7A\u65F6\u590D\u7528\u7ED8\u56FE\u63A5\u53E3\u7684\u914D\u7F6E\u3002\n4. \u300C\u9009\u56FE\u6A21\u5F0F\u300D\u9ED8\u8BA4\u662F**\u6253\u5206\u5236**\uFF1A\u5148\u53EA\u505A\u9700\u6C42\u5206\u6790\uFF08**\u4E0D\u7ED9\u6A21\u578B\u5019\u9009\u56FE**\uFF0C\u5B83\u6CA1\u6709\u300C\u53EF\u6311\u7684\u5BF9\u8C61\u300D\uFF09\uFF0C\n \u518D\u7528\u5173\u952E\u8BCD\u5728\u672C\u5730\u53EC\u56DE\uFF0C\u7136\u540E\u8981\u6C42\u6A21\u578B\u7ED9\u6BCF\u5F20\u5019\u9009\u56FE\u6253 0-100 \u5206\uFF0C\u4F4E\u4E8E\u300C\u6700\u4F4E\u5206\u6570\u7EBF\u300D\u7684\u4E00\u5F8B\u4E0D\u8981\u3002\n \u4E00\u5F20\u90FD\u4E0D\u8FBE\u6807\u5C31\u5982\u5B9E\u544A\u77E5\u7528\u6237\u300C\u56FE\u5E93\u91CC\u6CA1\u6709\u5408\u9002\u7684\u53C2\u8003\u56FE\u300D\uFF0C**\u4E0D\u4F1A\u786C\u51D1**\uFF1B\n \u82E5\u5F00\u542F\u300C\u7F3A\u5C11\u5408\u9002\u53C2\u8003\u56FE\u65F6\u8BE2\u95EE\u7528\u6237\u8865\u5145\u53D1\u9001\u300D\uFF0C\u4F1A\u8BF7\u7528\u6237\u8865\u53D1\u4E00\u5F20\u53C2\u8003\u56FE\uFF08\u7528\u6237\u53D1\u7684\u56FE\u4F1A\u88AB\u76F4\u63A5\u4F7F\u7528\uFF09\u3002\n \u60F3\u56DE\u5230\u8001\u884C\u4E3A\u5C31\u628A\u300C\u9009\u56FE\u6A21\u5F0F\u300D\u6539\u6210\u300C\u65E7\u7248\u300D\u3002\n5. \u5F53\u7528\u6237\u660E\u786E\u8BF4\u300C\u4E0D\u7528\u53C2\u8003 / \u968F\u610F\u753B / \u81EA\u7531\u53D1\u6325\u300D\u65F6\uFF0C\u76F4\u63A5\u8DF3\u8FC7\u9009\u56FE\uFF0C\u4E00\u6B21\u9009\u56FE\u8BF7\u6C42\u90FD\u4E0D\u53D1\u3002\n6. \u300C\u542F\u7528 AI \u667A\u80FD\u9009\u62E9\u53C2\u8003\u56FE\u7247\u300D\u9ED8\u8BA4\u5173\u95ED\uFF0C\u9700\u624B\u52A8\u5F00\u542F\uFF1B\u300CAI \u9009\u56FE\u5931\u8D25\u65F6\u56DE\u9000\u4E3A\u4F7F\u7528\u5019\u9009\u6C60\u5185\u5168\u90E8\u56FE\u7247\u300D\u9ED8\u8BA4\u5173\u95ED\uFF0C\n \u5931\u8D25\u65F6\u672C\u6B21\u4E0D\u4F7F\u7528\u53C2\u8003\u56FE\u7247\uFF08\u5F00\u542F\u5219\u6539\u7528\u5019\u9009\u6C60\u5185\u5168\u90E8\u56FE\u7247\uFF09\u3002\u6CE8\u610F\u300C\u56DE\u9000\u300D\u53EA\u5BF9**\u8BF7\u6C42\u5931\u8D25**\u751F\u6548\uFF0C\n \u5BF9\u300C\u6253\u5206\u4E0D\u8FBE\u6807\u300D\u4E0D\u751F\u6548 \u2014\u2014 \u540E\u8005\u5C31\u8BE5\u4E0D\u5E26\u53C2\u8003\u56FE\u3002\n7. \u300C\u65E7\u7248\u300D\u6A21\u5F0F\u4E0B\u53C2\u8003\u56FE\u8F83\u591A\u65F6\uFF08\u8D85\u8FC7 12 \u5F20\uFF09\u81EA\u52A8\u8D70\u4E24\u7EA7\u68C0\u7D22\uFF1A\u5148\u8BA9\u6A21\u578B\u4EA7\u51FA\u68C0\u7D22\u5173\u952E\u8BCD\u3001\u672C\u5730\u5339\u914D\u53EC\u56DE\uFF0C\n \u518D\u5BF9\u53EC\u56DE\u7ED3\u679C\u7CBE\u6392\uFF1B\u8BC6\u56FE\u6A21\u5F0F\u4E0B\u4E5F\u53EA\u53D1\u9001\u53EC\u56DE\u7684\u8FD9\u51E0\u5F20\u56FE\u7247\uFF0C\u907F\u514D\u6BCF\u6B21\u90FD\u628A\u6574\u4E2A\u56FE\u5E93\u53D1\u7ED9\u6A21\u578B\u3002\n8. \u63CF\u8FF0\u53EF\u4EE5\u7528\u300C\u751F\u6210\u63CF\u8FF0\u300D\u6307\u4EE4\u8BA9\u6A21\u578B\u770B\u56FE\u81EA\u52A8\u751F\u6210\u5E76\u5199\u56DE\u914D\u7F6E\uFF08\u9700\u8981\u652F\u6301\u56FE\u7247\u8F93\u5165\u7684\u6A21\u578B\uFF09\u3002\n \u82E5\u9009\u56FE\u6A21\u578B\u652F\u6301\u8BC6\u522B\u56FE\u7247\uFF08\u591A\u6A21\u6001\uFF09\uFF0C\u53EF\u52FE\u9009\u300C\u9009\u56FE\u6A21\u578B\u652F\u6301\u8BC6\u522B\u56FE\u7247\u300D\uFF0C\u63D2\u4EF6\u4F1A\u628A\u5019\u9009\u56FE\u7247\u672C\u8EAB\u53D1\u7ED9\u6A21\u578B\uFF0C\n \u6A21\u578B\u5BF9\u7740\u771F\u5B9E\u56FE\u7247\u6311\u9009\uFF1B\u5B83\u7ED9\u51FA\u7684\u5173\u952E\u89C6\u89C9\u7279\u5F81\u8FD8\u4F1A\u5E76\u5165\u7ED8\u56FE\u63D0\u793A\u8BCD\uFF0C\u8BA9\u51FA\u56FE\u66F4\u8FD8\u539F\u53C2\u8003\u56FE\u3002\n9. \u751F\u6210\u7ED3\u679C\u53EF\u81EA\u52A8\u5165\u5E93\uFF08\u9ED8\u8BA4\u5173\u95ED\uFF09\uFF0C\u4E0B\u6B21\u80FD\u88AB\u81EA\u5DF1\u68C0\u7D22\u5230\u5E76\u590D\u7528\uFF0C\u5F62\u6210\u95ED\u73AF\uFF1B\n \u5F00\u542F\u300C\u540E\u53F0\u7ED8\u56FE\u300D\u540E\u51FA\u56FE\u4E0D\u518D\u963B\u585E\uFF0C\u5148\u56DE\u300C\u6B63\u5728\u753B\u300D\uFF0C\u753B\u597D\u4E3B\u52A8\u63A8\u9001\u3002\n\n\u63D0\u793A\u8BCD\u6A21\u677F\u53EF\u7528\u5360\u4F4D\u7B26\uFF1A{candidates} \u5019\u9009\u56FE\u7247\u5217\u8868\u3001{userInput} \u7528\u6237\u9644\u52A0\u9700\u6C42\u3001{max} \u6700\u591A\u9009\u62E9\u6570\u91CF\u3001{command} \u6307\u4EE4\u540D\u3001{prompt} \u6307\u4EE4\u63D0\u793A\u8BCD\n\n---\n\u6B64\u9879\u76EE\u6240\u9700\u7684koishi\u670D\u52A1\uFF1A\u5FC5\u9700 'http', 'logger', 'i18n'\uFF1B\u53EF\u9009 'puppeteer'\uFF08\u4EC5\u300C\u6587\u5B57\u6E32\u67D3\u53C2\u8003\u56FE\u300D\u9700\u8981\uFF09\n\n---\n";
13
13
  /**
14
14
  * 解析模型返回的 JSON 选择结果(纯函数,便于单独测试)
15
15
  * @param raw 模型的原始回复
@@ -33,6 +33,56 @@ export declare function computeRetryDelay(status: any, retryAfter: number | unde
33
33
  export declare function matchCandidatesByKeywords(candidates: CandidateImage[], keywords: string[], topK: number): CandidateImage[];
34
34
  /** 去重合并候选(按 url 判重) */
35
35
  export declare function mergeCandidates(...lists: CandidateImage[][]): CandidateImage[];
36
+ /** 选图第一步的产物:先想清楚「要什么」,再去看「有什么」 */
37
+ export interface RequirementAnalysis {
38
+ /** 画面主体(一句话) */
39
+ subject: string;
40
+ /** 硬性要素:缺一个就不该选这张图 */
41
+ must: string[];
42
+ /** 加分要素:有更好,没有也能用 */
43
+ nice: string[];
44
+ /** 明确不要出现的 */
45
+ avoid: string[];
46
+ /** 画面需要出现的文字(台词/标题/招牌),交给浏览器渲染 */
47
+ text: string;
48
+ /** 用户表示「不用参考/随意画」时跳过检索 */
49
+ skipReference: boolean;
50
+ /** 判定需要用户补图时,用一句话说明要什么样的图 */
51
+ needTypes: string;
52
+ }
53
+ /** 单张候选图的打分结果(index 从 1 开始,对应候选列表位置) */
54
+ export interface ScoredCandidate {
55
+ index: number;
56
+ score: number;
57
+ why: string;
58
+ }
59
+ /** 从任意文本里挖出第一个 JSON 对象(容忍代码块围栏和前后废话) */
60
+ export declare function extractJsonObject(raw: string | null | undefined): any | null;
61
+ /** 把模型给的字段统一成字符串数组(支持数组 / 逗号顿号换行分隔的字符串) */
62
+ export declare function toStrList(value: any): string[];
63
+ /** 用户这句话是不是「不用参考了」 */
64
+ export declare function isSkipReferenceInput(userInput: string): boolean;
65
+ /** 解析「需求抽取」的返回 */
66
+ export declare function parseAnalysisResult(raw: string | null): RequirementAnalysis | null;
67
+ /** 把「需求要素」转成本地检索用的关键词 */
68
+ export declare function keywordsFromAnalysis(analysis: RequirementAnalysis | null): string[];
69
+ /** 把分数归一化到 0-100(兼容 0.85 这种小数写法、以及 "85分" 这种字符串) */
70
+ export declare function normalizeScore(value: any): number | null;
71
+ /** 解析「逐图打分」的返回 */
72
+ export declare function parseScoreResult(raw: string | null, candidates: CandidateImage[]): ScoredCandidate[];
73
+ /**
74
+ * 按分数阈值筛选:只有达到 minScore 的才留下,按分数从高到低取前 max 张。
75
+ * 一张都不达标时返回空数组 —— 这是「宁可不选也不乱选」的兜底。
76
+ */
77
+ export declare function selectByScore(scored: ScoredCandidate[], candidates: CandidateImage[], minScore: number, max: number): {
78
+ picked: CandidateImage[];
79
+ best: number;
80
+ passed: number;
81
+ };
82
+ /**
83
+ * 去掉重复图:链接相同的直接去掉;描述完全相同的(同一批入库的近似图)只留第一张。
84
+ */
85
+ export declare function dedupeCandidates(list: CandidateImage[]): CandidateImage[];
36
86
  /**
37
87
  * 图库容量裁剪:超出容量时淘汰最旧的(按 time 升序),只保留每个组最近 capacity 条
38
88
  */
@@ -314,6 +364,18 @@ interface AISelectorConfig {
314
364
  maxTokensCeiling?: number;
315
365
  /** 诊断指令名:发一次选图请求并回显模型原始返回 */
316
366
  debugCommand?: string;
367
+ /** 选图模式:score = 抽需求+逐图打分(默认);legacy = 旧的候选全量丢给模型挑 */
368
+ selectionMode?: 'score' | 'legacy';
369
+ /** 打分制的最低分数线(0-100),低于它的图一律不选 */
370
+ minScore?: number;
371
+ /** 打分制第一步「需求分析」的提示词模板 */
372
+ analyzePrompt?: string;
373
+ /** 打分制第二步「逐图打分」的提示词模板 */
374
+ scorePrompt?: string;
375
+ /** 是否合并描述完全相同的重复参考图(默认开) */
376
+ dedupe?: boolean;
377
+ /** 用户说「不用参考/随意画」时是否跳过选图(默认开) */
378
+ respectSkipReference?: boolean;
317
379
  }
318
380
  /** 交给 AI 挑选的候选图片 */
319
381
  interface CandidateImage {
package/lib/index.js CHANGED
@@ -26,7 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  ));
27
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
28
 
29
- // src/index.ts
29
+ // plugins/koishi-plugin-image-prompt/src/index.ts
30
30
  var src_exports = {};
31
31
  __export(src_exports, {
32
32
  Config: () => Config,
@@ -37,8 +37,10 @@ __export(src_exports, {
37
37
  buildSelectorContent: () => buildSelectorContent,
38
38
  buildTextHtml: () => buildTextHtml,
39
39
  computeRetryDelay: () => computeRetryDelay,
40
+ dedupeCandidates: () => dedupeCandidates,
40
41
  describeSelectorError: () => describeSelectorError,
41
42
  escapeHtml: () => escapeHtml,
43
+ extractJsonObject: () => extractJsonObject,
42
44
  extractResponseText: () => extractResponseText,
43
45
  extractServerMessage: () => extractServerMessage,
44
46
  extractTextToRender: () => extractTextToRender,
@@ -46,19 +48,26 @@ __export(src_exports, {
46
48
  inject: () => inject,
47
49
  isPassiveReplyError: () => isPassiveReplyError,
48
50
  isRetryableStatus: () => isRetryableStatus,
51
+ isSkipReferenceInput: () => isSkipReferenceInput,
52
+ keywordsFromAnalysis: () => keywordsFromAnalysis,
49
53
  matchCandidatesByKeywords: () => matchCandidatesByKeywords,
50
54
  mergeCandidates: () => mergeCandidates,
51
55
  mergePrompt: () => mergePrompt,
52
56
  name: () => name,
57
+ normalizeScore: () => normalizeScore,
58
+ parseAnalysisResult: () => parseAnalysisResult,
53
59
  parseRetryAfterHeader: () => parseRetryAfterHeader,
60
+ parseScoreResult: () => parseScoreResult,
54
61
  parseSelectionResult: () => parseSelectionResult,
55
62
  previewJson: () => previewJson,
56
63
  readImageSize: () => readImageSize,
57
64
  resolveTokenParam: () => resolveTokenParam,
58
65
  sanitizeCaption: () => sanitizeCaption,
66
+ selectByScore: () => selectByScore,
59
67
  stripCommandName: () => stripCommandName,
60
68
  stripThinkTags: () => stripThinkTags,
61
69
  supportsMarkdown: () => supportsMarkdown,
70
+ toStrList: () => toStrList,
62
71
  trimGallery: () => trimGallery,
63
72
  truncateText: () => truncateText,
64
73
  usage: () => usage,
@@ -87,18 +96,25 @@ var usage = `
87
96
  \u3010AI \u9009\u62E9\u53C2\u8003\u56FE\u7247\u3011
88
97
 
89
98
  1. \u5728\u300C\u53C2\u8003\u56FE\u7247\u7EC4\u300D\u4E2D\u6CE8\u518C\u5206\u7EC4\uFF1A\u6BCF\u7EC4\u586B\u5199\u82E5\u5E72\u5F20\u300C\u56FE\u7247\u94FE\u63A5 + \u63CF\u8FF0\u300D\uFF08\u63CF\u8FF0\u7528\u4E8E\u8BA9 AI \u5224\u65AD\u8BE5\u56FE\u7684\u7528\u9014\uFF09\u3002
99
+ **\u63CF\u8FF0\u4E00\u5B9A\u8981\u5199\u5177\u4F53**\uFF08\u53D1\u8272\u53D1\u578B\u3001\u670D\u88C5\u3001\u52A8\u4F5C\u3001\u753B\u98CE\uFF09\u2014\u2014 \u6253\u5206\u5236\u4E0B\u6CA1\u63CF\u8FF0\u7684\u56FE\u6C38\u8FDC\u9009\u4E0D\u4E0A\u3002
90
100
  2. \u5728\u6307\u4EE4\u914D\u7F6E\u7684\u300C\u5F15\u7528\u7684\u53C2\u8003\u56FE\u7247\u7EC4\u540D\u79F0\u300D\u4E2D\u586B\u5165\u7EC4\u540D\uFF08\u53EF\u586B\u591A\u4E2A\uFF09\uFF0C\u8BE5\u6307\u4EE4\u6267\u884C\u65F6\u4F1A\u628A\u7EC4\u5185\u56FE\u7247\u5168\u90E8\u4EA4\u7ED9 AI \u6311\u9009\u3002
91
- 3. \u300CAI \u9009\u56FE\u8BBE\u7F6E\u300D\u53EF\u4FEE\u6539\u5BF9\u8BDD\u6A21\u578B\uFF08\u9ED8\u8BA4 Qwen/Qwen2.5-7B-Instruct\uFF09\u3001\u9009\u62E9\u63D0\u793A\u8BCD\u6A21\u677F\u3001\u8D85\u65F6\u4E0E\u91CD\u8BD5\u7B49\uFF1B
101
+ 3. \u300CAI \u9009\u56FE\u8BBE\u7F6E\u300D\u53EF\u4FEE\u6539\u5BF9\u8BDD\u6A21\u578B\uFF08\u9ED8\u8BA4 Qwen/Qwen2.5-7B-Instruct\uFF09\u3001\u9009\u62E9\u6A21\u5F0F\u3001\u63D0\u793A\u8BCD\u6A21\u677F\u3001\u8D85\u65F6\u4E0E\u91CD\u8BD5\u7B49\uFF1B
92
102
  \u63A5\u53E3\u5730\u5740/\u5BC6\u94A5\u7559\u7A7A\u65F6\u590D\u7528\u7ED8\u56FE\u63A5\u53E3\u7684\u914D\u7F6E\u3002
93
- 4. \u5F53 AI \u5224\u5B9A\u5019\u9009\u56FE\u7247\u91CC\u6CA1\u6709\u5408\u9002\u7684\u53C2\u8003\u56FE\u65F6\uFF0C\u4F1A\u6309\u914D\u7F6E\u8BE2\u95EE\u7528\u6237\u8865\u5145\u53D1\u9001\u56FE\u7247\uFF08\u7528\u6237\u53D1\u9001\u540E\u4F1A\u88AB\u76F4\u63A5\u4F7F\u7528\uFF09\u3002
103
+ 4. \u300C\u9009\u56FE\u6A21\u5F0F\u300D\u9ED8\u8BA4\u662F**\u6253\u5206\u5236**\uFF1A\u5148\u53EA\u505A\u9700\u6C42\u5206\u6790\uFF08**\u4E0D\u7ED9\u6A21\u578B\u5019\u9009\u56FE**\uFF0C\u5B83\u6CA1\u6709\u300C\u53EF\u6311\u7684\u5BF9\u8C61\u300D\uFF09\uFF0C
104
+ \u518D\u7528\u5173\u952E\u8BCD\u5728\u672C\u5730\u53EC\u56DE\uFF0C\u7136\u540E\u8981\u6C42\u6A21\u578B\u7ED9\u6BCF\u5F20\u5019\u9009\u56FE\u6253 0-100 \u5206\uFF0C\u4F4E\u4E8E\u300C\u6700\u4F4E\u5206\u6570\u7EBF\u300D\u7684\u4E00\u5F8B\u4E0D\u8981\u3002
105
+ \u4E00\u5F20\u90FD\u4E0D\u8FBE\u6807\u5C31\u5982\u5B9E\u544A\u77E5\u7528\u6237\u300C\u56FE\u5E93\u91CC\u6CA1\u6709\u5408\u9002\u7684\u53C2\u8003\u56FE\u300D\uFF0C**\u4E0D\u4F1A\u786C\u51D1**\uFF1B
106
+ \u82E5\u5F00\u542F\u300C\u7F3A\u5C11\u5408\u9002\u53C2\u8003\u56FE\u65F6\u8BE2\u95EE\u7528\u6237\u8865\u5145\u53D1\u9001\u300D\uFF0C\u4F1A\u8BF7\u7528\u6237\u8865\u53D1\u4E00\u5F20\u53C2\u8003\u56FE\uFF08\u7528\u6237\u53D1\u7684\u56FE\u4F1A\u88AB\u76F4\u63A5\u4F7F\u7528\uFF09\u3002
107
+ \u60F3\u56DE\u5230\u8001\u884C\u4E3A\u5C31\u628A\u300C\u9009\u56FE\u6A21\u5F0F\u300D\u6539\u6210\u300C\u65E7\u7248\u300D\u3002
108
+ 5. \u5F53\u7528\u6237\u660E\u786E\u8BF4\u300C\u4E0D\u7528\u53C2\u8003 / \u968F\u610F\u753B / \u81EA\u7531\u53D1\u6325\u300D\u65F6\uFF0C\u76F4\u63A5\u8DF3\u8FC7\u9009\u56FE\uFF0C\u4E00\u6B21\u9009\u56FE\u8BF7\u6C42\u90FD\u4E0D\u53D1\u3002
109
+ 6. \u300C\u542F\u7528 AI \u667A\u80FD\u9009\u62E9\u53C2\u8003\u56FE\u7247\u300D\u9ED8\u8BA4\u5173\u95ED\uFF0C\u9700\u624B\u52A8\u5F00\u542F\uFF1B\u300CAI \u9009\u56FE\u5931\u8D25\u65F6\u56DE\u9000\u4E3A\u4F7F\u7528\u5019\u9009\u6C60\u5185\u5168\u90E8\u56FE\u7247\u300D\u9ED8\u8BA4\u5173\u95ED\uFF0C
110
+ \u5931\u8D25\u65F6\u672C\u6B21\u4E0D\u4F7F\u7528\u53C2\u8003\u56FE\u7247\uFF08\u5F00\u542F\u5219\u6539\u7528\u5019\u9009\u6C60\u5185\u5168\u90E8\u56FE\u7247\uFF09\u3002\u6CE8\u610F\u300C\u56DE\u9000\u300D\u53EA\u5BF9**\u8BF7\u6C42\u5931\u8D25**\u751F\u6548\uFF0C
111
+ \u5BF9\u300C\u6253\u5206\u4E0D\u8FBE\u6807\u300D\u4E0D\u751F\u6548 \u2014\u2014 \u540E\u8005\u5C31\u8BE5\u4E0D\u5E26\u53C2\u8003\u56FE\u3002
112
+ 7. \u300C\u65E7\u7248\u300D\u6A21\u5F0F\u4E0B\u53C2\u8003\u56FE\u8F83\u591A\u65F6\uFF08\u8D85\u8FC7 12 \u5F20\uFF09\u81EA\u52A8\u8D70\u4E24\u7EA7\u68C0\u7D22\uFF1A\u5148\u8BA9\u6A21\u578B\u4EA7\u51FA\u68C0\u7D22\u5173\u952E\u8BCD\u3001\u672C\u5730\u5339\u914D\u53EC\u56DE\uFF0C
113
+ \u518D\u5BF9\u53EC\u56DE\u7ED3\u679C\u7CBE\u6392\uFF1B\u8BC6\u56FE\u6A21\u5F0F\u4E0B\u4E5F\u53EA\u53D1\u9001\u53EC\u56DE\u7684\u8FD9\u51E0\u5F20\u56FE\u7247\uFF0C\u907F\u514D\u6BCF\u6B21\u90FD\u628A\u6574\u4E2A\u56FE\u5E93\u53D1\u7ED9\u6A21\u578B\u3002
114
+ 8. \u63CF\u8FF0\u53EF\u4EE5\u7528\u300C\u751F\u6210\u63CF\u8FF0\u300D\u6307\u4EE4\u8BA9\u6A21\u578B\u770B\u56FE\u81EA\u52A8\u751F\u6210\u5E76\u5199\u56DE\u914D\u7F6E\uFF08\u9700\u8981\u652F\u6301\u56FE\u7247\u8F93\u5165\u7684\u6A21\u578B\uFF09\u3002
94
115
  \u82E5\u9009\u56FE\u6A21\u578B\u652F\u6301\u8BC6\u522B\u56FE\u7247\uFF08\u591A\u6A21\u6001\uFF09\uFF0C\u53EF\u52FE\u9009\u300C\u9009\u56FE\u6A21\u578B\u652F\u6301\u8BC6\u522B\u56FE\u7247\u300D\uFF0C\u63D2\u4EF6\u4F1A\u628A\u5019\u9009\u56FE\u7247\u672C\u8EAB\u53D1\u7ED9\u6A21\u578B\uFF0C
95
116
  \u6A21\u578B\u5BF9\u7740\u771F\u5B9E\u56FE\u7247\u6311\u9009\uFF1B\u5B83\u7ED9\u51FA\u7684\u5173\u952E\u89C6\u89C9\u7279\u5F81\u8FD8\u4F1A\u5E76\u5165\u7ED8\u56FE\u63D0\u793A\u8BCD\uFF0C\u8BA9\u51FA\u56FE\u66F4\u8FD8\u539F\u53C2\u8003\u56FE\u3002
96
- 5. \u300C\u542F\u7528 AI \u667A\u80FD\u9009\u62E9\u53C2\u8003\u56FE\u7247\u300D\u9ED8\u8BA4\u5173\u95ED\uFF0C\u9700\u624B\u52A8\u5F00\u542F\uFF1B\u300CAI \u9009\u56FE\u5931\u8D25\u65F6\u56DE\u9000\u4E3A\u4F7F\u7528\u5019\u9009\u6C60\u5185\u5168\u90E8\u56FE\u7247\u300D\u9ED8\u8BA4\u5173\u95ED\uFF0C
97
- \u5931\u8D25\u65F6\u672C\u6B21\u4E0D\u4F7F\u7528\u53C2\u8003\u56FE\u7247\uFF08\u5F00\u542F\u5219\u6539\u7528\u5019\u9009\u6C60\u5185\u5168\u90E8\u56FE\u7247\uFF09\u3002
98
- 6. \u53C2\u8003\u56FE\u8F83\u591A\u65F6\uFF08\u8D85\u8FC7 12 \u5F20\uFF09\u81EA\u52A8\u8D70\u4E24\u7EA7\u68C0\u7D22\uFF1A\u5148\u8BA9\u6A21\u578B\u4EA7\u51FA\u68C0\u7D22\u5173\u952E\u8BCD\u3001\u672C\u5730\u5339\u914D\u53EC\u56DE\uFF0C\u518D\u5BF9\u53EC\u56DE\u7ED3\u679C\u7CBE\u6392\uFF1B
99
- \u8BC6\u56FE\u6A21\u5F0F\u4E0B\u4E5F\u53EA\u53D1\u9001\u53EC\u56DE\u7684\u8FD9\u51E0\u5F20\u56FE\u7247\uFF0C\u907F\u514D\u6BCF\u6B21\u90FD\u628A\u6574\u4E2A\u56FE\u5E93\u53D1\u7ED9\u6A21\u578B\u3002
100
- 7. \u63CF\u8FF0\u53EF\u4EE5\u7528\u300C\u751F\u6210\u63CF\u8FF0\u300D\u6307\u4EE4\u8BA9\u6A21\u578B\u770B\u56FE\u81EA\u52A8\u751F\u6210\u5E76\u5199\u56DE\u914D\u7F6E\uFF08\u9700\u8981\u652F\u6301\u56FE\u7247\u8F93\u5165\u7684\u6A21\u578B\uFF09\u3002
101
- 8. \u751F\u6210\u7ED3\u679C\u53EF\u81EA\u52A8\u5165\u5E93\uFF08\u9ED8\u8BA4\u5173\u95ED\uFF09\uFF0C\u4E0B\u6B21\u80FD\u88AB\u81EA\u5DF1\u68C0\u7D22\u5230\u5E76\u590D\u7528\uFF0C\u5F62\u6210\u95ED\u73AF\uFF1B
117
+ 9. \u751F\u6210\u7ED3\u679C\u53EF\u81EA\u52A8\u5165\u5E93\uFF08\u9ED8\u8BA4\u5173\u95ED\uFF09\uFF0C\u4E0B\u6B21\u80FD\u88AB\u81EA\u5DF1\u68C0\u7D22\u5230\u5E76\u590D\u7528\uFF0C\u5F62\u6210\u95ED\u73AF\uFF1B
102
118
  \u5F00\u542F\u300C\u540E\u53F0\u7ED8\u56FE\u300D\u540E\u51FA\u56FE\u4E0D\u518D\u963B\u585E\uFF0C\u5148\u56DE\u300C\u6B63\u5728\u753B\u300D\uFF0C\u753B\u597D\u4E3B\u52A8\u63A8\u9001\u3002
103
119
 
104
120
  \u63D0\u793A\u8BCD\u6A21\u677F\u53EF\u7528\u5360\u4F4D\u7B26\uFF1A{candidates} \u5019\u9009\u56FE\u7247\u5217\u8868\u3001{userInput} \u7528\u6237\u9644\u52A0\u9700\u6C42\u3001{max} \u6700\u591A\u9009\u62E9\u6570\u91CF\u3001{command} \u6307\u4EE4\u540D\u3001{prompt} \u6307\u4EE4\u63D0\u793A\u8BCD
@@ -212,6 +228,132 @@ function mergeCandidates(...lists) {
212
228
  }
213
229
  return result;
214
230
  }
231
+ var SKIP_REFERENCE_RE = new RegExp([
232
+ "(\u4E0D\u7528|\u4E0D\u9700\u8981|\u65E0\u9700|\u4E0D\u8981|\u522B)(\u53C2\u8003|\u57AB\u56FE|\u770B\u56FE|\u627E\u56FE|\u641C\u56FE|\u8C03\u56FE\u5E93)",
233
+ "(\u53C2\u8003\u56FE|\u57AB\u56FE|\u56FE\u5E93)(\u4E0D\u7528|\u4E0D\u9700\u8981|\u5C31\u522B|\u53BB\u6389)",
234
+ "(\u968F\u610F|\u968F\u4FBF|\u81EA\u7531|\u4EFB\u610F)\u53D1\u6325",
235
+ "\u968F\u610F\u753B|\u968F\u4FBF\u753B|\u4F60\u770B\u7740\u753B|\u770B\u7740\u529E|\u4F60\u51B3\u5B9A"
236
+ ].join("|"));
237
+ function extractJsonObject(raw) {
238
+ if (!raw) return null;
239
+ let text = String(raw).trim();
240
+ const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/);
241
+ if (fence) text = fence[1].trim();
242
+ const start = text.indexOf("{");
243
+ const end = text.lastIndexOf("}");
244
+ if (start === -1 || end <= start) return null;
245
+ try {
246
+ const value = JSON.parse(text.slice(start, end + 1));
247
+ return value && typeof value === "object" ? value : null;
248
+ } catch {
249
+ return null;
250
+ }
251
+ }
252
+ function toStrList(value) {
253
+ if (Array.isArray(value)) return value.map((v) => String(v ?? "").trim()).filter(Boolean);
254
+ if (typeof value === "string" && value.trim()) {
255
+ return value.split(/[,,、;;\n]+/).map((s) => s.trim()).filter(Boolean);
256
+ }
257
+ return [];
258
+ }
259
+ function isSkipReferenceInput(userInput) {
260
+ const text = String(userInput || "").trim();
261
+ if (!text) return false;
262
+ return SKIP_REFERENCE_RE.test(text);
263
+ }
264
+ function parseAnalysisResult(raw) {
265
+ const data = extractJsonObject(raw);
266
+ if (!data) return null;
267
+ const text = data.text ?? data.texts ?? data.lines ?? data.renderText;
268
+ return {
269
+ subject: String(data.subject ?? data.\u4E3B\u4F53 ?? "").trim(),
270
+ must: toStrList(data.must ?? data.mustHave ?? data.required ?? data.\u786C\u6027\u8981\u7D20),
271
+ nice: toStrList(data.nice ?? data.niceToHave ?? data.optional ?? data.\u52A0\u5206\u8981\u7D20),
272
+ avoid: toStrList(data.avoid ?? data.exclude ?? data.negative ?? data.\u4E0D\u8981\u51FA\u73B0),
273
+ text: Array.isArray(text) ? toStrList(text).join("\n") : String(text ?? "").trim(),
274
+ skipReference: data.skipReference === true || data.skip_reference === true,
275
+ needTypes: String(data.needTypes ?? data.need_types ?? data.\u9700\u8981\u7684\u56FE ?? "").trim()
276
+ };
277
+ }
278
+ function keywordsFromAnalysis(analysis) {
279
+ if (!analysis) return [];
280
+ const words = [];
281
+ const push = (value) => {
282
+ const item = String(value || "").trim();
283
+ if (item && !words.includes(item)) words.push(item);
284
+ };
285
+ if (analysis.subject) push(analysis.subject);
286
+ analysis.must.forEach(push);
287
+ analysis.nice.forEach(push);
288
+ return words;
289
+ }
290
+ function normalizeScore(value) {
291
+ if (typeof value === "number" && isFinite(value)) {
292
+ const num = value > 0 && value <= 1 ? Math.round(value * 100) : Math.round(value);
293
+ return Math.max(0, Math.min(100, num));
294
+ }
295
+ if (typeof value === "string") {
296
+ const matched = value.match(/-?\d+(?:\.\d+)?/);
297
+ if (!matched) return null;
298
+ return normalizeScore(parseFloat(matched[0]));
299
+ }
300
+ return null;
301
+ }
302
+ function parseScoreResult(raw, candidates) {
303
+ const data = extractJsonObject(raw);
304
+ if (!data) return [];
305
+ const list = data.scores ?? data.score ?? data.items ?? data.results ?? data.\u8BC4\u5206;
306
+ if (!Array.isArray(list)) return [];
307
+ const out = [];
308
+ for (const entry of list) {
309
+ if (!entry || typeof entry !== "object") continue;
310
+ const rawIndex = entry.index ?? entry.id ?? entry.no ?? entry.n ?? entry.\u7F16\u53F7;
311
+ const index = rawIndex === void 0 ? null : parseInt(String(rawIndex).replace(/[^0-9-]/g, ""), 10);
312
+ const score = normalizeScore(entry.score ?? entry.points ?? entry.\u5206\u6570 ?? entry.value);
313
+ if (index === null || isNaN(index) || score === null) continue;
314
+ const candidate = candidates[index - 1] || candidates[index];
315
+ if (!candidate) continue;
316
+ out.push({ index: candidates.indexOf(candidate) + 1, score, why: String(entry.why ?? entry.reason ?? entry.\u8BF4\u660E ?? "").trim() });
317
+ }
318
+ return out;
319
+ }
320
+ function selectByScore(scored, candidates, minScore, max) {
321
+ const threshold = minScore > 0 ? minScore : 0;
322
+ const best = /* @__PURE__ */ new Map();
323
+ for (const item of scored || []) {
324
+ const prev = best.get(item.index);
325
+ if (!prev || item.score > prev.score) best.set(item.index, item);
326
+ }
327
+ const eligible = [...best.values()].filter((item) => item.score >= threshold).sort((a, b) => b.score - a.score);
328
+ const limit = max > 0 ? max : 1;
329
+ const picked = [];
330
+ for (const item of eligible) {
331
+ if (picked.length >= limit) break;
332
+ const candidate = candidates[item.index - 1];
333
+ if (candidate && !picked.includes(candidate)) picked.push(candidate);
334
+ }
335
+ return {
336
+ picked,
337
+ best: (scored || []).reduce((acc, item) => Math.max(acc, item.score), 0),
338
+ passed: eligible.length
339
+ };
340
+ }
341
+ function dedupeCandidates(list) {
342
+ const out = [];
343
+ const seenUrl = /* @__PURE__ */ new Set();
344
+ const seenDesc = /* @__PURE__ */ new Set();
345
+ for (const item of list || []) {
346
+ if (!item || !item.url || seenUrl.has(item.url)) continue;
347
+ const desc = normalizeText(item.description || "");
348
+ if (desc.length >= 6) {
349
+ if (seenDesc.has(desc)) continue;
350
+ seenDesc.add(desc);
351
+ }
352
+ seenUrl.add(item.url);
353
+ out.push(item);
354
+ }
355
+ return out;
356
+ }
215
357
  function trimGallery(records, capacity) {
216
358
  const limit = capacity > 0 ? capacity : 50;
217
359
  const byGroup = /* @__PURE__ */ new Map();
@@ -694,6 +836,56 @@ var DEFAULT_KEYWORD_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u53C2\u8003\u56FE\u7247\u
694
836
 
695
837
  \u53EA\u8F93\u51FA\u4E00\u4E2A JSON \u5BF9\u8C61\uFF0C\u4E0D\u8981\u8F93\u51FA\u89E3\u91CA\u6216\u4EE3\u7801\u5757\uFF1A
696
838
  {"keywords":["\u5173\u952E\u8BCD1","\u5173\u952E\u8BCD2"],"candidates":[1,3]}`;
839
+ var DEFAULT_ANALYZE_PROMPT = `\u4F60\u662F\u7ED8\u56FE\u53C2\u8003\u56FE\u7684\u300C\u9700\u6C42\u5206\u6790\u52A9\u624B\u300D\u3002**\u8FD9\u4E00\u6B65\u53EA\u505A\u5206\u6790\uFF0C\u4E0D\u8981\u6311\u9009\u4EFB\u4F55\u56FE\u7247\u3002**
840
+
841
+ \u7ED8\u56FE\u6307\u4EE4\uFF1A{command}
842
+ \u8BE5\u6307\u4EE4\u7684\u7528\u9014\u63D0\u793A\u8BCD\uFF1A{prompt}
843
+ \u7528\u6237\u672C\u6B21\u7684\u9700\u6C42\uFF1A{userInput}
844
+
845
+ \u8BF7\u5148\u628A\u300C\u8FD9\u6B21\u753B\u9762\u4E0A\u5FC5\u987B\u6709 / \u6700\u597D\u6709\u7684\u89C6\u89C9\u8981\u7D20\u300D\u60F3\u6E05\u695A\uFF0C\u518D\u8F93\u51FA JSON\u3002
846
+
847
+ \u89C4\u5219\uFF1A
848
+ 1. must\uFF08\u786C\u6027\u8981\u7D20\uFF09\uFF1A\u7F3A\u4E00\u4E2A\u5C31\u4E0D\u8BE5\u9009\u8FD9\u5F20\u53C2\u8003\u56FE\u3002\u53EA\u5199**\u80FD\u4ECE\u56FE\u7247\u4E0A\u770B\u51FA\u6765\u7684**\u89C6\u89C9\u7279\u5F81\uFF0C
849
+ \u4F8B\u5982\u89D2\u8272\u540D\u3001\u53D1\u8272\u53D1\u578B\u3001\u77B3\u8272\u3001\u670D\u88C5\u6B3E\u5F0F\u3001\u914D\u9970\u3001\u7269\u79CD\u3001\u753B\u98CE\u3002\u4E0D\u8981\u5199\u62BD\u8C61\u8BCD\uFF08\u5982\u300C\u597D\u770B\u300D\u300C\u9AD8\u7EA7\u300D\uFF09\u3002
850
+ \u6307\u4EE4\u7528\u9014\u63D0\u793A\u8BCD\u91CC\u672C\u6765\u5C31\u56FA\u5B9A\u7684\u98CE\u683C\uFF08\u6BD4\u5982\u300C1/7 \u624B\u529E\u300D\u300C\u900F\u660E\u4E9A\u514B\u529B\u5E95\u5EA7\u300D\uFF09\u4E0D\u8981\u5199\u8FDB must\u3002
851
+ 2. nice\uFF08\u52A0\u5206\u8981\u7D20\uFF09\uFF1A\u6709\u66F4\u597D\u3001\u6CA1\u6709\u4E5F\u80FD\u7528\uFF0C\u4F8B\u5982\u52A8\u4F5C\u3001\u8868\u60C5\u3001\u6784\u56FE\u3001\u573A\u666F\u3001\u6C1B\u56F4\u3002
852
+ 3. avoid\uFF1A\u660E\u786E\u4E0D\u8981\u51FA\u73B0\u7684\u4E1C\u897F\uFF08\u7528\u6237\u8BF4\u300C\u4E0D\u8981 XX\u300D\u300C\u6362\u6210 YY\u300D\u65F6\uFF0C\u628A\u88AB\u5426\u6389\u7684\u90A3\u4E2A\u5199\u8FD9\u91CC\uFF09\u3002
853
+ 4. subject\uFF1A\u8FD9\u6B21\u753B\u9762\u7684\u4E3B\u4F53\uFF0C\u4E00\u53E5\u8BDD\uFF08\u5982\u300C\u767D\u53D1\u7684\u5C11\u5973\u300D\uFF09\u3002
854
+ 5. text\uFF1A\u753B\u9762\u4E0A\u9700\u8981\u51FA\u73B0\u7684\u6587\u5B57\uFF08\u53F0\u8BCD/\u6807\u9898/\u62DB\u724C/\u5B57\u5E55\uFF09\uFF0C\u591A\u884C\u7528 \\n \u6362\u884C\uFF1B\u4E0D\u9700\u8981\u5C31\u7A7A\u5B57\u7B26\u4E32\u3002
855
+ 6. skipReference\uFF1A\u7528\u6237\u660E\u786E\u8868\u793A\u300C\u4E0D\u7528\u53C2\u8003 / \u968F\u610F\u753B / \u81EA\u7531\u53D1\u6325 / \u4F60\u770B\u7740\u529E\u300D\u65F6\u4E3A true\u3002
856
+ 7. needTypes\uFF1A\u56FE\u5E93\u91CC**\u6CA1\u6709**\u5408\u9002\u53C2\u8003\u56FE\u65F6\uFF0C\u63D2\u4EF6\u8981\u8BF7\u7528\u6237\u8865\u53D1\u4E00\u5F20\u3002\u8FD9\u91CC\u5199\u4E00\u53E5**\u76F4\u63A5\u5BF9\u7528\u6237\u8BF4**\u7684\u8BDD
857
+ \uFF08\u4E2D\u6587\uFF0C40 \u5B57\u4EE5\u5185\uFF0C\u8BED\u6C14\u81EA\u7136\uFF0C\u4E0D\u8981\u7528\u300C\u8BF7\u63D0\u4F9B\u56FE\u7247\u300D\u8FD9\u79CD\u5957\u8BDD\uFF09\uFF0C\u4F8B\u5982\u300C\u80FD\u53D1\u4E00\u5F20\u767D\u53D1\u6C34\u624B\u670D\u7684\u7ACB\u7ED8\u5417\uFF1F\u300D\u3002
858
+ \u56FE\u5E93\u91CC\u4E0D\u7F3A\u56FE\u5C31\u7559\u7A7A\u5B57\u7B26\u4E32\u3002
859
+
860
+ \u53EA\u8F93\u51FA\u4E00\u4E2A JSON \u5BF9\u8C61\uFF0C\u4E0D\u8981\u89E3\u91CA\u3001\u4E0D\u8981 Markdown \u4EE3\u7801\u5757\uFF1A
861
+ {"subject":"","must":[],"nice":[],"avoid":[],"text":"","skipReference":false,"needTypes":""}`;
862
+ var DEFAULT_SCORE_PROMPT = `\u4F60\u662F\u7ED8\u56FE\u53C2\u8003\u56FE\u7684\u300C\u6253\u5206\u52A9\u624B\u300D\u3002\u4E0B\u9762\u662F\u5DF2\u7ECF\u5206\u6790\u597D\u7684\u9700\u6C42\uFF0C\u4EE5\u53CA\u82E5\u5E72\u5019\u9009\u53C2\u8003\u56FE\u3002
863
+ **\u4F60\u7684\u4EFB\u52A1\u662F\u7ED9\u6BCF\u4E00\u5F20\u5019\u9009\u56FE\u6253\u5206\uFF0C\u800C\u4E0D\u662F\u6311\u51FA\u300C\u6700\u50CF\u7684\u300D\u6765\u51D1\u6570\u3002**
864
+
865
+ \u672C\u6B21\u9700\u6C42\uFF1A
866
+ \u4E3B\u4F53\uFF1A{subject}
867
+ \u786C\u6027\u8981\u7D20\uFF08\u7F3A\u4E00\u4E2A\u5C31\u4E0D\u5408\u683C\uFF09\uFF1A{must}
868
+ \u52A0\u5206\u8981\u7D20\uFF1A{nice}
869
+ \u4E0D\u8981\u51FA\u73B0\uFF1A{avoid}
870
+
871
+ \u5019\u9009\u53C2\u8003\u56FE\uFF08\u7F16\u53F7 | \u6240\u5C5E\u7EC4 | \u63CF\u8FF0\uFF09\uFF1A
872
+ {candidates}
873
+
874
+ \u6253\u5206\u89C4\u5219\uFF1A
875
+ 1. **\u5FC5\u987B\u7ED9\u6BCF\u4E00\u5F20\u5019\u9009\u56FE\u90FD\u6253\u4E00\u4E2A 0-100 \u7684\u5206\u6570**\uFF0C\u4E00\u5F20\u90FD\u4E0D\u80FD\u6F0F\u3002
876
+ 2. \u6253\u5206\u53EA\u770B\u300C\u8FD9\u5F20\u56FE\u80FD\u4E0D\u80FD\u5F53\u672C\u6B21\u7ED8\u56FE\u7684\u53C2\u8003\u300D\uFF1A
877
+ - \u786C\u6027\u8981\u7D20\u547D\u4E2D\u5F97\u8D8A\u591A\u5206\u8D8A\u9AD8\uFF1B**\u786C\u6027\u8981\u7D20\u7F3A\u5931\u6216\u4E0E\u9700\u6C42\u77DB\u76FE \u2192 \u53EA\u80FD\u7ED9 0-30 \u5206**\u3002
878
+ - \u63CF\u8FF0\u4E3A\u7A7A\u3001\u770B\u4E0D\u61C2\u5185\u5BB9\u3001\u65E0\u6CD5\u5224\u65AD\u7684\u56FE \u2192 **\u4E00\u5F8B\u7ED9 0 \u5206**\uFF0C\u4E0D\u8981\u9760\u731C\u3002
879
+ - \u4E0E avoid \u51B2\u7A81\u7684 \u2192 0 \u5206\u3002
880
+ - \u547D\u4E2D\u52A0\u5206\u8981\u7D20\u3001\u6216\u7EC4\u540D/\u63CF\u8FF0\u4E0E\u9700\u6C42\u9AD8\u5EA6\u543B\u5408 \u2192 \u53EF\u4EE5\u7ED9 80-100\u3002
881
+ 3. **\u5B81\u53EF\u6F0F\u9009\uFF0C\u4E0D\u53EF\u4E71\u9009\u3002** \u62FF\u4E0D\u51C6\u5C31\u6253\u4F4E\u5206\u3002\u7ED9\u4E0D\u76F8\u5173\u7684\u56FE\u6253\u9AD8\u5206\u662F\u6700\u4E25\u91CD\u7684\u9519\u8BEF\u3002
882
+ 4. \u5982\u679C\u6240\u6709\u5019\u9009\u90FD\u4E0D\u5408\u9002\uFF0C\u5C31\u7167\u5B9E\u5168\u90E8\u6253\u4F4E\u5206\uFF0C**\u4E0D\u8981\u4E3A\u4E86\u51D1\u6570\u62AC\u9AD8\u5206\u6570**\u3002
883
+ 5. why \u7528\u4E0D\u8D85\u8FC7 15 \u4E2A\u5B57\u8BF4\u660E\u7406\u7531\uFF0C\u4F8B\u5982\u300C\u547D\u4E2D\u767D\u53D1+\u6C34\u624B\u670D\u300D\u300C\u63CF\u8FF0\u4E3A\u7A7A\u65E0\u6CD5\u5224\u65AD\u300D\u3002
884
+ 6. hint\uFF08\u53EF\u9009\uFF09\uFF1A**\u53EA\u6709\u5728\u4F60\u5B9E\u9645\u770B\u5230\u4E86\u56FE\u7247\u65F6**\u624D\u586B \u2014\u2014 \u7528\u4E00\u53E5\u8BDD\u6982\u62EC\u9009\u4E2D\u56FE\u7247\u91CC\u51B3\u5B9A\u5916\u89C2\u7684\u5173\u952E\u89C6\u89C9\u7279\u5F81
885
+ \uFF08\u53D1\u8272\u53D1\u578B/\u77B3\u8272/\u670D\u88C5/\u914D\u9970/\u753B\u98CE\uFF09\uFF0C\u4FBF\u4E8E\u5E76\u5165\u7ED8\u56FE\u63D0\u793A\u8BCD\uFF1B\u53EA\u7ED9\u4E86\u6587\u5B57\u63CF\u8FF0\u5C31\u7559\u7A7A\u5B57\u7B26\u4E32\u3002
886
+
887
+ \u53EA\u8F93\u51FA\u4E00\u4E2A JSON \u5BF9\u8C61\uFF0C\u4E0D\u8981\u89E3\u91CA\u3001\u4E0D\u8981 Markdown \u4EE3\u7801\u5757\uFF1A
888
+ {"scores":[{"index":1,"score":85,"why":"\u547D\u4E2D\u767D\u53D1\u53CC\u9A6C\u5C3E"},{"index":2,"score":0,"why":"\u63CF\u8FF0\u4E3A\u7A7A"}],"hint":""}`;
697
889
  var defaultCommands = [
698
890
  {
699
891
  name: "\u624B\u529E\u5316",
@@ -1195,6 +1387,13 @@ var Config = import_koishi.Schema.intersect([
1195
1387
  import_koishi.Schema.object({
1196
1388
  aiSelector: import_koishi.Schema.object({
1197
1389
  enabled: import_koishi.Schema.boolean().default(false).description("\u542F\u7528 AI \u667A\u80FD\u9009\u62E9\u53C2\u8003\u56FE\u7247\uFF08\u9ED8\u8BA4\u5173\u95ED\uFF0C\u9700\u8981\u65F6\u624B\u52A8\u5F00\u542F\uFF09"),
1390
+ selectionMode: import_koishi.Schema.union([
1391
+ import_koishi.Schema.const("score").description("\u6253\u5206\u5236\uFF08\u63A8\u8350\uFF09\uFF1A\u5148\u5206\u6790\u9700\u6C42 \u2192 \u5173\u952E\u8BCD\u53EC\u56DE \u2192 \u9010\u56FE\u6253\u5206 \u2192 \u5206\u6570\u4E0D\u591F\u5C31\u5982\u5B9E\u8BF4\u300C\u6CA1\u6709\u5408\u9002\u7684\u56FE\u300D"),
1392
+ import_koishi.Schema.const("legacy").description("\u65E7\u7248\uFF1A\u628A\u5019\u9009\u56FE\u5168\u90E8\u4E22\u7ED9\u6A21\u578B\u4E00\u6B21\u6311\u5B8C\uFF08\u5BB9\u6613\u786C\u51D1\u6570\uFF0C\u4EC5\u5728\u9700\u8981\u517C\u5BB9\u65F6\u4F7F\u7528\uFF09")
1393
+ ]).default("score").description("\u9009\u56FE\u6A21\u5F0F\u3002\u6253\u5206\u5236\u4F1A\u7ED9\u6A21\u578B\u7559\u9000\u8DEF\uFF0C\u4E0D\u4F1A\u5728\u6CA1\u6709\u5408\u9002\u56FE\u65F6\u786C\u6311\u4E00\u5F20"),
1394
+ minScore: import_koishi.Schema.number().default(60).min(0).max(100).step(5).description("\u6253\u5206\u5236\u7684\u6700\u4F4E\u5206\u6570\u7EBF\uFF080-100\uFF09\u3002\u4F4E\u4E8E\u8BE5\u5206\u6570\u7684\u53C2\u8003\u56FE\u4E00\u5F8B\u4E0D\u9009\uFF1B\u5168\u90E8\u4E0D\u8FBE\u6807\u5C31\u5224\u5B9A\u300C\u56FE\u5E93\u6CA1\u6709\u5408\u9002\u7684\u56FE\u300D\u5E76\u8BE2\u95EE\u7528\u6237\u8865\u56FE"),
1395
+ dedupe: import_koishi.Schema.boolean().default(true).description("\u5408\u5E76\u91CD\u590D\u53C2\u8003\u56FE\uFF08\u94FE\u63A5\u76F8\u540C\uFF0C\u6216\u63CF\u8FF0\u5B8C\u5168\u76F8\u540C\u7684\u8FD1\u4F3C\u56FE\u53EA\u4FDD\u7559\u4E00\u5F20\uFF09"),
1396
+ respectSkipReference: import_koishi.Schema.boolean().default(true).description("\u7528\u6237\u8BF4\u300C\u4E0D\u7528\u53C2\u8003 / \u968F\u610F\u753B / \u81EA\u7531\u53D1\u6325\u300D\u65F6\u8DF3\u8FC7\u9009\u56FE\uFF0C\u4E00\u6B21\u9009\u56FE\u8BF7\u6C42\u90FD\u4E0D\u53D1\uFF08\u9700\u6C42\u5206\u6790\u4E5F\u7701\u6389\uFF09"),
1198
1397
  baseUrl: import_koishi.Schema.string().role("link").description("AI \u9009\u56FE\u63A5\u53E3\u5730\u5740\uFF08OpenAI \u517C\u5BB9 Chat Completions\uFF0C\u7559\u7A7A\u5219\u590D\u7528\u7ED8\u56FE\u63A5\u53E3\u5730\u5740\uFF09"),
1199
1398
  apiKey: import_koishi.Schema.string().role("secret").description("AI \u9009\u56FE\u63A5\u53E3\u5BC6\u94A5\uFF08\u7559\u7A7A\u5219\u590D\u7528\u7ED8\u56FE API \u5BC6\u94A5\uFF09"),
1200
1399
  model: import_koishi.Schema.string().default("Qwen/Qwen2.5-7B-Instruct").description("\u7528\u4E8E\u9009\u62E9\u53C2\u8003\u56FE\u7247\u7684\u5BF9\u8BDD\u6A21\u578B"),
@@ -1215,10 +1414,12 @@ var Config = import_koishi.Schema.intersect([
1215
1414
  visionMaxImages: import_koishi.Schema.number().default(6).min(1).max(20).step(1).description("\u5F00\u542F\u8BC6\u522B\u56FE\u7247\u65F6\uFF0C\u6700\u591A\u9644\u5E26\u591A\u5C11\u5F20\u5019\u9009\u56FE\u7247\uFF08\u8D85\u51FA\u90E8\u5206\u53EA\u53D1\u6587\u5B57\u63CF\u8FF0\uFF0C\u907F\u514D\u8BF7\u6C42\u8FC7\u5927\uFF09"),
1216
1415
  visionFallback: import_koishi.Schema.boolean().default(true).description("\u5E26\u56FE\u7247\u8BF7\u6C42\u5931\u8D25\u65F6\uFF0C\u81EA\u52A8\u9000\u56DE\u7EAF\u6587\u5B57\u518D\u8BD5\u4E00\u6B21"),
1217
1416
  appendHint: import_koishi.Schema.boolean().default(true).description("\u628A\u6A21\u578B\u770B\u56FE\u540E\u7ED9\u51FA\u7684\u5173\u952E\u89C6\u89C9\u7279\u5F81\u5E76\u5165\u7ED8\u56FE\u63D0\u793A\u8BCD\uFF08\u4EC5\u5728\u5F00\u542F\u8BC6\u522B\u56FE\u7247\u65F6\u751F\u6548\uFF0C\u8BA9\u51FA\u56FE\u66F4\u8FD8\u539F\u53C2\u8003\u56FE\uFF09"),
1218
- twoStage: import_koishi.Schema.boolean().default(true).description("\u4E24\u7EA7\u68C0\u7D22\u9009\u56FE\uFF1A\u5148\u8BA9\u6A21\u578B\u4EA7\u51FA\u68C0\u7D22\u5173\u952E\u8BCD\u5E76\u5728\u672C\u5730\u53EC\u56DE\uFF0C\u518D\u5BF9\u53EC\u56DE\u7ED3\u679C\u7CBE\u6392\u3002\u53C2\u8003\u56FE\u5F88\u591A\u65F6\u5F3A\u70C8\u5EFA\u8BAE\u5F00\u542F"),
1417
+ twoStage: import_koishi.Schema.boolean().default(true).description("\u3010\u4EC5\u65E7\u7248\u6A21\u5F0F\u751F\u6548\u3011\u4E24\u7EA7\u68C0\u7D22\u9009\u56FE\uFF1A\u5148\u8BA9\u6A21\u578B\u4EA7\u51FA\u68C0\u7D22\u5173\u952E\u8BCD\u5E76\u5728\u672C\u5730\u53EC\u56DE\uFF0C\u518D\u5BF9\u53EC\u56DE\u7ED3\u679C\u7CBE\u6392\u3002\u6253\u5206\u5236\u5DF2\u5185\u7F6E\u53EC\u56DE\uFF0C\u65E0\u9700\u6B64\u9879"),
1219
1418
  twoStageThreshold: import_koishi.Schema.number().default(12).min(2).max(200).step(1).description("\u5019\u9009\u56FE\u7247\u8D85\u8FC7\u591A\u5C11\u5F20\u624D\u542F\u7528\u4E24\u7EA7\u68C0\u7D22\uFF08\u5C11\u4E8E\u6B64\u503C\u76F4\u63A5\u4E00\u6B21\u95EE\u5B8C\uFF0C\u66F4\u7701\u4E8B\uFF09"),
1220
1419
  retrievalTopK: import_koishi.Schema.number().default(12).min(1).max(50).step(1).description("\u7B2C\u4E00\u7EA7\u53EC\u56DE\u7684\u5019\u9009\u6570\u91CF\u4E0A\u9650\uFF08\u7CBE\u6392\u53EA\u770B\u8FD9\u4E48\u591A\u5F20\uFF09"),
1221
1420
  keywordPrompt: import_koishi.Schema.string().role("textarea", { rows: [10, 6] }).default(DEFAULT_KEYWORD_PROMPT).description("\u68C0\u7D22\u63D0\u793A\u8BCD\u6A21\u677F\uFF0C\u53EF\u7528\u5360\u4F4D\u7B26\uFF1A{index} \u56FE\u7247\u7D22\u5F15\u3001{userInput} \u7528\u6237\u9644\u52A0\u9700\u6C42\u3001{command} \u6307\u4EE4\u540D\u3001{prompt} \u6307\u4EE4\u63D0\u793A\u8BCD\u3001{topK} \u53EC\u56DE\u4E0A\u9650"),
1421
+ analyzePrompt: import_koishi.Schema.string().role("textarea", { rows: [12, 6] }).default(DEFAULT_ANALYZE_PROMPT).description("\uFF08\u6253\u5206\u5236\u7B2C\u4E00\u6B65\uFF09\u9700\u6C42\u5206\u6790\u63D0\u793A\u8BCD\uFF0C\u53EF\u7528\u5360\u4F4D\u7B26\uFF1A{command} \u6307\u4EE4\u540D\u3001{prompt} \u6307\u4EE4\u63D0\u793A\u8BCD\u3001{userInput} \u7528\u6237\u9644\u52A0\u9700\u6C42\u3002\u8FD9\u4E00\u6B65**\u4E0D\u628A\u5019\u9009\u56FE\u7ED9\u6A21\u578B**\uFF0C\u6240\u4EE5\u6A21\u578B\u6CA1\u6709\u4E71\u9009\u7684\u673A\u4F1A"),
1422
+ scorePrompt: import_koishi.Schema.string().role("textarea", { rows: [12, 6] }).default(DEFAULT_SCORE_PROMPT).description("\uFF08\u6253\u5206\u5236\u7B2C\u4E8C\u6B65\uFF09\u9010\u56FE\u6253\u5206\u63D0\u793A\u8BCD\uFF0C\u53EF\u7528\u5360\u4F4D\u7B26\uFF1A{subject} \u4E3B\u4F53\u3001{must} \u786C\u6027\u8981\u7D20\u3001{nice} \u52A0\u5206\u8981\u7D20\u3001{avoid} \u4E0D\u8981\u51FA\u73B0\u3001{candidates} \u5019\u9009\u56FE\u7247\u5217\u8868\u3001{max} \u6700\u591A\u9009\u62E9\u6570\u91CF"),
1222
1423
  captionCommand: import_koishi.Schema.string().default("\u751F\u6210\u63CF\u8FF0").description("\u81EA\u52A8\u751F\u6210\u63CF\u8FF0\u7684\u6307\u4EE4\u540D\uFF08\u6302\u5728\u6307\u4EE4\u6839\u4E0B\uFF1B\u76F4\u63A5\u53D1\u56FE\u5219\u53EA\u8BC6\u522B\u5E76\u8FD4\u56DE\u63CF\u8FF0\uFF09"),
1223
1424
  captionModel: import_koishi.Schema.string().description("\u751F\u6210\u63CF\u8FF0\u7528\u7684\u6A21\u578B\uFF08\u7559\u7A7A\u5219\u7528\u4E0A\u9762\u7684\u9009\u56FE\u6A21\u578B\uFF1B\u5FC5\u987B\u662F\u652F\u6301\u56FE\u7247\u8F93\u5165\u7684\u6A21\u578B\uFF09"),
1224
1425
  captionPrompt: import_koishi.Schema.string().role("textarea", { rows: [8, 4] }).default(DEFAULT_CAPTION_PROMPT).description("\u751F\u6210\u63CF\u8FF0\u7684\u63D0\u793A\u8BCD\uFF08\u8981\u6C42\u6A21\u578B\u5199\u51FA\u4FBF\u4E8E\u68C0\u7D22\u7684\u5173\u952E\u8BCD\uFF09"),
@@ -1409,10 +1610,11 @@ function apply(ctx, config) {
1409
1610
  selecting: "\u6B63\u5728\u667A\u80FD\u6311\u9009\u53C2\u8003\u56FE\u7247...",
1410
1611
  selectfailed: "AI \u9009\u56FE\u5931\u8D25\uFF08{0}\uFF09\uFF0C\u5DF2\u6539\u7528\u5168\u90E8\u5019\u9009\u53C2\u8003\u56FE\u7247",
1411
1612
  selectfailedNoFallback: "AI \u9009\u56FE\u5931\u8D25\uFF08{0}\uFF09\uFF0C\u672C\u6B21\u4E0D\u4F7F\u7528\u53C2\u8003\u56FE\u7247",
1613
+ nomatch: "\u56FE\u5E93\u91CC\u6CA1\u6709\u5408\u9002\u7684\u53C2\u8003\u56FE\uFF1A{0}",
1412
1614
  selected: "\u5DF2\u4E3A\u4F60\u6311\u9009\u53C2\u8003\u56FE\u7247\uFF1A\n{0}",
1413
1615
  askimage: "{0}\n\u8BF7\u5728{1}\u79D2\u5185\u53D1\u9001\u56FE\u7247...",
1414
1616
  askimageDefault: "\u5019\u9009\u53C2\u8003\u56FE\u7247\u91CC\u6CA1\u6709\u5408\u9002\u7684\u56FE\u7247\uFF0C\u9700\u8981\u4F60\u8865\u5145\u4E00\u5F20\u53C2\u8003\u56FE",
1415
- noask: "\u672A\u6536\u5230\u8865\u5145\u56FE\u7247\uFF0C\u5C06\u4F7F\u7528\u73B0\u6709\u56FE\u7247\u7EE7\u7EED\u5904\u7406",
1617
+ noask: "\u672A\u6536\u5230\u8865\u5145\u56FE\u7247\uFF0C\u7EE7\u7EED\u5904\u7406",
1416
1618
  gotimage: "\u5DF2\u6536\u5230\u8865\u5145\u56FE\u7247\uFF0C\u7EE7\u7EED\u5904\u7406...",
1417
1619
  recalled: "\uFF08\u53C2\u8003\u56FE\u68C0\u7D22\uFF1A{0} \u5F20\u4E2D\u547D\u4E2D {1} \u5F20\u5019\u9009\uFF09",
1418
1620
  hintsingle: "\u53C2\u8003\u56FE\u5173\u952E\u7279\u5F81\uFF08\u52A1\u5FC5\u8FD8\u539F\uFF09\uFF1A{0}",
@@ -1586,15 +1788,20 @@ ${session.text(`image-prompt.messages.${hintKey}`, [selection.hint])}`;
1586
1788
  aiRenderText = selection.renderText;
1587
1789
  logInfo2(`\u9009\u56FE\u6A21\u578B\u7ED9\u51FA\u9700\u8981\u6E32\u67D3\u7684\u6587\u5B57: ${aiRenderText.replace(/\n/g, " / ")}`);
1588
1790
  }
1589
- } else if (!selection.ok || selection.picked.length === 0) {
1590
- if (!selection.ok) {
1591
- ctx.logger.warn(`[${cmdConfig.name}] AI \u9009\u56FE\u5931\u8D25\uFF0CfallbackOnError=${selector.fallbackOnError}`);
1791
+ } else if (selection.ok && selection.picked.length === 0) {
1792
+ logInfo2(`[${cmdConfig.name}] \u6CA1\u6709\u53C2\u8003\u56FE\u8FBE\u5230\u9608\u503C\uFF1A${selection.reason}`);
1793
+ if (selector.notify) {
1794
+ selectionNote = "\n" + session.text("image-prompt.messages.nomatch", [
1795
+ selection.reason || "\u65E0\u5339\u914D"
1796
+ ]);
1592
1797
  }
1798
+ } else if (!selection.ok) {
1799
+ ctx.logger.warn(`[${cmdConfig.name}] AI \u9009\u56FE\u5931\u8D25\uFF0CfallbackOnError=${selector.fallbackOnError}`);
1593
1800
  if (selector.fallbackOnError) {
1594
1801
  images.push(...candidates.map((c) => c.url));
1595
1802
  aiProvidedImages += candidates.length;
1596
1803
  }
1597
- if (selector.notify && !selection.ok) {
1804
+ if (selector.notify) {
1598
1805
  selectionNote = "\n" + session.text(
1599
1806
  selector.fallbackOnError ? "image-prompt.messages.selectfailed" : "image-prompt.messages.selectfailedNoFallback",
1600
1807
  [selection.error || "\u672A\u77E5\u539F\u56E0"]
@@ -1667,7 +1874,7 @@ Text in the image must be rendered exactly as shown in the attached text-referen
1667
1874
  }
1668
1875
  }
1669
1876
  if (images.length === 0 && !textRef) {
1670
- await reply(session, [session.text("image-prompt.messages.needimages")]);
1877
+ await reply(session, [session.text("image-prompt.messages.needimages") + selectionNote]);
1671
1878
  return;
1672
1879
  }
1673
1880
  logInfo2(images);
@@ -2142,6 +2349,9 @@ ${lines.join("\n")}`]);
2142
2349
  }
2143
2350
  async function runAISelection(candidates, userInput, cmdConfig, promptText, max) {
2144
2351
  const selector = config.aiSelector || {};
2352
+ if (selector.selectionMode !== "legacy") {
2353
+ return runScoredSelection(candidates, userInput, cmdConfig, promptText, max);
2354
+ }
2145
2355
  const threshold = selector.twoStageThreshold || 12;
2146
2356
  if (selector.twoStage !== false && candidates.length > threshold) {
2147
2357
  const result = await runTwoStageSelection(candidates, userInput, cmdConfig, promptText, max);
@@ -2150,6 +2360,111 @@ ${lines.join("\n")}`]);
2150
2360
  }
2151
2361
  return runSingleStageSelection(candidates, userInput, cmdConfig, promptText, max);
2152
2362
  }
2363
+ async function runScoredSelection(candidates, userInput, cmdConfig, promptText, max) {
2364
+ const selector = config.aiSelector || {};
2365
+ const minScore = typeof selector.minScore === "number" ? selector.minScore : 60;
2366
+ const topK = selector.retrievalTopK || 12;
2367
+ const respectSkip = selector.respectSkipReference !== false;
2368
+ if (respectSkip && isSkipReferenceInput(userInput)) {
2369
+ logInfo2("\u7528\u6237\u8868\u793A\u4E0D\u9700\u8981\u53C2\u8003\u56FE\uFF0C\u8DF3\u8FC7\u9009\u56FE");
2370
+ return { picked: [], needUserImage: false, askMessage: "", reason: "\u7528\u6237\u8868\u793A\u4E0D\u9700\u8981\u53C2\u8003\u56FE", ok: true };
2371
+ }
2372
+ const analyzeContent = (selector.analyzePrompt || DEFAULT_ANALYZE_PROMPT).replace(/\{command\}/g, cmdConfig.name || "").replace(/\{prompt\}/g, promptText || "").replace(/\{userInput\}/g, userInput || "\uFF08\u7528\u6237\u672A\u9644\u52A0\u8BF4\u660E\uFF09");
2373
+ const analysisCall = await callSelectorModel(
2374
+ analyzeContent,
2375
+ analyzeContent,
2376
+ false,
2377
+ selector.keywordMaxTokens || 3e3,
2378
+ "\u4F60\u662F\u7ED8\u56FE\u53C2\u8003\u56FE\u9700\u6C42\u5206\u6790\u52A9\u624B\uFF0C\u53EA\u8F93\u51FA JSON\uFF0C\u4E0D\u8981\u6311\u9009\u56FE\u7247\u3002"
2379
+ );
2380
+ const analysis = parseAnalysisResult(analysisCall.raw);
2381
+ if (!analysis) {
2382
+ return {
2383
+ picked: [],
2384
+ needUserImage: false,
2385
+ askMessage: "",
2386
+ reason: "",
2387
+ ok: false,
2388
+ error: analysisCall.error || "\u9700\u6C42\u5206\u6790\u6CA1\u6709\u8FD4\u56DE\u53EF\u89E3\u6790\u7684\u7ED3\u679C"
2389
+ };
2390
+ }
2391
+ logInfo2(`\u9700\u6C42\u5206\u6790\uFF1A\u4E3B\u4F53=${analysis.subject || "\uFF08\u65E0\uFF09"} \u786C\u6027=${JSON.stringify(analysis.must)} \u52A0\u5206=${JSON.stringify(analysis.nice)}`);
2392
+ if (analysis.skipReference && respectSkip) {
2393
+ logInfo2("\u9700\u6C42\u5206\u6790\u5224\u5B9A\u672C\u6B21\u4E0D\u9700\u8981\u53C2\u8003\u56FE");
2394
+ return {
2395
+ picked: [],
2396
+ needUserImage: false,
2397
+ askMessage: "",
2398
+ reason: "\u672C\u6B21\u4E0D\u9700\u8981\u53C2\u8003\u56FE",
2399
+ ok: true,
2400
+ keywords: keywordsFromAnalysis(analysis)
2401
+ };
2402
+ }
2403
+ const pool = selector.dedupe === false ? candidates : dedupeCandidates(candidates);
2404
+ const keywords = keywordsFromAnalysis(analysis);
2405
+ const matched = matchCandidatesByKeywords(pool, keywords, topK);
2406
+ const shortlist = matched.length > 0 ? matched : pool.slice(0, Math.max(topK, 1) * 2);
2407
+ logInfo2(`\u5173\u952E\u8BCD\u53EC\u56DE ${shortlist.length}/${pool.length} \u5F20\u8FDB\u5165\u6253\u5206\uFF08\u5173\u952E\u8BCD\uFF1A${keywords.join("/") || "\u65E0"}\uFF09`);
2408
+ if (shortlist.length === 0) {
2409
+ return {
2410
+ picked: [],
2411
+ needUserImage: true,
2412
+ askMessage: analysis.needTypes,
2413
+ reason: "\u5019\u9009\u6C60\u91CC\u6CA1\u6709\u56FE\u7247",
2414
+ ok: true,
2415
+ keywords,
2416
+ recalled: 0,
2417
+ total: pool.length,
2418
+ renderText: analysis.text || void 0
2419
+ };
2420
+ }
2421
+ const scoreContent = (selector.scorePrompt || DEFAULT_SCORE_PROMPT).replace(/\{subject\}/g, analysis.subject || "\uFF08\u672A\u6307\u5B9A\uFF09").replace(/\{must\}/g, analysis.must.length ? analysis.must.join("\u3001") : "\uFF08\u65E0\uFF09").replace(/\{nice\}/g, analysis.nice.length ? analysis.nice.join("\u3001") : "\uFF08\u65E0\uFF09").replace(/\{avoid\}/g, analysis.avoid.length ? analysis.avoid.join("\u3001") : "\uFF08\u65E0\uFF09").replace(/\{candidates\}/g, buildIndexText(shortlist)).replace(/\{max\}/g, String(max));
2422
+ const visionOn = selector.vision === true;
2423
+ const scoreUserContent = buildSelectorContent(
2424
+ scoreContent,
2425
+ shortlist,
2426
+ visionOn,
2427
+ selector.visionMaxImages || 6
2428
+ );
2429
+ const scoreCall = await callSelectorModel(
2430
+ scoreUserContent,
2431
+ scoreContent,
2432
+ visionOn,
2433
+ selector.selectMaxTokens || 8e3,
2434
+ "\u4F60\u662F\u53C2\u8003\u56FE\u6253\u5206\u52A9\u624B\uFF0C\u53EA\u8F93\u51FA JSON\u3002"
2435
+ );
2436
+ const scored = parseScoreResult(scoreCall.raw, shortlist);
2437
+ if (scored.length === 0) {
2438
+ return {
2439
+ picked: [],
2440
+ needUserImage: false,
2441
+ askMessage: "",
2442
+ reason: "",
2443
+ ok: false,
2444
+ keywords,
2445
+ recalled: shortlist.length,
2446
+ total: pool.length,
2447
+ error: scoreCall.error || "\u6253\u5206\u9636\u6BB5\u6CA1\u6709\u62FF\u5230\u6709\u6548\u7684\u5206\u6570"
2448
+ };
2449
+ }
2450
+ const { picked, best, passed } = selectByScore(scored, shortlist, minScore, max);
2451
+ const detail = scored.slice().sort((a, b) => b.score - a.score).slice(0, 5).map((item) => `${item.index}:${item.score}\u5206(${item.why || "-"})`).join(" ");
2452
+ logInfo2(`\u9010\u56FE\u6253\u5206\uFF08\u9608\u503C ${minScore}\uFF09\uFF1A\u6700\u9AD8 ${best} \u5206\uFF0C\u8FBE\u6807 ${passed} \u5F20\uFF0C\u9009\u4E2D ${picked.length} \u5F20 | ${detail}`);
2453
+ const hint = String(extractJsonObject(scoreCall.raw)?.hint || "").trim() || void 0;
2454
+ return {
2455
+ picked,
2456
+ // 一张都不达标 = 图库里确实没有合适的 -> 让上层去问用户补图,而不是硬塞
2457
+ needUserImage: picked.length === 0,
2458
+ askMessage: picked.length === 0 ? analysis.needTypes || "" : "",
2459
+ reason: picked.length ? `\u547D\u4E2D\u9700\u6C42\uFF08\u6700\u9AD8 ${best} \u5206\uFF09` : `\u6CA1\u6709\u56FE\u8FBE\u5230 ${minScore} \u5206\u95E8\u69DB\uFF08\u6700\u9AD8\u53EA\u6709 ${best} \u5206\uFF09`,
2460
+ ok: true,
2461
+ keywords,
2462
+ recalled: shortlist.length,
2463
+ total: pool.length,
2464
+ renderText: analysis.text || void 0,
2465
+ hint: visionOn && selector.appendHint !== false ? hint : void 0
2466
+ };
2467
+ }
2153
2468
  function buildIndexText(candidates) {
2154
2469
  return candidates.map((c, i) => `[${i + 1}] \u6240\u5C5E\u7EC4\uFF1A${c.group} | \u63CF\u8FF0\uFF1A${c.description || "\uFF08\u65E0\u63CF\u8FF0\uFF09"}`).join("\n");
2155
2470
  }
@@ -2483,8 +2798,10 @@ ${lines.join("\n")}`]);
2483
2798
  buildSelectorContent,
2484
2799
  buildTextHtml,
2485
2800
  computeRetryDelay,
2801
+ dedupeCandidates,
2486
2802
  describeSelectorError,
2487
2803
  escapeHtml,
2804
+ extractJsonObject,
2488
2805
  extractResponseText,
2489
2806
  extractServerMessage,
2490
2807
  extractTextToRender,
@@ -2492,19 +2809,26 @@ ${lines.join("\n")}`]);
2492
2809
  inject,
2493
2810
  isPassiveReplyError,
2494
2811
  isRetryableStatus,
2812
+ isSkipReferenceInput,
2813
+ keywordsFromAnalysis,
2495
2814
  matchCandidatesByKeywords,
2496
2815
  mergeCandidates,
2497
2816
  mergePrompt,
2498
2817
  name,
2818
+ normalizeScore,
2819
+ parseAnalysisResult,
2499
2820
  parseRetryAfterHeader,
2821
+ parseScoreResult,
2500
2822
  parseSelectionResult,
2501
2823
  previewJson,
2502
2824
  readImageSize,
2503
2825
  resolveTokenParam,
2504
2826
  sanitizeCaption,
2827
+ selectByScore,
2505
2828
  stripCommandName,
2506
2829
  stripThinkTags,
2507
2830
  supportsMarkdown,
2831
+ toStrList,
2508
2832
  trimGallery,
2509
2833
  truncateText,
2510
2834
  usage,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-image-prompt",
3
3
  "description": "多功能 AI 图像处理插件,支持手办化、Coser化、Minecraft化等47种风格转换,并提供自定义提示词和多图融合功能",
4
- "version": "1.1.1",
4
+ "version": "1.2.0",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [