FUTURE_CAR SPEC OC - zero1ne-26/doc GitHub Wiki
- ์์ฑ: ๋ฏธ๋์ฐจ ํ, 2026-08-23 ยท ๋์ ์ฝ๋:
orchestration-server64065c7 - ๋ฏธ๋์ฐจ๊ฐ OC ์ ์์ฒญํ๋ ๋ณ๊ฒฝ์ 3 ์ (์ธ์ ๋ชจ๋) ํ๋๋ค. 2 ์ ์ OC ์์ ์ด ์๊ณ "๋์"๋ง ํ์ํ๋ค. 1 ์ ์ ์ด๋ฏธ ๋์ ์ค์ธ ๊ฒ(์ฐธ๊ณ ).
- ๊ณ์ฝ ์ ๋ณธ: ์ํค
MQTT_SCHEMA-FUTURE_CARยง3-2-1
| ๋ฐฉํฅ | ํ ํฝ | OC ์ฝ๋ | ๋น๊ณ |
|---|---|---|---|
| OC โ ๋ฏธ๋์ฐจ |
future-car/request type=plan (retained) |
mqttx/future_vehicle.go PublishFutureVehiclePlan
|
stops[] = {id, kind, place, contexts}. ๋ฏธ๋์ฐจ๋ stops ๋ฅผ ์ฅ์ ์นดํ๋ก๊ทธ๋ก๋ง ์ด๋ค |
| OC โ ๋ฏธ๋์ฐจ |
dashboard (retained) |
mqttx/location_tasks.go |
์ฐจ ์ฅ์์ tasks[] ๋ฅผ ์ฌ์ ํ์คํฌ๋ก ์ด๋ค. status ๋ก ์๋ฃ ์ฌ๋ถ ํ๋จ |
| OC โ ๋ฏธ๋์ฐจ |
me (retained) |
mqttx/create_persona.go PersonaDisplayPayload.ai_companion.prompt
|
์คํ ์์ฑ ์์คํ ํ๋กฌํํธ ์์ ๋ถ์ธ๋ค |
| OC โ ๋ฏธ๋์ฐจ | ai/response |
mqttx/ai_chat.go |
transcript = ๊ด๋๊ฐ ๋ฐํ. ๋ฏธ๋์ฐจ๊ฐ ๊ตฌ๋
ํด ์คํ
์์ฑ ์ฌ๋ฃ๋ก ์ด๋ค |
| ๋ฏธ๋์ฐจ โ ํ๋ธ๋ฆฟ |
ai/response {ok, context_id, message} ยท ai/suggestions
|
(OC ์ฝ๋ ์์) | ๋ฏธ๋์ฐจ๊ฐ ์ง์ ๋ฐํํ๋ค(2์ฅ). OC ๋ ๊ตฌ๋ ํ์ง ์๋ ํ ํฝ์ด๋ผ ์ํฅ ์์ |
| ๋ฏธ๋์ฐจ โ OC |
ai/mode (retained) |
์ ๊ท โ 3์ฅ |
{"mode":"car","ttl_sec":1800} / {"mode":"chat"} ๋ฅผ ์ด๋ฏธ ๋ฐํ ์ค(OC ๊ฐ ๊ตฌ๋
ํ๊ธฐ ์ ๊น์ง๋ retained ๋ก ๋จ์ ๋ฟ) |
| ๋ฏธ๋์ฐจ โ OC |
task/complete {task_id}
|
mqttx/task_complete.go |
ํ๋ธ๋ฆฟ๊ณผ ๋์ผ ํ์ด๋ก๋. OC ๊ฐ dashboardยทpoint ์ฌ๋ฐํ |
2. Aโฒ โ ๋ฏธ๋์ฐจ ๋ฌธ์ฅ์ ๋ฏธ๋์ฐจ๊ฐ ai/response ์ ์ง์ ์ด๋ค (OC ์์
์์, ๋์๋ง)
๋ฏธ๋์ฐจ๊ฐ ํ๋ธ๋ฆฟ ์ฑํ
์ ๋์ฐ๋ ๋ฌธ์ฅ("์ถ๋ฐ ํ ๊น์?", ๊ฐ step ์ ์ง๋ฌธ)์ LLM ๋ต๋ณ์ด ์๋๋ผ ์ ํด์ง ๋ฌธ์ฅ์ด๋ค.
OC ๋ฅผ ๊ฑฐ์น๋ฉด LLM ์ด ๋ฌธ์ฅ์ ๋ฐ๊พธ๊ณ , ์ง์๋ฌธ์ด ๋ํ ๊ธฐ๋ก์ ๋จ๊ณ , ์ธ์
๋น 1๊ฑด ๋ฝ์ ์ฐ๋ฆฌ ์์ฒญ์ด ์กฐ์ฉํ ๋๋กญ๋๋ค.
๊ทธ๋์ ๋ฏธ๋์ฐจ๊ฐ orchestration/{sid}/ai/response ์ ์ง์ ๋ฐํํ๋ค(๋ธ๋ก์ปค ACL ์ future-car ๊ณ์ ์ orchestration/# ์ฐ๊ธฐ ๊ฐ๋ฅ).
| ๋ฏธ๋์ฐจ๊ฐ ๋ฐํ | ํ์ด๋ก๋ | ํ๋ธ๋ฆฟ ๋์ |
|---|---|---|
ai/response |
{ "ok": true, "context_id": "<๋ฏธ๋์ฐจ ์์ฑ>", "message": "์ถ๋ฐ ํ ๊น์?" } |
chat-store.receiveAiResponse ๊ฐ ๋ชจ๋ฅด๋ context_id ์ message ๋ฅผ AI ๋งํ์ ์ผ๋ก ์ถ๊ฐ โ ์ด๋ฏธ ๊ทธ๋ ๊ฒ ๋์
|
ai/suggestions |
{ "context_id": "<๊ฐ์ ๊ฐ>", "suggestions": ["๋ค, ์ถ๋ฐํด์!", "์ ๊น๋ง์"] } |
๋ต ํ๋ณด ์นฉ ํ์ โ ์ด๋ฏธ ๊ทธ๋ ๊ฒ ๋์. ํญํ๋ฉด ai/request type=text ๋ก ๋๊ฐ transcript ๊ฐ ๋๋ค |
OC ๊ฐ ํ ์ผ์ ์๋ค. ์ด ๋ ํ ํฝ์ OC ๊ฐ ๊ตฌ๋
ํ์ง ์๋๋ค. ๋ค๋ง ๊ณ์ฝ์ OC ๊ฐ ๋ฐํ์์ธ ํ ํฝ์ ๋ฏธ๋์ฐจ๊ฐ ๊ฐ์ด ์ฐ๋
๊ฒ์ด๋ฏ๋ก ๋์๊ฐ ํ์ํ๋ค(AsyncAPI ๋ฌธ์ยท๋ชจ๋ํฐ๋ง ๊ด์ ). ๊ฑฐ์ ํ๋ฉด ๋์์ผ๋ก ai/request ์ passthrough: true
(LLMยทํ์คํ ๋ฆฌยทsuggestions ์๋ตํ๊ณ text ๋ฅผ ๊ทธ๋๋ก message ๋ก)๋ฅผ ๊ตฌํํด ๋ฌ๋ผ๊ณ ์์ฒญํ๋ค โ ๋ฏธ๋์ฐจ๋ env ํ ์ค
(OC_CHAT_PUBLISH=request)๋ก ๊ทธ ๊ฒฝ๋ก๋ก ๋์๊ฐ๋ค.
๋ฏธ๋์ฐจ๊ฐ ์ง์ ์ฐ๋ ๋ฉ์์ง๋ OC ์ ai_messages ํ์คํ ๋ฆฌ์ ๋จ์ง ์๋๋ค. ์ฌ์ ๋ค ์ผ๋ฐ ์ฑํ
์์ "์ฐจ๊ฐ ํ ๋ง"์
๋งฅ๋ฝ์ผ๋ก ์ฐ๊ณ ์ถ๋ค๋ฉด OC ๊ฐ ai/response ๋ฅผ ๊ตฌ๋
ํด assistant ํด์ผ๋ก ์ ์ฅํ๋ ๋ฐฉ๋ฒ์ด ์๋ค โ ์ ํ.
| ํญ๋ชฉ | ๊ฐ |
|---|---|
| ํ ํฝ | orchestration/{sessionId}/ai/mode |
| ๋ฐํ | ๋ฏธ๋์ฐจ (enter ์งํ / exit ์งํ) |
| retain / QoS | true / 1 |
| ํ์ด๋ก๋ |
{"mode":"car","ttl_sec":1800} ๋๋ {"mode":"chat"}
|
topics.go ์ ์ถ๊ฐ:
// AiModeTopic โ SUBSCRIBE orchestration/{sessionId}/ai/mode (retained, ๋ฏธ๋์ฐจ ๋ฐํ)
const AiModeTopic = "orchestration/+/ai/mode"
// AiModeTopicFmt โ PUBLISH(์ญ์ ์ฉ) orchestration/{sessionId}/ai/mode
const AiModeTopicFmt = "orchestration/%s/ai/mode"์ธ์
๋ณ (mode, expiresAt). ๊ธฐ์กด SessionStore ์ ํ๋๋ฅผ ์น์ด๋ ๋์ง๋ง, ๋ฏธ๋์ฐจ ์ฌ์ ์ ์ธ์
๋ณด๋ค
์งง๊ณ TTL ์ด ๋ณ๊ฐ๋ผ ๋ถ๋ฆฌ๋ฅผ ๊ถํ๋ค(TimedLock ๊ณผ ๊ฐ์ ๊ฒฐ์ธ ์์ ๋งต).
type AiModeStore struct {
mu sync.RWMutex
entries map[string]aiModeEntry // sessionID โ {mode, expiresAt}
}
func (s *AiModeStore) IsCar(sessionID string) bool // mode=="car" && time.Now() < expiresAt
func (s *AiModeStore) Set(sessionID, mode string, ttl time.Duration)
func (s *AiModeStore) Clear(sessionID string)
func RegisterAiModeHandler(svc *Service, store *AiModeStore) error {
return svc.Subscribe(AiModeTopic, 1, func(topic string, payload []byte) {
// ๋น payload = retained ์ญ์ โ Clear
// {"mode":"car","ttl_sec":N} โ Set(sid, "car", N์ด; 0/๋๋ฝ์ด๋ฉด ๊ธฐ๋ณธ 30๋ถ, ์ํ 2์๊ฐ)
// {"mode":"chat"} โ Clear
// ๋ชจ๋ฅด๋ mode ๋ ๋ฌด์ + ๋ก๊ทธ
})
}server.go ์ ํธ๋ค๋ฌ ๋ฑ๋ก ์ค์ RegisterAiModeHandler(mqttService, aiModeStore) ์ถ๊ฐ.
RegisterAiChatHandler ์ aiModeStore ์ธ์๋ฅผ ๋๊ธด๋ค.
if aiModeStore.IsCar(sessionID) {
// ๊ด๋๊ฐ ๋ฐํ: STT ๋ง ํ๊ณ LLM ๋ต๋ณยทsuggestions ๋ ์๋ตํ๋ค.
var transcript string
switch req.Type {
case "voice":
transcript = sttOnly(...) // ๊ธฐ์กด voice ๊ฒฝ๋ก์์ STT ๊น์ง๋ง (ChatWithAudio ์ ์ ์ฌ ๋ถ๋ถ)
case "text":
transcript = req.Text
}
// user ํด์ ์ ์ฅํ๋ค(์ฌ์ ๋ค ์ผ๋ฐ ์ฑํ
ยท์์ฝ์์ ๋งฅ๋ฝ์ผ๋ก ์ฐ์ผ ์ ์์) โ 6์ฅ ์ด๋ฆฐ ์ง๋ฌธ
_ = queries.InsertAiMessage(ctx, ...Role:"user", Transcript: transcript, Content: transcript)
resp := AiChatResponse{OK: true, ContextID: req.ContextID, Transcript: transcript, Message: ""}
publish(resp)
return
}llmClient.ChatWithAudio ํ ํธ์ถ์ ๋ฌถ์ฌ ์๋ค
(ai_chat.go voice case). car ๋ชจ๋์์๋ ์ ์ฌ๋ง ํ์ํ๋ฏ๋ก ์ ์ฌ๋ง ํ๋ ๊ฒฝ๋ก๊ฐ ํ์ํ๋ค
โ ChatWithAudio ์ "๋ต๋ณ ์๋ต" ์ต์
์ ๋๊ฑฐ๋, STT ์ ์ฉ ํจ์๋ฅผ ๋ถ๋ฆฌํ๋ค. ์ด๊ฒ ์ด ๋ณ๊ฒฝ์์
๊ฐ์ฅ ํฐ ์์
์ด๋ค.
| ๊ฒฝ๋ก | ์ด๋์ | ๋ฌด์์ |
|---|---|---|
| TTL | AiModeStore.IsCar |
expiresAt ์ง๋๋ฉด false (๋ณ๋ ์ ๋ฆฌ ์ค๋ ๋ ๋ถํ์) |
exit |
๋ฏธ๋์ฐจ๊ฐ {"mode":"chat"} ๋ฐํ |
ํธ๋ค๋ฌ๊ฐ Clear
|
signout |
end_session.go RegisterEndSessionHandler ์ store.DeleteByUUID ์งํ |
aiModeStore.Clear(sid) + retained ์ญ์ : svc.Publish(fmt.Sprintf(AiModeTopicFmt, sid), []byte{}, 1, true) (me ๋ฅผ ์ง์ฐ๋ ๊ฒ๊ณผ ๊ฐ์ ๋ฐฉ์) |
์
๋ ฅ ai/request
|
์ถ๋ ฅ ai/response
|
LLM | ํ์คํ ๋ฆฌ | suggestions |
|---|---|---|---|---|
ํ๋ธ๋ฆฟ type=voice
|
{ok, context_id, transcript} (message:"") |
โ | user ํด | โ |
ํ๋ธ๋ฆฟ type=text
|
{ok, context_id, transcript:text} (message:"") |
โ | user ํด | โ |
| chat ๋ชจ๋(๊ธฐ๋ณธ) | ์ง๊ธ๊ณผ ๋์ผ | โ | user+assistant | โ |
-
ai/mode์์ด ํ๋ธ๋ฆฟ ํ ์คํธ โ ์ง๊ธ๊ณผ ๋์ผ(ํ๊ท ์์) -
{"mode":"car"}๋ฐํ โ ํ๋ธ๋ฆฟ ํ ์คํธ โmessage:"",ai/suggestions๋ฏธ๋ฐํ - car ๋ชจ๋์์ ๋ฏธ๋์ฐจ๊ฐ ์ง์ ์ด
ai/response๊ฐ ํ๋ธ๋ฆฟ์ ๊ทธ๋๋ก ๋ณด์(OC ๋ฌด๊ด) -
{"mode":"chat"}โ 2 ์ ์์ฒญ์ด ๋ค์ LLM ๋ต๋ณ์ ๋ฐ์ -
ttl_sec:5๋ก car โ 6์ด ๋ค ํ๋ธ๋ฆฟ ํ ์คํธ โ LLM ๋ต๋ณ ๋ฐ์(TTL) - car ๋ชจ๋ ์ค
signoutโ retainedai/mode๊ฐ ๋น์ด ์์(MQTTX ๋ก ์ฌ๊ตฌ๋ ํด ํ์ธ) - OC ์ฌ์์ โ retained
ai/mode๊ฐ ๋ค์ ๋ค์ด์ ๋ชจ๋ ๋ณต์
| ๋ณ๊ฒฝ | ํ์ผ | ํฌ๊ธฐ |
|---|---|---|
| Aโฒ ์ง์ ๋ฐํ | (OC ์์) | ๋์๋ง. ๋์ passthrough ๋ฉด ai_chat.go ๋ถ๊ธฐ ~20์ค |
| B ํ ํฝยท์ํ |
topics.go, ai_mode.go(์ ๊ท), server.go
|
~80์ค |
| B car ๋ถ๊ธฐ | ai_chat.go |
~30์ค + STT ์ ์ฉ ๊ฒฝ๋ก ๋ถ๋ฆฌ(voice) |
| B ํด์ | end_session.go |
2์ค |
- car ๋ชจ๋์์ ๊ด๋๊ฐ user ํด์ ํ์คํ ๋ฆฌ์ ์ ์ฅํ ์ง โ ์ ์ฅ (๋ฆฌํ๋ ์ ยท์์ฝ ๋งฅ๋ฝ)
- ๋ฏธ๋์ฐจ๊ฐ ์ง์ ์ด ๋ฌธ์ฅ์ OC ๊ฐ ๊ตฌ๋ ํด assistant ํด์ผ๋ก ์ ์ฅํ ์ง โ ์ ํ(ํ๋ฉด ์ฌ์ ๋ค ์ฑํ ์ด "์ฐจ๊ฐ ํ ๋ง"์ ์)
-
ttl_sec๊ธฐ๋ณธ๊ฐ/์ํ โ 30๋ถ / 2์๊ฐ - ์ฌ์ ์ค ๊ด๋๊ฐ์ ์ผ๋ฐ ์ง๋ฌธ์ ๋ต์ด ์์ด๋ ๋๋๊ฐ โ ์ฐ์ถ ์๋๋ก ํ์ธ ํ์ (์ด ์คํ์ ์ ์ )