LiveKit ๊ธฐ๋ฐ AI ์์ด ๋ํ ์ค์ต ์์คํ . TBLT(Task-Based Language Teaching) ๋ฐฉ์์ผ๋ก, AI ์บ๋ฆญํฐ Kate์ ๊ณผ์ ๊ธฐ๋ฐ ์์ด ๋ํ๋ฅผ ์ํํ๋ ์ค์๊ฐ ์์ฑ ๋ํ ์์ด์ ํธ. ๋์: ํ๊ตญ์ด๊ถ ํ์.
๊ด๋ฆฌ์๋ /admin์์ ์์
์ด์ ๋ชจ๋๋ฅผ ์ ํํ ์ ์๋ค.
- ๊ทธ๋ฃน ๋ํ ๋ชจ๋ (
pipeline): ํ์ n๋ช + ์์ด์ ํธ 1๋ช ์ STT โ LLM โ TTS ํ์ดํ๋ผ์ธ. ์์ด์ ํธ ์ด๋ฆ:pipeline-agent - ๊ฐ๋ณ ๋ํ ๋ชจ๋ (
realtime): ํ์ 1๋ช + ์์ด์ ํธ 1๋ช ์ OpenAI Realtime speech-to-speech ํ์ดํ๋ผ์ธ. ์์ด์ ํธ ์ด๋ฆ:realtime-agent, role:dominant๋๋collaborative
CSCL_TBLT/
โโโ .env # API ํค (์ง์ ์
๋ ฅ ํ์, git ์ถ์ ์ ๋จ)
โโโ .env.example # ํ๊ฒฝ๋ณ์ ํ
ํ๋ฆฟ
โโโ .github/
โ โโโ workflows/
โ โโโ agent-tests.yml # agent/ ํ
์คํธ CI
โ โโโ client-build-and-test.yaml # client/ ๋น๋/๋ฆฐํธ CI
โ
โโโ agent/ # AI ์์ฑ ์์ด์ ํธ (LiveKit Agents Python SDK)
โ โโโ main.py # ์์ด์ ํธ ์ง์
์ (CLI ์ง์)
โ โโโ prompt_pipeline.py # ๊ทธ๋ฃน ๋ํ ๋ชจ๋ ์์คํ
ํ๋กฌํํธ
โ โโโ prompt_realtime.py # ๊ฐ๋ณ ๋ํ ๋ชจ๋ ์์คํ
ํ๋กฌํํธ
โ โโโ logger.py # ๋ํ JSON ๋ก๊ฑฐ
โ โโโ pyproject.toml
โ โโโ uv.lock
โ โโโ tests/
โ โโโ test_conversation.py # LLM ๋จ๋
๋ํ ํ๋ฆ ํ
์คํธ
โ
โโโ server/ # legacy static ํด๋ผ์ด์ธํธ์ฉ ํ ํฐ ๋ฐ๊ธ ์๋ฒ (FastAPI)
โ โโโ main.py # GET /token, POST /dispatch
โ โโโ pyproject.toml
โ โโโ uv.lock
โ
โโโ client/ # Next.js ๋ธ๋ผ์ฐ์ ํด๋ผ์ด์ธํธ
โ โโโ app/ # Next.js App Router + API routes
โ โโโ components/ # ๋ก๋น, ๊ด๋ฆฌ์, LiveKit UI ์ปดํฌ๋ํธ
โ โโโ static/ # legacy ์ฌํ ํด๋ผ์ด์ธํธ (HTML/JS)
โ โ โโโ index.html
โ โ โโโ app.js
โ โ โโโ style.css
โ โโโ .env.local # Next.js์ฉ ํ๊ฒฝ๋ณ์ (git ์ถ์ ์ ๋จ)
โ
โโโ config.example.json # ๋ก์ปฌ fallback/import์ฉ ์ด์ ์ค์ ์์
โโโ config.json # ๋ก์ปฌ fallback ์ด์ ์ค์ (git ์ถ์ ์ ๋จ)
โโโ logs/ # ๋ํ ๋ก๊ทธ JSON (์๋ ์์ฑ, git ์ถ์ ์ ๋จ)
์ด ์ ์ฅ์๋ main / develop ๊ธฐ์ค์ผ๋ก ์ด์ํ๋ค.
| ๋ธ๋์น | ์ญํ | ์ด์ ์์น |
|---|---|---|
main |
์ค์ ์๋ฒ ๋ฐฐํฌ์ ์ฐ๊ฒฐ๋๋ ์์ ๋ธ๋์น | ๋ฐฐํฌ ๊ฐ๋ฅํ ์ํ๋ง ์ ์งํ๋ค. ๊ฒ์ฆ๋ ๋ณ๊ฒฝ๋ง ๋ณํฉํ๊ณ , ๋ฏธ์์ฑ ์คํ์ ์ฌ๋ฆฌ์ง ์๋๋ค. |
develop |
๊ธฐ๋ฅ ๊ฐ๋ฐ๊ณผ ์คํ ํตํฉ ๋ธ๋์น | ์ ๊ธฐ๋ฅ, UI/ํ๋กฌํํธ ์คํ, ๊ฒ์ฆ ์ ๋ณ๊ฒฝ์ ๊ธฐ๋ณธ ํฉ๋ฅ ์ง์ ์ผ๋ก ์ฌ์ฉํ๋ค. |
๊ธฐ๋ณธ ํ๋ฆ:
- ์ ์์
์
develop์์feature/...,issue-...,experiment/...๋ธ๋์น๋ฅผ ๋ง๋ ๋ค. - ์ผ๋ฐ ๋ณ๊ฒฝ์ ์์
๋ธ๋์น์์
develop์ผ๋ก PR์ ์ด์ด ํฉ์น๋ค. - ๋ฐฐํฌํ ๋ฌถ์์ด ์์ ํ๋๋ฉด
develop์์main์ผ๋ก PR์ ์ด์ด ํฉ์น ๋ค ์ค์ ์๋ฒ์ ๋ฐฐํฌํ๋ค. - ์ด์ ์ฅ์ ๋ ๊ธด๊ธ ์์ ์
main์์hotfix/...๋ธ๋์น๋ฅผ ๋ง๋ค๊ณ , ๋ฐฐํฌ ํ ๊ฐ์ ์์ ์ดdevelop์๋ ๋ฐ์๋๋๋ก ํ๋ค.
๋ธ๋ผ์ฐ์ ํด๋ผ์ด์ธํธ (client/ Next.js)
โ
โโ[GET /api/rooms]โโโโโโโโโโโโโโโบ Supabase app_settings + LiveKit room ์กฐํ
โโ[POST /api/admin/config]โโโโโโโบ Supabase app_settings์ ์ด์ ์ค์ ์ ์ฅ
โโ[POST /api/token]โโโโโโโโโโโโโโบ LiveKit ์ฐธ๊ฐ์ ํ ํฐ ๋ฐ๊ธ
โ room_config.agents๋ก agent ์๋ ๋ฐฐ์น
โ
โโ[WebRTC ์ฐ๊ฒฐ]โโโโโโโโโโโโโโโโโบ LiveKit Cloud
โ
โโ pipeline-agent: STT โ LLM โ TTS
โโ realtime-agent: OpenAI Realtime
- Next.js ํด๋ผ์ด์ธํธ (
client/): ํ์ ๋ก๋น, ๊ด๋ฆฌ์ ์ค์ , ๊ด๋ฆฌ์ ๋์๋ณด๋, ํ ํฐ ๋ฐ๊ธ API๋ฅผ ํฌํจํ๋ค. - AI ์์ด์ ํธ (
agent/): ๊ฐ์ ์ฝ๋์์pipeline-agent,realtime-agentworker๋ฅผ ์คํํ๋ค. Realtime role์dominant๋๋collaborative๋ก ์ ํํ๋ค. - legacy ํ ํฐ ์๋ฒ (
server/):client/staticHTML ํด๋ผ์ด์ธํธ์ฉ์ด๋ค. ํ์ฌ Next.js ์ฑ ์คํ์๋ ํ์ํ์ง ์๋ค.
| ๋ชจ๋ | UX ํ์๋ช | Room ์ ์ฑ | Agent | ์์ฑ ์ฒ๋ฆฌ |
|---|---|---|---|---|
pipeline |
๊ทธ๋ฃน ๋ํ ๋ชจ๋ | 12๋ฐ-1๊ทธ๋ฃน ๊ฐ์ ๊ทธ๋ฃน room |
pipeline-agent |
STT deepgram/nova-3 โ LLM openai/gpt-4.1-mini โ TTS cartesia/sonic-3 |
realtime |
๊ฐ๋ณ ๋ํ ๋ชจ๋ | realtime-{๋ฐ}-{ํ์๋ช
}-{suffix} ์๋ ์์ฑ |
realtime-agent |
OpenAI Realtime speech-to-speech |
curl -LsSf https://astral.sh/uv/install.sh | shcp .env.example .env
cp config.example.json config.jsonconfig.json์ Supabase๊ฐ ์ค์ ๋์ง ์์ ๋ก์ปฌ ๊ฐ๋ฐ fallback ๋ฐ ์ด๊ธฐ import ์ฐธ๊ณ ์ฉ์ด๋ค. Production ์ด์ ์ค์ ์ Supabase app_settings์ ์ ์ฅ๋๋ค.
.env ํ์ผ์ ๊ฐ ์
๋ ฅ:
# LiveKit Cloud (https://cloud.livekit.io โ ํ๋ก์ ํธ ์์ฑ ํ ๋ฐ๊ธ)
LIVEKIT_URL=wss://your-project.livekit.cloud
LIVEKIT_API_KEY=your_api_key
LIVEKIT_API_SECRET=your_api_secret
# ์ฑํ
๋ฐฉ ๋ฐ ์์ด์ ํธ ์ด๋ฆ
ROOM_NAME=english-practice
AGENT_NAME=pipeline-agent
AGENT_WORKER_MODE=pipeline
AGENT_ROLE=dominant
# Realtime ๋ชจ๋์๋ OPENAI_API_KEY๊ฐ ํ์
# Pipeline ๋ชจ๋๋ LiveKit Inference๋ฅผ ์ฌ์ฉํ๋ฉด OPENAI_API_KEY / DEEPGRAM_API_KEY ์์ด๋ ๋์
OPENAI_API_KEY=
DEEPGRAM_API_KEY=Next.js ์ฑ์ ์ด์ ๋ชจ๋๋
.env๊ฐ ์๋๋ผ Supabaseapp_settings.agent_mode๋ก ๊ฒฐ์ ๋๋ฉฐ,/admin์์ ๋ณ๊ฒฝํ๋ค. Supabase๊ฐ ์๋ ๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ์์๋config.jsonfallback์ ์ฌ์ฉํ ์ ์๋ค. Realtime ์ํธ์์ฉ role์ ๊ด๋ฆฌ์ ์ ์ฉagentRole์ค์ ์ผ๋ก๋ง ๋ ธ์ถ๋๋ค.
cd agent
uv sync
uv run python main.py download-files์ต์ด 1ํ ์์กด์ฑ์ ์ค์นํ๋ค.
pnpm setup๋ฃจํธ์์ ๋ค์ ๋ช
๋ น ํ๋๋ก agent worker์ Next.js ํด๋ผ์ด์ธํธ๋ฅผ ํจ๊ป ์คํํ๋ค. pnpm dev์ auto ๋ชจ๋๋ ๋ก์ปฌ ํ๋ก์ธ์ค ์ ํ์ ์ํด config.json์ agentMode/agentRole์ ์ฝ๋๋ค. ์ฑ route์ ๋ฐํ์ ์ด์ ์ค์ ์ Supabase app_settings๋ฅผ ์ฌ์ฉํ๋ฉฐ, Supabase๊ฐ ์๋ ๋ก์ปฌ fallback ํ๊ฒฝ์์๋ config.json ๊ฐ์ ์ฌ์ฉํ๋ค.
pnpm dev๋ธ๋ผ์ฐ์ ์์:
- ํ์ ๋ก๋น: http://localhost:3000
- ๊ด๋ฆฌ์ ์ค์ : http://localhost:3000/admin
- ๊ด๋ฆฌ์ ๋์๋ณด๋: http://localhost:3000/admin/dashboard
Next.js ํด๋ผ์ด์ธํธ๋ ์์ฒด
/api/token๋ผ์ฐํธ๋ฅผ ๊ฐ์ง๋ฏ๋กserver/FastAPI ํ ํฐ ์๋ฒ๊ฐ ํ์ ์๋ค.
๊ฐ๋ณ ๋ํ ๋ชจ๋ worker๋ก ์คํํ๋ ค๋ฉด ๋ค์ ๋ช ๋ น์ ์ฌ์ฉํ๋ค.
pnpm dev:realtimecollaborative ์กฐ๊ฑด์ผ๋ก ์คํํ๋ ค๋ฉด ๋ค์ ๋ช ๋ น์ ์ฌ์ฉํ๋ค.
pnpm dev:realtime:collaborativepipeline, realtime dominant, realtime collaborative worker๋ฅผ ๋ชจ๋ ํจ๊ป ๋์ฐ๋ ค๋ฉด ๋ค์ ๋ช ๋ น์ ์ฌ์ฉํ๋ค.
pnpm dev:all/admin์์ ๊ฐ๋ณ ๋ํ ๋ชจ๋์ ์ํธ์์ฉ ๋ฐฉ์์ ์ ํํ๋ฉด ํ์์ ์กฐ๊ฑด๋ช
์ ๋ณด์ง ์๊ณ ๊ฐ๋ณ room์ผ๋ก ์
์ฅํ๋ค.
๊ธฐ์กด์ฒ๋ผ ํฐ๋ฏธ๋์ ๋๋ ์คํํด์ผ ํ ๋๋ ๋ค์ ๊ฐ๋ณ ๋ช ๋ น์ ์ฌ์ฉํ ์ ์๋ค.
pnpm dev:agent:pipeline
pnpm dev:clientclient/static์ ์ฌ์ฉํ ๋๋ง FastAPI ํ ํฐ ์๋ฒ๊ฐ ํ์ํ๋ค.
# ํฐ๋ฏธ๋ 1 โ legacy ํ ํฐ ์๋ฒ
cd server
uv sync
uv run python -m uvicorn main:app --port 8000 --reload# ํฐ๋ฏธ๋ 2 โ pipeline-agent
cd agent
AGENT_WORKER_MODE=pipeline uv run python main.py dev# ํฐ๋ฏธ๋ 3 โ static HTML ํด๋ผ์ด์ธํธ
cd client/static
python -m http.server 3000๋ธ๋ผ์ฐ์ ์์ http://localhost:3000 ์ ์ โ ์ด๋ฆ ์ ๋ ฅ โ ์ ์ฅ โ ๐ค ์์ด์ ํธ ์์ฑ ๋ฒํผ ํด๋ฆญ.
์์ด์ ํธ๋ฅผ ํฐ๋ฏธ๋์์ ์ง์ ์คํํด ๋ฐฑ์๋๋ง ๋น ๋ฅด๊ฒ ์คํํ ์ ์์ต๋๋ค. ๋ง์ดํฌ์ ์คํผ์ปค๋ง ์์ผ๋ฉด ๋๊ณ , ํ ํฐ ์๋ฒ์ ํด๋ผ์ด์ธํธ๋ ๋ถํ์ํฉ๋๋ค.
cd agent
# ํฐ๋ฏธ๋ ์์ฑ ๋ํ ๋ชจ๋ (๋ง์ดํฌ๋ก ๋งํ๊ณ ์คํผ์ปค๋ก ๋ฃ๊ธฐ)
uv run python main.py console
# ํ๋ก ํธ์๋ ์ฐ๊ฒฐ ๋๊ธฐ ๋ชจ๋ (ํ ํฐ ์๋ฒยทํด๋ผ์ด์ธํธ์ ํจ๊ป ์ฌ์ฉ)
uv run python main.py dev
# ํ๋ก๋์
๋ชจ๋
uv run python main.py start| ๋ช ๋ น | ์ค๋ช | ํ์ํ ๊ฒ |
|---|---|---|
console |
ํฐ๋ฏธ๋ ๋จ๋ ์์ฑ ๋ํ | ๋ง์ดํฌ, ์คํผ์ปค |
dev |
ํ๋ก ํธ์๋ ์ฐ๊ฒฐ ๋๊ธฐ (์๋ ์ฌ์์) | Next.js ํด๋ผ์ด์ธํธ |
start |
ํ๋ก๋์ ์คํ | Next.js ํด๋ผ์ด์ธํธ |
download-files |
VAD ๋ฑ ML ๋ชจ๋ธ ๋ค์ด๋ก๋ | โ |
client/.env.local์ LiveKit ์๊ฒฉ์ฆ๋ช
์ด ํ์ํ๋ค.
LIVEKIT_URL=wss://your-project.livekit.cloud
LIVEKIT_API_KEY=your_api_key
LIVEKIT_API_SECRET=your_api_secret
# Supabase admin auth / persistent state migration
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_...
SUPABASE_SECRET_KEY=sb_secret_...Next.js API routes:
| Route | ์ญํ |
|---|---|
POST /api/token |
์ฐธ๊ฐ์ ํ ํฐ ๋ฐ๊ธ, ํ์ฌ ์ด์ ๋ชจ๋์ ๋ง๋ agent ์๋ ๋ฐฐ์น |
GET /api/rooms |
ํ์ฑ ๋ฐ์ ๊ทธ๋ฃน room๊ณผ realtime ๊ฐ๋ณ room ํํฉ ์กฐํ |
GET/POST /api/admin/config |
Supabase app_settings ์ฝ๊ธฐ/์ฐ๊ธฐ |
GET/POST /api/dispatch |
๊ทธ๋ฃน ๋ํ ๋ชจ๋์์ pipeline-agent ์๋ ๋ฐฐ์น/์กด์ฌ ํ์ธ |
POST /api/rooms/terminate |
room ๊ฐ์ ์ข ๋ฃ |
GET /api/logs, GET /api/logs/stream |
๋ํ ๋ก๊ทธ ๋ชฉ๋ก/์คํธ๋ฆฌ๋ฐ |
์ฃผ์: Production์์
/admin,/api/admin, ๊ด๋ฆฌ์์ฑ API(/api/dispatch,/api/logs,/api/rooms/terminate)๋ Supabase Auth session๊ณผprofiles.role = 'admin'๊ถํ์ผ๋ก ๋ณดํธ๋๋ค.ADMIN_USERNAME/ADMIN_PASSWORDBasic Auth๋ ๋ ์ด์ ์ฌ์ฉํ์ง ์๋๋ค.
Supabase ๋์ ์ GitHub issue #4์ ๋จ๊ณ๋ณ ์์ ์ผ๋ก ์งํํ๋ค. ํ์ฌ admin auth์ ์์ ์ด์ ์ค์ ์ Supabase๋ฅผ ์ฌ์ฉํ๋ค.
client/lib/supabase/client.ts: browser/client component์ฉ Supabase clientclient/lib/supabase/server.ts: server component, route handler, server action์ฉ cookie-aware clientclient/lib/supabase/admin.ts: ์๋ฒ ์ ์ฉ secret-key clientclient/lib/supabase/proxy.ts: Auth session refresh์ admin route guard์ฉ proxy/middleware helperclient/lib/settings-store.ts:app_settings๊ธฐ๋ฐ ์ด์ ์ค์ store์ local fallback adaptersupabase/config.toml: ๋ก์ปฌ Supabase CLI ์คํ ํฌํธ์ migration ์ค์ supabase/migrations/20260611000000_foundation.sql: ์ด๊ธฐ schema์ RLS policysupabase/migrations/20260612000000_realtime_prompt_version_rpc.sql: active prompt version ๊ต์ฒด RPC
์ด๊ธฐ schema๋ ๋ค์ ํ ์ด๋ธ์ ๋ง๋ ๋ค.
| Table | ์ญํ |
|---|---|
profiles |
Supabase Auth ์ฌ์ฉ์๋ณ ์ฑ ๊ถํ ๋ฐ ํ์๋ช |
app_settings |
์์ ์ด์ ์ค์ ์ ์ฅ์ |
realtime_prompt_versions |
Realtime prompt override version ์ ์ฅ์ |
์ต์ด admin์ Supabase Auth ์ฌ์ฉ์ ์์ฑ ํ SQL ๋๋ dashboard์์ profiles.role = 'admin'์ผ๋ก ๋ถ์ฌํ๋ค.
insert into public.profiles (user_id, role, display_name)
values ('<auth-user-id>', 'admin', 'Admin')
on conflict (user_id) do update
set role = 'admin',
display_name = excluded.display_name;SUPABASE_SECRET_KEY๋ RLS๋ฅผ ์ฐํํ ์ ์์ผ๋ฏ๋ก server-only ์ฝ๋์์๋ง ์ฌ์ฉํ๋ค. Next.js API routes๋ client/.env.local์์ ์ด ๊ฐ์ ์ฝ๊ณ , Python realtime agent๋ root .env์์ ๊ฐ์ URL/secret์ ์ฝ์ด promptVersionId์ ํด๋นํ๋ realtime_prompt_versions row๋ฅผ ๊ฐ์ ธ์จ๋ค.
๊ด๋ฆฌ์ ๋ก๊ทธ์ธ ํ๋ฉด์ /admin/login์ด๋ค. ๋ก๊ทธ์ธ์ Supabase email/password ๊ณ์ ์ ์ฌ์ฉํ๋ฉฐ, ๊ณ์ ์ profiles.role์ด admin์ด์ด์ผ /admin ๋ฐ ๊ด๋ฆฌ์์ฑ API๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
๊ธฐ์กด config.json ๊ฐ์ Supabase๋ก ์ฎ๊ธธ ๋๋ ๋ค์ ํํ๋ก app_settings ๊ธฐ๋ณธ row๋ฅผ seedํ๋ค.
insert into public.app_settings (
id,
num_classes,
num_groups_per_class,
class_start,
active_class,
agent_mode,
agent_role,
feedback_condition_id,
realtime_resetting
)
values (
'default',
4,
12,
9,
9,
'realtime',
'collaborative',
'explicit_correction',
false
)
on conflict (id) do update
set num_classes = excluded.num_classes,
num_groups_per_class = excluded.num_groups_per_class,
class_start = excluded.class_start,
active_class = excluded.active_class,
agent_mode = excluded.agent_mode,
agent_role = excluded.agent_role,
feedback_condition_id = excluded.feedback_condition_id,
realtime_resetting = excluded.realtime_resetting;Production์์๋ client/.env.local๊ณผ root .env์ Supabase ์ฐ๊ฒฐ๊ฐ์ด ํ์ํ๋ค. Next.js์๋ NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY, SUPABASE_SECRET_KEY๊ฐ ํ์ํ๊ณ , Python realtime agent์๋ NEXT_PUBLIC_SUPABASE_URL ๋๋ SUPABASE_URL๊ณผ SUPABASE_SECRET_KEY ๋๋ SUPABASE_SERVICE_ROLE_KEY๊ฐ ํ์ํ๋ค. Supabase๊ฐ ๋ฏธ์ค์ ์ด๊ฑฐ๋ DB ์ฅ์ ๊ฐ ์์ผ๋ฉด ์ด์ ์ค์ route๋ setup/runtime error๋ฅผ ๋ฐํํ๋ค. ๋ก์ปฌ ๊ฐ๋ฐ์์๋ Supabase๊ฐ ์๊ฑฐ๋ ์ผ์์ ์ผ๋ก ์คํจํ ๋ config.json fallback์ ์ฌ์ฉํ๋ค.
๊ธฐ์กด prompt_config.json override๋ supabase/README.md์ realtime_prompt_versions Migration ์ ์ฐจ๋ก ํ ๋ฒ๋ง active version row๋ก ์ด๊ดํ๋ค. ์ด๊ด ํ Realtime custom prompt source of truth๋ Supabase prompt version row๋ค. /api/token์ active row์ promptVersionId๋ฅผ LiveKit metadata์ ๋ฃ๊ณ , Python realtime agent๋ ๊ทธ id๋ก ๊ฐ์ row๋ฅผ fetchํ๋ค. promptVersionId๊ฐ ์๋ default session์ tracked markdown prompt๋ฅผ ์ฌ์ฉํ๋ค.
๋ก์ปฌ Supabase CLI๋ ๊ธฐ์กด ํ๋ก์ ํธ์ ๊ธฐ๋ณธ ํฌํธ๊ฐ ์ถฉ๋ํ์ง ์๋๋ก 5532x ๋์ญ์ ์ฌ์ฉํ๋ค.
supabase start
supabase db reset --no-seed
supabase statusclient/.env.local์์ ๋ก์ปฌ Supabase๋ฅผ ์ฌ์ฉํ ๋๋ supabase status ์ถ๋ ฅ์ key๋ฅผ ๋ฃ๋๋ค.
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:55321
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=<publishable-key-from-supabase-status>
SUPABASE_SECRET_KEY=<secret-key-from-supabase-status>Studio๋ http://127.0.0.1:55323, Postgres๋ postgresql://postgres:postgres@127.0.0.1:55322/postgres์์ ์ด๋ฆฐ๋ค.
/admin์์ ๋ค์ ๊ฐ์ ๊ด๋ฆฌํ๋ค.
| ์ค์ | ์ ์ฅ ์์น | ์ค๋ช |
|---|---|---|
| ์์ ์ด์ ๋ชจ๋ | app_settings.agent_mode |
pipeline ๋๋ realtime |
| ํ์ฌ ์์ ์ค์ธ ๋ฐ | app_settings.active_class |
ํ์ ๋ก๋น์ ํ์๋๋ ๋ฐ |
| ๋ฐ ๋ฒํธ ์์ | app_settings.class_start |
๋ฐ ๋ฒํธ ๋ฒ์์ ์์ |
| ์ ์ฒด ํ๊ธ ์ | app_settings.num_classes |
๊ด๋ฆฌ์ ํ๋ฉด์ ํ์ํ ๋ฐ ๊ฐ์ |
| ๋ฐ๋น ๊ทธ๋ฃน ์ | app_settings.num_groups_per_class |
๊ทธ๋ฃน ๋ํ ๋ชจ๋์์ ํ์ํ ๊ทธ๋ฃน ์ |
์์:
{
"numClasses": 4,
"numGroupsPerClass": 12,
"classStart": 9,
"activeClass": 12,
"agentMode": "pipeline"
}๋ง์ดํฌยท๋ธ๋ผ์ฐ์ ์์ด LLM๋ง ์ฌ์ฉํด ๋ํ ํ๋ฆ์ ์๋ ๊ฒ์ฆํฉ๋๋ค.
cd agent
uv run pytest tests/ -vํ
์คํธ ๋ด์ฉ (tests/test_conversation.py):
- 5ํด ๋ํ ํ๋ฆ ๊ฒ์ฆ (์ฃผ๋ง ์ฝ์ ๋ง๋ค๊ธฐ ํ์คํฌ)
- ๋ฌธ๋ฒ ์ค๋ฅ ๋ฐํ์ ๋ํ Kate์ ์์ฐ์ค๋ฌ์ด ๋ฐ์ ํ์ธ
- ์ญ์ง๋ฌธ(์ฃผ๋ง ํ๋)์ ๋ํ ์๋ต ํ์ธ
Next.js ํด๋ผ์ด์ธํธ๊ฐ ์คํ ์ค์ผ ๋ ๋ธ๋ผ์ฐ์ ์์ ์๋ ์ฃผ์์ ์ ์ํ๋ฉด ์ธ์ ๋ก๊ทธ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
http://localhost:3000/admin/dashboard
- ์ ์ฅ๋ ์ธ์ ๋ชฉ๋ก ํ์ธ
- ์ธ์ ๋ณ ๋ํ ๋ก๊ทธ ํ์ธ
- ์ฐธ๊ฐ์ ์ด๋ฆ, ๋ฐํ ์๊ฐ, ์ญํ (User/Agent) ํ์
legacy static ํด๋ผ์ด์ธํธ๋ฅผ ์ฌ์ฉํ ๋๋ client/static/admin.html์ ์ฌ์ฉํ ์ ์๋ค.
๋ํ๊ฐ ๋๋๋ฉด logs/ ํด๋์ JSON ํ์ผ๋ก ์๋ ์ ์ฅ๋ฉ๋๋ค.
root .env์์ LOG_PROMPT_STACK=true๋ฅผ ์ค์ ํ๋ฉด ๊ฐ Realtime ์ธ์
์ ์ ์ฒด resolved prompt stack์ด metadata.prompt_stack์ ํจ๊ป ์ ์ฅ๋๊ณ /admin/dashboard์์ ์ ์ด์ ํ์ธํ ์ ์์ต๋๋ค. ๊ธฐ๋ณธ๊ฐ์ off์
๋๋ค.
logs/
โโโ RM_9GkS7d8BWCsf--260409_14:23.json
ํ์ผ๋ช
ํ์: {session_id}_{started_at}.json
{
"session_id": "RM_9GkS7d8BWCsf",
"room": "english-practice",
"entries": [
{
"timestamp": "2026-04-09T14:23:05",
"role": "user",
"text": "Hello!",
"participant_identity": "user_1234",
"participant_name": "๊ณ ์ค๋ณด"
},
{
"timestamp": "2026-04-09T14:23:07",
"role": "agent",
"text": "Hi! How are you today?"
}
]
}| ํญ๋ชฉ | ์์น |
|---|---|
| Realtime AI ์์คํ ํ๋กฌํํธ ์์ | ๊ธฐ๋ณธ๊ฐ์ prompts/realtime/*.md, ๋ฐํ์ ์์ ์ /admin์์ Supabase realtime_prompt_versions๋ก ์ ์ฅ |
| ๊ธฐ๋ณธ AI ์์คํ ํ๋กฌํํธ fallback | ๊ทธ๋ฃน ๋ํ๋ agent/prompt_pipeline.py, ๊ฐ๋ณ ๋ํ๋ prompts/realtime/*.md |
| ๊ทธ๋ฃน ๋ํ ๋ชจ๋ ๋ชจ๋ธ ๋ณ๊ฒฝ | agent/main.py (STT deepgram/nova-3, LLM openai/gpt-4.1-mini, TTS cartesia/sonic-3) |
| ๊ฐ๋ณ ๋ํ ๋ชจ๋ ๋ชจ๋ธ ๋ณ๊ฒฝ | agent/main.py (openai.realtime.RealtimeModel) |
| ์คํํ worker ๋ชจ๋ | AGENT_WORKER_MODE=pipeline ๋๋ AGENT_WORKER_MODE=realtime + AGENT_ROLE=dominant/collaborative |
| ์์ ์ด์ ๋ชจ๋ ๋ณ๊ฒฝ | /admin ๋๋ Supabase app_settings.agent_mode |
| Realtime ์ํธ์์ฉ role ๋ณ๊ฒฝ | /admin ๋๋ Supabase app_settings.agent_role |
| ํ ํฐ ์๋ฒ ํฌํธ ๋ณ๊ฒฝ | server/main.py + client/static/app.js ์๋จ SERVER ๋ณ์ |
| legacy static Room ์ด๋ฆ ๋ณ๊ฒฝ | .env โ ROOM_NAME |
| ์์ด์ ํธ ์ด๋ฆ ๋ณ๊ฒฝ | agent/main.py, client/lib/agent-role.ts (pipeline-agent, realtime-agent) |
| ๋ก๊ทธ ์ ์ฅ ์์น ๋ณ๊ฒฝ | agent/logger.py โ LOGS_DIR |
Realtime ๊ธฐ๋ณธ ํ๋กฌํํธ๋ฅผ ์์ ํ ๋๋ prompts/realtime/ ์๋์ ๋ฌธ์ํ ํ๋กฌํํธ๋ฅผ ์์ ํฉ๋๋ค. agent์ admin ๊ธฐ๋ณธ๊ฐ์ ์ด md ํ์ผ๋ค์ ์ง์ ์ฝ์ต๋๋ค.
pnpm prompts:check์๋ณธ ํ์ผ์ base.md, roles/dominant.md, roles/collaborative.md, condition-combinations/*.md, task-cards/*.md์
๋๋ค. Realtime runtime stack์ base prompt, role prompt, role/feedback condition-combination prompt, task card ์์๋ก ์กฐํฉ๋ฉ๋๋ค. task-cards/manifest.json์ ๋ฑ๋ก๋ ์ฃผ์ ๋ณ task card ์ค /admin์์ ์ ํํ ํญ๋ชฉ์ด ๊ฐ๋ณ Realtime ์ธ์
์ ์ ์ฉ๋ฉ๋๋ค. pnpm prompts:check๋ ํ์ผ ๋๋ฝ์ด๋ ๋น ํ์ผ ๊ฐ์ ๊ธฐ๋ณธ ์ค๋ฅ๋ฅผ ๊ฒ์ฌํฉ๋๋ค.
/admin์์ ํ๋กฌํํธ๋ฅผ ์ ์ฅํ๋ฉด Supabase realtime_prompt_versions์ ์ version row๊ฐ ์์ฑ๋๊ณ ํด๋น row๋ง active๊ฐ ๋ฉ๋๋ค. active custom version์ prompts/realtime/*.md ๊ธฐ๋ณธ๊ฐ๋ณด๋ค ์ฐ์ ํ๋ฉฐ, base/role prompt์ ํจ๊ป conditionCombinationPrompts, feedbackConditionId, taskCardId, taskCardPrompt snapshot์ ๋ณด์กดํฉ๋๋ค. feedbackPrompt๋ legacy metadata๋ก ๋จ์ ์์ง๋ง runtime stack์๋ ์ฝ์
๋์ง ์์ต๋๋ค. md ๊ธฐ๋ณธ๊ฐ์ ๋ค์ ์ฐ๋ ค๋ฉด /admin์์ "๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋ณต์"์ ์คํํด active custom version์ ๋นํ์ฑํํฉ๋๋ค.
๋จ์ผ ๋ฌธ์์์ ๋ณต์ฌํ ํ๋กฌํํธ๋ฅผ ํ ํ์ผ๋ก ๊ฒ์ฌ๊ธฐ์ ๋๊ธธ ์๋ ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ ๊ฒ์ฌ๊ธฐ๋ # BASE PROMPT:, # INTERLOCUTOR ROLE PROMPT: Dominant, # INTERLOCUTOR ROLE PROMPT: Collaborative, # TASK CARD: ํค๋ฉ์ ๊ธฐ์ค์ผ๋ก ๋ค ๊ตฌ๊ฐ์ ๋๋๋๋ค.
pnpm dev, pnpm build, pnpm start, pnpm dev:agent:*, ํ๋ก๋์
๋ฐฐํฌ ์คํฌ๋ฆฝํธ๋ ์คํ ์ ์ prompts:check์ ๊ฐ์ ๊ฒ์ฌ๋ฅผ ์ํํฉ๋๋ค.
ํ์ฌ production ๊ธฐ์ค๊ฐ๊ณผ ๋ฐฐํฌ ์ ์ฐจ๋ docs/operations/๋ฅผ ๋ฐ๋ฅธ๋ค.
- Production Environment: ํ์ฌ EC2, ์๋น์ค๋ช , ์ด์ ๋ชจ๋, ์ธ๋ถ ์๋น์ค ๊ธฐ์ค๊ฐ
- Deployment Runbook: GitHub Actions ๋ฐฐํฌ, ์ฌ์์, health check ์ ์ฐจ
- Capacity Plan: ํ์์/์คํ์ผ ์ธ์คํด์ค์ Realtime ๋์ ์ธ์ ๊ธฐ์ค
- Incident Checklist: ์ฅ์ ํ์ธ ์์์ ์ฃผ์ ๋ก๊ทธ
์์ฝ:
| ํญ๋ชฉ | ํ์ฌ ๊ธฐ์ค |
|---|---|
| Production domain | https://tblt-agent.net/ |
| App path | /opt/cscl-tblt |
| Client | PM2 cscl-client, port 3000 |
| Agent | systemd cscl-agent.service |
| Operation mode source of truth | Supabase app_settings |
| Current production mode | realtime |
| Deploy script | scripts/deploy-production.sh |
| Health check | https://tblt-agent.net/api/health |
GitHub Actions๋ ์ธ ์ข ๋ฅ๋ก ๊ตฌ์ฑํ๋ค.
| Workflow | Trigger | ์ญํ |
|---|---|---|
pre-commit |
PR/push โ main, develop, ์๋ ์คํ |
๊ณตํต ํ์ผ ํ์ ๋ฐ repo guardrail ๊ฒ์ฌ |
CI |
PR โ main, develop, ์๋ ์คํ |
Client lint/format/build, Agent tests |
Deploy Production |
main push ๋๋ ์๋ ์คํ |
๊ฒ์ฆ ํ EC2 production ๋ฐฐํฌ |
Production ๋ฐฐํฌ๋ GitHub Actions๊ฐ SSH๋ก EC2์ ์ ์ํด scripts/deploy-production.sh๋ฅผ ์คํํ๋ค.
ํ์ํ GitHub Environment/Secrets:
| ์ด๋ฆ | ์ข ๋ฅ | ์ค๋ช |
|---|---|---|
PROD_SSH_HOST |
Secret | EC2 host ๋๋ IP |
PROD_SSH_USER |
Secret | ๋ฐฐํฌ SSH ์ฌ์ฉ์, ์: ubuntu |
PROD_SSH_KEY |
Secret | ๋ฐฐํฌ์ฉ private key |
PROD_SSH_PORT |
Secret | SSH port, ๊ธฐ๋ณธ๊ฐ 22 |
PROD_APP_DIR |
Environment variable | ์๋ฒ repo ๊ฒฝ๋ก, ๊ธฐ๋ณธ๊ฐ /opt/cscl-tblt |
์๋ฒ ์กฐ๊ฑด:
/opt/cscl-tblt๊ฐ Git checkout์ด์ด์ผ ํ๋ค.- ๋ฐฐํฌ ์ฌ์ฉ์๊ฐ
systemctl restart/is-active cscl-agent๋ฅผ password ์์ด ์คํํ ์ ์์ด์ผ ํ๋ค. pnpm,uv,pm2,curl์ด ์ค์น๋์ด ์์ด์ผ ํ๋ค.- ์๋ฒ์
.env,client/.env.local,config.json,prompt_config.json์ ๋ฐฐํฌ ์ค ๋ฐฑ์ ํ ๋ณต์๋๋ค. config.json์ git ์ถ์ ๋์์ด ์๋๋ฉฐ, Supabase๊ฐ ์๋ ๋ก์ปฌ fallback/import ์ฉ๋๋ก๋ง ์ฌ์ฉํ๋ค.- Production์์๋
client/.env.local์NEXT_PUBLIC_SUPABASE_URL,NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY,SUPABASE_SECRET_KEY๊ฐ ์์ด์ผ ํ๋ค. Realtime custom prompt๋ฅผ ์ฐ๋ agent host์ root.env์๋ Supabase URL๊ณผ server-only secret์ด ์์ด์ผ ํ๋ค. - Production ์ด์ ์ค์ ์ Supabase
app_settings(id = 'default')row์ ์ ์ฅ๋๋ค. - ์ต์ด admin ๊ณ์ ์ Supabase Auth ์ฌ์ฉ์ ์์ฑ ํ
profiles.role = 'admin'์ผ๋ก bootstrapํด์ผ ํ๋ค.
๋ฐฐํฌ ํ health check:
cscl-agentactive ํ์ธcscl-clientPM2 process ํ์ธ ๋ฐ ์ฌ์์http://localhost:3000/api/health์๋ต ํ์ธ
main์ merge๋๋ฉด ๋ฐฐํฌ๊ฐ ๋ฐ๋ก ์คํ๋๋ฏ๋ก, GitHub branch protection์์ pre-commit, CI / Client, CI / Agent๋ฅผ required check๋ก ์ค์ ํ๋ค.
# Agent ์ํ
sudo systemctl status cscl-agent
sudo journalctl -u cscl-agent --no-pager | tail -30
# Client ์ํ
pm2 list
pm2 logs cscl-client --lines 30
# nginx ์ํ
sudo systemctl status nginx
sudo nginx -t| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ์ด์ ๋ชจ๋ ์ ์ฅ | Supabase app_settings.agent_mode์ pipeline ๋๋ realtime ์ ์ฅ |
| Realtime ์ํธ์์ฉ role ์ ์ฅ | Supabase app_settings.agent_role์ dominant ๋๋ collaborative ์ ์ฅ |
| Agent entrypoint | agent/main.py์ pipeline-agent, realtime-agent ๋ฑ๋ก |
| ํ๋กฌํํธ ๋ถ๋ฆฌ | ๊ทธ๋ฃน ๋ํ๋ agent/prompt_pipeline.py, ๊ฐ๋ณ ๋ํ ๊ธฐ๋ณธ๊ฐ์ prompts/realtime/*.md |
| Realtime ์์กด์ฑ | livekit-agents[openai,silero,turn-detector]~=1.5 |
| Egress ์ ๋ก๋ | S3_ENDPOINT ๊ฐ์ด http๋ก ์์ํ๋ ๊ฒฝ์ฐ๋ง endpoint๋ก ์ฌ์ฉ |
์ธ์ ์์ ์ ์๋์ผ๋ก LiveKit Egress API๊ฐ ํธ์ถ๋์ด ๋ชจ๋ ์ฐธ๊ฐ์(ํ์ + AI) ์์ฑ์ด ํผํฉ๋ MP3 ํ์ผ์ด S3์ ์ ์ฅ๋ฉ๋๋ค.
- ์ ์ฅ ๊ฒฝ๋ก:
s3://tblt-agent-recordings/recordings/{๋ฃธ๋ช }-{LiveKit room SID}-{ํ์์คํฌํ}.mp3 - ํธ๋ฆฌ๊ฑฐ:
session.start()์งํ ์๋ ์์ - ์ข
๋ฃ: ๋ฃธ
disconnected์ด๋ฒคํธ ๋ฐ์ ์ ์๋ ์ข ๋ฃ - ๊ด๋ จ ์ฝ๋:
agent/egress_recorder.py
- ๋ก์ปฌ ๋ค์ด๋ก๋:
scripts/download_s3_recordings.sh [output_dir] - ๋ ์ง ํํฐ:
SINCE_YYYYMMDD=20260701 scripts/download_s3_recordings.sh [output_dir] - ์ญ์ dry run:
BEFORE_YYYYMMDD=20260601 scripts/delete_s3_recordings.sh - ์ค์ ์ญ์ :
BEFORE_YYYYMMDD=20260601 APPLY=true scripts/delete_s3_recordings.sh - LastModified ๊ธฐ์ค ์ญ์ :
BEFORE_LAST_MODIFIED=2026-06-01T00:00:00Z scripts/delete_s3_recordings.sh
Egress๋ LiveKit Cloud ์ธํ๋ผ์์ ์คํ๋๋ฏ๋ก ์๋ฒ ๋ถํ ์์. ๋จ, S3 ๋ฒํท ๋ฐ IAM ๊ถํ(
s3:PutObject) ์ค์ ์ด ๋์ด ์์ด์ผ ํจ.
S3์ ์ ์ฅ๋ ๋ น์ ํ์ผ์ ๋ก์ปฌ๋ก ๋ฐ๊ฑฐ๋ ์ ๋ฆฌํ ๋๋ ์๋ ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๋ค.
์ฌ์ ์กฐ๊ฑด:
- ๋ก์ปฌ์ AWS CLI(
aws)๊ฐ ์ค์น๋์ด ์์ด์ผ ํ๋ค. .env๋๋ ์ง์ ํENV_FILE์ ์ต์ํ ๋ค์ ๊ฐ์ด ์์ด์ผ ํ๋ค.S3_BUCKETS3_REGIONAWS_ACCESS_KEY๋๋AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY
- AWS IAM ๊ถํ:
- ๋ค์ด๋ก๋:
s3:ListBucket,s3:GetObject - ์ญ์ :
s3:ListBucket,s3:DeleteObject
- ๋ค์ด๋ก๋:
์คํฌ๋ฆฝํธ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํ๋ก์ ํธ ๋ฃจํธ์ .env๋ฅผ ์ฝ๋๋ค. ์ด์ ์๋ฒ์ ๊ฐ์ ์ฐ๊ณ ์ถ์ผ๋ฉด ENV_FILE=/opt/cscl-tblt/.env์ฒ๋ผ ์ง์ ํ๋ฉด ๋๋ค.
์ ์ฒด ๋ น์ ํ์ผ์ ๋ก์ปฌ ๋๋ ํฐ๋ฆฌ๋ก ๋ค์ด๋ก๋ํ๋ค.
scripts/download_s3_recordings.sh๊ธฐ๋ณธ ๋ค์ด๋ก๋ ๊ฒฝ๋ก๋ ./downloads/recordings์ด๋ค. ๋ค๋ฅธ ๊ฒฝ๋ก๋ก ๋ฐ๊ณ ์ถ์ผ๋ฉด ์ฒซ ๋ฒ์งธ ์ธ์๋ก ๋๊ธด๋ค.
scripts/download_s3_recordings.sh ./tmp/recordingsํ์ผ๋ช
์ ํฌํจ๋ ๋ ์ง ๊ธฐ์ค์ผ๋ก ํน์ ์์ ์ดํ๋ง ๋ฐ๊ณ ์ถ์ผ๋ฉด SINCE_YYYYMMDD๋ฅผ ์ฌ์ฉํ๋ค. ํ์์ ๋ฐ๋์ YYYYMMDD๋ค.
SINCE_YYYYMMDD=20260701 scripts/download_s3_recordings.sh
SINCE_YYYYMMDD=20260601 scripts/download_s3_recordings.sh ./tmp/recordings์ด ํํฐ๋ S3 LastModified๊ฐ ์๋๋ผ ํ์ผ๋ช
๊ท์น recordings/{๋ฃธ๋ช
}-{LiveKit room SID}-YYYYMMDD_HHMMSS.mp3์ ๋ ์ง ๋ถ๋ถ์ ๊ธฐ์ค์ผ๋ก ๋์ํ๋ค.
์ฌ์ฉ ๊ฐ๋ฅํ ํ๊ฒฝ๋ณ์:
ENV_FILE: ์ฝ์ด์ฌ env ํ์ผ ๊ฒฝ๋กS3_PREFIX: ๊ธฐ๋ณธ๊ฐrecordingsSINCE_YYYYMMDD: ํด๋น ๋ ์ง ์ด์์ธ ํ์ผ๋ง ๋ค์ด๋ก๋S3_ENDPOINT: Cloudflare R2, MinIO ๊ฐ์ S3 ํธํ ์คํ ๋ฆฌ์ง์ฉ endpoint
S3 ๊ฐ์ฒด ์ญ์ ๋ ์ค์ ๋น์ฉ์ด ํฌ๋ฏ๋ก ๊ธฐ๋ณธ๊ฐ์ด dry run์ด๋ค. APPLY=true๋ฅผ ์ฃผ์ง ์์ผ๋ฉด ์ค์ ์ญ์ ํ์ง ์๊ณ ์ญ์ ๋์๋ง ์ถ๋ ฅํ๋ค.
ํ์ผ๋ช ๋ ์ง ๊ธฐ์ค์ผ๋ก ํน์ ๋ ์ง ์ด์ ํ์ผ์ ํ์ธ:
BEFORE_YYYYMMDD=20260601 scripts/delete_s3_recordings.shํ์ผ๋ช ๋ ์ง ๊ธฐ์ค์ผ๋ก ์ค์ ์ญ์ :
BEFORE_YYYYMMDD=20260601 APPLY=true scripts/delete_s3_recordings.shS3 ๊ฐ์ฒด์ LastModified ๊ธฐ์ค์ผ๋ก ํน์ ์๊ฐ ์ด์ ํ์ผ์ ํ์ธ:
BEFORE_LAST_MODIFIED=2026-06-01T00:00:00Z scripts/delete_s3_recordings.shLastModified ๊ธฐ์ค ์ค์ ์ญ์ :
BEFORE_LAST_MODIFIED=2026-06-01T00:00:00Z APPLY=true scripts/delete_s3_recordings.sh์ฃผ์์ฌํญ:
BEFORE_YYYYMMDD์BEFORE_LAST_MODIFIED๋ ๋์์ ์ค์ ํ ์ ์๋ค.BEFORE_YYYYMMDDํ์์YYYYMMDD๋ค.BEFORE_LAST_MODIFIEDํ์์ UTC ISO-8601์ด์ด์ผ ํ๋ค. ์:2026-06-01T00:00:00Z- ์ค์ ์ญ์ ์ ์ dry run ์ถ๋ ฅ์ด ์๋ํ ๋์๊ณผ ๋ง๋์ง ๋จผ์ ํ์ธํ๋ ๊ฒ์ด ์ข๋ค.
์ด์ ์๋ฒ env๋ฅผ ์ง์ ์จ์ ์ญ์ ๋์์ ํ์ธํ๋ ค๋ฉด:
ENV_FILE=/opt/cscl-tblt/.env BEFORE_YYYYMMDD=20260601 scripts/delete_s3_recordings.sh- ๊ทธ๋ฃน ๋ํ ๋ชจ๋ ๊ธฐ์ค: ๋์ 30๋ช = ์ฝ 15๊ฐ Agent ์ธ์ (2์ธ 1๊ทธ๋ฃน)
- ๊ทธ๋ฃน ๋ํ ๋ชจ๋ ๋ณ๋ชฉ: Silero VAD (10ms ๋จ์ ๋ก์ปฌ ์ ๊ฒฝ๋ง ์ถ๋ก , ์ธ์
๋น
812% CPU) - ๊ทธ๋ฃน ๋ํ ๋ชจ๋ STT/LLM/TTS: LiveKit Cloud inference์์ ์ฒ๋ฆฌ
- ๊ฐ๋ณ ๋ํ ๋ชจ๋: ํ์ 1๋ช ๋น realtime Agent ์ธ์ 1๊ฐ
- ๊ฐ๋ณ ๋ํ ๋ชจ๋์๋ OpenAI Realtime ์ฌ์ฉ๋๊ณผ ๋์ ์ธ์ ํ๋ ํ์ธ ํ์
- ํ์ฌ production ๊ธฐ์ค์ Capacity Plan์ ๋ฐ๋ฅธ๋ค.
- t3 ๊ณ์ด์ ์ง์ ๊ณ ๋ถํ ์์ ์ด์ ์ CPU ํฌ๋ ๋ง ์์ง์ผ๋ก ์ค๋กํ๋ง๋ ์ ์๋ค.