CodeActAgent 是 OpenHands 框架中的核心组件,旨在通过统一的代码操作空间(CodeAct)简化和增强 LLM(大语言模型)代理的功能。本文将详细解析 CodeActAgent 的组成、功能和运作方式,并结合一个具体案例(TODO 应用)展示其工作流程。
CodeActAgent 的核心组成
CodeActAgent 是一个高度模块化的代理,其核心组成部分包括 Profile、Prompt 和 Memory。这些模块共同协作,确保代理能够高效地分解任务、调用工具并与用户交互。
1. Profile
Profile 是什么
- Profile 是 CodeActAgent 的配置文件,定义了代理的行为、工具支持、内存管理等。
- 它通过
AgentConfig
类进行配置,包含以下关键参数:codeact_enable_browsing
:是否启用浏览器工具。codeact_enable_jupyter
:是否启用 IPython 工具。codeact_enable_llm_editor
:是否启用基于 LLM 的文件编辑工具。condenser
:事件压缩器的配置,用于优化内存中的历史记录。enable_prompt_extensions
:是否启用扩展提示(Prompt)。
Profile 的结构
Profile 是一个字典或对象,包含以下关键参数:
config = AgentConfig(
codeact_enable_browsing=True,
codeact_enable_jupyter=True,
codeact_enable_llm_editor=False,
condenser={"type": "default", "max_events": 100},
enable_prompt_extensions=True,
disabled_microagents=["npm", "github"],
)
Profile 的作用
- Profile 决定了 CodeActAgent 的能力范围和行为模式。
- 例如:
- 如果启用
codeact_enable_browsing
,代理可以使用浏览器工具与网页交互。 - 如果启用
codeact_enable_jupyter
,代理可以运行 Python 代码来完成任务。
- 如果启用
2. Prompt
Prompt 是什么
- Prompt 是 CodeActAgent 与 LLM 交互的输入内容,定义了任务的上下文、目标和约束。
- 它通过
PromptManager
动态生成,包含以下部分:- 系统消息:定义代理的角色和目标。
- 用户消息:用户输入的任务描述。
- 工具调用消息:代理调用工具的记录。
- 观察消息:工具返回的结果。
Prompt 的结构
Prompt 是一个消息列表,包含以下类型的消息:
系统消息:
System: You are CodeActAgent, a highly capable assistant designed to perform tasks by executing code and interacting with tools.
用户消息:
User: 创建一个 TODO 应用,支持添加、删除和查看任务。
工具调用消息:
Assistant: Calling tool: execute_ipython_cell Tool arguments: {"code": "tasks = []"}
观察消息:
Observation: Tool returned: "Initialized an empty task list."
Prompt 的生成逻辑
Prompt 的生成是 CodeActAgent 与 LLM 交互的关键步骤,以下是其详细逻辑:
系统消息初始化:
- CodeActAgent 使用
PromptManager
添加系统消息,定义代理的角色和目标。
- CodeActAgent 使用
用户消息处理:
- 用户输入的任务描述被添加到消息列表中。
工具调用与观察消息:
- CodeActAgent 从
ConversationMemory
中提取工具调用记录和观察结果,并将其添加到 Prompt。
- CodeActAgent 从
消息增强:
- 使用
PromptManager
为用户消息添加上下文信息(如示例或扩展信息)。
- 使用
3. Memory
Memory 的作用
Memory 是 CodeActAgent 的会话内存模块,负责记录代理的对话历史、工具调用记录以及观察结果。它通过 ConversationMemory
和 Condenser
协同工作,确保代理能够在多轮交互中保持上下文一致性。
Memory 的结构
Memory 包含以下几个关键部分:
- 事件历史:存储所有的动作和观察,完整记录代理的交互过程。
- 压缩历史:通过
Condenser
压缩后的关键事件,优化存储空间并提高检索效率。 - 缓存:用于特定 LLM 的提示缓存,减少重复计算并提高响应速度。
Memory 的工作流程
事件记录:
- 每次工具调用或用户交互都会生成一个事件,并存储到事件历史中。
- 示例:
event = {"type": "tool_call", "tool": "execute_ipython_cell", "result": "Initialized task list."} memory.add_event(event)
事件压缩:
- 使用
Condenser
对事件历史进行压缩,提取关键事件以减少冗余。 - 示例:
condensed_events = memory.condenser.condensed_history(state)
- 使用
上下文检索:
- 在生成 Prompt 时,Memory 提供上下文信息以确保对话的连贯性。
- 示例:
messages = memory.process_events(condensed_history=condensed_events)
在 TODO 应用中的作用
在实现 TODO 应用时,Memory 的主要作用包括:
- 记录任务分解过程:存储每个子任务的工具调用记录和结果。
- 维护上下文一致性:确保代理能够根据用户的后续输入调整任务。
- 优化交互效率:通过事件压缩减少冗余信息,提高响应速度。
Memory 是什么
- Memory 是代理的会话内存,用于存储对话历史、工具调用记录和观察结果。
- 它通过
ConversationMemory
管理,支持事件压缩和历史记录的动态更新。
Memory 的结构
Memory 包含以下部分:
- 事件历史:存储所有的动作和观察。
- 压缩历史:通过
Condenser
压缩后的关键事件。 - 缓存:用于特定 LLM 的提示缓存。
Memory 的作用
- Memory 记录了任务的分解过程和工具调用结果,确保代理能够在多轮对话中保持上下文一致。
结合案例:实现一个 TODO 应用
任务描述
用户输入任务:“创建一个 TODO 应用,支持添加、删除和查看任务。”
任务分解
CodeActAgent 的任务分解通过与 LLM 的交互实现,以下是具体流程:
用户输入任务:
- 用户输入任务描述:“创建一个 TODO 应用,支持添加、删除和查看任务。”
构建 Prompt:
- CodeActAgent 构建消息列表并发送给 LLM。
解析 LLM 响应:
- LLM 返回的响应被解析为一系列子任务:
- 初始化任务列表。
- 实现添加任务功能。
- 实现删除任务功能。
- 实现查看任务功能。
- LLM 返回的响应被解析为一系列子任务:
子任务分发
CodeActAgent 的子任务分发通过工具调用实现,以下是具体流程:
工具调用:
- 根据子任务调用相应的工具(如执行 Python 代码、编辑文件)。
- 示例:
action = CmdRunAction(command="tasks = []") result = action.execute()
结果存储:
- 工具返回的结果被存储为观察消息。
- 示例:
observation_message = Message(role="observation", content="Tool returned: \"Initialized an empty task list.\"") state.add_observation(observation_message)
反馈给用户:
- CodeActAgent 将工具调用结果反馈给用户。
- 示例:
final_message = Message(role="assistant", content="TODO 应用已创建,支持添加、删除和查看任务。") state.add_message(final_message)
完整实现示例
以下是一个完整的实现示例,展示如何使用 CodeActAgent 创建一个 TODO 应用:
from openhands.core.config import AgentConfig
from openhands.llm.llm import LLM
from openhands.agenthub.codeact_agent.codeact_agent import CodeActAgent
from openhands.controller.state.state import State
from openhands.core.message import Message
# 配置代理
config = AgentConfig(
codeact_enable_browsing=False,
codeact_enable_jupyter=True,
codeact_enable_llm_editor=False,
condenser={"type": "default", "max_events": 50},
enable_prompt_extensions=True,
disabled_microagents=[],
)
# 初始化 LLM 和代理
llm = LLM(model_name="gpt-4")
agent = CodeActAgent(llm=llm, config=config)
# 创建初始状态
state = State()
# 用户输入任务
user_message = Message(role="user", content="创建一个 TODO 应用,支持添加、删除和查看任务。")
state.add_message(user_message)
# 构建 Prompt 并与 LLM 交互
messages = agent._get_messages(state)
response = llm.completion(messages=agent.llm.format_messages_for_llm(messages))
# 解析 LLM 响应
actions = agent.tools.response_to_actions(response)
# 执行子任务
for action in actions:
result = action.execute()
observation_message = Message(role="observation", content=f"Tool returned: {result}")
state.add_observation(observation_message)
# 反馈结果
final_message = Message(role="assistant", content="TODO 应用已创建,支持添加、删除和查看任务。")
state.add_message(final_message)
# 输出对话历史
for msg in state.history:
print(f"{msg.role}: {msg.content}")
总结
通过上述分析和实现示例,我们可以清晰地了解 CodeActAgent 的 Prompt 和 Profile,以及它们在任务分解和子任务分发中的作用。您可以根据这些信息手动实现一个类似的代理,并根据任务需求动态生成 Prompt 和配置 Profile。