DEV Community

matias yoon
matias yoon

Posted on

터미널 AI 에이전트 구축 (v46)

터미널 AI 에이전트 구축 (v46)

터미널에서 직접 작동하는 AI 에이전트를 구축해보는 실전 가이드입니다. 이 가이드는 로컬에서 작동하는 LLM을 활용한 개발자용 AI 에이전트를 구축하고 최적화하는 방법을 실습 중심으로 설명합니다.

1. CLI AI 에이전트 생태계

현재 CLI AI 에이전트 시장은 다음과 같은 주요 도구들로 구성되어 있습니다:

주요 도구 비교:

# Aider - 최신 인기 도구
# pip install aider
# aider --help

# Continue.dev - VSCode 확장이지만 CLI 지원
# npm install -g @continue-dev/cli

# OpenCode - 코드 생성 전용
# pip install opencode
Enter fullscreen mode Exit fullscreen mode

현실적인 선택지: 로컬에서 작동하는 자체 에이전트가 가장 안정적입니다. 특히 회사 내부에서 사용할 경우.

2. 로컬 LLM API 엔드포인트 설정

로컬 LLM을 CLI에서 사용하기 위해 API 서버를 설정합니다:

# Ollama 설치 (가장 쉬운 방법)
curl -fsSL https://ollama.com/install.sh | sh

# 모델 다운로드
ollama pull llama3
ollama pull codellama:7b

# API 서버 시작
ollama serve
# 기본 포트: 11434

# 테스트
curl http://localhost:11434/api/generate -d '{
  "model": "llama3",
  "prompt": "Hello, how are you?",
  "stream": false
}'
Enter fullscreen mode Exit fullscreen mode

3. 간단한 Python CLI 에이전트 구축

기본적인 CLI 에이전트를 만들어보겠습니다:

# agent.py
import json
import requests
import sys
from typing import Dict, List

class LocalAI:
    def __init__(self, base_url="http://localhost:11434"):
        self.base_url = base_url

    def chat(self, messages: List[Dict], model="llama3", temperature=0.7):
        """기본 채팅 API 호출"""
        payload = {
            "model": model,
            "messages": messages,
            "temperature": temperature,
            "stream": False
        }

        response = requests.post(
            f"{self.base_url}/api/chat",
            json=payload,
            timeout=30
        )

        if response.status_code == 200:
            return response.json()['message']['content']
        else:
            raise Exception(f"API Error: {response.text}")

def main():
    if len(sys.argv) < 2:
        print("사용법: python agent.py <질문>")
        return

    question = " ".join(sys.argv[1:])

    ai = LocalAI()
    messages = [
        {"role": "system", "content": "You are a helpful coding assistant. Respond in concise markdown."},
        {"role": "user", "content": question}
    ]

    try:
        response = ai.chat(messages)
        print(response)
    except Exception as e:
        print(f"에러 발생: {e}")

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

실행 방법:

python agent.py "Python으로 factorial 함수를 작성해주세요"
Enter fullscreen mode Exit fullscreen mode

4. tmux와 통합

터미널을 분할해서 사용할 수 있도록 tmux 통합:

# tmux_agent.py
import subprocess
import os
import time
import json

class TmuxAgent:
    def __init__(self, session_name="ai_session"):
        self.session_name = session_name
        self._setup_session()

    def _setup_session(self):
        """tmux 세션 생성"""
        subprocess.run([
            "tmux", "new-session", "-d", "-s", self.session_name
        ], check=True)

    def create_window(self, name):
        """새 창 생성"""
        subprocess.run([
            "tmux", "new-window", "-t", f"{self.session_name}:{name}"
        ], check=True)

    def send_command(self, window, command):
        """명령어 전송"""
        subprocess.run([
            "tmux", "send-keys", "-t", f"{self.session_name}:{window}",
            command, "Enter"
        ], check=True)

    def get_window_output(self, window):
        """창의 출력 가져오기"""
        result = subprocess.run([
            "tmux", "capture-pane", "-p", "-t", f"{self.session_name}:{window}"
        ], capture_output=True, text=True)
        return result.stdout

# 사용 예시
agent = TmuxAgent("coding_session")
agent.create_window("editor")
agent.send_command("editor", "vim main.py")
Enter fullscreen mode Exit fullscreen mode

5. 맞춤형 도구 개발

코드 검색 도구:

# search_tool.py
import subprocess
import os
import re

class CodeSearchTool:
    def __init__(self, project_root="."):
        self.project_root = project_root

    def search_patterns(self, pattern, file_extensions=None):
        """정규식 패턴으로 코드 검색"""
        if file_extensions is None:
            file_extensions = ["*.py", "*.js", "*.ts", "*.java"]

        cmd = ["find", self.project_root]
        cmd.extend(["-name", f"*.{file_extensions[0]}"])

        try:
            result = subprocess.run(
                cmd, capture_output=True, text=True, check=True
            )
            files = result.stdout.strip().split('\n')

            matches = []
            for file in files:
                if file.strip():
                    with open(file, 'r') as f:
                        content = f.read()
                        if re.search(pattern, content):
                            matches.append(file)
            return matches
        except subprocess.CalledProcessError:
            return []

# 사용 예시
searcher = CodeSearchTool()
results = searcher.search_patterns(r"def\s+\w+\s*\(")
print("함수 정의 찾기:", results)
Enter fullscreen mode Exit fullscreen mode

Git 통합 도구:

# git_tool.py
import subprocess
import json

class GitTool:
    def __init__(self, repo_path="."):
        self.repo_path = repo_path

    def get_diff(self, commit_range="HEAD~1..HEAD"):
        """Git diff 가져오기"""
        try:
            result = subprocess.run([
                "git", "-C", self.repo_path, "diff", commit_range
            ], capture_output=True, text=True, check=True)
            return result.stdout
        except subprocess.CalledProcessError:
            return ""

    def get_staged_files(self):
        """스테이징된 파일들 가져오기"""
        try:
            result = subprocess.run([
                "git", "-C", self.repo_path, "diff", "--cached", "--name-only"
            ], capture_output=True, text=True, check=True)
            return result.stdout.strip().split('\n')
        except subprocess.CalledProcessError:
            return []

# 사용 예시
git_tool = GitTool()
diff = git_tool.get_diff()
staged = git_tool.get_staged_files()
Enter fullscreen mode Exit fullscreen mode

6. 컨텍스트 윈도우 관리

대용량 코드베이스에서 컨텍스트 윈도우를 최적화:

# context_manager.py
class ContextManager:
    def __init__(self, max_tokens=4096):
        self.max_tokens = max_tokens
        self.context = []

    def add_message(self, role, content):
        """메시지 추가 (토큰 제한 고려)"""
        message = {"role": role, "content": content}

        # 간단한 토큰 추정 (실제 구현은 tokenizer 사용)
        token_count = len(content.split())

        if token_count > self.max_tokens:
            # 너무 긴 내용은 자르기
            content = " ".join(content.split()[:self.max_tokens])
            message["content"] = content

        self.context.append(message)
        return self._trim_context()

    def _trim_context(self):
        """컨텍스트 트리밍"""
        total_tokens = sum(len(msg["content"].split()) for msg in self.context)

        while total_tokens > self.max_tokens:
            if len(self.context) > 2:  # 최소 2개 유지
                self.context.pop(1)  # 중간 메시지 제거
                total_tokens = sum(len(msg["content"].split()) for msg in self.context)
            else:
                break

        return self.context

# 사용 예시
cm = ContextManager(max_tokens=2048)
cm.add_message("system", "You are a helpful AI assistant")
cm.add_message("user", "Write a function that does something complex with large data structures...")
Enter fullscreen mode Exit fullscreen mode

7. 비용/성능 최적화

로컬 vs API 모델 비교:


python
# optimization.py
import time
from typing import Optional

class ModelOptimizer:
    def __init__(self):
        self.models = {
            "llama3": {"local": True, "speed": "fast", "cost": "free"},
            "codellama": {"local": True, "speed": "fast

---

📥 **Get the full guide on Gumroad**: https://gumroad.com/l/auto ($5)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)