DEV Community

matias yoon
matias yoon

Posted on

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

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

터미널에서 작동하는 AI 에이전트를 구축하여 개발 생산성을 향상시킬 수 있습니다. 이 가이드에서는 로컬 LLM API 엔드포인트 설정부터 커스텀 CLI 에이전트 구축까지 실질적인 방법을 설명합니다.

1. CLI AI 에이전트 생태계

현재 CLI AI 에이전트 시장은 다양한 도구로 구성되어 있습니다:

대표 도구 비교

Aider: GitHub Copilot과 유사한 기능, 실시간 코드 생성

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

Continue.dev: VS Code 확장으로 구동되는 터미널 통합

npm install -g continue
continue server
Enter fullscreen mode Exit fullscreen mode

OpenCode: 오픈소스 커뮤니티 기반 터미널 에이전트

git clone https://github.com/opencode/open-code-agent.git
Enter fullscreen mode Exit fullscreen mode

커스텀 스크립트: 최소한의 요구사항만 만족하는 가벼운 에이전트

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

로컬 LLM을 터미널에서 사용하려면 API 서버를 설정해야 합니다.

LM Studio 설치 및 실행

# macOS
brew install lm-studio

# 또는 직접 다운로드
wget https://github.com/lmstudio-ai/LMStudio/releases/latest/download/LMStudio-macOS.dmg

# 실행
open /Applications/LMStudio.app
Enter fullscreen mode Exit fullscreen mode

LocalAI 설정

# Docker를 통한 LocalAI 실행
docker run -p 8080:8080 -v ~/.localai:/models localai/localai:latest

# 모델 다운로드 및 설정
curl -X POST http://localhost:8080/models \
  -H "Content-Type: application/json" \
  -d '{
    "name": "llama3",
    "url": "https://huggingface.co/TheBloke/Llama-3-8B-Instruct-GGUF/resolve/main/llama-3-8b-instruct.Q4_K_M.gguf"
  }'
Enter fullscreen mode Exit fullscreen mode

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

다음은 기능 호출을 지원하는 간단한 CLI 에이전트입니다:

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

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

    def call_llm(self, prompt: str, functions: List[Dict] = None) -> Dict:
        payload = {
            "model": "llama3",
            "prompt": prompt,
            "stream": False
        }

        if functions:
            payload["functions"] = functions

        response = requests.post(
            f"{self.api_url}/generate",
            headers=self.headers,
            json=payload
        )

        return response.json()

    def execute_code(self, code: str) -> str:
        """코드 실행 기능"""
        try:
            exec(code)
            return "코드가 성공적으로 실행되었습니다."
        except Exception as e:
            return f"오류 발생: {str(e)}"

    def search_files(self, pattern: str, directory: str = ".") -> List[str]:
        """파일 검색"""
        import glob
        search_path = os.path.join(directory, pattern)
        return glob.glob(search_path)

def main():
    parser = argparse.ArgumentParser(description="터미널 AI 에이전트")
    parser.add_argument("prompt", help="LLM에게 전달할 프롬프트")
    parser.add_argument("--execute", action="store_true", help="코드 실행")
    parser.add_argument("--search", help="파일 검색 패턴")

    args = parser.parse_args()

    agent = TerminalAgent()

    if args.search:
        files = agent.search_files(args.search)
        print("검색된 파일들:")
        for file in files:
            print(f"  {file}")
        return

    # 기능 정의
    functions = [
        {
            "name": "execute_code",
            "description": "Python 코드를 실행합니다",
            "parameters": {
                "type": "object",
                "properties": {
                    "code": {"type": "string", "description": "실행할 코드"}
                },
                "required": ["code"]
            }
        }
    ]

    result = agent.call_llm(args.prompt, functions)

    if args.execute and result.get("generated_code"):
        print("생성된 코드:")
        print(result["generated_code"])
        response = input("코드를 실행하시겠습니까? (y/n): ")
        if response.lower() == 'y':
            agent.execute_code(result["generated_code"])
    else:
        print("AI 응답:")
        print(result.get("response", "응답 없음"))

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

실행 방법:

# 기본 사용
python ai_agent.py "Python으로 factorial 함수를 만들어줘"

# 파일 검색
python ai_agent.py --search "*.py"

# 코드 실행
python ai_agent.py --execute "import math; print(math.sqrt(16))"
Enter fullscreen mode Exit fullscreen mode

4. tmux와 통합

터미널 에이전트를 tmux 세션과 통합하여 작업 흐름을 개선합니다:

# tmux 세션 생성
tmux new-session -s ai-agent -d

# 세션에 명령어 전달
tmux send-keys -t ai-agent "python ai_agent.py '작업 내용'" Enter

# 세션 목록 보기
tmux ls

# 특정 세션 연결
tmux attach -t ai-agent
Enter fullscreen mode Exit fullscreen mode

tmux 스크립트 자동화

#!/bin/bash
# ai_workflow.sh

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

# 세션에 명령어 실행
tmux send-keys -t developer "cd /path/to/project && git status" Enter

# 세션에 AI 에이전트 실행
tmux send-keys -t developer "python ai_agent.py '필요한 코드를 생성해줘'" Enter

# 세션 연결
tmux attach -t developer
Enter fullscreen mode Exit fullscreen mode

5. 사용자 정의 도구 개발

코드 검색 도구

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

class CodeSearcher:
    def __init__(self, root_dir: str = "."):
        self.root_dir = root_dir
        self.supported_extensions = {'.py', '.js', '.ts', '.java', '.cpp', '.c'}

    def search_patterns(self, pattern: str, case_sensitive: bool = True) -> List[Dict]:
        results = []
        search_flags = 0 if case_sensitive else re.IGNORECASE

        for root, dirs, files in os.walk(self.root_dir):
            for file in files:
                if os.path.splitext(file)[1] in self.supported_extensions:
                    file_path = os.path.join(root, file)
                    try:
                        with open(file_path, 'r', encoding='utf-8') as f:
                            content = f.read()
                            matches = re.finditer(pattern, content, search_flags)
                            for match in matches:
                                results.append({
                                    'file': file_path,
                                    'line': content[:match.start()].count('\n') + 1,
                                    'context': self.get_context(content, match.start(), match.end())
                                })
                    except Exception:
                        continue
        return results

    def get_context(self, content: str, start: int, end: int, context_lines: int = 3) -> str:
        lines = content.split('\n')
        line_num = content[:start].count('\n')
        start_line = max(0, line_num - context_lines)
        end_line = min(len(lines), line_num + context_lines + 1)
        return '\n'.join(lines[start_line:end_line])

# 사용 예제
if __name__ == "__main__":
    searcher = CodeSearcher(".")
    results = searcher.search_patterns("def.*create.*")
    for result in results:
        print(f"{result['file']}:{result['line']}")
        print(f"  {result['context']}")
Enter fullscreen mode Exit fullscreen mode

Git 통합 도구


python
# git_tools.py
import subprocess
import json
from typing import Dict, List

class GitTools:
    @staticmethod
    def get_staged_files() -> List[str]:
        result = subprocess.run(['git', 'diff', '--cached', '--name-only'], 
                               capture_output=True, text=True)
        return result.stdout.strip().split('\n') if result.stdout.strip() else []

    @staticmethod
    def get_diff_stats() -> Dict:
        result = subprocess.run(['git', 'diff', '--stat'], 
                              capture_output=True, text=True)
        return {
            'stat': result.stdout.strip(),
            'status': result.returncode
        }

    @

---

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

Top comments (0)