다크 모드
외부 연동
truloop-assistant가 연동하는 외부 서비스들을 설명합니다.
연동 서비스 개요
Sendbird
역할
사용자와 AI 비서 간의 실시간 채팅 채널입니다.
| 방향 | 용도 |
|---|---|
| Sendbird → Assistant | Webhook으로 사용자 메시지 수신 (POST /sendbird/webhook/{eid}) |
| Assistant → Sendbird | AI 응답 메시지 전송, JSON-RPC 요청 전송 |
통합 구현
src/truloop_assistant/integrations/sendbird/
├── client.py # SendbirdClient (비동기 HTTP 클라이언트)
└── schemas.py # Sendbird 데이터 모델SendbirdClient 기능:
- 채널 메시지 전송
- 메시지 이력 조회
- 사용자 정보 조회
custom_type지정 메시지 발송 (JSON-RPC용)
환경변수
| 변수 | 설명 |
|---|---|
SENDBIRD_API_URL | Sendbird API URL |
SENDBIRD_API_TOKEN | Sendbird API 토큰 |
SENDBIRD_TIMEOUT | API 타임아웃 (기본: 20초) |
truloop Core API
역할
사용자 정보, 룹 정보 등 비즈니스 데이터 조회 및 truloop-core에서 발생하는 Internal Event 수신을 담당합니다.
통합 구현
src/truloop_assistant/integrations/truloop/
├── client.py # TruloopClient (도메인별 API 메서드)
├── schemas.py # Pydantic 모델 (appointments, locations, users)
├── exceptions.py # TruloopError
└── enums.py # 상태 Enum아키텍처
| 패턴 | 설명 |
|---|---|
| Per-Request 인스턴스 | 각 Background task마다 별도의 TruloopClient 생성 |
| httpx.AsyncClient | async with 컨텍스트 매니저로 연결 수명 관리 |
| 격리 | 각 Background task가 독립적인 HTTP 클라이언트 보유 |
python
async with httpx.AsyncClient(
base_url=settings.truloop_core_api_base_url,
timeout=settings.truloop_core_timeout,
) as truloop_http:
truloop_client = TruloopClient(truloop_http)
deps = AgentDeps(user=user, truloop_client=truloop_client, ...)Internal Events (truloop-core → assistant)
truloop-core에서 POST /internal/events로 전달되는 이벤트입니다. Discriminated union 기반 자동 검증:
| 이벤트 | 설명 |
|---|---|
assistant.selected | 사용자가 봇을 선택 → 자기소개 메시지 발송 |
loop.scheduling_requested | 일정 조율 시작 → 참여자 전원에게 초대 알림 |
loop.guest_participant_added | 비회원이 초대 링크로 참여 → 호스트에게 알림 |
loop.guest_invited | 호스트가 비회원 추가 → SMS 초대 발송 |
loop.participant_availability_updated | 참여자 가용 시간 제출 → 호스트에게 알림 |
인증
| 변수 | 설명 |
|---|---|
TRULOOP_CORE_API_BASE_URL | Core API 기본 URL (기본: https://dev-api.truloop.app) |
TRULOOP_CORE_TIMEOUT | API 타임아웃 (기본: 30초) |
TRULOOP_CORE_API_KEY | Internal API 인증 키 (outgoing 요청 Authorization: Bearer) |
INTERNAL_API_KEY | Internal 이벤트 수신 인증 (X-Internal-API-Key 헤더 검증) |
OpenRouter (LLM)
역할
AI Agent의 추론 엔진입니다. Claude Sonnet 4.6을 기본 모델로 사용합니다.
통합
pydantic-ai의 OpenRouterModel + OpenRouterProvider를 통해 연동합니다:
python
model = OpenRouterModel(
settings.openrouter_model, # "anthropic/claude-sonnet-4.6"
provider=OpenRouterProvider(
api_key=settings.openrouter_api_key,
),
profile=anthropic_model_profile(model_name),
)설정
| 변수 | 기본값 | 설명 |
|---|---|---|
OPENROUTER_API_KEY | - | OpenRouter API 키 |
OPENROUTER_MODEL | anthropic/claude-sonnet-4.6 | 사용 모델 |
MODEL_TEMPERATURE | 0 | 응답 온도 (0: 결정적, 1: 창의적) |
MAX_TOKENS | 1000 | 최대 응답 토큰 수 |
Redis
역할
대화 이력과 Pending tool call을 관리합니다.
| 용도 | TTL | 설명 |
|---|---|---|
| 대화 이력 | 30일 | 사용자별 메시지 기록 |
| Pending tool calls | 1시간 | Deferred Tool 호출 추적 |
서비스
| 서비스 | 역할 |
|---|---|
MessageHistoryService | 대화 이력 저장/조회 (최대 20개 메시지) |
PendingCallService | Deferred Tool 호출 상태 관리 |
| 변수 | 기본값 | 설명 |
|---|---|---|
REDIS_URL | redis://localhost:6379/0 | Redis 연결 URL |
MESSAGE_HISTORY_MAX_LENGTH | 20 | 최대 대화 이력 수 |
MESSAGE_HISTORY_TTL_SECONDS | 2,592,000 (30일) | 이력 TTL |
RAG (Knowledge Base)
역할
Notion 문서 기반의 지식 검색 시스템입니다. Agent가 사용자 질문에 답변할 때 관련 문서를 검색하여 컨텍스트를 보강합니다.
구현
| 컴포넌트 | 기술 | 역할 |
|---|---|---|
RAGService | - | 검색 오케스트레이션 |
VectorStore | NumPy | 벡터 저장/검색 (Cosine similarity) |
| Embedding | OpenAI text-embedding-3-small | 텍스트 → 벡터 변환 |
Vector Store 구조:
/app/vector_db/
├── {collection}_metadata.json # 문서 메타데이터
└── {collection}_embeddings.npy # 임베딩 벡터 (NumPy)정보
RAG는 선택적 기능입니다. OPENAI_API_KEY가 설정되지 않으면 비활성화되며, 관련 Tool은 graceful하게 비활성 상태를 처리합니다.
| 변수 | 기본값 | 설명 |
|---|---|---|
OPENAI_API_KEY | - | OpenAI API 키 (비어있으면 RAG 비활성화) |
VECTOR_DB_PATH | /app/vector_db | 벡터 스토어 디렉토리 |
RAG_COLLECTION_NAME | notion_docs | 컬렉션 이름 |
RAG_EMBEDDING_MODEL | text-embedding-3-small | 임베딩 모델 |
RAG_TOP_K | 3 | 검색 결과 수 |
Supermemory (장기 기억)
역할
사용자별 장기 기억을 저장하고 검색합니다. Agent가 이전 대화에서 학습한 사용자 선호도, 습관 등을 기억합니다.
데이터 격리
container_tags 패턴으로 사용자별 데이터를 격리합니다:
python
# 사용자별 태그 생성
tags = [
f"user_{user_eid}",
f"project_{project_name}",
f"env_{env}",
]| 변수 | 기본값 | 설명 |
|---|---|---|
SUPERMEMORY_API_KEY | - | Supermemory API 키 (비어있으면 비활성화) |
SUPERMEMORY_PROJECT_NAME | truloop-assistant | 프로젝트 이름 |
Google Maps
역할
장소 검색 기능을 제공합니다. places_tools.py의 search_places 도구에서 사용합니다.
구현
동기 라이브러리(googlemaps)를 asyncio.to_thread()로 비동기 실행합니다. 사용자 locale에서 region bias를 추출합니다 (예: ko-KR → kr).
| 변수 | 기본값 | 설명 |
|---|---|---|
GOOGLE_MAPS_API_KEY | `` (빈 문자열) | API 키 (비어있으면 장소 검색 비활성화) |
정보
Google Maps API는 선택적 기능입니다. GOOGLE_MAPS_API_KEY가 비어있으면 search_places 도구가 에러 메시지를 반환합니다.
Messaging Gateway (비회원)
주의
미구현 기능: SMS two-way 메시징은 향후 구현 예정입니다. 아래 코드 구조(인터페이스, 프로바이더, 스키마)는 scaffolding으로 존재하지만, 현재 프로덕션에서는 stub 프로바이더만 존재하며 실제 SMS 발송/수신은 동작하지 않습니다.
역할
비회원 사용자에게 SMS/RCS/WhatsApp 메시지를 전송하고, 비회원의 응답을 수신하는 것이 목표입니다.
프로바이더
| 프로바이더 | 환경 | 설명 |
|---|---|---|
stub | 개발/테스트 | 메시지 로깅만 수행 (현재 유일하게 동작하는 프로바이더) |
twilio | 프로덕션 | SMS 실제 발송 (미구현) |
Webhook 수신 (미구현)
비회원의 SMS 응답은 POST /messaging/webhook/{provider} 엔드포인트로 수신될 예정입니다. GuestMessagingProcessorService가 비회원 Agent를 실행하여 가용 시간을 수집하는 구조이나, 현재 프로덕션에서 동작하지 않습니다.
src/truloop_assistant/integrations/messaging/
├── gateway.py # MessagingGateway 인터페이스
├── providers/ # 프로바이더 구현 (stub.py만 동작)
└── schemas.py # IncomingMessage 등 메시징 스키마| 변수 | 기본값 | 설명 |
|---|---|---|
MESSAGING_PROVIDER | stub | 프로바이더 선택 (stub / twilio). 현재 stub만 유효 |
연동 요약 다이어그램
정보
선택적 연동은 해당 API Key가 비어있으면 자동으로 비활성화됩니다. 서비스 시작이나 기본 기능에는 영향을 미치지 않습니다.
기타 환경변수
| 변수 | 기본값 | 설명 |
|---|---|---|
JWT_SECRET | `` | JWT 토큰 검증 시크릿 (프로덕션에서 32자 이상 필수) |
JWT_ALGORITHM | HS256 | JWT 알고리즘 |
SENTRY_DSN | `` | Sentry DSN (비어있으면 비활성화) |
CHUNK_DELAY_MIN_MS | 400 | 메시지 청킹 최소 딜레이 (ms) |
CHUNK_DELAY_MAX_MS | 800 | 메시지 청킹 최대 딜레이 (ms) |
변경 이력
| 날짜 | 변경 내용 |
|---|---|
| 2026-03-11 | 비회원 Agent / SMS two-way 기능을 미구현 상태로 정정 |
| 2026-03-10 | Internal Events, Google Maps, Sentry/Logfire, JWT/청킹 환경변수 추가. Messaging Gateway Webhook 수신 설명 보강 |