Bootstrap

agenthub\codeact_agent\action_parser.py

OpenDevin/OpenDevin: 🐚 

action_parser.py

import re

from opendevin.controller.action_parser import ActionParser, ResponseParser
from opendevin.events.action import (
    Action,
    AgentDelegateAction,
    AgentFinishAction,
    CmdRunAction,
    IPythonRunCellAction,
    MessageAction,
)


class CodeActResponseParser(ResponseParser):
    """
    Parser action:
        - CmdRunAction(command) - bash command to run
        - IPythonRunCellAction(code) - IPython code to run
        - AgentDelegateAction(agent, inputs) - delegate action for (sub)task
        - MessageAction(content) - Message action to run (e.g. ask for clarification)
        - AgentFinishAction() - end the interaction
    """

    def __init__(self):
        # Need pay attention to the item order in self.action_parsers
        super().__init__()
        self.action_parsers = [
            CodeActActionParserFinish(),
            CodeActActionParserCmdRun(),
            CodeActActionParserIPythonRunCell(),
            CodeActActionParserAgentDelegate(),
        ]
        self.default_parser = CodeActActionParserMessage()

    def parse(self, response) -> Action:
        action_str = self.parse_response(response)
        return self.parse_action(action_str)

    def parse_response(self, response) -> str:
        action = response.choices[0].message.content
        for lang in ['bash', 'ipython', 'browse']:
            if f'<execute_{lang}>' in action and f'</execute_{lang}>' not in action:
                action += f'</execute_{lang}>'
        return action

    def parse_action(self, action_str: str) -> Action:
        for action_parser in self.action_parsers:
            if action_parser.check_condition(action_str):
                return action_parser.parse(action_str)
        return self.default_parser.parse(action_str)


class CodeActActionParserFinish(ActionParser):
    """
    Parser action:
        - AgentFinishAction() - end the interaction
    """

    def __init__(
        self,
    ):
        self.finish_command = None

    def check_condition(self, action_str: str) -> bool:
        self.finish_command = re.search(r'<finish>.*</finish>', action_str, re.DOTALL)
        return self.finish_command is not None

    def parse(self, action_str: str) -> Action:
        assert (
            self.finish_command is not None
        ), 'self.finish_command should not be None when parse is called'
        thought = action_str.replace(self.finish_command.group(0), '').strip()
        return AgentFinishAction(thought=thought)


class CodeActActionParserCmdRun(ActionParser):
    """
    Parser action:
        - CmdRunAction(command) - bash command to run
        - AgentFinishAction() - end the interaction
    """

    def __init__(
        self,
    ):
        self.bash_command = None

    def check_condition(self, action_str: str) -> bool:
        self.bash_command = re.search(
            r'<execute_bash>(.*?)</execute_bash>', action_str, re.DOTALL
        )
        return self.bash_command is not None

    def parse(self, action_str: str) -> Action:
        assert (
            self.bash_command is not None
        ), 'self.bash_command should not be None when parse is called'
        thought = action_str.replace(self.bash_command.group(0), '').strip()
        # a command was found
        command_group = self.bash_command.group(1).strip()
        if command_group.strip() == 'exit':
            return AgentFinishAction()
        return CmdRunAction(command=command_group, thought=thought)


class CodeActActionParserIPythonRunCell(ActionParser):
    """
    Parser action:
        - IPythonRunCellAction(code) - IPython code to run
    """

    def __init__(
        self,
    ):
        self.python_code = None
        self.jupyter_kernel_init_code: str = 'from agentskills import *'

    def check_condition(self, action_str: str) -> bool:
        self.python_code = re.search(
            r'<execute_ipython>(.*?)</execute_ipython>', action_str, re.DOTALL
        )
        return self.python_code is not None

    def parse(self, action_str: str) -> Action:
        assert (
            self.python_code is not None
        ), 'self.python_code should not be None when parse is called'
        code_group = self.python_code.group(1).strip()
        thought = action_str.replace(self.python_code.group(0), '').strip()
        return IPythonRunCellAction(
            code=code_group,
            thought=thought,
            kernel_init_code=self.jupyter_kernel_init_code,
        )


class CodeActActionParserAgentDelegate(ActionParser):
    """
    Parser action:
        - AgentDelegateAction(agent, inputs) - delegate action for (sub)task
    """

    def __init__(
        self,
    ):
        self.agent_delegate = None

    def check_condition(self, action_str: str) -> bool:
        self.agent_delegate = re.search(
            r'<execute_browse>(.*)</execute_browse>', action_str, re.DOTALL
        )
        return self.agent_delegate is not None

    def parse(self, action_str: str) -> Action:
        assert (
            self.agent_delegate is not None
        ), 'self.agent_delegate should not be None when parse is called'
        thought = action_str.replace(self.agent_delegate.group(0), '').strip()
        browse_actions = self.agent_delegate.group(1).strip()
        task = f'{thought}. I should start with: {browse_actions}'
        return AgentDelegateAction(agent='BrowsingAgent', inputs={'task': task})


class CodeActActionParserMessage(ActionParser):
    """
    Parser action:
        - MessageAction(content) - Message action to run (e.g. ask for clarification)
    """

    def __init__(
        self,
    ):
        pass

    def check_condition(self, action_str: str) -> bool:
        # We assume the LLM is GOOD enough that when it returns pure natural language
        # it wants to talk to the user
        return True

    def parse(self, action_str: str) -> Action:
        return MessageAction(content=action_str, wait_for_response=True)

GPT-4o (OpenAI) 

这段代码实现了一个复杂的解析器系统,用于解析响应(例如,从LLM生成的响应)并将其转换为具体的动作(action)。这些动作可能包括运行命令、执行IPython代码、委托任务给其他代理,或发送消息进行澄清。以下是对代码的详细讲解:
文件结构
1. Imports:
    - 引入必要的模块和类。
    - 这些模块包括正则表达式(`re`)、自定义的 ActionParser 和 ResponseParser 类,以及具体的动作类(`Action` 类及其子类)。
2. 顶层类 CodeActResponseParser:
    - 这个类继承自 ResponseParser,并负责整个解析流程。它包含几个具体的解析器(用来解析不同类型的动作)。
CodeActResponseParser 类
- 构造函数 __init__:
    - 初始化不同的动作解析器,并设置默认的消息解析器。
- 方法 parse:
    - 负责解析响应,将响应转换为动作。
    - 调用了 parse_response 方法解析响应,再调用 parse_action 方法解析动作。
- 方法 parse_response:
    - 从响应中提取文本信息,并检查是否缺少特定标签(如 </execute_bash>)。如果缺少,则进行补充。
- 方法 parse_action:
    - 依次尝试不同的动作解析器,直到找到合适的解析器并返回相应动作。如果没有找到合适的解析器,则使用默认消息解析器。
具体的动作解析器
1. CodeActActionParserFinish:
    - 用于解析 <finish> 标签,指示交互结束的动作。
    - 匹配 <finish> 标签并提取相应的内容,返回 AgentFinishAction 动作。
2. CodeActActionParserCmdRun:
    - 用于解析 <execute_bash> 标签,即执行 bash 命令的动作。
    - 提取标签内的 bash 命令,并返回 CmdRunAction 动作。如果命令是 exit,则返回 AgentFinishAction 动作。
3. CodeActActionParserIPythonRunCell:
    - 用于解析 <execute_ipython> 标签,即执行 IPython 代码的动作。
    - 提取标签内的 IPython 代码,并返回 IPythonRunCellAction 动作,包含内核初始化代码。
4. CodeActActionParserAgentDelegate:
    - 用于解析 <execute_browse> 标签,即委托任务给其他代理的动作。
    - 提取标签内的浏览任务,并返回 AgentDelegateAction 动作,指定委托的代理及其任务。
5. CodeActActionParserMessage:
    - 用于解析默认情况下的消息交互,即纯自然语言的响应。
    - 将响应内容包装为 MessageAction 动作,等待用户进一步响应。
使用示例
假设我们有一段响应内容像这样:

<execute_bash>ls -al</execute_bash>

通过 CodeActResponseParser 解析,我们会得到一个 CmdRunAction 动作,其 command 属性为 ls -al。这个动作随后可以由相应的执行器来执行。
总结
这个代码实现的解析器系统可以从复杂的响应中提取不同类型的动作,允许系统根据这些动作进行具体操作,如运行命令、执行代码、委派任务或发送消息。这种设计具有很强的扩展性和灵活性,能够适应多种不同的操作需求。 

DeepSeek

这段代码定义了一个用于解析和处理不同类型动作的系统,主要用于处理从某种响应中提取的动作指令。以下是对代码的详细讲解:

主要类和功能

  1. CodeActResponseParser

    • 这是一个主要的解析器类,负责解析响应并将其转换为具体的动作对象。

    • 它包含多个子解析器(CodeActActionParserFinishCodeActActionParserCmdRunCodeActActionParserIPythonRunCellCodeActActionParserAgentDelegate)和一个默认解析器(CodeActActionParserMessage)。

    • parse_response 方法从响应中提取动作字符串,并确保所有动作标签都被正确关闭。

    • parse_action 方法根据动作字符串选择合适的子解析器进行解析。

  2. CodeActActionParserFinish

    • 用于解析结束交互的动作。

    • 通过正则表达式检查是否包含 <finish> 标签,并返回 AgentFinishAction 对象。

  3. CodeActActionParserCmdRun

    • 用于解析执行 Bash 命令的动作。

    • 通过正则表达式检查是否包含 <execute_bash> 标签,并返回 CmdRunAction 对象。如果命令是 exit,则返回 AgentFinishAction

  4. CodeActActionParserIPythonRunCell

    • 用于解析执行 IPython 代码的动作。

    • 通过正则表达式检查是否包含 <execute_ipython> 标签,并返回 IPythonRunCellAction 对象。

  5. CodeActActionParserAgentDelegate

    • 用于解析委托给其他代理的动作。

    • 通过正则表达式检查是否包含 <execute_browse> 标签,并返回 AgentDelegateAction 对象。

  6. CodeActActionParserMessage

    • 默认解析器,用于解析纯文本消息。

    • 总是返回 MessageAction 对象,表示需要等待用户响应。

代码逻辑和结构

  • 初始化和配置

    • CodeActResponseParser 初始化时配置了多个子解析器和一个默认解析器。

    • 每个子解析器负责检查特定类型的动作标签,并解析相应的动作。

  • 解析流程

    • parse_response 方法首先从响应中提取动作字符串,并确保所有动作标签都被正确关闭。

    • parse_action 方法遍历所有子解析器,检查哪个解析器能够处理当前的动作字符串,并调用其 parse 方法返回具体的动作对象。

  • 错误处理

    • 每个子解析器的 parse 方法都包含断言,确保在调用 parse 方法时,相关的属性(如 self.bash_command)已经被正确设置。

总结

这段代码实现了一个灵活的动作解析系统,能够根据不同的动作标签解析并执行相应的动作。通过使用多个子解析器和一个默认解析器,系统能够处理多种类型的动作,并确保在解析过程中的一致性和正确性。

;