DEV Community

matias yoon
matias yoon

Posted on

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

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

개발자들을 위한 로컬 AI 코딩 에이전트 구축 가이드

1. CLI AI 에이전트 생태계

현재 CLI AI 에이전트 시장은 다양한 솔루션으로 분산되어 있습니다:

주요 플랫폼 비교

Aider: GitHub Copilot 기반의 실시간 코드 작성 도구

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

Continue.dev: VSCode 기반 AI 코딩 도우미

npm install -g continue
Enter fullscreen mode Exit fullscreen mode

OpenCode: 커스텀 AI 모델 연동 가능

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

커스텀 스크립트: 최대한의 커스터마이징이 가능한 방법

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

로컬 LLM을 사용하여 프라이버시와 성능 문제를 해결합니다:

LMStudio 설치 및 실행

# macOS
brew install lmstudio
# 또는 Windows/Linux
wget https://github.com/lmstudio-ai/LMStudio/releases/download/v0.1.25/LMStudio-0.1.25-x86_64.AppImage

# 서버 실행 (기본 포트 1234)
./LMStudio-0.1.25-x86_64.AppImage --port 1234 --model-path ./models/
Enter fullscreen mode Exit fullscreen mode

API 테스트

curl -X POST http://localhost:1234/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "local-model",
    "messages": [
      {"role": "user", "content": "Hello, how are you?"}
    ],
    "temperature": 0.7
  }'
Enter fullscreen mode Exit fullscreen mode

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

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

class TerminalAgent:
    def __init__(self, api_key: str = None, model: str = "gpt-4"):
        # 로컬 API 설정
        if api_key:
            openai.api_key = api_key
        else:
            # 로컬 모델을 위한 설정
            openai.base_url = "http://localhost:1234/v1/"
            openai.api_key = "not-needed"

        self.model = model
        self.functions = [
            {
                "name": "run_command",
                "description": "터미널 명령어 실행",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "command": {"type": "string", "description": "실행할 명령어"}
                    },
                    "required": ["command"]
                }
            },
            {
                "name": "read_file",
                "description": "파일 내용 읽기",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "file_path": {"type": "string", "description": "파일 경로"}
                    },
                    "required": ["file_path"]
                }
            }
        ]

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

    def read_file(self, file_path: str) -> str:
        """파일 내용 읽기"""
        try:
            with open(file_path, 'r') as f:
                return f.read()
        except Exception as e:
            return f"Error reading file: {str(e)}"

    def process_function_call(self, function_name: str, arguments: Dict) -> str:
        """함수 호출 처리"""
        if function_name == "run_command":
            return self.run_command(arguments['command'])
        elif function_name == "read_file":
            return self.read_file(arguments['file_path'])
        return "Unknown function"

    def ask(self, question: str) -> str:
        """질문에 대한 답변 생성"""
        response = openai.ChatCompletion.create(
            model=self.model,
            messages=[
                {"role": "system", "content": "You are a helpful coding assistant that can execute commands and read files."},
                {"role": "user", "content": question}
            ],
            functions=self.functions,
            function_call="auto"
        )

        message = response.choices[0].message
        if message.get('function_call'):
            function_name = message['function_call']['name']
            arguments = json.loads(message['function_call']['arguments'])
            result = self.process_function_call(function_name, arguments)
            return result
        return message.content

def main():
    parser = argparse.ArgumentParser(description='Terminal AI Agent')
    parser.add_argument('question', nargs='*', help='질문 내용')
    args = parser.parse_args()

    agent = TerminalAgent()
    question = ' '.join(args.question) if args.question else "help"

    print(f"Question: {question}")
    answer = agent.ask(question)
    print(answer)

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

4. tmux와 통합

tmux 세션을 활용한 멀티탭 환경 구축:

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

# 세션에 윈도우 추가 (코드 편집용)
tmux new-window -t smart_agent -n editor
# 세션에 윈도우 추가 (터미널용)
tmux new-window -t smart_agent -n terminal

# 세션 실행
tmux attach -t smart_agent
Enter fullscreen mode Exit fullscreen mode

tmux 스크립트 파일

# agent_tmux.sh
#!/bin/bash
SESSION="smart_agent"

# 세션이 없으면 생성
if ! tmux has-session -t $SESSION 2>/dev/null; then
    tmux new-session -d -s $SESSION
    tmux new-window -t $SESSION -n "editor"
    tmux new-window -t $SESSION -n "terminal"
    tmux new-window -t $SESSION -n "logs"
fi

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

5. 사용자 정의 도구 개발

코드 검색 도구

# tools/code_search.py
import os
import re
from pathlib import Path

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

    def search_function(self, function_name: str, file_extensions: list = ['.py', '.js', '.ts']) -> list:
        """함수 정의 검색"""
        results = []

        for file_path in self.root_path.rglob("*"):
            if file_path.suffix in file_extensions:
                try:
                    with open(file_path, 'r') as f:
                        content = f.read()
                        if f"def {function_name}" in content or f"function {function_name}" in content:
                            lines = content.split('\n')
                            for i, line in enumerate(lines):
                                if function_name in line and ('def ' in line or 'function ' in line):
                                    results.append({
                                        'file': str(file_path),
                                        'line': i + 1,
                                        'content': line.strip()
                                    })
                except:
                    continue
        return results

    def search_pattern(self, pattern: str, case_sensitive: bool = True) -> list:
        """정규표현식으로 패턴 검색"""
        results = []
        regex = re.compile(pattern, 0 if case_sensitive else re.IGNORECASE)

        for file_path in self.root_path.rglob("*"):
            if file_path.is_file():
                try:
                    with open(file_path, 'r') as f:
                        content = f.read()
                        matches = regex.finditer(content)
                        for match in matches:
                            results.append({
                                'file': str(file_path),
                                'line': content[:match.start()].count('\n') + 1,
                                'content': match.group().strip()
                            })
                except:
                    continue
        return results
Enter fullscreen mode Exit fullscreen mode

Git 연동 도구


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

class GitTools:
    def __init__(self):
        pass

    def get_staged_files(self) -> List[str]:
        """스테이징된 파일 목록"""
        try:
            result = subprocess.run(['git', 'diff', '--cached', '--name-only'], 
                                   capture_output=True, text=True)
            return result.stdout.strip().split('\n') if result.stdout.strip() else []
        except:
            return []

    def get_git_log(self, limit: int = 10) -> List[Dict]:
        """Git 로그 가져오기"""
        try:
            result = subprocess.run([
                'git', 'log', '--oneline', f'-{limit}'
            ], capture_output=True, text=True)

            logs

---

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

Top comments (0)