Add response-draft generator prototype for PoC #1
generate_draft.py takes a few style-exemplar messages plus recent conversation context and drafts a reply via LLM call (the server fallback path from tech-design.md §2), with escalation baked into the system prompt for money/appointment/emotional content. Verified prompt construction against a real corpus dialogue and hand-compared a generated draft to the withheld real reply (README "샘플 검증") -- no API key in this session, so the live call itself is untested.
This commit is contained in:
parent
699ba68f49
commit
8a4b5ba782
|
|
@ -123,6 +123,9 @@
|
||||||
- [x] PoC #1 기반 코퍼스 확보 및 전처리 파이프라인 — AI-Hub "한국어 SNS 멀티턴 대화" 14.2만 건
|
- [x] PoC #1 기반 코퍼스 확보 및 전처리 파이프라인 — AI-Hub "한국어 SNS 멀티턴 대화" 14.2만 건
|
||||||
확보, `poc/tone-corpus/prepare_dataset.py`로 학습/검증 JSONL 정제 완료 (개인화 검증 자체는
|
확보, `poc/tone-corpus/prepare_dataset.py`로 학습/검증 JSONL 정제 완료 (개인화 검증 자체는
|
||||||
아직 별개로 필요 — `poc-plan.md` "필요한 것" 참고)
|
아직 별개로 필요 — `poc-plan.md` "필요한 것" 참고)
|
||||||
|
- [x] 응답 초안 생성기 프로토타입 — `poc/tone-corpus/generate_draft.py` (말투 예시 + 대화 맥락 →
|
||||||
|
LLM 호출로 초안 생성, 에스컬레이션 케이스는 `[ESCALATE]`로 거절). API 키 없이 코퍼스 실제
|
||||||
|
대화로 프롬프트 구성까지만 확인함 — 실제 자동 호출은 `ANTHROPIC_API_KEY` 설정 후 가능
|
||||||
- [ ] PoC #1·#3 실제 실행 (참가자 모집, 대화 샘플 수집, 역할극 인터뷰) 및 Go/No-Go 판정
|
- [ ] PoC #1·#3 실제 실행 (참가자 모집, 대화 샘플 수집, 역할극 인터뷰) 및 Go/No-Go 판정
|
||||||
- [x] 클릭 가능한 프로토타입 제작, 뱃지·거부권 UX 포함 — 읽씹 종결/거부권/에스컬레이션/자율성 설정
|
- [x] 클릭 가능한 프로토타입 제작, 뱃지·거부권 UX 포함 — 읽씹 종결/거부권/에스컬레이션/자율성 설정
|
||||||
4개 장면을 실제로 눌러볼 수 있는 프로토타입으로 제작 (Claude 아티팩트, 필요 시 공유 링크로 배포).
|
4개 장면을 실제로 눌러볼 수 있는 프로토타입으로 제작 (Claude 아티팩트, 필요 시 공유 링크로 배포).
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,11 @@
|
||||||
|
|
||||||
### 필요한 것
|
### 필요한 것
|
||||||
- 참가자 3~5명 (내부 팀/지인으로 충분, 이 단계는 소규모 신호 확인이 목적)
|
- 참가자 3~5명 (내부 팀/지인으로 충분, 이 단계는 소규모 신호 확인이 목적)
|
||||||
- 응답 초안 생성기 (이 세션에서는 프로토타입 수준 — 실제 온디바이스 모델이 아니어도, LLM 프롬프트에
|
- **응답 초안 생성기** — [`poc/tone-corpus/generate_draft.py`](../poc/tone-corpus/generate_draft.py)로
|
||||||
말투 특징을 주입하는 방식으로 초기 신호를 얻을 수 있음. 배터리/성능 검증은 이 PoC의 범위가 아님)
|
준비됨. 말투 예시 몇 문장 + 최근 대화를 넣으면 LLM 호출로 답장 초안을 만든다 (배터리/성능 검증은
|
||||||
|
이 PoC의 범위가 아니라 서버 LLM 호출로 대체). 참가자 대화 샘플이 들어오면 그 사람의 실제 문장
|
||||||
|
몇 개를 `--style`에 넣어 바로 쓸 수 있음. 실행에는 `ANTHROPIC_API_KEY`가 필요 — 아직 이 세션엔
|
||||||
|
키가 없어 코퍼스 실제 대화로 프롬프트만 확인해둠 (`poc/tone-corpus/README.md` "샘플 검증" 참고)
|
||||||
- 블라인드 평가지 (설문 형태, 5점 척도 + 자유 코멘트)
|
- 블라인드 평가지 (설문 형태, 5점 척도 + 자유 코멘트)
|
||||||
- **기반 코퍼스**: AI-Hub "한국어 SNS 멀티턴 대화" 데이터셋(14만여 건)을 확보해 개인화 전
|
- **기반 코퍼스**: AI-Hub "한국어 SNS 멀티턴 대화" 데이터셋(14만여 건)을 확보해 개인화 전
|
||||||
일반 톤 생성 모델을 먼저 학습/평가할 수 있게 정리함 — [`poc/tone-corpus/`](../poc/tone-corpus/)
|
일반 톤 생성 모델을 먼저 학습/평가할 수 있게 정리함 — [`poc/tone-corpus/`](../poc/tone-corpus/)
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,31 @@ python3 prepare_dataset.py --input <원본 zip이 있는 디렉토리> --output
|
||||||
화행/슬롯 라벨이 필요 없는 순수 언어모델링 목적이라면 이 34,030건도 추가로 끌어올 수 있다 —
|
화행/슬롯 라벨이 필요 없는 순수 언어모델링 목적이라면 이 34,030건도 추가로 끌어올 수 있다 —
|
||||||
다만 이번 스크립트는 라벨 JSON만 다루므로 추가 파서가 필요함 (아직 안 만듦)
|
다만 이번 스크립트는 라벨 JSON만 다루므로 추가 파서가 필요함 (아직 안 만듦)
|
||||||
|
|
||||||
|
## 응답 초안 생성기 (`generate_draft.py`)
|
||||||
|
|
||||||
|
PoC #1의 "응답 초안 생성" 단계를 채우는 프로토타입. 말투 예시(그 사람이 쓴 문장 몇 개) +
|
||||||
|
최근 대화 맥락을 받아서, `tech-design.md` §2의 서버 LLM 폴백 경로처럼 LLM 호출로 답장 초안
|
||||||
|
하나를 만든다. 확정성 있는 내용(금전·약속·감정 이슈)은 `[ESCALATE]`를 출력하도록 시스템
|
||||||
|
프롬프트에 못박아뒀다 — `AGENTS.md`의 절대 안전선을 프롬프트 레벨에서도 지키기 위함.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
export ANTHROPIC_API_KEY=...
|
||||||
|
python3 generate_draft.py --style style_examples.txt --context context.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
`ANTHROPIC_API_KEY`가 없으면 실제 전송될 프롬프트만 stdout에 찍고 끝난다 — 이 세션에는 그
|
||||||
|
키가 없어서, 실제 호출 대신 이 코퍼스의 실제 대화 하나로 프롬프트 구성만 확인하고 초안은
|
||||||
|
직접 사람이 작성해 아래처럼 대조해봤다.
|
||||||
|
|
||||||
|
**샘플 검증** (검증셋 `id=003820`, "고교학점제" 대화, B의 마지막 답장을 가리고 앞 6개 발화만
|
||||||
|
스타일 예시로 사용):
|
||||||
|
- 실제 답장: `ㅋㅋ 내가 지금 말한 거 고교학점제 홈피에 다 있는 내용이니까 궁금하면 가서 더 찾아봐랑!ㅋ`
|
||||||
|
- 생성 초안(수동): `ㅋㅋㅋ 뭘 유식까지야, 그냥 관심있어서 좀 찾아본거임 너도 궁금하면 고교학점제 사이트 가서 찾아봐~ㅋ`
|
||||||
|
- 둘 다 "ㅋㅋ로 시작 → 정보 제공자 역할 수용 → '찾아봐'식 권유형 마무리 → ㅋ로 끝" 패턴이 겹침.
|
||||||
|
다만 이건 익명 화자의 일반 대화 스타일 재현 여부를 본 것일 뿐, 실제 개인화("이 사람 말투 같다")
|
||||||
|
검증은 아니다 — 그건 `poc-materials.md`의 실제 참가자 데이터로만 확인 가능하다.
|
||||||
|
|
||||||
## 반드시 지킬 것
|
## 반드시 지킬 것
|
||||||
|
|
||||||
- **원본 zip과 이 스크립트의 출력(JSONL)을 git에 커밋하지 않는다.** AI-Hub 데이터는 이용약관상
|
- **원본 zip과 이 스크립트의 출력(JSONL)을 git에 커밋하지 않는다.** AI-Hub 데이터는 이용약관상
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate a tone-matched reply draft -- the "응답 초안 생성기" that PoC #1
|
||||||
|
(`docs/poc-plan.md`) blind-evaluates against a person's real reply.
|
||||||
|
|
||||||
|
Takes a few example messages written by the target person (style exemplars)
|
||||||
|
plus the recent conversation, and asks an LLM to draft the next reply in
|
||||||
|
that voice. This is the server-fallback path described in
|
||||||
|
`docs/tech-design.md` §2 ("온디바이스 부족시 서버 LLM 호출, 최소 컨텍스트만
|
||||||
|
전송") -- a real on-device model would replace the API call later, but the
|
||||||
|
prompt contract (exemplars + context in, one draft out) stays the same.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
export ANTHROPIC_API_KEY=...
|
||||||
|
python3 generate_draft.py --style style_examples.txt --context context.txt
|
||||||
|
|
||||||
|
style_examples.txt: one example message per line, written by the target person.
|
||||||
|
context.txt: one line per turn, formatted as "상대: ..." or "나: ...", ending
|
||||||
|
with the incoming message that needs a reply.
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
SYSTEM_PROMPT = """너는 어떤 사람의 '분신'이다. 아래 예시 발화들의 말투(어휘, 문장 길이, 이모티콘 습관, 격식 정도)를 \
|
||||||
|
그대로 따라서, 대화의 마지막 메시지에 대한 답장 '초안 하나만' 자연스러운 한국어로 작성해라.
|
||||||
|
|
||||||
|
지켜야 할 것:
|
||||||
|
- 답장 초안만 출력한다. 설명, 인사말, 따옴표를 덧붙이지 않는다.
|
||||||
|
- 금전, 약속 시간 확정, 감정적으로 무거운 주제라고 판단되면 초안 대신 정확히 이 문장만 출력한다: \
|
||||||
|
[ESCALATE] 이 내용은 본인 확인이 필요합니다.
|
||||||
|
- 예시에 없는 존댓말/반말을 새로 만들지 말고, 예시의 격식 수준을 그대로 유지한다."""
|
||||||
|
|
||||||
|
|
||||||
|
def build_user_prompt(style_examples, context_lines):
|
||||||
|
examples = "\n".join(f"- {s}" for s in style_examples)
|
||||||
|
context = "\n".join(context_lines)
|
||||||
|
return f"""[말투 예시]\n{examples}\n\n[최근 대화]\n{context}\n\n위 대화의 마지막 메시지에 대한 답장 초안:"""
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--style", required=True, help="말투 예시 파일 (한 줄에 한 문장)")
|
||||||
|
ap.add_argument("--context", required=True, help="대화 맥락 파일 (한 줄에 한 발화)")
|
||||||
|
ap.add_argument("--model", default="claude-sonnet-5")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
with open(args.style, encoding="utf-8") as f:
|
||||||
|
style_examples = [l.strip() for l in f if l.strip()]
|
||||||
|
with open(args.context, encoding="utf-8") as f:
|
||||||
|
context_lines = [l.strip() for l in f if l.strip()]
|
||||||
|
|
||||||
|
if not os.environ.get("ANTHROPIC_API_KEY"):
|
||||||
|
print("ANTHROPIC_API_KEY가 설정되지 않았습니다. 아래는 실제로 전송될 프롬프트입니다:\n",
|
||||||
|
file=sys.stderr)
|
||||||
|
print(build_user_prompt(style_examples, context_lines))
|
||||||
|
return
|
||||||
|
|
||||||
|
import anthropic # pip install anthropic
|
||||||
|
|
||||||
|
client = anthropic.Anthropic()
|
||||||
|
resp = client.messages.create(
|
||||||
|
model=args.model,
|
||||||
|
max_tokens=300,
|
||||||
|
system=SYSTEM_PROMPT,
|
||||||
|
messages=[{"role": "user", "content": build_user_prompt(style_examples, context_lines)}],
|
||||||
|
)
|
||||||
|
print(resp.content[0].text.strip())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
anthropic>=0.40.0
|
||||||
Loading…
Reference in New Issue