TechY
OpenManus 오픈소스 훑어보기 본문
깃헙 링크 : https://github.com/mannaandpoem/OpenManus?tab=readme-ov-file
참고하면 좋을 이전 포스팅 : https://techy8855.tistory.com/68
manus ai 가 나왔지만, 아직은 초대된 자들만 사용할 수 있다. 비슷한 인터페이스로 OpenManus 가 나왔고, 엄청난 스타수와 함께 비상하고 있다. 한번 써봤는데, manus ai보다는 별로지만 썩 나쁘지는 않다. 한번 어떻게 만들었나 보자.
참고로 현재 single agent 버전이 main.py 에 있고, multi-agent 버전이 unstable 로 run_flow.py 에 있다. 이번 포스팅에서는 single agent 구현에 대해서만 훑어본다.
요약을 하자면, "ReAct 구조를 따르며, 주목할 부분은 browser-use 를 쓴다 정도이다."
manus.py
내부적으로 ToolCallAgent 를 상속하고는 별일을 하지 않는다. 참고할 부분은 manus agent 는 총 5개의 tool을 갖고 있으며 각각 python 실행, 웹서치, browser-use, local file save, terminate 이다.
class Manus(ToolCallAgent):
"""
A versatile general-purpose agent that uses planning to solve various tasks.
This agent extends PlanningAgent with a comprehensive set of tools and capabilities,
including Python execution, web browsing, file operations, and information retrieval
to handle a wide range of user requests.
"""
(...중략...)
available_tools: ToolCollection = Field(
default_factory=lambda: ToolCollection(
PythonExecute(), WebSearch(), BrowserUseTool(), FileSaver(), Terminate()
)
)
...
toolcall.py
이 부분에 핵심 로직이 다 들었다고 봐도 된다. 기본적인 구조는 ReAct 이다. ReAct 의 Reasoning 에 해당하는 부분이 아래의 think 함수이다. history message 가 context 로 들어가면서, 적합한 tool 을 선택하게 한다. llm.ask_tool() 함수에 들어가는 message 의 마지막에 self.next_step_prompt 가 들어가는데 이따가 나오겠지만, 우리가 갖고 있는 tool 이 무엇이 있으며 필요에 따라 선택하라는 instruction 이 담겨져 있다. act 함수에서는 llm 사용없이 실제 tool 이 작동한다.
class ToolCallAgent(ReActAgent):
"""Base agent class for handling tool/function calls with enhanced abstraction"""
... (중략)
async def think(self) -> bool:
"""Process current state and decide next actions using tools"""
if self.next_step_prompt:
user_msg = Message.user_message(self.next_step_prompt)
self.messages += [user_msg]
# Get response with tool options
response = await self.llm.ask_tool(
messages=self.messages,
system_msgs=[Message.system_message(self.system_prompt)]
if self.system_prompt
else None,
tools=self.available_tools.to_params(),
tool_choice=self.tool_choices,
)
self.tool_calls = response.tool_calls
... (중략)
async def act(self) -> str:
"""Execute tool calls and handle their results"""
if not self.tool_calls:
if self.tool_choices == ToolChoice.REQUIRED:
raise ValueError(TOOL_CALL_REQUIRED)
# Return last message content if no tool calls
return self.messages[-1].content or "No content or commands to execute"
results = []
for command in self.tool_calls:
result = await self.execute_tool(command)
... (중략)
async def execute_tool(self, command: ToolCall) -> str:
"""Execute a single tool call with robust error handling"""
if not command or not command.function or not command.function.name:
return "Error: Invalid command format"
name = command.function.name
if name not in self.available_tools.tool_map:
return f"Error: Unknown tool '{name}'"
try:
# Parse arguments
args = json.loads(command.function.arguments or "{}")
# Execute the tool
logger.info(f"🔧 Activating tool: '{name}'...")
result = await self.available_tools.execute(name=name, tool_input=args)
... (중략)
prompt.py
manus agent 가 사용하는 prompt 이다.
system prompt 에서 persona 를 주입하고, next_step_prompt 에서는 우리가 갖고 있는 available tools 에 대한 소개, 그리고 약간의 지시사항이 있다.
전반적으로 굉장히 심플하다.
SYSTEM_PROMPT = "You are OpenManus, an all-capable AI assistant, aimed at solving any task presented by the user. You have various tools at your disposal that you can call upon to efficiently complete complex requests. Whether it's programming, information retrieval, file processing, or web browsing, you can handle it all."
NEXT_STEP_PROMPT = """You can interact with the computer using PythonExecute, save important content and information files through FileSaver, open browsers with BrowserUseTool, and retrieve information using GoogleSearch.
PythonExecute: Execute Python code to interact with the computer system, data processing, automation tasks, etc.
FileSaver: Save files locally, such as txt, py, html, etc.
BrowserUseTool: Open, browse, and use web browsers.If you open a local HTML file, you must provide the absolute path to the file.
WebSearch: Perform web information retrieval
Terminate: End the current interaction when the task is complete or when you need additional information from the user. Use this tool to signal that you've finished addressing the user's request or need clarification before proceeding further.
Based on user needs, proactively select the most appropriate tool or combination of tools. For complex tasks, you can break down the problem and use different tools step by step to solve it. After using each tool, clearly explain the execution results and suggest the next steps.
Always maintain a helpful, informative tone throughout the interaction. If you encounter any limitations or need more details, clearly communicate this to the user before terminating.
"""
base.py
여기선 위의 toolcall 에서 각각 정의되었던, think() 와 act() 를 step() 으로 묶어서 여러번 진행하는 것이다.
async with self.state_context(AgentState.RUNNING):
while (
self.current_step < self.max_steps and self.state != AgentState.FINISHED
):
self.current_step += 1
logger.info(f"Executing step {self.current_step}/{self.max_steps}")
step_result = await self.step()
# Check for stuck state
if self.is_stuck():
self.handle_stuck_state()
results.append(f"Step {self.current_step}: {step_result}")
if self.current_step >= self.max_steps:
self.current_step = 0
self.state = AgentState.IDLE
results.append(f"Terminated: Reached max steps ({self.max_steps})")
ReAct 구조를 최근에 다뤄서 그런지 OpenManus 정도의 큰 프로젝트에서 사용되는 것이 신기했고, think() 라고 정의된 부분이 너무 간략하게 구현된 것이 의아했다. 해당 부분에 개선의 여지가 많지 않을까라는 생각도 든다. (reasoning model 을 사용하지 않은 것은 llm memory의 이슈일 수도 있겠다)
'[오픈 소스 공부]' 카테고리의 다른 글
| [오픈 소스 공부] babyagi 에 대해 알아보자 (1) | 2023.05.09 |
|---|