2026년 5월 24일 일요일

OpenUSD 그래프 구조, 객체지향 메커니즘 및 실제 포맷 예시

이 글은 OpenUSD의 그래프 구조, 객체지향 특성, 실행 가능 스크립트 및 실제 포맷 형식에 대한 조사 내용을 요약 정리한다.

OpenUSD는 단순한 static 3D 데이터 포맷이 아니다. 계층적 씬 그래프 구조 위에 객체지향 데이터 모델과 동작 메커니즘까지 포함 가능한 확장형 포맷이다.

USD Scene Graph 계층구조 및 References 인스턴싱 개념도

USD의 계층적 씬 그래프 구조

USD의 기본 데이터 모델은 씬 그래프 구조를 바탕으로 작동한다.

1) Stage와 Prim 그래프 계층

  • Stage: 씬 전체 데이터를 관리하는 그래프의 루트 컨테이너이다.
  • Prim: 그래프를 구성하는 개별 노드이다. 자식 Prim을 가질 수 있는 트리 구조 형태를 띤다.
  • Relationship 및 Attribute: 노드 간의 논리적 연결선 역할을 수행한다. 하나의 Prim이 다른 Prim의 재질, 물리 속성, 센서 데이터를 참조할 때 DAG 그래프 형태로 확장된다.

2) 객체지향(OOP) 스키마 구조 지원

USD는 객체지향 프로그래밍의 클래스, 상속, 캡슐화 개념을 데이터 모델 수준에서 지원한다.

  • Typed Schema: C++ 및 Python 클래스에 1:1로 매핑되는 스키마이다. 상속 구조를 가지며 예로 UsdGeomGprim 클래스를 상속받아 UsdGeomMesh가 정의된다.
  • API Schema: 기존 Prim에 특정 기능만 추가하는 다중 상속 형태의 스키마이다. 예로 일반 Mesh Prim에 UsdPhysicsRigidBodyAPI를 적용하여 물리 속성을 가미한다.
  • Class Prim과 Inherits: USD 파일 내부에서 class 키워드로 추상 클래스를 정의하고 inherits 속성으로 이를 상속받아 유산 속성을 재사용한다.

파이썬 스크립트 및 동작 메커니즘 포함 기능

USD 포맷은 형상, 재질, 물리, 센서 데이터 같은 정적 속성 외에 가동형 로직 및 스크립트 실행 메커니즘도 포함한다.

1) OmniActionGraph 및 Scripting Node

Omniverse 환경과 결합된 USD 포맷은 ActionGraph 노드 내부에 파이썬 스크립트를 직접 저장할 수 있다. 시뮬레이션 프레임마다 파이썬 메서드가 호출되어 로봇 제어 및 이벤트 처리가 구동된다.

2) AssetInfo 및 Custom Data 기반 바인딩

Prim 내부에 customData나 파이썬 실행 모듈 경로를 메타데이터로 매핑할 수 있다. 로드 시점에 파이썬 로직을 바인딩하여 실행하는 동적 객체 구조 구현이 가능하다.

USD 그래프 구조 실제 형식 예시

텍스트 포맷인 USDA 형식의 실제 예시이다. 객체 상속, 물리 속성 적용, 센서 설정 및 로직 매핑 구조를 확인할 수 있다.

#usda 1.0

# 1. 추상 클래스 정의 (Class Prim)
class "_BaseRobot"
{
    color3f[] primvars:displayColor = [(0.2, 0.2, 0.2)]
    float physics:mass = 10.0
}

# 2. 상속 및 객체 생성 (Inherits & Typed Schema)
def Xform "SpotRobot" (
    inherits = </_BaseRobot>
)
{
    # API Schema를 통한 물리 속성 다중 상속
    prepend apiSchemas = ["PhysicsRigidBodyAPI"]

    # 3. 자식 Prim 구성 (Mesh 형상)
    def Mesh "Body"
    {
        float3[] points = [(-1, -1, 0), (1, -1, 0), (1, 1, 0), (-1, 1, 0)]
        int[] faceVertexCounts = [4]
        int[] faceVertexIndices = [0, 1, 2, 3]
    }

    # 4. 센서 데이터 포함 (Camera Sensor)
    def Camera "FrontCamera"
    {
        float focalLength = 24.0
        float2 clippingRange = (0.1, 100.0)
    }

    # 5. 파이썬 노드 및 동작 로직 포함 예시 (OmniActionGraph)
    def Omnigraph "RobotControlGraph"
    {
        def OmnigraphNode "PythonController"
        {
            token nodeType = "omni.graph.action.PythonScript"
            string inputs:script = "def compute(db):\n    # 로봇 제어 파이썬 메서드\n    pass"
        }
    }
}

마무리

USD는 단순 데이터 저장 포맷이 아닌 객체지향적 스키마와 계층적 DAG 그래프를 결합한 포맷이다.
형상과 재질 외에도 물리, 센서, 파이썬 기반 제어 로직까지 단일 씬 그래프 안에 포함할 수 있다.
클래스 상속 및 지연 로딩 메커니즘을 바탕으로 복잡한 피지컬 AI 및 시뮬레이션 환경의 표준 데이터 모델 역할을 수행한다.

2026년 5월 22일 금요일

무료 토큰 바이브 코딩 도구 Continue 설치 및 사용기

이 글은 무료 토큰 바이브 코딩 도구 Continue 설치 및 사용기를 간단히 나눔한다.

이외, 오픈소스 AI 에이전트에 대해서는 다음 링크를 참고한다.
개요
Continue는 개발 환경(IDE)에 인공지능을 통합하여 코드 자동 완성, 리팩토링, 채팅 등을 제공하는 오픈소스 AI 코딩 어시스턴트(일명 바이브 코딩 도구)이다.
설치
VS Code 환경에서 Continue와 로컬 모델인 Ollama를 연동하는 설치 과정은 다음과 같다.

1.Ollama 설치 및 모델 다운로드
Ollama 공식 홈페이지에서 운영체제에 맞는 프로그램을 설치한다. 설치 완료 후 터미널(명렬 프롬프트)을 열고 코딩에 최적화된 로컬 모델을 다운로드한다. 예를 들어 라마3 기반의 코딩 모델을 받으려면 코드 모델인 ollama run qwen2.5-coder 혹은 ollama run llama3 등을 실행해 로컬에 다운로드 받아둔다.

2.IDE에서 Continue 확장 프로그램 설치
VS Code 에디터를 열고 확장 프로그램 마켓플레이스(Extensions)로 이동한다. 검색창에 Continue를 입력한 후 공식 플러그인을 찾아 설치한다. 설치가 완료되면 에디터 측면 바에 Continue 아이콘이 생성된다.

3.Continue 설정 파일(config.json) 수정
Continue 아이콘을 클릭한 뒤 하단의 설정(톱니바퀴) 버튼을 눌러 config.json 파일을 연다. 

models 배열 항목에 방금 Ollama로 다운로드한 로컬 모델 정보를 다음과 같이 추가하고 저장한다.
name: Local Config
version: 1.0.0
schema: v1
models:
  - name: Autodetect
    provider: ollama
    model: AUTODETECT

4.연동 확인 및 테스트
Continue 채팅창 상단의 모델 선택 드롭다운 메뉴에서 등록한 로컬 모델(예: Ollama qwen2.5-coder)을 선택한다. 질문을 입력하여 로컬 환경에서 답변이 정상적으로 출력되는지 확인한다.

사용기
다음과 같이 프롬프트를 입력해 테트리스를 개발해 본다.

can you make tetris game by using pygame?

결과는 다음과 같다. 

실행하자마자 빠져나오는 에러가 있어 해당 부분을 수정해달라고 했다. 그 결과 다음과 같이 동작되는 코드를 얻을 수 있었다. 무료 토큰 오픈웨이트 모델을 사용해 얻은 결과 치고는 괜찬아 보인다. 
 

이제 PRD를 다음과 같이 작성해 코딩을 시켜본다. 
# [PRD] Pygame 기반 테트리스(Tetris) 개발 요구사항 정의서

## 1. 개요

본 프로젝트는 Python의 `pygame` 라이브러리를 활용하여 로컬 환경에서 실행 가능한 고전 테트리스 게임을 개발하는 것이다. LLM(qwen2.5-coder)이 단번에 구조화된 객체 지향 코드를 생성할 수 있도록 게임 규칙, 시스템 아키텍처, 데이터 구조를 명확히 정의하는 것을 목적으로 한다.

## 2. 게임 명세 및 규칙 (Game Specifications)

* **그리드(Grid) 크기:** 가로 10칸 $\times$ 세로 20칸이다.
* **블록(Tetromino) 종류:** I, J, L, O, S, T, Z 총 7가지 형태이다. 각 블록은 고유의 색상을 가진다.
* **게임 루프 메커니즘:**
* 일정 시간(FPS 및 속도 레벨에 비례)마다 블록이 한 칸씩 자동으로 하강한다.
* 가로 한 줄이 빈칸 없이 블록으로 가득 차면 해당 줄이 소거되고 점수가 누적된다.
* 새로 생성된 블록이 배치될 공간이 없거나, 블록이 화면 최상단(세로 0번 인덱스)을 넘어서면 게임 오버(Game Over)이다.

## 3. 기술 스택 및 아키텍처 구조

* **언어 및 라이브러리:** `Python 3.x`, `pygame` 이다.
* **설계 패턴:** 관리가 용이하도록 **객체 지향 프로그래밍(OOP)** 구조로 클래스를 분리하여 구현한다.

### 3.1 핵심 클래스 구조 (Class Architecture)

```
[Main Loop (main.py)]
       │
       ▼
┌──────────────────────────────────────────────┐
│                 TetrisGame                   │ (게임 상태, 점수, 루프 관리)
└──────┬────────────────────────────────┬──────┘
       │                                │
       ▼                                ▼
┌─────────────────────┐        ┌─────────────────────┐
│      Piece          │        │      GameBoard      │
│ (블록 형태, 좌표, 회전) │        │ (그리드 표현, 줄소거) │
└─────────────────────┘        └─────────────────────┘

```

#### ① `Piece` 클래스

* **역할:** 현재 조종 중인 테트로미노 블록 하나를 표현한다.
* **필수 변수:**
* `x`, `y`: 그리드 상의 현재 좌표 (정수형) 이다.
* `shape`: 블록의 형태를 나타내는 2차원 리스트(행렬)이다.
* `color`: RGB 튜플 형태의 블록 색상 정보이다.

* **필수 메서드:**
* `rotate()`: 블록 행렬을 시계 방향으로 90도 회전시킨다. (단, 회전 후 그리드 경계를 벗어나거나 기존 블록과 겹치면 회전을 취소해야 한다.)

#### ② `GameBoard` 클래스

* **역할:** 10 $\times$ 20 그리드의 상태를 관리하고 쌓인 블록들을 기록한다.
* **필수 변수:**
* `grid`: 10 $\times$ 20 크기의 2차원 리스트이다. 빈 공간은 `(0,0,0)` 또는 `None`으로, 블록이 찬 곳은 해당 블록의 RGB 색상 값으로 채워진다.

