Tools: 터미널 AI 에이전트 구축 (v17) (2026)
터미널 AI 에이전트 구축 (v17)
1. CLI AI 에이전트 생태계
주요 도구 비교
2. 로컬 LLM API 엔드포인트 설정
3. 간단한 Python CLI 에이전트 구축
4. tmux와 통합
5. 커스텀 도구 개발 터미널에서 작동하는 AI 에이전트를 구축하여 개발 생산성을 극대화하는 방법을 알아봅니다. 이 가이드에서는 오픈소스 도구와 커스텀 솔루션을 사용해 실용적인 터미널 AI 에이전트를 구현하는 방법을 설명합니다. 현재 터미널 AI 에이전트는 여러 플랫폼으로 나뉩니다: Aider는 가장 널리 사용되는 터미널 기반 에이전트로, 코드 생성과 수정에 강점을 가집니다. 그러나 복잡한 작업에는 한계가 있습니다. 로컬 LLM을 터미널에서 사용하기 위해 API 서버를 설정합니다: 터미널 multiplexer와 통합하여 작업 환경을 개선: Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse
# Aider: 코드 생성 및 수정
-weight: 500;">pip -weight: 500;">install aider # Continue.dev: VS Code 확장 + CLI
-weight: 500;">npm -weight: 500;">install -g @continue_dev/cli # OpenCode: GitHub 기반
-weight: 500;">pip -weight: 500;">install opencode # 커스텀 스크립트: 최대한의 커스터마이즈
# Aider: 코드 생성 및 수정
-weight: 500;">pip -weight: 500;">install aider # Continue.dev: VS Code 확장 + CLI
-weight: 500;">npm -weight: 500;">install -g @continue_dev/cli # OpenCode: GitHub 기반
-weight: 500;">pip -weight: 500;">install opencode # 커스텀 스크립트: 최대한의 커스터마이즈
# Aider: 코드 생성 및 수정
-weight: 500;">pip -weight: 500;">install aider # Continue.dev: VS Code 확장 + CLI
-weight: 500;">npm -weight: 500;">install -g @continue_dev/cli # OpenCode: GitHub 기반
-weight: 500;">pip -weight: 500;">install opencode # 커스텀 스크립트: 최대한의 커스터마이즈
# -weight: 500;">docker-compose.yml
version: '3.8'
services: llama-server: image: ghcr.io/ggerganov/llama.cpp:latest command: | --model models/llama-3-8b-instruct.Q4_K_M.gguf --port 8080 --host 0.0.0.0 --n-gpu-layers 35 volumes: - ./models:/models ports: - "8080:8080" deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu]
# -weight: 500;">docker-compose.yml
version: '3.8'
services: llama-server: image: ghcr.io/ggerganov/llama.cpp:latest command: | --model models/llama-3-8b-instruct.Q4_K_M.gguf --port 8080 --host 0.0.0.0 --n-gpu-layers 35 volumes: - ./models:/models ports: - "8080:8080" deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu]
# -weight: 500;">docker-compose.yml
version: '3.8'
services: llama-server: image: ghcr.io/ggerganov/llama.cpp:latest command: | --model models/llama-3-8b-instruct.Q4_K_M.gguf --port 8080 --host 0.0.0.0 --n-gpu-layers 35 volumes: - ./models:/models ports: - "8080:8080" deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu]
# API 서버 시작
-weight: 500;">docker-compose up -d # 테스트 명령어
-weight: 500;">curl -X POST http://localhost:8080/completion \ -H "Content-Type: application/json" \ -d '{ "prompt": "Write a Python function to calculate Fibonacci numbers", "n_predict": 100 }'
# API 서버 시작
-weight: 500;">docker-compose up -d # 테스트 명령어
-weight: 500;">curl -X POST http://localhost:8080/completion \ -H "Content-Type: application/json" \ -d '{ "prompt": "Write a Python function to calculate Fibonacci numbers", "n_predict": 100 }'
# API 서버 시작
-weight: 500;">docker-compose up -d # 테스트 명령어
-weight: 500;">curl -X POST http://localhost:8080/completion \ -H "Content-Type: application/json" \ -d '{ "prompt": "Write a Python function to calculate Fibonacci numbers", "n_predict": 100 }'
# ai_agent.py
import openai
import json
import subprocess
from typing import List, Dict
import os class TerminalAIAgent: def __init__(self, api_base="http://localhost:8080"): self.client = openai.OpenAI( base_url=api_base, api_key="EMPTY" ) def call_function(self, function_name: str, args: dict) -> str: """함수 호출""" if function_name == "execute_command": return self.execute_command(args["command"]) elif function_name == "read_file": return self.read_file(args["filepath"]) elif function_name == "write_file": return self.write_file(args["filepath"], args["content"]) return "Unknown function" def execute_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: os.makedirs(os.path.dirname(filepath), exist_ok=True) 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 process_request(self, user_prompt: str) -> str: """사용자 요청 처리""" messages = [ {"role": "system", "content": "You are a helpful coding assistant. Use available functions to help the user."}, {"role": "user", "content": user_prompt} ] # 함수 호출 정의 tools = [ { "type": "function", "function": { "name": "execute_command", "description": "Execute shell commands", "parameters": { "type": "object", "properties": { "command": {"type": "string", "description": "Shell command to execute"} }, "required": ["command"] } } }, { "type": "function", "function": { "name": "read_file", "description": "Read file content", "parameters": { "type": "object", "properties": { "filepath": {"type": "string", "description": "Path to file"} }, "required": ["filepath"] } } }, { "type": "function", "function": { "name": "write_file", "description": "Write content to file", "parameters": { "type": "object", "properties": { "filepath": {"type": "string", "description": "Path to file"}, "content": {"type": "string", "description": "Content to write"} }, "required": ["filepath", "content"] } } } ] response = self.client.chat.completions.create( model="local-model", messages=messages, tools=tools, tool_choice="auto" ) return response.choices[0].message.content # 사용법
if __name__ == "__main__": agent = TerminalAIAgent() while True: prompt = input("AI Agent> ") if prompt.lower() in ['quit', 'exit']: break result = agent.process_request(prompt) print(result)
# ai_agent.py
import openai
import json
import subprocess
from typing import List, Dict
import os class TerminalAIAgent: def __init__(self, api_base="http://localhost:8080"): self.client = openai.OpenAI( base_url=api_base, api_key="EMPTY" ) def call_function(self, function_name: str, args: dict) -> str: """함수 호출""" if function_name == "execute_command": return self.execute_command(args["command"]) elif function_name == "read_file": return self.read_file(args["filepath"]) elif function_name == "write_file": return self.write_file(args["filepath"], args["content"]) return "Unknown function" def execute_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: os.makedirs(os.path.dirname(filepath), exist_ok=True) 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 process_request(self, user_prompt: str) -> str: """사용자 요청 처리""" messages = [ {"role": "system", "content": "You are a helpful coding assistant. Use available functions to help the user."}, {"role": "user", "content": user_prompt} ] # 함수 호출 정의 tools = [ { "type": "function", "function": { "name": "execute_command", "description": "Execute shell commands", "parameters": { "type": "object", "properties": { "command": {"type": "string", "description": "Shell command to execute"} }, "required": ["command"] } } }, { "type": "function", "function": { "name": "read_file", "description": "Read file content", "parameters": { "type": "object", "properties": { "filepath": {"type": "string", "description": "Path to file"} }, "required": ["filepath"] } } }, { "type": "function", "function": { "name": "write_file", "description": "Write content to file", "parameters": { "type": "object", "properties": { "filepath": {"type": "string", "description": "Path to file"}, "content": {"type": "string", "description": "Content to write"} }, "required": ["filepath", "content"] } } } ] response = self.client.chat.completions.create( model="local-model", messages=messages, tools=tools, tool_choice="auto" ) return response.choices[0].message.content # 사용법
if __name__ == "__main__": agent = TerminalAIAgent() while True: prompt = input("AI Agent> ") if prompt.lower() in ['quit', 'exit']: break result = agent.process_request(prompt) print(result)
# ai_agent.py
import openai
import json
import subprocess
from typing import List, Dict
import os class TerminalAIAgent: def __init__(self, api_base="http://localhost:8080"): self.client = openai.OpenAI( base_url=api_base, api_key="EMPTY" ) def call_function(self, function_name: str, args: dict) -> str: """함수 호출""" if function_name == "execute_command": return self.execute_command(args["command"]) elif function_name == "read_file": return self.read_file(args["filepath"]) elif function_name == "write_file": return self.write_file(args["filepath"], args["content"]) return "Unknown function" def execute_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: os.makedirs(os.path.dirname(filepath), exist_ok=True) 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 process_request(self, user_prompt: str) -> str: """사용자 요청 처리""" messages = [ {"role": "system", "content": "You are a helpful coding assistant. Use available functions to help the user."}, {"role": "user", "content": user_prompt} ] # 함수 호출 정의 tools = [ { "type": "function", "function": { "name": "execute_command", "description": "Execute shell commands", "parameters": { "type": "object", "properties": { "command": {"type": "string", "description": "Shell command to execute"} }, "required": ["command"] } } }, { "type": "function", "function": { "name": "read_file", "description": "Read file content", "parameters": { "type": "object", "properties": { "filepath": {"type": "string", "description": "Path to file"} }, "required": ["filepath"] } } }, { "type": "function", "function": { "name": "write_file", "description": "Write content to file", "parameters": { "type": "object", "properties": { "filepath": {"type": "string", "description": "Path to file"}, "content": {"type": "string", "description": "Content to write"} }, "required": ["filepath", "content"] } } } ] response = self.client.chat.completions.create( model="local-model", messages=messages, tools=tools, tool_choice="auto" ) return response.choices[0].message.content # 사용법
if __name__ == "__main__": agent = TerminalAIAgent() while True: prompt = input("AI Agent> ") if prompt.lower() in ['quit', 'exit']: break result = agent.process_request(prompt) print(result)
# tmux 세션 생성
tmux new-session -d -s ai_session # 세션에 명령어 전달
tmux send-keys -t ai_session "python ai_agent.py" Enter # 세션에 파일 전송
tmux send-keys -t ai_session "cat main.py" Enter
# tmux 세션 생성
tmux new-session -d -s ai_session # 세션에 명령어 전달
tmux send-keys -t ai_session "python ai_agent.py" Enter # 세션에 파일 전송
tmux send-keys -t ai_session "cat main.py" Enter
# tmux 세션 생성
tmux new-session -d -s ai_session # 세션에 명령어 전달
tmux send-keys -t ai_session "python ai_agent.py" Enter # 세션에 파일 전송
tmux send-keys -t ai_session "cat main.py" Enter
# tmux 스크립트
#!/bin/bash
# ai_tmux.sh SESSION_NAME="ai_dev"
WINDOW_NAME="agent" # 세션이 존재하는지 확인
if ! tmux has-session -t $SESSION_NAME 2>/dev/null; then # 새 세션 생성 tmux new-session -d -s $SESSION_NAME tmux new-window -t $SESSION_NAME:$WINDOW_NAME
fi # 윈도우로 전환
tmux select-window -t $SESSION_NAME:$WINDOW_NAME # 작업 디렉토리 설정
tmux send-keys -t $SESSION_NAME:$WINDOW_NAME "cd ~/projects/myapp" Enter
tmux send-keys -t $SESSION_NAME:$WINDOW_NAME "export PYTHONPATH=." Enter echo "AI session ready in tmux"
# tmux 스크립트
#!/bin/bash
# ai_tmux.sh SESSION_NAME="ai_dev"
WINDOW_NAME="agent" # 세션이 존재하는지 확인
if ! tmux has-session -t $SESSION_NAME 2>/dev/null; then # 새 세션 생성 tmux new-session -d -s $SESSION_NAME tmux new-window -t $SESSION_NAME:$WINDOW_NAME
fi # 윈도우로 전환
tmux select-window -t $SESSION_NAME:$WINDOW_NAME # 작업 디렉토리 설정
tmux send-keys -t $SESSION_NAME:$WINDOW_NAME "cd ~/projects/myapp" Enter
tmux send-keys -t $SESSION_NAME:$WINDOW_NAME "export PYTHONPATH=." Enter echo "AI session ready in tmux"
# tmux 스크립트
#!/bin/bash
# ai_tmux.sh SESSION_NAME="ai_dev"
WINDOW_NAME="agent" # 세션이 존재하는지 확인
if ! tmux has-session -t $SESSION_NAME 2>/dev/null; then # 새 세션 생성 tmux new-session -d -s $SESSION_NAME tmux new-window -t $SESSION_NAME:$WINDOW_NAME
fi # 윈도우로 전환
tmux select-window -t $SESSION_NAME:$WINDOW_NAME # 작업 디렉토리 설정
tmux send-keys -t $SESSION_NAME:$WINDOW_NAME "cd ~/projects/myapp" Enter
tmux send-keys -t $SESSION_NAME:$WINDOW_NAME "export PYTHONPATH=." Enter echo "AI session ready in tmux"
python
# custom_tools.py
import os
import subprocess
import re
from typing import List class CodeSearchTool: """코드 검색 도구""" @staticmethod def search_code(pattern: str, directory: str = ".") -> List[str]: """정규 표현식으로 코드 검색""" results = [] try: cmd = f"find {directory} -name '*.py' -type f -exec grep -l '{pattern}' {{}} \\;" output = subprocess.check_output(cmd, shell=True, text=True) files = output.strip().split('\n') for file in files: if file: results.append(file) except: pass return results @staticmethod def grep_content(pattern: str, directory: str = ".") -> List[str]: """내용 검색""" try: cmd = f"find {directory} -name '*.py' -type f -exec grep -n '{pattern}' {{}} \\;" output = subprocess.check_output(cmd, shell=True, text=True) return output.strip().split('\n') except: return [] class GitTool: """Git 도구""" @staticmethod def get_recent_commits(n: int = 5) -> str: """최근 커밋 목록 가져오기""" try: cmd = f"-weight: 500;">git log --oneline -{n}" output = subprocess.check_output(cmd, shell=True, text=True) return output.strip() except: return "No -weight: 500;">git repository" @staticmethod def get_status() -> str: """Git 상태""" try: cmd = "-weight: 500;">git -weight: 500;">status --- 📥 **Get the full guide on Gumroad**: https://gumroad.com/l/auto ($5)
python
# custom_tools.py
import os
import subprocess
import re
from typing import List class CodeSearchTool: """코드 검색 도구""" @staticmethod def search_code(pattern: str, directory: str = ".") -> List[str]: """정규 표현식으로 코드 검색""" results = [] try: cmd = f"find {directory} -name '*.py' -type f -exec grep -l '{pattern}' {{}} \\;" output = subprocess.check_output(cmd, shell=True, text=True) files = output.strip().split('\n') for file in files: if file: results.append(file) except: pass return results @staticmethod def grep_content(pattern: str, directory: str = ".") -> List[str]: """내용 검색""" try: cmd = f"find {directory} -name '*.py' -type f -exec grep -n '{pattern}' {{}} \\;" output = subprocess.check_output(cmd, shell=True, text=True) return output.strip().split('\n') except: return [] class GitTool: """Git 도구""" @staticmethod def get_recent_commits(n: int = 5) -> str: """최근 커밋 목록 가져오기""" try: cmd = f"-weight: 500;">git log --oneline -{n}" output = subprocess.check_output(cmd, shell=True, text=True) return output.strip() except: return "No -weight: 500;">git repository" @staticmethod def get_status() -> str: """Git 상태""" try: cmd = "-weight: 500;">git -weight: 500;">status --- 📥 **Get the full guide on Gumroad**: https://gumroad.com/l/auto ($5)
python
# custom_tools.py
import os
import subprocess
import re
from typing import List class CodeSearchTool: """코드 검색 도구""" @staticmethod def search_code(pattern: str, directory: str = ".") -> List[str]: """정규 표현식으로 코드 검색""" results = [] try: cmd = f"find {directory} -name '*.py' -type f -exec grep -l '{pattern}' {{}} \\;" output = subprocess.check_output(cmd, shell=True, text=True) files = output.strip().split('\n') for file in files: if file: results.append(file) except: pass return results @staticmethod def grep_content(pattern: str, directory: str = ".") -> List[str]: """내용 검색""" try: cmd = f"find {directory} -name '*.py' -type f -exec grep -n '{pattern}' {{}} \\;" output = subprocess.check_output(cmd, shell=True, text=True) return output.strip().split('\n') except: return [] class GitTool: """Git 도구""" @staticmethod def get_recent_commits(n: int = 5) -> str: """최근 커밋 목록 가져오기""" try: cmd = f"-weight: 500;">git log --oneline -{n}" output = subprocess.check_output(cmd, shell=True, text=True) return output.strip() except: return "No -weight: 500;">git repository" @staticmethod def get_status() -> str: """Git 상태""" try: cmd = "-weight: 500;">git -weight: 500;">status --- 📥 **Get the full guide on Gumroad**: https://gumroad.com/l/auto ($5)