DEV Community

matias yoon
matias yoon

Posted on

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

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

터미널에서 작동하는 AI 에이전트를 구축하는 것은 현대 개발 워크플로우에서 핵심적인 도구로 자리 잡고 있습니다. 이 가이드는 실질적인 비용 ($3-$7)의 가치를 제공하는 터미널 기반 AI 에이전트를 구축하는 방법을 다룹니다.

1. CLI AI 에이전트 생태계

현재 CLI AI 에이전트는 다양한 솔루션으로 구성되어 있습니다:

Aider: Git 기반 코드 생성 도구

pip install aider
aider --help
Enter fullscreen mode Exit fullscreen mode

Continue.dev: VS Code 확장이지만 CLI에서도 사용 가능

npm install -g continue
Enter fullscreen mode Exit fullscreen mode

OpenCode: 직접 구축한 커스텀 에이전트

# 커스텀 스크립트 예시
pip install openai
Enter fullscreen mode Exit fullscreen mode

내부 에이전트: 최소한의 기능을 가진 자체 구축 도구

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

로컬 LLM을 위한 API 엔드포인트를 설정하여 성능과 비용을 최적화합니다:

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

# 로컬 모델 실행
ollama run llama3.2:3b
ollama run codellama:7b

# API 엔드포인트 시작
ollama serve
Enter fullscreen mode Exit fullscreen mode

API 엔드포인트 구성:

# server.py
from flask import Flask, request, jsonify
import requests

app = Flask(__name__)

@app.route('/chat', methods=['POST'])
def chat():
    data = request.json
    prompt = data.get('prompt', '')

    response = requests.post(
        'http://localhost:11434/api/generate',
        json={
            'model': 'llama3.2:3b',
            'prompt': prompt,
            'stream': False
        }
    )

    return jsonify(response.json())

if __name__ == '__main__':
    app.run(port=8000)
Enter fullscreen mode Exit fullscreen mode

3. 함수 호출 기능을 가진 간단한 Python CLI 에이전트

# agent.py
import openai
import json
import subprocess
from typing import List, Dict
import os

class TerminalAgent:
    def __init__(self, api_key: str = None):
        self.client = openai.OpenAI(
            base_url="http://localhost:8000",
            api_key="ollama"
        )

    def execute_command(self, command: str) -> str:
        """명령어 실행 및 결과 반환"""
        try:
            result = subprocess.run(
                command, 
                shell=True, 
                capture_output=True, 
                text=True
            )
            return result.stdout + result.stderr
        except Exception as e:
            return f"Error: {str(e)}"

    def call_function(self, function_name: str, args: Dict) -> str:
        """함수 호출"""
        functions = {
            'execute_command': self.execute_command,
            'list_files': self.list_files,
            'git_status': self.git_status
        }

        if function_name in functions:
            return functions[function_name](**args)
        return f"Function {function_name} not found"

    def list_files(self, path: str = ".") -> str:
        """파일 목록 조회"""
        try:
            files = os.listdir(path)
            return json.dumps(files, indent=2)
        except Exception as e:
            return f"Error listing files: {str(e)}"

    def git_status(self) -> str:
        """Git 상태 조회"""
        return self.execute_command("git status --porcelain")

    def chat(self, prompt: str, functions: List[Dict] = None) -> str:
        """AI 채팅 메시지 처리"""
        messages = [
            {
                "role": "system",
                "content": "You are a helpful terminal assistant that can execute commands and provide code assistance."
            },
            {
                "role": "user",
                "content": prompt
            }
        ]

        response = self.client.chat.completions.create(
            model="llama3.2:3b",
            messages=messages,
            functions=functions,
            function_call="auto"
        )

        return response.choices[0].message.content

# 사용법
if __name__ == "__main__":
    agent = TerminalAgent()
    result = agent.chat("git 상태를 확인해줘")
    print(result)
Enter fullscreen mode Exit fullscreen mode

4. tmux/터미널 멀티플렉서 통합

터미널 에이전트와 tmux를 통합하여 복잡한 작업 처리:

# tmux 스크립트 생성
cat > tmux_agent.sh << 'EOF'
#!/bin/bash
# tmux 세션에 에이전트 실행
SESSION_NAME="ai_agent"

# 새 세션 생성
tmux new-session -d -s $SESSION_NAME

# 에이전트 실행
tmux send-keys -t $SESSION_NAME "python agent.py" Enter

# 세션을 포그라운드로 전환
tmux attach -t $SESSION_NAME
EOF

chmod +x tmux_agent.sh
Enter fullscreen mode Exit fullscreen mode
# enhanced_agent.py
import subprocess
import os
import sys
import time

class EnhancedTerminalAgent(TerminalAgent):
    def create_tmux_session(self, session_name: str, command: str):
        """tmux 세션 생성"""
        try:
            # 새 세션 생성
            subprocess.run([
                'tmux', 'new-session', '-d', '-s', session_name
            ], check=True)

            # 명령어 실행
            subprocess.run([
                'tmux', 'send-keys', '-t', session_name, command, 'Enter'
            ], check=True)

            return True
        except subprocess.CalledProcessError:
            return False

    def get_tmux_session_status(self, session_name: str) -> str:
        """tmux 세션 상태 확인"""
        try:
            result = subprocess.run([
                'tmux', 'list-sessions', '-F', '#{session_name}: #{session_attached}'
            ], capture_output=True, text=True)
            return result.stdout
        except:
            return "Error checking session"

# 사용 예시
agent = EnhancedTerminalAgent()
agent.create_tmux_session("coding_session", "vim main.py")
Enter fullscreen mode Exit fullscreen mode

5. 커스텀 도구 개발


python
# custom_tools.py
import os
import glob
from pathlib import Path

class CustomTools:
    @staticmethod
    def find_files(pattern: str, directory: str = ".") -> str:
        """파일 찾기"""
        try:
            search_path = os.path.join(directory, pattern)
            files = glob.glob(search_path, recursive=True)
            return json.dumps(files, indent=2)
        except Exception as e:
            return f"Error: {str(e)}"

    @staticmethod
    def search_code(pattern: str, directory: str = ".") -> str:
        """코드 검색"""
        try:
            results = []
            for root, dirs, files in os.walk(directory):
                for file in files:
                    if file.endswith(('.py', '.js', '.ts', '.html', '.css')):
                        filepath = os.path.join(root, file)
                        with open(filepath, 'r') as f:
                            content = f.read()
                            if pattern in content:
                                results.append({
                                    'file': filepath,
                                    'line': content.count('\n', 0, content.find(pattern)) + 1
                                })
            return json.dumps(results, indent=2)
        except Exception as e:
            return f"Error: {str(e)}"

    @staticmethod
    def get_project_info(directory: str = ".") -> str:
        """프로젝트 정보 조회"""
        info = {
            'current_directory': os.getcwd(),
            'git_status': subprocess.getoutput('git status --porcelain'),
            'file_count': len([f for f in Path(directory).rglob('*') if f.is_file()]),
            'python_files': len([f for f in Path(directory).rglob('*.py') if f.is_file()]),
            'node_modules': os.path.exists(os.path.join(directory, 'node_modules'))
        }
        return json.dumps(info, indent=2)

# 도구 등록
functions = [
    {
        "name": "execute_command",
        "description": "명령어 실행",
        "parameters": {
            "type": "object",
            "properties": {
                "command": {"type": "string"}
            },
            "required": ["command"]
        }
    },
    {
        "name": "find_files",
        "description": "파일 찾기",
        "parameters": {
            "type": "object",
            "properties": {
                "pattern": {"type": "string"},
                "directory": {"type": "string"}
            },
            "required": ["pattern"]
        }
    },
    {
        "name": "search_code",
        "description": "코드 검색",
        "parameters": {
            "type": "object",
            "properties": {


---

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

Top comments (0)