mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-06-11 15:15:18 +02:00
Update the prompt to include the editor and terminal state
This commit is contained in:
parent
7b11bfabc1
commit
10b55155de
6 changed files with 89 additions and 60 deletions
|
|
@ -5,7 +5,7 @@ You are an autonomous programmer
|
|||
|
||||
The special interface consists of a file editor that shows you 100 lines of a file at a time.
|
||||
|
||||
You can use any bash commands you want (e.g., find, grep, cat, ls, cd) by calling Bash.run.
|
||||
You can use any terminal commands you want (e.g., find, grep, cat, ls, cd) by calling Terminal.run_command.
|
||||
|
||||
You should carefully observe the behavior and results of the previous action, and avoid triggering repeated errors.
|
||||
|
||||
|
|
@ -23,9 +23,8 @@ Note:
|
|||
5. NOTE ABOUT THE EDIT COMMAND: Indentation really matters! When editing a file, make sure to insert appropriate indentation before each line! Ensuring the code adheres to PEP8 standards. If a edit command fails, you can try to edit the file again to correct the indentation, but don't repeat the same command without changes.
|
||||
6. YOU CAN ONLY ENTER ONE COMMAND AT A TIME and must wait for feedback, plan your commands carefully.
|
||||
7. To avoid syntax errors when editing files multiple times, consider opening the file to view the surrounding code related to the error line and make modifications based on this context.
|
||||
8. When using the Editor tool, remember it operates within a closed range. This is crucial to prevent accidental deletion of non-targeted code during code replacement.
|
||||
9. Ensure to observe the currently open file and the current working directory, which is displayed right after the open file. The open file might be in a different directory than the working directory. Remember, commands like 'create' open files and might alter the current open file.
|
||||
10. Effectively using Use search commands (`search_dir`, `search_file`, `find_file`) and navigation commands (`open_file`, `goto_line`) to locate and modify files efficiently. The Editor tool can fully satisfy the requirements. Follow these steps and considerations for optimal results:
|
||||
8. Ensure to observe the currently open file and the current working directory, which is displayed right after the open file. The open file might be in a different directory than the working directory. Remember, commands like 'create' open files and might alter the current open file.
|
||||
9. Effectively using Use search commands (`search_dir`, `search_file`, `find_file`) and navigation commands (`open_file`, `goto_line`) to locate and modify files efficiently. The Editor tool can fully satisfy the requirements. Follow these steps and considerations for optimal results:
|
||||
**General Search Guidelines:**
|
||||
- Ensure you are in the repository's root directory before starting your search.
|
||||
- Always double-check the current working directory and the currently open file to avoid confusion.
|
||||
|
|
@ -60,33 +59,34 @@ Note:
|
|||
- Use wildcard characters (`*`, `?`) in search terms to broaden or narrow down your search scope.
|
||||
- If search commands return too many results, refine your search criteria or use more specific terms.
|
||||
- If a search command fails, modify the search criteria and check for typos or incorrect paths, then try again.
|
||||
- Based on feedback of observation or bash command in trajectory to guide adjustments in your search strategy.
|
||||
- Based on feedback of observation or Terminal command in trajectory to guide adjustments in your search strategy.
|
||||
|
||||
11. If provided an issue link, you MUST go to the issue page using Browser tool to understand the issue before starting your fix.
|
||||
12. When the edit fails, try to enlarge the starting line.
|
||||
13. You must use the Editor.open_file command to open a file before using the Bash.run tool's edit command to modify it. When you open a file, any currently open file will be automatically closed.
|
||||
14. The Editor command can only be used once in a single response. If there are multiple places in the code that need modification, list all of them but only modify the first unmodified location.
|
||||
15. Remember, when you use Editor.insert_content_at_line or Editor.edit_file_by_replace, the line numbers will change after the operation. Therefore, if there are multiple operations, perform only the first operation in the current response, and defer the subsequent operations to the next turn.
|
||||
16. If you choose Editor.insert_content_at_line, you must ensure that there is no duplication between the inserted content and the original code. If there is overlap between the new code and the original code, use Editor.edit_file_by_replace instead.
|
||||
17. If you choose Editor.edit_file_by_replace, the original code that needs to be replaced must start at the beginning of the line and end at the end of the line
|
||||
10. When the edit fails, try to enlarge the starting line.
|
||||
11. You must use the Editor.open_file command to open a file before using the Editor tool's edit command to modify it. When you open a file, any currently open file will be automatically closed.
|
||||
12. Remember, when you use Editor.insert_content_at_line or Editor.edit_file_by_replace, the line numbers will change after the operation. Therefore, if there are multiple operations, perform only the first operation in the current response, and defer the subsequent operations to the next turn.
|
||||
13. If you choose Editor.insert_content_at_line, you must ensure that there is no duplication between the inserted content and the original code. If there is overlap between the new code and the original code, use Editor.edit_file_by_replace instead.
|
||||
14. If you choose Editor.edit_file_by_replace, the original code that needs to be replaced must start at the beginning of the line and end at the end of the line
|
||||
|
||||
18. When not specified, you should write files in a folder named "src". If you know the project path, then write in a "src" folder under the project path.
|
||||
19. When provided system design or project schedule, you MUST read them first before making a plan, then adhere to them in your implementation, especially in the programming language, package, or framework. You MUST implement all code files prescribed in the system design or project schedule. You can create a plan first with each task corresponding to implementing one code file.
|
||||
20. When planning, initially list the files for coding, then outline all coding and review tasks in your first response.
|
||||
21. If you plan to read a file, do not include other plans in the same response.
|
||||
22. Use Engineer2.write_new_code to create or modify a file. Write only one code file each time.
|
||||
23. When the requirement is simple, you don't need to create a plan, just do it right away.
|
||||
24. If the code exists, use the Bash.run tool's open and edit commands to modify it. Since it is not a new code, do not use write_new_code.
|
||||
25. Aways user absolute path as parameter. if no specific root path given, use "workspace/'project_name'" as default work space.
|
||||
15. When not specified, you should write files in a folder named "src". If you know the project path, then write in a "src" folder under the project path.
|
||||
16. When provided system design or project schedule, you MUST read them first before making a plan, then adhere to them in your implementation, especially in the programming language, package, or framework. You MUST implement all code files prescribed in the system design or project schedule. You can create a plan first with each task corresponding to implementing one code file.
|
||||
17. When planning, initially list the files for coding, then outline all coding and review tasks in your first response.
|
||||
18. If you plan to read a file, do not include other plans in the same response.
|
||||
19. Use Engineer2.write_new_code to create or modify a file. Write only one code file each time.
|
||||
20. When the requirement is simple, you don't need to create a plan, just do it right away.
|
||||
21. If the code exists, use the Editor tool's open and edit commands to modify it. Since it is not a new code, do not use write_new_code.
|
||||
22. Aways user absolute path as parameter. if no specific root path given, use "workspace/'project_name'" as default work space.
|
||||
"""
|
||||
|
||||
CURRENT_EDITOR_STATE = """
|
||||
# Output Next Step
|
||||
The current bash state is:
|
||||
The current editor state is:
|
||||
(Open file: {open_file})
|
||||
(Current directory: {working_dir})
|
||||
"""
|
||||
|
||||
CURRENT_TERMINAL_STATE = """
|
||||
The current terminal state is:
|
||||
(Current directory: {working_dir})
|
||||
"""
|
||||
ENGINEER2_INSTRUCTION = ROLE_INSTRUCTION + EXTRA_INSTRUCTION.strip()
|
||||
|
||||
WRITE_CODE_SYSTEM_PROMPT = """
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ Fifth, describe if you should terminate, you should use **end** command to termi
|
|||
REGENERATE_PROMPT = """
|
||||
Review and reflect on the history carefully, provide a different response.
|
||||
Describe if you should terminate using **end** command, or use **RoleZero.ask_human** to ask human for help, or try a different approach and output different commands. You are NOT allowed to provide the same commands again.
|
||||
ou should use "end" to stop when all tasks have been completed and the requirements are satisfied.
|
||||
You should use "end" to stop when all tasks have been completed and the requirements are satisfied.
|
||||
Your reflection, then the commands in a json array:
|
||||
"""
|
||||
ASK_HUMAN_COMMAND = """
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from metagpt.config2 import Config
|
||||
from metagpt.logs import logger
|
||||
|
||||
# from metagpt.actions.write_code_review import ValidateAndRewriteCode
|
||||
from metagpt.prompts.di.engineer2 import (
|
||||
CURRENT_EDITOR_STATE,
|
||||
CURRENT_TERMINAL_STATE,
|
||||
ENGINEER2_INSTRUCTION,
|
||||
WRITE_CODE_PROMPT,
|
||||
WRITE_CODE_SYSTEM_PROMPT,
|
||||
|
|
@ -18,7 +21,7 @@ from metagpt.roles.di.role_zero import RoleZero
|
|||
from metagpt.schema import Message, UserMessage
|
||||
from metagpt.strategy.experience_retriever import ENGINEER_EXAMPLE
|
||||
from metagpt.tools.libs.git import git_create_pull
|
||||
from metagpt.tools.libs.terminal import Bash, Terminal
|
||||
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
|
||||
|
|
@ -34,7 +37,7 @@ class Engineer2(RoleZero):
|
|||
CMD_PROMPT
|
||||
+ "\nWhen using the Editor tool, the command list must contain a single command. Because the command is mutually exclusive."
|
||||
)
|
||||
terminal: Terminal = Field(default_factory=Bash, exclude=True)
|
||||
terminal: Terminal = Field(default_factory=Terminal, exclude=True)
|
||||
|
||||
tools: list[str] = [
|
||||
"Plan",
|
||||
|
|
@ -58,16 +61,19 @@ class Engineer2(RoleZero):
|
|||
async def _format_instruction(self):
|
||||
"""
|
||||
Formats the instruction message for the Engineer2.
|
||||
Runs the "state" command in the terminal, parses its output as JSON,
|
||||
and uses it to format the `_instruction` template.
|
||||
Uses Editor's state to format the `_instruction` template.
|
||||
"""
|
||||
bash_working_dir = await self.terminal.run_command("pwd")
|
||||
bash_state = {"working_dir": bash_working_dir}
|
||||
editor_state = {"open_file": self.editor.current_file, "working_dir": self.editor.working_dir}
|
||||
self.cmd_prompt_current_state = CURRENT_EDITOR_STATE.format(**editor_state).strip()
|
||||
self.cmd_prompt_current_state = CURRENT_EDITOR_STATE.format(
|
||||
**editor_state
|
||||
).strip() + CURRENT_TERMINAL_STATE.format(**bash_state)
|
||||
|
||||
def _update_tool_execution(self):
|
||||
self.tool_execution_map.update(
|
||||
{
|
||||
"Bash.run": self.eval_terminal_run if self.run_eval else self.terminal.run,
|
||||
"Terminal.run_command": self.eval_terminal_run if self.run_eval else self.terminal.run_command,
|
||||
"git_create_pull": git_create_pull,
|
||||
"Engineer2.write_new_code": self.write_new_code,
|
||||
# "ValidateAndRewriteCode.run": validate.run,
|
||||
|
|
@ -86,7 +92,7 @@ class Engineer2(RoleZero):
|
|||
self._set_state(-1)
|
||||
command_output = "Current test case is finished."
|
||||
else:
|
||||
command_output = await self.terminal.run(cmd)
|
||||
command_output = await self.terminal.run_command(cmd)
|
||||
return command_output
|
||||
|
||||
async def _act(self) -> Message:
|
||||
|
|
@ -112,19 +118,19 @@ class Engineer2(RoleZero):
|
|||
"""
|
||||
Handles actions based on parsed commands.
|
||||
|
||||
Parses commands, checks for a "submit" action, and generates a patch using `git diff`.
|
||||
When detecting engineer2 at the final action round, the process will stop immediately.
|
||||
generates a patch using `git diff`.
|
||||
Stores the cleaned patch in `output_diff`. Logs any exceptions.
|
||||
|
||||
This function is specifically added for SWE bench evaluation.
|
||||
"""
|
||||
# If todo switches to None, it indicates that this is the final round of reactions, and the Engineer2 will stop. Use git diff to store any changes made.
|
||||
if not self.rc.todo:
|
||||
print("finish current task *******************************************************")
|
||||
from metagpt.tools.swe_agent_commands.swe_agent_utils import extract_patch
|
||||
|
||||
try:
|
||||
logger.info(await self.terminal.run("submit"))
|
||||
diff_output = await self.terminal.run("git diff --cached")
|
||||
logger.info(await self.submit())
|
||||
diff_output = await self.terminal.run_command("git diff --cached")
|
||||
clear_diff = extract_patch(diff_output)
|
||||
logger.info(f"Diff output: \n{clear_diff}")
|
||||
if clear_diff:
|
||||
|
|
@ -132,9 +138,22 @@ class Engineer2(RoleZero):
|
|||
except Exception as e:
|
||||
logger.error(f"Error during submission: {e}")
|
||||
|
||||
async def submit(self):
|
||||
if "SWE_CMD_WORK_DIR" not in os.environ:
|
||||
os.environ["SWE_CMD_WORK_DIR"] = str(Config.default().workspace.path)
|
||||
if os.path.exists(os.environ["SWE_CMD_WORK_DIR"] + "/test.patch"):
|
||||
await self.terminal.run_command('git apply -R < "$SWE_CMD_WORK_DIR/test.patch"')
|
||||
cmd = """
|
||||
git add -A
|
||||
echo "<<SUBMISSION START||"
|
||||
git diff --cached
|
||||
echo "||SUBMISSION DONE>>"
|
||||
"""
|
||||
diff_output = await self.terminal.run_command(cmd)
|
||||
return diff_output
|
||||
|
||||
async def write_new_code(self, path: str, instruction: str = "") -> str:
|
||||
"""Write a new code file.
|
||||
When used, make sure content arg contains the full content of the 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 "".
|
||||
|
|
|
|||
|
|
@ -917,19 +917,6 @@ I will use browser to review the detailed information of this issue in order to
|
|||
```
|
||||
|
||||
## example 5
|
||||
I need to locating the `openai_api.py` file, so I will search for the `openai_api.py` file.
|
||||
```json
|
||||
[
|
||||
{
|
||||
"command_name": "Bash.run",
|
||||
"args": {
|
||||
"cmd": "find_file 'openai_api.py'"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## example 6
|
||||
The target working directory is "/workspace/MetaGPT/provider/", but the current working directory is different. I will use the set_workdir command to change the working directory.
|
||||
```json
|
||||
[
|
||||
|
|
@ -942,6 +929,21 @@ The target working directory is "/workspace/MetaGPT/provider/", but the current
|
|||
]
|
||||
```
|
||||
|
||||
## example 6
|
||||
I need to locating the `openai_api.py` file, so I will search for the `openai_api.py` file.
|
||||
```json
|
||||
[
|
||||
{
|
||||
"command_name": "Editor.find_file",
|
||||
"args": {
|
||||
"file_name": "openai_api.py"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
|
||||
## example 7
|
||||
I have located the openai_api.py file. I want to edit this file, so I will open it first.
|
||||
```json
|
||||
|
|
@ -999,6 +1001,7 @@ Note that the edit command must be executed in a single response, so this step w
|
|||
]
|
||||
```
|
||||
|
||||
## example 11
|
||||
```
|
||||
#### Save the changes and commit them to the remote repository.
|
||||
|
||||
|
|
@ -1007,7 +1010,7 @@ Thought: All changes have been saved, let's push the code to the remote reposito
|
|||
```json
|
||||
[
|
||||
{
|
||||
"command_name": "Bash.run",
|
||||
"command_name": "Terminal.run_command",
|
||||
"args": {
|
||||
"cmd": "git push origin test-fix"
|
||||
}
|
||||
|
|
@ -1034,9 +1037,11 @@ Thought: Now that the changes have been pushed to the remote repository, due to
|
|||
}
|
||||
]
|
||||
```
|
||||
"""
|
||||
|
||||
"""
|
||||
## example 11
|
||||
I have finish all task, so I will use 'Plan.finish_current_task' and then fellowing the command "end" to stop.
|
||||
I have finish all the tasks, so I will use 'Plan.finish_current_task' and then fellowing the command "end" to stop.
|
||||
```json
|
||||
[
|
||||
{
|
||||
|
|
@ -1052,7 +1057,6 @@ I have finish all task, so I will use 'Plan.finish_current_task' and then fellow
|
|||
]
|
||||
```
|
||||
"""
|
||||
|
||||
WEB_SCRAPING_EXAMPLE = """
|
||||
## action 1
|
||||
User Requirement: Scrap and list the restaurant names of first page by searching for the keyword `beef` on the website https://www.yelp.com/.
|
||||
|
|
|
|||
|
|
@ -640,7 +640,7 @@ class Editor(BaseModel):
|
|||
|
||||
ret_str += "[This is how your edit would have looked if applied]\n"
|
||||
ret_str += "-------------------------------------------------\n"
|
||||
ret_str += self._print_window(file_name, show_line, editor_lines, return_str=True) + "\n"
|
||||
ret_str += self._print_window(file_name, show_line, editor_lines) + "\n"
|
||||
ret_str += "-------------------------------------------------\n\n"
|
||||
|
||||
ret_str += "[This is the original code before your edit]\n"
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@ from metagpt.config2 import Config
|
|||
from metagpt.const import DEFAULT_WORKSPACE_ROOT, METAGPT_ROOT
|
||||
from metagpt.logs import logger
|
||||
from metagpt.roles.di.engineer2 import Engineer2
|
||||
from metagpt.tools.libs.editor import Editor
|
||||
from metagpt.tools.libs.terminal import Terminal
|
||||
from metagpt.tools.swe_agent_commands.swe_agent_utils import load_hf_dataset
|
||||
|
||||
config = Config.default()
|
||||
# Specify by yourself
|
||||
Role = Engineer2
|
||||
MAX_MINUTES_PRE_INSTANCE = 20
|
||||
MAX_MINUTES_PRE_INSTANCE = 5
|
||||
TEST_REPO_DIR = METAGPT_ROOT / "data" / "test_repo"
|
||||
DATA_DIR = METAGPT_ROOT / "data/hugging_face"
|
||||
|
||||
|
|
@ -75,8 +76,10 @@ async def run(instance, swe_result_dir):
|
|||
clone_command = f"git clone 'https://github.com/{repo_identifier}.git' {repo_path}"
|
||||
checkout_command = f"cd {repo_path} && git checkout -f {base_commit}" if base_commit else ""
|
||||
await terminal.run_command(clone_command)
|
||||
ignore_temp_file_cmd = "echo '.backup.*' >> .gitignore"
|
||||
logger.info(await terminal.run_command(checkout_command))
|
||||
logger.info(await terminal.run_command("git branch"))
|
||||
await terminal.run_command(ignore_temp_file_cmd)
|
||||
|
||||
user_requirement_and_issue = INSTANCE_TEMPLATE.format(
|
||||
issue=instance["problem_statement"],
|
||||
|
|
@ -89,9 +92,10 @@ async def run(instance, swe_result_dir):
|
|||
logger.info(f"**** Starting to run {instance['instance_id']}****")
|
||||
logger.info("User Requirement", user_requirement_and_issue)
|
||||
try:
|
||||
role = Role(run_eval=True)
|
||||
role = Role(run_eval=True, editor=Editor(enable_auto_lint=True))
|
||||
await asyncio.wait_for(role.run(user_requirement_and_issue), timeout=MAX_MINUTES_PRE_INSTANCE * 60)
|
||||
except:
|
||||
except Exception as e:
|
||||
print(e)
|
||||
logger.info(f"**** exception lead to end: {instance['instance_id']}****")
|
||||
pass
|
||||
|
||||
|
|
@ -103,7 +107,7 @@ def save_predictions(role, instance, swe_result_dir):
|
|||
output_file = swe_result_dir / "all_preds.jsonl"
|
||||
instance["model_name_or_path"] = role.config.llm.model
|
||||
instance["model_patch"] = role.output_diff
|
||||
logger.info("model_patch", role.output_diff)
|
||||
logger.info("model_patch:" + role.output_diff)
|
||||
logger.info(f"Preparing to save predictions to {output_file}")
|
||||
|
||||
# Save the predictions to a JSONL file
|
||||
|
|
@ -122,12 +126,14 @@ async def async_main():
|
|||
|
||||
exp_name = f"nano_mgx_{date_time}_{_round}"
|
||||
|
||||
# now = datetime.now()
|
||||
# formatted_time = now.strftime("%Y_%m_%d_%H_%M_%S")
|
||||
now = datetime.now()
|
||||
formatted_time = now.strftime("%Y_%m_%d_%H_%M_%S")
|
||||
swe_result_dir = (
|
||||
DEFAULT_WORKSPACE_ROOT / f"result_{config.llm.model.replace('/', '_')}_start_time_{formatted_time}" / exp_name
|
||||
)
|
||||
# swe_result_dir = (
|
||||
# DEFAULT_WORKSPACE_ROOT / f"result_{config.llm.model.replace('/', '_')}_start_time_{formatted_time}" / exp_name
|
||||
# DEFAULT_WORKSPACE_ROOT / f"result_{config.llm.model.replace('/', '_')}" / exp_name
|
||||
# )
|
||||
swe_result_dir = DEFAULT_WORKSPACE_ROOT / f"result_{config.llm.model.replace('/', '_')}" / exp_name
|
||||
swe_result_dir.mkdir(parents=True, exist_ok=True)
|
||||
for index, instance in enumerate(dataset):
|
||||
# switch to a new logger file
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue