아키텍처 개요
StarNion은 완전히 자체 호스팅(self-hosted) 가능한 AI 개인 어시스턴트입니다. 모든 데이터는 사용자의 서버에 저장되며, 5개의 핵심 서비스로 구성됩니다.
전체 시스템 구조
┌─────────────────────────────────────────────────────────┐
│ 사용자 접근 │
│ │
│ 웹 브라우저 텔레그램 앱 │
│ │ │ │
└───────┼────────────────────┼────────────────────────────┘
│ │
▼ ▼
┌───────────────┐ ┌────────────────────────────────────┐
│ UI (Next.js) │ │ Gateway (Go) │
│ :3893 │──▶│ :8080 │
│ │ │ │
│ - 채팅 UI │ │ ┌──────────┐ ┌────────────────┐ │
│ - 대시보드 │ │ │ REST API │ │ Telegram Bot │ │
│ - 24+ 페이지 │ │ │ /api/v1/ │ │ Manager │ │
│ - 설정 │ │ └────┬─────┘ └───────┬────────┘ │
└───────────────┘ │ │ │ │
│ ┌────┴────────────────┘ │
│ │ WebSocket Hub (/ws/chat) │
│ └────────────────┬─────────────────┘
│ │ gRPC
└───────────────────┼────────────────────┘
│
▼
┌───────────────────────────────────────┐
│ Agent (TypeScript) │
│ :50051 │
│ │
│ ┌─────────────────────────────────┐ │
│ │ AI SDK v5 · 멀티 LLM │ │
│ │ │ │
│ │ 24+ 스킬: finance, diary, │ │
│ │ goals, search, wellness, ... │ │
│ └──────────────┬──────────────────┘ │
│ │ │
│ ┌──────────────┴──────────────────┐ │
│ │ SSE Streaming │ │
│ └──────────────┬──────────────────┘ │
└─────────────────┼──────────────────────┘
│
┌─────────────────┴──────────────────────┐
│ │
▼ ▼ │
┌──────────────────┐ ┌──────────────────────┐ │
│ PostgreSQL │ │ MinIO │ │
│ (pgvector) │ │ (Object Storage) │ │
│ │ │ │ │
│ - 대화 내역 │ │ - 이미지 │ │
│ - 가계부 │ │ - 오디오 │ │
│ - 일기/메모 │ │ - 문서 파일 │ │
│ - 임베딩 벡터 │ │ - 생성된 파일 │ │
└──────────────────┘ └──────────────────────┘ │
│
┌────────────────────────────────────────────────┐ │
│ LLM Providers │──┘
│ Gemini / OpenAI / Claude / GLM / Ollama │
└────────────────────────────────────────────────┘
5가지 핵심 서비스
1. UI (Next.js) — 포트 3893
웹 프론트엔드입니다. 사용자가 브라우저에서 직접 접하는 화면을 담당합니다.
- 채팅 인터페이스: 실시간 스트리밍 응답, 파일 첨부, 대화 내역 조회
- 대시보드: 가계부 요약, 목표 현황, 디데이, 일기, 메모, 문서, 이미지
- 24+ 기능 페이지: 가계부, 예산, 분석, 일기, 웰니스, 데이터 가든, 목표, 디데이, 메모, 기억, 리포트, 통계, 검색, 스킬, 페르소나, 모델, 채널, 로그, 사용량, 파일 등
- 설정: 프로바이더/모델 관리, 텔레그램 채널 설정, 알림 센터 (크론)
- 다국어: next-intl 기반 4개 언어 지원 (한국어, 영어, 일본어, 중국어)
Next.js의 API Routes가 프록시 역할을 하여 Gateway의 REST API로 요청을 전달합니다.
2. Gateway (Go) — 포트 8080
모든 트래픽의 중심부입니다. UI와 AI 에이전트 사이에서 중계 역할을 합니다.
- REST API (
/api/v1/): 채팅, 파일 업로드, 설정, 스킬 관리, 채널 설정 등 - WebSocket (
/ws/chat): 실시간 스트리밍 채팅 연결 - Telegram BotManager: 사용자별 텔레그램 봇 인스턴스를 동적으로 시작/중지
- gRPC 클라이언트: TypeScript Agent와 통신
- 크론 스케줄러: 사용자별 켜기/끄기 가능한 알림 작업 (주간 리포트, 예산 초과 경고, 일일 요약 등)
- MinIO 연동: 업로드된 파일을 오브젝트 스토리지에 저장
Go 언어의 높은 동시성 처리 능력 덕분에 다수의 사용자가 동시에 접속해도 안정적으로 동작합니다.
3. Agent (TypeScript/Node.js) — 포트 50051 (gRPC)
AI 두뇌입니다. Vercel AI SDK v5 기반의 에이전트가 여러 LLM 프로바이더를 통해 메시지를 분석하고 스킬을 실행합니다.
- AI SDK v5 Agent: 메시지 처리, 스킬 선택, 응답 생성
- 멀티 LLM: Anthropic Claude · Google Gemini · OpenAI · GLM (Z.AI) · Ollama
- 스킬 시스템: 24+ 내장 스킬 — 가계부, 일기, 목표, 웰니스, 검색, 메모, 문서, 이미지, 오디오 등
- SSE 스트리밍: AI SDK 표준 스트리밍 형식으로 실시간 응답
- 임베딩 서비스: 텍스트를 벡터로 변환하여 PostgreSQL(pgvector)에 저장
- RAG 메모리: 모든 사용자 데이터에 걸친 4계층 시맨틱 메모리
4. PostgreSQL (pgvector)
주요 데이터 저장소입니다. pgvector 확장을 통해 벡터 임베딩도 함께 저장합니다.
저장 데이터: 대화 내역, 가계부, 일기, 메모, 목표, 디데이, 문서 색인, 임베딩 벡터, 채널 설정, 스킬 설정, 페르소나, 크론 스케줄, 사용량 로그
5. MinIO (Object Storage)
파일 저장소입니다. S3 호환 API를 제공하므로 AWS S3로 교체도 가능합니다.
저장 파일: 업로드된 이미지·오디오·문서, AI가 생성한 파일(QR코드, 생성 이미지 등)
데이터 흐름: 메시지 처리
사용자가 “오늘 점심 12,000원”이라고 입력했을 때의 처리 흐름입니다.
1. 사용자 → UI (Next.js)
"오늘 점심 12,000원"
2. UI → Gateway (HTTP POST /api/v1/chat 또는 WebSocket)
{ message: "오늘 점심 12,000원", user_id: "...", thread_id: "..." }
3. Gateway → Agent (gRPC Chat RPC)
서버 스트리밍으로 호출
4. Agent: AI SDK v5 처리
4-1. 메시지 분석: "식비 지출 12,000원으로 인식"
4-2. 스킬 선택: finance 스킬
4-3. DB 조회: 이번 달 식비 합계 확인
4-4. 가계부 기록: INSERT INTO finance_entries
4-5. 응답 생성: "점심 12,000원 기록했습니다. 이번 달 식비: 87,500원"
5. Agent → Gateway (gRPC 스트리밍)
토큰 단위로 응답 스트리밍
6. Gateway → UI (WebSocket 또는 SSE)
실시간 스트리밍 전달
7. UI → 사용자
화면에 응답 표시
gRPC 통신
Gateway(Go)와 Agent(TypeScript) 사이는 gRPC로 통신합니다.
// proto/starnion/v1/agent.proto (요약)
service AgentService {
// 일반 채팅 (서버 스트리밍)
rpc Chat(ChatRequest) returns (stream ChatResponse);
// 건강 체크
rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse);
}
gRPC를 선택한 이유:
- 서버 스트리밍: LLM 응답을 토큰 단위로 실시간 전달
- 타입 안전성: Protobuf 스키마로 인터페이스 보장
- 효율성: HTTP/2 기반으로 낮은 레이턴시
WebSocket: 실시간 채팅
웹 UI의 채팅은 WebSocket으로 구현됩니다. Gateway의 WebSocket Hub가 연결을 관리합니다.
브라우저 ──WebSocket── Gateway Hub ──gRPC Stream── Agent
│ /ws/chat 서버 스트리밍 │
│◀─────────────────────────────────────────────────│
토큰 단위 실시간 스트리밍
연결 흐름:
- 브라우저가
/ws/chat?user_id=...로 WebSocket 연결 - 사용자가 메시지 입력 → JSON 전송
- Gateway가 Agent에 gRPC 스트리밍 요청
- Agent 응답 토큰을 WebSocket으로 즉시 중계
- 브라우저 화면에 실시간으로 글자가 나타남
멀티 채널: 단일 에이전트
Web UI와 텔레그램이 같은 TypeScript Agent에 연결됩니다.
텔레그램 사용자 ──▶ Telegram Bot ──▶ Gateway ──▶ Agent ──▶ 동일 DB
웹 사용자 ──▶ WebSocket ──▶ Gateway ──▶ Agent ──▶ 동일 DB
어느 채널에서 기록한 내용이든 같은 PostgreSQL에 저장되므로, 웹에서 메모한 것을 텔레그램에서 조회하거나 그 반대도 가능합니다.
각 채널 메시지는 platform 필드로 구분됩니다: web, telegram.
4계층 RAG 메모리 시스템
Agent가 과거 기록을 참조할 때 사용하는 4계층 메모리 구조입니다.
질문: "지난주에 뭐 먹었지?"
Layer 1: 데일리 로그 (일일 로그 벡터)
├─ 최근 7일 대화에 대한 벡터 검색
└─ 식비 관련 항목 추출
Layer 2: 지식 베이스 (지식 기반 벡터)
├─ 소비 패턴 분석 결과
└─ 자주 가는 식당 패턴
Layer 3: 문서 섹션 (문서 섹션 벡터)
└─ 업로드된 영수증·문서의 색인된 내용
Layer 4: 최근 가계부 (최근 지출 기록)
└─ 최근 지출 항목에 대한 직접 DB 쿼리
각 계층에서 관련 컨텍스트를 가져와 LLM에 함께 전달하여 “지난주에 삼겹살 먹었었잖아?”와 같은 자연스러운 기억 참조가 가능합니다.
멀티 프로바이더 LLM
Agent는 여러 LLM 프로바이더를 지원합니다. 웹 UI의 설정 > 모델에서 프로바이더와 모델을 전환할 수 있습니다.
| 프로바이더 | 예시 모델 | 특징 |
|---|---|---|
| Anthropic Claude | claude-sonnet-4-5, claude-haiku | 긴 컨텍스트 처리 |
| Google Gemini | gemini-2.0-flash, gemini-2.5-pro | 빠른 응답, 멀티모달 |
| OpenAI | gpt-4o, gpt-4o-mini | 높은 품질의 응답 |
| GLM (Z.AI) | glm-4-flash, glm-4-plus | 중국어 강점 |
| Ollama | llama3, mistral, qwen | 완전 로컬 (인터넷 불필요) |
웹 UI 또는 CLI(starnion config models)로 사용자별 모델 설정이 가능합니다.
보안 고려사항
Self-hosted 설계
StarNion은 처음부터 self-hosted를 전제로 설계되었습니다.
- 모든 개인 데이터(대화, 가계부, 일기)는 사용자의 서버에만 저장
- LLM API 호출 시 메시지 내용이 외부 서버로 전송되지만, 이는 선택한 LLM 프로바이더에 한정
- Ollama를 사용하면 완전한 오프라인 실행 가능
JWT 인증
웹 UI 로그인은 NextAuth v5 기반 JWT(JSON Web Token) 인증입니다.
- 로그인 시 서버가 JWT 발급
- 이후 모든 API 요청에 토큰 포함
- 토큰 만료 시 재로그인 필요
- Gateway 토큰 검증으로 API 레벨 보안 보장
PostgreSQL 어드바이저리 락
텔레그램 봇의 중복 실행을 방지하기 위해 PostgreSQL 세션 레벨 어드바이저리 락을 사용합니다. 동일 봇 토큰이 두 개의 Gateway 인스턴스에서 동시에 폴링되는 것을 방지합니다.
데이터 격리
각 사용자의 데이터는 user_id 외래키로 완전히 격리됩니다. 한 사용자가 다른 사용자의 데이터에 접근할 수 없습니다.