Skip to content

Latest commit

ย 

History

260 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿง  tryna brain

์ผ์ •์„ ์ดํ•ดํ•˜๊ณ  ๋งฅ๋ฝ์„ ์—ฐ๊ฒฐํ•ด ํ•„์š”ํ•œ ์ค€๋น„ยท์‹คํ–‰ ํ•ญ๋ชฉ์„ ์ถ”์ฒœํ•˜๋Š” FastAPI ๊ธฐ๋ฐ˜ ๋ถ„์„ ์„œ๋ฒ„

tryna brain์€ tryna ์„œ๋น„์Šค์˜ ์ผ์ • ํŒŒ์‹ฑ ๋ฐ ์ถ”์ฒœ ์ „์šฉ ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค. Spring ์„œ๋ฒ„์™€ ์—ญํ• ์„ ๋ถ„๋ฆฌํ•˜์—ฌ ์ž์—ฐ์–ด ์ผ์ • ํŒŒ์‹ฑ, Neo4j ๊ธฐ๋ฐ˜ ์˜๋ฏธ ๋งคํ•‘๊ณผ ํ›„๋ณด ์กฐํšŒ, Upstage ๊ธฐ๋ฐ˜ ์ž„๋ฒ ๋”ฉ ๋ฐ ์ถ”์ฒœ ๋ฌธ๊ตฌ ์ •์ œ๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ“Œ ์†Œ๊ฐœ

tryna๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์งง์€ ์ผ์ •์˜ ๋งฅ๋ฝ์„ ์ดํ•ดํ•˜๊ณ , ์ผ์ • ์ „ํ›„์— ํ•„์š”ํ•œ ํ•  ์ผ์„ ์ œ์•ˆํ•˜๋Š” ์ผ์ • ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.

tryna brain์€ ๋‹ค์Œ ๋‘ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  • ์ž์—ฐ์–ด ์ผ์ •์—์„œ ๋‚ ์งœ, ์‹œ๊ฐ„, ์žฅ์†Œ ๋“ฑ ์ผ์ • ํ›„๋ณด ์ •๋ณด ์ถ”์ถœ
  • ์ผ์ • ์œ ํ˜•ยท๋งฅ๋ฝยท์žฅ์†Œ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ์ค€๋น„ ๋ฐ ์‹คํ–‰ ํ•ญ๋ชฉ ์ถ”์ฒœ

์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž…๋ ฅํ•˜๋ฉด:

๊ธˆ์š”์ผ 3์‹œ ํŒ€ํ”Œ ํšŒ์˜

ํŒŒ์‹ฑ API๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ผ์ • ํ›„๋ณด ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

{
  "tempEventId": "tmp_f7258568-0374-4710-8473-329576255448",
  "eventTitle": "๊ธˆ์š”์ผ 3์‹œ ํŒ€ํ”Œ ํšŒ์˜",
  "draftRevision": 1,
  "startDate": "2026-08-14",
  "dateSource": "RELATIVE_EXPRESSION",
  "startTime": "15:00:00",
  "placeCandidate": null,
  "toEmbedding": ["ํŒ€ํ”Œ", "ํšŒ์˜"],
  "isAllDayCandidate": false,
  "needsConfirmation": false,
  "warnings": []
}

์ดํ›„ ์ถ”์ฒœ API๋Š” Neo4j ๊ด€๊ณ„ยท๋ฒกํ„ฐ ํ›„๋ณด์™€ Upstage LLM์„ ํ™œ์šฉํ•˜์—ฌ ํšŒ์˜ ์‹œ๊ฐ„ ํ™•์ธ, ์–˜๊ธฐํ•  ๋‚ด์šฉ ์ •๋ฆฌ์™€ ๊ฐ™์€ ์ค€๋น„ยท์‹คํ–‰ ํ•ญ๋ชฉ์„ ์ตœ๋Œ€ 3๊ฐœ๊นŒ์ง€ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.


๐Ÿงฉ Server Responsibility

tryna ๋ฐฑ์—”๋“œ๋Š” ์—ญํ• ์— ๋”ฐ๋ผ Spring ์„œ๋ฒ„์™€ FastAPI brain ์„œ๋ฒ„๋กœ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

Spring Server

Spring ์„œ๋ฒ„๋Š” ์„œ๋น„์Šค ์šด์˜์— ํ•„์š”ํ•œ ํ•ต์‹ฌ ๋ฐฑ์—”๋“œ ๊ธฐ๋Šฅ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

  • ํšŒ์›/๋น„ํšŒ์› ์ธ์ฆ ๋ฐ ์‚ฌ์šฉ์ž ๊ณ„์ • ๊ด€๋ฆฌ
  • ์ผ์ • CRUD ๋ฐ ์บ˜๋ฆฐ๋” ์กฐํšŒ
  • ์ถ”์ฒœ ์š”์ฒญ ์ค‘ ์ตœ์‹  draftRevision ๋“ฑ๋ก
  • ์ถ”์ฒœ ๊ฒฐ๊ณผ ์ €์žฅ
  • ์•Œ๋ฆผ ๋ฐ ์™ธ๋ถ€ ์บ˜๋ฆฐ๋” ์—ฐ๋™
  • DB ํŠธ๋žœ์žญ์…˜ ๋ฐ ๊ถŒํ•œ ๊ฒ€์ฆ

FastAPI Brain Server

FastAPI brain ์„œ๋ฒ„๋Š” ์ผ์ • ๋ฌธ์žฅ์„ ์ดํ•ดํ•˜๊ณ  ์ถ”์ฒœ ํ›„๋ณด๋ฅผ ์ƒ์„ฑยท์ •์ œํ•˜๋Š” ๋ถ„์„ ์—”์ง„ ์—ญํ• ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

  • Rule Based Parser์™€ Kiwi ๊ธฐ๋ฐ˜ ์ž์—ฐ์–ด ์ผ์ • ํŒŒ์‹ฑ
  • Upstage ์ž„๋ฒ ๋”ฉ ๊ธฐ๋ฐ˜ ์ผ์ • ์œ ํ˜•ยท๋งฅ๋ฝยท์žฅ์†Œ ์˜๋ฏธ ๋งคํ•‘
  • Neo4j ๊ด€๊ณ„ ๋ฐ ๋ฒกํ„ฐ ๊ธฐ๋ฐ˜ ์ถ”์ฒœ ํ›„๋ณด ์กฐํšŒ
  • Upstage LLM ๊ธฐ๋ฐ˜ ์ถ”์ฒœ ํ•ญ๋ชฉ ์„ ํƒ ๋ฐ displayText ์ •์ œ
  • ์‹œ๊ฐ„ ๋งฅ๋ฝ ๊ฒ€์ฆ๊ณผ TIMED_ACTIONยทUNTIMED_PREP ๋ถ„๋ฅ˜
  • Redis ๊ธฐ๋ฐ˜ ์ตœ์‹  draftRevision ๊ฒ€์ฆ
  • Spring ์„œ๋ฒ„์— ํŒŒ์‹ฑ ๋ฐ ์ถ”์ฒœ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜

๐Ÿ›  ๊ธฐ์ˆ  ์Šคํƒ

Type Tool
Language Python
Framework FastAPI
ASGI Server Uvicorn
Database Neo4j
Cache Redis
LLM Upstage
NLP Kiwi
Configuration pydantic-settings python-dotenv
Package Manager pip
Version Control Git GitHub
Collaboration Notion

๐Ÿ— Architecture

Client
   โ”‚
   โ–ผ
Spring Server
   โ”‚
   โ”‚ ๋‚ด๋ถ€ API ์ธ์ฆ ๋ฐ ์ผ์ • ํŒŒ์‹ฑ/์ถ”์ฒœ ์š”์ฒญ
   โ–ผ
FastAPI Brain Server
   โ”‚
   โ”œโ”€โ”€ Rule Based Parser + Kiwi
   โ”‚     ๋‚ ์งœ, ์‹œ๊ฐ„, ์žฅ์†Œ ๋“ฑ ์ผ์ • ํ›„๋ณด ์ถ”์ถœ
   โ”‚
   โ”œโ”€โ”€ Upstage Embedding + Neo4j
   โ”‚     ์˜๋ฏธ ๋งคํ•‘ ๋ฐ ๊ด€๊ณ„ยท๋ฒกํ„ฐ ์ถ”์ฒœ ํ›„๋ณด ์กฐํšŒ
   โ”‚
   โ”œโ”€โ”€ Upstage LLM
   โ”‚     ์ถ”์ฒœ ํ•ญ๋ชฉ ์„ ํƒ ๋ฐ displayText ์ •์ œ
   โ”‚
   โ””โ”€โ”€ Redis/Valkey
         ์ตœ์‹  draftRevision ๊ฒ€์ฆ
   โ”‚
   โ–ผ
Spring Server
   โ”‚
   โ”‚ ์ถ”์ฒœ ๊ฒฐ๊ณผ ์ €์žฅ/์‘๋‹ต
   โ–ผ
Client

๐Ÿ”„ Recommendation Pipeline

Step Component Responsibility
D101 Schedule Context + Upstage Embedding ์ผ์ • ์ •๋ณด๋ฅผ ์ž„๋ฒ ๋”ฉ ์ž…๋ ฅ์œผ๋กœ ๊ตฌ์„ฑํ•˜๊ณ  ์ฟผ๋ฆฌ ๋ฒกํ„ฐ ์ƒ์„ฑ
D102 Neo4j EventType, Context, PlaceType ์˜๋ฏธ ๋งคํ•‘ ๋ฐ ๊ด€๊ณ„ยท๋ฒกํ„ฐ ์ถ”์ฒœ ํ›„๋ณด ๊ฒฐํ•ฉ
D103 Upstage LLM ํ›„๋ณด ์ค‘ ์ตœ๋Œ€ 3๊ฐœ๋ฅผ ์„ ํƒํ•˜๊ณ  ์ž์—ฐ์Šค๋Ÿฌ์šด displayText๋กœ ์ •์ œ
D104 Temporal Validator ๋‚ ์งœ ๋งฅ๋ฝ ๊ฒ€์ฆ ๋ฐ TIMED_ACTIONยทUNTIMED_PREP ํ™•์ •
D105 Suggestion Composer ์ˆœ์œ„์™€ ๋ถ€๋ชจ ์ž„์‹œ ์ผ์ • ID๋ฅผ ๊ฒ€์ฆํ•˜๊ณ  ์ตœ์ข… ์‘๋‹ต ๊ตฌ์„ฑ

Spring ์„œ๋ฒ„๋Š” tempEventId๋ณ„ ์ตœ์‹  draftRevision์„ Redis์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. FastAPI๋Š” D101~D105 ๋‹จ๊ณ„ ์‚ฌ์ด์—์„œ recommendation:latest-revision:{tempEventId}๋ฅผ ์กฐํšŒํ•˜๊ณ , ์˜ค๋ž˜๋œ ์š”์ฒญ์ด๋ฉด 409 STALE_DRAFT_REVISION_409๋กœ ํ›„์† ์ฒ˜๋ฆฌ๋ฅผ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค. Redis๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์„ ๋•Œ์—๋Š” ์ถ”์ฒœ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ณ„์† ์‹คํ–‰ํ•˜๋Š” fail-open ์ •์ฑ…์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

Upstage์—๋Š” ๋ถ„์„๊ณผ ์ถ”์ฒœ ์ •์ œ์— ํ•„์š”ํ•œ ์ผ์ • ์ •๋ณด ๋ฐ Neo4j ํ›„๋ณด๋งŒ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๊ฐœ์ธ์ •๋ณด๋‚˜ ๋ถˆํ•„์š”ํ•œ ๋ฏผ๊ฐ ์ •๋ณด๋Š” ์ „๋‹ฌํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ์›์น™์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ“‚ Project Structure

brain/
โ”œโ”€ app/
โ”‚  โ”œโ”€ main.py
โ”‚  โ”œโ”€ api/
โ”‚  โ”‚  โ””โ”€ v1/
โ”‚  โ”‚     โ”œโ”€ router.py
โ”‚  โ”‚     โ””โ”€ routes/
โ”‚  โ”‚        โ”œโ”€ event_previews.py
โ”‚  โ”‚        โ”œโ”€ health.py
โ”‚  โ”‚        โ””โ”€ recommendations.py
โ”‚  โ”œโ”€ core/
โ”‚  โ”‚  โ”œโ”€ config.py
โ”‚  โ”‚  โ”œโ”€ deps.py
โ”‚  โ”‚  โ”œโ”€ error_code.py
โ”‚  โ”‚  โ”œโ”€ handlers.py
โ”‚  โ”‚  โ”œโ”€ internal_auth.py
โ”‚  โ”‚  โ”œโ”€ responses.py
โ”‚  โ”‚  โ””โ”€ valkey_client.py
โ”‚  โ”œโ”€ graph/
โ”‚  โ”‚  โ”œโ”€ neo4j_client.py
โ”‚  โ”‚  โ”œโ”€ models/
โ”‚  โ”‚  โ””โ”€ repositories/
โ”‚  โ”œโ”€ schemas/
โ”‚  โ”‚  โ”œโ”€ recommendation/
โ”‚  โ”‚  โ”œโ”€ event_preview.py
โ”‚  โ”‚  โ””โ”€ health.py
โ”‚  โ””โ”€ services/
โ”‚     โ”œโ”€ event_preview_service.py
โ”‚     โ”œโ”€ parser_service.py
โ”‚     โ””โ”€ recommendation/
โ”‚        โ”œโ”€ schedule_context_service.py
โ”‚        โ”œโ”€ candidate_search_service.py
โ”‚        โ”œโ”€ refinement_service.py
โ”‚        โ”œโ”€ temporal_validation_service.py
โ”‚        โ”œโ”€ suggestion_compose_service.py
โ”‚        โ””โ”€ revision_guard_service.py
โ”œโ”€ tests/
โ”œโ”€ nginx/
โ”œโ”€ Dockerfile
โ”œโ”€ requirements.txt
โ”œโ”€ .env.example
โ””โ”€ README.md

๐Ÿ“ฆ Package Responsibility

Package Responsibility
app.main FastAPI ์•ฑ ์ง„์ž…์ ๊ณผ Neo4jยทRedis ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ
app.core ํ™˜๊ฒฝ๋ณ€์ˆ˜, ์˜์กด์„ฑ, ๋‚ด๋ถ€ ์ธ์ฆ, ๊ณตํ†ต ์‘๋‹ตยท์˜ˆ์™ธ ๋ฐ Redis ์—ฐ๊ฒฐ ๊ด€๋ฆฌ
app.api ํ—ฌ์Šค์ฒดํฌ, ์ผ์ • ๋ฏธ๋ฆฌ๋ณด๊ธฐ, ์ถ”์ฒœ API ์—”๋“œํฌ์ธํŠธ ์ •์˜
app.graph Neo4j ์—ฐ๊ฒฐ๊ณผ ์ผ์ • ์˜๋ฏธ ๋งคํ•‘ยท์ถ”์ฒœ ํ›„๋ณด ์กฐํšŒ
app.schemas ํŒŒ์‹ฑยท์ถ”์ฒœ ํŒŒ์ดํ”„๋ผ์ธ์˜ ์š”์ฒญ/์‘๋‹ต ๋ชจ๋ธ ์ •์˜
app.services ์ž์—ฐ์–ด ํŒŒ์‹ฑ ๋ฐ D101~D105 ์ถ”์ฒœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ˆ˜ํ–‰

๐Ÿš€ ์‹œ์ž‘ํ•˜๊ธฐ

Python 3.13 ํ™˜๊ฒฝ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

1. ๊ฐ€์ƒํ™˜๊ฒฝ ์ƒ์„ฑ

python3.13 -m venv .venv

2. ์˜์กด์„ฑ ์„ค์น˜

.venv/bin/python -m pip install -r requirements.txt

Windows PowerShell์—์„œ๋Š” .venv/bin/python ๋Œ€์‹  .\.venv\Scripts\python.exe๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

3. ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํŒŒ์ผ ์ƒ์„ฑ

cp .env.example .env

๋กœ์ปฌ Neo4j, Redis/Valkey ๋ฐ Upstage API ์ •๋ณด๋ฅผ .env์— ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. VALKEY_HOST๋ฅผ ๋น„์›Œ๋‘๋ฉด Redis ๊ธฐ๋ฐ˜ ๊ธฐ๋Šฅ์€ ๋น„ํ™œ์„ฑํ™”๋˜๋ฉฐ ์„œ๋ฒ„๋Š” ๊ณ„์† ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

4. ์„œ๋ฒ„ ์‹คํ–‰

.venv/bin/python -m uvicorn app.main:app --reload --host 127.0.0.1 --port 8000

ํ…Œ์ŠคํŠธ๋Š” ๋‹ค์Œ ๋ช…๋ น์–ด๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

PYTHONPATH=. .venv/bin/python -m pytest

๐Ÿ“– API Documentation

FastAPI brain ์„œ๋ฒ„๋Š” FastAPI ๊ธฐ๋ณธ Swagger UI๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

http://127.0.0.1:8000/docs

๋น„์ฆˆ๋‹ˆ์Šค API๋Š” X-Internal-Api-Key ํ—ค๋”๋กœ Spring ์„œ๋ฒ„์™€์˜ ๋‚ด๋ถ€ ํ†ต์‹ ์„ ์ธ์ฆํ•ฉ๋‹ˆ๋‹ค. ํ—ฌ์Šค์ฒดํฌ๋Š” ์ธ์ฆ ๋Œ€์ƒ์—์„œ ์ œ์™ธ๋ฉ๋‹ˆ๋‹ค.

Method Path Description
GET /api/v1/health Neo4j ๋ฐ Redis ์—ฐ๊ฒฐ ์ƒํƒœ ํ™•์ธ
POST /api/v1/event-previews ์ž์—ฐ์–ด ์ผ์ • ํŒŒ์‹ฑ ๋ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ƒ์„ฑ
POST /api/v1/recommendations D101~D105 ์ถ”์ฒœ ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰

์ถ”์ฒœ API์˜ stop_after_step ์ฟผ๋ฆฌ๋Š” ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ์ค‘๊ฐ„ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ๋•Œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” 403์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ”— API ์‚ฌ์šฉ ๋ฐ ์—ฐ๋™ ํ๋ฆ„

ํด๋ผ์ด์–ธํŠธ๋Š” brain API๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Spring ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉ์ž ์ธ์ฆ๊ณผ ์š”์ฒญ ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ํ•œ ๋’ค X-Internal-Api-Key๋ฅผ ํฌํ•จํ•ด brain API๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

1. ์ตœ์ดˆ ์ผ์ • ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์š”์ฒญ

์ƒˆ๋กœ์šด ์ผ์ • ์ž‘์„ฑ์„ ์‹œ์ž‘ํ•  ๋•Œ tempEventId๋Š” JSON null๋กœ ์ „๋‹ฌํ•˜๊ฑฐ๋‚˜ ํ•„๋“œ๋ฅผ ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ž์—ด "null"์„ ์ „๋‹ฌํ•˜๋ฉด ์‹ค์ œ ์ž„์‹œ ์ผ์ • ID๋กœ ์ทจ๊ธ‰๋˜๋ฏ€๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

{
  "tempEventId": null,
  "eventTitle": "๋‹ค์Œ์ฃผ ๊ธˆ์š”์ผ 3์‹œ๋ถ€ํ„ฐ 4์‹œ๊นŒ์ง€ ํšŒ์˜",
  "draftRevision": 0,
  "selectedDate": "2026-08-13"
}

brain์€ ์ตœ์ดˆ ์š”์ฒญ์— ์ƒˆ๋กœ์šด tempEventId๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์š”์ฒญ์˜ draftRevision์„ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

{
  "tempEventId": "tmp_f7258568-0374-4710-8473-329576255448",
  "eventTitle": "๋‹ค์Œ์ฃผ ๊ธˆ์š”์ผ 3์‹œ๋ถ€ํ„ฐ 4์‹œ๊นŒ์ง€ ํšŒ์˜",
  "draftRevision": 0,
  "startDate": "2026-08-21",
  "dateSource": "RELATIVE_EXPRESSION",
  "endDate": null,
  "startTime": "15:00:00",
  "endTime": "16:00:00",
  "placeCandidate": null,
  "toEmbedding": ["ํšŒ์˜"],
  "isAllDayCandidate": false,
  "needsConfirmation": false,
  "warnings": []
}

2. ์ž…๋ ฅ ์ˆ˜์ • ์š”์ฒญ

๊ฐ™์€ ์ผ์ •์„ ์ž‘์„ฑํ•˜๋Š” ๋™์•ˆ์—๋Š” ์ด์ „ ์‘๋‹ต์˜ tempEventId๋ฅผ ๊ทธ๋Œ€๋กœ ์ „๋‹ฌํ•˜๊ณ , ํ˜ธ์ถœํ•˜๋Š” ์ชฝ์—์„œ draftRevision์„ ์ฆ๊ฐ€์‹œํ‚ต๋‹ˆ๋‹ค. brain์€ draftRevision์„ ์ž๋™์œผ๋กœ ์ฆ๊ฐ€์‹œํ‚ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

{
  "tempEventId": "tmp_f7258568-0374-4710-8473-329576255448",
  "eventTitle": "๋‹ค์Œ์ฃผ ๊ธˆ์š”์ผ 4์‹œ๋ถ€ํ„ฐ 5์‹œ๊นŒ์ง€ ํšŒ์˜",
  "draftRevision": 1,
  "selectedDate": "2026-08-13"
}
  • ๋™์ผํ•œ ์ผ์ • ์ž‘์„ฑ ๊ณผ์ •์—์„œ๋Š” ๊ฐ™์€ tempEventId๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ์ผ์ • ์ €์žฅ, ์ž‘์„ฑ ์ทจ์†Œ ๋˜๋Š” ์ƒˆ๋กœ์šด ์ผ์ • ์ž‘์„ฑ์„ ์‹œ์ž‘ํ•˜๋ฉด ํ˜ธ์ถœํ•˜๋Š” ์ชฝ์—์„œ ๊ธฐ์กด tempEventId๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • brain์€ ์ž„์‹œ ์ผ์ •์˜ ์ €์žฅยท์ทจ์†Œ ์ƒํƒœ๋ฅผ ๋ณด๊ด€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์ถ”์ฒœ ํŒŒ์ดํ”„๋ผ์ธ์€ tempEventId๋ณ„ ์ตœ์‹  draftRevision์„ ๊ธฐ์ค€์œผ๋กœ ์˜ค๋ž˜๋œ ์š”์ฒญ์˜ ํ›„์† ๋‹จ๊ณ„๋ฅผ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค.

3. ๋‚ ์งœ ๊ฒฐ์ • ์šฐ์„ ์ˆœ์œ„

์ผ์ • ์‹œ์ž‘ ๋‚ ์งœ๋Š” ๋‹ค์Œ ์ˆœ์„œ๋กœ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

  1. ์ผ์ • ์›๋ฌธ์—์„œ ํŒŒ์‹ฑํ•œ ๋‚ ์งœ
  2. ์š”์ฒญ์œผ๋กœ ์ „๋‹ฌ๋œ selectedDate
  3. Asia/Seoul ๊ธฐ์ค€ ์˜ค๋Š˜

dateSource๋Š” ๋‚ ์งœ๊ฐ€ ๊ฒฐ์ •๋œ ์ถœ์ฒ˜๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

Value Description
EXPLICIT ์‚ฌ์šฉ์ž๊ฐ€ ์ ˆ๋Œ€ ๋‚ ์งœ๋ฅผ ์ง์ ‘ ์ž…๋ ฅ
RELATIVE_EXPRESSION ๋‚ด์ผ, ๋‹ค์Œ ์ฃผ ๊ธˆ์š”์ผ ๋“ฑ์˜ ์ƒ๋Œ€ ๋‚ ์งœ ํ‘œํ˜„์„ ํŒŒ์‹ฑ
SELECTED_DATE ์›๋ฌธ์— ๋‚ ์งœ๊ฐ€ ์—†์–ด ์š”์ฒญ์˜ selectedDate ์‚ฌ์šฉ
DEFAULT_TODAY ์›๋ฌธ๊ณผ ์š”์ฒญ์— ๋‚ ์งœ๊ฐ€ ์—†์–ด Asia/Seoul ๊ธฐ์ค€ ์˜ค๋Š˜ ์‚ฌ์šฉ

4. ์ผ์ • ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ฒฐ๊ณผ๋ฅผ ์ถ”์ฒœ ์š”์ฒญ์œผ๋กœ ๋ณ€ํ™˜

์ผ์ • ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์‘๋‹ต๊ณผ ์ถ”์ฒœ ์š”์ฒญ์€ ํ•„๋“œ ์ด๋ฆ„์ด ์ผ๋ถ€ ๋‹ค๋ฅด๋ฏ€๋กœ Spring ์„œ๋ฒ„์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Event Preview Response Recommendation Request
startDate startDateCandidate
startTime startTimeCandidate
endDate endDateCandidate
endTime endTimeCandidate
dateSource startDateSource
toEmbedding embeddingWords

์ถ”์ฒœ ์š”์ฒญ์—๋Š” ์ผ์ • ์ž…๋ ฅ ์ถœ์ฒ˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” sourceType๋„ ํฌํ•จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

{
  "tempEventId": "tmp_f7258568-0374-4710-8473-329576255448",
  "draftRevision": 1,
  "eventTitle": "๋‹ค์Œ์ฃผ ๊ธˆ์š”์ผ 4์‹œ๋ถ€ํ„ฐ 5์‹œ๊นŒ์ง€ ํšŒ์˜",
  "sourceType": "USER_NATURAL_LANGUAGE",
  "startDateCandidate": "2026-08-21",
  "startTimeCandidate": "16:00:00",
  "endDateCandidate": null,
  "endTimeCandidate": "17:00:00",
  "startDateSource": "RELATIVE_EXPRESSION",
  "placeCandidate": null,
  "description": null,
  "embeddingWords": ["ํšŒ์˜"]
}
sourceType Description
USER_NATURAL_LANGUAGE ์‚ฌ์šฉ์ž๊ฐ€ ์ž์—ฐ์–ด๋กœ ์ž…๋ ฅํ•œ ์ผ์ •
USER_MANUAL_EDIT ์‚ฌ์šฉ์ž๊ฐ€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ •๋ณด๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•œ ์ผ์ •

โœ… Health Check

GET /api/v1/health

Response:

{
  "status": "UP",
  "timestamp": "2026-08-13T09:00:00Z",
  "components": {
    "neo4j": {
      "status": "UP",
      "detail": null
    },
    "redis": {
      "status": "UP",
      "detail": null
    }
  }
}

Redis/Valkey๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์€ ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ๋Š” components.redis.status๊ฐ€ DISABLED๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ์ „์ฒด ์ƒํƒœ์™€ HTTP ์ƒํƒœ ์ฝ”๋“œ๋Š” Neo4j ์—ฐ๊ฒฐ ์ƒํƒœ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ“ฆ Common Response

๋น„์ฆˆ๋‹ˆ์Šค ์˜ˆ์™ธ์™€ ์š”์ฒญ๊ฐ’ ๊ฒ€์ฆ ์˜ค๋ฅ˜๋Š” ๊ณตํ†ต ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Error:

{
  "success": false,
  "code": "COMMON_400",
  "message": "์ž˜๋ชป๋œ ์š”์ฒญ์ž…๋‹ˆ๋‹ค.",
  "data": null
}

์ผ์ • ๋ฏธ๋ฆฌ๋ณด๊ธฐ, ์ถ”์ฒœ ์„ฑ๊ณต ์‘๋‹ต ๋ฐ ํ—ฌ์Šค์ฒดํฌ ์‘๋‹ต์€ ๊ฐ API์˜ ์‘๋‹ต ์Šคํ‚ค๋งˆ๋ฅผ ์ง์ ‘ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ชจ๋“  ์„ฑ๊ณต ์‘๋‹ต์ด success, code, message, data ํ˜•์‹์œผ๋กœ ๊ฐ์‹ธ์ง€์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.


โš ๏ธ Error Handling

๊ณตํ†ต ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋Š” app/core์—์„œ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ ์ฃผ์š” ์—๋Ÿฌ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Code HTTP Status Description
COMMON_400 400 ์ž˜๋ชป๋œ ์š”์ฒญ
INTERNAL_AUTH_401 401 ์„œ๋ฒ„ ๊ฐ„ ์ธ์ฆ ์‹คํŒจ
COMMON_403 403 ํ˜„์žฌ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๊ธฐ๋Šฅ
COMMON_404 404 ๋ฆฌ์†Œ์Šค ์—†์Œ
STALE_DRAFT_REVISION_409 409 ์ตœ์‹  ์ž…๋ ฅ์ด ์กด์žฌํ•˜๋Š” ์ด์ „ ์ถ”์ฒœ ์š”์ฒญ ์ค‘๋‹จ
COMMON_422 422 ์š”์ฒญ๊ฐ’ ๊ฒ€์ฆ ์‹คํŒจ
COMMON_500 500 ์„œ๋ฒ„ ๋‚ด๋ถ€ ์˜ค๋ฅ˜
INTERNAL_AUTH_500 500 ๋‚ด๋ถ€ API ์ธ์ฆ ์„ค์ • ๋ˆ„๋ฝ
EMBEDDING_400 400 ์ž„๋ฒ ๋”ฉ ์ž…๋ ฅ๊ฐ’ ๋ˆ„๋ฝ
EMBEDDING_503 503 ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ์—ฐ๋™ ๋ถˆ๊ฐ€
NEO4J_503 503 Neo4j ์—ฐ๊ฒฐ ๋ถˆ๊ฐ€
LLM_503 503 LLM ์—ฐ๋™ ๋ถˆ๊ฐ€

โš™๏ธ Environment Configuration

brain ์„œ๋ฒ„๋Š” .env ๊ธฐ๋ฐ˜์œผ๋กœ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

APP_NAME=tryna brain
APP_ENV=local
API_V1_PREFIX=/api/v1
ROOT_PATH=
INTERNAL_API_KEY=local-internal-api-key

VALKEY_HOST=localhost
VALKEY_PORT=6379
VALKEY_PASSWORD=

NEO4J_URI=neo4j://localhost:7687
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=password
NEO4J_DATABASE=neo4j

UPSTAGE_API_KEY=
UPSTAGE_API_KEY_MULTI=
UPSTAGE_QUERY_EMBEDDING_MODEL=solar-embedding-1-large-query
UPSTAGE_PASSAGE_EMBEDDING_MODEL=solar-embedding-1-large-passage
UPSTAGE_EMBEDDING_TIMEOUT_SECONDS=10
UPSTAGE_CHAT_MODEL=solar-pro3
UPSTAGE_CHAT_TIMEOUT_SECONDS=20

