DEV Community

matias yoon
matias yoon

Posted on

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

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

터미널에서 작동하는 AI 에이전트를 구축하는 것은 현대 개발자에게 매우 실용적인 도구입니다. 이 가이드에서는 실제 개발 workflow에 통합할 수 있는 로컬 LLM 기반 CLI 에이전트를 구축하는 방법을 설명합니다.

1. CLI AI 에이전트 생태계

현재 CLI AI 에이전트 시장에는 여러 선택지가 있습니다:

Aider: Git 기반 코드 수정을 위한 간단한 에이전트

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

Continue.dev: VSCode 확장으로도 사용 가능하지만 CLI 버전도 존재

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

OpenCode: 오픈소스 CLI 에이전트로 커스터마이징이 용이

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

커스텀 스크립트: 최대한의 유연성을 위해 직접 구현

# simple-agent.py
import openai
import os

def main():
    client = openai.Client(api_key=os.getenv("OPENAI_API_KEY"))
    response = client.chat.completions.create(
        model="gpt-4-turbo",
        messages=[{"role": "user", "content": "Hello"}]
    )
    print(response.choices[0].message.content)
Enter fullscreen mode Exit fullscreen mode

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

로컬 LLM을 사용하여 비용과 보안 문제를 해결할 수 있습니다. LM Studio 또는 Ollama를 사용하는 것을 추천합니다.

Ollama 설치 및 실행:

# Ubuntu/Debian
curl -fsSL https://ollama.com/install.sh | sh

# 시작
ollama serve

# 모델 다운로드
ollama pull llama3.2:3b
ollama pull codestral:22b
Enter fullscreen mode Exit fullscreen mode

로컬 API 서버 구성:

# ollama-config.yaml
model: llama3.2:3b
host: 0.0.0.0
port: 11434
Enter fullscreen mode Exit fullscreen mode
# API 서버 시작
ollama serve -c ollama-config.yaml
Enter fullscreen mode Exit fullscreen mode

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

#!/usr/bin/env python3
# ai-agent.py

import openai
import os
import json
import argparse
from pathlib import Path

class TerminalAgent:
    def __init__(self):
        # 로컬 Ollama 사용
        self.client = openai.OpenAI(
            base_url="http://localhost:11434/v1/",
            api_key="ollama"
        )
        self.model = "llama3.2:3b"

    def get_context(self):
        """현재 디렉토리의 코드 컨텍스트를 수집"""
        context = []

        # 현재 디렉토리 파일 목록
        current_dir = Path(".")
        for file_path in current_dir.rglob("*"):
            if file_path.is_file() and file_path.suffix in ['.py', '.js', '.ts', '.html', '.css']:
                try:
                    with open(file_path, 'r', encoding='utf-8') as f:
                        content = f.read()
                        context.append(f"File: {file_path}\n{content[:500]}...")
                except Exception as e:
                    context.append(f"File: {file_path} - Error reading file")

        return "\n\n".join(context)

    def analyze_code(self, code_snippet, task):
        """코드 분석 및 제안"""
        prompt = f"""
        Analyze the following code and provide suggestions:
        Task: {task}
        Code:
        {code_snippet}

        Please provide:
        1. Code quality assessment
        2. Performance improvements
        3. Security considerations
        4. Best practices
        """

        response = self.client.chat.completions.create(
            model=self.model,
            messages=[{"role": "user", "content": prompt}],
            temperature=0.3
        )

        return response.choices[0].message.content

def main():
    parser = argparse.ArgumentParser(description='Terminal AI Agent')
    parser.add_argument('--analyze', help='Analyze specific code snippet')
    parser.add_argument('--task', help='Task description for analysis')
    parser.add_argument('--file', help='File to analyze')

    args = parser.parse_args()

    agent = TerminalAgent()

    if args.file:
        with open(args.file, 'r') as f:
            code = f.read()
        result = agent.analyze_code(code, args.task or "Code review")
        print(result)
    elif args.analyze:
        result = agent.analyze_code(args.analyze, args.task or "Code review")
        print(result)
    else:
        print("Usage: python ai-agent.py --file <filename> --task 'task description'")
        print("OR: python ai-agent.py --analyze 'code snippet' --task 'task description'")

if __name__ == "__main__":
    main()
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 --file main.py --task 'Optimize database queries'" Enter

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

tmux 스크립트 (tmux-agent.sh):

#!/bin/bash
# tmux-agent.sh

SESSION="ai-agent"
WINDOW="code-review"

# 세션 존재 여부 확인
if ! tmux has-session -t $SESSION 2>/dev/null; then
    tmux new-session -d -s $SESSION -n $WINDOW
fi

# 현재 작업 디렉토리에서 실행
tmux send-keys -t $SESSION:$WINDOW "cd $(pwd) && python ai-agent.py --file $1 --task '$2'" Enter
Enter fullscreen mode Exit fullscreen mode

5. 사용자 정의 도구 개발

다음은 몇 가지 유용한 커스텀 도구입니다:

코드 검색 도구 (code-search.py):

#!/usr/bin/env python3
import os
import re
from pathlib import Path

class CodeSearcher:
    def __init__(self, root_dir="."):
        self.root_dir = Path(root_dir)

    def search_functions(self, pattern):
        """함수 정의 검색"""
        results = []
        for py_file in self.root_dir.rglob("*.py"):
            with open(py_file, 'r') as f:
                content = f.read()
                # 함수 정의 찾기
                func_pattern = rf'def\s+({pattern})\s*\('
                matches = re.finditer(func_pattern, content)
                for match in matches:
                    line_num = content[:match.start()].count('\n') + 1
                    results.append({
                        'file': str(py_file),
                        'function': match.group(1),
                        'line': line_num
                    })
        return results

    def search_patterns(self, pattern):
        """일반 패턴 검색"""
        results = []
        for file_path in self.root_dir.rglob("*"):
            if file_path.is_file() and file_path.suffix in ['.py', '.js', '.ts']:
                try:
                    with open(file_path, 'r') as f:
                        content = f.read()
                        matches = re.finditer(pattern, content)
                        for match in matches:
                            line_num = content[:match.start()].count('\n') + 1
                            results.append({
                                'file': str(file_path),
                                'match': match.group(),
                                'line': line_num
                            })
                except Exception:
                    continue
        return results

# 사용법
searcher = CodeSearcher(".")
functions = searcher.search_functions(r"test_.*")
print(json.dumps(functions, indent=2))
Enter fullscreen mode Exit fullscreen mode

Git 통합 도구 (git-tools.py):

import subprocess
import json

class GitTools:
    @staticmethod
    def get_changed_files():
        """변경된 파일 목록 가져오기"""
        try:
            result = subprocess.run(
                ['git', 'diff', '--name-only', 'HEAD~1', 'HEAD'],
                capture_output=True, text=True
            )
            return result.stdout.strip().split('\n') if result.stdout.strip() else []
        except Exception as e:
            return []

    @staticmethod
    def get_git_history():
        """Git 히스토리 가져오기"""
        try:
            result = subprocess.run(
                ['git', 'log', '--oneline', '-10'],
                capture_output=True, text=True
            )
            return result.stdout.strip().split('\n')
        except Exception as e:
            return []

# 사용법
git_tool = GitTools()
changed_files = git_tool.get_changed_files()
print("Changed files:", changed_files)
Enter fullscreen mode Exit fullscreen mode

6. 컨텍스트 창 관리

대규모


📥 Get the full guide on Gumroad: https://gumroad.com/l/auto ($5)

Top comments (0)