상세페이지 문구 AI 초안, 스펙표에 있는 것만 쓰게 하기
상세페이지를 AI 에 맡기면 스펙표에 없는 시각·조건·효능이 끼어듭니다. 문장마다 근거 행을 달게 하고, 숫자와 근거 표시를 스크립트로 대조하는 방법입니다.
상품 정보를 한 줄로 주고 「상세페이지 써줘」 라고 하면 이런 문장이 돌아옵니다. 「아침 8시에 담은 따뜻한 음료를 오후 2시까지 마실 수 있습니다」. 스펙표에는 「보온 6시간」 밖에 없습니다. 시각은 모델이 계산해 넣은 것이고, 판매자가 그대로 올리면 표시·광고 문제가 될 수 있습니다.
가상 텀블러로, 입력을 스펙표로 한정하고 문장마다 근거 행을 달게 한 뒤 숫자를 검사합니다.
[1] 먼저 짧은 프롬프트로 빗나가는 모습을 봅니다. 스펙을 한 줄에 풀어 쓴 입력입니다.
텀블러 상세페이지 써줘. 용량 500ml, 스테인리스 304, 보온 6시간, 무게 320g. 출근길에 들고 다니는 직장인 대상.$ claude -p --model claude-sonnet-5-5 --max-turns 1 --tools "" --setting-sources "" --strict-mcp-config < short.ko.txt > out.short.ko.1.txt33줄 결과에서 스펙표에 없는 문장만 옮깁니다. 같은 프롬프트를 한 번 더 돌린 결과에도 위의 「8시 … 2시」 문장이 들어 있었습니다.
7시 반에 담은 커피를 회의 끝나고 마셔도 따뜻합니다.*(보온 시간 기준: 00°C 물 담은 뒤 6시간 경과 시 00°C 이상. 실제 테스트 조건을 꼭 넣어 주세요.)*스마트폰 두 개 정도의 무게입니다.카페 그란데 사이즈(473ml) 한 잔이 넉넉히 들어갑니다.커피나 차를 담아도 냄새와 맛이 잘 배지 않고, 매일 쓰는 용기로 안심하고 쓸 수 있습니다.시각 계산, 한 잔 크기의 비교(473ml), 냄새·맛이 배지 않는다는 성능이 전부 입력에 없던 내용입니다. 끝에 「확인이 필요한 부분」 메모가 붙기는 했지만 본문 문장은 그대로 남아 있습니다.
[2] 스펙표를 행 번호가 있는 파일로 만듭니다. 나중에 문장마다 이 번호를 달게 합니다.
S1 | 용량 | 500mlS2 | 본체 소재 | 스테인리스 304S3 | 보온 | 6시간 (자체 시험값)S4 | 무게 | 320gS5 | 세척 | 식기세척기 사용 불가, 손세척S6 | 구성 | 본체 1개, 뚜껑 1개[3] 프롬프트를 만듭니다. 스펙과 타깃은 태그로 감싸 지시와 나눕니다. 가장 손이 가는 것은 근거 행 규칙입니다. 근거를 못 찾는 주장은 빼고 [] 로 표시하게 하는 방식은 Anthropic 의 환각 줄이기 가이드에서 가져왔습니다.
아래 <spec> 의 정보만으로 온라인 쇼핑몰 상세페이지 섹션 초안을 쓰세요. 규칙:- 섹션은 정확히 이 네 개: 문제 공감 / 핵심 장점 / 스펙 / 사용·관리. 각 섹션 제목은 `## ` 로 씁니다.- <spec> 에 없는 사실은 쓰지 않습니다. 원료·인증·효능·수치·비교 표현(최고, 업계 최초 등)을 지어내지 않습니다.- 문장은 한 줄에 하나씩 쓰고, 문장 끝에 근거 행 번호를 `[S2]` 처럼 붙입니다. 여러 행이면 `[S1][S3]`.- 근거 행이 없는 문장은 쓰지 말고, 꼭 필요한 자리에는 문장 끝에 `[]` 만 붙여 그대로 둡니다.- 「가볍다」「넉넉하다」 같은 평가 표현이 들어간 문장은 수치가 근거여도 `[S4]` 대신 `[]` 를 붙입니다. 평가는 스펙표에 없습니다.- 같은 사실을 두 섹션에 반복하지 않습니다.- 숫자와 단위는 <spec> 에 적힌 그대로 옮깁니다. 환산하지 않습니다.- 섹션 외의 설명·인사말은 쓰지 않습니다. <target>출근길에 매일 가방에 넣고 다니는 직장인</target> <spec>평가 표현 규칙과 반복 금지는 이 두 줄 없이 두 번 돌린 결과를 보고 더한 것입니다. 두 번 모두 용량·소재·보온·무게가 「핵심 장점」 과 「스펙」 에 한 번씩 나와, 사실마다 두 곳(무게는 문제 공감까지 세 곳)에 적혔습니다. 한 번은 「하루 종일 들고 다니려면 무게가 신경 쓰입니다. [S4]」 처럼 S4 가 320g 일 뿐인데 느낌을 말하는 문장에 번호가 붙었습니다.
[4] 스펙과 닫는 태그를 붙여 실행합니다. 섹션을 고정한 것은 결과를 줄 단위로 검사하려는 것입니다.
$ (cat head2.ko.txt spec.ko.txt; echo "</spec>") > prompt2.ko.txt$ claude -p --model claude-sonnet-5-5 --max-turns 1 --tools "" --setting-sources "" --strict-mcp-config < prompt2.ko.txt > out2.ko.1.txt$ cat out2.ko.1.txt## 문제 공감출근길마다 가방에 넣을 물병을 챙기는 일은 매일 반복됩니다. []오전에 담은 음료를 나중에도 따뜻하게 마시고 싶을 때가 있습니다. [] ## 핵심 장점보온은 6시간입니다 (자체 시험값). [S3]매일 들고 다니기에 가벼운 무게입니다. [] ## 스펙용량은 500ml입니다. [S1]본체 소재는 스테인리스 304입니다. [S2]무게는 320g입니다. [S4]구성은 본체 1개, 뚜껑 1개입니다. [S6] ## 사용·관리식기세척기는 사용할 수 없습니다. [S5]사용 후에는 손세척으로 관리해 주세요. [S5]「(자체 시험값)」 단서도 따라왔고, 「가벼운 무게」 는 [] 로 옮겨 갔으며 같은 사실은 한 곳에만 있습니다.
[5] 검사 스크립트를 만듭니다. 초안에서 숫자와 단위를 뽑아 스펙표 값에 있는지 보고, 문장 끝이 [S숫자] 나 [] 인지 봅니다. 제목 줄은 숫자 검사에서 뺍니다.
#!/usr/bin/env python3# usage: check.py spec.txt draft.txtimport re, sysNUM = re.compile(r"(\d+(?:\.\d+)?)\s*(ml|kg|g|시간|개|℃|%|시|분|일|hours?|)", re.I)pairs = lambda t: {(n, u.lower()) for n, u in NUM.findall(t)}spec = open(sys.argv[1], encoding="utf-8").read()allowed = pairs("\n".join(l.split("|", 2)[2] for l in spec.splitlines() if l.count("|") >= 2))draft = open(sys.argv[2], encoding="utf-8").read()body = "\n".join(l for l in re.sub(r"\[S\d+\]", "", draft).splitlines() if not l.lstrip().startswith("#"))bad = sorted(p for p in pairs(body) if p not in allowed)print("not in spec :", bad or "none")lines = [l.strip() for l in draft.splitlines() if l.strip() and not l.lstrip().startswith(("#", "---"))]noref = [l for l in lines if not re.search(r"(\[S\d+\])+$|\[\]$", l)]for l in noref: print("NO REF :", l[:60])print(f"{len(lines)} lines, {len(noref)} without a reference, {len(bad)} numbers not in spec")sys.exit(1 if bad or noref else 0)[6] 이 결과와 처음의 짧은 프롬프트 결과를 모두 검사합니다.
$ python3 -I check.py spec.ko.txt out2.ko.1.txt; echo "exit $?"not in spec : none10 lines, 0 without a reference, 0 numbers not in specexit 0$ python3 -I check.py spec.ko.txt out.short.ko.1.txt | grep -v '^NO REF'; echo "exit ${PIPESTATUS[0]}"not in spec : [('00', ''), ('07', ''), ('1', ''), ('10', ''), ('13', ''), ('2', ''), ('3', ''), ('3', '시'), ('30', ''), ('40', ''), ('473', 'ml'), ('7', '시')]33 lines, 33 without a reference, 12 numbers not in specexit 1짧은 결과에서 실제 문제는 7시 · 3시 · 473ml 입니다. 07 40 10 13 30 은 사용 장면 시각표(07:40 · 10:00 · 13:30)를 쪼갠 조각이고, 1 2 3 은 번호 매기기, 00 은 「00°C」 자리표시자라 잡음입니다. 숫자 없는 지어내기(「냄새와 맛이 잘 배지 않고」)는 이 검사로 잡히지 않습니다.
[7] 통과해도 눈으로 볼 곳이 있습니다. [] 는 「써도 된다」 가 아니라 「올리기 전에 판단하라」 는 표시입니다. 위 결과의 [] 세 줄 중 「매일 들고 다니기에 가벼운 무게입니다」 는 320g 을 보고 판매자가 정할 평가이고, 「오전에 담은 음료를 나중에도 따뜻하게」 는 보온 6시간보다 더 길게 읽힐 여지가 있어 문구를 다듬을 만합니다. 또 검사 스크립트는 번호가 붙었는지만 보므로, 규칙 없이 돌린 두 결과(사실을 두세 곳에 반복)도 모두 통과했습니다. 반복은 읽어서 거릅니다.
이 방식은 스펙표가 정확할 때만 의미가 있습니다. 표에 없는 인증·원료·시험 조건은 AI 가 아니라 판매자가 채워야 하고, 「자체 시험값」 같은 표시광고 관련 문구는 AI 초안과 별개로 직접 확인해야 합니다. 같은 방식의 근거 대조는 요약에 원문 인용 붙이기 에도 있습니다.