resumate 16. 지원 동기 문장 설계와 후처리 필터
공고 맞춤 이력서 기능은 항목 순서만 요건에 맞춰 바꾸는 것이었다. 그런데 순서만 바꾼 이력서는 HR이 봐도 다른 회사에 낸 것과 구별되지 않았다.
"왜 이 회사인지"를 이력서 어디에 넣을 것인가
"지원 동기" 섹션을 새로 만들 수는 없었다. 거기서 나오는 문장은 "귀사의 혁신적인 기업 문화에 공감하여"이고, 이건 관심의 신호가 아니라 AI로 썼다는 신호다. 이력서가 검증 가능한 사실만 담는다는 제약도 그대로 지켜야 했다.
그래서 자리를 새로 만들지 않고 SUMMARY(경력 요약) 둘째 문장에 맡기기로 했다. "이 회사가 다루는 문제"와 "내가 이미 해본 일"이 겹치는 지점을 사실로 말하면, 섹션을 늘리지 않고도 지원 동기가 전달된다.
이 결정을 곧바로 화면에 연결하지 않고 프롬프트·파싱·검증만 먼저 순수 모듈로 만들었다. 계약이 잘못되면 화면까지 함께 흔들리니, 검증 가능한 단위로 먼저 세운 뒤 붙이는 편이 안전했다.
문장이 공고를 가리키면 안 된다
처음 세운 규칙은 하나였다. 문장이 공고 문구를 인용하면 안 된다. 공고 문구는 HR이 쓴 말이라, 그걸 옮기면 "당신 공고를 붙여넣었다"로 읽힌다. 실제로 나온 나쁜 예다.
가온컴퍼니가 공고에서 요구한 '대규모 트래픽 처리 경험'이 그동안 풀어온
문제와 같습니다.이 규칙만 걸었을 때는 통과했지만, 여기서 끝나지 않았다.
한쪽을 막으면 모델은 반대쪽 극단으로 옮겨갔다
같은 일이 "왜 이 회사인지" 문장에서 세 번 반복됐다.
공고만 막으면 회사가 통째로 사라진다
"공고를 가리키지 않는다"만 규칙으로 걸었더니, 이번엔 회사 이야기가 아예 없는 문장이 나왔다.
나쁨(회사가 없다) 판매자와 운영자가 같은 주문 자료를 보는 화면을
네 개 만들었습니다.이건 그냥 이력서 항목을 한 줄로 줄인 것이라 어느 회사에 내도 똑같다. 막기만 하고 빈자리에 무엇이 와야 하는지 말하지 않으면, 모델은 남은 규칙(공고를 가리키지 말 것)만 지키느라 회사 이야기 자체를 지워버린다.
그래서 금지를 셋으로 못 박았다. 공고 문구 옮기기, "귀사/비전에 공감", 근거 없는 다짐. 그 밖에는 회사 이야기를 해도 되고, 회사 이름을 써도 된다. 금지 범위를 넓히면 회사가 또 사라진다.
재료를 표로 주면 두 절이 생긴다 — 이어 붙인 문장
"이 회사가 하는 일"과 "내가 해본 것"을 나란히 주면, 모델은 두 칸을 각각 문장으로 바꿔 이었다. 가장 끈질기게 반복된 실패였다.
나쁨 가온컴퍼니는 사용자와 파트너사를 예약으로 연결하고, 저는 광고주·마케터·
관리자가 역할별로 쓰는 4개 웹사이트를 단독 구축했습니다.
→ 주어가 둘. 회사 소개 + 이력서"~와 맞닿아"·"~와 겹쳐"처럼 잇는 말이 낀 문장부터 시작해, 낱말을 하나씩 금지했다. 그랬더니 같은 병이 이름만 바꿔 계속 돌아왔다. 맞닿아 → 겹쳐 → "{회사}는 A하고, 저는 B했습니다". 프롬프트를 여섯 번 고치는 동안 모델은 금지한 낱말은 피했지만 구조는 그대로 유지했다.
flowchart LR
A["잇는 낱말 금지"] --> B["모델이 다른 낱말로 같은 구조 재생산"]
B --> C["새 낱말 다시 금지"]
C --> B
B --> D["낱말이 아니라 구조 문제로 인정"]
D --> E["받는 쪽에서 코드로 거르기"]
이게 낱말 문제가 아니라 구조 문제라는 걸 인정하고 나서야 방향이 바뀌었다. 문장은 처음부터 끝까지 내 경험이어야 하고, 회사가 다루는 대상은 내 문장 안의 낱말로만 등장해야 한다.
좋음 역할이 다른 사용자가 하나의 거래 흐름을 함께 쓰는 화면을 네 개
만들어 왔고, 주문이 결제까지 끊기지 않게 잇는 일도 같은 방식으로
다룰 수 있습니다.그리고 프롬프트 지시만 믿지 않고 받는 쪽에서 한 번 더 걸렀다(isWeldedSentence). 사람이 1초에 알아보는 모양이면 코드도 알아볼 수 있다.
flowchart TD
A["후보 문장"] --> B{"회사 이름(별칭 포함) 뒤에 조사 '은/는'이 붙었나"}
B -->|예| X["버림"]
B -->|아니오| C{"'저는'·'제가'가 있나"}
C -->|예| X
C -->|아니오| D["후보로 남김"]
D --> E{"남은 후보가 2개 미만인가"}
E -->|예| F["저장하지 않고 다시 만들기"]
E -->|아니오| G["후보 목록 저장"]
회사 이름이 조사 '은/는'과 함께 주어 자리에 나오면 버린다("{회사}는 …"). 수식으로 쓰인 "{회사} 운영 도구처럼 …"은 통과시킨다. "저는"·"제가"가 있으면 버린다. 이력서 문장은 원래 주어를 적지 않으니, 그게 있다는 건 앞에 이미 다른 주어(회사)가 섰다는 뜻이다. 거른 뒤 후보가 2개 미만이면 저장하지 않는다. 나쁜 문장을 보여주느니 다시 만드는 게 낫다.
이미 저장해둔 문장은 프롬프트를 고쳐도 바뀌지 않는다는 것도 그 사이에 다시 확인했다. 프롬프트를 전부 고친 뒤에도 지원본에는 옛 규칙으로 만든 "맞닿아" 문장이 계속 실렸다. 고른 문장이 있으면 후보를 새로 뽑지 않기 때문이다. 그래서 이력서에 넣는 자리(chosenAngleBlock)에서 한 번 더 거르게 했다. 프롬프트 규칙은 만들 때와 쓸 때 두 곳에 걸어야 저장된 값도 낫는다.
isWeldedSentence의 회사 이름 매칭도 처음에는 정식 명칭 하나만 봤다. 그런데 실제 저장된 값이 "가온컴퍼니"인데 모델이 "가온에서 …"라고 줄여 쓰는 사례가 나왔고, 정식 명칭 매칭으로는 이걸 통과시켜 버렸다. companyAliases를 만들어 법인 접미사(컴퍼니·주식회사 등)를 뗀 이름과 띄어쓰기로 나뉜 낱말도 각각 후보로 본다. 다만 두 글자 미만은 별칭으로 쓰지 않는다. 한 글자짜리는 아무 문장에나 우연히 걸려 멀쩡한 후보까지 버리게 된다.
회사 쪽 절을 막으면 이력서 항목 요약으로 되돌아간다
같은 병을 반대쪽에서도 겪었다. 회사 쪽 절을 세게 막았더니 문장이 처음부터 끝까지 이력서 항목 요약이 되어, 왜 이 회사인지가 다시 한 글자도 없어졌다. 그래서 준 검사 기준은 "이 문장을 다른 회사 지원서에 그대로 붙여도 말이 되면 실패다"였다. 실패한 문장은 "내가 무엇을 만들었나"로 시작하고, 성공한 문장은 "그 물건이 왜 어려운가"로 시작한다는 차이를 프롬프트에 그대로 적었다.
재료는 화면에서 가르치고, 문장에는 옮기지 않는다
"왜 이 회사인지" 문장이 완성문 하나로만 나오면 사용자는 그걸 그대로 쓰고 끝난다. 다음 회사에 지원할 때는 스스로 쓸 방법을 모른다. 그래서 재료(공고가 원하는 것 / 내가 해본 것 / 이력서에 넣을 문장) 세 줄을 카드로 나란히 보여주기로 했다. 완성문만 주지 않고 어떻게 그 문장이 나왔는지 배치로 가르쳤다. 지원 동기를 어떻게 써야 할지 모르는 사용자가 그 세 줄의 순서에서 배우게 하려는 것이었다.
낱말 하나만 금지하면 같은 뜻의 다른 상투어로 옮겨간다는 것도 이 과정에서 되풀이해 확인했다. 그래서 금지어와 바꿔 쓸 말을 짝지어 표로 준다(ANGLE_TONE). "자연스럽게 쓰라"는 지시만으로는 몇 문장 만에 원래 문투로 돌아간다. 그게 학습 데이터의 기본값이기 때문이다.
재료의 비중은 회사 조사 7 : 공고 요건 3
동기는 "그 회사가 무엇을 하는 곳인가"에서 나오지 "그 자리가 무엇을 요구하는가"에서 나오지 않는다. 요건에 기대면 어느 회사에 내도 말이 되는 문장이 나온다. 그런데 비중을 프롬프트에 숫자로 적는 것만으로는 지켜지지 않았다. 처음에 공고 짝을 "재료 첫째" 자리에 두고 "가장 확실한 재료다"라고 적어뒀더니, 모델은 글이 아니라 배치를 따라 공고 위주 문장을 냈다. 자기소개서 쪽도 같은 병이었다. 공고 원문 10,000자가 먼저 오고 회사 조사가 그 뒤에 있었다. 순서와 라벨이 곧 비중이라는 걸 확인하고, 두 프롬프트 모두 회사 조사를 먼저 두는 것으로 고쳤다.
이 비중을 세게 걸었더니 반대쪽 실패도 났다. "막힌 원인을 붙드는 쪽을 주로 맡았고, 어디서 들어와도 다음 행동을 이어가는지를 중요하게 봅니다" — 무슨 기술로 무엇을 만드는 개발자인지 한 글자도 없는 문장이었다. 회사 조사는 왜 이 회사인지를 정하고, 공고 요건은 내 경력 중 무엇을 앞세울지를 정한다는 역할 구분을 다시 프롬프트에 적었다. 도구·기술 이름, 만든 것의 종류, 규모 중 최소 둘은 반드시 넣게 했다. 도구 이름이 하나도 없는 글은 직무 자체가 보이지 않는다.
회사 조사에 "사업"을 따로 담는다
회사 조사 안에서도 무게가 다르다. 사업·도메인, 사업이 향하는 방향, 다루는 대상 순이다. 그런데 예전 구조에는 "비전"만 있었다. 비전은 "~한 세상을 만든다" 같은 방향 선언이라, 지원자가 자기 경험을 붙일 자리를 찾기 어렵다. 그래서 business(무엇을 누구에게 파는 회사인지) 필드를 새로 뒀다.
사업을 확인할 수 없는 페이지에서는 조사를 끊는다(hasBusinessInfo). 실제로 채용 사이트 첫 화면(공고 목록뿐인 페이지)이 회사 조사 입력으로 들어와 "물류센터 안내와 채용공고를 운영한다"가 그 회사의 사업으로 저장된 적이 있다. 그 위에 세운 추론과 SUMMARY가 전부 채용공고 이야기가 되어버렸다.
기준은 페이지 종류가 아니라 내용으로 잡았다. 한때 "채용 사이트면 거절"로 막았다가 되돌렸다. 대기업은 채용 사이트에도 회사 소개를 싣는다. 실제로 어느 물류 회사의 채용 사이트 소개 페이지는 1,553자 분량으로 "2015년 이래 유통업의 패러다임을 바꿔왔다" 같은 사업 설명을 담고 있었다. 페이지 종류로 막으면 자료가 있는 페이지까지 함께 버리게 된다.
추론은 사실이 아니라 추론으로 적는다
사용자가 그 회사 서비스를 실제로 써봤는지는 앱이 가진 자료가 아니다. 그러면 진짜 관심이라는 신호를 만드는 길은 그 회사의 문제를 실제로 생각해본 흔적 하나뿐이다. 그래서 프롬프트가 생각의 순서를 강제한다. 사업 구조 → 지금 무엇이 어려울지 추론 → 내 경력의 같은 모양 → 그제야 문장 작성.
이 추론은 반드시 "~로 보인다" 같은 추측형으로 적고, 화면에 그대로 노출한다("그래서 지금 어려울 것 · 추론"). 맞는지 판단하는 건 사용자다. 내부 사정을 아는 척하다 틀리면 아무 말도 안 하는 것보다 나쁘다.
지원 동기는 한 가지 모양이 아니다 — 유형으로 나누다
동기 문항에 뼈대를 하나만 줬을 때 후보 세 개가 전부 같은 문장이 됐다.
~를 만들고 ~정리했습니다. ~걷어낸 뒤로는 ~부터 잡습니다. ~계속 맡고 싶습니다.세 후보를 나란히 보여주는 의미가 없었다. 뼈대가 하나뿐이라 시작점이 항상 같았다. 그래서 지원 동기를 "이유의 종류"로 나누고, 이력서 SUMMARY 후보도 같은 방식으로 나눴다.
| 유형 | 어디서 시작하나 | 근거가 없으면 |
|---|---|---|
| 겪어본 문제 | 그때의 장면 | 애초에 고르지 않는다 |
| 써본 제품 | — | 전제를 needsInput으로 뺀다 |
| 도메인 관심 | 그 도메인이 갖는 문제 | 애초에 고르지 않는다 |
| 다음 단계 | "여기까지 해왔다" | 애초에 고르지 않는다 |
| 역량 기여 | — | — |
| 일하는 방식 | — | 전제를 needsInput으로 뺀다 |
도메인 관심은 끌린다는 말만 적으면 그게 곧 상투어이므로, 확인된 사실로 말하게 했다. 기본 뼈대(사실 → 달라진 것 → 맡고 싶은 일)는 유지하되, 시작점은 유형이 정한다고 명시했다. 이렇게 적지 않으면 두 지시가 부딪혀 모델이 회차마다 다르게 행동한다.
확인할 수 있는 유형과 없는 유형도 갈라 다뤘다. 겪어본 문제·다음 단계·도메인은 이력서에 근거가 없으면 애초에 고르지 않는다. 반면 "써본 제품"·"일하는 방식"은 그 전제가 우리 자료에 있을 수 없다. 금지하면 가장 진짜인 두 유형이 통째로 사라지고, 단정하면 지어내기가 된다. 그래서 제안은 하되 전제는 needsInput(전제를 사용자에게 확인받도록 따로 빼둔 필드)으로 뺐다.
9월 1일에는 유형을 다시 짜고 말투를 풀었다. 딱딱한 보고서 문체("~으로 판단된다") 대신 말하듯 쓰게 했다. 그리고 SUMMARY가 "만든 것의 목록"으로 굳어지는 것, 추상 명사로 도망가는 것을 각각 별도로 고쳤다. 회사가 다루는 대상을 "상품과 개인화 결과가 함께 바뀌는 서비스"처럼 명사 두 개를 붙여 압축하면 두 번 읽어야 뜻이 들어온다는 것도 이 과정에서 확인한 실패 사례다.
마지막으로 회사 이름과 직무를 SUMMARY 문장이 아니라 문서 맨 위 첫 줄로 올렸다. SUMMARY 안에서 회사 이름을 부르면 "{회사}에서 … 다뤘습니다"처럼 그 회사에서 일했다는 뜻으로 오독될 수 있어서다.
동기는 다짐도 논평도 아니다
지원 동기 문장의 마지막 마디를 다듬는 과정에서 반대쪽 실패가 두 번 더 나왔다.
하나는 신입 말투, 다른 하나는 평론가 말투였다. "재미있어졌습니다"는 신입 말투이고, "~가 속도를 가른다고 보게 됐습니다"·"~의 중요성을 알게 됐습니다"는 평론가 말투다. 신입 말투를 막았더니 모델이 곧바로 반대편으로 갔다. 둘 다 경력자가 평소에 쓰지 않는 말이라 지어낸 티가 난다. 경력자는 판단을 논평하지 않는다. 대신 둘 중 하나로 말한다. 결과("네 번째 사이트는 이틀이면 붙었습니다") 또는 주로 맡아온 자리("화면을 늘리는 일보다 여러 화면이 같은 규칙을 쓰게 만드는 쪽을 주로 맡아왔습니다").
처음에는 뒤쪽을 "지금 일하는 방식"이라고만 적었는데, 그러니 세 후보가 전부 "~부터 확인합니다"·"~부터 봅니다"로 끝났다. 버릇을 적었을 뿐이라 읽고 나도 이 사람이 무슨 개발자인지 알 수 없었다. 이 자리는 정체성이 서는 자리다. 무엇을 주로 맡아왔는지로 말하면 판단을 한마디도 하지 않는데도 어떤 개발자인지가 드러난다.
다짐과 동기도 갈라야 했다. "다짐 금지"만 걸었더니 사실 나열로만 끝나 동기가 들어갈 자리 자체가 없어졌다. "기여하겠습니다"는 누구나 쓸 수 있어 안 되지만, "~를 만들다 보니 ~가 궁금해졌습니다"는 앞 문장에 그렇게 된 경위가 있어 동기가 된다. 재료는 반복이다. "네 번 걷어내면서"처럼 경위가 앞에 있어야 그 말이 선다.
문장은 네 마디로 잇게 했다. 겪은 일 → 거기서 얻은 것 → 이 회사에선 무엇이 중요할 것 같은지 → 그래서 맡고 싶은 일. 세 번째가 다리다. 이게 없으면 마지막 문장이 허공에서 튄다. "…이탈 구간부터 확인합니다. 상품 탐색이 주문으로 넘어가는 자리를 먼저 맡고 싶습니다" — 앞뒤가 이어지지 않아 두 문장이 따로 논다. 그리고 이 다리는 단정이 아니라 추측이다. 한때 "추론을 문장에 옮기지 마라"로 막았더니, 막힌 것이 아는 척("이 회사는 ~를 다룬다")뿐 아니라 판단("~가 중요할 것 같습니다")까지 함께 사라져 다리가 통째로 없어졌다. 아는 척과 판단은 다른 것이라 갈라서 다뤄야 했다.
마지막 마디도 "~부터 맡고 싶습니다"·"~를 담당하고 싶습니다"로 끝내지 않는다. 이건 일감을 배정받는 말이라 어떤 사람인지가 안 보인다. 대신 무엇을 함께 고민할지와 사용자에게 무엇이 달라지는지를 적는다.
공고를 두 번 분석하게 하지 않는다
이력서를 바꾸면 적합도와 요건별 근거가 다시 계산돼야 하는데, 그 근거가 바로 "왜 이 회사인지" 문장의 재료이기도 하다. 예전에는 화면이 "공고를 다시 분석하세요"라고 따로 시켰다. 지금은 "공고 맞춤 이력서 만들기" 버튼 하나가 필요할 때 재분석을 대신 돌린다. "왜 이 회사인지"를 별도 호출로 받는 것과 같은 원칙이다. 사용자에게는 버튼 하나지만 내부에서는 두 번 부른다.
한 응답에 이력서 마크다운과 JSON을 함께 요구할 수 없다는 것도 이 과정에서 부딪혔다. 이력서 서식 규약(RESUME_MARKDOWN_FORMAT)이 "답은 반드시 resume 코드블록 하나로만"·"앞뒤에 설명을 붙이지 말 것"인데 그 뒤에 "JSON을 덧붙여라"를 얹었더니, 모델은 더 강한 쪽인 서식 규약을 따랐다. "왜 이 회사인지" 문장이 한 번도 안 나오던 이유였다. 규약을 느슨하게 고쳐 우회하지 않고 별도 호출로 분리했다. 첨삭·반영본이 같은 규약을 그대로 믿고 파싱하기 때문이다.
이력서를 바꾼 뒤 화면이 "이력서 없이 분석된 결과입니다"라고 말하는 문제도 함께 고쳤다. 실제로는 두 가지 경우가 같은 문구 아래 섞여 있었다. 정말 이력서 없이 분석한 경우와, 이력서를 바꿔서 대조 결과가 정리된 경우. 후자에게 그 문구는 거짓이다. 지금은 "이력서와 대조되지 않은 결과입니다"로 바꾸고, 그 자리에 다시 분석하는 버튼을 함께 뒀다.
합격 자소서는 자소서에만 쓴다
합격한 자소서를 참고자료로 붙이는 기능은 용도가 하나다. 합격한 자소서의 구조와 문맥을 보고 내 자소서를 쓴다. 그런데 한동안 공고 분석과 맞춤 이력서 생성에도 함께 실려 있었다.
이력서에 넣는 것이 특히 위험했다. 자소서의 구성·전개 방식을 이력서에 적용하면 그건 틀린 문서가 된다. 이력서는 명사를 쌓아 짧게 적는 글이라, 서사 문단이 들어가면 읽히지 않는다. 자소서 프롬프트에 이미 있던 "이력서 문장을 그대로 옮기지 말 것"과 같은 이야기를 반대 방향에서 한 것이다.
용도를 바로잡으면서 탭 이름도 실체에 맞춰 정리했다. "참고"를 "합격 자소서"로 바꿔 무엇을 참고하는지 이름에서 보이게 하고, "이력서"를 "맞춤 이력서"로 바꿔 앱의 다른 이력서와 구별되게 했다. 같은 실수가 되살아나지 않게 소스를 검사하는 테스트도 붙였다.
Paws 화면 재구성 — 하는 일을 둘로 가른다
Paws는 네 가지 일을 한다. 공고 분석, 회사 조사, 맞춤 이력서, 자소서. 그 넷은 두 갈래로 묶인다. 공고·회사 조사에서 맞춤 이력서로, 합격 자소서에서 자소서로. 그런데 화면에는 다섯 개 탭이 평평하게 늘어서 있어서, 무엇이 무엇의 재료인지 전혀 보이지 않았다. 게다가 그 탭이 320px 사이드바 안에 있어 글씨 크기가 10px까지 내려갔고, 정작 탭을 눌러 바뀌는 결과는 오른쪽 넓은 패널에 있었다.
상위를 "맞춤 이력서 / 자기소개서" 두 그룹으로 나눴다. 그룹 안에서는 자료와 결과가 만들어지는 순서대로 쌓여서, 탭에 가려져 있던 관계가 화면 배치만으로 드러난다. 끝난 단계는 한 줄로 접었다(DoneStep). 왼쪽 열이 1,300px까지 늘어져 있던 건 이미 끝난 입력 상자와 안내문이 자리를 그대로 차지하고 있었기 때문이다. "다시"를 누르면 펼쳐진다.
왼쪽에 있던 결과 미리보기 두 개(공고 본문·회사 조사 요약)도 걷어냈다. 오른쪽 패널이 이미 같은 것을 그리고 있어 정보가 두 번 보였다. 같은 김에 합격 자소서 편당 글자 수 제한을 2,000자에서 4,000자·3편까지로 늘렸다. 2,000자는 자소서 앞 절반만 보여줘서, 정작 배울 것(근거를 어떻게 대는가)이 대개 중반 이후에 있다는 목적과 어긋났다.
목록이 아니라 글이 되게 — SUMMARY 문체 규칙
구조를 다 고쳐도 결과가 심심했다. 그건 "목록"이기 때문이었다(SUMMARY_VOICE). 실제로 나온 문장이다.
나쁨 A·B·C가 쓰는 네 개 사이트를 단독 개발하고, 공통 UI·유틸·API
클라이언트를 모노레포로 통합해온 개발자입니다.명사를 쌓고 관형절(명사를 꾸미는 절)로 눌러서 무엇이 어려웠고 무엇이 달라졌는지가 한 글자도 없다. 같은 병을 이력서 경력 항목 규약(RESUME_STORY_SHAPE)이 이미 진단해뒀지만, 그건 항목 단위 규약이라 SUMMARY에는 걸리지 않았다. 요약용으로 줄여 따로 세웠다.
- 만든 것의 이름이 아니라 무엇이 어긋났고 그걸 어떻게 한곳으로 모았는지를 쓴다
- 숫자는 하나만. 여러 개면 다시 목록이 된다
- 추상 명사로 도망가지 않게 막는다. "흐름"·"구조"·"경험"·"지점"·"방식"만 남으면 어느 개발자에게나 맞는 글이 된다. 문장마다 확인할 수 있는 것(만든 것의 종류·도구 이름·숫자·역할 이름)이 하나는 있어야 한다
이 규칙 자체가 앞선 "한 사례가 정체성이 되면 안 된다"가 만든 부작용이었다. 폭을 요구하니 폭을 추상어로 채웠다. 그래서 함께 적었다. 폭은 도메인·기간·만든 것의 종류로 담는 것이지 낱말을 뭉뚱그리는 것이 아니다.
"한 사례가 정체성이 되면 안 된다"는 규칙도 그 자체로 실패를 겪고 나온 것이다. 유형 하나를 붙들라고 했더니 첫 문장부터 그 사례로 자기를 규정해서("인증이 끊기는 문제를 풀어온 개발자입니다") 그 문제 하나만 담당하는 사람으로 읽혔다. 첫 문장은 이력서 전체에서 뽑은 폭이고, 회사와 닿는 사례는 다음 문장에서 예시로만 들게 했다. 사례는 예시이지 지원 범위가 아니다.
후보는 저장되고, 다시 뽑을 길을 남긴다
한 번 고른 문장은 프롬프트를 고쳐도 저절로 바뀌지 않는다는 사실은 앞서 확인했지만, 반대 방향의 문제도 있었다. 한 번 고르면 갇힌다. 맞춤 이력서 버튼은 이미 고른 것이 있으면 후보를 새로 뽑지 않는다. 선택을 지우지 않으려는 규칙이다. 그런데 그 버튼만 있으면 문장이 마음에 안 들 때 빠져나갈 길이 없다. 그래서 "후보 다시 뽑기"를 별도로 뒀고, 화면 위치는 이후 후보 목록 아래로 옮겼다.
저장된 옛 후보는 새로 뽑아도 지우지 않고 함께 알린다. 지금 고른 문장이 그중 하나일 수 있어서, 조용히 없애면 선택이 사라진다. 후보가 2개 미만이면 아예 저장하지 않는다는 규칙도 여러 번 반복해서 적용됐다. 고를 수 없으면 아무것도 한 게 아니다.
그 밖의 수정
- 우대 사항의 노란색을 걷어냈다. 노랑·호박색은 이 앱에서 "확인 필요"를 뜻하는 색인데(낡음 안내, 자소서의 "확인 필요" 등), 우대 사항은 경고가 아니라 그냥 정보였다. 같은 색이 두 가지 일을 하면 화면에서 가장 급하지 않은 자료가 가장 급한 것처럼 보인다.
- 좁은 창에서 아이콘이 글자를 덮는 문제. flex 안에서 svg는 기본으로 줄어드는데, 버튼이 좁아질 때 아이콘이 글자 위에 겹쳐 보였다. 아이콘에
shrink-0, 버튼에whitespace-nowrap을 붙였다. - 진행 중 말풍선이 실제 작업과 다른 말을 했다. Paws가 하는 일이 네 가지인데 로딩 문구는 캐릭터 단위 하나뿐이라, 맞춤 이력서를 만드는 동안에도 "공고 분석"이라고 말하고 있었다. 진행 중인 일을 각자 말하게 표로 갈랐다.
- "접점"이라는 내부 용어가 화면까지 새어 나갔다. 우리끼리 쓰던 말이라 취준생이 읽기엔 어색해서, 말풍선과 프롬프트 주석 모두 서술형 표현으로 바꿨다. 코드 안 이름(
ApplicationAngle)은 화면에 안 나오니 남겼다. - v0.2.13 릴리스.
이 필터들은 모두 회귀를 막는 테스트를 함께 붙였다. isWeldedSentence가 검사하는 조건 하나하나(회사 이름 뒤 '은/는', "저는"·"제가", 별칭 매칭)마다 실제로 나왔던 나쁜 문장을 픽스처(테스트가 쓰는 고정 입력 데이터)로 넣어뒀다. 프롬프트만 고치고 테스트를 안 늘리면, 나중에 프롬프트를 다시 만질 때 같은 실패가 소리 없이 되살아난다. pawsLayout.test.ts도 같은 이유로 붙였다. 공고 분석과 결과물 패널이 같은 요건 목록을 두 번 그리는 회귀, "첨삭에 반영" 버튼이 두 자리에 중복되는 회귀는 런타임 테스트로는 잡히지 않고 소스 구조를 직접 봐야 잡힌다.
생성 규칙과 검증 규칙은 다른 층에 있어야 했다
금지만 걸면 모델은 반대쪽 극단으로 갔다. 공고 인용을 막으면 회사가 사라지고, 회사 쪽 절을 막으면 이력서 항목 요약으로 돌아가고, 뼈대를 하나만 주면 세 후보가 똑같아졌다. 프롬프트 튜닝은 나쁜 것을 나열하는 일이 아니라 빈자리에 무엇이 와야 하는지를 구체적으로 채우는 일이라는 걸 이만큼 명확하게 겪은 적이 없었다.
그리고 프롬프트만으로는 끝내 안 되는 지점이 있었다. "이어 붙인 문장"은 낱말을 여섯 번 바꿔가며 막아도 구조가 그대로 살아남았다. 사람이 1초에 알아보는 패턴을 모델은 매번 다른 이름으로 재생산했다. 결국 받는 쪽에서 코드로 거르고 나서야 끝났다. 프롬프트는 확률을 낮추는 도구이고, 확실히 막아야 하는 것은 코드가 막아야 한다.
실패 사례가 쌓일수록 규칙 파일도 함께 늘어나는 구조라, 이 이틀 동안 규칙만 계속 불어났다. 규칙 파일에 하나 더 얹는 대신 필터 쪽에서 더 일반적으로 잡을 방법은 아직 찾지 못했다. 지원 동기 유형도 9월 1일에 한 번 더 재편했으니, 유형 경계가 최종인지는 사용자 반응을 더 봐야 안다.