FUTURE_CAR SPEC TABLET - zero1ne-26/doc GitHub Wiki
- ์์ฑ: ๋ฏธ๋์ฐจ ํ ยท 2026-08-29 (2026-08-23ํ ์ ๋ฉด ์ฌ์์ฑ โ ๊ณผ๊ฑฐ ๋ด์ฉ์ ํ๊ธฐ)
- ๋์ ์ฝ๋:
tablet-fe60d353c(main) ยท ๋ฏธ๋์ฐจ ์๋ฒzer01ne-future-car-servermain-2026a1d3673(์กฐ์ง ๋ฏธ๋ฌ:zero1ne-26/future-car-server) - ๊ณ์ฝ ์ ๋ณธ: ์ํค
MQTT_SCHEMA-FUTURE_CARโ ์ด ๋ฌธ์๋ ํ๋ธ๋ฆฟ ๊ฐ๋ฐ์๊ฐ ํ ์ผ๋ง ๋ค๋ฃฌ๋ค. ํ ํฝยทํ๋ ์ ์๋ ์ ๋ณธ์ ๋ณธ๋ค. - ๋ฏธ๋์ฐจ ์๋ฒ ๋ฐฐํฌ ์ํ: 2026-08-29 Mac Studio ๋ฐฐํฌ ์๋ฃ(์๋ฒ
a1d3673ยทํด๋ผ51ba4a7, ๋ ๋ค5cc571f/8e22f63์ดํ). ยง7 "๋ฐฐํฌ ์ ํ ์ฐจ์ด"์ "๋ฐฐํฌ ์ " ๋์์ ๋ ์ด์ ํด๋น ์์ โ ๊ธฐ๋ก์ฉ์ผ๋ก๋ง ๋จ๊ธด๋ค.
์์ฝ: ํ๋ธ๋ฆฟ์ด ๋ฏธ๋์ฐจ์ ์ฃผ๊ณ ๋ฐ๋ ์ฝ๋๋ ์ด๋ฏธ ๋ค ๋ค์ด๊ฐ ์๋ค(
enter/exit๋ฐํ,future-car/response์์ , ๋ ธ์ ๋). 2026-08-28 ์ ์ฑ ๋ณ๊ฒฝ์ผ๋ก ๋ฑ ํ๋๊ฐ ์๋ก ํ์ํ๋ค โ ์ฐจ ์์ ์๋ ๋์ ํ๋ธ๋ฆฟ์ ์ฑํ ์ ๋ ฅ(ํ ์คํธยท๋ง์ดํฌยท์ ์ ์นฉ)์ ์ ๊ทธ๋ ๊ฒ(ยง2-A). ๋๋จธ์ง๋ ๊ถ์ฅ ์ฌํญ๊ณผ ํ์ธ ์ฌํญ์ด๋ค.
- 0. ๋ฐฐ๊ฒฝ โ ๋ฏธ๋์ฐจ๊ฐ ๋ฌด์์ด๊ณ ๊ด๋๊ฐ์๊ฒ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๋
- 1. ํ์ฌ ๊ตฌํ ์ํ (60d353c ๋์กฐ)
- 2. ์ถ๊ฐ ๊ตฌํ
- 3. ๋ฏธ๋์ฐจ โ ํ๋ธ๋ฆฟ: ์ธ์ ๋ฌด์์ด ์ค๊ณ ๋ฌด์์ ๋ณด์ฌ์ฃผ๋
- 4. ํ๋ธ๋ฆฟ โ ๋ฏธ๋์ฐจ:
enterยทexit - 5. ์ฑํ ํ ํฝ์์ ์ผ์ด๋๋ ์ผ
- 6. ๊ฒ์ฆ ์ ์ฐจ
- 7. ๋ฐฐํฌ ์ ํ ์ฐจ์ด
- 8. ์ฅ์ ยท์์ธ ์ํฉ
- 9. ๋ณ๊ฒฝ ์ด๋ ฅ ยท ๋ฌธ์
์ ์์ฅ์๋ ๋ฏธ๋์ฐจ(์ค๋ฌผ ์ฐจ + ์ฐจ ์ ๋์คํ๋ ์ด + ๋ง์ดํฌ)๊ฐ ํ ๋ ์๋ค. ๊ด๋๊ฐ์ ํ๋ธ๋ฆฟ์ ๋ค๊ณ ์ฌ๋ฌ ์ฅ์(์งยท๊ณต์ยท๋งํธยท์ฐจ)๋ฅผ ๋๋ฉฐ ํ์คํฌ๋ฅผ ์ํํ๋๋ฐ, ๊ทธ์ค "์ฐจ" ์ฅ์๊ฐ ๋ฏธ๋์ฐจ๋ค. ๋ฏธ๋์ฐจ ์ชฝ ์์คํ ์ ๋์ด๋ค.
| ๊ตฌ์ฑ | ์ญํ |
|---|---|
๋ฏธ๋์ฐจ ์๋ฒ (Mac Studio :8100, FastAPI) |
MQTT ๋ก OCยทํ๋ธ๋ฆฟ๊ณผ ํต์ . ๊ด๋๊ฐ ๋ฐํ๋ฅผ ์ฌ๋ฃ๋ก LLM ์ด "์คํ "(์ฐจ ์ ์ฐ์ถ ํ ์ฅ๋ฉด)์ 4๊ฐ ์์ฑํด ํ๋ฉด์ ๋ด๋ ค๋ณด๋ด๊ณ , ํ๋ธ๋ฆฟ ์ฑํ ์ ์ง๋ฌธ์ ๋์ฐ๊ณ , OC ์ ํ์คํฌ ์์ยท์๋ฃ๋ฅผ ๋ณด๊ณ ํ๋ค |
๋ฏธ๋์ฐจ ํ๋ฉด (/ambient, ์ฐจ ์ ๋์คํ๋ ์ด) |
์คํ ์ ์ฌ์ํ๊ณ , ์ฌ์์ด ๋๋๋ฉด ์๋ฒ์ ๋ณด๊ณ ํ๊ณ , ๊ด๋๊ฐ ์ฐจ๋ก์ ๋ง์ดํฌ๋ฅผ ์ด์ด ๋ฐํ๋ฅผ STT ํด ์๋ฒ๋ก ๋ณด๋ธ๋ค |
- ํ๋ธ๋ฆฟ์์ "์ฐจ๋ก ์ด๋"์ ํ์ ํ๋ฉด(
TaskCompleteDialog) ํ๋ธ๋ฆฟ์ดenter๋ฅผ ๋ณด๋ธ๋ค. ์ฐจ ํ๋ฉด์ด ๋๊ธฐ ํ๋ฉด์ผ๋ก ๋ฐ๋๊ณ , ํ๋ธ๋ฆฟ ์ฑํ ์ "์ถ๋ฐ ํ ๊น์?" ์ ๋ต ํ๋ณด ์นฉ 2๊ฐ๊ฐ ๋ฌ๋ค. - ๊ด๋๊ฐ์ ์ฐจ์ ๋๊ณ ๋ตํ๋ค("๋ค, ์ถ๋ฐํด์"). ์ฐจ ๋ง์ดํฌ๊ฐ ๋ฐ์ ์๋ฒ๋ก ๋ณด๋ด๊ณ , ์๋ฒ๊ฐ ์คํ
1์ ๋ง๋ ๋ค(10~30์ด). ํ๋ธ๋ฆฟ์๋ ์ด ๊ตฌ๊ฐ(
route)์ด ๋ ธ์ ๋์ ๊ทธ๋ ค์ง๋ค. - ์ฐจ ํ๋ฉด์ด ์คํ
1์ ์ฌ์ํ๋ค. ๊ทธ๋์ ํ๋ธ๋ฆฟ์ "์ฐจ๋ ์ํยท์ฃผํ ์ ๊ฒ" ๊ฐ์
basicํ์คํฌ ์นด๋๊ฐ "์งํ ์ค" ์ด ๋๋ค. - ์ฌ์์ด ๋๋๋ฉด ์นด๋๊ฐ "์๋ฃ" ๋ก ๋ฐ๋๊ณ , ์ฑํ ์ ๋ค์ ์ง๋ฌธ์ด ๋ฌ๋ค. ๊ด๋๊ฐ์ ๋ค์ ์ฐจ์ ๋๊ณ ๋ตํ๋ค.
- 2~4 ๊ฐ ์คํ
4๊น์ง ๋ฐ๋ณต๋๋ค. ์คํ
4 ์ฌ์์ด ๋๋๋ฉด ์ง๋ฌธ ์์ด ์ฐจ ํ๋ฉด์ด ์๋ฉ์ ๋ณด์ฌ์ฃผ๊ณ , ํ๋ธ๋ฆฟ์๋ ํ์ฐจ ๊ฐ๋ฅ ์ ํธ(
state.next="exit")๊ฐ ์จ๋ค. - ํ๋ธ๋ฆฟ์์ "๋ค์ ์ฅ์๋ก ์ด๋"์ ํ์ ํ๋ฉด
exit๋ฅผ ๋ณด๋ธ๋ค. ์ฐจ ํ๋ฉด์ ์๋ณ ํ๋ฉด, ์ฌ์ ์ข ๋ฃ.
์ด์ ์ค๊ณ๋ ๊ด๋๊ฐ์ด ํ๋ธ๋ฆฟ ์ฑํ
์ ๋ตํ๋ฉด ๊ทธ ๋ต(ai/response.transcript)์ ๋ฏธ๋์ฐจ๊ฐ ๊ตฌ๋
ํด ์ฌ๋ฃ๋ก ์ผ๋ค. ์ง๊ธ์
์ฐจ ๋ง์ดํฌ๋ก๋ง ๋ฐ๋๋ค. ์ด์ ๋ ๋์ด๋ค.
- ํ๋ธ๋ฆฟ์ ๋ตํ๋ฉด OC ์ AI ๋๋ฐ์๊ฐ ๋ฐ๋ก ๋ต์ ํ๋ค(์ฐจ ์ ์ฐ์ถ๊ณผ ํ๋ธ๋ฆฟ ์ฑํ
์ด "๋ฐ๋ก ๋
ธ๋" ๋ฌธ์ ). OC ๊ฐ
ai/mode=car๋ฅผ ๊ตฌํํด ์์ฑ ๋ฐํ์๋ ๋ต์ ์๋ตํ์ง๋ง, ํ ์คํธ(์นฉ ํญยทํ์ดํ)์๋ ์ฌ์ ํ ๋ตํ๋ค(ai_chat.gocar ๋ถ๊ธฐ๊ฐtype=voice์์๋ง ์์). - ๋ฏธ๋์ฐจ ์๋ฒ๋ ์์ง
ai/response.transcript๋ฅผ ๊ตฌ๋ ํ๊ณ ์์ด์, ํ๋ธ๋ฆฟ์์ ๋ณด๋ธ ๋ฌธ์ฅ๋ ์คํ ์ฌ๋ฃ๋ก ์์ฌ ๋ค์ด๊ฐ๋ค.
๊ทธ๋์ ์ฐจ ์์ ์๋ ๋์(enter~exit) ํ๋ธ๋ฆฟ์ ์ฑํ
์
๋ ฅ์ ๋ฐ์ง ์๋๋ค. ๋ฏธ๋์ฐจ๊ฐ ๋์ฐ๋ ์ง๋ฌธยท์นฉ์ ๋ณด์ฌ์ฃผ๊ธฐ๋ง ํ๋ค.
๊ด๋๊ฐ์ด "๋ฌด์จ ๋ง์ ํ๋ฉด ๋๋์ง" ํํธ๋ก ์ฐ๋ผ๋ ๋ป์ด๋ค.
- ์ฐจ ์ฅ์ ํ์คํฌ ์ค
type=basic๋ง ๋ฏธ๋์ฐจ๊ฐ ์ํํ๋ค โ ํ๋ธ๋ฆฟ์ ์กฐ์ UI ๊ฐ ์์ด ์๋ฒ๊ฐ ์๋ฃํด ์ค์ผ ํ๋ ํ์ . ์คํ ๋ง๋ค ํ๋์ฉ AI ๊ฐ ๊ณจ๋ผ ์คํ ์์์task/start, ๋ ๋ ์๋ฃ์task/complete๋ฅผ OC ์ ๋ณด๋ธ๋ค. - ๊ทธ ์ธ ํ์
(
vehicle_readyยทdetailedยทquote_briefingโฆ ๋๋ณด๊ธฐ ๋ชจ๋ฌ)์ ๋ฏธ๋์ฐจ๊ฐ ๊ฑด๋๋ฆฌ์ง ์๋๋ค. ์ง๊ธ์ฒ๋ผ ๊ด๋๊ฐ์ด ํ๋ธ๋ฆฟ ๋ชจ๋ฌ์์ ์๋ฃํ๋ค.
๋ฏธ๋์ฐจ ํ์ด tablet-fe ์ฝ๋๋ฅผ ์ฝ๊ณ ํ์ธํ ๊ฒ. ์๋ ํญ๋ชฉ์ ์ถ๊ฐ ์์ ์ด ์๋ค.
| ํญ๋ชฉ | ์์น | ์ํ |
|---|---|---|
future-car/response ๊ตฌ๋
ยท SSE ์ค๊ณ ยท state replay ์บ์ |
server-bus.ts SESSION_SUFFIX.futureCar, handleFutureCarResponse(1014), bus.futureCarState(280) |
โ |
zod ์คํค๋ง 4์ข
(stateยทrouteยทerrorยทstep strip) |
schema.ts 995~1053 |
โ |
future-car-store (phaseยทstepยทcanExitยทrouteยทsegmentsยทlastError) |
store/future-car-store.ts |
โ |
enter/exit ๋ฐํ โ BFF ๋ผ์ฐํธ ยท ์ด๋ํฐ ยท ๊ฐ๋ |
app/api/orchestration/future-car/route.ts, future-car-client.ts, task-complete-dialog.tsx 101~135 |
โ |
| ๋ ธ์ ๋(์ง๋์จ ๊ตฌ๊ฐ ๋์ ) | components/main/cctv/vehicle-route-view.tsx |
โ |
| ๋ฏธ๋์ฐจ ์ง๋ฌธยท์นฉ ํ์ |
chat-store.receiveAiResponse(๋ชจ๋ฅด๋ context_id โ AI ๋ฒ๋ธ) ยท receiveSuggestions
|
โ ๊ธฐ์กด ๋ก์ง ๊ทธ๋๋ก |
| signout ์ storeยทBFF ์บ์ ์ด๊ธฐํ |
end-session.ts 80, server-bus.ts 357 |
โ |
| ๋ฏธ๋์ฐจ ๋ชจ๋ ์ ๋ ฅ ์ ๊ธ |
chat-input.tsx, chat-thread.tsx, use-ai-chat.ts
|
โฌ ์์ โ ยง2-A |
ํ์ฐจ ์ ๋ UI (useFutureCarCanExit) |
future-car-store.ts 120 โ ์ ์๋ง ์๊ณ ๋ฏธ์ฌ์ฉ |
โฌ ๊ถ์ฅ โ ยง2-B |
future-car-store.phase ๊ฐ waiting ยท driving ยท arrived ์ค ํ๋์ธ ๋์(= enter ์ดํ exit ์ด์ ):
| ์ ๊ธ ๊ฒ | ์ง๊ธ ์ฝ๋ | ์ ๊ทผ ๋ค |
|---|---|---|
| ํ ์คํธ ์ ๋ ฅ์ฐฝ |
chat-input.tsx:43 disabled = pending || sessionUuid === null
|
์กฐ๊ฑด์ inCar ์ถ๊ฐ โ Input disabled
|
| ๋ง์ดํฌ(press-and-hold) | ๊ฐ์ disabled ๊ฐ usePushToTalk ๋ก ์ ๋ฌ๋จ |
์๋์ผ๋ก ํจ๊ป ์ ๊น |
| ์ ์ก ๋ฒํผ |
canSend โ disabled
|
์๋ |
| ์ ์ ์นฉ ํญ |
chat-thread.tsx:76 onPickSuggestion โ sendText
|
์นฉ์ ํ์ํ๋ disabled(ํญ ๋ฌด์) |
| ํ๋ก๊ทธ๋จ์ ์ ์ก |
use-ai-chat.ts dispatch (sendTextยทsendSystemText ๊ณต์ฉ) |
inCar ๋ฉด return false โ ์ด๋ค ๊ฒฝ๋ก๋ก๋ ai/request ๊ฐ ๋๊ฐ์ง ์๊ฒ ํ๋ ์ต์ข
๋ฐฉ์ด์
|
ํ์: ์ ๋ ฅ์ฐฝ placeholder ๋ฅผ "์ฐจ๋ ์์์๋ ์ฐจ์ ์ง์ ๋ง์ํด ์ฃผ์ธ์" ๋ก. ์นฉ์ ํ์์ผ๋ก ๋ณด์ด๋ ๋๋ฆฌ์ง ์๊ฒ(๊ด๋๊ฐ์ด "์ด๋ ๊ฒ ๋ตํ๋ฉด ๋๋๊ตฌ๋" ํํธ๋ก ์ฝ๋๋ค).
ํด์ : phase ๊ฐ done(exit ๋ค) ๋๋ idle(์ plan) ๋๋ null(state ๋ฏธ์์ ) ์ด๋ฉด ์ ๊ทธ์ง ์๋๋ค.
- ์ ๊ธ์ ๊ทผ๊ฑฐ๋ "๋ฏธ๋์ฐจ ์๋ฒ๊ฐ ๋ฐํ ์์ง ์ฐฝ์ ์ด์ด ๋๊ณ ์๋๊ฐ"์ด๊ณ , ๊ทธ๊ฑด ์ ํํ
state.phase โ {waiting, driving, arrived}๋ค. -
state๋ retained ๋ผ ์๋ก๊ณ ์นจยทSSE ์ฌ์ฐ๊ฒฐ ๋ค์๋ ๋ง์ง๋ง ๊ฐ์ด ์ฆ์ ๋ณต์๋๋ค(BFFfutureCarState์บ์ + ๋ธ๋ก์ปค retained ์ด์ค). ์ฅ์(activePhase.zone)๋ก ํ๋จํ๋ฉด ํ๋ธ๋ฆฟ ๋จ๊ณ์ ์ฐจ ์ํ๊ฐ ์ด๊ธ๋๋ ์๊ฐ(์ด๋ ํ์ ์งํ ๋ฑ)์ ๊ตฌ๋ฉ์ด ์๊ธด๋ค. -
null(์์ง state ๋ฅผ ๋ชป ๋ฐ์)์ ์ ๊ทธ์ง ์๋๋ค โ ๋ฏธ๋์ฐจ ์๋ฒ๊ฐ ์ฃฝ์ด ์์ด๋ ํ๋ธ๋ฆฟ ์ฑํ ์ ์ด์ ์์ด์ผ ํ๋ค.
// use-ai-chat.ts โ dispatch ๋งจ ์
const carPhase = useFutureCarStore.getState().phase
if (carPhase === 'waiting' || carPhase === 'driving' || carPhase === 'arrived') return false
// chat-input.tsx
const carPhase = useFutureCarPhase()
const inCar = carPhase === 'waiting' || carPhase === 'driving' || carPhase === 'arrived'
const disabled = pending || sessionUuid === null || inCar
// placeholder: inCar ? '์ฐจ๋ ์์์๋ ์ฐจ์ ์ง์ ๋ง์ํด ์ฃผ์ธ์' : โฆ
// chat-thread.tsx โ ์นฉ ๋ฒํผ์ disabled={inCar}, onPickSuggestion ์ inCar ๋ฉด no-opinCar ํ์ ์ store ์
๋ ํฐ(useFutureCarInCar())๋ก ํ๋ ๋๊ณ ์ธ ๊ณณ์ด ๊ฐ์ด ์ฐ๋ฉด ์กฐ๊ฑด์ด ํฉ์ด์ง์ง ์๋๋ค.
| ์ํฉ | ๊ธฐ๋ ๋์ |
|---|---|
enter ๋ฐํ ์งํ state waiting ์ด ์ค๊ธฐ ์ 0.1~1์ด |
์์ง ์ ์ ๊น. ์ด ํ์ ๋ณด๋ธ ๋ฌธ์ฅ์ ๋ฏธ๋์ฐจ ์ฐฝ์ด ์ด๋ฏธ ์ด๋ ค ์์ด ์ฌ๋ฃ๋ก ๋ค์ด๊ฐ ์ ์๋ค โ ํ์ฉ ๋ฒ์(๊ด๋๊ฐ์ด ๊ทธ ์๊ฐ ํ์ดํํ ๊ฐ๋ฅ์ฑ ๋ฎ์). ์ํ๋ฉด enter ๋ฐํ ์์ ์ ๋๊ด์ ์ผ๋ก ์ ๊ฐ๋ ๋๋ค |
pending ์ํ(ํ๋ธ๋ฆฟ์ด ๋ต์ ๊ธฐ๋ค๋ฆฌ๋ ์ค)์์ enter
|
pending ์ด ๋จผ์ ์ ๊ทธ๊ณ ์์ผ๋ฏ๋ก ์ถฉ๋ ์์. car ๋ชจ๋์์ OC ๊ฐ message ์์ด transcript ๋ง ๋๋ ค์ค๋ receiveAiResponse ๊ฐ pending ์ ํผ๋ค(๋น ๋ฒ๋ธ ์ ์๊น) |
exit ๋ฐํ โ state done
|
์ฆ์ ํด์ . done ๋ค OC ai/mode=chat ๋ ํจ๊ป ๊ฐ๋ฏ๋ก OC AI ๋ต๋ณ์ด ์ ์ ๋ณต๊ท |
| ์๋ก๊ณ ์นจ | retained state ๋ก phase ๋ณต์ โ ์ ๊ธ ๋ณต์ |
| ๋ฏธ๋์ฐจ ์๋ฒ ์ฌ์์ |
state ๋ DB ๋ณต์ ํ ์ฌ๋ฐํ๋๋ค. ๊ทธ ์ฌ์ด์ ๋ง์ง๋ง retained ๊ฐ์ด ์ ์ง |
| signout |
useFutureCarStore.reset() โ phase null โ ํด์ |
state ยท phase=arrived ยท next="exit" (์คํ
4 ๋ ๋ ์๋ฃ) ๊ฐ ์ค๋ฉด canExit === true ๊ฐ ๋๋ค. ์ง๊ธ์ ์ด ๊ฐ์ ์๋ฌด UI ๋ ์ฝ์ง ์์
๊ด๋๊ฐ์ด ์ค์ค๋ก "๋ค์ ์ฅ์๋ก ์ด๋"์ ๋๋ฌ์ผ ํ๋ค. ์ฐจ ํ๋ฉด์ ์ด๋ "์ฒดํ์ด ๋ชจ๋ ๋๋ฌ์ต๋๋ค / ๋ท์ชฝ ์ถ๊ตฌ๋ก ํด์ฅํด ์ฃผ์ธ์" ๋ฅผ
๋ณด์ฌ์ฃผ๊ณ ์์ผ๋ฏ๋ก, ํ๋ธ๋ฆฟ์๋ ๊ฐ์ ๋งฅ๋ฝ์ ์๋ด("์ฐจ์์ ๋ด๋ ค ๋ค์ ์ฅ์๋ก ์ด๋ํด ์ฃผ์ธ์" + ์ด๋ ํ์ ์ผ๋ก ์ฐ๊ฒฐ)๋ฅผ ๋์ฐ๋ฉด ๊ด๋๊ฐ์ด
ํค๋งค์ง ์๋๋ค. useFutureCarCanExit() ๊ฐ ์ด๋ฏธ ์๋ค. ์ด๋์ ๋์ธ์ง๋ ํ๋ธ๋ฆฟ UX ๊ฒฐ์ ์ด๋ค(๋
ธ์ ๋ ์บก์
, ํ์คํฌ ํจ๋ ์๋จ ๋ฐฐ๋ ๋ฑ).
๋ฏธ๋์ฐจ ์๋ฒ๊ฐ ์คํ
๋ง๋ค task/start(์คํ
์์) โ task/complete(๋ ๋ ์๋ฃ) ๋ฅผ OC ์ ๋ณด๋ด๋ฉด OC ๊ฐ dashboard ๋ฅผ
in_progress โ completed ๋ก ์ฌ๋ฐํํ๋ค. ํ๋ธ๋ฆฟ์ basic ์นด๋(task-panel.tsx STATUS_STYLE)๋ ์ด๋ฏธ active(์งํ ์ค) ๋ฐฐ์ง๋ฅผ
๊ทธ๋ฆฌ๋ฏ๋ก ์ฝ๋ ๋ณ๊ฒฝ ์์ด ์คํ
๋์ "์งํ ์ค" โ ๋๋๋ฉด "์๋ฃ" ๋ก ๋ณด์ฌ์ผ ํ๋ค. ์ค์ ๋ก ๊ทธ๋ ๊ฒ ๋ณด์ด๋์ง๋ง ํ์ธํด ๋ฌ๋ผ.
non-basic ์นด๋(๋๋ณด๊ธฐ)๋ ๋ฐฐ์ง๊ฐ ์๊ณ ๋ฏธ๋์ฐจ๊ฐ ๊ฑด๋๋ฆฌ์ง ์์ผ๋ฏ๋ก ๋ณํ ์์.
use-task-summary.ts ๋ dashboard ์ฒซ ์์ ๋ ํ๋ฃจ ์์ฝ์ ai/request type=text ๋ก ๋ณด๋ธ๋ค. ์ด๊ฑด ์ธ์
์ด์
(= enter ์ )์ด๋ผ
๋ฏธ๋์ฐจ๊ฐ ์ฐฝ ๋ฐ(closed)์ผ๋ก ๋ฒ๋ฆฌ๋ฏ๋ก ๋ฌธ์ ์๋ค. ์์ผ๋ก ์ฐจ ์(waiting~arrived)์์ ์๋์ผ๋ก ai/request ๋ฅผ ๋ณด๋ด๋ ๊ธฐ๋ฅ์
์ถ๊ฐํ์ง ๋ง ๊ฒ โ ๋ฏธ๋์ฐจ๊ฐ ๊ด๋๊ฐ ๋ฐํ๋ก ์ค์ธํด ์คํ
์ ์์ฑํ๋ค. ยง2-A ์ dispatch ๊ฐ๋๊ฐ ์ด๊ฑธ ๋ง๋ ์ต์ข
๋ฐฉ์ด์ ์ด๋ค.
ํ ํฝ orchestration/{sessionId}/future-car/response (QoS 1). ํ๋ ์ ์๋ ์ ๋ณธ ยง5. ์ฌ๊ธฐ์๋ ์๊ฐ ์์ผ๋ก ๋ฌด์์ด ์ค๋์ง๋ฅผ ์ ๋๋ค.
| ์์ | ๊ด๋๊ฐ ยท ์ฐจ ํ๋ฉด | ๋์ฐฉํ๋ ๋ฉ์์ง | ํ๋ธ๋ฆฟ |
|---|---|---|---|
| ํ๋ฅด์๋ ์์ฑ ์งํ | ์ฐจ์ ๋ฌด๊ด |
state {step:0, phase:"idle", prev_place?, next_place?} (retained) |
์ ์ฌ์ . ๊ตฌ๊ฐยท์๋ฌ ๋น์ฐ๊ธฐ(๊ตฌํ๋จ). ์ ๊ธ ์๋ |
ํ๋ธ๋ฆฟ์ด enter ๋ฐํ |
์ฐจ ํ๋ฉด ๋๊ธฐ. ๋ง์ดํฌ ์ด๋ฆผ |
state {phase:"waiting"} โ ์ด์ด์ ์ฑํ
ํ ํฝ์ ai/response {message:"์ถ๋ฐ ํ ๊น์?"} + ai/suggestions ["๋ค, ์ถ๋ฐํด์!","์ ๊น๋ง์, ์ค๋น ์ข ํ ๊ฒ์"]
|
์ ๊ธ ์์. ์ฑํ ์ ์ง๋ฌธยท์นฉ ํ์(์นฉ ๋นํ์ฑ) |
| ๊ด๋๊ฐ์ด ์ฐจ์ ๋งํจ โ ์์ฑ ์ค | ๋๊ธฐ ํ๋ฉด ์ ์ง. "์ ์๋ง์" | (์์, 10~30์ด) | ๋ณํ ์์ |
| ์์ฑ ์๋ฃ | ์คํ ์ฌ์ ์์. ๋ง์ดํฌ ๋ซํ |
route {step:1, from_stop_id?, from_place?, to_stop_id?, to_place?} โ step {step:1, phase:"driving", data:{โฆ}}
|
๋
ธ์ ๋ ๊ตฌ๊ฐ ํ์. step.data ๋ ๋ฌด์(์คํค๋ง๊ฐ strip). basic ์นด๋ "์งํ ์ค"(OC dashboard ๊ฒฝ์ ) |
| ์ฌ์ ์๋ฃ | ๋ง์ง๋ง ์ฅ๋ฉด ์ ์ง. ๋ง์ดํฌ ์ด๋ฆผ |
state {step:1, phase:"arrived"} (next ์์) โ ์ฑํ
์ ai/response {message:<์ง๋ฌธ>} + ai/suggestions
|
์นด๋ "์๋ฃ". ์ฑํ ์ ๋ค์ ์ง๋ฌธยท์นฉ(๋นํ์ฑ) |
| ์คํ 2ยท3 | ์ ๋ฐ๋ณต | ||
| ์คํ 4 ์ฌ์ ์๋ฃ | ์๋ฉ ํ๋ฉด. ๋ง์ดํฌ ์ ์ด๋ฆผ |
state {step:4, phase:"arrived", next:"exit"} โ ์ง๋ฌธ ์์ |
canExit=true โ ํ์ฐจ ์ ๋(ยง2-B) |
ํ๋ธ๋ฆฟ์ด exit ๋ฐํ |
์๋ณ ํ๋ฉด | state {phase:"done"} |
์ ๊ธ ํด์ . ๋ ธ์ ๋ ์ข ๋ฃ ์ํ |
| ๋ค์ ๊ด๋๊ฐ | state {phase:"idle"} |
์ด๊ธฐํ | |
| ์์ฑ ์คํจ ๋ฑ | ์ฐจ ํ๋ฉด์ด ๋ง์ดํฌ๋ฅผ ๋ค์ ์ฐ๋ค | error {step?, code, message} |
JOURNEY_ENDED ๋ ํ์ ์ ํจ(์ ์ ์ข
๋ฃ). ๊ทธ ์ธ๋ ์๋ด |
์์๋ ๊ฒ:
-
์์ฑ ์ค์๋ ์๋ฌด ๋ฉ์์ง๋ ์ ์จ๋ค.
route๊ฐ ์ฌ ๋๊น์ง "์ด๋ ์ค๋น ์ค" ์ ๋๋ก ๋๋ฉด ๋๋ค. ์งํ ํ์๋ฅผ ํ๋ธ๋ฆฟ์ด ๋ง๋ค ํ์ ์๋ค. -
state๋ง retained.routeยทstepยทerror๋ ๋์น๋ฉด ๋ค์ ์ ์จ๋ค โ ๊ทธ๋์segments๋ฅผ ํด๋ผ์ด์ธํธ๊ฐ ๋์ ํ๋ค(๊ตฌํ๋จ). -
next๋ ์คํ 4arrived์๋ง ์ค๋ฆฐ๋ค. ํค ์กด์ฌ ์ฌ๋ถ๋ก ํ๋จ(๊ตฌํ๋จ:message.next === 'exit'). -
error.code:STEP_NOT_READY(์์ฑ ์ค ์งํ ์์ฒญ) ยทGENERATION_FAILED(LLM ์คํจ โ ๊ด๋๊ฐ์ด ๋ค์ ๋งํ๋ฉด ์ฌ์๋) ยทPLAN_MISSING(plan ์์) ยทJOURNEY_ENDED(exit ๋ค/์คํ ์ด๊ณผ, ์ ์).
ํ ํฝ orchestration/{sessionId}/future-car/request (QoS 1, retain=false). ํ์ด๋ก๋ {"type":"enter"} / {"type":"exit"}. ๊ตฌํ๋ผ ์๋ค.
| ๋ฐํ ์์ (๊ตฌํ๋จ) | ๊ฐ๋ (๊ตฌํ๋จ) | ๋ฏธ๋์ฐจ์์ ์ผ์ด๋๋ ์ผ |
|---|---|---|
| ๋ค์ ๋ชฉ์ ์ง๊ฐ ์ฐจ๋์ด๊ณ ์ด๋์ ํ์ ํ ๋ |
phase โ {null, idle} ์ผ ๋๋ง |
๋ฐํ ์ฐฝ ์ด๋ฆผ ยท state waiting ยท ai/mode car ยท ์ฑํ
์ "์ถ๋ฐ ํ ๊น์?" ยท ์ฐจ ํ๋ฉด ๋๊ธฐ + ๋ง์ดํฌ |
| ์ง๊ธ ์ฅ์๊ฐ ์ฐจ๋์ด๊ณ ๋ค์ ์ฅ์๋ก ์ด๋์ ํ์ ํ ๋ | phase โ {null, waiting, driving, arrived} |
๋ฐํ ์ฐฝ ๋ซํ ยท state done ยท ai/mode chat ยท ์ฐจ ํ๋ฉด ์๋ณ |
exit ๋ ์ธ์ ๋ณด๋ด๋ ์ฌ์ ์ ๊ทธ ์๋ฆฌ์์ ๋๋ธ๋ค. ์คํ
2 ์ฌ์ ์ค์ ๊ด๋๊ฐ์ด ํ๋ธ๋ฆฟ์์ "์ด๋ ํ์ "์ ๋๋ฌ ๋ฒ๋ฆฌ๋ฉด
์ฐจ ํ๋ฉด์ ์ฆ์ ์๋ณ ํ๋ฉด์ผ๋ก ๊ฐ๊ณ ๋จ์ ์คํ
์ ๋ฒ๋ ค์ง๋ค. ํ์ฌ ๊ฐ๋๋ ์ด๋ฅผ ๋ง์ง ์๋๋ค(๊ณ์ฝ์ ์ ํจ). ์ด์์์ "์คํ
4 ๋๋๊ธฐ
์ ์ ์ด๋ ํ์ ์ ๋๋ฅด์ง ์๊ฒ" ์๋ดํ๊ฑฐ๋, ยง2-B ์ฒ๋ผ canExit ๊ฐ ์ฐธ์ผ ๋๋ง ์ฐจ๋ ๋จ๊ณ์ ์ด๋ ํ์ ์ ์ด์ด ๋๋ ๊ฒ์ ๊ถํ๋ค โ
ํ์๋ ํ๋ธ๋ฆฟ UX ๊ฒฐ์ ์ด๋ผ ์์ฒญ์ด ์๋๋ผ ์ ์์ด๋ค.
advanceยทstart ๋ ๋ณด๋ด์ง ์๋๋ค(ํ๊ธฐ. ๋ณด๋ด๋ฉด ๋ฐํ ์์ด ๊ฐ์ ์งํ๋ผ deprecated ๋ก ๊ธฐ๋ก๋๋ค).
ํ๋ธ๋ฆฟ์ด ์ด๋ฏธ ๊ตฌ๋
ํ๋ ai/responseยทai/suggestions ์ ๋ฏธ๋์ฐจ๊ฐ ์ง์ ๋ฐํํ๋ค. OC ๋ฅผ ๊ฑฐ์น์ง ์๋๋ค.
| ํ ํฝ | ๋ฏธ๋์ฐจ ๋ฐํ ๋ด์ฉ | ํ๋ธ๋ฆฟ ๋์ (๊ธฐ์กด ๋ก์ง) |
|---|---|---|
orchestration/{sid}/ai/response |
{"ok":true, "context_id":"<๋ฏธ๋์ฐจ UUID>", "message":"์ถ๋ฐ ํ ๊น์?"} |
context_id ๊ฐ ํ๋ธ๋ฆฟ ๊ฒ์ด ์๋๋ฏ๋ก isCurrent=false โ AI ๋ฒ๋ธ๋ก ํ์ |
orchestration/{sid}/ai/suggestions |
{"context_id":"<๊ฐ์ ๊ฐ>", "suggestions":["๋ค, ์ถ๋ฐํด์!","์ ๊น๋ง์, ์ค๋น ์ข ํ ๊ฒ์"]} (์ต๋ 3๊ฐ) |
receiveSuggestions โ ์นฉ ํ์. ยง2-A ๋ค์๋ ํญ ๋ถ๊ฐ
|
๋ฐํ ์์ : enter ์งํ 1ํ, ์คํ
1~3 ๋ ๋ ์๋ฃ ์งํ ๊ฐ 1ํ. ์คํ
4 ๋ค์๋ ์๋ค. retain=false.
OC ์ชฝ ๋์(์ฐธ๊ณ ): enter ๋ ๋ฏธ๋์ฐจ๊ฐ ai/mode {car} ๋ฅผ retained ๋ก ์ฌ๋ฆฌ๋ฉด OC ๋ ๊ทธ ์ธ์
์ ์์ฑ ai/request ์ LLM ๋ต๋ณยท์ ์์
์๋ตํ๊ณ transcript ๋ง ๋๋ ค์ค๋ค(message ์์ โ ํ๋ธ๋ฆฟ์ ๋น ๋ฒ๋ธ ์์ด pending ๋ง ํ๋ฆผ). exit ๋ {chat} ์ผ๋ก ๋ณต๊ท. ์ด๊ฑด
์ ๊ธ์ด ๋ซ๋ ธ์ ๋์ 2์ฐจ ์์ ์ฅ์น์ด๊ณ , 1์ฐจ๋ ยง2-A ๋ค.
๋ฏธ๋์ฐจ ์๋ฒ ๋๊ตฌ(์ ์๋ง Tailscale ์์์๋ง):
| ์ฃผ์ | ์ฉ๋ |
|---|---|
http://creator-mac-studio.tail69930a.ts.net:8100/mqtt |
์ฃผ๊ณ ๋ฐ์ MQTT ์๋ฌธ ์๊ฐ์ + ์๋ ๋ฐํ(enter/exit/advance) + DB ์ด๋ ฅ ํ ๊ธ |
POST โฆ:8100/ambient/utterance {"session_id":"โฆ","transcript":"๋ค, ์ถ๋ฐํด์","source":"debug"}
|
์ฐจ ๋ง์ดํฌ ์์ด ๊ด๋๊ฐ ๋ฐํ ํ๋ด โ ์คํ ์์ฑ |
GET โฆ:8100/ambient/session/{session_id} |
์ธ์ ์ค๋ ์ท(phaseยทstepยท์๋ฃ ํ์คํฌยท์์ง ๋ฐํ) |
GET โฆ:8100/logs?session_id=โฆ |
์๋ฒ ์งํ ๋ก๊ทธ(DB, ์ฌ์์ ๋ค์๋ ๋จ์) |
โฆ:8100/docs |
Swagger |
- ํ๋ธ๋ฆฟ์์ ํ๋ฅด์๋ ์์ฑ โ
/mqtt์์ ๊ทธ ์ธ์ ์state idleํ์ธ. ํ๋ธ๋ฆฟ ์ฑํ ์ ๋ ฅ ๊ฐ๋ฅ์ด์ด์ผ ํ๋ค. - ํ๋ธ๋ฆฟ์์ ์ฐจ๋ก ์ด๋ ํ์ (๋๋
/mqtt์๋enter) โstate waiting์์ โ ์ ๋ ฅ์ฐฝยท๋ง์ดํฌยท์นฉ ์ ๊น, placeholder ์๋ด ๋ฌธ๊ตฌ, ์ฑํ ์ "์ถ๋ฐ ํ ๊น์?" + ์นฉ 2๊ฐ(๋นํ์ฑ). -
POST /ambient/utterance๋ก "๋ค, ์ถ๋ฐํด์" โ 10~30์ด ๋คrouteยทstepโ ๋ ธ์ ๋ ๊ตฌ๊ฐ ํ์. ์ฌ์ ํ ์ ๊น. -
/mqtt์์ ๊ทธ ์ธ์ ์state arrived๊ฐ ์ฌ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ๋ค(์ฐจ ํ๋ฉด์ด ์์ผ๋ฉด ๋ ๋ ์๋ฃ ๋ณด๊ณ ๊ฐ ์ ์ค๋ฏ๋กPOST โฆ:8100/ambient/step-rendered {"session_id","step":1}๋ก ํ๋ด) โ ์ฑํ ์ ์คํ 1 ์ง๋ฌธยท์นฉ. ์ฌ์ ํ ์ ๊น.basic์นด๋๊ฐ "์งํ ์ค"โ"์๋ฃ"(ยง2-C). - 3~4 ๋ฅผ ์คํ
4๊น์ง โ
state arrived next=exitโ ํ์ฐจ ์ ๋(ยง2-B) ํ์. - ์ด๋ ํ์ (๋๋ ์๋
exit) โstate doneโ ์ ๊ธ ํด์ , ์ฑํ ์ ๋ ฅ ๊ฐ๋ฅ. - ์๋ก๊ณ ์นจํด์ 2 ๋จ๊ณ ์ํ๋ฅผ ๋ค์ ์ด์ด ๋ดค์ ๋ ์ ๊ธ์ด ๋ณต์๋๋์ง(retained state).
์ฐจ ํ๋ฉด /ambient ๊ฐ ์ด๋ ค ์์ผ๋ฉด 3~4 ๋จ๊ณ์ ๋ฐํยท๋ ๋ ์๋ฃ๊ฐ ์๋์ผ๋ก ์ผ์ด๋๋ค. ๊ด๋๊ฐ์ด ์ฐจ์ ๋งํ์ ๋ ํ๋ธ๋ฆฟ ์ฑํ
์ ๊ทธ
๋ง์ด ์ฌ์ฉ์ ๋ฒ๋ธ๋ก ๋จ์ง ์๋ ๊ฒ์ด ์ ์์ด๋ค(์ฐจ ๋ง์ดํฌ ๋ฐํ๋ ํ๋ธ๋ฆฟ ์ฑํ
๊ณผ ๋ฌด๊ดํ HTTP ๊ฒฝ๋ก).
2026-08-29 ๋ฐฐํฌ ์๋ฃ๋ก ์๋๋ ๊ธฐ๋ก์ฉ์ด๋ค.
๋ฏธ๋์ฐจ ์๋ฒ 5cc571f ๊ฐ Mac Studio ์ ๋ฐฐํฌ๋๊ธฐ ์ (10321ac)์๋:
-
task/start๊ฐ ๋๊ฐ์ง ์๋๋ค โ ์นด๋๊ฐ "๋๊ธฐ"์์ ๋ฐ๋ก "์๋ฃ"๋ก ๊ฐ๋ค(ยง2-C ๋ ๋ฐฐํฌ ํ ํ์ธ). - ๋ชจ๋ฌํ(non-basic) ํ์คํฌ๋ ๋ฏธ๋์ฐจ๊ฐ
task/complete๋ฅผ ๋ณด๋ผ ์ ์๋ค โ ๊ด๋๊ฐ์ด ์ด์ด๋ณด์ง ์์ ๋๋ณด๊ธฐ ํ์คํฌ๊ฐ ์๋ฃ๋ก ๋ฐ๋ ์ ์๋ค(๋ฐฐํฌ ํ ์ฌ๋ผ์ง). - ์ฐจ ๋ง์ดํฌ ๊ฒฝ๋ก(
/ambient/utterance์ ์ ํธ๋ฆฌ๊ฑฐ)๊ฐ ์๋ค โ ์ค์ฐจ ๊ฒ์ฆ(ยง6-2)์ ๋ฐฐํฌ ํ. ยง6-1 ์ ์ง๊ธ๋ ๊ฐ๋ฅ.
ยง2-A ์ ๊ธ์ ๋ฐฐํฌ ์ ํ์ ๋ฌด๊ดํ๊ฒ ์ง๊ธ ๋ฃ์ด๋ ๋๋ค โ state.phase ๋ ์ด๋ฏธ ๋๊ฐ๊ณ ์๋ค.
| ์ํฉ | ํ๋ธ๋ฆฟ์ ๋ณด์ด๋ ๊ฒ | ํ๋ธ๋ฆฟ์ด ํ ์ผ |
|---|---|---|
| ๋ฏธ๋์ฐจ ์๋ฒ๊ฐ ์ฃฝ์ด ์์ |
enter ๋ค state waiting ์ด ์ ์ด |
์ ๊ธ ์ ๊ฑธ๋ฆผ(phase null/idle). ์ฑํ
์ OC ์ ํ์์ฒ๋ผ. ๊ด๋๊ฐ์ ์ฐจ ์ฒดํ ์์ด ๋ค์ ์ฅ์๋ก โ exit ๋ phase null ์์๋ ๋ฐํ๋๋ฏ๋ก ๋ฌธ์ ์์ |
| ์คํ ์์ฑ ์คํจ | error {code:"GENERATION_FAILED"} |
์๋ด๋ง. ์ฐจ ํ๋ฉด์ด ๋ง์ดํฌ๋ฅผ ๋ค์ ์ด๊ณ ๊ด๋๊ฐ์ด ๋ค์ ๋งํ๋ฉด ์ฌ์๋๋๋ค. ํ๋ธ๋ฆฟ์ ์๋ฌด๊ฒ๋ ๋ณด๋ด์ง ์๋๋ค |
| ๊ด๋๊ฐ์ด ์ฐจ์ ๋งํ์ง ์์ |
arrived ์์ ๋ฉ์ถค |
์งํ ๋ถ๊ฐ(์ค๊ณ ์ ์ ). ํ์ฅ ์คํํ๊ฐ ์ฐจ์ ๋งํ๋๋ก ์๋ด. ๋น์์ ์๋ฒ /mqtt ์ advance ๋ก ๊ฐ์ ์งํ |
| ์ฐจ ๋ง์ดํฌ ๊ถํ ๊ฑฐ๋ถยทSTT ์ค๋ฅ | ์์ ๊ฐ์ | ์์ ๊ฐ์(์ฐจ ํ๋ฉด ํ๋จ์ ๋ถ์ ํ์๊ฐ ๋ฌ๋ค โ ์ด์์ ํ์ธ์ฉ) |
| SSE ์ฌ์ฐ๊ฒฐ | BFF ์บ์ replay ๋ก state ๋ณต์ |
์์(๊ตฌํ๋จ) |
| ๋ฏธ๋์ฐจ ์๋ฒ ์ฌ์์ |
state ๋ DB ๋ณต์ ํ ์ฌ๋ฐํ. ๋ํ ํ์คํ ๋ฆฌ๋ ๋ณต์ ์ ๋จ(๋ค์ ์คํ
ํ์งโ) |
์์ |
| OC ์ด๋ฒคํธ ํ์ ์ด ์ฐจ ์์์ ๋ธ |
event ํ์
(์ฅ์ ๋ฌด๊ด) |
์ง๊ธ์ฒ๋ผ ํ์ยท๋ซ๊ธฐ. ์ฐจ๊ฐ ์ด๋ฒคํธ์ ๋ฐ์ํ ์ง๋ ๋ฏธ์ โ ํ๋ธ๋ฆฟ ๋ณ๊ฒฝ ์์ฒญ ์์ |
exit ๋ฅผ ์ผ์ฐ ๋ณด๋ |
state done, ์ฐจ ์๋ณ ํ๋ฉด |
ยง4 ๊ฒฝ๊ณ ์ฐธ๊ณ |
| ๋ ์ง | ๋ด์ฉ |
|---|---|
| 2026-08-23 | ์ดํ โ ๊ตฌ๋ ยทstoreยทenter/exit ๋ฐํ ๊ฐ์ด๋ (tablet-fe ์ ๋ฏธ๋์ฐจ ์ฝ๋ 0๊ฑด ์์ ) |
| 2026-08-29 | ์ ๋ฉด ์ฌ์์ฑ โ ๊ตฌํ ์๋ฃ๋ถ ์ ๋ฆฌ, ์ ๋ ฅ ์ ๊ธ(ยง2-A) ์ ๊ท ์์ฒญ, ํ์ฐจ ์ ๋ยทํ์คํฌ ์นด๋ยทํ์๋ผ์ธยท๊ฒ์ฆยท์์ธ ์ถ๊ฐ. ์ฐจ ๋ง์ดํฌ STT ์ ์ฑ ๋ฐ์ |
๋ฌธ์: ๋ฏธ๋์ฐจ ํ. ์๋ฒ ํธ๋ํฝยท๋ก๊ทธ๋ ์ ยง6 ๋๊ตฌ๋ก ์ง์ ๋ณผ ์ ์๋ค โ "์ ์ ์๋"๋ /mqtt ์ /logs?session_id= ์ ๋จ๋๋ค.