mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-07-05 16:02:14 +02:00
1. RunCode -> DebugError loop done; 2. modify default k for memory search
This commit is contained in:
parent
6bf527d31e
commit
8abdca3057
10 changed files with 140 additions and 70 deletions
|
|
@ -141,7 +141,8 @@ class Engineer(Role):
|
|||
msg = Message(content=code, role=self.profile, cause_by=type(self._rc.todo))
|
||||
self._rc.memory.add(msg)
|
||||
|
||||
code_msg_all.append(FILENAME_CODE_SEP.join([todo, str(file_path), code]))
|
||||
code_msg = todo + FILENAME_CODE_SEP + str(file_path)
|
||||
code_msg_all.append(code_msg)
|
||||
|
||||
logger.info(f'Done {self.get_workspace()} generating.')
|
||||
msg = Message(
|
||||
|
|
@ -188,7 +189,8 @@ class Engineer(Role):
|
|||
msg = Message(content=code, role=self.profile, cause_by=WriteCode)
|
||||
self._rc.memory.add(msg)
|
||||
|
||||
code_msg_all.append(FILENAME_CODE_SEP.join([todo, str(file_path), code]))
|
||||
code_msg = todo + FILENAME_CODE_SEP + str(file_path)
|
||||
code_msg_all.append(code_msg)
|
||||
|
||||
logger.info(f'Done {self.get_workspace()} generating.')
|
||||
msg = Message(
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ from metagpt.logs import logger
|
|||
from metagpt.roles import Role
|
||||
from metagpt.schema import Message
|
||||
from metagpt.roles.engineer import Engineer
|
||||
from metagpt.utils.common import CodeParser
|
||||
from metagpt.utils.common import CodeParser, parse_recipient
|
||||
from metagpt.utils.special_tokens import MSG_SEP, FILENAME_CODE_SEP
|
||||
|
||||
class QaEngineer(Role):
|
||||
|
|
@ -24,7 +24,9 @@ class QaEngineer(Role):
|
|||
constraints="The test code you write should conform to code standard like PEP8, be modular, easy to read and maintain"):
|
||||
super().__init__(name, profile, goal, constraints)
|
||||
self._init_actions([WriteTest])
|
||||
self._watch([WriteCode])
|
||||
self._watch([WriteCode, WriteTest, RunCode, DebugError])
|
||||
self.test_round = 0
|
||||
self.test_round_allowed = 5 # hard code for 1 WriteTest round + 2 x (RunCode -> DebugError loop)
|
||||
|
||||
@classmethod
|
||||
def parse_workspace(cls, system_design_msg: Message) -> str:
|
||||
|
|
@ -38,12 +40,11 @@ class QaEngineer(Role):
|
|||
return WORKSPACE_ROOT / 'src'
|
||||
workspace = self.parse_workspace(msg)
|
||||
# project directory: workspace/{package_name}, which contains package source code folder, tests folder, resources folder, etc.
|
||||
# source codes directory: workspace/{package_name}/{package_name}
|
||||
if return_proj_dir:
|
||||
return WORKSPACE_ROOT / workspace
|
||||
# development codes directory: workspace/{package_name}/{package_name}
|
||||
return WORKSPACE_ROOT / workspace / workspace
|
||||
|
||||
|
||||
|
||||
def write_file(self, filename: str, code: str):
|
||||
workspace = self.get_workspace() / 'tests'
|
||||
file = workspace / filename
|
||||
|
|
@ -60,8 +61,12 @@ class QaEngineer(Role):
|
|||
for code_msg in code_msgs:
|
||||
|
||||
# write tests
|
||||
file_name, file_path, code_to_test = code_msg.split(FILENAME_CODE_SEP)
|
||||
file_name, file_path = code_msg.split(FILENAME_CODE_SEP)
|
||||
code_to_test = open(file_path, "r").read()
|
||||
if "test" in file_name:
|
||||
continue # Engineer might write some test files, skip testing a test file
|
||||
test_file_name = "test_" + file_name
|
||||
test_file_path = self.get_workspace() / "tests" / test_file_name
|
||||
logger.info(f'Writing {test_file_name}..')
|
||||
test_code = await WriteTest().run(
|
||||
code_to_test=code_to_test,
|
||||
|
|
@ -72,48 +77,86 @@ class QaEngineer(Role):
|
|||
)
|
||||
self.write_file(test_file_name, test_code)
|
||||
|
||||
# run tests
|
||||
result_msg = await RunCode().run(
|
||||
mode="script",
|
||||
code=code_to_test,
|
||||
code_file_name=file_name,
|
||||
test_code=test_code,
|
||||
test_file_name=test_file_name,
|
||||
command=['python', f'tests/{test_file_name}'],
|
||||
working_directory=self.get_workspace(), # workspace/package_name, will run tests/test_xxx.py here
|
||||
additional_python_paths=[self.get_workspace(return_proj_dir=False)], # workspace/package_name/package_name,
|
||||
# import statement inside package code needs this
|
||||
# prepare context for run tests in next round
|
||||
command = ['python', f'tests/{test_file_name}']
|
||||
file_info = {
|
||||
"file_name": file_name, "file_path": str(file_path),
|
||||
"test_file_name": test_file_name, "test_file_path": str(test_file_path),
|
||||
"command": command
|
||||
}
|
||||
msg = Message(
|
||||
content=str(file_info), role=self.profile, cause_by=WriteTest,
|
||||
sent_from="QaEngineer", send_to="QaEngineer"
|
||||
)
|
||||
|
||||
result_msg_all.append(result_msg)
|
||||
|
||||
# RunCode().run(
|
||||
# mode="script",
|
||||
# working_directory=self.get_workspace(),
|
||||
# additional_python_paths=[self.get_workspace(return_proj_dir=False)],
|
||||
# command=['python', '-m', 'unittest', 'discover', '-s', 'tests']
|
||||
# )
|
||||
self._publish_message(msg)
|
||||
|
||||
logger.info(f'Done {self.get_workspace()}/tests generating.')
|
||||
msg_content = MSG_SEP.join(result_msg_all)
|
||||
msg = Message(content=msg_content, role=self.profile, cause_by=RunCode, send_to=QaEngineer)
|
||||
return msg
|
||||
|
||||
async def _run_code(self, msg):
|
||||
file_info = eval(msg.content)
|
||||
code_to_test = open(file_info["file_path"], "r").read()
|
||||
test_code = open(file_info["test_file_path"], "r").read()
|
||||
proj_dir = self.get_workspace()
|
||||
development_code_dir = self.get_workspace(return_proj_dir=False)
|
||||
|
||||
result_msg = await RunCode().run(
|
||||
mode="script",
|
||||
code=code_to_test,
|
||||
code_file_name=file_info["file_name"],
|
||||
test_code=test_code,
|
||||
test_file_name=file_info["test_file_name"],
|
||||
command=file_info["command"],
|
||||
working_directory=proj_dir, # workspace/package_name, will run tests/test_xxx.py here
|
||||
additional_python_paths=[development_code_dir], # workspace/package_name/package_name,
|
||||
# import statement inside package code needs this
|
||||
)
|
||||
|
||||
recipient = parse_recipient(result_msg) # the recipient might be Engineer or myself
|
||||
content = str(file_info) + FILENAME_CODE_SEP + result_msg
|
||||
msg = Message(
|
||||
content=content, role=self.profile, cause_by=RunCode,
|
||||
sent_from="QaEngineer", send_to=recipient
|
||||
)
|
||||
self._publish_message(msg)
|
||||
|
||||
async def _debug_error(self, msg):
|
||||
# process the msg, if the code works fine, no need to debug
|
||||
|
||||
# else: debug and rewrite the code
|
||||
|
||||
pass
|
||||
file_info, context = msg.content.split(FILENAME_CODE_SEP)
|
||||
file_name, code = await DebugError().run(context)
|
||||
if file_name:
|
||||
self.write_file(file_name, code)
|
||||
recipient = msg.sent_from # send back to the one who ran the code for another run, might be one's self
|
||||
msg = Message(content=file_info, role=self.profile, cause_by=DebugError, sent_from="QaEngineer", send_to=recipient)
|
||||
self._publish_message(msg)
|
||||
|
||||
async def _observe(self) -> int:
|
||||
await super()._observe()
|
||||
self._rc.news = [msg for msg in self._rc.news \
|
||||
if msg.send_to == "QaEngineer"] # only relevant msgs count as observed news
|
||||
return len(self._rc.news)
|
||||
|
||||
async def _act(self) -> Message:
|
||||
if self.test_round > self.test_round_allowed:
|
||||
result_msg = Message(
|
||||
content=f"Exceeding {self.test_round_allowed} rounds of tests, skip (writing code counts as a round, too)",
|
||||
role=self.profile, cause_by=WriteTest, sent_from="QaEngineer", send_to=""
|
||||
)
|
||||
return result_msg
|
||||
|
||||
for msg in self._rc.news:
|
||||
if msg.send_to != "QaEngineer":
|
||||
continue
|
||||
# Decide what to do based on observed msg type, currently defined by human,
|
||||
# might potentially be moved to _think, that is, let the agent decides for itself
|
||||
if msg.cause_by == WriteCode:
|
||||
# engineer wrote a code, write a test for it
|
||||
# engineer wrote a code, time to write a test for it
|
||||
result_msg = await self._write_test(msg)
|
||||
elif msg.cause_by in [WriteTest, DebugError]:
|
||||
# I wrote or debugged my test code, time to run it
|
||||
result_msg = await self._run_code(msg)
|
||||
elif msg.cause_by == RunCode:
|
||||
# I wrote and ran my test code, fix bugs, if any
|
||||
# I ran my test code, time to fix bugs, if any
|
||||
result_msg = await self._debug_error(msg)
|
||||
|
||||
self.test_round += 1
|
||||
result_msg = Message(
|
||||
content=f"Round {self.test_round} of tests done",
|
||||
role=self.profile, cause_by=WriteTest, sent_from="QaEngineer", send_to=""
|
||||
)
|
||||
return result_msg
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue