DEV Community

matias yoon
matias yoon

Posted on

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

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

터미널에서 작동하는 AI 에이전트는 개발자들에게 강력한 도구가 되지만, 대부분의 기존 솔루션은 복잡하거나 클라우드 기반으로 의존합니다. 이 가이드는 로컬에서 작동하는 가벼운 AI 에이전트를 구축하여 코드 리뷰, 자동완성, 프로젝트 탐색을 수행하는 실용적인 방법을 설명합니다.

1. CLI AI 에이전트 랜드스케이프

기존 솔루션 비교

Aider: GitHub에서 개발한 코드 에이전트로, 코드 변경을 직접 수정합니다. 하지만 GUI 기반으로 구조화되어 있어 터미널에서 사용하기 어려움.

Continue.dev: VSCode 확장 기반의 AI 에이전트. 터미널에서 사용하려면 복잡한 설정 필요.

OpenCode: 코드 기반 LLM 도구로, 로컬 모델 사용 가능하지만 커스터마이즈가 어려움.

사용자 정의 스크립트: 터미널에서 사용 가능한 가장 유연한 방식으로, 필요한 기능만 포함 가능.

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

LM Studio를 통한 로컬 LLM 실행

# LM Studio 설치 (macOS)
brew install --cask lm-studio

# 또는 직접 다운로드
# https://lmstudio.ai/

# 로컬 서버 시작 (기본 포트 1234)
lm-studio-server --port 1234
Enter fullscreen mode Exit fullscreen mode

API 서버 테스트

# 테스트 요청
curl -X POST http://localhost:1234/v1/completions \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Hello, how are you?",
    "max_tokens": 100
  }'
Enter fullscreen mode Exit fullscreen mode

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

기본 에이전트 스크립트

# aider_agent.py
import os
import json
import requests
import argparse
from typing import Dict, List, Any

class TerminalAgent:
    def __init__(self, api_url: str = "http://localhost:1234/v1/completions"):
        self.api_url = api_url
        self.headers = {"Content-Type": "application/json"}

    def call_model(self, prompt: str) -> str:
        """로컬 LLM API 호출"""
        payload = {
            "prompt": prompt,
            "max_tokens": 500,
            "temperature": 0.7
        }

        try:
            response = requests.post(
                self.api_url,
                headers=self.headers,
                json=payload,
                timeout=30
            )
            response.raise_for_status()
            return response.json()['choices'][0]['text']
        except Exception as e:
            return f"에러 발생: {str(e)}"

    def code_review(self, file_path: str) -> str:
        """코드 리뷰 요청"""
        with open(file_path, 'r') as f:
            code = f.read()

        prompt = f"""
        다음 코드를 분석하고, 개선 제안을 제공해주세요:

        {code}

        요청사항:
        1. 버그나 문제점
        2. 성능 개선 제안
        3. 코드 스타일 개선
        """

        return self.call_model(prompt)

    def generate_docstring(self, function_code: str) -> str:
        """함수 문서화 생성"""
        prompt = f"""
        다음 함수의 문서화를 생성해주세요:

        {function_code}

        문서화 형식:
        - 함수 설명
        - 파라미터 설명
        - 반환값 설명
        """

        return self.call_model(prompt)

def main():
    parser = argparse.ArgumentParser(description="터미널 AI 에이전트")
    parser.add_argument("--review", help="리뷰할 파일 경로")
    parser.add_argument("--docstring", help="문서화할 함수 코드")

    args = parser.parse_args()

    agent = TerminalAgent()

    if args.review:
        result = agent.code_review(args.review)
        print("코드 리뷰 결과:")
        print(result)

    if args.docstring:
        result = agent.generate_docstring(args.docstring)
        print("문서화 결과:")
        print(result)

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

실행 방법

# 코드 리뷰
python aider_agent.py --review src/main.py

# 문서화 생성
python aider_agent.py --docstring "def hello(name): return f'Hello {name}'"
Enter fullscreen mode Exit fullscreen mode

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

tmux 세션에 에이전트 통합

# tmux 세션 생성
tmux new-session -d -s ai_agent

# 세션에 명령어 실행
tmux send-keys -t ai_agent "python aider_agent.py --review src/main.py" Enter

# 세션에 명령어 실행 (대화형)
tmux send-keys -t ai_agent "python aider_agent.py" Enter
Enter fullscreen mode Exit fullscreen mode

tmux 스크립트 설정

# ai_session.sh
#!/bin/bash
SESSION="ai_dev"

# 새로운 세션 생성
tmux new-session -d -s $SESSION

# 첫 번째 윈도우: 코드 리뷰
tmux new-window -t $SESSION -n "review"
tmux send-keys -t $SESSION:0 "python aider_agent.py --review" Enter

# 두 번째 윈도우: 코딩
tmux new-window -t $SESSION -n "coding"
tmux send-keys -t $SESSION:1 "vim" Enter

# 세션에 연결
tmux attach -t $SESSION
Enter fullscreen mode Exit fullscreen mode

5. 사용자 정의 도구 개발

코드 검색 도구

# code_search.py
import os
import re
from pathlib import Path
from typing import List, Dict

class CodeSearcher:
    def __init__(self, project_root: str):
        self.project_root = Path(project_root)
        self.ignore_patterns = [
            '.git', '__pycache__', '.venv', '.env'
        ]

    def search_functions(self, pattern: str, file_extensions: List[str] = None) -> List[Dict]:
        """함수 검색"""
        if file_extensions is None:
            file_extensions = ['.py', '.js', '.ts']

        results = []

        for file_path in self.project_root.rglob('*'):
            if file_path.is_file() and file_path.suffix in file_extensions:
                if any(ignore in str(file_path) for ignore in self.ignore_patterns):
                    continue

                try:
                    with open(file_path, 'r') as f:
                        content = f.read()

                    # 함수 정의 찾기
                    function_pattern = rf'def\s+(\w+)\s*\([^)]*\)'
                    functions = re.findall(function_pattern, content)

                    if pattern.lower() in file_path.name.lower():
                        results.append({
                            'file': str(file_path),
                            'functions': functions,
                            'line_count': len(content.split('\n'))
                        })

                except Exception:
                    continue

        return results

    def search_imports(self, module_name: str) -> List[str]:
        """모듈 검색"""
        results = []
        for file_path in self.project_root.rglob('*.py'):
            if any(ignore in str(file_path) for ignore in self.ignore_patterns):
                continue

            try:
                with open(file_path, 'r') as f:
                    content = f.read()

                if f'import {module_name}' in content or f'from {module_name}' in content:
                    results.append(str(file_path))

            except Exception:
                continue

        return results

# 사용 예시
searcher = CodeSearcher('.')
functions = searcher.search_functions('database')
print(json.dumps(functions, indent=2))
Enter fullscreen mode Exit fullscreen mode

Git 연동 도구


python
# git_tools.py
import subprocess
import json
from datetime import datetime

class GitTools:
    @staticmethod
    def get_recent_commits(limit: int = 5) -> List[Dict]:
        """최근 커밋 정보 가져오기"""
        try:
            result = subprocess.run(
                ['git', 'log', f'--max-count={limit}', '--format=%H|%an|%ae|%s|%ad'],
                capture_output=True,
                text=True,
                check=True
            )

            commits = []
            for line in result.stdout.strip().split('\n'):
                if line:
                    parts = line.split('|')
                    commits.append({
                        'hash': parts[0],
                        'author': parts[1],
                        'email': parts[2],
                        'message': parts[3],
                        'date': parts[4]
                    })

            return commits
        except subprocess.CalledProcessError:
            return []

    @staticmethod
    def get_branch_status() -> Dict:
        """브랜치 상태 정보"""
        try:
            #

---

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

Top comments (0)