@cicctencent/agent-core 0.1.1

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.
Files changed (134) hide show
  1. package/README.md +952 -0
  2. package/dist/agent/engine.d.ts +113 -0
  3. package/dist/agent/engine.d.ts.map +1 -0
  4. package/dist/agent/history-manager.d.ts +78 -0
  5. package/dist/agent/history-manager.d.ts.map +1 -0
  6. package/dist/agent/llm-caller.d.ts +58 -0
  7. package/dist/agent/llm-caller.d.ts.map +1 -0
  8. package/dist/agent/tool-executor.d.ts +95 -0
  9. package/dist/agent/tool-executor.d.ts.map +1 -0
  10. package/dist/agent/types.d.ts +128 -0
  11. package/dist/agent/types.d.ts.map +1 -0
  12. package/dist/agent/workflow.d.ts +74 -0
  13. package/dist/agent/workflow.d.ts.map +1 -0
  14. package/dist/context/collector.d.ts +12 -0
  15. package/dist/context/collector.d.ts.map +1 -0
  16. package/dist/context/manager.d.ts +16 -0
  17. package/dist/context/manager.d.ts.map +1 -0
  18. package/dist/context/types.d.ts +39 -0
  19. package/dist/context/types.d.ts.map +1 -0
  20. package/dist/hooks/types.d.ts +47 -0
  21. package/dist/hooks/types.d.ts.map +1 -0
  22. package/dist/index.d.ts +53 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/llm/anthropic-provider.d.ts +28 -0
  25. package/dist/llm/anthropic-provider.d.ts.map +1 -0
  26. package/dist/llm/factory.d.ts +19 -0
  27. package/dist/llm/factory.d.ts.map +1 -0
  28. package/dist/llm/ollama-provider.d.ts +13 -0
  29. package/dist/llm/ollama-provider.d.ts.map +1 -0
  30. package/dist/llm/openai-provider.d.ts +18 -0
  31. package/dist/llm/openai-provider.d.ts.map +1 -0
  32. package/dist/llm/provider.d.ts +7 -0
  33. package/dist/llm/provider.d.ts.map +1 -0
  34. package/dist/llm/types.d.ts +78 -0
  35. package/dist/llm/types.d.ts.map +1 -0
  36. package/dist/mcp/base-client.d.ts +50 -0
  37. package/dist/mcp/base-client.d.ts.map +1 -0
  38. package/dist/mcp/client.d.ts +27 -0
  39. package/dist/mcp/client.d.ts.map +1 -0
  40. package/dist/mcp/factory.d.ts +16 -0
  41. package/dist/mcp/factory.d.ts.map +1 -0
  42. package/dist/mcp/http-client.d.ts +24 -0
  43. package/dist/mcp/http-client.d.ts.map +1 -0
  44. package/dist/mcp/proxy-agent.d.ts +49 -0
  45. package/dist/mcp/proxy-agent.d.ts.map +1 -0
  46. package/dist/mcp/stdio-client.d.ts +16 -0
  47. package/dist/mcp/stdio-client.d.ts.map +1 -0
  48. package/dist/mcp/types.d.ts +60 -0
  49. package/dist/mcp/types.d.ts.map +1 -0
  50. package/dist/memory/checkpoint.d.ts +62 -0
  51. package/dist/memory/checkpoint.d.ts.map +1 -0
  52. package/dist/memory/extractor.d.ts +59 -0
  53. package/dist/memory/extractor.d.ts.map +1 -0
  54. package/dist/memory/persona.d.ts +38 -0
  55. package/dist/memory/persona.d.ts.map +1 -0
  56. package/dist/memory/pipeline.d.ts +50 -0
  57. package/dist/memory/pipeline.d.ts.map +1 -0
  58. package/dist/memory/prompts.d.ts +9 -0
  59. package/dist/memory/prompts.d.ts.map +1 -0
  60. package/dist/memory/recall.d.ts +66 -0
  61. package/dist/memory/recall.d.ts.map +1 -0
  62. package/dist/memory/scene.d.ts +49 -0
  63. package/dist/memory/scene.d.ts.map +1 -0
  64. package/dist/memory/types.d.ts +43 -0
  65. package/dist/memory/types.d.ts.map +1 -0
  66. package/dist/middleware/types.d.ts +52 -0
  67. package/dist/middleware/types.d.ts.map +1 -0
  68. package/dist/sandbox/types.d.ts +59 -0
  69. package/dist/sandbox/types.d.ts.map +1 -0
  70. package/dist/security/guard.d.ts +41 -0
  71. package/dist/security/guard.d.ts.map +1 -0
  72. package/dist/security/types.d.ts +36 -0
  73. package/dist/security/types.d.ts.map +1 -0
  74. package/dist/skill/manager.d.ts +95 -0
  75. package/dist/skill/manager.d.ts.map +1 -0
  76. package/dist/skill/router.d.ts +26 -0
  77. package/dist/skill/router.d.ts.map +1 -0
  78. package/dist/skill/types.d.ts +55 -0
  79. package/dist/skill/types.d.ts.map +1 -0
  80. package/dist/tool/delegate.d.ts +34 -0
  81. package/dist/tool/delegate.d.ts.map +1 -0
  82. package/dist/tool/registry.d.ts +24 -0
  83. package/dist/tool/registry.d.ts.map +1 -0
  84. package/dist/tool/types.d.ts +101 -0
  85. package/dist/tool/types.d.ts.map +1 -0
  86. package/dist/types.d.ts +150 -0
  87. package/dist/types.d.ts.map +1 -0
  88. package/dist/utils/token-counter.d.ts +22 -0
  89. package/dist/utils/token-counter.d.ts.map +1 -0
  90. package/package.json +42 -0
  91. package/src/agent/engine.ts +875 -0
  92. package/src/agent/history-manager.ts +323 -0
  93. package/src/agent/llm-caller.ts +383 -0
  94. package/src/agent/tool-executor.ts +713 -0
  95. package/src/agent/types.ts +136 -0
  96. package/src/agent/workflow.ts +339 -0
  97. package/src/context/collector.ts +59 -0
  98. package/src/context/manager.ts +40 -0
  99. package/src/context/types.ts +51 -0
  100. package/src/hooks/types.ts +77 -0
  101. package/src/index.ts +178 -0
  102. package/src/llm/anthropic-provider.ts +412 -0
  103. package/src/llm/factory.ts +34 -0
  104. package/src/llm/ollama-provider.ts +39 -0
  105. package/src/llm/openai-provider.ts +200 -0
  106. package/src/llm/provider.ts +15 -0
  107. package/src/llm/types.ts +91 -0
  108. package/src/mcp/base-client.ts +159 -0
  109. package/src/mcp/client.ts +81 -0
  110. package/src/mcp/factory.ts +45 -0
  111. package/src/mcp/http-client.ts +149 -0
  112. package/src/mcp/proxy-agent.ts +227 -0
  113. package/src/mcp/stdio-client.ts +104 -0
  114. package/src/mcp/types.ts +65 -0
  115. package/src/memory/checkpoint.ts +146 -0
  116. package/src/memory/extractor.ts +164 -0
  117. package/src/memory/persona.ts +106 -0
  118. package/src/memory/pipeline.ts +192 -0
  119. package/src/memory/prompts.ts +73 -0
  120. package/src/memory/recall.ts +217 -0
  121. package/src/memory/scene.ts +137 -0
  122. package/src/memory/types.ts +54 -0
  123. package/src/middleware/types.ts +62 -0
  124. package/src/sandbox/types.ts +185 -0
  125. package/src/security/guard.ts +249 -0
  126. package/src/security/types.ts +46 -0
  127. package/src/skill/manager.ts +420 -0
  128. package/src/skill/router.ts +158 -0
  129. package/src/skill/types.ts +67 -0
  130. package/src/tool/delegate.ts +105 -0
  131. package/src/tool/registry.ts +64 -0
  132. package/src/tool/types.ts +124 -0
  133. package/src/types.ts +188 -0
  134. package/src/utils/token-counter.ts +108 -0
@@ -0,0 +1,164 @@
1
+ // ============================================================
2
+ // MemoryExtractor — LLM 驱动的原子事实提取器
3
+ // ============================================================
4
+
5
+ import type { LLMProvider } from '../llm/types.ts';
6
+ import type { Message } from '../types.ts';
7
+ import {
8
+ EXTRACTION_SYSTEM_PROMPT,
9
+ buildExtractionUserPrompt,
10
+ } from './prompts.ts';
11
+
12
+ /** 从对话中提取的原子事实 */
13
+ export interface AtomFact {
14
+ /** 记忆键(snake_case,用于 upsert) */
15
+ key: string;
16
+ /** 事实内容 */
17
+ content: string;
18
+ /** 分类 */
19
+ category: 'user_preference' | 'project_knowledge' | 'learned_fact' | 'custom';
20
+ /** 重要程度 1-5 */
21
+ importance: number;
22
+ /** 来源会话 ID */
23
+ sourceSessionId: string;
24
+ }
25
+
26
+ /** 提取配置 */
27
+ export interface ExtractorOptions {
28
+ /** LLM Provider 实例 */
29
+ llmProvider: LLMProvider;
30
+ /** 单次提取最大原子事实数,默认 5 */
31
+ maxFactsPerSession?: number;
32
+ /** 触发提取的最小迭代次数,默认 3 */
33
+ minIterations?: number;
34
+ /** 提取超时(毫秒),默认 30000 */
35
+ timeoutMs?: number;
36
+ }
37
+
38
+ /**
39
+ * LLM 驱动的原子事实提取器
40
+ * 从对话历史中提取持久性、可复用的结构化事实(L1 Atom)
41
+ */
42
+ export class MemoryExtractor {
43
+ private llmProvider: LLMProvider;
44
+ private maxFactsPerSession: number;
45
+ private minIterations: number;
46
+ private timeoutMs: number;
47
+
48
+ constructor(opts: ExtractorOptions) {
49
+ this.llmProvider = opts.llmProvider;
50
+ this.maxFactsPerSession = opts.maxFactsPerSession ?? 5;
51
+ this.minIterations = opts.minIterations ?? 3;
52
+ this.timeoutMs = opts.timeoutMs ?? 30000;
53
+ }
54
+
55
+ /**
56
+ * 从对话消息中提取原子事实
57
+ * @param messages 对话消息列表
58
+ * @param sessionId 当前会话 ID
59
+ * @param existingKeys 已有的记忆键(用于去重)
60
+ * @returns 提取到的原子事实数组
61
+ */
62
+ async extract(
63
+ messages: Message[],
64
+ sessionId: string,
65
+ existingKeys: string[],
66
+ ): Promise<AtomFact[]> {
67
+ // 构建对话摘要(避免超长对话)
68
+ const summary = this.buildMessagesSummary(messages);
69
+
70
+ const userPrompt = buildExtractionUserPrompt(summary, existingKeys);
71
+
72
+ try {
73
+ const result = await this.callLLM(userPrompt);
74
+ return this.parseFacts(result, sessionId);
75
+ } catch (err: any) {
76
+ console.warn(`[MemoryExtractor] Extraction failed: ${err?.message || err}`);
77
+ return [];
78
+ }
79
+ }
80
+
81
+ /**
82
+ * 调用 LLM 提取事实
83
+ */
84
+ private async callLLM(userPrompt: string): Promise<string> {
85
+ const messages: Message[] = [
86
+ { role: 'system', content: EXTRACTION_SYSTEM_PROMPT },
87
+ { role: 'user', content: userPrompt },
88
+ ];
89
+
90
+ const chunks: string[] = [];
91
+ const stream = this.llmProvider.chatStream({
92
+ messages,
93
+ abortSignal: AbortSignal.timeout(this.timeoutMs),
94
+ });
95
+
96
+ for await (const chunk of stream) {
97
+ if (chunk.delta.content) {
98
+ chunks.push(chunk.delta.content);
99
+ }
100
+ }
101
+
102
+ return chunks.join('');
103
+ }
104
+
105
+ /**
106
+ * 解析 LLM 输出的 JSON 数组
107
+ */
108
+ private parseFacts(raw: string, sessionId: string): AtomFact[] {
109
+ // 尝试提取 JSON 数组(LLM 可能输出额外文本)
110
+ const jsonMatch = raw.match(/\[[\s\S]*\]/);
111
+ if (!jsonMatch) return [];
112
+
113
+ try {
114
+ const parsed = JSON.parse(jsonMatch[0]);
115
+ if (!Array.isArray(parsed)) return [];
116
+
117
+ return parsed
118
+ .filter((item: any) => item.key && item.content)
119
+ .slice(0, this.maxFactsPerSession)
120
+ .map((item: any) => ({
121
+ key: String(item.key).replace(/[^a-z0-9_]/g, '_').toLowerCase(),
122
+ content: String(item.content),
123
+ category: this.normalizeCategory(item.category),
124
+ importance: Math.min(5, Math.max(1, Number(item.importance) || 3)),
125
+ sourceSessionId: sessionId,
126
+ }));
127
+ } catch {
128
+ console.warn('[MemoryExtractor] Failed to parse LLM output as JSON');
129
+ return [];
130
+ }
131
+ }
132
+
133
+ private normalizeCategory(cat: string): AtomFact['category'] {
134
+ const valid: AtomFact['category'][] = ['user_preference', 'project_knowledge', 'learned_fact', 'custom'];
135
+ return valid.includes(cat as AtomFact['category']) ? (cat as AtomFact['category']) : 'custom';
136
+ }
137
+
138
+ /**
139
+ * 构建对话摘要(将消息列表压缩为文本,限制总字符数)
140
+ */
141
+ private buildMessagesSummary(messages: Message[]): string {
142
+ const maxChars = 6000;
143
+ const lines: string[] = [];
144
+ let totalLen = 0;
145
+
146
+ for (const msg of messages) {
147
+ if (msg.role === 'system') continue;
148
+ const role = msg.role === 'user' ? 'USER' : msg.role === 'assistant' ? 'ASSISTANT' : 'TOOL';
149
+ const content = typeof msg.content === 'string'
150
+ ? msg.content.slice(0, 500)
151
+ : JSON.stringify(msg.content).slice(0, 500);
152
+ const line = `[${role}] ${content}`;
153
+
154
+ if (totalLen + line.length > maxChars) {
155
+ lines.push(`... (${messages.length - lines.length} more messages truncated)`);
156
+ break;
157
+ }
158
+ lines.push(line);
159
+ totalLen += line.length;
160
+ }
161
+
162
+ return lines.join('\n');
163
+ }
164
+ }
@@ -0,0 +1,106 @@
1
+ // ============================================================
2
+ // PersonaGenerator — L3 用户画像生成器
3
+ // ============================================================
4
+ // 基于 L2 场景块生成/增量更新用户画像(L3 Persona),
5
+ // 画像以 markdown 格式存储,注入 prompt 时提供用户全貌
6
+
7
+ import type { LLMProvider } from '../llm/types.ts';
8
+ import type { Message } from '../types.ts';
9
+ import { PERSONA_GENERATION_SYSTEM_PROMPT } from './prompts.ts';
10
+ import type { SceneBlock } from './scene.ts';
11
+
12
+ /** 画像维度 */
13
+ export interface PersonaFacet {
14
+ /** 维度键(如 coding_style, tool_preference) */
15
+ key: string;
16
+ /** 维度内容(markdown) */
17
+ content: string;
18
+ }
19
+
20
+ /** 画像生成器配置 */
21
+ export interface PersonaGeneratorOptions {
22
+ llmProvider: LLMProvider;
23
+ /** 超时(毫秒),默认 30000 */
24
+ timeoutMs?: number;
25
+ }
26
+
27
+ /**
28
+ * L3 用户画像生成器
29
+ *
30
+ * 工作流程:
31
+ * 1. 接收 L2 场景块列表
32
+ * 2. 通过 LLM 从场景中提炼稳定用户特征
33
+ * 3. 增量更新画像(保留已有内容,仅修改受影响的部分)
34
+ */
35
+ export class PersonaGenerator {
36
+ private llmProvider: LLMProvider;
37
+ private timeoutMs: number;
38
+
39
+ constructor(opts: PersonaGeneratorOptions) {
40
+ this.llmProvider = opts.llmProvider;
41
+ this.timeoutMs = opts.timeoutMs ?? 30000;
42
+ }
43
+
44
+ /**
45
+ * 生成或更新用户画像
46
+ * @param scenes L2 场景块列表
47
+ * @param existingPersona 已有画像(markdown 文本,用于增量更新)
48
+ * @returns 更新后的画像 markdown
49
+ */
50
+ async generate(
51
+ scenes: SceneBlock[],
52
+ existingPersona: string = '',
53
+ ): Promise<string> {
54
+ if (scenes.length === 0 && !existingPersona) return '';
55
+
56
+ const userPrompt = this.buildUserPrompt(scenes, existingPersona);
57
+
58
+ try {
59
+ return await this.callLLM(userPrompt);
60
+ } catch (err: any) {
61
+ console.warn(`[PersonaGenerator] Generation failed: ${err?.message || err}`);
62
+ return existingPersona;
63
+ }
64
+ }
65
+
66
+ private buildUserPrompt(scenes: SceneBlock[], existingPersona: string): string {
67
+ let prompt = '';
68
+
69
+ if (scenes.length > 0) {
70
+ const scenesText = scenes
71
+ .map(s => `### ${s.title}\n${s.description}\nAtoms: ${s.atomIds.length}`)
72
+ .join('\n\n');
73
+ prompt += `**Scenario blocks:**\n${scenesText}\n\n`;
74
+ }
75
+
76
+ if (existingPersona) {
77
+ prompt += `**Existing persona (update incrementally, preserve unaffected sections):**\n${existingPersona}\n\n`;
78
+ prompt += `Update the persona based on the new scenario blocks above. Preserve sections not affected by new data.`;
79
+ } else {
80
+ prompt += `Generate a user persona based on the scenario blocks above. Use markdown with clear section headers.`;
81
+ }
82
+
83
+ return prompt;
84
+ }
85
+
86
+ private async callLLM(userPrompt: string): Promise<string> {
87
+ const messages: Message[] = [
88
+ { role: 'system', content: PERSONA_GENERATION_SYSTEM_PROMPT },
89
+ { role: 'user', content: userPrompt },
90
+ ];
91
+
92
+ const chunks: string[] = [];
93
+ const stream = this.llmProvider.chatStream({
94
+ messages,
95
+ abortSignal: AbortSignal.timeout(this.timeoutMs),
96
+ });
97
+
98
+ for await (const chunk of stream) {
99
+ if (chunk.delta.content) {
100
+ chunks.push(chunk.delta.content);
101
+ }
102
+ }
103
+
104
+ return chunks.join('').trim();
105
+ }
106
+ }
@@ -0,0 +1,192 @@
1
+ // ============================================================
2
+ // MemoryPipeline — L1→L2→L3 记忆管道调度器
3
+ // ============================================================
4
+ // 调度记忆层级的渐进式提炼:
5
+ // L1 (原子事实) → L2 (场景聚合) → L3 (用户画像)
6
+ // 在对话结束后 fire-and-forget 触发
7
+
8
+ import type { LLMProvider } from '../llm/types.ts';
9
+ import type { MemoryEntry, MemoryLayer, MemoryServiceLike } from './types.ts';
10
+ import { MemoryExtractor } from './extractor.ts';
11
+ import { SceneAggregator, type SceneBlock } from './scene.ts';
12
+ import { PersonaGenerator } from './persona.ts';
13
+ import { getMemoryCheckpoint } from './checkpoint.ts';
14
+ import type { Message } from '../types.ts';
15
+
16
+ /** 管道配置 */
17
+ export interface PipelineOptions {
18
+ llmProvider: LLMProvider;
19
+ /** 触发 L2 聚合的 L1 增量阈值(新 L1 条目数),默认 5 */
20
+ l2Threshold?: number;
21
+ /** 触发 L3 画像更新的 L2 增量阈值,默认 3 */
22
+ l3Threshold?: number;
23
+ }
24
+
25
+ /** 管道执行结果 */
26
+ export interface PipelineResult {
27
+ /** 本次提取的 L1 原子事实数 */
28
+ l1Extracted: number;
29
+ /** 是否触发了 L2 聚合 */
30
+ l2Triggered: boolean;
31
+ /** 是否触发了 L3 画像更新 */
32
+ l3Triggered: boolean;
33
+ /** 新的场景块(如果触发了 L2) */
34
+ newScenes?: SceneBlock[];
35
+ /** 更新的画像(如果触发了 L3) */
36
+ newPersona?: string;
37
+ }
38
+
39
+ /**
40
+ * 记忆管道调度器
41
+ *
42
+ * 调度策略:
43
+ * 1. 每次对话结束:提取 L1 原子事实
44
+ * 2. L1 累积达到阈值:触发 L2 场景聚合
45
+ * 3. L2 累积达到阈值:触发 L3 画像生成
46
+ *
47
+ * 所有操作均为 fire-and-forget,不阻塞对话响应
48
+ */
49
+ export class MemoryPipeline {
50
+ private llmProvider: LLMProvider;
51
+ private l2Threshold: number;
52
+ private l3Threshold: number;
53
+
54
+ constructor(opts: PipelineOptions) {
55
+ this.llmProvider = opts.llmProvider;
56
+ this.l2Threshold = opts.l2Threshold ?? 5;
57
+ this.l3Threshold = opts.l3Threshold ?? 3;
58
+ }
59
+
60
+ /**
61
+ * 执行完整管道(L1 提取 → 条件触发 L2 → 条件触发 L3)
62
+ *
63
+ * @param messages 对话消息
64
+ * @param sessionId 会话 ID
65
+ * @param memoryService MemoryService 实例(延迟导入避免循环依赖)
66
+ */
67
+ async run(
68
+ messages: Message[],
69
+ sessionId: string,
70
+ memoryService: MemoryServiceLike,
71
+ ): Promise<PipelineResult> {
72
+ const result: PipelineResult = {
73
+ l1Extracted: 0,
74
+ l2Triggered: false,
75
+ l3Triggered: false,
76
+ };
77
+
78
+ // === Phase 1: L1 提取 ===
79
+ const existingKeys = memoryService.getKeys();
80
+ const extractor = new MemoryExtractor({ llmProvider: this.llmProvider });
81
+ const facts = await extractor.extract(messages, sessionId, existingKeys);
82
+
83
+ // 写入 L1 条目
84
+ for (const fact of facts) {
85
+ memoryService.save(fact.key, fact.content, fact.category, fact.importance, {
86
+ memoryLayer: 'L1',
87
+ sourceSessionId: sessionId,
88
+ sourceTimestamp: new Date().toISOString(),
89
+ });
90
+ }
91
+ result.l1Extracted = facts.length;
92
+
93
+ if (facts.length === 0) return result;
94
+
95
+ console.log(`[Pipeline] L1: extracted ${facts.length} facts from session ${sessionId}`);
96
+
97
+ // === Phase 2: 条件触发 L2 聚合 ===
98
+ const l1Entries = memoryService.getAllEntries('L1');
99
+ const l1Count = l1Entries.length;
100
+ const checkpoint = getMemoryCheckpoint();
101
+
102
+ if (checkpoint.shouldTriggerL2(l1Count, this.l2Threshold)) {
103
+ try {
104
+ const existingL2 = memoryService.getAllEntries('L2');
105
+ const existingScenes: SceneBlock[] = existingL2.map(e => ({
106
+ sceneId: e.sceneId || e.key,
107
+ title: e.key,
108
+ description: e.content,
109
+ atomIds: e.sourceIds || [],
110
+ }));
111
+
112
+ const aggregator = new SceneAggregator({ llmProvider: this.llmProvider });
113
+ const atoms = l1Entries.map(e => ({
114
+ id: e.id,
115
+ key: e.key,
116
+ content: e.content,
117
+ category: e.category,
118
+ }));
119
+
120
+ const scenes = await aggregator.aggregate(atoms, existingScenes);
121
+ result.l2Triggered = true;
122
+ result.newScenes = scenes;
123
+
124
+ // 写入 L2 条目(upsert by sceneId)
125
+ for (const scene of scenes) {
126
+ memoryService.save(scene.sceneId, `${scene.title}\n${scene.description}`, 'scene', 4, {
127
+ memoryLayer: 'L2',
128
+ sceneId: scene.sceneId,
129
+ sourceLayer: 'L1',
130
+ sourceIds: scene.atomIds,
131
+ });
132
+ }
133
+
134
+ // 回填 L1 条目的 sceneId
135
+ for (const scene of scenes) {
136
+ for (const atomId of scene.atomIds) {
137
+ const entry = l1Entries.find(e => e.id === atomId);
138
+ if (entry && entry.sceneId !== scene.sceneId) {
139
+ memoryService.save(entry.key, entry.content, entry.category, entry.importance, {
140
+ memoryLayer: 'L1',
141
+ sceneId: scene.sceneId,
142
+ });
143
+ }
144
+ }
145
+ }
146
+
147
+ console.log(`[Pipeline] L2: aggregated ${scenes.length} scenes from ${l1Count} atoms`);
148
+
149
+ // 更新检查点
150
+ checkpoint.updateL2(l1Count);
151
+ } catch (err: any) {
152
+ console.warn(`[Pipeline] L2 aggregation failed:`, err?.message);
153
+ }
154
+ }
155
+
156
+ // === Phase 3: 条件触发 L3 画像 ===
157
+ const l2Entries = memoryService.getAllEntries('L2');
158
+ if (checkpoint.shouldTriggerL3(l2Entries.length, this.l3Threshold)) {
159
+ try {
160
+ const existingL3 = memoryService.getAllEntries('L3');
161
+ const existingPersona = existingL3.length > 0 ? existingL3[0].content : '';
162
+
163
+ const generator = new PersonaGenerator({ llmProvider: this.llmProvider });
164
+ const scenes: SceneBlock[] = l2Entries.map(e => ({
165
+ sceneId: e.sceneId || e.key,
166
+ title: e.key,
167
+ description: e.content,
168
+ atomIds: e.sourceIds || [],
169
+ }));
170
+
171
+ const persona = await generator.generate(scenes, existingPersona);
172
+ result.l3Triggered = true;
173
+ result.newPersona = persona;
174
+
175
+ if (persona) {
176
+ memoryService.save('user_persona', persona, 'persona', 5, {
177
+ memoryLayer: 'L3',
178
+ sourceLayer: 'L2',
179
+ sourceIds: l2Entries.map(e => e.id),
180
+ });
181
+ console.log(`[Pipeline] L3: persona updated (${persona.length} chars)`);
182
+ checkpoint.updateL3(l2Entries.length);
183
+ }
184
+ } catch (err: any) {
185
+ console.warn(`[Pipeline] L3 generation failed:`, err?.message);
186
+ }
187
+ }
188
+
189
+ return result;
190
+ }
191
+ }
192
+
@@ -0,0 +1,73 @@
1
+ // ============================================================
2
+ // Memory Extraction Prompt Templates
3
+ // ============================================================
4
+
5
+ /** 原子事实提取 Prompt(系统提示) */
6
+ export const EXTRACTION_SYSTEM_PROMPT = `You are a memory extraction engine. Analyze the conversation and extract persistent, reusable facts.
7
+
8
+ **Rules:**
9
+ - Only extract OBJECTIVE, PERSISTENT facts that will be useful in future conversations
10
+ - Extract: user preferences, project knowledge, technical stack, learned patterns, important decisions
11
+ - DO NOT extract: one-off task details (e.g. "fix bug X"), temporary state, debugging logs
12
+ - Each fact must be atomic (one discrete piece of knowledge)
13
+ - Use concise, declarative language
14
+ - Prefer updating existing keys over creating duplicates
15
+
16
+ **Categories:**
17
+ - user_preference: User's working habits, tool preferences, coding style
18
+ - project_knowledge: Project architecture, tech stack, conventions
19
+ - learned_fact: New knowledge gained during the conversation
20
+
21
+ **Output format (strict JSON array):**
22
+ [
23
+ { "key": "snake_case_key", "content": "Brief factual statement", "category": "user_preference|project_knowledge|learned_fact", "importance": 1-5 }
24
+ ]
25
+
26
+ If no extractable facts exist, output an empty array: []
27
+ Do not add explanations or markdown outside the JSON.`;
28
+
29
+ /** 构建提取用户提示 */
30
+ export function buildExtractionUserPrompt(
31
+ messagesSummary: string,
32
+ existingKeys: string[],
33
+ ): string {
34
+ const keysSection = existingKeys.length > 0
35
+ ? `**Existing memory keys (avoid duplicates, update if needed):**\n${existingKeys.map(k => `- ${k}`).join('\n')}\n\n`
36
+ : '';
37
+
38
+ return `${keysSection}**Conversation to analyze:**
39
+ ${messagesSummary}
40
+
41
+ Extract persistent facts from the above conversation. Output ONLY a JSON array.`;
42
+ }
43
+
44
+ /** 场景聚合 Prompt(Phase 4 L2) */
45
+ export const SCENE_AGGREGATION_SYSTEM_PROMPT = `You are a memory organizer. Group related atomic facts into thematic scenario blocks.
46
+
47
+ **Rules:**
48
+ - Each scenario should have a clear title and description
49
+ - Group facts by topic/project/domain (e.g. "连接器功能开发", "项目架构决策")
50
+ - Max 15 scenarios total. Merge or archive old scenarios if needed.
51
+ - A fact can belong to only one scenario
52
+
53
+ **Output format (strict JSON array):**
54
+ [
55
+ {
56
+ "sceneId": "snake_case_id",
57
+ "title": "场景标题",
58
+ "description": "场景描述(1-2句)",
59
+ "atomIds": ["atom_id_1", "atom_id_2"]
60
+ }
61
+ ]`;
62
+
63
+ /** 画像生成 Prompt(Phase 4 L3) */
64
+ export const PERSONA_GENERATION_SYSTEM_PROMPT = `You are a user profiling engine. Based on the scenario blocks provided, generate or incrementally update a user persona.
65
+
66
+ **Rules:**
67
+ - Focus on stable, enduring traits: working style, technical preferences, communication patterns
68
+ - Use clear section headers (## 技术偏好, ## 工作风格, ## 项目背景, etc.)
69
+ - Keep the persona concise (max 800 words)
70
+ - When updating: preserve existing content, only modify sections affected by new scenarios
71
+
72
+ **Output format: Markdown**
73
+ Write the persona directly as markdown text.`;