* **필수 메서드:**
* `check_collision(piece, offset_x, offset_y)`: 입력받은 `piece`가 지정된 오프셋만큼 이동하거나 회전했을 때, 벽면에 부딪히거나 바닥 혹은 기존에 쌓인 블록과 충돌하는지 여부를 검사하여 Boolean 값(`True`/`False`)을 반환한다.
* `lock_piece(piece)`: 바닥에 닿은 블록을 `grid` 데이터에 영구적으로 고정시킨다.
* `clear_lines()`: 가로 줄이 꽉 찼는지 검사하고, 채워진 줄을 삭제한 뒤 상단의 블록들을 아래로 내린다. 지워진 줄의 개수를 반환한다.

#### ③ `TetrisGame` 클래스 (또는 메인 제어 루프)

* **역할:** Pygame 초기화, 화면 렌더링, 키보드 이벤트 처리, 전체 게임 상태(점수, 게임오버 여부)를 총괄한다.
* **키 입력 이벤트 명세:**
* `KEY_LEFT` / `KEY_RIGHT`: 블록을 좌우로 1칸 이동한다.
* `KEY_DOWN`: 소프트 드롭(Soft Drop)으로 하강 속도를 가속한다.
* `KEY_UP`: 블록을 시계방향으로 회전한다.
* `KEY_SPACE`: 하드 드롭(Hard Drop)으로 블록을 즉시 바닥으로 떨어뜨리고 고정한다.

## 4. UI 및 그래픽 화면 구성 요구사항

* **화면 해상도:** 최소 $width = 400px$, $height = 500px$ 이상으로 설정한다.
* **레이아웃 분할:**
* **메인 게임 보드 영역:** 가로 10칸 $\times$ 세로 20칸 영역을 격자 모양으로 화면 중앙 혹은 좌측에 배치한다. (예: 한 칸당 $30px \times 30px$ 크기)
* **사이드 바 영역:** 현재 점수(Score)와 게임 오버 메시지를 텍스트로 표시한다.


개발을 시켜보았더니, 단순히 지시한 거보다는 품질이 더 좋아보이는 게임화면이 표시된다. 

사용 후 장단점 
실제 개발 업무에서 Ollama와 Continue 조합을 사용해 보면 장단점이 명확하게 체감된다.

가장 큰 만족감을 주는 부분은 비용이다. 아무리 긴 코드 컨텍스트(문맥)를 집어넣고 대화를 나눠도 비용이 0원이며, 토큰 제한으로 인한 결제 압박이 없다. 내부 소스코드가 외부 클라우드로 전송되지 않아 보안 규정이 엄격한 프로젝트에서도 안심하고 사용할 수 있다.

사용 편의성은 깃허브 코파일럿(GitHub Copilot) 못지않게 뛰어나다. 드래그한 코드 영역을 바로 프롬프트에 넣거나(Ctrl + L), 인라인으로 코드를 바로 수정하는 기능(Ctrl + I)이 매끄럽게 작동한다. 

다만 AI의 답변 속도, 성능, 퀄리티는 온전히 개인 PC의 하드웨어 사양(특히 GPU VRAM 용량)에 비례한다. 사양이 낮은 컴퓨터에서는 답변 속도가 느려 답답할 수 있으며, 가벼운 파라미터(Parameter)의 로컬 모델을 쓸 경우 복잡한 아키텍처 설계나 고난도 코드 로직 구현 시 상용 모델(GPT-4o, Claude 등)보다 정확도가 떨어진다. 

마무리
Continue와 Ollama의 조합은 나만의 비용 없는 보안 코딩 비서를 만들 수 있는 최적의 솔루션이다. 상용 서비스 수준의 추론 성능을 확보하려면 고사양의 그래픽 카드가 필요하다는 숙제가 있지만, 간단한 함수 작성, 코드 리팩토링, 주석 생성 및 단순 반복 작업에서는 로컬 모델만으로도 충분히 돈값 그 이상의 생산성을 뽑아낼 수 있다. 오픈소스 생태계와 로컬 LLM의 발전 속도가 매우 빠르기 때문에, 앞으로 비용 절감과 보안을 동시에 잡으려는 개인 개발자와 기업들에게 Continue는 좋은 선택지로 자리 잡을 것으로 생각된다.

레퍼런스

2026년 5월 9일 토요일

헤르메스 에이전트 개발배경, 설치 및 사용방법

이 글은 헤르메스 에이전트 개발 배경, 설치, 사용방법 등을 나눔한다.


오픈클로는 다음 링크를 참고한다.

개요
헤르메스는 서버리스로 자체 로컬 컴퓨터에 설치할 수 있는 에이전트이다. 스스로 발전할 수 있도록 구현되어 있어, 지식을 계속 축척하고 이를 재사용할 수 있다. 헤르메스 프로젝트는 중앙 집중형 AI의 한계를 극복하기 위해 NOUS RESEARCH - Open Source AI에 의해 개발되었다. 누스 리서치는 특정 기업의 이익보다 오픈소스 AI 모델의 성능 향상과 보급을 목적으로 하는 연구 커뮤니티이자 조직이다. 이들은 이미 AI 오픈소스 프로젝트를 통해 세계 AI 개발자들 사이에서 신뢰를 쌓아왔다. 특정 기업에 종속된 클라우드 기반 AI와 달리, 사용자의 로컬 환경에서 독립적으로 작동하는 주권적 AI(Sovereign AI)구현을 목표로 한다. 개발팀은 사용자가 자신의 하드웨어 자원을 활용해 보안 걱정 없이 고성능 AI 비서를 운용할 수 있는 생태계를 구축하고자 이 에이전트를 세상에 내놓았다.

헤르메스 에이전트의 공식 정보와 최신 소스 코드는 공식 깃허브(GitHub) 저장소 및 프로젝트 웹사이트를 통해 제공된다. 개발자들과의 원활한 소통 및 기술 지원은 주로 디스코드(Discord) 커뮤니티나 관련 포럼을 통해 이루어지며, 이곳에서 사용자는 최신 업데이트 소식과 문제 해결 방법을 공유받을 수 있다.

헤르메스 에이전트는 현재 매우 빠른 속도로 발전 중이다. 초기에는 단순한 텍스트 기반 응답에 집중했으나, 현재는 도구 사용(Tool Use), 웹 브라우징, 그리고 장기 기억(Long-term Memory) 기능을 통합하여 복잡한 태스크를 수행할 수 있는 수준에 도달했다. 특히 오픈소스 커뮤니티의 활발한 참여로 인해 다양한 로컬 LLM(대규모 언어 모델)과의 호환성이 강화되고 있으며, 최적화 작업을 통해 사양이 낮은 개인용 PC에서도 원활하게 구동될 수 있도록 경량화가 진행되고 있다.

설치 방법
설치는 리눅스에서 다음과 같이 실행한다.
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash


만약 이전에 Open claw 에이전트를 설치했다면, 해당 설정을 임포트할 것인지 다음과 같이 질문한다. 본인은 오픈 클로 설치할때 사용할 LLM모델 등을 미리 설정하였기 때문에, 이 설정 정보를 그대로 가져오기로 했다. 

이제 헤르메스 설정이 진행된다. 본인의 경우, 신속 설정 모드를 선택하였다. 

대부분 디폴트값으로 설정한다. 그럼, 다음과 같이 마지막에 설정 수정 명령과 함께 에이전트 실행할 지 물어본다. 엔터를 눌러 실행한다. 

그럼, 다음과 같은 화면을 볼 수 있을 것이다.

다시 Ctrl+D 를 눌러 헤르메스 에이전트를 빠져나온다. 
LLM 모델을 다시 OpenAI Codex로 변경하기 위해 다음 명령을 실행한다.
hermes setup model

화면에서 모델로 OpenAI codex 를 선택하면, 설정 단계를 안내한다. 그대로 따라해 본다. 

앞의 코덱스 로그인 때 적색 표시로 코덱스 장치 코드 인증 활성화하라는 메시지가 뜰 수 있다. 다음과 같이 오픈AI의 설정 해당 메뉴를 찾아, 그 옵션을 다음 화면과 같이 활성화한다.

이제 다시 해당 코덱스 링크를 방문하면, 다음과 같은 화면이 나타날 것이다. 헤르메스에서 알려준 코드 를 입력한다. 

이제 헤르메스 모델 설정 터미널 화면에서 다음과 같이 로긴되고 모델 설정 옵션 입력을 받을 것이다. 적절한 모델을 설정한다. 그리고 다시 헤르메스 에이전트를 실행한다.
hermes

헤르메스에 다음과 같이 입력한다.
hi

그럼, 해당 모델을 호출해 다음 화면과 같이 인시할 것이다. 

헤르메스 에이전트 사용하기
정상적으로 설정되었으니, 헤르메스가 무엇을 할 수 있는 지 다음과 같이 물어보자. 

너는 현재 이 컴퓨터에서 무엇을 할 수 있니?

그럼, 다음과 같이 답변할 것이다. 

다음 명령을 입력해 본다. 

컴퓨터 현재 상태 간단히 점검해줘.

그럼, 헤르메스가 내 컴퓨터 정보를 읽고 다음과 같이 알려줄 것이다. 

다음과 같이 현재 웹 정보도 검색 가능한지 확인해 보자. 

현재 전쟁과 주식에 관련된 소식을 웹 검색해바

그럼, 헤르메스가 다음과 같이 관련 소식을 검색하기 위한 파이썬 코드를 생성, 실행한 후, 관련 정보를 얻는다. 얻은 정보는 설정된 언어모델을 통해 질문에 대한 답변을 추론해 보여줄 것이다. 

바이브 코딩도 간단히 해보자. 

코사인 파형이 시간에 따라 흐르는 시뮬레이션을 하는 프로그램 개발해

그럼, 다음과 같이 코딩하고, 프로그램을 헤르메스가 실행된 폴더에 저장한다. 

실행하라고 명령을 줘본다.

실행을 했는데, 코사인 파형이 애니메이션되지 않는다. 제대로 파형이 안보이니 문제 원인을 수정해 달라고한다. 그럼, 다음과 같이 코드를 읽고 수정해 줄것이다.

이제 다시 실행해 본다. 그럼, 다음과 같이 프로그램이 실행될 것이다. 

이제, 오픈 클로에서 입력햇던 나의 아이덴티티 관련 질문을 해본다. 

내 이름이 머지?

다음과 같이 잘 기억하고 있는 것을 알 수 있다.

앞에서 검색했던 뉴스도 한번 더 보여달라고 해보자. 과거에 명령 조사했던 내용을 기억하고 있는 것을 알 수 있다. 

이외에 주기적으로 서버 관리용 리포트를 생성하는 등의 일들도 시켜본다. 

헤르메스는 여러 터미널에 에이전트를 실행해고, 서로 협업하게도 할 수 있다. 이런 궁금한 것은 헤르메스에게 물어보면 다음과 같이 실행 순서를 알려준다. 그래서 별도 메뉴얼을 공부할 필요가 없다. 

참고로 헤르메스가 알려준 tmux 도구는 터미널 멀티플렉서로 터미널 내에서 여러 터미널 세션을 만들어 특정 프로그램들을 실행하고 창을 닫아도 해당 프로그램 실행 세션이 살아있게 해준다. 다음과 같이 tmux를 설치해 달라고 해보자. 그럼 sudo 암호를 달라고 한 후 apt get install 명령으로 설치해준다. 이런 방식으로 헤르메스에 도움을 요청하면 여러 도구와 명령들을 일일히 기억하지 않아도 되어 참 편리하다.

해르메스 사용 중에 축척한 지식은 어디에 저장되는 지도 확인해 보자. 

넌 내가 너를 통해 알아낸 지식을 어디에 어떤 방식으로 저장하니?

다음과 같이 메모리 메커니즘을 잘 설명해 줄 것이다.

멀티 에이전트 실행하기
다양한 멀티 에이전트로 실행하기 위해서 다음 명령을 입력한다. 
hermes kanban init
hermes kanban stats

헤르메스 도구를 활성화한다. 
hermes tools

다음 명령을 입력해 본다. 
 tmux, kanban 설치 다함. 이제 특정 웹서비스를 개발하기 위한 각 에이전트 멀티로 만들수 있도록 
각 단계별 알려줘. 요구사항 조사, 설계, 개발, 테스트는 각각 영어이름으로 에이전트 만들어. 하나 
에이전트가 끝나면, 다른 에이전트가 자동으로 실행될 수 있도록도 설정해줘.

그럼, 헤르메스가 이를 고려해 명령들을 실행하는 배치파일(쉘 스크립트)를 자동으로 생성해준다. 

이외에도 헤르메스가 할 수 있는 것들은 다음과 같다.

- 개발
      - 코드 작성/수정, 버그 디버깅, 테스트 자동화, 성능 점검
    - 시스템 작업
      - 프로세스/포트/메모리/디스크 점검, 로그 분석, 서버 실행
    - 데이터 작업
      - CSV/JSON 분석, 리포트 요약, 그래프 생성
    - 웹 리서치
      - 최신 뉴스/기술 문서/가격 정보 검색 후 핵심 요약
    - 문서 업무
      - 보고서/메일/발표자료 초안 작성, 한국어/영어 다듬기
    - 자동화
      - 반복 작업 스크립트화, 정기 실행(크론) 구성
    - 이미지/음성
      - 이미지 생성, 텍스트 음성 변환(TTS)
    - 외부 도구 연동
      - GitHub, Notion, Google Workspace, 메신저 전송 등(연결된 경우)

- 기타 기능    
   - 시스템 상태 리포트 자동 생성
   - 웹 뉴스 요약 스크립트
   - 폴더 정리 자동화 스크립트

이제, ctrl+D를 눌러 헤르메스를 종료한다. 그럼 다음과 같이 이번 헤르메스 사용시 기록한 내용을 세션으로 저장해 보여주고, 사용된 시간, 호출한 도구 개수 등을 요약해 보여줄 것이다.

마무리
최근, 오픈 클로, 헤르메스 에이전트와 같은 멀티 에이전트 시스템이 크게 발전하고 있다. 다양한 모델과 스킬을 입맛에 맞게 사용할 수 있고, 컴퓨터 정리, 개발, 테스트, 서버 모니터링 등 다양한 목적으로 사용할 수 있다. 올라마 같은 로컬 모델 서빙 도구를 설치하면, 클로드나 제미니 같은 상업묭 모델 유료 토큰 사용하지 않고 무료로 로컬에서 실행되는 에이전트를 만들 수 있다. 좀 더 상세한 내용은 레퍼런스를 참고한다.


레퍼런스
부록: 직접 설치 후 텔레그램, LLM & VLM 연결, 앱개발 테스트 결과
테스트 결과는 다음과 같다.

개발된 테양계 시뮬레이션
LLM, VLM 테스트 화면

1. 사전 준비

Hermes를 설정하기 전에 Ollama가 떠 있고 응답하는지 먼저 확인한다.

curl -s http://127.0.0.1:11434/v1/models | python3 -m json.tool | head
nvidia-smi --query-gpu=index,name,memory.total,power.limit --format=csv

GPU 전력 제한은 드라이버 수준에서 걸리므로 컨테이너 안에서도 그대로 적용된다. 장시간 작업 전에 확인한다.

nvidia-smi --query-gpu=index,power.limit,power.max_limit --format=csv


2. Hermes 설치

설치 스크립트는 실행 전에 직접 읽어본다. 이 스크립트는 ~/.hermes 밖에는 쓰지 않고, uv를 SHA256 검증과 함께 고정 버전으로 받으며, libatomic.so.1 이 이미 있으면 sudo 분기를 건너뛴다.

curl -fsSL https://hermes-agent.nousresearch.com/install.sh -o /tmp/hermes-install.sh
less /tmp/hermes-install.sh        # 실행 전에 읽는다
bash /tmp/hermes-install.sh

hermes --version

실행 파일은 ~/.local/bin/hermes 에, 소스는 ~/.hermes/hermes-agent 에 설치된다.

외부 API 호출 없이 동작만 확인하는 테스트:

hermes -z "Reply with exactly the word READY and nothing else." --safe-mode


3. 모델 설치와 선택

3.1 측정한 모델 비교

동일한 과제를 동일한 자동 채점 스크립트로 평가했다. 사람의 판단은 개입하지 않았다. 과제는 Flask와 Three.js로 태양계 시뮬레이터를 만드는 것이고, 요구사항은 여섯 가지였다. 파일 구성, 포트 지정, 살아 있는 CDN 주소, 매 프레임 애니메이션, 파이썬 API에서 받아오는 데이터, 서버 자동 실행 금지.

모델

크기

소요

점수

실패 내용

qwen3-coder:30b-a3b-q8_0

32 GB

39초

6/6

없음

gpt-oss:120b (온도 조정판)

65 GB

수 분

5/6

존재하지 않는 CDN 주소를 지어냄

devstral:24b

14 GB

54초

0/6

도구를 부르지 않고 글로 적음

·         파일 크기는 성능이 아니다. 두 모델 모두 전문가 혼합 구조이다. gpt-oss:120b 은 1168억 파라미터를 저장하지만 토큰당 128개 전문가 중 4개만 쓴다. 활성 파라미터는 약 51억이다. qwen3-coder:30b 은 128개 중 8개를 쓰고 활성 파라미터가 약 33억이다. 디스크는 3.6배인데 토큰당 연산은 약 1.5배다.

·         devstral 은 OpenHands 프레임워크 전용으로 학습되었다. 모델 시스템 프롬프트에 그렇게 적혀 있다. Hermes의 도구 호출 규약을 따르지 않으므로 이 조합에서는 쓸 수 없다.


3.2 설치

ollama pull qwen3-coder:30b-a3b-q8_0     # 코딩 모델, 32 GB
ollama pull qwen3-vl:32b-instruct        # 비전 모델, 20 GB
ollama list


3.3 Hermes에 연결

hermes config set model.provider ollama
hermes config set model.base_url http://127.0.0.1:11434/v1
hermes config set model.default  qwen3-coder:30b-a3b-q8_0

Hermes는 매 요청에 reasoning_effort 를 보낸다. 이를 지원하지 않는 모델은 400 오류로 거절하고, Hermes는 턴마다 API 호출 하나를 복구에 낭비한다. 오류 문구는 다음과 같다.

"<모델명>" does not support thinking

모델별 예외를 넣어야 한다. 키에 콜론이 들어가므로 hermes config set 으로는 쓸 수 없고 ~/.hermes/config.yaml 을 직접 편집한다.

agent:
  reasoning_overrides:
    "qwen3-coder:30b-a3b-q8_0": "none"

먼저 지원 여부를 확인한다.

ollama show qwen3-coder:30b-a3b-q8_0 | sed -n "/Capabilities/,/Parameters/p"
# completion, tools            -> 예외 필요
# completion, tools, thinking  -> 예외 불필요


3.4 코드 생성용 샘플링 조정

Hermes는 temperature, top_p, top_k 를 보내지 않는다. 따라서 모델 자체의 기본값이 그대로 쓰인다. 실제 요청을 가로채어 확인한 내용이다.

model             qwen3-coder:30b-a3b-q8_0
options           {"num_ctx": 131072}
reasoning_effort  medium
tools             19
temperature       없음

모델을 탓하기 전에 출고 기본값부터 비교한다.

ollama show <모델명> | sed -n "/Parameters/,/License/p"

qwen3-coder 는 temperature 0.7, top_p 0.8, top_k 20, repeat_penalty 1.05 로 출고된다. 이미 코드용으로 조여져 있다. 반면 gpt-oss 는 temperature 1 만 설정되어 있어 코드 생성에는 너무 높다. 다시 내려받지 않고 기존 가중치를 재사용하여 변형을 만들 수 있다.

cat > Modelfile.code <<EOF
FROM gpt-oss:120b
PARAMETER temperature 0.4
PARAMETER top_p 0.9
PARAMETER top_k 40
PARAMETER repeat_penalty 1.05
EOF
ollama create gpt-oss:120b-code -f Modelfile.code   # 블롭 재사용, 추가 용량 없음


4. 터미널 백엔드

두 가지 선택지가 있다. 이것이 보안 경계이므로 의식적으로 골라야 한다.

항목

도커 샌드박스

로컬

CPU

1코어 (기본값)

8코어 전부

메모리

5 GB (기본값)

62 GB

포트

공개되지 않음. 내부 웹앱에 호스트에서 접속 불가

제약 없음

conda 환경

사용 불가

사용 가능

파일 접근 범위

마운트한 디렉터리만

사용자가 읽을 수 있는 모든 것

 

4.1 방법 A: 도커 샌드박스

hermes config set terminal.backend docker
hermes config set terminal.docker_image nousresearch/hermes-sandbox:desktop
hermes config set terminal.docker_mount_cwd_to_workspace true
hermes config set terminal.docker_run_as_host_user true
hermes config set terminal.container_persistent true
hermes config set terminal.cwd /home/tom3/project      # 호스트 경로. 아래 함정 참고

컨테이너 안의 경로를 넣으면 마운트가 조용히 비활성화된다. tools/terminal_tool.py 의 해당 부분은 다음과 같다.

candidate = os.path.abspath(os.path.expanduser(
    _tenv("TERMINAL_CWD") or _safe_getcwd()))
if (_is_host_cwd(candidate)
    or (os.path.isabs(candidate) and os.path.isdir(candidate)
        and not candidate.startswith(("/workspace", "/root")))):
    host_cwd = candidate
    cwd = "/workspace"

값이 /workspace 로 시작하면 조건을 통과하지 못해 host_cwd 가 None 으로 남고 바인드 마운트가 생성되지 않는다. 호스트 경로를 /workspace 로 바꿔주는 일은 Hermes가 알아서 한다.

자원 상한을 올린다. 기본값이 매우 낮다.

hermes config set terminal.container_cpu 6
hermes config set terminal.container_memory 32768

샌드박스에 GPU를 넘기려면 nvidia-container-toolkit 이 필요하다.

hermes config set terminal.docker_extra_args '["--gpus","device=1"]'

컨테이너는 넘겨받은 GPU를 0번부터 다시 번호 매긴다. 호스트의 device=1 은 컨테이너 안에서 GPU 0 으로 보인다. 메모리 사용량을 대조하면 확인할 수 있다.

docker inspect <컨테이너> --format '{{json .HostConfig.DeviceRequests}}'

마운트는 설정이 아니라 컨테이너 내부에서 확인한다.

docker inspect <컨테이너> --format \
  '{{range .Mounts}}{{.Source}} -> {{.Destination}} rw={{.RW}}{{"\n"}}{{end}}'
hermes -z "Use the terminal tool to run: ls -1 /workspace | head -20"


4.2 방법 B: 로컬 + 전용 conda 환경

더 빠르고 웹앱 개발이 가능해진다. 대신 샌드박스를 잃는다.

conda create -y -n venv_hm python=3.12
PYTHONNOUSERSITE=1 ~/miniconda3/envs/venv_hm/bin/pip install --ignore-installed \
  flask requests numpy pandas pillow matplotlib beautifulsoup4 lxml \
  httpx rich pyyaml python-dotenv tqdm

ENABLE_USER_SITE 가 True 이면 pip 는 ~/.local 의 패키지를 보고 이미 설치되었다고 판단하여 가상환경에는 아무것도 넣지 않는다. 그 상태로 import 를 확인하면 유출된 쪽을 읽어 통과해 버린다. 반드시 사용자 site 를 끈 상태로 검증한다.

PYTHONNOUSERSITE=1 ~/miniconda3/envs/venv_hm/bin/python -c "
import sys, flask
print(sys.executable)
print([p for p in sys.path if p and 'venv_hm' not in p] or '유출 없음')
print(flask.__file__)"

스냅샷 셸에서 conda activate 로 바꾼 PATH 는 살아남지 못한다. 반면 CUDA_VISIBLE_DEVICES 처럼 Hermes가 관리하지 않는 변수는 살아남는다. conda 셸 함수에 의존하지 말고 PATH 를 직접 지정한다.

~/.hermes/shell_init.sh 파일을 만든다.

HM_ENV="$HOME/miniconda3/envs/venv_hm"
if [ -d "$HM_ENV/bin" ]; then
    PATH="$HM_ENV/bin:$(printf '%s' "$PATH" | tr ':' '\n' \
          | grep -vxF "$HM_ENV/bin" | paste -sd:)"
    export PATH
    export CONDA_PREFIX="$HM_ENV"
    export CONDA_DEFAULT_ENV=venv_hm
fi
export PYTHONNOUSERSITE=1
export CUDA_VISIBLE_DEVICES=1    # GPU 0 은 Ollama 서버 전용

Hermes가 이 파일을 읽도록 지정한다. 사용자의 .bashrc 는 건드리지 않는다.

hermes config set terminal.backend local
hermes config set terminal.cwd /home/tom3/project
hermes config set terminal.shell_init_files '["~/.hermes/shell_init.sh"]'

설정이 아니라 에이전트를 통해 확인한다.

hermes -z "Use the terminal tool to run: which python; \
  python -c 'import flask; print(flask.__file__)'; nproc; \
  echo CUDA=$CUDA_VISIBLE_DEVICES; pwd"


5. 텔레그램 연결

5.1 봇 생성 (휴대폰)

1. 텔레그램에서 @BotFather 를 검색한다. 파란 인증 표시가 있는 계정을 고른다.

2. START 를 누르고 /newbot 을 보낸다.

3. 표시 이름을 입력하고, 이어서 bot 으로 끝나는 사용자명을 입력한다.

4. 응답에 나오는 토큰을 복사한다. Use this token to access the HTTP API 아래 줄이다.

5. @userinfobot 을 검색해 START 를 누르면 숫자 Id 를 알려준다.


5.2 자격증명 기록

토큰을 대화 기록에 남기지 않는다. 입력을 가린 채 받고, 기록하기 전에 텔레그램 서버에 실제로 물어 검증한다.

hermes gateway setup 을 쓰면 같은 일을 대화형으로 해준다. 단 TTY 가 필요하다.


5.3 게이트웨이 실행

cd /home/tom3/project        # 도커 백엔드에서는 이 경로가 마운트에 쓰인다
hermes gateway run

로그에 Connected to Telegram (polling mode) 가 나오면 성공이다. 폴링 방식이므로 공인 IP, 포트포워딩, 인바운드 방화벽 규칙이 모두 필요 없다.

안정적으로 동작하는 것을 확인한 뒤 서비스로 등록한다.

hermes gateway install
hermes gateway status


5.4 텔레그램 연결 안정성

이 회선에서 60분 동안 30초 간격으로 측정한 결과이다.

표본 120회, 정상 115회 (95.8%)
끊김 1회, 지속 시간 2분 20초
같은 시각 다른 사이트는 정상 (github 0.02초, discord 0.04초)

api.telegram.org 만 선택적으로 막혔고, 모든 IP가 동시에 TCP 443 연결에 실패했다. 따라서 TELEGRAM_FALLBACK_IPS 로 IP를 바꿔도 해결되지 않는다. Hermes가 지수 백오프로 10회까지 자동 재접속하고, 텔레그램이 미수신 업데이트를 24시간 보관하므로 메시지는 유실되지 않는다. 응답이 수 분 늦어질 뿐이다. 복구는 로그에 남는다.

[Telegram] Telegram API transport recovered via 149.154.166.110
[Telegram] Using sticky IPv4 Telegram API path (dual-stack hostname tried last)

DNS가 AAAA 레코드만 반환하는데 호스트에 IPv6 경로가 없으면 같은 증상이 난다. 다음으로 확인한다.

getent ahostsv4 api.telegram.org
getent ahostsv6 api.telegram.org
ip -6 route show default          # 비어 있으면 IPv6 경로 없음


6. 비전 기능

hermes config set auxiliary.vision.provider ollama
hermes config set auxiliary.vision.model    qwen3-vl:32b-instruct
hermes config set auxiliary.vision.base_url http://127.0.0.1:11434/v1

aux.vision 으로 쓰면 Hermes가 모르는 섹션이 만들어지고 경고 없이 무시된다. 이후 에이전트는 vision_analyze tool is not available in this environment 라고 답한다.

게이트가 열렸는지 확인하고, 반드시 실제 이미지로 다시 확인한다.

