터미널 AI AGENT vega

터미널에서 쓸 만한 에이전트 비슷한 걸 작성하고 있습니다.

계기는 간단합니다. 도무지 뭔 말인지 모를 수많은 배쉬 스크립트와 문법들때문에 구글링해서 복사복붙하는 것도 귀찮고 그 많은 문법을 쓰기도 귀찮았습니다.

어차피 AI 쓰는거 대화형으로 작업하려고 만들었습니다.

최종 목표는 SSH나 네트웍에 접속한 다른 리눅스 박스 까지 설정할 수 있게 하는 겁니다.

현재 하나의 명령어를 실행하다 막히면 대화형으로 질문을 이어갈 수 있게 처리 되어 있습니다.

귀차니즘은 언제나 새로운 길을 만들게 하네요.

1개의 좋아요

귀찮게 실행한 다음 거기서 명령어 입력하는 일 없습니다. 그냥 명령어 치고 자연어 명령을 입력하면 됩니다.

실행할때 명령어를 ““등으로 구분할 필요 없습니다. 그냥 생각나는 데로 자연어 입력하면 됩니다.

1개의 좋아요

api 토큰 먹는 괴물을 만들었으니 이제 다이어트 시켜야합니다. 터미널 에이전트 주제에 추론까지 하려고 합니다.

제미나이 식으로 표현하면

비서가 말 한마디 할 때마다 돈 달라고 하길래, 내가 아예 '꼭 필요한 말 아니면 메모지에 적어서 한꺼번에 보고해’라고 규칙을 바꿨어. 이제는 돈도 덜 들고 일 처리도 더 빨라질 거야

라는군요.

2개의 좋아요

사용자 시스템의 정보를 미리 긁어옵니다. ( vega config )

여기에는 언어 설정이 포함되어 있습니다.

그래서 언어에 따라 달라지는 파일 명칭을 구분합니다.

예를 들어 마운트된 HDD 폴더안에서 스크린캐스트 기능으로 녹화한 파일을 검색해달라고 하면

Screencast를 스크린캐스트로 변환하고 관련된 확장자명을 스스로 판단하여 검색 옵션을 조절합니다.

결과는 아래 동영상 처럼 됩니다.

만약 내 nvim의 확장을 업데이트해달라고 명령하면 이미 내가 사용하는 nvim의 확장자 관리자가 어떤건지 파악하고 여기에 맞춰서 nvim의 확장자를 업데이트 해줍니다.

만약 내 OS를 업데이트 해 달라고 말하면 vega config에서 저장된 OS 정보를 찾아내서 이 녀석이 사용하는 게 dnf인지 aur인지 혹은 apt인지를 인지하고 여기에 맞는 명령어를 찾아 실행합니다. sudo 권한이 있으니 당연히 이명령을 실행할지 여부는 물어봅니다.

만약 어떤 앱을 실행해달라고 하면 이 앱을 실행해보고 없으면 설치할 것인지 묻고 난 다음 실행해줍니다

1개의 좋아요

토큰이 다되면 웹세션으로 바꿔서 계속 사용할 수 있게 하려는데 이게 시간이 좀 걸리네요.
그래도 결국 했습니다. ㅠㅠㅠ

1개의 좋아요

vega “내 구글 드라이브의 input 폴더의 내용을 모두 복사해줘”
:robot: [VEGA] Analyzing natural language request…
Input: “내 구글 드라이브의 input 폴더의 내용을 모두 복사해줘”
:warning: IP Unknown. Running automatic discovery…
:magnifying_glass_tilted_left: Discovery: Found specific configuration: lazy.nvim
:cloud: Discovery: Found 1 rclone remotes.
:satellite_antenna: Remote identified: REMOTE_01
:information_source: Network discovery completed.
:information_source: DEBUG: No OAuth Token found. Falling back to API Key.
:high_voltage: [Router] Routing to: Gemini
:hourglass_not_done: [auth: renewing…]
:memo: Explanation: Copies all content from the ‘input’ folder on REMOTE_01 (Google Drive) to the current directory.
:warning: Risk Level: INFO

