feat(l4): upgrade engines with LLM-powered capabilities (Phase 2)
Phase 2 LLM Engine Upgrades: - ReflectionEngine: Add LLM semantic analysis for pattern detection - ContextCompactor: Add LLM summarization for high-quality compaction - MemoryExtractor: Add LLM importance scoring for memory extraction - Add unified LLM service adapter (OpenAI, Volcengine, Gateway, Mock) - Add MemorySource 'llm-reflection' for LLM-generated memories - Add 13 integration tests for LLM-powered features Config options added: - useLLM: Enable LLM mode for each engine - llmProvider: Preferred LLM provider - llmFallbackToRules: Fallback to rules if LLM fails Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,11 +9,20 @@
|
||||
*
|
||||
* Also handles auto-updating USER.md with discovered preferences.
|
||||
*
|
||||
* Phase 1: Rule-based extraction (pattern matching).
|
||||
* Phase 4: LLM-powered semantic extraction with importance scoring.
|
||||
*
|
||||
* Reference: ZCLAW_AGENT_INTELLIGENCE_EVOLUTION.md §6.2.2
|
||||
*/
|
||||
|
||||
import { getMemoryManager, type MemoryType } from './agent-memory';
|
||||
import { getAgentIdentityManager } from './agent-identity';
|
||||
import {
|
||||
getLLMAdapter,
|
||||
llmExtract,
|
||||
type LLMServiceAdapter,
|
||||
type LLMProvider,
|
||||
} from './llm-service';
|
||||
|
||||
// === Types ===
|
||||
|
||||
@@ -36,6 +45,15 @@ export interface ConversationMessage {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface ExtractionConfig {
|
||||
useLLM: boolean; // Use LLM for semantic extraction (Phase 4)
|
||||
llmProvider?: LLMProvider; // Preferred LLM provider
|
||||
llmFallbackToRules: boolean; // Fall back to rules if LLM fails
|
||||
minMessagesForExtraction: number; // Minimum messages before extraction
|
||||
extractionCooldownMs: number; // Cooldown between extractions
|
||||
minImportanceThreshold: number; // Only save items with importance >= this
|
||||
}
|
||||
|
||||
// === Extraction Prompt ===
|
||||
|
||||
const EXTRACTION_PROMPT = `请从以下对话中提取值得长期记住的信息。
|
||||
@@ -59,38 +77,80 @@ const EXTRACTION_PROMPT = `请从以下对话中提取值得长期记住的信
|
||||
对话内容:
|
||||
`;
|
||||
|
||||
// === Default Config ===
|
||||
|
||||
export const DEFAULT_EXTRACTION_CONFIG: ExtractionConfig = {
|
||||
useLLM: false,
|
||||
llmFallbackToRules: true,
|
||||
minMessagesForExtraction: 4,
|
||||
extractionCooldownMs: 30_000,
|
||||
minImportanceThreshold: 3,
|
||||
};
|
||||
|
||||
// === Memory Extractor ===
|
||||
|
||||
export class MemoryExtractor {
|
||||
private minMessagesForExtraction = 4;
|
||||
private extractionCooldownMs = 30_000; // 30 seconds between extractions
|
||||
private config: ExtractionConfig;
|
||||
private lastExtractionTime = 0;
|
||||
private llmAdapter: LLMServiceAdapter | null = null;
|
||||
|
||||
constructor(config?: Partial<ExtractionConfig>) {
|
||||
this.config = { ...DEFAULT_EXTRACTION_CONFIG, ...config };
|
||||
|
||||
// Initialize LLM adapter if configured
|
||||
if (this.config.useLLM) {
|
||||
try {
|
||||
this.llmAdapter = getLLMAdapter();
|
||||
} catch (error) {
|
||||
console.warn('[MemoryExtractor] Failed to initialize LLM adapter:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract memories from a conversation using rule-based heuristics.
|
||||
* This is the Phase 1 approach — no LLM call needed.
|
||||
* Phase 2 will add LLM-based extraction using EXTRACTION_PROMPT.
|
||||
* Extract memories from a conversation.
|
||||
* Uses LLM if configured, falls back to rule-based extraction.
|
||||
*/
|
||||
async extractFromConversation(
|
||||
messages: ConversationMessage[],
|
||||
agentId: string,
|
||||
conversationId?: string
|
||||
conversationId?: string,
|
||||
options?: { forceLLM?: boolean }
|
||||
): Promise<ExtractionResult> {
|
||||
// Cooldown check
|
||||
if (Date.now() - this.lastExtractionTime < this.extractionCooldownMs) {
|
||||
if (Date.now() - this.lastExtractionTime < this.config.extractionCooldownMs) {
|
||||
return { items: [], saved: 0, skipped: 0, userProfileUpdated: false };
|
||||
}
|
||||
|
||||
// Minimum message threshold
|
||||
const chatMessages = messages.filter(m => m.role === 'user' || m.role === 'assistant');
|
||||
if (chatMessages.length < this.minMessagesForExtraction) {
|
||||
if (chatMessages.length < this.config.minMessagesForExtraction) {
|
||||
return { items: [], saved: 0, skipped: 0, userProfileUpdated: false };
|
||||
}
|
||||
|
||||
this.lastExtractionTime = Date.now();
|
||||
|
||||
// Phase 1: Rule-based extraction (pattern matching)
|
||||
const extracted = this.ruleBasedExtraction(chatMessages);
|
||||
// Try LLM extraction if enabled
|
||||
let extracted: ExtractedItem[];
|
||||
if ((this.config.useLLM || options?.forceLLM) && this.llmAdapter?.isAvailable()) {
|
||||
try {
|
||||
console.log('[MemoryExtractor] Using LLM-powered semantic extraction');
|
||||
extracted = await this.llmBasedExtraction(chatMessages);
|
||||
} catch (error) {
|
||||
console.error('[MemoryExtractor] LLM extraction failed:', error);
|
||||
if (!this.config.llmFallbackToRules) {
|
||||
throw error;
|
||||
}
|
||||
console.log('[MemoryExtractor] Falling back to rule-based extraction');
|
||||
extracted = this.ruleBasedExtraction(chatMessages);
|
||||
}
|
||||
} else {
|
||||
// Rule-based extraction
|
||||
extracted = this.ruleBasedExtraction(chatMessages);
|
||||
}
|
||||
|
||||
// Filter by importance threshold
|
||||
extracted = extracted.filter(item => item.importance >= this.config.minImportanceThreshold);
|
||||
|
||||
// Save to memory
|
||||
const memoryManager = getMemoryManager();
|
||||
@@ -135,6 +195,23 @@ export class MemoryExtractor {
|
||||
return { items: extracted, saved, skipped, userProfileUpdated };
|
||||
}
|
||||
|
||||
/**
|
||||
* LLM-powered semantic extraction.
|
||||
* Uses LLM to understand context and score importance semantically.
|
||||
*/
|
||||
private async llmBasedExtraction(messages: ConversationMessage[]): Promise<ExtractedItem[]> {
|
||||
const conversationText = messages
|
||||
.filter(m => m.role === 'user' || m.role === 'assistant')
|
||||
.map(m => `[${m.role === 'user' ? '用户' : '助手'}]: ${m.content}`)
|
||||
.join('\n\n');
|
||||
|
||||
// Use llmExtract helper from llm-service
|
||||
const llmResponse = await llmExtract(conversationText, this.llmAdapter!);
|
||||
|
||||
// Parse the JSON response
|
||||
return this.parseExtractionResponse(llmResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Phase 1: Rule-based extraction using pattern matching.
|
||||
* Extracts common patterns from user messages.
|
||||
|
||||
Reference in New Issue
Block a user