Merge branch 'opt_speed_quality' into 'mgx_ops'

Faster & Better Engineer2, & some small updates

See merge request pub/MetaGPT!359
This commit is contained in:
林义章 2024-08-29 03:28:20 +00:00
commit bbbaf08563
4 changed files with 94 additions and 62 deletions

View file

@ -1,14 +1,25 @@
from __future__ import annotations
from pathlib import Path
from pydantic import Field
from metagpt.actions.write_code_review import ValidateAndRewriteCode
from metagpt.prompts.di.engineer2 import ENGINEER2_INSTRUCTION
# from metagpt.actions.write_code_review import ValidateAndRewriteCode
from metagpt.prompts.di.engineer2 import (
ENGINEER2_INSTRUCTION,
WRITE_CODE_PROMPT,
WRITE_CODE_SYSTEM_PROMPT,
)
from metagpt.roles.di.role_zero import RoleZero
from metagpt.schema import UserMessage
from metagpt.strategy.experience_retriever import ENGINEER_EXAMPLE
from metagpt.tools.libs.terminal import Terminal
from metagpt.tools.tool_registry import register_tool
from metagpt.utils.common import CodeParser, awrite
from metagpt.utils.report import EditorReporter
@register_tool(include_functions=["write_new_code"])
class Engineer2(RoleZero):
name: str = "Alex"
profile: str = "Engineer"
@ -17,16 +28,16 @@ class Engineer2(RoleZero):
terminal: Terminal = Field(default_factory=Terminal, exclude=True)
tools: list[str] = ["Plan", "Editor:write,read", "RoleZero", "Terminal:run_command", "ValidateAndRewriteCode"]
tools: list[str] = ["Plan", "Editor:read", "RoleZero", "Terminal:run_command", "Engineer2"]
def _update_tool_execution(self):
validate = ValidateAndRewriteCode()
# validate = ValidateAndRewriteCode()
self.tool_execution_map.update(
{
"Terminal.run_command": self.terminal.run_command,
"ValidateAndRewriteCode.run": validate.run,
"ValidateAndRewriteCode": validate.run,
"Engineer2.write_new_code": self.write_new_code,
# "ValidateAndRewriteCode.run": validate.run,
# "ValidateAndRewriteCode": validate.run,
}
)
@ -42,3 +53,28 @@ class Engineer2(RoleZero):
command_output += "All tasks are finished.\n"
command_output += await super()._run_special_command(cmd)
return command_output
async def write_new_code(self, path: str, instruction: str = "") -> str:
"""Write a new code file.
Args:
path (str): The absolute path of the file to be created.
instruction (optional, str): Further hints or notice other than the current task instruction, must be very concise and can be empty. Defaults to "".
"""
plan_status, _ = self._get_plan_status()
prompt = WRITE_CODE_PROMPT.format(
user_requirement=self.planner.plan.goal,
plan_status=plan_status,
instruction=instruction,
)
context = self.llm.format_msg(self.rc.memory.get(self.memory_k) + [UserMessage(content=prompt)])
async with EditorReporter(enable_llm_stream=True) as reporter:
await reporter.async_report({"type": "code", "filename": Path(path).name, "src_path": path}, "meta")
rsp = await self.llm.aask(context, system_msgs=[WRITE_CODE_SYSTEM_PROMPT])
code = CodeParser.parse_code(text=rsp)
await awrite(path, code)
await reporter.async_report(path, "path")
# TODO: Consider adding line no to be ready for editing.
return f"The file {path} has been successfully created, with content:\n{code}"

View file

@ -4,6 +4,7 @@ import inspect
import json
import re
import traceback
from datetime import datetime
from typing import Annotated, Callable, Dict, List, Literal, Optional, Tuple
from pydantic import Field, model_validator
@ -67,7 +68,7 @@ class RoleZero(Role):
# React Mode
react_mode: Literal["react"] = "react"
max_react_loop: int = 20 # used for react mode
max_react_loop: int = 50 # used for react mode
# Tools
tools: list[str] = [] # Use special symbol ["<all>"] to indicate use of all registered tools
@ -233,6 +234,10 @@ class RoleZero(Role):
msg.add_metadata(IMAGES, images)
return memory
def _get_prefix(self) -> str:
time_info = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return super()._get_prefix() + f" The current time is {time_info}."
async def _act(self) -> Message:
if self.use_fixed_sop:
return await super()._act()
@ -276,6 +281,14 @@ class RoleZero(Role):
logger.debug(f"{self._setting}: {self.rc.state=}, will do {self.rc.todo}")
rsp = await self._act()
actions_taken += 1
# post-check
if self.rc.max_react_loop >= 10 and actions_taken >= self.rc.max_react_loop:
# If max_react_loop is a small value (e.g. < 10), it is intended to be reached and make the agent stop
logger.warning(f"reached max_react_loop: {actions_taken}")
rsp = await self.ask_human("I have reached my max action rounds, do you want me to continue? Yes or no")
if "yes" in rsp.lower():
actions_taken = 0
return rsp # return output from the last action
def format_quick_system_prompt(self) -> str: