터미널 AI 에이전트 구축 (v34)
요약
터미널 환경에서 동작하는 AI 코드 보조 에이전트를 구축하는 실전 가이드를 제공합니다. Aider, Continue.dev 등 기존 생태계 소개와 함께 Ollama를 활용한 로컬 LLM 설정 및 Python 기반의 커스텀 에이전트 구현 방법을 다룹니다.
핵심 포인트
- Aider, Continue.dev 등 주요 CLI AI 에이전트 생태계 분석
- Ollama 및 LM Studio를 활용한 로컬 LLM API 환경 구축
- Python과 OpenAI SDK를 이용한 커스텀 CLI 에이전트 구현 방법
- 파일 생성 및 명령어 실행 등 도구 사용(Tool Use) 기능 구현
터미널 AI 에이전트 구축 (v34)
터미널에서 AI 코드 보조 도구를 직접 구축하는 실전 가이드
1. CLI AI 에이전트 생태계
현재 CLI AI 에이전트 시장은 다음과 같은 주요 플랫폼들로 구성되어 있습니다:
Aider: GitHub Copilot과 유사하지만 오픈소스 버전. aider --help 명령으로 간단히 시작 가능합니다.
Continue.dev: VSCode 확장 프로그램이지만 터미널에서 사용할 수 있는 라이브러리도 제공. continue 명령을 통해 실행합니다.
OpenCode: 오픈소스 코드 생성 도구. opencode --help로 도움말을 확인할 수 있습니다.
커스텀 스크립트: 가장 유연한 접근 방식. Python 스크립트를 직접 작성하여 원하는 기능을 구현.
# 예시: 간단한 Aider 설치
pip install aider
# 사용 예시
aider --help
2. 로컬 LLM API 엔드포인트 설정
로컬 LLM 서버를 설정하여 비용과 보안 문제를 해결합니다:
# LM Studio 설치 (macOS)
brew install lm-studio
# 또는 Ollama 사용
curl -fsSL https://ollama.com/install.sh | sh
# Ollama로 모델 다운로드
ollama pull mistral:7b
# 로컬 서버 시작
ollama serve
API 엔드포인트 설정:
# config.yaml
model: mistral:7b
port: 11434
host: localhost
timeout: 30
3. Python CLI 에이전트 구축
기본적인 Python CLI 에이전트를 구현합니다:
# ai_agent.py
import openai
import json
import sys
import os
from pathlib import Path
class TerminalAgent:
def __init__(self, model="mistral:7b"):
self.model = model
self.client = openai.OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama"
)
def call_function(self, function_name, args):
"""함수 호출을 처리합니다"""
if function_name == "create_file":
return self.create_file(args['path'], args.get('content', ''))
elif function_name == "run_command":
return self.run_command(args['command'])
elif function_name == "git_add":
return self.git_add(args['files'])
def create_file(self, path, content):
"""파일 생성"""
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content)
return f"Created {path}"
def run_command(self, command):
"""명령어 실행"""
import subprocess
result = subprocess.run(
command, shell=True, capture_output=True, text=True
)
return {
"stdout": result.stdout,
"stderr": result.stderr,
"returncode": result.returncode
}
def git_add(self, files):
"""Git 추가"""
import subprocess
for file in files:
subprocess.run(["git", "add", file])
return f"Added {len(files)} files to git"
# 메인 실행 로직
if __name__ == "__main__":
agent = TerminalAgent()
# 예시: 함수 호출
result = agent.call_function("create_file", {
"path": "test.py",
"content": "print('Hello, World!')"
})
print(result)
4. tmux와 통합
터미널 멀티플렉서와 통합하여 작업 흐름 개선:
# tmux 세션 생성
tmux new-session -s ai_agent -d
# 세션에 명령어 전달
tmux send-keys -t ai_agent "python ai_agent.py --mode chat" Enter
# 세션에 파일 추가
tmux send-keys -t ai_agent "vim main.py" Enter
Python 스크립트에서 tmux 통합:
# tmux_integration.py
import subprocess
import os
class TmuxAgent:
def __init__(self, session_name="ai_session"):
self.session_name = session_name
self._ensure_session()
def _ensure_session(self):
"""세션 존재 여부 확인"""
result = subprocess.run(
["tmux", "has-session", "-t", self.session_name],
capture_output=True
)
if result.returncode != 0:
subprocess.run([
"tmux", "new-session", "-s", self.session_name, "-d"
])
def send_to_session(self, command, pane=0):
"""세션에 명령어 전송"""
subprocess.run([
"tmux", "send-keys", "-t", f"{self.session_name}:{pane}",
command, "Enter"
])
def create_new_pane(self):
"""새로운 파인 생성"""
subprocess.run([
"tmux", "split-window", "-h", "-t", self.session_name
])
5. 커스텀 도구 개발
자신만의 코드 검색 및 Git 도구 개발:
# custom_tools.py
import os
import subprocess
from pathlib import Path
import re
class CodeSearchTool:
def __init__(self, project_root):
self.project_root = Path(project_root)
def search_patterns(self, patterns, file_extensions=None):
"""코드 패턴 검색"""
results = []
if file_extensions is None:
file_extensions = ['.py', '.js', '.ts', '.java']
for root, dirs, files in os.walk(self.project_root):
for file in files:
if any(file.endswith(ext) for ext in file_extensions):
file_path = Path(root) / file
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
for pattern in patterns:
matches = re.finditer(pattern, content)
for match in matches:
results.append({
'file': str(file_path),
'line': content[:match.start()].count('\n') + 1,
'match': match.group()
})
except Exception as e:
continue
return results
class GitTool:
def __init__(self):
self.repo_path = Path(".")
def git_status(self):
"""Git 상태 조회"""
result = subprocess.run(
["git", "status", "--porcelain"],
capture_output=True, text=True
)
return result.stdout.strip().split('\n') if result.stdout else []
def git_diff(self, file_path):
"""Git 차이점 조회"""
result = subprocess.run(
["git", "diff", file_path],
capture_output=True, text=True
)
return result.stdout
# 사용 예제
search_tool = CodeSearchTool(".")
results = search_tool.search_patterns([r"def.*\(.*\)"])
print("검색 결과:", results)
6. 컨텍스트 윈도우 관리
대규모 코드베이스를 위한 컨텍스트 윈도우 관리:
# context_manager.py
import os
import json
from pathlib import Path
class ContextManager:
def __init__(self, max_tokens=4096, context_limit=10000):
self.max_tokens = max_tokens
self.context_limit = context_limit
self.context_cache = {}
def get_context(self, file_path, lines_before=5, lines_after=5):
"""지정된 파일의 컨텍스트 얻기"""
file_path = Path(file_path)
if not file_path.exists():
return ""
with open(file_path, 'r') as f:
lines = f.readlines()
# 현재 커서 위치 찾기 (간단한 예시)
start_line = max(0, 0 - lines_before)
end_line = min(len(lines), 0 + lines_after)
context = "".join(lines[start_line:end_line])
return context
def build_prompt(self, files, additional_context=""):
"""프롬프트 빌드"""
prompt = additional_context + "\n\n"
for file_path in files:
file_context = self.get_context(file_path)
prompt += f"File: {file_path}\n{file_context}\n\n"
return prompt[:self.context_limit]
7. 비용/속도 최적화
로컬 vs API 모델의 비용과 성능 비교:
python
# optimization.py
import time
import subprocess
import json
class ModelOptimizer:
def __init__(self):
self.local_models = {
"mistral": {"size": "7b", "speed": "fast", "cost": "free"},
"llama": {"size": "7b", "speed": "medium", "cost": "free"},
"phi": {"size": "
---
📥 **Get the full guide on Gumroad**: https://gumroad.com/l/auto ($5)
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기