Command: rclone copy REMOTE_01:input ./
Execute this command? [y/N] y
:high_voltage: Executing…
:warning: IP Unknown. Running automatic discovery…
:magnifying_glass_tilted_left: Discovery: Found specific configuration: lazy.nvim
:cloud: Discovery: Found 1 rclone remotes.
:satellite_antenna: Remote identified: REMOTE_01
:information_source: Network discovery completed.
:warning: IP Unknown. Running automatic discovery…
:magnifying_glass_tilted_left: Discovery: Found specific configuration: lazy.nvim
:cloud: Discovery: Found 1 rclone remotes.
:satellite_antenna: Remote identified: REMOTE_01
:information_source: Network discovery completed.
:link: [Resolved] rclone copy gdrive:input ./
:white_check_mark: Execution Successful.

:cloud: 클라우드 통합 (rclone)

VEGA는 rclone을 활용하여 무중단 프로젝트 백업 및 상태 동기화를 지원합니다.

1. 사전 준비

  • 시스템에 rclone이 설치되어 있어야 합니다: sudo dnf install rclone (Fedora) 또는 sudo apt install rclone (Ubuntu).

  • 클라우드 리모트를 설정합니다: rclone config.

2. 자율 탐색 (Autonomous Discovery)

VEGA의 탐색 엔진은 활성화된 rclone 리모트를 자동으로 식별하며, AI와 통신할 때 민감한 리모트 이름을 마스킹(예: gdrive:REMOTE_01)하여 보안을 유지합니다.

3. 자연어 클라우드 작업

자연어로 클라우드 저장소와 상호작용할 수 있습니다. VEGA는 실행 직전에 마스킹된 이름을 원래의 리모트 이름으로 자동 복원합니다.

# 예시: 구글 드라이브에서 폴더 복사
vega "구글 드라이브의 'input' 폴더를 여기로 복사해줘"

# 예시: 현재 프로젝트를 클라우드와 동기화
vega sync

4. 안전 가드레일 (Safety Guardrails)

  • 용량 제한: 데이터 비용 및 오버헤드 방지를 위해, 동기화 크기가 1GB를 초과할 경우 자동으로 차단됩니다.

  • 사용자 승인: 모든 클라우드 작업은 실행 전 사용자의 명시적 확인을 거칩니다.

이제 SSH 만 남았습니다.

1개의 좋아요

활용 방법은 다음과 같습니다
파이어 폭스가 스냅설치인지 네이티브 설치인지 모를 때
그냥
vega firefox가 Snap으로 설치된건지 아니면 apt로 설치된건지 확인해줘
:robot: [VEGA] Analyzing natural language request…
Input: “firefox가 Snap으로 설치된건지 아니면 apt로 설치된건지 확인해줘”
:warning: IP Unknown. Running automatic discovery…
:magnifying_glass_tilted_left: Discovery: Found specific configuration: lazy.nvim
:cloud: Discovery: Found 1 rclone remotes.
:satellite_antenna: Remote identified: REMOTE_01
:information_source: Network discovery completed.
:information_source: DEBUG: No OAuth Token found. Falling back to API Key.
:high_voltage: [Router] Routing to: Gemini
:hourglass_not_done: [auth: renewing…]
:memo: Explanation: Checks if ‘firefox’ is present in the snap package list, then falls back to checking the apt installed package list.
:warning: Risk Level: INFO

Command: snap list | grep -i firefox || apt list --installed | grep -i firefox
Execute this command? [y/N] y
:high_voltage: Executing…
:warning: IP Unknown. Running automatic discovery…
:magnifying_glass_tilted_left: Discovery: Found specific configuration: lazy.nvim
:cloud: Discovery: Found 1 rclone remotes.
:satellite_antenna: Remote identified: REMOTE_01
:information_source: Network discovery completed.
:warning: IP Unknown. Running automatic discovery…
:magnifying_glass_tilted_left: Discovery: Found specific configuration: lazy.nvim
:cloud: Discovery: Found 1 rclone remotes.
:satellite_antenna: Remote identified: REMOTE_01
:information_source: Network discovery completed.
firefox 148.0-1 7901 latest/stable/… mozilla** -
:white_check_mark: Execution Successful.

vega firefox가 snap으로 설치되어 있다면 apt 설치로 영구히 바꿔줘
:robot: [VEGA] Analyzing natural language request…
Input: “firefox가 snap으로 설치되어 있다면 apt 설치로 영구히 바꿔줘”
:warning: IP Unknown. Running automatic discovery…
:magnifying_glass_tilted_left: Discovery: Found specific configuration: lazy.nvim
:cloud: Discovery: Found 1 rclone remotes.
:satellite_antenna: Remote identified: REMOTE_01
:information_source: Network discovery completed.
:information_source: DEBUG: No OAuth Token found. Falling back to API Key.
:high_voltage: [Router] Routing to: Gemini
:hourglass_not_done: [auth: renewing…]
:memo: Explanation: Removes Snap Firefox, adds Mozilla PPA, sets APT preference for PPA Firefox, updates package lists, then installs Firefox via APT.
:warning: Risk Level: WARNING

Command: sudo snap remove firefox && sudo add-apt-repository ppa:mozillateam/ppa -y && echo ‘Package: firefox*\nPin: release o=LP-PPA-mozillateam\nPin-Priority: 1001’ | sudo tee /etc/apt/preferences.d/mozillateamppa > /dev/null && sudo apt update && sudo apt install firefox -y
Execute this command? [y/N] y
:high_voltage: Executing…
:warning: IP Unknown. Running automatic discovery…
:magnifying_glass_tilted_left: Discovery: Found specific configuration: lazy.nvim
:cloud: Discovery: Found 1 rclone remotes.
:satellite_antenna: Remote identified: REMOTE_01
:information_source: Network discovery completed.
:warning: IP Unknown. Running automatic discovery…
:magnifying_glass_tilted_left: Discovery: Found specific configuration: lazy.nvim
:cloud: Discovery: Found 1 rclone remotes.
:satellite_antenna: Remote identified: REMOTE_01
:information_source: Network discovery completed.
[sudo: authenticate] Password:

자동으로 권한 요청하고 명령어 수행합니다.

적어도 시스템 관리 측면에서는 귀찮은 명령어들의 옵션은 내부에서 추론해버립니다. 토큰 적게 쓰려고 미친 짓 많이 했네요

이제 Rclone도 거의 완료된거 같습니다.
rclone confg 되어 있으면 자동으로 읽어들입니다
굳이 google drive 주소 일일이 설정할 필요없이 구글 드라이브의 특정 폴더의 내용을 복사하거나 현재 디렉토리의 내용을 rclone으로 보내 버릴 수 있습니다.

1개의 좋아요

vega에 로컬 LLM을 붙여서 동작할 수 있게 했습니다.
현재 테스트해본 것은

  1. vega에서 LLM 선택시 로컬 LLM 선택 → 엔드 포인트 입력 → 자동으로 모델 리스트 불러오기
  2. local LLM을 vega의 AI로 채택
  3. ssh 정보 확인 및 ssh 접속
  4. ssh로 접속한 서버의 상태 확인
  5. ssh로 접속한 서버의 업데이트
  6. ssh로 접속한 서버의 특정 프로그램 ( ex: Nvim )의 플러그인 업데이트
  7. ssh로 접속한 서버의 특정 서비스 ( 혹은 프로그램 ) 종료 / 실행
  8. ssh로 접속한 서버의 종료

까지 테스트되었습니다

이제 서버의 서비스 설치 및 삭제 테스트를 하고 있습니다.

테스트 내용은 ollama에 특정 모델 설치 / 특정 모델 삭제

입니다.

거기에 혹시라도 자기가 AI로 쓰고 있는 모델을 삭제하지 않게 하기 위해 보호 로직 추가해두었습니다.

vega를 사용하여 ssh로 접속한 LLM 서버에 ollama에 mistral 설치 영상

