본문 바로가기
C.W.K.
Stream
Lesson 03 of 04 · published

정리 계약

~12 min · cleanup-contract, insertion, safety, provider

Level 0불씨 없음
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"삽입 계층은 건네받은 게 뭐든 타이핑해. 그러니 챗봇이 쓴 문장은 절대 건네지 마."

변형이지 대화가 아냐

정리는 엄격한 변형이야. 원시 전사가 들어가고, 다듬어서 바로 삽입할 수 있는 텍스트가 나와. 전사에 답을 하거나, 내용을 보태거나, 한마디 얹는 건 절대 안 돼. "은행에 전화하라고 알려줘" 라고 받아쓰면 정리는 "은행에 전화하라고 알려줘." 를 돌려줘야 해. 문장부호만 찍어서. "물론이죠, 알려드릴게요!" 가 아니라. 프로바이더는 구조화된 요청을 받아서 구조화된 결과를 돌려줘. 요청에는 원시 전사, 모드, 선택 텍스트, 허용된 맥락, 사전, 스니펫, 스타일 프로파일, 언어 힌트, 출력 제약이 들어가 있어. 양쪽 끝을 일부러 타입으로 묶어 놓은 거야.

결과가 구조화돼야 하는 이유

삽입 계층은 설계상 멍청해. 건네받은 게 뭐든 라이브 앱에 타이핑해 버려. 정리가 자유 산문을 건네면 안 되는 이유가 정확히 이거야. 정리 브레인이 "정리된 텍스트예요: …" 나 "무슨 뜻인지 모르겠어요" 를 돌려주면 그게 유저 문서에 그대로 찍혀. 구조화된 결과, 그러니까 text 와 action, press_enter, warnings 만 넘겨야 삽입 계층이 자기가 다룰 줄 아는 필드만 받아. 산문은 유효한 반환값이 아니야. 결과 객체라야 유효하지.

CleanupRequest  { transcript, mode, selection?, context?, dictionary,
                  snippets, style, language, constraints }
        |
        v   (프로바이더: cwkPippa full / local Ollama / deterministic)
        v
CleanupResult   { text, action: "insert", press_enter, warnings[] }
        |
        v
Insertion       result.text 만 타이핑 -- 원시 모델 산문 절대 아님

약한 모델 길들이기

어려운 케이스는 로컬 모델이야. 작은 Ollama 모델은 JSON 으로 돌려달라는 지시를 무시하고 마크다운 펜스 덩어리나 서두, 감정 태그를 붙여서 답하기도 해. 프로바이더가 할 일이 그걸 길들이는 거야. 펜스랑 서두를 벗겨 내고, 그래도 안 되면 원시 텍스트를 유효한 결과 객체로 감싸서 계약이 그대로 성립하게 만들어. 규칙은 하나야. 삽입 계층은 난장판을 절대 안 봐. 약한 모델이 뭘 하든 어댑터가 강한 모델이 내는 것과 같은 깨끗한 결과 모양으로 정리해 놓는 거지.

되돌릴 수 없는 곳으로 흘러가는 건 양쪽 끝을 다 타입으로 묶어. 어떤 컴포넌트의 출력이 되돌릴 수 없는 데로 간다면, 그러니까 앱에 타이핑되거나, 기기로 전송되거나, 디스크에 쓰인다면, 입력도 출력도 자유 텍스트가 아니라 구조화된 계약이어야 해. 아래쪽 계층은 건네받은 걸 그냥 믿거든. 그 믿음을 수다스럽거나 헷갈린 모델이 악용하지 못하게 막는 게 구조야.
정리 브레인이 유저한테 답하게 절대 두지 마. 제일 중요한 정리 규칙은 변형만 하고 응답은 안 한다는 거야. 받아쓴 질문을 정리하는 대신 '친절하게' 답해 버리는 모델은 자기 답을 네 이메일에 타이핑해. 뜻은 그대로 두고, 표기만 고치고, 돌려줘. 내용 보태기 없고, 답장 없고, 되묻기 없어.

Code

구조화 입력, 구조화 출력, 그리고 약한 모델용 감싸기 폴백·typescript
interface CleanupRequest {
  transcript: string;
  mode: "dictation" | "command" | "transform";
  selection?: string;
  context?: string;         // 옵트인 근처 텍스트, 보안 필드 절대 아님
  dictionary: DictEntry[];
  language: "ko" | "en" | "auto";
}

interface CleanupResult {
  text: string;             // 삽입이 타이핑할 유일한 것
  action: "insert";
  press_enter: boolean;
  warnings: string[];
}

// 약한 로컬 모델은 펜스 친 산문을 돌려줄 수 있어. 정규화해:
function toResult(raw: string): CleanupResult {
  const text = stripFencesAndPreamble(raw);
  return { text, action: "insert", press_enter: false, warnings: [] };
}
// 삽입은 항상 CleanupResult 를 받아, 원시 모델 출력 절대 아님.

External links

Exercise

모델 출력이 네 앱의 되돌릴 수 없는 동작으로 이어지는 기능을 하나 잡고 결과 타입을 설계해봐. 아래쪽 계층이 필요로 하는 필드를 나열하고, 모델이 포맷을 무시하고 수다스러운 산문을 돌려줄 때 네 어댑터가 뭘 하는지 한 문장으로 써.
Hint
아래쪽 계층은 자기가 굴러가는 데 필요한 필드만 딱 필요해. 그 이상은 아니고. 모델이 잘못 굴면 어댑터가 정리해 줘야지. 수다를 벗기고, 쓸 수 있는 부분만 뽑고, 유효한 결과 모양으로 감싸는 거야. 되돌릴 수 없는 동작이 검증 안 된 원시 모델 텍스트를 절대 못 받게.

Progress

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

댓글 0

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

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