feat: implement ZCLAW Agent Intelligence Evolution Phase 1-3
Phase 1: Persistent Memory + Identity Dynamic Evolution - agent-memory.ts: MemoryManager with localStorage persistence, keyword search, deduplication, importance scoring, pruning, markdown export - agent-identity.ts: AgentIdentityManager with per-agent SOUL/AGENTS/USER.md, change proposals with approval workflow, snapshot rollback - memory-extractor.ts: Rule-based conversation memory extraction (Phase 1), LLM extraction prompt ready for Phase 2 - MemoryPanel.tsx: Memory browsing UI with search, type filter, delete, export (integrated as 4th tab in RightPanel) Phase 2: Context Governance - context-compactor.ts: Token estimation, threshold monitoring (soft/hard), memory flush before compaction, rule-based summarization - chatStore integration: auto-compact when approaching token limits Phase 3: Proactive Intelligence + Self-Reflection - heartbeat-engine.ts: Periodic checks (pending tasks, memory health, idle greeting), quiet hours, proactivity levels (silent/light/standard/autonomous) - reflection-engine.ts: Pattern analysis from memory corpus, improvement suggestions, identity change proposals, meta-memory creation Chat Flow Integration (chatStore.ts): - Pre-send: context compaction check -> memory search -> identity system prompt injection - Post-complete: async memory extraction -> reflection conversation tracking -> auto-trigger reflection Tests: 274 passing across 12 test files - agent-memory.test.ts: 42 tests - context-compactor.test.ts: 23 tests - heartbeat-reflection.test.ts: 28 tests - chatStore.test.ts: 11 tests (no regressions) Refs: ZCLAW_AGENT_INTELLIGENCE_EVOLUTION.md updated with implementation progress
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { getGatewayClient, AgentStreamDelta } from '../lib/gateway-client';
|
||||
import { getMemoryManager } from '../lib/agent-memory';
|
||||
import { getAgentIdentityManager } from '../lib/agent-identity';
|
||||
import { getMemoryExtractor } from '../lib/memory-extractor';
|
||||
import { getContextCompactor } from '../lib/context-compactor';
|
||||
import { getReflectionEngine } from '../lib/reflection-engine';
|
||||
|
||||
export interface MessageFile {
|
||||
name: string;
|
||||
@@ -269,8 +274,54 @@ export const useChatStore = create<ChatState>()(
|
||||
const { addMessage, currentAgent, sessionKey } = get();
|
||||
const effectiveSessionKey = sessionKey || `session_${Date.now()}`;
|
||||
const effectiveAgentId = resolveGatewayAgentId(currentAgent);
|
||||
const agentId = currentAgent?.id || 'zclaw-main';
|
||||
|
||||
// Add user message
|
||||
// Check context compaction threshold before adding new message
|
||||
try {
|
||||
const compactor = getContextCompactor();
|
||||
const check = compactor.checkThreshold(get().messages.map(m => ({ role: m.role, content: m.content })));
|
||||
if (check.shouldCompact) {
|
||||
console.log(`[Chat] Context compaction triggered (${check.urgency}): ${check.currentTokens} tokens`);
|
||||
const result = await compactor.compact(
|
||||
get().messages.map(m => ({ role: m.role, content: m.content, id: m.id, timestamp: m.timestamp })),
|
||||
agentId,
|
||||
get().currentConversationId ?? undefined
|
||||
);
|
||||
// Replace messages with compacted version
|
||||
const compactedMsgs: Message[] = result.compactedMessages.map((m, i) => ({
|
||||
id: m.id || `compacted_${i}_${Date.now()}`,
|
||||
role: m.role as Message['role'],
|
||||
content: m.content,
|
||||
timestamp: m.timestamp || new Date(),
|
||||
}));
|
||||
set({ messages: compactedMsgs });
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[Chat] Context compaction check failed:', err);
|
||||
}
|
||||
|
||||
// Build memory-enhanced content
|
||||
let enhancedContent = content;
|
||||
try {
|
||||
const memoryMgr = getMemoryManager();
|
||||
const identityMgr = getAgentIdentityManager();
|
||||
const relevantMemories = await memoryMgr.search(content, {
|
||||
agentId,
|
||||
limit: 8,
|
||||
minImportance: 3,
|
||||
});
|
||||
const memoryContext = relevantMemories.length > 0
|
||||
? `\n\n## 相关记忆\n${relevantMemories.map(m => `- [${m.type}] ${m.content}`).join('\n')}`
|
||||
: '';
|
||||
const systemPrompt = identityMgr.buildSystemPrompt(agentId, memoryContext);
|
||||
if (systemPrompt) {
|
||||
enhancedContent = `<context>\n${systemPrompt}\n</context>\n\n${content}`;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[Chat] Memory enhancement failed, proceeding without:', err);
|
||||
}
|
||||
|
||||
// Add user message (original content for display)
|
||||
const userMsg: Message = {
|
||||
id: `user_${Date.now()}`,
|
||||
role: 'user',
|
||||
@@ -297,7 +348,7 @@ export const useChatStore = create<ChatState>()(
|
||||
// Try streaming first (OpenFang WebSocket)
|
||||
if (client.getState() === 'connected') {
|
||||
const { runId } = await client.chatStream(
|
||||
content,
|
||||
enhancedContent,
|
||||
{
|
||||
onDelta: (delta: string) => {
|
||||
set((state) => ({
|
||||
@@ -343,6 +394,21 @@ export const useChatStore = create<ChatState>()(
|
||||
m.id === assistantId ? { ...m, streaming: false } : m
|
||||
),
|
||||
}));
|
||||
// Async memory extraction after stream completes
|
||||
const msgs = get().messages
|
||||
.filter(m => m.role === 'user' || m.role === 'assistant')
|
||||
.map(m => ({ role: m.role, content: m.content }));
|
||||
getMemoryExtractor().extractFromConversation(msgs, agentId, get().currentConversationId ?? undefined).catch(err =>
|
||||
console.warn('[Chat] Memory extraction failed:', err)
|
||||
);
|
||||
// Track conversation for reflection trigger
|
||||
const reflectionEngine = getReflectionEngine();
|
||||
reflectionEngine.recordConversation();
|
||||
if (reflectionEngine.shouldReflect()) {
|
||||
reflectionEngine.reflect(agentId).catch(err =>
|
||||
console.warn('[Chat] Reflection failed:', err)
|
||||
);
|
||||
}
|
||||
},
|
||||
onError: (error: string) => {
|
||||
set((state) => ({
|
||||
@@ -375,7 +441,7 @@ export const useChatStore = create<ChatState>()(
|
||||
}
|
||||
|
||||
// Fallback to REST API (non-streaming)
|
||||
const result = await client.chat(content, {
|
||||
const result = await client.chat(enhancedContent, {
|
||||
sessionKey: effectiveSessionKey,
|
||||
agentId: effectiveAgentId,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user