Open Manus 로컬 및 Docker 사용

징니징징·2025년 4월 11일

2025년 3월 6일 중국의 Monica에서 manus AI를 출시함
완전 자율형 인공지능 에이전트로서 단순한 챗봇을 넘어, 복잡한 작업을 독립적으로 계획하고 실행하는 존재

ReAct 기반으로 동작

  • ReAct 프레임워크(Reasoning + Acting)를 기반으로 하여,
    "생각(Thought)" → "행동(Action)" → "관찰 결과(Observation)"의 반복 과정을 통해 도구들을 필요에 따라 골라서 실행

환경 세팅

Method 1 : 로컬 Anaconda 환경 설정

[conda 가상환경 생성]
conda create -n open_manus python=3.12
conda activate open_manus

[git clone]
git clone https://github.com/mannaandpoem/OpenManus.git
cd OpenManus

[dependencies 설치]
pip install -r requirements.txt

Method 2 : Docker 환경 설정

[install uv]
curl -LsSf https://astral.sh/uv/install.sh | sh

[git clone]
git clone https://github.com/mannaandpoem/OpenManus.git
cd OpenManus

[venv 가상환경 세팅]
uv venv --python 3.12
source .venv/bin/activate  # On Unix/macOS
# Or on Windows:
# .venv\Scripts\activate

[dependencies 설치]
uv pip install -r requirements.txt


playwright install (option)
  • uv
    • Rust로 만든 최신 툴
    • 가상환경 생성, 패키지 설치/관리 (pip + venv + Poetry)
  • venv
    • python 기본 도구
    • python 가상환경 생성 (.venv/ 폴더 생성)
cp config/config.example.toml config/config.toml

이후 변경 및 실행

  • toml 파일 복사 : 해당 파일에 사용할 api key를 넣어줄 수 있음 (gpt-4o/gemini 2.0 flash 사용)

  • vs code 에서 docker 컨테이너로 작업 중 headless를 True로 바꿔야 웹 서칭이 가능해졌음

    headless False > True
  • vs code 상에서 실행 시 웹 브라우저 검색의 과정을 실시간으로 확인할 수 없어 아쉬움이 있었음

  • 그래서 로컬 가상환경에서 실행한 결과, 첫번째 step에서 웹 브라우징 창을 띄우며 조사하고 그에 대한 결과를 출력해주었음

  • 이후 사용자 입력 프롬프트에 대해 웹 브라우징 및 deep_searching 등의 tool을 사용하지 않고, LLM의 응답만을 출력하도록 명시했을 때,

  • Gemini API만 사용하여 응답하는 것을 확인했음

OpenManus 코드 구조

  • 터미널에서 python main.py로 실행하므로 main.py 파일부터 올라가봄

