notes
목표 구조
AI 작업·프로젝트 목표를 구조화하고 실행 단위로 나누는 메모.
- 텔레그램으로 말하면 내 PC 의 로컬 LLM 이 해석하고 작업을 돌리는 구조다
- 실행은 allowlist 에 있는 명령과 내 계정으로만 제한한다
- polling 으로 시작하고 안정되면 launchd 로 데몬화한다
목표 구조
밖에서 텔레그램으로 메시지를 보내면 내 PC 가 알아서 일을 하게 만들고 싶었다. 텔레그램에서 로컬 허브 서버로, 허브에서 로컬 Llama 로, 그다음 명령 실행으로 이어지는 흐름이다. 클라우드 LLM 없이 내 PC 안에서 끝내는 게 목표다.
스택 (맥 기준)
LLM 런타임은 Ollama, 모델은 llama3.1:8b 로 시작한다. 텔레그램은 python-telegram-bot, 허브는 Python 단일 프로세스로 두고 처음에는 polling 으로 받는다. 자동 실행은 launchd 나 pm2 로 데몬화한다.
llama3.1:8b 는 자료가 많고 도구 호출도 되지만, 도구가 많아지거나 여러 단계를 이어야 하면 흔들린다. 명령 해석이 자꾸 틀리면 qwen3:8b 같은 모델로 바꿔 보는 게 낫다.
로컬 Llama 세팅
brew install ollama
brew services start ollama
ollama pull llama3.1:8b
# 테스트
ollama run llama3.1:8b "안녕, 로컬 에이전트 테스트"
텔레그램 봇은 @BotFather 에서 /newbot 으로 만들고 나온 BOT_TOKEN 을 저장해 둔다.
허브 에이전트 최소 구현
하는 일은 네 가지다. 텔레그램 메시지를 받고, Llama 에 넘겨서 의도와 명령을 해석하고, 허용된 로컬 명령만 실행하고, 결과를 텔레그램으로 돌려준다.
import os, subprocess, requests
from telegram import Update
from telegram.ext import Application, MessageHandler, ContextTypes, filters
TOKEN = os.environ["BOT_TOKEN"]
OWNER_ID = int(os.environ["OWNER_ID"]) # 내 텔레그램 ID
ALLOW = {"disk": ["df", "-h"], "uptime": ["uptime"]}
def ask_llm(text: str) -> str:
r = requests.post("http://localhost:11434/api/chat", json={
"model": "llama3.1:8b",
"stream": False,
"messages": [
{"role": "system", "content": f"다음 중 하나만 답해: {', '.join(ALLOW)}, none"},
{"role": "user", "content": text},
],
}, timeout=60)
return r.json()["message"]["content"].strip()
async def on_message(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
if update.effective_user.id != OWNER_ID:
return
cmd = ask_llm(update.message.text)
if cmd not in ALLOW:
await update.message.reply_text("허용된 명령이 아니야")
return
out = subprocess.run(ALLOW[cmd], capture_output=True, text=True, timeout=30)
await update.message.reply_text(out.stdout[:3500] or out.stderr[:3500])
app = Application.builder().token(TOKEN).build()
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, on_message))
app.run_polling()안전장치
LLM 이 내 PC 에서 명령을 실행하는 구조라 여기가 제일 중요하다. 메시지는 내 텔레그램 ID 에서 온 것만 받고, 실행은 allowlist 에 있는 명령만 한다. LLM 이 명령 문자열을 직접 만들게 두지 않고, 정해진 이름 중에서 고르게만 한다.
rm -rf, sudo, 외부로 데이터를 보내는 명령은 아예 막는다. 로그는 stdout.log 와 stderr.log 로 나눠 둔다.
운영
처음에는 polling 으로 시작한다. 설정이 쉽고 밖에서 들어오는 포트를 열 필요가 없다. 안정되면 webhook 으로 바꿀 수 있지만, 내 PC 라면 polling 으로 두는 쪽이 더 단순하다. 재부팅해도 자동으로 뜨도록 Ollama 는 brew services 로, 허브는 launchd 로 등록한다.
구축 순서
Ollama 와 모델을 설치하고, 봇 토큰을 받고, 텔레그램에서 Ollama 를 거쳐 로컬 실행기로 이어지는 허브 스크립트를 짠다. 그다음 사용자 ID 와 allowlist 를 적용하고, 마지막에 백그라운드 서비스로 등록한다.