Possible bug with the Role Memory
Bug description When I put two agents (AgentA and AgentB) in an environment, AgentB is automatically picks up the published message of AgentA even though _watch function is not set to AgentAAction.
AgentAAction: take a string and publish one letter at a time AgentBAction: given any string simple add the word "Add"
There is no LLM functionality here.
Code is shown below:
Defining AgentA
from metagpt.actions import Action, UserRequirement from metagpt.roles import Role from metagpt.logs import logger from metagpt.schema import Message
class AgentAAction(Action): name: str = "AgentAAction"
async def run(self, task: str):
sub_task = [letter for letter in task]
return sub_task
class AgentA(Role): name: str = "AgentA" profile: str = "AgentA"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_actions([AgentAAction])
self._watch({UserRequirement})
async def _act(self) -> None:
logger.info(f"{self._setting}: to do {self.rc.todo}({self.rc.todo.name}) {type(self.rc)}")
todo = self.rc.todo # todo will be SimpleWriteCode()
task = self.get_memories(k=1)[0] # find the most recent messages
sub_tasks = await todo.run(task.content)
for i in sub_tasks:
msg = Message(content=i, role=self.profile, cause_by=type(todo))
self.rc.env.publish_message(msg)
Defining AgentB
class AgentBAction(Action): name: str = "AgentBAction"
async def run(self, sub_task: str):
final_output = sub_task + "Add"
return final_output
class AgentB(Role): name: str = "AgentB" profile: str = "AgentB"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_actions([AgentBAction])
self._watch({UserRequirement}) ## Note!! Not watching AgentAAction
async def _act(self) -> Message:
logger.info(f"{self._setting}: to do {self.rc.todo}({self.rc.todo.name}) {type(self.rc)}")
todo = self.rc.todo # todo will be AgentBAction
sub_tasks = self.rc.history # find all the messages
print(sub_tasks)
for i in sub_tasks:
final_output = await todo.run(i.content)
final_output = Message(content=final_output, role=self.profile, cause_by=type(todo))
self.rc.env.publish_message(final_output)
self.rc.memory.add(final_output)
from metagpt.context import Context import asyncio from metagpt.environment import Environment
agentA = AgentA() agentB = AgentB() context = Context() # Load config2.yaml env = Environment(context=context) env.add_roles([agentA, agentB]) env.publish_message(Message(content='APPLE', send_to=AgentA)) env.publish_message(Message(content='CAT', send_to=AgentB)) # Send the user's message to Agent A to start the process. await env.run()
print all the messages in the memory of agentB
for index, memory in enumerate(agentB.get_memories(k=0)): print(f'{index}: {memory}')
Output: 0: user: CAT 1: AgentA: A 2: AgentA: P 3: AgentA: P 4: AgentA: L 5: AgentA: E 6: AgentB: CATAdd 7: AgentB: AAdd 8: AgentB: PAdd 9: AgentB: PAdd 10: AgentB: LAdd 11: AgentB: EAdd
Ideally the output should just be: 0: user: CAT 1: user: CATadd
Bug solved method
Environment information
Windows 12
- LLM type and model name: NA
- System version:
- Python version: 3.11
- MetaGPT version or branch: 0.8
- packages version:
- installation method: using pip
Screenshots or logs
looks like message is still observed even if cause_by is set to type(todo). Can @iorisa check this?
I think in current implementation of publish_message does not take care of role watch actions.
As in metagpt/environment/base_env.py#L187
for role, addrs in self.member_addrs.items():
if is_send_to(message, addrs):
role.put_message(message)
found = True
It should be
for role, addrs in self.member_addrs.items():
if is_send_to(message, addrs) and role.is_watch(message.cause_by):
role.put_message(message)
found = True
Create this pull request: https://github.com/geekan/MetaGPT/pull/1314
PS: I am a bit new, I don't know if it will break other examples or not.
I think in current implementation of publish_message does not take care of role watch actions.
As in metagpt/environment/base_env.py#L187
for role, addrs in self.member_addrs.items(): if is_send_to(message, addrs): role.put_message(message) found = TrueIt should be
for role, addrs in self.member_addrs.items(): if is_send_to(message, addrs) and role.is_watch(message.cause_by): role.put_message(message) found = TrueCreate this pull request: #1314
PS: I am a bit new, I don't know if it will break other examples or not.
The use of is_send_to(message, addrs) only is for the purpose of sending messages to the agents who want to know what's happening and the agents responsible for auditing, log processing, debuging, and experience generation, as these agents need to receive all messages.
I noticed that you have closed the PR, so I will go ahead and close this issue as well. If you have any questions, feel free to reopen this issue or create a new one to continue asking.