DEV Community

matias yoon
matias yoon

Posted on

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

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

터미널에서 작동하는 AI 에이전트를 직접 구축하여 개발 생산성을 높이는 방법을 안내합니다. 이 가이드는 로컬에서 실행 가능한 고성능 AI 에이전트를 구축하는 실용적인 접근법을 제공합니다.

1. CLI AI 에이전트 생태계

현재 터미널 AI 에이전트 시장은 다음과 같은 주요 플랫폼으로 구성되어 있습니다:

주요 도구 비교

Aider:

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

Continue.dev:

npm install -g @continue_dev/cli
Enter fullscreen mode Exit fullscreen mode

OpenCode:

git clone https://github.com/open-code/open-code.git
cd open-code && pip install -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

이 중 OpenCode가 가장 간단하고 커스터마이징이 용이합니다.

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

로컬 LLM을 위한 API 엔드포인트를 설정합니다:

# llama.cpp 설치
git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp
make

# 모델 다운로드 및 변환
wget https://huggingface.co/TheBloke/Mistral-7B-v0.1-GGUF/resolve/main/mistral-7b-v0.1.Q4_K_M.gguf
./llama.cpp/llama-server --model mistral-7b-v0.1.Q4_K_M.gguf --port 8080
Enter fullscreen mode Exit fullscreen mode

API 테스트:

curl -X POST http://localhost:8080/completion \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Write a Python function to calculate Fibonacci numbers",
    "max_tokens": 100
  }'
Enter fullscreen mode Exit fullscreen mode

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

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

class TerminalAgent:
    def __init__(self, model="http://localhost:8080"):
        self.model = model
        self.functions = {
            "run_command": self.run_command,
            "read_file": self.read_file,
            "write_file": self.write_file,
            "search_code": self.search_code
        }

    def run_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 read_file(self, filepath: str) -> str:
        """파일 읽기"""
        try:
            with open(filepath, 'r') as f:
                return f.read()
        except Exception as e:
            return f"Error reading file: {str(e)}"

    def write_file(self, filepath: str, content: str) -> str:
        """파일 쓰기"""
        try:
            with open(filepath, 'w') as f:
                f.write(content)
            return f"File written to {filepath}"
        except Exception as e:
            return f"Error writing file: {str(e)}"

    def search_code(self, pattern: str, directory: str = ".") -> str:
        """코드 검색"""
        try:
            result = subprocess.run(
                f"find {directory} -type f -name '*.py' -exec grep -l '{pattern}' {{}} \\;",
                shell=True, capture_output=True, text=True
            )
            return result.stdout
        except Exception as e:
            return f"Search error: {str(e)}"

    def call_function(self, name: str, arguments: Dict[str, Any]) -> str:
        """함수 호출"""
        if name in self.functions:
            return self.functions[name](**arguments)
        return f"Unknown function: {name}"

    def process_request(self, prompt: str, tools: List[str] = None) -> str:
        """요청 처리"""
        # 툴 정보 생성
        tool_descriptions = []
        for tool in tools or []:
            if tool in self.functions:
                tool_descriptions.append({
                    "name": tool,
                    "description": f"Use {tool} for specific tasks",
                    "parameters": {}
                })

        # 실제 API 호출 (llama.cpp API)
        response = self._call_llm(prompt, tool_descriptions)
        return response

    def _call_llm(self, prompt: str, tools: List[Dict]) -> str:
        """LLM 호출"""
        # 실제 구현은 외부 API 호출
        return f"LLM response to: {prompt}"

if __name__ == "__main__":
    agent = TerminalAgent()
    print("Terminal AI Agent ready. Type 'quit' to exit.")

    while True:
        user_input = input(">>> ")
        if user_input.lower() == 'quit':
            break

        response = agent.process_request(user_input, ["run_command", "read_file"])
        print(response)
Enter fullscreen mode Exit fullscreen mode

4. tmux와 통합

터미널 세션 관리를 위해 tmux 통합:

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

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

# 세션 정보 확인
tmux list-sessions
Enter fullscreen mode Exit fullscreen mode

Python 스크립트에서 tmux 사용:

# tmux_integration.py
import subprocess
import os

class TmuxManager:
    def __init__(self, session_name: str = "ai_agent"):
        self.session_name = session_name

    def create_session(self):
        """세션 생성"""
        subprocess.run(["tmux", "new-session", "-d", "-s", self.session_name])

    def send_command(self, command: str):
        """세션에 명령어 전송"""
        subprocess.run([
            "tmux", "send-keys", "-t", self.session_name, command, "Enter"
        ])

    def get_output(self) -> str:
        """출력 가져오기"""
        result = subprocess.run([
            "tmux", "capture-pane", "-p", "-t", self.session_name
        ], capture_output=True, text=True)
        return result.stdout

# 사용 예제
tmux = TmuxManager()
tmux.create_session()
tmux.send_command("python ai_agent.py")
Enter fullscreen mode Exit fullscreen mode

5. 커스텀 툴 개발

코드 검색 툴

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

class CodeSearchTool:
    def __init__(self, project_root: str = "."):
        self.project_root = project_root

    def search_patterns(self, patterns: List[str], file_extensions: List[str] = None) -> Dict[str, List[str]]:
        """패턴 검색"""
        if file_extensions is None:
            file_extensions = ['.py', '.js', '.cpp', '.java']

        results = {}
        for pattern in patterns:
            results[pattern] = []
            for root, dirs, files in os.walk(self.project_root):
                for file in files:
                    if any(file.endswith(ext) for ext in file_extensions):
                        filepath = os.path.join(root, file)
                        try:
                            with open(filepath, 'r') as f:
                                content = f.read()
                                matches = re.finditer(pattern, content)
                                for match in matches:
                                    results[pattern].append({
                                        'file': filepath,
                                        'line': content[:match.start()].count('\n') + 1,
                                        'context': self._get_context(content, match.start())
                                    })
                        except Exception:
                            continue
        return results

    def _get_context(self, content: str, position: int, context_lines: int = 3) -> str:
        """문맥 추출"""
        lines = content.split('\n')
        line_num = content[:position].count('\n')
        start = max(0, line_num - context_lines)
        end = min(len(lines), line_num + context_lines + 1)
        return '\n'.join(lines[start:end])

# 사용 예제
search_tool = CodeSearchTool("/path/to/project")
results = search_tool.search_patterns([r"def.*function"])
Enter fullscreen mode Exit fullscreen mode

Git 통합 툴


python
# tools/git_tool.py
import subprocess
import json
from datetime import datetime

class GitTool:
    def __init__(self, repo_path: str = "."):
        self.repo_path = repo_path

    def get_commits(self, limit: int = 10) -> List[Dict]:
        """최근 커밋 목록"""
        cmd = [
            "git", "-C", self.repo_path,
            "log", "--oneline", f"--max-count={limit}",
            "--format=%H|%an|%ad|%s"
        ]
        result = subprocess.run(cmd, capture_output=True, text=True)

        commits = []
        for line in result.stdout.strip().split('\n'):
            if line:
                hash_, author, date, message = line.split

---

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

Top comments (0)