mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-06-08 15:05:17 +02:00
fix/insert_more_than_one_pre_rsp
This commit is contained in:
parent
4ddabd0f1b
commit
33b9c57756
5 changed files with 83 additions and 41 deletions
|
|
@ -5,7 +5,8 @@ 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 terminal commands you want (e.g., find, grep, cat, ls, cd) by calling Terminal.run_command.
|
||||
You can use terminal commands (e.g., cat, ls, cd) by calling Terminal.run_command.
|
||||
Do Not run the code.
|
||||
|
||||
You should carefully observe the behavior and results of the previous action, and avoid triggering repeated errors.
|
||||
|
||||
|
|
@ -63,7 +64,7 @@ Note:
|
|||
9. When the edit fails, try to enlarge the range of code.
|
||||
10. 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.
|
||||
11. 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.
|
||||
11.1 Using Editor.insert_content_at_line and Editor.edit_file_by_replace more than once in the current command list is forbidden.
|
||||
11.1 Do not use Editor.insert_content_at_line or Editor.edit_file_by_replace more than once per command list.
|
||||
12. 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.
|
||||
13. 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
|
||||
|
||||
|
|
@ -75,7 +76,7 @@ Note:
|
|||
19. When the requirement is simple, you don't need to create a plan, just do it right away.
|
||||
20. 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.
|
||||
21. Aways user absolute path as parameter. if no specific root path given, use "workspace/'project_name'" as default work space.
|
||||
22. Running the Python code in the terminal is strictly forbidden.
|
||||
22. Forbidden to run code in the terminal.
|
||||
"""
|
||||
ENGINEER2_CMD_PROMPT = (
|
||||
CMD_PROMPT
|
||||
|
|
|
|||
|
|
@ -77,6 +77,13 @@ class Engineer2(RoleZero):
|
|||
# "ValidateAndRewriteCode": validate.run,
|
||||
}
|
||||
)
|
||||
if self.run_eval:
|
||||
self.tool_execution_map.update(
|
||||
{
|
||||
"RoleZero.ask_human": self._end,
|
||||
"RoleZero.reply_to_human": self._end,
|
||||
}
|
||||
)
|
||||
|
||||
async def eval_terminal_run(self, cmd):
|
||||
"""change command pull/push/commit to end."""
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@ class RoleZero(Role):
|
|||
"Editor.insert_content_at_line",
|
||||
"Editor.append_file",
|
||||
]
|
||||
exclusive_command_enable_flag: bool = True
|
||||
# Equipped with three basic tools by default for optional use
|
||||
editor: Editor = Editor(enable_auto_lint=True)
|
||||
browser: Browser = Browser()
|
||||
|
|
@ -223,11 +222,8 @@ class RoleZero(Role):
|
|||
async with ThoughtReporter(enable_llm_stream=True) as reporter:
|
||||
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)
|
||||
|
||||
self.rc.memory.add(AIMessage(content=self.command_rsp))
|
||||
self.exclusive_command_enable_flag = True
|
||||
return True
|
||||
|
||||
@exp_cache(context_builder=RoleZeroContextBuilder(), serializer=RoleZeroSerializer())
|
||||
|
|
@ -267,7 +263,8 @@ class RoleZero(Role):
|
|||
if self.use_fixed_sop:
|
||||
return await super()._act()
|
||||
|
||||
commands, ok = await self._parse_commands(self.command_rsp)
|
||||
commands, ok, self.command_rsp = await self._parse_commands(self.command_rsp)
|
||||
self.rc.memory.add(AIMessage(content=self.command_rsp))
|
||||
if not ok:
|
||||
error_msg = commands
|
||||
self.rc.memory.add(UserMessage(content=error_msg))
|
||||
|
|
@ -415,12 +412,25 @@ class RoleZero(Role):
|
|||
tb = traceback.format_exc()
|
||||
print(tb)
|
||||
error_msg = str(e)
|
||||
return error_msg, False
|
||||
return error_msg, False, command_rsp
|
||||
|
||||
# 为了对LLM不按格式生成进行容错
|
||||
if isinstance(commands, dict):
|
||||
commands = commands["commands"] if "commands" in commands else [commands]
|
||||
return commands, True
|
||||
|
||||
# Set the exclusive command flag to False.
|
||||
command_flag = [command["command_name"] not in self.exclusive_tool_commands for command in commands]
|
||||
if command_flag.count(False) > 1:
|
||||
# Set the flag of the first exclusive command to True.
|
||||
index_of_first_exclusive = command_flag.index(False)
|
||||
command_flag[index_of_first_exclusive] = True
|
||||
# Select command which flag is True.
|
||||
commands = [commands[index] for index, flag in enumerate(command_flag) if flag is True]
|
||||
command_rsp = "```json\n" + json.dumps(commands, indent=4, ensure_ascii=False) + "\n```json"
|
||||
logger.info(
|
||||
"exclusive command more than one in current command list. change the command list.\n" + command_rsp
|
||||
)
|
||||
return commands, True, command_rsp
|
||||
|
||||
async def _run_commands(self, commands) -> str:
|
||||
outputs = []
|
||||
|
|
@ -455,7 +465,7 @@ class RoleZero(Role):
|
|||
return outputs
|
||||
|
||||
def _is_special_command(self, cmd) -> bool:
|
||||
return cmd["command_name"] in self.special_tool_commands or cmd["command_name"] in self.exclusive_tool_commands
|
||||
return cmd["command_name"] in self.special_tool_commands
|
||||
|
||||
async def _run_special_command(self, cmd) -> str:
|
||||
"""command requiring special check or parsing"""
|
||||
|
|
@ -464,7 +474,9 @@ class RoleZero(Role):
|
|||
if cmd["command_name"] == "Plan.finish_current_task":
|
||||
if not self.planner.plan.is_plan_finished():
|
||||
self.planner.plan.finish_current_task()
|
||||
command_output = "Current task is finished. If all tasks are finished, use 'end' to stop."
|
||||
command_output = (
|
||||
"Current task is finished. If you no longer need to take action, use the command ‘end’ to stop."
|
||||
)
|
||||
|
||||
elif cmd["command_name"] == "end":
|
||||
command_output = await self._end()
|
||||
|
|
@ -480,13 +492,6 @@ class RoleZero(Role):
|
|||
else:
|
||||
command_output += f"\n[command]: {cmd['args']['cmd']} \n[command output] : {tool_output}"
|
||||
|
||||
elif cmd["command_name"] in self.exclusive_tool_commands:
|
||||
if self.exclusive_command_enable_flag is True:
|
||||
tool_obj = self.tool_execution_map[cmd["command_name"]]
|
||||
command_output += tool_obj(**cmd["args"])
|
||||
else:
|
||||
command_output += "This command has not been executed."
|
||||
self.exclusive_command_enable_flag = False
|
||||
return command_output
|
||||
|
||||
def _get_plan_status(self) -> Tuple[str, str]:
|
||||
|
|
@ -532,12 +537,13 @@ class RoleZero(Role):
|
|||
from metagpt.environment.mgx.mgx_env import MGXEnv # avoid circular import
|
||||
|
||||
if not isinstance(self.rc.env, MGXEnv):
|
||||
return "Not in MGXEnv, command will not be executed."
|
||||
rsp = await self.rc.env.reply_to_human(content, sent_from=self)
|
||||
rsp += " If all tasks are finished, use 'end' to stop."
|
||||
return
|
||||
rsp = "Not in MGXEnv, command will not be executed."
|
||||
else:
|
||||
rsp = await self.rc.env.reply_to_human(content, sent_from=self)
|
||||
rsp += " If you no longer need to take action, use the command ‘end’ to stop."
|
||||
return rsp
|
||||
|
||||
async def _end(self):
|
||||
async def _end(self, **kwarg):
|
||||
self._set_state(-1)
|
||||
memory = self.rc.memory.get(self.memory_k)
|
||||
# Ensure reply to the human before the "end" command is executed. Hard code k=5 for checking.
|
||||
|
|
|
|||
|
|
@ -988,6 +988,24 @@ Note that the edit command must be executed in a single response, so this step w
|
|||
]
|
||||
```
|
||||
|
||||
## example 10.1
|
||||
To enhance the functionality of the 2048 game, including game end detection and score tracking, we need to add these features to the existing game_2048.py file. First, we will add a score tracking feature, and then we will insert game end detection logic into the game loop.
|
||||
We will use the Editor.insert_content_at_line command to insert new code into the file for adding score tracking and game end detection.
|
||||
Since Editor.insert_content_at_line can only be used once per response, this time I will use it to create the variable self.score
|
||||
```json
|
||||
[
|
||||
{
|
||||
"command_name": "Editor.insert_content_at_line",
|
||||
"args": {
|
||||
"file_name": "/home/mgx/mgx/MetaGPT/workspace/2048_game_py/game_2048.py",
|
||||
"line_number": 4,
|
||||
"content": " self.score = 0\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
In the next turn, I will try to add another code snippet
|
||||
|
||||
## example 11
|
||||
```
|
||||
#### Save the changes and commit them to the remote repository.
|
||||
|
|
@ -1028,14 +1046,9 @@ Thought: Now that the changes have been pushed to the remote repository, due to
|
|||
|
||||
"""
|
||||
## example 11
|
||||
I have finish all the tasks, so I will use 'Plan.finish_current_task' and then fellowing the command "end" to stop.
|
||||
I have finished all the tasks, so I will use Plan.finish_current_task and then follow the command ‘end’ to stop.
|
||||
```json
|
||||
[
|
||||
{
|
||||
"command_name": "Plan.finish_current_task",
|
||||
"args": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_name": "end",
|
||||
"args": {
|
||||
|
|
|
|||
|
|
@ -633,9 +633,11 @@ class Editor(BaseModel):
|
|||
first_error_line = None
|
||||
|
||||
if lint_error is not None:
|
||||
if first_error_line is not None:
|
||||
show_line = int(first_error_line)
|
||||
elif is_append:
|
||||
# if first_error_line is not None:
|
||||
# show_line = int(first_error_line)
|
||||
|
||||
# show the first insert line.
|
||||
if is_append:
|
||||
# original end-of-file
|
||||
show_line = len(lines)
|
||||
# insert OR edit WILL provide meaningful line numbers
|
||||
|
|
@ -666,7 +668,7 @@ class Editor(BaseModel):
|
|||
)
|
||||
ret_str += "-------------------------------------------------\n"
|
||||
|
||||
ret_str += "\n" + self.get_indentation_infromation(content, first_error_line)
|
||||
ret_str += self.get_indentation_infromation(content, start or len(lines))
|
||||
|
||||
ret_str += (
|
||||
"Your changes have NOT been applied. Please fix your edit command and try again.\n"
|
||||
|
|
@ -687,12 +689,14 @@ class Editor(BaseModel):
|
|||
except ValueError as e:
|
||||
ret_str += f"Invalid input: {e}\n"
|
||||
except Exception as e:
|
||||
error_str = ""
|
||||
if is_append:
|
||||
error_str += self.get_indentation_infromation(content, len(lines))
|
||||
else:
|
||||
# insert or replace
|
||||
error_str += self.get_indentation_infromation(content, start)
|
||||
error_str = "[This is how your edit would have looked if applied]\n"
|
||||
error_str += "-------------------------------------------------\n"
|
||||
error_str += self._print_window(file_name, start or len(lines), 40) + "\n"
|
||||
error_str += "-------------------------------------------------\n"
|
||||
error_str += self.get_indentation_infromation(content, start or len(lines))
|
||||
if not is_insert and not is_append:
|
||||
error_str += "enlarge the range of original code."
|
||||
error_str += "\nTry to enlarge the range of the orginal code"
|
||||
# Clean up the temporary file if an error occurs
|
||||
with original_file_backup_path.open() as fin, file_name.open("w") as fout:
|
||||
fout.write(fin.read())
|
||||
|
|
@ -720,7 +724,8 @@ class Editor(BaseModel):
|
|||
return ret_str
|
||||
|
||||
def edit_file_by_replace(self, file_name: str, to_replace: str, new_content: str) -> str:
|
||||
"""Edit a file. This will search for `to_replace` in the given file and replace it with `new_content`.
|
||||
"""
|
||||
Edit a file. This will search for `to_replace` in the given file and replace it with `new_content`.
|
||||
|
||||
Every *to_replace* must *EXACTLY MATCH* the existing source code, character for character, including all comments, docstrings, etc.
|
||||
|
||||
|
|
@ -764,6 +769,10 @@ class Editor(BaseModel):
|
|||
file_name: str: The name of the file to edit.
|
||||
to_replace: str: The content to search for and replace.
|
||||
new_content: str: The new content to replace the old content with.
|
||||
|
||||
NOTE:
|
||||
This tool is exclusive. If you use this tool, you cannot use any other commands in the current response.
|
||||
If you need to use it multiple times, wait for the next turn.
|
||||
"""
|
||||
# FIXME: support replacing *all* occurrences
|
||||
if to_replace.strip() == "":
|
||||
|
|
@ -839,6 +848,9 @@ class Editor(BaseModel):
|
|||
file_name: str: The name of the file to edit.
|
||||
line_number: int: The line number (starting from 1) to insert the content after.
|
||||
content: str: The content to insert.
|
||||
NOTE:
|
||||
This tool is exclusive. If you use this tool, you cannot use any other commands in the current response.
|
||||
If you need to use it multiple times, wait for the next turn.
|
||||
"""
|
||||
file_name = self._try_fix_path(file_name)
|
||||
|
||||
|
|
@ -859,6 +871,9 @@ class Editor(BaseModel):
|
|||
Args:
|
||||
file_name: str: The name of the file to edit.
|
||||
content: str: The content to insert.
|
||||
NOTE:
|
||||
This tool is exclusive. If you use this tool, you cannot use any other commands in the current response.
|
||||
If you need to use it multiple times, wait for the next turn.
|
||||
"""
|
||||
file_name = self._try_fix_path(file_name)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue