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。
