fix di bug, small enhancement for tl

This commit is contained in:
yzlin 2024-04-26 15:42:52 +08:00
parent 531fe12e3f
commit ede3e36944
4 changed files with 26 additions and 21 deletions

View file

@ -33,7 +33,7 @@ class MGXEnv(Environment):
if user_defined_recipient:
self._publish_message(message)
# bypass team leader, team leader only needs to know but not to react
tl.rc.memory.add(message)
tl.rc.memory.add(self.move_message_info_to_content(message))
elif self.message_within_software_sop(message) and not self.has_user_requirement():
# Quick routing for messages within software SOP, bypassing TL.
@ -43,7 +43,7 @@ class MGXEnv(Environment):
# Consider replacing this in the future.
self._publish_message(message)
if self.is_software_task_finished(message):
tl.rc.memory.add(message)
tl.rc.memory.add(self.move_message_info_to_content(message))
tl.finish_current_task()
elif publicer == tl.profile:
@ -52,6 +52,7 @@ class MGXEnv(Environment):
else:
# every regular message goes through team leader
message = self.move_message_info_to_content(message)
message.send_to.add(tl.name)
tl.put_message(message)
@ -70,7 +71,7 @@ class MGXEnv(Environment):
def message_within_software_sop(self, message: Message) -> bool:
return message.sent_from in any_to_str_set([ProductManager, Architect, ProjectManager, Engineer, QaEngineer])
def has_user_requirement(self, k=3) -> bool:
def has_user_requirement(self, k=2) -> bool:
"""A heuristics to check if there is a recent user intervention"""
return any_to_str(UserRequirement) in [msg.cause_by for msg in self.history.get(k)]
@ -79,3 +80,16 @@ class MGXEnv(Environment):
return message.cause_by in any_to_str_set([WritePRD, WriteDesign, WriteTasks, SummarizeCode]) or (
message.cause_by == any_to_str(WriteTest) and "Exceeding" in message.content
)
def move_message_info_to_content(self, message: Message) -> Message:
"""Two things here:
1. Convert role, since role field must be reserved for LLM API, and is limited to, for example, one of ["user", "assistant", "system"]
2. Add sender and recipient info to content, making TL aware, since LLM API only takes content as input
"""
if message.role in ["system", "user", "assistant"]:
sent_from = message.sent_from
else:
sent_from = message.role
message.role = "assistant"
message.content = f"from {sent_from} to {message.send_to}: {message.content}"
return message

View file

@ -14,7 +14,7 @@ from metagpt.roles import Role
from metagpt.schema import Message, Task, TaskResult
from metagpt.strategy.task_type import TaskType
from metagpt.tools.tool_recommend import BM25ToolRecommender, ToolRecommender
from metagpt.utils.common import CodeParser, role_raise_decorator
from metagpt.utils.common import CodeParser
REACT_THINK_PROMPT = """
# User Requirement
@ -62,7 +62,7 @@ class DataInterpreter(Role):
async def _think(self) -> bool:
"""Useful in 'react' mode. Use LLM to decide whether and what to do next."""
user_requirement = self.get_memories()[0].content
user_requirement = self.get_memories()[-1].content
context = self.working_memory.get()
if not context:
@ -86,6 +86,7 @@ class DataInterpreter(Role):
return Message(content=code, role="assistant", cause_by=WriteAnalysisCode)
async def _plan_and_act(self) -> Message:
self._set_state(0)
try:
rsp = await super()._plan_and_act()
await self.execute_code.terminate()
@ -153,7 +154,7 @@ class DataInterpreter(Role):
logger.info(f"ready to {todo.name}")
use_reflection = counter > 0 and self.use_reflection # only use reflection after the first trial
user_requirement = self.get_memories()[0].content # issue: 1多次用户交互时永远只读用户的第1次request2prerequisite没处理
user_requirement = self.get_memories()[-1].content
code = await todo.run(
user_requirement=user_requirement,
@ -186,11 +187,3 @@ class DataInterpreter(Role):
print(result)
data_info = DATA_INFO.format(info=result)
self.working_memory.add(Message(content=data_info, role="user", cause_by=CheckData))
@role_raise_decorator
async def run(self, with_message=None) -> Message | None:
if not self.rc.todo:
self.set_actions([WriteAnalysisCode])
self._set_state(0)
return await super().run(with_message)

View file

@ -70,12 +70,8 @@ class TeamLeader(Role):
self._set_state(-1)
def get_memory(self, k=10) -> list[Message]:
mem = self.rc.memory.get(k=k)
for m in mem:
if m.role not in ["system", "user", "assistant"]:
m.content = f"from {m.role} to {m.send_to}: {m.content}"
m.role = "assistant"
return mem
"""A wrapper with default value"""
return self.rc.memory.get(k=k)
async def _think(self) -> bool:
"""Useful in 'react' mode. Use LLM to decide whether and what to do next."""
@ -115,7 +111,7 @@ class TeamLeader(Role):
"""Useful in 'react' mode. Return a Message conforming to Role._act interface."""
self.run_commands(self.commands)
self.task_result = TaskResult(result="Success", is_success=True)
msg = Message(content="Commands executed", role="user", send_to=self)
msg = Message(content="Commands executed", send_to="no one") # a dummy message to conform to the interface
self.rc.memory.add(msg)
return msg

View file

@ -492,6 +492,8 @@ class Role(SerializationMixin, ContextMixin, BaseModel):
await self.planner.process_task_result(task_result)
rsp = self.planner.get_useful_memories()[0] # return the completed plan as a response
rsp.role = "assistant"
rsp.sent_from = self._setting
self.rc.memory.add(rsp) # add to persistent memory