too much
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import logging
|
||||
import random
|
||||
from datetime import datetime
|
||||
from http.client import responses
|
||||
from pprint import pprint
|
||||
from tqdm.asyncio import tqdm
|
||||
from typing import List, Self
|
||||
from living_agents import MemoryStream, LLMAgent, Character, PromptManager, Memory
|
||||
from living_agents.datatypes import CharacterTemplate
|
||||
from living_agents.datatypes import CharacterTemplate, CharacterTrait
|
||||
from llm_connector import LLMMessage
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -37,9 +39,10 @@ class CharacterAgent:
|
||||
async def perceive(self, observation: str, skip_scoring=False) -> None:
|
||||
"""Add new observation to memory stream"""
|
||||
if skip_scoring:
|
||||
await self.memory_stream.add_observation(observation)
|
||||
new_memory = await self.memory_stream.add_observation(observation)
|
||||
else:
|
||||
await self.memory_stream.add_observation(observation, self._score_memory_importance)
|
||||
new_memory = await self.memory_stream.add_observation(observation, self._score_memory_importance)
|
||||
await self._analyze_trait_impact(new_memory)
|
||||
|
||||
async def react_to_situation(self, situation: str) -> str:
|
||||
"""Generate reaction based on memory and character"""
|
||||
@@ -118,37 +121,26 @@ Summary:"""
|
||||
except:
|
||||
return f"{self.character.name} is a {self.character.age}-year-old {self.character.occupation}."
|
||||
|
||||
async def _get_related_memories_for_scoring(self, memory_text: str, exclude_self=None, k=5) -> List:
|
||||
"""Get memories related to the one being scored"""
|
||||
# Get embedding for the memory being scored
|
||||
memory_embedding = await self.llm.get_embedding(memory_text)
|
||||
|
||||
# Calculate similarity to other memories
|
||||
similarities = []
|
||||
for mem in self.memory_stream.memories:
|
||||
if mem == exclude_self:
|
||||
continue
|
||||
|
||||
if mem.embedding:
|
||||
from sklearn.metrics.pairwise import cosine_similarity
|
||||
similarity = cosine_similarity([memory_embedding], [mem.embedding])[0][0]
|
||||
similarities.append((similarity, mem))
|
||||
|
||||
# Return top K most similar memories
|
||||
similarities.sort(reverse=True, key=lambda x: x[0])
|
||||
return [mem for _, mem in similarities[:k]]
|
||||
|
||||
async def _score_memory_importance(self, memory: Memory) -> int:
|
||||
"""Score importance with related memories as context"""
|
||||
|
||||
related_memories = await self._get_related_memories_for_scoring(memory.description, exclude_self=memory, k=5)
|
||||
related_memories = await self.memory_stream.get_related_memories_for_scoring(memory.description, exclude_self=memory, k=5)
|
||||
|
||||
prompt_context = {'character': self._get_character_prompt(),
|
||||
prompt_context = {'character_context': self._get_character_prompt(),
|
||||
'character_name': self.character.name,
|
||||
'related_memories': "\n".join([m.description for m in related_memories]),
|
||||
'memory_text': memory.description,
|
||||
'memory_type': memory.memory_type}
|
||||
'memory_text': memory.description}
|
||||
if memory.memory_type == 'observation':
|
||||
prompt = PromptManager.get_prompt('score_observation_importance', prompt_context)
|
||||
elif memory.memory_type == 'reflection':
|
||||
prompt = PromptManager.get_prompt('score_reflection_importance', prompt_context)
|
||||
elif memory.memory_type == 'plan':
|
||||
prompt = PromptManager.get_prompt('score_plan_importance', prompt_context)
|
||||
|
||||
prompt = PromptManager.get_prompt('score_importance_with_context', prompt_context)
|
||||
# if reflection or plan, add related memories.
|
||||
if memory.memory_type == 'reflection' or memory.memory_type == 'plan':
|
||||
for rel_memory in related_memories:
|
||||
memory.related_memories.append(rel_memory)
|
||||
|
||||
try:
|
||||
response = await self.llm.chat([{"role": "user", "content": prompt}], max_tokens=5)
|
||||
@@ -157,7 +149,38 @@ Summary:"""
|
||||
except:
|
||||
return 5 # Default
|
||||
|
||||
async def _extract_character_from_memories(self) -> Character:
|
||||
async def _analyze_trait_impact(self, memory: Memory):
|
||||
|
||||
traits_summary = "\n".join([f" - {trait.strength}/10 {trait.name} ({trait.description})" for trait in self.character.traits]) if self.character.traits else "No traits yet."
|
||||
prompt_context = {'character_name': self.character.name,
|
||||
'current_traits': traits_summary,
|
||||
'new_observation': memory.description}
|
||||
prompt, schema = PromptManager.get_prompt_with_schema('assess_trait_impact', prompt_context)
|
||||
messages: List[LLMMessage] = [{'role': 'user', 'content': prompt}]
|
||||
response = await self.llm.client.get_structured_response(messages, schema)
|
||||
for trait_update in response['trait_updates']:
|
||||
trait_to_update = self.character.get_trait(trait_update['trait_name'], trait_update['description'])
|
||||
if trait_update['action'] == 'create' or trait_update['action'] == 'strengthen':
|
||||
await self._strengthen_trait(trait_to_update)
|
||||
else:
|
||||
await self._weaken_trait(trait_to_update)
|
||||
|
||||
@staticmethod
|
||||
async def _strengthen_trait(trait: CharacterTrait, steepness: float = 1.0):
|
||||
if trait.strength >= 10:
|
||||
return
|
||||
if random.random() < trait.change_by_probability(steepness):
|
||||
trait.strength += 1
|
||||
trait.updated = datetime.now()
|
||||
|
||||
async def _weaken_trait(self, trait: CharacterTrait, steepness: float = 1.0):
|
||||
if random.random() < trait.change_by_probability(steepness):
|
||||
trait.strength -= 1
|
||||
trait.updated = datetime.now()
|
||||
if trait.strength <= 0:
|
||||
self.character.traits.remove(trait)
|
||||
|
||||
async def _generate_character_from_memories(self) -> Character:
|
||||
"""Extract Character info from memories using JSON"""
|
||||
|
||||
# Get different types of memories with targeted queries
|
||||
@@ -221,14 +244,16 @@ Summary:"""
|
||||
# create the character before we score to include the character in the prompts
|
||||
# Extract character info from memories to populate Character object
|
||||
logger.info(f"Creating Character...")
|
||||
instance.character = await instance._extract_character_from_memories()
|
||||
instance.character = await instance._generate_character_from_memories()
|
||||
|
||||
logger.info(f"Added {len(instance.memory_stream.memories)} memories, now scoring importance...")
|
||||
|
||||
# Score all memories with full context
|
||||
for memory in tqdm(instance.memory_stream.memories, desc="Scoring memory importance", unit="memory"):
|
||||
# Score all observations with importance
|
||||
observations = [memory for memory in instance.memory_stream.memories if memory.memory_type == 'observation']
|
||||
for memory in tqdm(observations, desc="Scoring memory importance", unit="memory"):
|
||||
# Score with related context
|
||||
memory.importance_score = await instance._score_memory_importance(memory)
|
||||
await instance._analyze_trait_impact(memory)
|
||||
|
||||
logger.info(f"Character {instance.character.name} created successfully")
|
||||
return instance
|
||||
|
||||
Reference in New Issue
Block a user