❯ vega 192.168.0.150에서 실행되는 ollama에 mistral 삭제해줘
:robot: [VEGA] Analyzing natural language request…
Input: “192.168.0.150에서 실행되는 ollama에 mistral 삭제해줘”
:satellite_antenna: Identifying as: dogsinatas-axon (192.168.0.240)
:magnifying_glass_tilted_left: Discovery: Found specific configuration: lazy.nvim
:cloud: Discovery: Found 1 rclone remotes.
:satellite_antenna: Remote identified: STORAGE:REMOTE_C4CC
:key: Discovery: Found 1 potential SSH targets in config.
:satellite_antenna: SSH Target identified: HOST:REMOTE_73B8
:high_voltage: [Router] Routing to: Ollama
:warning: Risk Level: INFO
:magnifying_glass_tilted_left: Discovery: Found specific configuration: lazy.nvim
:cloud: Discovery: Found 1 rclone remotes.
:satellite_antenna: Remote identified: STORAGE:REMOTE_C4CC
:key: Discovery: Found 1 potential SSH targets in config.
:satellite_antenna: SSH Target identified: HOST:REMOTE_73B8
:memo: Explanation: 192.168.0.150:22 호스트에서 모델 'mistral’을(를) 삭제합니다.
:warning: Risk Level: INFO
:bullseye: Intent: Ollama(Remove { model: “mistral”, force: false })
Execute this semantic action? [y/N] y
:high_voltage: Executing…
:shield: [Sovereign] Semantic intent detected. Executing via Secure Pipeline.
:satellite_antenna: [Target] Resolved host: 192.168.0.150:22 (User: “dogsinatas”, Port: 22)
:bullseye: [Semantic Intent] Recognized: Ollama(Remove { model: “mistral”, force: false })
:building_construction: [Action] Resolved to: Ollama: Remove Model (Force: false)
:magnifying_glass_tilted_left: [Validation] Running capability checks for: 192.168.0.150:22
:magnifying_glass_tilted_left: [Safety] Checking if model ‘mistral’ is currently running…
:magnifying_glass_tilted_left: [Safety] Building dependency graph to protect downstream models…
:white_check_mark: [Validation] All checks passed.
:memo: [Execution Plan]

  • Last-minute runtime re-validation on 192.168.0.150:22
  • Execute ‘ollama rm mistral’
    Impact: Permanently deletes model ‘mistral’. Force: false
    :high_voltage: [Execution] Initiating semantic action…

:bar_chart: [Result Presentation]
Model ‘mistral’ removed successfully.
deleted ‘mistral’

:white_check_mark: [Sovereign] Orchestration completed successfully.

mistral:latest 6577803aa9a0 4.4 GB 5 minutes ago
Llama3:latest 365c0bd3c000 4.7 GB 5 days ago
qwen2.5:7b-instruct-q4_K_M 845dbda0ea48 4.7 GB 7 days ago

❯ source ~/.llm.sh

:hammer_and_wrench: AXON Node-01 Check
:white_check_mark: Ollama: Running
:white_check_mark: Venv: Active
:bar_chart: Models:
NAME ID SIZE MODIFIED
Llama3:latest 365c0bd3c000 4.7 GB 5 days ago
qwen2.5:7b-instruct-q4_K_M 845dbda0ea48 4.7 GB 7 days ago

설치 삭제까지 성공입니다.

이제 다음 목표는
복합 추론을 하게 하려고 합니다.
예시 :
vega ssh에 연결된 시스템에 설치된 LLM 모델 정보를 알려줘

이건 추론의 과정이 복합적입니다. ollama라는 상위 정보는 없고 llm 모델이라는 정보 만 있죠.
이게 가능한 수준까지 올리는게 목표입니다.
즉 시스템이 “LLM 모델” 이라는 추상 목표에서 가능한 provider/runtime 들을 추론해야 합니다.

❯ vega ssh에 연결된 시스템에 설치된 LLM 모델 정보를 알려줘

  1. 구체적인 타겟의 정보를 지정하지 않았습니다.
  2. ollama라는 정보를 주지 않았습니다.
  3. 그 상태로 vega에서 추론 단계를 거쳐 ollama → 설치된 모델을 확인합니다.
    :robot: [VEGA] Analyzing natural language request…
    Input: “ssh에 연결된 시스템에 설치된 LLM 모델 정보를 알려줘”
    :satellite_antenna: Identifying as: dogsinatas-axon (192.168.0.240)
    :magnifying_glass_tilted_left: Discovery: Found specific configuration: lazy.nvim
    :cloud: Discovery: Found 1 rclone remotes.
    :satellite_antenna: Remote identified: STORAGE:REMOTE_C4CC
    :key: Discovery: Found 1 potential SSH targets in config.
    :satellite_antenna: SSH Target identified: HOST:REMOTE_73B8
    :high_voltage: [Router] Routing to: Ollama
    :satellite_antenna: [Target] Resolved to canonical: 192.168.0.150
    :memo: Explanation: 192.168.0.150에 설치된 Ollama 모델 목록을 조회합니다.
    :bullseye: Action: OLLAMA_LIST_INSTALLED on 192.168.0.150
    Execute this semantic action? [y/N] y
    :high_voltage: Executing…
    :satellite: [Executor] Skipping heavy snapshot capture for Ollama: List Installed Models
    :magnifying_glass_tilted_left: [Action] Validating Ollama: List Installed Models based on host capabilities…
    :memo: [Plan] Execution Strategy:
  • ollama list
    :warning: [Safety] Impact: Retrieves the list of models installed on the system., Level: Safe
    :high_voltage: [Executor] Dispatching Remote Command: OLLAMA_HOST=127.0.0.1:11434 ollama list to 192.168.0.150
    :satellite_antenna: [SSH] CMD: ssh -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=no -p 22 dogsinatas@192.168.0.150 ‘OLLAMA_HOST=127.0.0.1:11434 ollama list’

:high_voltage: [RAW SSH TRANSPORT DEBUG] ------------------

  • Host: 192.168.0.150
  • Command: ‘OLLAMA_HOST=127.0.0.1:11434 ollama list’

:green_circle: [STDOUT]
NAME ID SIZE MODIFIED
Llama3:latest 365c0bd3c000 4.7 GB 6 days ago
qwen2.5:7b-instruct-q4_K_M 845dbda0ea48 4.7 GB 8 days ago

성공입니다.

그 상태로 다시 아까처럼 mistral을 삭제해 보겠습니다.

vega 192.168.0.150에서 실행되는 ollama에 mistral 삭제해줘
아까와는 다르게 ssh라는 말대신 구체적인 IP를 제시해주었습니다.
:robot: [VEGA] Analyzing natural language request…
Input: “192.168.0.150에서 실행되는 ollama에 mistral 삭제해줘”
:satellite_antenna: Identifying as: dogsinatas-axon (192.168.0.240)
:magnifying_glass_tilted_left: Discovery: Found specific configuration: lazy.nvim
:cloud: Discovery: Found 1 rclone remotes.
:satellite_antenna: Remote identified: STORAGE:REMOTE_C4CC
:key: Discovery: Found 1 potential SSH targets in config.
:satellite_antenna: SSH Target identified: HOST:REMOTE_73B8
:high_voltage: [Router] Routing to: Ollama
:satellite_antenna: [Target] Resolved to canonical: 192.168.0.150
:memo: Explanation: 192.168.0.150에서 ‘mistral’ 모델을 제거합니다.
:bullseye: Action: OLLAMA_REMOVE on 192.168.0.150
Execute this semantic action? [y/N] y
:high_voltage: Executing…
:satellite: [Executor] Skipping heavy snapshot capture for Ollama: Remove Model (Force: false)
:magnifying_glass_tilted_left: [Action] Validating Ollama: Remove Model (Force: false) based on host capabilities…
:memo: [Plan] Execution Strategy:

  • ollama rm mistral
    :warning: [Safety] Impact: Permanently deletes model ‘mistral’., Level: Dangerous
    :warning: WARNING: This command may modify your system.
    Command: Ollama: Remove Model (Force: false)
    Do you want to proceed? [y/N]: y
    :high_voltage: [Executor] Dispatching Remote Command: OLLAMA_HOST=127.0.0.1:11434 ollama rm mistral to 192.168.0.150
    :satellite_antenna: [SSH] CMD: ssh -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=no -p 22 dogsinatas@192.168.0.150 ‘OLLAMA_HOST=127.0.0.1:11434 ollama rm mistral’

:high_voltage: [RAW SSH TRANSPORT DEBUG] ------------------

  • Host: 192.168.0.150
  • Command: ‘OLLAMA_HOST=127.0.0.1:11434 ollama rm mistral’

:green_circle: [STDOUT]

:red_circle: [STDERR]
Error: model ‘mistral’ not found

:yellow_circle: [EXIT CODE] 1

— REMOTE EXECUTION FAILED —
STDERR:
Error: model ‘mistral’ not found

EXIT CODE: 1

Error: model ‘mistral’ not found

성공입니다. 푸쉬하고 이번주는 좀 쉬어야 겠습니다.

1개의 좋아요

.c와 .h까지 작성됩니다. ir 분리가 어느 정도 안정화되는 거 같습니다.
검증을 위한 컴파일 단계까지 진행해서 완료되면 릴리즈해도 될거 같습니다.
ir 분리했으니 아마 python과 rust도 새로 테스트해봐야 할 거 같습니다.

작업하면서 LLM들이 어떤 노가리를 떨까요? 이런 식이랍니다.
이 녀석들의 페르소나는 인사관리에서 주입됩니다.
그 전에 먼저 할 건 인사관리에서 LLM의 시니어/주니어 고용 / 해고 기능 넣은 후 페르소나를 개별 주입하려고 합니다.