feat: Aggiunge miglioramenti per memoria e pianificazione con integrazione MCP
- Aggiunto sistema SimplePersistentMemory per fatti, preferenze e cronologia conversazioni - Aggiunto EnhancedPlanner con dipendenze task, priorità e tracciamento progresso - Integrati strumenti di miglioramento nell'agente (remember, recall, preference, planning) - Aggiunti comandi CLI /memory e /plan per accesso diretto da terminale - Aggiunto client MCP per connessione a server Model Context Protocol - Aggiornato README con documentazione completa per tutte le nuove funzionalità - Corretto parsing comando creazione piano per gestire nomi multi-parola Nuovi comandi: - /memory remember <testo> - Ricorda un fatto - /memory recall <query> - Cerca nella memoria - /memory preference set/get - Gestisci preferenze - /memory stats - Statistiche memoria - /plan create <nome> <task1;task2;...> - Crea un piano - /plan list - Elenca tutti i piani - /plan progress <plan_id> - Mostra progresso piano - /plan start <task_id> - Inizia un task - /plan complete <task_id> - Completa un task
This commit is contained in:
1 parent
5a8fcda993
commit
56f2819a92
9 files changed
+1889
-1
No files matched your search
@@ -6,6 +6,7 @@ Enhanced with unified provider tools and custom tools
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from typing import Optional, List, Dict, Any
|
||||
from deepagents import create_deep_agent
|
||||
from langchain.chat_models import init_chat_model
|
||||
@@ -37,6 +38,18 @@ except ImportError:
|
||||
SKILL_MANAGER_AVAILABLE = False
|
||||
print("Warning: Skill manager not available.")
|
||||
|
||||
# Import enhancement modules
|
||||
try:
|
||||
from enhancements.simple_memory import simple_memory, create_simple_memory_tools
|
||||
from enhancements.planning_enhancement import enhanced_planner
|
||||
ENHANCEMENTS_AVAILABLE = True
|
||||
except ImportError:
|
||||
ENHANCEMENTS_AVAILABLE = False
|
||||
print("Warning: Enhancement modules not available.")
|
||||
|
||||
# Import json for memory preference handling
|
||||
import json
|
||||
|
||||
|
||||
def load_model_from_config():
|
||||
"""
|
||||
@@ -298,6 +311,23 @@ def get_custom_tools() -> List[Tool]:
|
||||
except Exception as e:
|
||||
print(f"⚠️ Could not load skill tools: {e}")
|
||||
|
||||
# Add enhancement tools
|
||||
try:
|
||||
if ENHANCEMENTS_AVAILABLE:
|
||||
# Add simple memory tools
|
||||
memory_tools = create_simple_memory_tools(simple_memory)
|
||||
custom_tools.extend(memory_tools)
|
||||
print(f"✅ Added {len(memory_tools)} simple memory tools")
|
||||
|
||||
# Add planning tools
|
||||
planning_tools = enhanced_planner.create_planning_tools()
|
||||
custom_tools.extend(planning_tools)
|
||||
print(f"✅ Added {len(planning_tools)} planning tools")
|
||||
else:
|
||||
print("⚠️ Enhancement modules not available, skipping enhancement tools")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Could not load enhancement tools: {e}")
|
||||
|
||||
return custom_tools
|
||||
|
||||
|
||||
@@ -315,7 +345,8 @@ def create_langcode_agent(system_prompt: Optional[str] = None):
|
||||
2. Custom tools for managing Proxmox virtual machines
|
||||
3. MCP tools for accessing external services and skills registries (like skills.sh)
|
||||
4. Skill management tools for discovering, installing, and managing agent skills
|
||||
5. Standard DeepAgents tools (planning, filesystem, shell, subagents)
|
||||
5. Enhancement tools for persistent memory and advanced planning
|
||||
6. Standard DeepAgents tools (planning, filesystem, shell, subagents)
|
||||
|
||||
Use these tools when asked about:
|
||||
- LLM provider management (listing models, testing connections, setting defaults)
|
||||
@@ -614,6 +645,173 @@ def skills_details(skill_name: str):
|
||||
print(f"Error getting skill details: {str(e)}")
|
||||
|
||||
|
||||
def memory_remember(text: str):
|
||||
"""Remember a fact for future reference"""
|
||||
from enhancements.simple_memory import simple_memory
|
||||
try:
|
||||
# Default category is "general"
|
||||
simple_memory.add_fact(text, "general")
|
||||
print(f"✅ Remembered: {text[:100]}{'...' if len(text) > 100 else ''}")
|
||||
except Exception as e:
|
||||
print(f"❌ Error remembering: {e}")
|
||||
|
||||
def memory_recall(query: str):
|
||||
"""Recall facts by searching memory"""
|
||||
from enhancements.simple_memory import simple_memory
|
||||
try:
|
||||
results = simple_memory.search_facts(query)
|
||||
if not results:
|
||||
print(f"No memories found for: {query}")
|
||||
return
|
||||
|
||||
print(f"\nFound {len(results)} memories:")
|
||||
print("=" * 50)
|
||||
for i, fact in enumerate(results[:10], 1): # Limit to 10 results
|
||||
print(f"{i}. {fact['fact']}")
|
||||
print(f" Category: {fact['category']}")
|
||||
print(f" Time: {fact['timestamp']}")
|
||||
print()
|
||||
except Exception as e:
|
||||
print(f"Error recalling: {e}")
|
||||
|
||||
def memory_set_preference(key: str, value: str):
|
||||
"""Set a user preference"""
|
||||
from enhancements.simple_memory import simple_memory
|
||||
try:
|
||||
# Try to parse value as JSON for complex types
|
||||
try:
|
||||
parsed_val = json.loads(value)
|
||||
except:
|
||||
parsed_val = value
|
||||
simple_memory.set_preference(key, parsed_val)
|
||||
print(f"✅ Set preference '{key}' = {parsed_val}")
|
||||
except Exception as e:
|
||||
print(f"Error setting preference: {e}")
|
||||
|
||||
def memory_preference_get(key: str):
|
||||
"""Get a user preference"""
|
||||
from enhancements.simple_memory import simple_memory
|
||||
try:
|
||||
val = simple_memory.get_preference(key)
|
||||
if val is not None:
|
||||
print(f"Preference '{key}': {val}")
|
||||
else:
|
||||
print(f"Preference '{key}' not found")
|
||||
except Exception as e:
|
||||
print(f"Error getting preference: {e}")
|
||||
|
||||
def memory_stats():
|
||||
"""Get memory statistics"""
|
||||
from enhancements.simple_memory import simple_memory
|
||||
try:
|
||||
stats = simple_memory.get_memory_stats()
|
||||
print("\nMemory Statistics:")
|
||||
print("=" * 30)
|
||||
print(f"Total Conversations: {stats['total_conversations']}")
|
||||
print(f"Total Facts: {stats['total_facts']}")
|
||||
print(f"Total Preferences: {stats['total_preferences']}")
|
||||
print(f"Total Skills Used: {stats['total_skills_used']}")
|
||||
print(f"Last Updated: {stats['last_updated'] or 'Never'}")
|
||||
except Exception as e:
|
||||
print(f"Error getting memory stats: {e}")
|
||||
|
||||
def _strip_quotes(s: str) -> str:
|
||||
"""Strip surrounding quotes from a string"""
|
||||
s = s.strip()
|
||||
if len(s) >= 2:
|
||||
if (s.startswith('"') and s.endswith('"')) or (s.startswith("'") and s.endswith("'")):
|
||||
return s[1:-1]
|
||||
return s
|
||||
|
||||
def plan_create(name: str, tasks_string: str):
|
||||
"""Create a plan with multiple tasks"""
|
||||
from enhancements.planning_enhancement import enhanced_planner
|
||||
try:
|
||||
# Strip quotes from name
|
||||
name = _strip_quotes(name)
|
||||
# Split tasks by semicolon and clean up
|
||||
task_descriptions = [_strip_quotes(t.strip()) for t in tasks_string.split(';') if t.strip()]
|
||||
if not task_descriptions:
|
||||
print("❌ No tasks provided")
|
||||
return
|
||||
|
||||
plan_id = enhanced_planner.create_plan(name, task_descriptions)
|
||||
print(f"✅ Created plan '{name}' with ID: {plan_id}")
|
||||
print(f" Tasks: {len(task_descriptions)}")
|
||||
for i, desc in enumerate(task_descriptions, 1):
|
||||
print(f" {i}. {desc}")
|
||||
except Exception as e:
|
||||
print(f"❌ Error creating plan: {e}")
|
||||
|
||||
def plan_list():
|
||||
"""List all plans"""
|
||||
from enhancements.planning_enhancement import enhanced_planner
|
||||
try:
|
||||
if not enhanced_planner.plans:
|
||||
print("No plans created yet")
|
||||
return
|
||||
|
||||
print(f"\nPlans ({len(enhanced_planner.plans)}):")
|
||||
print("=" * 50)
|
||||
for plan_id, task_ids in enhanced_planner.plans.items():
|
||||
task_count = len(task_ids)
|
||||
print(f"Plan ID: {plan_id}")
|
||||
print(f" Tasks: {task_count}")
|
||||
# Show first few tasks
|
||||
for i, task_id in enumerate(task_ids[:3], 1):
|
||||
if task_id in enhanced_planner.tasks:
|
||||
task = enhanced_planner.tasks[task_id]
|
||||
print(f" {i}. [{task.status.value}] {task.description}")
|
||||
else:
|
||||
print(f" {i}. [unknown] Task {task_id} not found")
|
||||
if len(task_ids) > 3:
|
||||
print(f" ... and {len(task_ids) - 3} more tasks")
|
||||
print()
|
||||
except Exception as e:
|
||||
print(f"Error listing plans: {e}")
|
||||
|
||||
def plan_progress(plan_id: str):
|
||||
"""Get progress information for a plan"""
|
||||
from enhancements.planning_enhancement import enhanced_planner
|
||||
try:
|
||||
progress = enhanced_planner.get_plan_progress(plan_id)
|
||||
if "error" in progress:
|
||||
print(f"❌ {progress['error']}")
|
||||
return
|
||||
|
||||
print(f"\nPlan {plan_id} Progress:")
|
||||
print("=" * 40)
|
||||
print(f"Total Tasks: {progress['total_tasks']}")
|
||||
print(f"Completed: {progress['completed_tasks']}")
|
||||
print(f"Failed: {progress['failed_tasks']}")
|
||||
print(f"In Progress: {progress['in_progress_tasks']}")
|
||||
print(f"Pending: {progress['pending_tasks']}")
|
||||
print(f"Progress: {progress['progress_percentage']:.1f}%")
|
||||
except Exception as e:
|
||||
print(f"Error getting plan progress: {e}")
|
||||
|
||||
def plan_start(task_id: str):
|
||||
"""Start working on a task"""
|
||||
from enhancements.planning_enhancement import enhanced_planner
|
||||
try:
|
||||
if enhanced_planner.start_task(task_id):
|
||||
print(f"✅ Started task {task_id}")
|
||||
else:
|
||||
print(f"❌ Failed to start task {task_id} - check dependencies or task ID")
|
||||
except Exception as e:
|
||||
print(f"Error starting task: {e}")
|
||||
|
||||
def plan_complete(task_id: str):
|
||||
"""Mark a task as completed"""
|
||||
from enhancements.planning_enhancement import enhanced_planner
|
||||
try:
|
||||
if enhanced_planner.complete_task(task_id):
|
||||
print(f"✅ Completed task {task_id}")
|
||||
else:
|
||||
print(f"❌ Failed to complete task {task_id} - check task ID or status")
|
||||
except Exception as e:
|
||||
print(f"Error completing task: {e}")
|
||||
|
||||
def process_command(user_input: str) -> bool:
|
||||
"""
|
||||
Process a command starting with "/"
|
||||
@@ -665,6 +863,65 @@ def process_command(user_input: str) -> bool:
|
||||
else:
|
||||
print("❌ Sottocomando sconosciuto o argomenti mancanti")
|
||||
print("Sottocomandi disponibili: search <query>, install <name>, uninstall <name>, list, details <name>")
|
||||
elif command == 'memory':
|
||||
if not args:
|
||||
print("❌ Specifica un sottocomando: remember, recall, preference, stats")
|
||||
elif args[0] == 'remember' and len(args) > 1:
|
||||
memory_remember(' '.join(args[1:]))
|
||||
elif args[0] == 'recall' and len(args) > 1:
|
||||
memory_recall(' '.join(args[1:]))
|
||||
elif args[0] == 'preference':
|
||||
if len(args) >= 4 and args[1] == 'set':
|
||||
# Join everything after 'set' as the value (key is args[2], value is args[3:])
|
||||
key = args[2]
|
||||
value = ' '.join(args[3:])
|
||||
memory_set_preference(key, value)
|
||||
elif len(args) == 3 and args[1] == 'get':
|
||||
key = args[2]
|
||||
memory_preference_get(key)
|
||||
else:
|
||||
print("❌ Usage: preference set <key> <value> or preference get <key>")
|
||||
elif args[0] == 'stats':
|
||||
memory_stats()
|
||||
else:
|
||||
print("❌ Sottocomando sconosciuto o argomenti mancanti")
|
||||
print("Sottocomandi disponibili: remember <text>, recall <query>, preference set <key> <value>, preference get <key>, stats")
|
||||
elif command == 'plan':
|
||||
if not args:
|
||||
print("❌ Specifica un sottocomando: create, list, progress, start, complete")
|
||||
elif args[0] == 'create':
|
||||
# Handle plan creation with proper parsing
|
||||
# Format: /plan create "Plan Name" "Task 1; Task 2; Task 3"
|
||||
# Or: /plan create PlanName Task1;Task2;Task3
|
||||
if len(args) < 3:
|
||||
print("❌ Usage: /plan create <name> <task1;task2;...>")
|
||||
else:
|
||||
# Find the first semicolon to split name from tasks
|
||||
remaining = ' '.join(args[1:])
|
||||
|
||||
# Check if there's a semicolon to determine name/tasks split
|
||||
if ';' in remaining:
|
||||
# Find the first semicolon
|
||||
semi_idx = remaining.index(';')
|
||||
name = remaining[:semi_idx].strip()
|
||||
tasks = remaining[semi_idx+1:].strip()
|
||||
plan_create(name, tasks)
|
||||
else:
|
||||
# No semicolon found - use second arg as first task name, rest as description
|
||||
name = args[1]
|
||||
tasks = ' '.join(args[2:])
|
||||
plan_create(name, tasks)
|
||||
elif args[0] == 'list':
|
||||
plan_list()
|
||||
elif args[0] == 'progress' and len(args) > 1:
|
||||
plan_progress(args[1])
|
||||
elif args[0] == 'start' and len(args) > 1:
|
||||
plan_start(args[1])
|
||||
elif args[0] == 'complete' and len(args) > 1:
|
||||
plan_complete(args[1])
|
||||
else:
|
||||
print("❌ Sottocomando sconosciuto o argomenti mancanti")
|
||||
print("Sottocomandi disponibili: create <name> <task1> <task2> ..., list, progress <plan_id>, start <task_id>, complete <task_id>")
|
||||
elif command == 'models':
|
||||
list_models()
|
||||
elif command == 'clear':
|
||||
|
||||
Reference in new issue
Block a user