Fix agent kn reset (#2765)

* CLI command added

* Added reset agent knowledge function

* Reduced verbose

* Added test cases

* Added docs

* Llama test case failing

* Changed _reset_agent_knowledge function

* Fixed new line error

* Added docs

* fixed the new line error

* Refractored

* Uncommmented some test cases

* ruff check fixed

* fixed run type checks

* fixed run type checks

* fixed run type checks

* Made reset_fn callable by casting to silence run type checks

* Changed the reset_knowledge as it expects only list of knowledge

* Fixed typo in docs

* Refractored the memory_system

* Minor Changes

* fixed test case

* Fixed linting issues

* Network test cases failing

---------

Co-authored-by: Lucas Gomide <lucaslg200@gmail.com>
This commit is contained in:
Vidit Ostwal
2025-05-15 00:43:39 +05:30
committed by GitHub
parent e1541b2619
commit 30ef8ed70b
8 changed files with 279 additions and 40 deletions

View File

@@ -110,6 +110,8 @@ crewai reset-memories [OPTIONS]
- `-s, --short`: Reset SHORT TERM memory - `-s, --short`: Reset SHORT TERM memory
- `-e, --entities`: Reset ENTITIES memory - `-e, --entities`: Reset ENTITIES memory
- `-k, --kickoff-outputs`: Reset LATEST KICKOFF TASK OUTPUTS - `-k, --kickoff-outputs`: Reset LATEST KICKOFF TASK OUTPUTS
- `-kn, --knowledge`: Reset KNOWLEDGE storage
- `-akn, --agent-knowledge`: Reset AGENT KNOWLEDGE storage
- `-a, --all`: Reset ALL memories - `-a, --all`: Reset ALL memories
Example: Example:

View File

@@ -497,6 +497,13 @@ crew = Crew(
result = crew.kickoff( result = crew.kickoff(
inputs={"question": "What is the storage capacity of the XPS 13?"} inputs={"question": "What is the storage capacity of the XPS 13?"}
) )
# Resetting the agent specific knowledge via crew object
crew.reset_memories(command_type = 'agent_knowledge')
# Resetting the agent specific knowledge via CLI
crewai reset-memories --agent-knowledge
crewai reset-memories -akn
``` ```
<Info> <Info>

View File

@@ -679,6 +679,7 @@ crewai reset-memories [OPTIONS]
| `-e`, `--entities` | Reset ENTITIES memory. | Flag (boolean) | False | | `-e`, `--entities` | Reset ENTITIES memory. | Flag (boolean) | False |
| `-k`, `--kickoff-outputs` | Reset LATEST KICKOFF TASK OUTPUTS. | Flag (boolean) | False | | `-k`, `--kickoff-outputs` | Reset LATEST KICKOFF TASK OUTPUTS. | Flag (boolean) | False |
| `-kn`, `--knowledge` | Reset KNOWLEDEGE storage | Flag (boolean) | False | | `-kn`, `--knowledge` | Reset KNOWLEDEGE storage | Flag (boolean) | False |
| `-akn`, `--agent-knowledge` | Reset AGENT KNOWLEDGE storage | Flag (boolean) | False |
| `-a`, `--all` | Reset ALL memories. | Flag (boolean) | False | | `-a`, `--all` | Reset ALL memories. | Flag (boolean) | False |
Note: To use the cli command you need to have your crew in a file called crew.py in the same directory. Note: To use the cli command you need to have your crew in a file called crew.py in the same directory.
@@ -716,9 +717,11 @@ my_crew.reset_memories(command_type = 'all') # Resets all the memory
| `entities` | Reset ENTITIES memory. | | `entities` | Reset ENTITIES memory. |
| `kickoff_outputs` | Reset LATEST KICKOFF TASK OUTPUTS. | | `kickoff_outputs` | Reset LATEST KICKOFF TASK OUTPUTS. |
| `knowledge` | Reset KNOWLEDGE memory. | | `knowledge` | Reset KNOWLEDGE memory. |
| `agent_knowledge` | Reset AGENT KNOWLEDGE memory. |
| `all` | Reset ALL memories. | | `all` | Reset ALL memories. |
## Benefits of Using CrewAI's Memory System ## Benefits of Using CrewAI's Memory System
- 🦾 **Adaptive Learning:** Crews become more efficient over time, adapting to new information and refining their approach to tasks. - 🦾 **Adaptive Learning:** Crews become more efficient over time, adapting to new information and refining their approach to tasks.

View File

@@ -1,6 +1,5 @@
import os
from importlib.metadata import version as get_version from importlib.metadata import version as get_version
from typing import Optional, Tuple from typing import Optional
import click import click
@@ -138,12 +137,8 @@ def log_tasks_outputs() -> None:
@click.option("-s", "--short", is_flag=True, help="Reset SHORT TERM memory") @click.option("-s", "--short", is_flag=True, help="Reset SHORT TERM memory")
@click.option("-e", "--entities", is_flag=True, help="Reset ENTITIES memory") @click.option("-e", "--entities", is_flag=True, help="Reset ENTITIES memory")
@click.option("-kn", "--knowledge", is_flag=True, help="Reset KNOWLEDGE storage") @click.option("-kn", "--knowledge", is_flag=True, help="Reset KNOWLEDGE storage")
@click.option( @click.option("-akn", "--agent-knowledge", is_flag=True, help="Reset AGENT KNOWLEDGE storage")
"-k", @click.option("-k","--kickoff-outputs",is_flag=True,help="Reset LATEST KICKOFF TASK OUTPUTS")
"--kickoff-outputs",
is_flag=True,
help="Reset LATEST KICKOFF TASK OUTPUTS",
)
@click.option("-a", "--all", is_flag=True, help="Reset ALL memories") @click.option("-a", "--all", is_flag=True, help="Reset ALL memories")
def reset_memories( def reset_memories(
long: bool, long: bool,
@@ -151,18 +146,20 @@ def reset_memories(
entities: bool, entities: bool,
knowledge: bool, knowledge: bool,
kickoff_outputs: bool, kickoff_outputs: bool,
agent_knowledge: bool,
all: bool, all: bool,
) -> None: ) -> None:
""" """
Reset the crew memories (long, short, entity, latest_crew_kickoff_ouputs). This will delete all the data saved. Reset the crew memories (long, short, entity, latest_crew_kickoff_ouputs, knowledge, agent_knowledge). This will delete all the data saved.
""" """
try: try:
if not all and not (long or short or entities or knowledge or kickoff_outputs): memory_types = [long, short, entities, knowledge, agent_knowledge, kickoff_outputs, all]
if not any(memory_types):
click.echo( click.echo(
"Please specify at least one memory type to reset using the appropriate flags." "Please specify at least one memory type to reset using the appropriate flags."
) )
return return
reset_memories_command(long, short, entities, knowledge, kickoff_outputs, all) reset_memories_command(long, short, entities, knowledge, agent_knowledge, kickoff_outputs, all)
except Exception as e: except Exception as e:
click.echo(f"An error occurred while resetting memories: {e}", err=True) click.echo(f"An error occurred while resetting memories: {e}", err=True)

View File

@@ -10,6 +10,7 @@ def reset_memories_command(
short, short,
entity, entity,
knowledge, knowledge,
agent_knowledge,
kickoff_outputs, kickoff_outputs,
all, all,
) -> None: ) -> None:
@@ -23,10 +24,11 @@ def reset_memories_command(
kickoff_outputs (bool): Whether to reset the latest kickoff task outputs. kickoff_outputs (bool): Whether to reset the latest kickoff task outputs.
all (bool): Whether to reset all memories. all (bool): Whether to reset all memories.
knowledge (bool): Whether to reset the knowledge. knowledge (bool): Whether to reset the knowledge.
agent_knowledge (bool): Whether to reset the agents knowledge.
""" """
try: try:
if not any([long, short, entity, kickoff_outputs, knowledge, all]): if not any([long, short, entity, kickoff_outputs, knowledge, agent_knowledge, all]):
click.echo( click.echo(
"No memory type specified. Please specify at least one type to reset." "No memory type specified. Please specify at least one type to reset."
) )
@@ -67,6 +69,11 @@ def reset_memories_command(
click.echo( click.echo(
f"[Crew ({crew.name if crew.name else crew.id})] Knowledge has been reset." f"[Crew ({crew.name if crew.name else crew.id})] Knowledge has been reset."
) )
if agent_knowledge:
crew.reset_memories(command_type="agent_knowledge")
click.echo(
f"[Crew ({crew.name if crew.name else crew.id})] Agents knowledge has been reset."
)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
click.echo(f"An error occurred while resetting the memories: {e}", err=True) click.echo(f"An error occurred while resetting the memories: {e}", err=True)

View File

@@ -1356,7 +1356,7 @@ class Crew(FlowTrackable, BaseModel):
Args: Args:
command_type: Type of memory to reset. command_type: Type of memory to reset.
Valid options: 'long', 'short', 'entity', 'knowledge', Valid options: 'long', 'short', 'entity', 'knowledge', 'agent_knowledge'
'kickoff_outputs', or 'all' 'kickoff_outputs', or 'all'
Raises: Raises:
@@ -1369,6 +1369,7 @@ class Crew(FlowTrackable, BaseModel):
"short", "short",
"entity", "entity",
"knowledge", "knowledge",
"agent_knowledge",
"kickoff_outputs", "kickoff_outputs",
"all", "all",
"external", "external",
@@ -1393,19 +1394,14 @@ class Crew(FlowTrackable, BaseModel):
def _reset_all_memories(self) -> None: def _reset_all_memories(self) -> None:
"""Reset all available memory systems.""" """Reset all available memory systems."""
memory_systems = [ memory_systems = self._get_memory_systems()
("short term", getattr(self, "_short_term_memory", None)),
("entity", getattr(self, "_entity_memory", None)),
("external", getattr(self, "_external_memory", None)),
("long term", getattr(self, "_long_term_memory", None)),
("task output", getattr(self, "_task_output_handler", None)),
("knowledge", getattr(self, "knowledge", None)),
]
for name, system in memory_systems: for memory_type, config in memory_systems.items():
if system is not None: if (system := config.get('system')) is not None:
name = config.get('name')
try: try:
system.reset() reset_fn: Callable = cast(Callable, config.get('reset'))
reset_fn(system)
self._logger.log( self._logger.log(
"info", "info",
f"[Crew ({self.name if self.name else self.id})] {name} memory has been reset", f"[Crew ({self.name if self.name else self.id})] {name} memory has been reset",
@@ -1424,24 +1420,17 @@ class Crew(FlowTrackable, BaseModel):
Raises: Raises:
RuntimeError: If the specified memory system fails to reset RuntimeError: If the specified memory system fails to reset
""" """
reset_functions = { memory_systems = self._get_memory_systems()
"long": (getattr(self, "_long_term_memory", None), "long term"), config = memory_systems[memory_type]
"short": (getattr(self, "_short_term_memory", None), "short term"), system = config.get('system')
"entity": (getattr(self, "_entity_memory", None), "entity"), name = config.get('name')
"knowledge": (getattr(self, "knowledge", None), "knowledge"),
"kickoff_outputs": (
getattr(self, "_task_output_handler", None),
"task output",
),
"external": (getattr(self, "_external_memory", None), "external"),
}
memory_system, name = reset_functions[memory_type] if system is None:
if memory_system is None:
raise RuntimeError(f"{name} memory system is not initialized") raise RuntimeError(f"{name} memory system is not initialized")
try: try:
memory_system.reset() reset_fn: Callable = cast(Callable, config.get('reset'))
reset_fn(system)
self._logger.log( self._logger.log(
"info", "info",
f"[Crew ({self.name if self.name else self.id})] {name} memory has been reset", f"[Crew ({self.name if self.name else self.id})] {name} memory has been reset",
@@ -1450,3 +1439,64 @@ class Crew(FlowTrackable, BaseModel):
raise RuntimeError( raise RuntimeError(
f"[Crew ({self.name if self.name else self.id})] Failed to reset {name} memory: {str(e)}" f"[Crew ({self.name if self.name else self.id})] Failed to reset {name} memory: {str(e)}"
) from e ) from e
def _get_memory_systems(self):
"""Get all available memory systems with their configuration.
Returns:
Dict containing all memory systems with their reset functions and display names.
"""
def default_reset(memory):
return memory.reset()
def knowledge_reset(memory):
return self.reset_knowledge(memory)
# Get knowledge for agents
agent_knowledges = [getattr(agent, "knowledge", None) for agent in self.agents
if getattr(agent, "knowledge", None) is not None]
# Get knowledge for crew and agents
crew_knowledge = getattr(self, "knowledge", None)
crew_and_agent_knowledges = ([crew_knowledge] if crew_knowledge is not None else []) + agent_knowledges
return {
'short': {
'system': getattr(self, "_short_term_memory", None),
'reset': default_reset,
'name': 'Short Term'
},
'entity': {
'system': getattr(self, "_entity_memory", None),
'reset': default_reset,
'name': 'Entity'
},
'external': {
'system': getattr(self, "_external_memory", None),
'reset': default_reset,
'name': 'External'
},
'long': {
'system': getattr(self, "_long_term_memory", None),
'reset': default_reset,
'name': 'Long Term'
},
'kickoff_outputs': {
'system': getattr(self, "_task_output_handler", None),
'reset': default_reset,
'name': 'Task Output'
},
'knowledge': {
'system': crew_and_agent_knowledges if crew_and_agent_knowledges else None,
'reset': knowledge_reset,
'name': 'Crew Knowledge and Agent Knowledge'
},
'agent_knowledge': {
'system': agent_knowledges if agent_knowledges else None,
'reset': knowledge_reset,
'name': 'Agent Knowledge'
}
}
def reset_knowledge(self, knowledges: List[Knowledge]) -> None:
"""Reset crew and agent knowledge storage."""
for ks in knowledges:
ks.reset()

View File

@@ -162,8 +162,18 @@ def test_reset_knowledge(mock_get_crews, runner):
assert call_count == 1, "reset_memories should have been called once" assert call_count == 1, "reset_memories should have been called once"
def test_reset_memory_from_many_crews(mock_get_crews, runner): def test_reset_agent_knowledge(mock_get_crews, runner):
result = runner.invoke(reset_memories, ["--agent-knowledge"])
call_count = 0
for crew in mock_get_crews.return_value:
crew.reset_memories.assert_called_once_with(command_type="agent_knowledge")
assert f"[Crew ({crew.name})] Agents knowledge has been reset." in result.output
call_count += 1
assert call_count == 1, "reset_memories should have been called once"
def test_reset_memory_from_many_crews(mock_get_crews, runner):
crews = [] crews = []
for crew_id in ["id-1234", "id-5678"]: for crew_id in ["id-1234", "id-5678"]:
mock_crew = mock.Mock(spec=Crew) mock_crew = mock.Mock(spec=Crew)

View File

@@ -14,6 +14,7 @@ from crewai.agents import CacheHandler
from crewai.crew import Crew from crewai.crew import Crew
from crewai.crews.crew_output import CrewOutput from crewai.crews.crew_output import CrewOutput
from crewai.flow import Flow, start from crewai.flow import Flow, start
from crewai.knowledge.knowledge import Knowledge
from crewai.knowledge.source.string_knowledge_source import StringKnowledgeSource from crewai.knowledge.source.string_knowledge_source import StringKnowledgeSource
from crewai.llm import LLM from crewai.llm import LLM
from crewai.memory.contextual.contextual_memory import ContextualMemory from crewai.memory.contextual.contextual_memory import ContextualMemory
@@ -4403,3 +4404,165 @@ def test_sets_parent_flow_when_inside_flow(researcher, writer):
flow = MyFlow() flow = MyFlow()
result = flow.kickoff() result = flow.kickoff()
assert result.parent_flow is flow assert result.parent_flow is flow
def test_reset_knowledge_with_no_crew_knowledge(researcher,writer):
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
tasks=[
Task(description="Task 1", expected_output="output", agent=researcher),
Task(description="Task 2", expected_output="output", agent=writer),
]
)
with pytest.raises(RuntimeError) as excinfo:
crew.reset_memories(command_type='knowledge')
# Optionally, you can also check the error message
assert "Crew Knowledge and Agent Knowledge memory system is not initialized" in str(excinfo.value) # Replace with the expected message
def test_reset_knowledge_with_only_crew_knowledge(researcher,writer):
mock_ks = MagicMock(spec=Knowledge)
with patch.object(Crew,'reset_knowledge') as mock_reset_agent_knowledge:
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
tasks=[
Task(description="Task 1", expected_output="output", agent=researcher),
Task(description="Task 2", expected_output="output", agent=writer),
],
knowledge=mock_ks
)
crew.reset_memories(command_type='knowledge')
mock_reset_agent_knowledge.assert_called_once_with([mock_ks])
def test_reset_knowledge_with_crew_and_agent_knowledge(researcher,writer):
mock_ks_crew = MagicMock(spec=Knowledge)
mock_ks_research = MagicMock(spec=Knowledge)
mock_ks_writer = MagicMock(spec=Knowledge)
researcher.knowledge = mock_ks_research
writer.knowledge = mock_ks_writer
with patch.object(Crew,'reset_knowledge') as mock_reset_agent_knowledge:
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
tasks=[
Task(description="Task 1", expected_output="output", agent=researcher),
Task(description="Task 2", expected_output="output", agent=writer),
],
knowledge=mock_ks_crew
)
crew.reset_memories(command_type='knowledge')
mock_reset_agent_knowledge.assert_called_once_with([mock_ks_crew,mock_ks_research,mock_ks_writer])
def test_reset_knowledge_with_only_agent_knowledge(researcher,writer):
mock_ks_research = MagicMock(spec=Knowledge)
mock_ks_writer = MagicMock(spec=Knowledge)
researcher.knowledge = mock_ks_research
writer.knowledge = mock_ks_writer
with patch.object(Crew,'reset_knowledge') as mock_reset_agent_knowledge:
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
tasks=[
Task(description="Task 1", expected_output="output", agent=researcher),
Task(description="Task 2", expected_output="output", agent=writer),
],
)
crew.reset_memories(command_type='knowledge')
mock_reset_agent_knowledge.assert_called_once_with([mock_ks_research,mock_ks_writer])
def test_reset_agent_knowledge_with_no_agent_knowledge(researcher,writer):
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
tasks=[
Task(description="Task 1", expected_output="output", agent=researcher),
Task(description="Task 2", expected_output="output", agent=writer),
],
)
with pytest.raises(RuntimeError) as excinfo:
crew.reset_memories(command_type='agent_knowledge')
# Optionally, you can also check the error message
assert "Agent Knowledge memory system is not initialized" in str(excinfo.value) # Replace with the expected message
def test_reset_agent_knowledge_with_only_crew_knowledge(researcher,writer):
mock_ks = MagicMock(spec=Knowledge)
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
tasks=[
Task(description="Task 1", expected_output="output", agent=researcher),
Task(description="Task 2", expected_output="output", agent=writer),
],
knowledge=mock_ks
)
with pytest.raises(RuntimeError) as excinfo:
crew.reset_memories(command_type='agent_knowledge')
# Optionally, you can also check the error message
assert "Agent Knowledge memory system is not initialized" in str(excinfo.value) # Replace with the expected message
def test_reset_agent_knowledge_with_crew_and_agent_knowledge(researcher,writer):
mock_ks_crew = MagicMock(spec=Knowledge)
mock_ks_research = MagicMock(spec=Knowledge)
mock_ks_writer = MagicMock(spec=Knowledge)
researcher.knowledge = mock_ks_research
writer.knowledge = mock_ks_writer
with patch.object(Crew,'reset_knowledge') as mock_reset_agent_knowledge:
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
tasks=[
Task(description="Task 1", expected_output="output", agent=researcher),
Task(description="Task 2", expected_output="output", agent=writer),
],
knowledge=mock_ks_crew
)
crew.reset_memories(command_type='agent_knowledge')
mock_reset_agent_knowledge.assert_called_once_with([mock_ks_research,mock_ks_writer])
def test_reset_agent_knowledge_with_only_agent_knowledge(researcher,writer):
mock_ks_research = MagicMock(spec=Knowledge)
mock_ks_writer = MagicMock(spec=Knowledge)
researcher.knowledge = mock_ks_research
writer.knowledge = mock_ks_writer
with patch.object(Crew,'reset_knowledge') as mock_reset_agent_knowledge:
crew = Crew(
agents=[researcher, writer],
process=Process.sequential,
tasks=[
Task(description="Task 1", expected_output="output", agent=researcher),
Task(description="Task 2", expected_output="output", agent=writer),
],
)
crew.reset_memories(command_type='agent_knowledge')
mock_reset_agent_knowledge.assert_called_once_with([mock_ks_research,mock_ks_writer])