VENV=$(ls -d ~/.hermes/installs/*/environments/*/venv | head -1)
cd ~/.hermes/hermes-agent && HERMES_HOME=~/.hermes "$VENV/bin/python" -c "
import sys; sys.path.insert(0,'.')
from tools.vision_tools import check_vision_requirements
print(check_vision_requirements())"      # True 여야 한다

hermes -z "Use vision_analyze on /경로/스크린샷.png and quote the text you can read."

설정이 맞아 보이는지가 아니라, 글자를 실제로 읽어내는지로 판정한다.

비전 모델은 필요할 때 적재되고 유휴 시간이 지나면 해제된다. qwen3-vl:32b-instruct 는 약 83 GB를 쓴다. GPU 0 에 55 GB짜리 코딩 모델이 있으면 Ollama가 GPU 1 에 올리므로, 이미지를 분석하는 동안에는 CUDA_VISIBLE_DEVICES=1 로 확보해 둔 몫이 거의 남지 않는다. 더 가벼운 선택지는 qwen3-vl:30b-a3b-instruct (19 GB) 이다.


7. 개발 작업 흐름

7.1 프롬프트 틀

중요한 것은 두 가지다. 외부 주소 검증과 자기 검증이다. 둘 다 실제로 관찰된 실패를 막기 위한 것이다. 모델은 그럴듯한 CDN 경로를 지어내고, 아무것도 실행하지 않은 채 성공했다고 보고한다.

<무엇>을 /home/tom3/project/<앱> 에 만들어라.

1. 파일은 app.py 와 templates/index.html 두 개만.
2. Flask 포트 <N>. 이미 쓰는 프로세스가 있으면 먼저 kill.
3. 라이브러리는 https://cdnjs.cloudflare.com 에서 버전 고정 UMD 빌드로 받는다.
   script
주소를 쓰기 전에 curl -sI "<주소>" 로 HTTP 200 인지 확인해라.
   404
면 다른 버전으로 바꾸고 다시 확인해라.
4. <
동작 요구사항>
5.
데이터는 app.py 의 파이썬 리스트에 두고 GET /api/<x> 로 내보내고
   JS
가 fetch 로 받는다. HTML 에 하드코딩 금지.
6.
작업이 끝나면 서버를 띄우고 curl 로 모든 경로가 200 인지 확인해라.
  
그 확인을 하기 전에는 완료라고 하지 마라.


7.2 주소 검증이 필요한 이유

가정이 아니라 관찰된 사실이다. cdnjs 는 three.js 코어만 제공하며 애드온 하위 경로는 존재하지 않는다.

three.js/r128/three.min.js                         200
three.js/r128/controls/OrbitControls.min.js        404
three.js/r128/postprocessing/EffectComposer.min.js 404
three.js/r128/objects/Lensflare.min.js             404

코어는 로드되므로 THREE 객체는 존재한다. 그러나 없는 애드온을 처음 쓰는 줄에서 Uncaught TypeError 가 나고 페이지가 멈춘다. 로딩 문구가 지워지지 않는다. 서버는 내내 200 을 반환하므로 서버 쪽 점검으로는 아무 이상도 발견되지 않는다.


7.3 신규 작성과 기존 코드 수리의 차이

같은 모델로 두 가지를 측정했다.

작업 유형

결과

백지에서 신규 작성

요구사항 6개 전부 통과, 39초

깨진 기존 코드 수리 (1차)

의존성을 절반만 제거. 콘솔 오류 45건

깨진 기존 코드 수리 (2차, 같은 프롬프트)

한 줄도 고치지 않고 오류 없음이라고 보고

 

이 모델은 새로 쓰는 일은 잘하고, 남이 망가뜨린 코드를 외과적으로 고치는 일은 못한다. 수리를 두 번 시도해 실패하면 새 폴더에 처음부터 만드는 편이 빠르다. 기존 폴더는 지우지 말고 서버만 멈춰 두면 비교가 가능하다.


7.4 실행 중인 웹앱 캡처

google-chrome --headless=new --disable-gpu --no-sandbox \
  --use-gl=angle --use-angle=swiftshader --enable-unsafe-swiftshader \
  --window-size=1400,900 --virtual-time-budget=12000 \
  --screenshot=/tmp/shot.png "http://127.0.0.1:<포트>/"

스크린샷으로는 보이지 않는 콘솔 오류는 따로 받아야 한다.

google-chrome --headless=new --disable-gpu --no-sandbox \
  --enable-logging=stderr --virtual-time-budget=10000 \
  --dump-dom "http://127.0.0.1:<포트>/" > /dev/null 2> console.log
grep -iE 'Uncaught|ERROR|Failed to load' console.log

페이지의 모든 외부 주소를 한 번에 검사한다.

for u in $(grep -oE 'https://[^"\' ]+\.js' templates/index.html | sort -u); do
  printf "%-70s " "$u"; curl -sI --max-time 12 "$u" | head -1
done


7.5 텔레그램 세션 관리

모델, 백엔드, 경로 중 하나라도 바꾸었으면 /new 또는 /reset 을 보낸다. 대화 기록은 새 모델에게도 그대로 전달되므로, 예전의 잘못된 결론과 더 이상 존재하지 않는 경로가 설정 변경 후에도 살아남아 사실처럼 반복된다.


8. 운영

hermes gateway status
hermes gateway restart
hermes config get <키>
curl -s http://127.0.0.1:11434/api/ps | python3 -m json.tool    # 적재된 모델
nvidia-smi --query-gpu=index,memory.used,power.draw --format=csv

Ollama를 멈추지 않고 모델만 메모리에서 내린다.

curl -s http://127.0.0.1:11434/api/generate -d '{"model":"<모델>","keep_alive":0}'