mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-05-27 14:25:20 +02:00
Merge branch 'fix/engineer_write_command_in_code_file' into 'mgx_ops'
解决了在文件中写指令的问题 See merge request pub/MetaGPT!386
This commit is contained in:
commit
f03f2c43a3
7 changed files with 62 additions and 42 deletions
|
|
@ -80,7 +80,8 @@ Note:
|
|||
21. When using the editor, pay attention to the editor's current directory. When you use editor tools, the paths must be either absolute or relative to the editor's current directory.
|
||||
22. The default programming languages are Native HTML.
|
||||
23. When planning, consider whether images are needed. If you are developing a showcase website, start by using ImageGetter.get_image to obtain the necessary images.
|
||||
24. If you finish all the tasks, use the command "end" to end.
|
||||
24. When planning, merge multiple tasks that operate on the same file into a single task. For example, create one task for writing unit tests for all functions in a class. Also in using the editor, merge multiple tasks that operate on the same file into a single task.
|
||||
25. When create unit tests for a code file, use Editor.read() to read the code file before planing. And create one plan to writing the unit test for the whole file.
|
||||
"""
|
||||
CURRENT_STATE = """
|
||||
The current editor state is:
|
||||
|
|
@ -108,8 +109,11 @@ WRITE_CODE_PROMPT = """
|
|||
# Current Coding File
|
||||
{file_path}
|
||||
|
||||
# Further Instruction
|
||||
{instruction}
|
||||
# File Description
|
||||
{file_description}
|
||||
|
||||
# Instruction
|
||||
Your task is to write the {file_name} according to the User Requirement.
|
||||
|
||||
# Output
|
||||
While some concise thoughts are helpful, code is absolutely required. Always output one and only one code block in your response. Output code in the following format:
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ you must respond in {respond_language}.
|
|||
Pay close attention to the Example provided, you can reuse the example for your current situation if it fits.
|
||||
If you open a file, the line number is displayed at the front of each line.
|
||||
You may use any of the available commands to create a plan or update the plan. You may output mutiple commands, they will be executed sequentially.
|
||||
If you finish current task, you will automatically take the next task in the existing plan, use Plan.finish_task, DON'T append a new task.
|
||||
If you finish current task, you will automatically take the next task in the existing plan, use Plan.finish_current_task, DON'T append a new task.
|
||||
Review the latest plan's outcome, focusing on achievements. If your completed task matches the current, consider it finished.
|
||||
Using Editor.insert_content_at_line and Editor.edit_file_by_replace more than once in the current command list is forbidden. Because the command is mutually exclusive and will change the line number after execution.
|
||||
In your response, include at least one command.
|
||||
|
|
@ -119,18 +119,14 @@ END_COMMAND = """
|
|||
```
|
||||
"""
|
||||
|
||||
ASK_HUMAN_COMMAND = """
|
||||
```json
|
||||
[
|
||||
{
|
||||
"command_name": "RoleZero.ask_human",
|
||||
"args": {
|
||||
"question": "I'm a little uncertain about the next step, could you provide me with some guidance?"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
SUMMARY_PROBLEM_WHEN_DUPLICATE = """You has meet a problem and cause duplicate command.Please directly tell me what is confusing or troubling you. Do Not output any command.Ouput you problem in {language} and within 30 words."""
|
||||
ASK_HUMAN_GUIDANCE_FORMAT = """
|
||||
I am facing the following problem:
|
||||
{problem}
|
||||
Could you please provide me with some guidance?If you want to stop, please include "<STOP>" in your guidance.
|
||||
"""
|
||||
ASK_HUMAN_COMMAND = [{"command_name": "RoleZero.ask_human", "args": {"question": ""}}]
|
||||
|
||||
JSON_REPAIR_PROMPT = """
|
||||
## json data
|
||||
{json_data}
|
||||
|
|
@ -226,8 +222,10 @@ Response Category: TASK.
|
|||
Thought: The request is vague and lacks specifics, requiring clarification on the process to optimize.
|
||||
Response Category: AMBIGUOUS.
|
||||
|
||||
9. Request: "Change the color of the text to blue in styles.css, add a new button in web page, delete the old background image."
|
||||
Thought: The request is an incremental development task that requires modifying one or more files.
|
||||
Response Category: TASK.
|
||||
"""
|
||||
|
||||
QUICK_RESPONSE_SYSTEM_PROMPT = """
|
||||
{role_info}
|
||||
However, you MUST respond to the user message by yourself directly, DON'T ask your team members.
|
||||
|
|
@ -237,11 +235,11 @@ REPORT_TO_HUMAN_PROMPT = """
|
|||
## Examlpe
|
||||
example 1:
|
||||
User requirement: create a 2048 game
|
||||
reply: The development of the 2048 game has been completed. All files (index.html, style.css, and script.js) have been created and reviewed.
|
||||
Reply: The development of the 2048 game has been completed. All files (index.html, style.css, and script.js) have been created and reviewed.
|
||||
|
||||
example 2:
|
||||
User requirement: Crawl and extract all the herb names from the website, Tell me the number of herbs.
|
||||
reply : The herb names have been successfully extracted. A total of 8 herb names were extracted.
|
||||
Reply : The herb names have been successfully extracted. A total of 8 herb names were extracted.
|
||||
|
||||
------------
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ Note:
|
|||
13. Instructions and reply must be in the same language.
|
||||
14. Default technology stack is HTML (.html), CSS (.css), and Pure JavaScript (.js). Web app is the default option when developing software.
|
||||
15. You are the only one who decides the programming language for the software, so the instruction must contain the programming language.
|
||||
16. Data collection and web/software development are two separate tasks. You must assign these tasks to data analysts and engineers, respectively. Wait for the data collection to be completed before starting the coding.
|
||||
"""
|
||||
TL_THOUGHT_GUIDANCE = (
|
||||
THOUGHT_GUIDANCE
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
|
|
@ -106,26 +107,23 @@ class Engineer2(RoleZero):
|
|||
async def _run_special_command(self, cmd) -> str:
|
||||
"""command requiring special check or parsing."""
|
||||
# finish current task before end.
|
||||
command_output = ""
|
||||
if cmd["command_name"] == "end" and not self.planner.plan.is_plan_finished():
|
||||
self.planner.plan.finish_all_tasks()
|
||||
command_output += "All tasks are finished.\n"
|
||||
command_output += await super()._run_special_command(cmd)
|
||||
command_output = await super()._run_special_command(cmd)
|
||||
return command_output
|
||||
|
||||
async def write_new_code(self, path: str, instruction: str = "Write code for the current file.") -> str:
|
||||
async def write_new_code(self, path: str, file_description: 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 "".
|
||||
file_description (optional, str): "Brief description and important notes of the file content, 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,
|
||||
file_path=path,
|
||||
plan_status=plan_status,
|
||||
instruction=instruction,
|
||||
file_path=path,
|
||||
file_description=file_description,
|
||||
file_name=os.path.basename(path),
|
||||
)
|
||||
# Sometimes the Engineer repeats the last command to respond.
|
||||
# Replace the last command with a manual prompt to guide the Engineer to write new code.
|
||||
|
|
@ -152,3 +150,8 @@ class Engineer2(RoleZero):
|
|||
else:
|
||||
command_output = await self.terminal.run_command(cmd)
|
||||
return command_output
|
||||
|
||||
async def _end(self):
|
||||
if not self.planner.plan.is_plan_finished():
|
||||
self.planner.plan.finish_all_tasks()
|
||||
return await super()._end()
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ from metagpt.logs import logger
|
|||
from metagpt.memory.role_zero_memory import RoleZeroLongTermMemory
|
||||
from metagpt.prompts.di.role_zero import (
|
||||
ASK_HUMAN_COMMAND,
|
||||
ASK_HUMAN_GUIDANCE_FORMAT,
|
||||
CMD_PROMPT,
|
||||
DETECT_LANGUAGE_PROMPT,
|
||||
END_COMMAND,
|
||||
|
|
@ -31,6 +32,7 @@ from metagpt.prompts.di.role_zero import (
|
|||
REGENERATE_PROMPT,
|
||||
REPORT_TO_HUMAN_PROMPT,
|
||||
ROLE_INSTRUCTION,
|
||||
SUMMARY_PROBLEM_WHEN_DUPLICATE,
|
||||
SUMMARY_PROMPT,
|
||||
SYSTEM_PROMPT,
|
||||
)
|
||||
|
|
@ -74,7 +76,7 @@ class RoleZero(Role):
|
|||
tools: list[str] = [] # Use special symbol ["<all>"] to indicate use of all registered tools
|
||||
tool_recommender: Optional[ToolRecommender] = None
|
||||
tool_execution_map: Annotated[dict[str, Callable], Field(exclude=True)] = {}
|
||||
special_tool_commands: list[str] = ["Plan.finish_current_task", "end", "Bash.run"]
|
||||
special_tool_commands: list[str] = ["Plan.finish_current_task", "end", "Bash.run", "RoleZero.ask_human"]
|
||||
# List of exclusive tool commands.
|
||||
# If multiple instances of these commands appear, only the first occurrence will be retained.
|
||||
exclusive_tool_commands: list[str] = [
|
||||
|
|
@ -247,7 +249,6 @@ class RoleZero(Role):
|
|||
await reporter.async_report({"type": "react"})
|
||||
self.command_rsp = await self.llm_cached_aask(req=req, system_msgs=[system_prompt], state_data=state_data)
|
||||
self.command_rsp = await self._check_duplicates(req, self.command_rsp)
|
||||
|
||||
return True
|
||||
|
||||
@exp_cache(context_builder=RoleZeroContextBuilder(), serializer=RoleZeroSerializer())
|
||||
|
|
@ -406,7 +407,7 @@ class RoleZero(Role):
|
|||
|
||||
async def _check_duplicates(self, req: list[dict], command_rsp: str, check_window: int = 10):
|
||||
past_rsp = [mem.content for mem in self.rc.memory.get(check_window)]
|
||||
if command_rsp in past_rsp:
|
||||
if command_rsp in past_rsp and '"command_name": "end"' not in command_rsp:
|
||||
# Normal response with thought contents are highly unlikely to reproduce
|
||||
# If an identical response is detected, it is a bad response, mostly due to LLM repeating generated content
|
||||
# In this case, ask human for help and regenerate
|
||||
|
|
@ -415,10 +416,15 @@ class RoleZero(Role):
|
|||
# Hard rule to ask human for help
|
||||
if past_rsp.count(command_rsp) >= 3:
|
||||
if '"command_name": "Plan.finish_current_task",' in command_rsp:
|
||||
# Detect the deplicate of "Plan.finish_current_task" command, use command "end" to finish the task
|
||||
# Detect the duplicate of the 'Plan.finish_current_task' command, and use the 'end' command to finish the task.
|
||||
logger.warning(f"Duplicate response detected: {command_rsp}")
|
||||
return END_COMMAND
|
||||
return ASK_HUMAN_COMMAND
|
||||
problem = await self.llm.aask(
|
||||
req + [UserMessage(content=SUMMARY_PROBLEM_WHEN_DUPLICATE.format(language=self.respond_language))]
|
||||
)
|
||||
ASK_HUMAN_COMMAND[0]["args"]["question"] = ASK_HUMAN_GUIDANCE_FORMAT.format(problem=problem).strip()
|
||||
ask_human_command = "```json\n" + json.dumps(ASK_HUMAN_COMMAND, indent=4, ensure_ascii=False) + "\n```"
|
||||
return ask_human_command
|
||||
# Try correction by self
|
||||
logger.warning(f"Duplicate response detected: {command_rsp}")
|
||||
regenerate_req = req + [UserMessage(content=REGENERATE_PROMPT)]
|
||||
|
|
@ -526,7 +532,15 @@ class RoleZero(Role):
|
|||
|
||||
elif cmd["command_name"] == "end":
|
||||
command_output = await self._end()
|
||||
|
||||
elif cmd["command_name"] == "RoleZero.ask_human":
|
||||
human_response = await self.ask_human(**cmd["args"])
|
||||
if "<STOP>" in human_response:
|
||||
human_response += "The user has asked me to stop because I have encountered a problem."
|
||||
self.rc.memory.add(UserMessage(content=human_response, cause_by=RunCommand))
|
||||
end_output = "\nCommand end executed:"
|
||||
end_output += await self._end()
|
||||
return end_output
|
||||
return human_response
|
||||
# output from bash.run may be empty, add decorations to the output to ensure visibility.
|
||||
elif cmd["command_name"] == "Bash.run":
|
||||
tool_obj = self.tool_execution_map[cmd["command_name"]]
|
||||
|
|
|
|||
|
|
@ -783,8 +783,8 @@ class Editor(BaseModel):
|
|||
lines = content.splitlines(True)
|
||||
total_lines = len(lines)
|
||||
check_list = [
|
||||
("first_replaced", first_replaced_line_number, first_replaced_line_content),
|
||||
("last_replaced", last_replaced_line_number, last_replaced_line_content),
|
||||
("first", first_replaced_line_number, first_replaced_line_content),
|
||||
("last", last_replaced_line_number, last_replaced_line_content),
|
||||
]
|
||||
for position, line_number, line_content in check_list:
|
||||
if lines[line_number - 1].rstrip() != line_content:
|
||||
|
|
@ -947,7 +947,7 @@ class Editor(BaseModel):
|
|||
|
||||
Args:
|
||||
file_name: (str): The name of the file to edit.
|
||||
line_number (int): The line number (starting from 1) to insert the content after.the insert content will be add between the line of line_number-1 and line_number
|
||||
line_number (int): The line number (starting from 1) to insert the content after. The insert content will be add between the line of line_number-1 and line_number
|
||||
insert_content (str): The content to insert betweed the previous_line_content and current_line_content.The insert_content must be a complete block of code at.
|
||||
|
||||
NOTE:
|
||||
|
|
|
|||
|
|
@ -550,9 +550,9 @@ def test_edit_file_by_replace(temp_py_file):
|
|||
|
||||
|
||||
MISMATCH_ERROR = """
|
||||
Error: The `first_replaced_replaced_line_number` does not match the `first_replaced_replaced_line_content`. Please correct the parameters.
|
||||
The `first_replaced_replaced_line_number` is 5 and the corresponding content is " b = 2".
|
||||
But the `first_replaced_replaced_line_content ` is "".
|
||||
Error: The `first_replaced_line_number` does not match the `first_replaced_line_content`. Please correct the parameters.
|
||||
The `first_replaced_line_number` is 5 and the corresponding content is " b = 2".
|
||||
But the `first_replaced_line_content ` is "".
|
||||
The content around the specified line is:
|
||||
The 002 line is "def test_function_for_fm():"
|
||||
The 003 line is " "some docstring""
|
||||
|
|
@ -561,9 +561,9 @@ The 005 line is " b = 2"
|
|||
The 006 line is " c = 3"
|
||||
The 007 line is " # this is the 7th line"
|
||||
Pay attention to the new content. Ensure that it aligns with the new parameters.
|
||||
Error: The `last_replaced_replaced_line_number` does not match the `last_replaced_replaced_line_content`. Please correct the parameters.
|
||||
The `last_replaced_replaced_line_number` is 5 and the corresponding content is " b = 2".
|
||||
But the `last_replaced_replaced_line_content ` is "".
|
||||
Error: The `last_replaced_line_number` does not match the `last_replaced_line_content`. Please correct the parameters.
|
||||
The `last_replaced_line_number` is 5 and the corresponding content is " b = 2".
|
||||
But the `last_replaced_line_content ` is "".
|
||||
The content around the specified line is:
|
||||
The 002 line is "def test_function_for_fm():"
|
||||
The 003 line is " "some docstring""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue