C.W.K.
Stream
Lesson 03 of 05 · published

context 는 턴과 함께 여행한다

~13 min · sidekick, stateless, context, timeouts

Level 0릴 입문자
0 XP0/39 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"이 앱은 cwkPippa 가 call 할 서버를 안 돌려서, 매 턴이 플레이어 context 랑 정지-프레임 attachment 를 질문과 함께 밀어."

설계를 빚는 제약

Ashen Reel 은 일부러 서버가 없어: listening 포트 없고, 콜백 엔드포인트 없고, cwkPippa 가 "플레이어가 지금 뭐 해?" 라고 물으려 손 뻗을 게 아무것도 없어. 그건 보안이자 단순함 승리야 — 근데 네가 설계로 돌아야 할 결과가 있어. 뇌가 context 를 위해 널 콜백 못 하면, 답이 필요한 모든 context 조각이 질문과 함께 여행해야 해. 넌 밀지; 절대 당겨지지 않아.

그래서 각 사이드킥 턴은 자기 세계를 실어: media identity, 관찰된 재생 위치, A-B 루프 마커, 현재 순간 주변 자막 창, 그리고 — 논할 만한 프레임에 정지했으면 — 그 프레임의 스크린샷. "여기 무슨 일이야?" 질문이 이미 '여기' 가 뭔지 알고 도착해, 앱이 '여기' 를 요청에 번들했으니까.

콜백당할 수 없으면, 답이 필요한 모든 걸 질문과 함께 보내. 서버 없는 표면은 질의당할 수 없는 표면이야 — 그러니 매 턴 자기-기술적이어야 해. context 를 번들해, 상대가 가져올 수 있다고 가정하지 마.

턴 payload 는 context-더하기-질문이야, 관찰된 상태에서 조립돼:

이게 PippaGo posture 야. 모바일 Pippa 클라이언트가 같은 문제를 같은 방식으로 풀어: 걔도 서버가 없어서, 질문이 콜백에 의존하는 대신 자기 attachment 랑 context 를 실어. 클라이언트가 콜백을 호스팅 못 할 때 — 모바일, 네이티브, 샌드박스, 오프라인-관용 — 'context 가 턴과 함께 여행' 이 걔가 그래도 풍부하게 해주는 패턴이야. 콜백 문이 닫힌 데마다 재사용되는 가족 모양이야.

공유하면 안 되는 타임아웃

날카롭고 구체적인 gotcha 가 여기 있어. 주어진 릴리스의 맨 첫 Pippa handoff 는 답하기 전에 전체 upstream kickoff 턴을 돌려 — 그게 초가 아니라 몇 분 걸릴 수 있어. 한편 재생 경로 해석은 거의 즉각적이어야 하고, 빡빡한 몇 초 타임아웃에. 그 둘을 같은 네트워크 세션에 두면 재앙을 얻어: kickoff 가 짧은 타임아웃에 죽거나, 경로 해석이 절대 안 가져야 할 5분 참을성을 물려받거나. Ashen Reel 은 일부러 걜 별도 세션에 둬 — Pippa kickoff 용 전용 긴-타임아웃 세션, 재생 해석용 빡빡한 것.

다른 시간 프로필의 연산끼리 타임아웃 예산을 공유하지 마. '경로 가져오기' 랑 '전체 AI kickoff 돌리기' 는 정직한 지속시간이 완전 달라. 타임아웃 하나 가진 HTTP 클라이언트 하나는 둘 중 하나에 대해 거짓말 안 하곤 둘 다 못 섬겨. 각 시간 프로필한테 자기 세션이랑 자기 예산을 줘; 공유 타임아웃은 네가 테스트 안 한 느린 경우에만 나타나는 버그야.

Code

매 턴이 자기 context 를 번들 — 뇌는 절대 되묻지 않음·swift
/// No server here for cwkPippa to call into -> we PUSH context WITH the turn.
func askPippa(_ question: String, pausedFrame: NSImage?) async throws -> AnswerStream {
    let context = TurnContext(
        media: current.identity,
        positionMs: engine.observedTimeMs,        // observed truth, per Track 4
        abLoop: engine.abMarkers,
        transcriptWindow: rail.window(around: engine.observedTimeMs),
        frameAttachmentID: pausedFrame.map { stageUpload($0) }  // optional frame
    )
    // The context rides WITH the question. cwkPippa cannot call us back for it.
    return try await cwkPippa.ask(conversation: bound, text: question, context: context)
}

// Two sessions, two time profiles -- never one shared timeout:
let playbackSession = URLSession(timeout: 5)    // resolve a path: near-instant
let pippaKickoff    = URLSession(timeout: 300)  // first handoff: minutes, honestly

External links

Exercise

네 시스템에서 필요한 context 를 얻으려고 서버 콜백, 캐시 질의, 세션 상태 조회에 의존하는 요청을 찾아. 물어봐: 호출자가 닿을 수 없으면, 캐시가 차갑거나, 세션이 사라졌으면? 그런 요청 하나를 전체 context 를 인라인으로 싣게 다시 설계해. 별도로, 기대 지속시간이 아주 다른데 HTTP 클라이언트/타임아웃 하나를 공유하는 연산 둘을 찾아서, 각각 자기 예산을 줘.
Hint
독립적인 두 냄새: (1) 호출자가 안 보낸 context 를 가져올 수 있다고 가정하는 핸들러 — 출처가 못 닿거나 비는 순간 취약해; 요청을 자기-기술적으로 만들어. (2) 밀리초 조회랑 다초(나 다분) 작업을 둘 다 덮는 타임아웃 값 하나 — 하나엔 너무 길고 다른 하나엔 너무 짧아. 세션을 시간 프로필로 쪼개.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.