OpenManus

  • main.py
    • Manus 클래스 인스턴스를 생성, agent로 할당, 이 객체가 사용자 프롬프트를 받아서 행동을 수행
    • 사용자 입력값만 받아서 app/agent/manus.py의 Manus 클래스로 비동기 통신
    • main.py의 실질적 실행 로직은 우선 manus.py 파일로 가야 하는 것
  • app/agent/manus.py
    • Pydantic 모델과 ToolcallAgent 를 상속
    • OpenManus 에이전트의 핵심 기능(기억, 추론, 툴 실행 등)이 ToolCallAgent에 구현되어있음 (ToolcallAgent > toolcall.py 파일에 정의됨)
    • 에이전트가 사용할 수 있는 툴을 정의
    • 최근 메세지 3개를 추출하여 그 안에 Browser_use_tool을 사용했다면, 브라우저 컨텍스트 도우미를 사용하여 브라우저 상태를 반영한 새로운 프롬프트를 구성 ex) next_step_prompt :: “검색 결과 ~~ 결과가 있어. 이제 어떤 도구를 사용해서 무슨 작업을 진행할까?”
    • super().think() 호출하여 상위 ToolCallAgent의 추론 로직을 사용 (새로운 프롬프트를 통해 LLM에게 다음에 어떤 과정을 진행할지 답변을 받아오는 과정) ex) next_step_prompt > GPT > 이번엔 ~ 웹 페이지로 들어가서 웹 브라우징을 해보자.
    • 이후 next_step_prompt는 원래의 프롬프트로 복원시킴 (추후 혼란 방지를 위해)
  • app/agent/toolcall.py
    • ReActAgent를 상속 → LLM이 생각하고 행동하는 구조기반
    • 실제 행동(추론)을 하는 부분 → manus.py는 이 추론을 위해 방향성을 잡아주는 부분인 것
    • 실행단
      • 하단의 run(request)에서 상휘 에이전트의 run()을 실행
      • 마지막에 cleanup()을 사용해서 자원 정리
    • 추론단
      • LLM에게 어떤 도구를 사용할지 물어보고, 응답에 따라 도구 호출 목록(tool_calls) 또는 텍스트 응답(content)을 저장
      • next_step_prompt 있다면 메세지 스택에 추가
      • llm.ask_tool()로 LLM 호출
      • LLM 응답에 따라 도구면 tool_calls, 텍스트면 content에 저장
    • 개별 도구 실행단
      • execute_tool(command) : 도구 이름과 인자를 파싱하고 해당 도구 개체를 호출하여 실행
      • available_tools_execute(name, tool_input) 호출 : 결과를 observation 형태로 정리
      • specialtool(terminate)이면 _handle**special_tool()호출 → 상태 종료 → 결과 반환
  • 도구들은 어디있나?
    • 도구 정의 : app/tool/*.py
    • 도구 수집 : ToolCollection class
    • 실행 호출 : available_tools.execute() 이름에 맞는 툴을 찾아 실행
    • 입력값 : LLM이 호출한 도구의 arguments를 dict로 전달
    • 실행 메서드 : call() 또는 run()
    • 출력값 : ToolResult / ToolFailure
    • → 모든 도구는 baseTool 인터페이스를 상속해야만 ToolCollection으로 등록될 수 있음
  • app/sandbox
    • 도구가 직접 파일을 만지지 않고 sandbox를 우회해서 접근하도록 함

예시 상황
"웹 사이트 검색을 통해서 딥러닝이 어떤 건지 정리하고 txt 파일로 저장해줘”

  1. prompt = "웹 사이트 검색을 통해서 딥러닝이 어떤 건지 정리하고 txt 파일로 저장해줘”
  2. main.py → Manus.run(prompt)
    • 해당 프롬프트는 Manus.next_step_prompt 로 들어가게 됨
  3. Manus.think()
    # 현재 prompt를 메시지로 추가
        selt.message += [Message.user_message(self.next_step_prompt)]
    • system_prompt : “너는 도구를 사용할 수 있는 AI 에이전트야. 필요한 경우 도구를 써”
    • next_step_prompt : 사용자 프롬프트 적용
  4. ToolCallAgent.think() → llm.ask_tool()
    - LLM에게 다음 질문을 함
    “너는 Manus야. 지금 사용자 요청은 이거야. 사용할 수 있는 도구는 이것들이야. 다음에 뭐 할래?”
    tools: [BrowserUserTool, StrReplaceEditor, PythonExecute, Terminate]
     tool_choice : AUTO
  5. 여기서 LLM이 판단
    • 문장에 “웹 사이트 검색” → BrowserUserTool 호출
    • 문장에 “정리” → 내부 요약
    • 문장에 “txt 파일로 저장” → StrReplaceEditor 호출
      • 즉 LLM은 아래와 같이 tool_call 구조로 응답

        {
          "tool_calls": [
            {
              "name": "BrowserUseTool",
              "arguments": { "query": "딥러닝이란" }
            }
          ]
        }
  6. act()
    • act()가 이 도구를 찾아서 실제로 실행함
    • 결과를 memory에 저장
  7. 다시 think() 반복
    • “이제 웹 검색 결과가 있어. 이젠 뭘 할래?”
    • LLM이 이번엔 “StrReplaceEditor”를 호출해서 결과를 파일에 저장

MCP 파일의 역할?

  • MCP는 외부 서버에 존재하는 도구들을 OpenManus 내부의 에이전트가 “실시간으로 연결해서 사용하는 인터페이스”
  • 이를 통해 OpenManus는 자기 내부에 없는 기능도 외부 MCP 서버를 통해 확장할 수 있게 됨
  1. app/tool/mcp.py

    • 외부 MCP 서버와의 연동을 통해 “원격 툴”을 실행할 수 있도록 도와주는 도구 통합 클래스
    • mcp.py는 MCP 서버에 연결해 원격 도구(tool)를 로딩하고, 해당 도구들을 마치 로컬 도구처럼 사용할 수 있게 래핑하는 역할
    • 구성 요소
      • MCPClientTool
        • MCP 서버에 정의된 원격 도구를 나타냄
        • execute() 시 실제로는 MCP 서버로 요청을 보내고 결과를 받아오는 프록시 역할
        • 내부적으로 self.session.call_tool() 호출
      • MCPClients (ToolCollection 상속)
        • 여러 MCP 원격 도구들을 관리
        • connect_sse() 또는 connect_stdio()로 MCP 서버와 연결
        • session.list_tools()로 원격 툴 목록 조회 → MCPClientTool 객체로 변환 → tool_map에 저장
        • 도구 목록을 정리해 OpenManus에서 쓸 수 있게 등록
  2. app/agent/mcp.py

    • MCPAgent
      • MCP 서버에 연결된 상태에서 도구를 사용하는 에이전트
  3. app/prompt/mcp.py
    - LLM이 MCP 도구를 적절히 사용할 수 있게 지침 제공
    - SYSTEM_PROMPT
    - NEXT_STEP_PROMPT
    - TOOL_ERROR_PROMPT …

    MCP 작동 흐름 (Manus 에이전트 관점)

    [1] MCPAgent 생성

    [2] MCPAgent.initalize() 호출

    → MCPClients.connect_sse() 또는 connect_stdio()

    → MCP 서버에 연결

    → list_tools() → 사용 가능한 도구 목록 수신

    → MCPClientTool 인스턴스화 → Toolcollection으로 등록

    [3] run(request) 호출

    → BaseAgent.run() → step() → think() → act()

    → LLM에게 SYSTEM_PROMPT+ nexy_step_prompt + tools 전달

    → LLM이 tool_call 반환 → MCPClientTool.execute() 실행

    [4] 결과 수신

    → base64 이미지 포함 시 MULTIMEDIA_PROMPT 추가

    → terminate 호출 시 종료

profile
공부로그

0개의 댓글