@akemi-mio/creativity 0.0.0-stage → 0.1.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.
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ /**
3
+ * NoveltyScorer — 代码层新颖度评估
4
+ *
5
+ * 在 LLM 自评 novelty 之外,通过文本相似度对输入假设进行再评估:
6
+ * 1. 与近期假设(past 30)的 idea 文本对比,Jaccard 相似度 > 0.5 → 扣 15 分
7
+ * 2. 与已拒绝假设的 idea 文本对比,Jaccard 相似度 > 0.6 → 标记为 rejected
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.jaccardSimilarity = jaccardSimilarity;
11
+ exports.evaluateNovelty = evaluateNovelty;
12
+ // ==================== Tokenizer ====================
13
+ /**
14
+ * 简易 tokenizer:将文本拆分为 token 集合
15
+ * - 英文词按空格/标点分割、转小写
16
+ * - 中文按二元组(bigram)拆分(中文分词需要词典,这里用 bigram 近似)
17
+ * - 去重、去空
18
+ */
19
+ function tokenize(text) {
20
+ const tokens = new Set();
21
+ // 英文/数字 token
22
+ const enTokens = text
23
+ .toLowerCase()
24
+ .split(/[^a-z0-9一-鿿]+/g)
25
+ .filter((t) => t.length > 1 && /[a-z0-9]/.test(t));
26
+ for (const t of enTokens)
27
+ tokens.add(t);
28
+ // 中文 bigram
29
+ const chChars = text.replace(/[^一-鿿]/g, '');
30
+ for (let i = 0; i < chChars.length - 1; i++) {
31
+ tokens.add(chChars.slice(i, i + 2));
32
+ }
33
+ return tokens;
34
+ }
35
+ // ==================== Similarity ====================
36
+ /** Jaccard 相似度:|A ∩ B| / |A ∪ B| */
37
+ function jaccardSimilarity(a, b) {
38
+ const setA = tokenize(a);
39
+ const setB = tokenize(b);
40
+ const union = new Set([...setA, ...setB]);
41
+ if (union.size === 0)
42
+ return 0;
43
+ let intersection = 0;
44
+ for (const t of setA) {
45
+ if (setB.has(t))
46
+ intersection++;
47
+ }
48
+ return intersection / union.size;
49
+ }
50
+ /**
51
+ * 对一条新假设进行代码层新颖度评估
52
+ *
53
+ * @param candidate 新假设
54
+ * @param recentHypotheses 近期假设(用于扣分)
55
+ * @param rejectedHypotheses 已拒绝假设(用于自动拒绝)
56
+ * @returns NoveltyVerdict
57
+ */
58
+ function evaluateNovelty(candidate, recentHypotheses, rejectedHypotheses) {
59
+ const candidateText = `${candidate.title} ${candidate.idea}`;
60
+ let adjustedNovelty = candidate.novelty;
61
+ let mostSimilarTitle = '';
62
+ let mostSimilarScore = 0;
63
+ // 1. 与近期假设对比
64
+ for (const h of recentHypotheses) {
65
+ const sim = jaccardSimilarity(candidateText, `${h.title} ${h.idea}`);
66
+ if (sim > mostSimilarScore) {
67
+ mostSimilarScore = sim;
68
+ mostSimilarTitle = h.title;
69
+ }
70
+ }
71
+ if (mostSimilarScore > 0.5) {
72
+ adjustedNovelty = Math.max(0, adjustedNovelty - 15);
73
+ }
74
+ // 2. 与已拒绝假设对比 — 高度相似则自动拒绝
75
+ for (const h of rejectedHypotheses) {
76
+ const sim = jaccardSimilarity(candidateText, `${h.title} ${h.idea}`);
77
+ if (sim > 0.6) {
78
+ return {
79
+ adjustedNovelty,
80
+ shouldReject: true,
81
+ rejectReason: `与已拒绝假设"${h.title}"内容高度相似 (Jaccard=${sim.toFixed(2)})`,
82
+ mostSimilarTitle: h.title,
83
+ mostSimilarScore: sim,
84
+ };
85
+ }
86
+ }
87
+ return {
88
+ adjustedNovelty,
89
+ shouldReject: false,
90
+ mostSimilarTitle,
91
+ mostSimilarScore,
92
+ };
93
+ }
@@ -0,0 +1,21 @@
1
+ import type { CreativitySource } from './types';
2
+ export type SourceDomain = 'module' | 'behavior' | 'observation' | 'external' | 'failure' | 'feedback';
3
+ export interface SourceProvider {
4
+ domain: SourceDomain;
5
+ name: string;
6
+ collect: () => CreativitySource[];
7
+ }
8
+ /**
9
+ * SourceAggregator — 来源总线
10
+ *
11
+ * 统一收集多域来源,做去重、每域限流与失败降级,
12
+ * 供创意产生与发酵两端共用同一份信号。
13
+ */
14
+ export declare class SourceAggregator {
15
+ private providers;
16
+ private maxPerDomain;
17
+ private minDomains;
18
+ constructor(maxPerDomain?: number, minDomains?: number);
19
+ addProvider(provider: SourceProvider): void;
20
+ build(): CreativitySource[];
21
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SourceAggregator = void 0;
4
+ const Logger_1 = require("@akemi-mio/core/logger/Logger");
5
+ /**
6
+ * SourceAggregator — 来源总线
7
+ *
8
+ * 统一收集多域来源,做去重、每域限流与失败降级,
9
+ * 供创意产生与发酵两端共用同一份信号。
10
+ */
11
+ class SourceAggregator {
12
+ providers = [];
13
+ maxPerDomain;
14
+ minDomains;
15
+ constructor(maxPerDomain = 4, minDomains = 3) {
16
+ this.maxPerDomain = maxPerDomain;
17
+ this.minDomains = minDomains;
18
+ }
19
+ addProvider(provider) {
20
+ this.providers.push(provider);
21
+ }
22
+ build() {
23
+ const byDomain = new Map();
24
+ const seen = new Set();
25
+ for (const provider of this.providers) {
26
+ let collected = [];
27
+ try {
28
+ collected = provider.collect() ?? [];
29
+ }
30
+ catch (err) {
31
+ (0, Logger_1.log)('WARN', 'source_provider_failed', { provider: provider.name, error: String(err?.message ?? err) });
32
+ continue;
33
+ }
34
+ const unique = collected.filter((s) => {
35
+ const key = `${provider.domain}:${s.name}`;
36
+ if (seen.has(key))
37
+ return false;
38
+ seen.add(key);
39
+ return true;
40
+ });
41
+ const list = byDomain.get(provider.domain) ?? [];
42
+ byDomain.set(provider.domain, [...list, ...unique]);
43
+ }
44
+ const output = [];
45
+ let domainsWithData = 0;
46
+ for (const [domain, sources] of byDomain) {
47
+ if (sources.length > 0)
48
+ domainsWithData++;
49
+ const sorted = [...sources].sort((a, b) => b.weight - a.weight);
50
+ output.push(...sorted.slice(0, this.maxPerDomain));
51
+ }
52
+ if (domainsWithData < this.minDomains) {
53
+ (0, Logger_1.log)('WARN', 'source_aggregator_low_diversity', { domains: domainsWithData, min: this.minDomains });
54
+ }
55
+ (0, Logger_1.log)('INFO', 'source_aggregator_build', { total: output.length, domains: domainsWithData });
56
+ return output;
57
+ }
58
+ }
59
+ exports.SourceAggregator = SourceAggregator;
@@ -0,0 +1,29 @@
1
+ import type { CreativitySource, ConceptCombo, Hypothesis } from './types';
2
+ /**
3
+ * TemplateLibrary — 本地假设生成引擎
4
+ *
5
+ * 使用方式:
6
+ * 1. selectBest(combo, sources) 根据来源类型选择 2-3 个最佳模板
7
+ * 2. fill(template, sourceA, sourceB) 填充模板内容
8
+ * 3. 直接 generate(combo, sources) 一站式生成 Hypothesis
9
+ */
10
+ export declare class TemplateLibrary {
11
+ private rng;
12
+ constructor(seed?: number);
13
+ /**
14
+ * 为一个 ConceptCombo 生成一条假设
15
+ * @returns 生成的 Hypothesis,如果无适用模板则返回 null
16
+ */
17
+ generate(combo: ConceptCombo, sources: CreativitySource[]): Hypothesis | null;
18
+ /**
19
+ * 返回适用于某个类型组合的所有模板列表
20
+ * @param nameKey 对 source name 排序后的组合键,用于 nameFit 二次路由
21
+ */
22
+ private selectForType;
23
+ private typeKey;
24
+ /** 对 source name 排序生成 nameFit 查询键 */
25
+ private nameKey;
26
+ private stableHash;
27
+ /** 评分的小幅随机扰动(0-15),保持一定多样性 */
28
+ private scoreNoise;
29
+ }