D102_EMBEDDING_DIMENSION=4096
D102_EVENT_TYPE_MIN_SCORE=0.61
D102_CONTEXT_MIN_SCORE=0.63
D102_PLACE_TYPE_MIN_SCORE=0.63
D102_RECOMMENDATION_MIN_SCORE=0.62

์šด์˜ ํ™˜๊ฒฝ(APP_ENV=prod)์—์„œ๋Š” Redis/Valkey ์—ฐ๊ฒฐ์— TLS์™€ ์‹œ์Šคํ…œ CA ์ธ์ฆ์„œ ๊ฒ€์ฆ์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. .env๋Š” Git์— ์ปค๋ฐ‹ํ•˜์ง€ ์•Š๊ณ  .env.example๋งŒ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ” Security

๋ฏผ๊ฐ ์ •๋ณด๋Š” ์ €์žฅ์†Œ์— ์ปค๋ฐ‹ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ๊ฐ’์€ ๋กœ์ปฌ .env ๋˜๋Š” ๋ฐฐํฌ ํ™˜๊ฒฝ์˜ secret์œผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

  • INTERNAL_API_KEY
  • NEO4J_PASSWORD
  • VALKEY_PASSWORD
  • UPSTAGE_API_KEY
  • UPSTAGE_API_KEY_MULTI

.gitignore์— ๋‹ค์Œ ํŒŒ์ผ๊ณผ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ œ์™ธํ•˜๋„๋ก ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

.env
.env.local
.env.*.local
.venv
__pycache__/

โœ… Current Setup Checklist

  • ์ž์—ฐ์–ด ์ผ์ • ๋ฏธ๋ฆฌ๋ณด๊ธฐ API ๊ตฌํ˜„
  • D101~D105 ์ถ”์ฒœ ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌํ˜„
  • Neo4j ์—ฐ๊ฒฐ ๋ฐ ๊ด€๊ณ„ยท๋ฒกํ„ฐ ์ถ”์ฒœ ํ›„๋ณด ์กฐํšŒ
  • Upstage ์ž„๋ฒ ๋”ฉ ๋ฐ ์ถ”์ฒœ ๋ฌธ๊ตฌ ์ •์ œ
  • Redis/Valkey ์—ฐ๊ฒฐ ๋ฐ ์ตœ์‹  revision ๊ฒ€์ฆ
  • ๋‚ด๋ถ€ API ํ‚ค ์ธ์ฆ
  • ๊ณตํ†ต ์˜ˆ์™ธ ์ฒ˜๋ฆฌ์™€ Health Check API
  • Docker ๋ฐ GitHub Actions CI/CD ๊ตฌ์„ฑ
  • pytest ํ…Œ์ŠคํŠธ ๊ตฌ์„ฑ

๐Ÿ—บ ๊ฐœ๋ฐœ ๋กœ๋“œ๋งต

  • Python ๋ฐ FastAPI ์‹คํ–‰ ํ™˜๊ฒฝ ๊ตฌ์ถ•
  • ์ž์—ฐ์–ด ์ผ์ • 1์ฐจ ํŒŒ์‹ฑ๊ณผ Kiwi ์—ฐ๋™
  • Neo4j ์—ฐ๊ฒฐ ๋ฐ ์ง€์‹๋ฒ ์ด์Šค ๊ตฌ์ถ•
  • Neo4j ์˜๋ฏธ ๋งคํ•‘๊ณผ ์ถ”์ฒœ ํ›„๋ณด ์กฐํšŒ ๊ตฌํ˜„
  • ๊ด€๊ณ„ยท๋ฒกํ„ฐ ์ถ”์ฒœ ํ›„๋ณด ๊ฒฐํ•ฉ
  • Upstage ์ž„๋ฒ ๋”ฉ ๋ฐ LLM ์—ฐ๋™
  • ์ถ”์ฒœ ํ•ญ๋ชฉ ์‹œ๊ฐ„ ๋งฅ๋ฝ ๊ฒ€์ฆ๊ณผ ์ตœ์ข… ์‘๋‹ต ๊ตฌ์„ฑ
  • Redis ๊ธฐ๋ฐ˜ ์˜ค๋ž˜๋œ ์ถ”์ฒœ ์š”์ฒญ ํ›„์† ์ฒ˜๋ฆฌ ์ฐจ๋‹จ
  • Spring ์„œ๋ฒ„ ๋‚ด๋ถ€ API ์—ฐ๋™

About

tryna FastAPI repository

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages