여행 개요(travel brief)는 조건이 중요하기 때문에 간단해 보일 수 있습니다.
다음 가상 예시를 고려해 봅시다. 고객 워크숍이 2026년 11월 12일과 13일에 계획되었습니다. 여행자는 11월 11일에 SFO에서 JFK로 이동하고, 이코노미 항공편을 이용하며, 11월 14일 또는 가능하다면 11월 15일에 돌아올 예정입니다. 가족과 함께 숙박하므로 호텔은 필요하지 않습니다. 또한, 고객이 금요일까지 확인해야만 여행이 진행되어야 합니다.
요약본(summary)은 도시와 워크숍 날짜는 유지할 수 있지만, 이러한 조건이나 불확실한 복귀 날짜를 잃을 수 있습니다. 만약 애플리케이션이 그 요약본을 사용하여 양식을 채운다면, 읽기 보조 도구에서의 누락이 제안된 확정 사항으로 변질될 수 있습니다.
이는 제가 관찰한 모델 결과가 아니라 테스트해야 할 실패 시나리오입니다. 이는 저의 공개 React 데모인 Trip Workbench에서 중요한 선택을 설명해 줍니다. 바로 Chrome의 온디바이스(on-device) Summarizer API와 클라우드 기반 Gemini 제안을 결합할 때 원래 개요를 보존하는 것입니다.
이 두 기능은 서로 다른 역할을 합니다. 하나는 사람이 읽도록 돕고, 다른 하나는 특정 필드의 변경 사항을 제안합니다. 둘 다 자신들을 생성한 출처 및 편집 컨텍스트와 연결되어 있어야 합니다.
각 AI 액션에 명확한 계약(contract) 부여하기
편집기는 로컬 요약본과 별도의 Gemini 제안을 제공합니다. 이 요약본은 개요를 대체하지 않으며, 제안 요청 시 원래의 개요가 애플리케이션 백엔드로 전송됩니다.
이 워크플로우는 세 가지 뚜렷한 액션으로 구성되어 있습니다:
이 기기에서 요약하기(Summarize on this device):
- 입력: 원본 개요(Original brief)
- 실행: Chrome Summarizer
- 결과: 사람이 검토할 텍스트
여행 필드 제안하기(Suggest trip fields):
- 입력: 원본 개요 및 승인된 저장 필드(authorized saved fields)
- 실행: Gemini를 호출하는 백엔드(Backend calling Gemini)
- 결과: 후보 패치(Candidate patch) 및 명확화 질문
제안된 변경 사항 적용하기(Apply proposed changes):
- 입력: 검토된 패치 및 예상 수정본(expected revision)
- 실행: 기존 백엔드 뮤테이션 경로(Existing backend mutation path)
- 결과: 업데이트된 저장 초안(Updated saved draft)
이러한 분리는 데이터 이동을 이해하기 쉽게 만듭니다. 로컬 요약 기능을 선택하는 것이 클라우드 요청을 암묵적으로 승인하지 않습니다. Gemini를 선택한다고 해서 결과로 발생하는 변경 사항까지 암묵적으로 수락하는 것은 아닙니다.
또한 클라우드 기능이 로컬 모델의 가용성에 의존하는 것을 방지합니다. 사용자는 요약본을 먼저 생성하지 않고도 제안(proposal)을 요청할 수 있습니다.
제안 경로에는 실질적인 세부 사항이 있습니다. 백엔드는 접근 권한을 확인한 후 저장된 초안(draft)을 읽습니다. 브라우저의 저장되지 않은 폼 버퍼는 받지 않습니다.
이는 제안이 사용자가 현재 보고 있는 필드와 다른 필드를 기반으로 할 수 있음을 의미합니다. 코디네이터는 로컬 편집 내용(local edits)이 '더티(dirty)' 상태인 동안 에이전트 패치(agent patch)를 적용하는 것을 차단합니다. 저는 이 제약 조건을 사용자가 제안을 요청하기 전에 해당 편집 내용을 저장하거나 폐기하도록 요구함으로써 더 일찍 눈에 띄게 만들 것입니다.
원본 브리프(original brief)는 출처 자료로서 권위적이지만, 여전히 신뢰할 수 없는 입력값입니다. 이를 그대로 유지한다고 해서 그 안에 포함된 지침이 애플리케이션의 규칙을 우회할 권한을 갖게 되는 것은 아닙니다.
Gemini에게 제한적인 제안 요청하기 (Ask Gemini for a bounded proposal)
Gemini 어댑터는 원본 브리프, 저장된 필드, 지원되는 공항 및 객실 정보, 그리고 참조 날짜와 시간대를 받습니다. 서버가 API 자격 증명(credential)을 소유합니다.
요청된 출력은 두 부분으로 구성됩니다: 편집 가능한 여행 필드에 대한 패치(patch)와 명확화 질문 목록입니다. 유용한 응답은 부분적일 수 있습니다. 조작된 목적지나 날짜보다는 좋은 질문과 함께 빈 패치가 더 바람직합니다.
합성 브리프(synthetic brief)의 경우, 이것은 캡처된 Gemini 출력이 아니라 예시적인 응답 형태입니다:
{
"patch": {
"origin": "SFO",
...
}
생략(Omission)과 지우기(Clearing)는 다른 의미를 가집니다. 생략된 필드는 기존 값을 보존합니다. 허용된 null은 선택적 필드(nullable field)를 명시적으로 비웁니다. 인터페이스는 저장하기 전에 이 구분을 눈에 띄게 해야 합니다.
고객 확인 조건은 또한 폼의 한계를 드러냅니다. 날짜, 공항 및 좌석에 대한 스키마는 자신이 표현하지 못하는 조건을 강제할 수 없습니다. 원래의 개요(brief)와 명확화 영역은 검토 과정 동안 유지되어야 하며; 실제 예약 워크플로우는 해당 전제 조건(prerequisite)을 처리하기 위한 명시적인 방법이 필요합니다.
고정된 커밋에서는 어댑터가 responseMimeType: "application/json"을 가진 JSON을 요청한 다음, 이를 구문 분석하고 런타임 및 도메인 검증을 적용합니다. Gemini에게 출력 스키마를 전송하지는 않습니다.
제가 다음에 변경할 부분은 경계가 지정된 응답 스키마(bounded response schema)도 제공하는 것입니다. Gemini의 structured-output 문서에는 JSON Schema의 일부가 지원되며 반환되는 값에 대한 애플리케이션 검증을 명시적으로 요구합니다. 현재 예시는 Interactions API를 사용하고; 데모는 Generate Content API를 사용합니다. 마이그레이션은 요청 형태를 혼합하기보다는 선택된 API의 구성을 사용해야 합니다.
애플리케이션 계약(application contract)은 편집 가능한 필드, 지원되는 열거형 값(enum values), 경계가 지정된 문자열(bounded strings), 그리고 소수의 명확화 질문만을 허용해야 합니다. 저는 외부 응답 및 명확화 객체를 포함하여 모든 객체를 엄격하게 만들 것입니다. 기존의 패치 객체는 이미 알 수 없는 키를 거부합니다.
그런 다음 제안된 병합을 도메인 규칙에 따라 검증해야 합니다. 날짜 형태의 문자열은 불가능한 날짜를 지정할 수 있습니다. 개별적으로 유효한 두 날짜가 무효한 여행을 구성할 수 있습니다. 지원되는 공항이라 할지라도 개요에 대한 잘못된 해석일 수 있습니다.
마지막으로, 권한 부여(authorization), 개정 검사(revision checks), 그리고 영속성(persistence)은 백엔드의 일반적인 업데이트 경로에 유지해야 합니다. 스키마는 허용 가능한 데이터를 설명할 뿐이며; 사람과 애플리케이션이 그것을 사용할지 여부를 결정합니다.
생성된 출력을 편집 컨텍스트에 연결하기
원래의 개요를 보존하는 것만으로는 불충분하며, 이전 개요에서 나온 결과가 더 새로운 결과 옆에 나타난다면 더욱 그렇습니다.
Gemini가 revision 12의 저장된 초안(saved draft)으로 시작한다고 가정해 봅시다. Gemini가 실행되는 동안, 사용자가 로컬에서 목적지를 변경하거나 “return November 14”를 “return November 15”로 편집합니다. 이 응답은 이전 입력에 대해서는 완벽하게 합리적일 수 있지만, 현재 화면의 내용과는 다를 수 있습니다.
코디네이터(coordinator)는 각각 다른 질문에 답하기 때문에 여러 식별자(identity)를 유지합니다:
Draft ID 및 editor/session generation: 확립하는 것: 어떤 편집 컨텍스트가 결과물을 소유하는지
Saved revision: 확립하는 것: 어떤 영속화된 필드(persisted fields)가 사용되었는지
Local edit version: 확립하는 것: 폼 버퍼(form buffer)가 변경되었는지 여부
Brief version: 확립하는 것: 소스 텍스트가 변경되었는지 여부
백엔드(backend)는 리비전 및 버전 바인딩(revision and version bindings)을 응답 엔벨로프(response envelope)에 첨부합니다. 이것들은 모델이 만들어낸 값이 아니라 애플리케이션 메타데이터(application metadata)입니다.
요청이 완료된 후, 코디네이터는 변경된 초안이나 편집 세션(editing session)을 거부합니다. 에디터는 버전 바인딩이 더 이상 일치하지 않을 때 제안을 만료(stale)로 표시합니다. 사용자가 '적용(Apply)' 버튼을 누를 때 신선도(freshness)를 다시 확인합니다.
두 가지 확인 모두 중요합니다. 응답은 올바른 컨텍스트에서 도착할 수 있지만, 사용자가 검토하는 동안 만료될 수 있습니다.
로컬 요약(Local summaries) 역시 실제 입력에 범위가 지정된 유사한 규칙이 필요합니다. 브리프(brief)만을 기반으로 하는 요약은 브리프 버전과 편집 식별자(editing identity)를 필요로 하며, 단순히 관련 없는 폼 필드가 변경되었다는 이유만으로 만료될 필요는 없습니다.
React 확장(extension)의 경우, 저는 요약 상태(summary state)를 세션 생성(session generation), 에디터 생성(editor generation), Draft ID, 그리고 Brief Version을 키로 지정한 전용 자식 컴포넌트(dedicated child)에 배치할 것입니다. 이 키가 변경되면 완료된 출력이 초기화되고 보류 중인 작업에 대한 정리(cleanup)가 실행됩니다. 브리프 텍스트 영역은 부모 컴포넌트에 남아있어 타이핑 시 반복적으로 재마운트(remount)되는 것을 방지합니다.
해당 자식 컴포넌트 내에서도 각 요청은 자체적인 식별자가 필요합니다. 더 새로운 요청은 둘 다 동일한 브리프를 사용하더라도 이전 요청을 대체해야 합니다(supersede).
취소 기능은 지원될 경우 작업을 저장합니다. 요청 식별자와 소스 컨텍스트(source context)를 비교하는 것이 결과가 여전히 나타날 수 있는지 여부를 결정합니다. 사용자 인터페이스(UI)는 둘 다 필요합니다.
로컬 모델에 완전한 생명 주기 부여하기
Chrome의 Summarizer API는 지원되는 구성 및 리소스 요구 사항을 전제로 기기에서 모델을 실행합니다. 초기 모델 다운로드에는 네트워크 연결이 필요하지만, 이후 모델 사용 시 입력 데이터가 Google이나 제3자에게 전송되지는 않습니다.
이는 이 API의 모델 작동 방식을 설명합니다. 동일한 애플리케이션이라도 사용자가 Gemini를 선택할 때는 원래의 간략한 내용을 백엔드로 전송합니다. 애플리케이션 텔레메트리(telemetry) 및 기타 요청은 별도의 데이터 검토가 필요합니다.
로컬 기능은 '지원됨(supported)'과 '미지원(unsupported)' 외에도 더 많은 상태를 가집니다. 사용 가능 여부는 available, downloadable, downloading, 또는 unavailable로 보고될 수 있습니다. 기능 감지 및 사용 가능성 검사는 생성 시와 동일한 옵션을 사용해야 합니다.
준비성 프로브(readiness probe)는 클릭 전에 실행될 수 있습니다. 생성(Creation)은 명시적인 사용자 액션에 속합니다: Chrome의 시작 가이드는 다운로드가 필요한 경우 사용자 상호 작용을 요구합니다. UI는 준비, 다운로드 진행 상황, 생성, 그리고 취소 과정을 설명해야 합니다.
기존 어댑터는 간결합니다. 제어된 프로브(controlled probes)에서 사용 가능성 거부(availability rejection)가 오류 처리 로직을 벗어났고, 이미 취소된 요청이 여전히 세션을 생성했습니다. 이의 생성 호출은 중단 신호(abort signal)를 받지 못했습니다.
준비성 프로브는 사용 가능성 오류를 포착하고 복구 가능한 상태를 노출해야 합니다. 아래의 후보 생성 함수가 생성, 취소 및 정리 작업을 처리합니다. 이는 고정된 리포지토리(pinned repository)에 대한 확장입니다.
const options = {
type: "key-points",
format: "plain-text",
...
Summarizer.create()에 신호 전달하기
신호를 전달함으로써 취소(cancellation)가 보류 중인 생성 작업까지 도달할 수 있게 합니다. 생성 후 다시 확인하는 것은 구현체 또는 테스트 더블(test double)이 취소에도 불구하고 완료될 경우, 늦은 출력이 받아들여지는 것을 방지합니다.
React 호출자는 컨트롤러를 유지하고, 취소되거나 언마운트(unmount)될 때 이를 중단시키며, 대체된 요청으로부터의 콜백을 무시하고, 실패를 눈에 보이는 상태로 변환해야 합니다. 어댑터만으로는 그러한 UI 보장을 확립할 수 없습니다.
결과는 React를 통해 렌더링된 텍스트로 유지하고, 원본도 보이게 해야 합니다. 입력 내용이 모델의 할당량(quota)을 초과하는 경우, summarize()는 QuotaExceededError를 발생시킬 수 있습니다. 이 경우 사용자에게 소스 내용을 단순히 잘라내는 대신 더 작은 부분을 선택하도록 요청해야 합니다.
로컬 요약 기능이 사용 불가능하거나 실패할 때에도 브리프(brief)를 읽고 편집하는 것은 여전히 유용합니다. 클라우드 폴백은 전송이 발생하기 전에 그 과정을 설명하는 별도의 작업이어야 합니다.
검토 과정에서 실제로 변경될 내용을 보여주기
검토 단계는 관련 비교 사항과 결과(consequence)를 노출할 때만 유용합니다.
현재의 제안 패널은 각 제안된 필드와 값을 보여줍니다. 여기에 현재 값도 나란히 추가하고, 명시적인 삭제가 읽기 가능하도록 만들며, 질문에 대한 설명(clarification questions)을 제안 옆에 유지해야 합니다.
합성 예시의 경우, 사용자는 양식을 검색할 필요 없이 해결되지 않은 반환 날짜를 볼 수 있어야 합니다. 또한 고객 확인에 관한 원본 문장과 모든 요약 내용을 비교할 수 있어야 합니다.
“제안된 변경 사항 적용”은 기존의 뮤테이션 경로(mutation path)를 통해 즉시 저장됩니다. 단순히 저장되지 않은 미리보기를 채우는 것이 아닙니다. 인터페이스가 그렇게 명시해야 합니다. 제출(Submission)은 별도의 작업으로 남아 있어야 하며, 패치(patch)를 적용하는 것이 여행을 구매하는 것은 아닙니다.
이러한 세부 사항들은 검토 과정을 구체적인 결정과 연결합니다: 이 브리프를 기반으로 이 초안에 변경 사항을 저장한다. 일반적인 확인 대화 상자는 사용자에게 정보가 부족할 것입니다.
백엔드(backend)는 여전히 저장 시점에 접근 권한과 예상되는 개정판(revision)을 확인해야 합니다. UI의 최신성 검사 이후에도 다른 작가가 초안을 변경할 수 있습니다. 충돌이 발생하면 새로운 저장 시도를 하기 전에 사용자에게 현재 검토 가능한 상태로 되돌려야 합니다.
전체 상호작용 측정
후보 로컬 어댑터(candidate local adapter)의 경우, 주입된 가짜 요약기(fake Summarizer)를 사용하여 8가지 제어된 검사(controlled checks)가 통과했습니다. 이 검사들은 사용 가능 상태 및 오류, 생성 전후 취소, 호출 경로에서 준비 상태 검사(readiness probe)를 기다리지 않는 생성, 진행 상황 및 입력 전달, 지연 출력 거부(late-output rejection), 그리고 생성 실패 후 정리 작업 등을 다룹니다.
이러한 검사들은 특정 생명주기 동작(lifecycle behavior)을 확립합니다. 하지만 요약 품질 자체는 확립하지 못합니다. 이 워크스루에서는 React 확장 기능이 마운트되거나 타입 검사되지 않았으며, 네이티브 모델 동작, 네트워크 페이로드, 성능은 검증되지 않았습니다.
Gemini 고정값 모드(fixture mode) 역시 유사하게 제한된 목적을 가집니다. 이는 라이브 모델 없이 애플리케이션 흐름을 연습할 수 있도록 미리 준비된 제안(canned proposals)을 반환하는 역할을 합니다. 따라서 추출 정확도나 유용한 명확화 동작(useful clarification behavior)을 시연할 수는 없습니다.
저는 다음의 소규모 브리프를 사용하여 결합된 워크플로우를 평가할 것입니다:
완전하고 모호하지 않은 경우:
- 평가 항목: 정확한 필드와 불필요한 질문 없음
모호한 도시 또는 상대적 날짜의 경우:
- 평가 항목: 추측 대신 유용한 명확화 제공
기존 여행에 대한 부분 업데이트의 경우:
- 평가 항목: 언급되지 않은 필드 보존
불확실한 복귀일이 포함된 조건부 여행의 경우:
- 평가 항목: 검토 과정에서 조건과 불확실성 유지