5d02a8a56f
실제 회의록 산출물 분석에서 드러난 4개 결함을 프롬프트 정책으로 차단.
공유 출력 형식(단일샷+reduce)과 추출(map) 단계 모두에 적용.
- 익명·번호 화자("참석자 1")를 액션 담당으로 확정 금지 → [미지정-확인필요].
한 익명에 액션 몰리면 "(동일 화자 여부 확인)" — 가짜 책임 배정 차단.
- 내용 없는 인용("이렇게 이렇게/그거") 근거 금지 → [내용 확인필요].
- 기한 역참조: 리스크·논의의 마감("다음 주 수요일")을 결부된 액션 기한에 반영.
- 참석자 명단 정합성: 본문 화자가 명단에 없으면 "(명단 외 화자 — 확인필요)".
§4 지시문·최종 점검 체크리스트 반영. 회귀 가드 +10건, 전체 694 통과.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
61 lines
2.7 KiB
TypeScript
61 lines
2.7 KiB
TypeScript
/**
|
|
* Regression guard for the /meet prompt policy.
|
|
*
|
|
* These 4 rules were added after a real meeting record showed the failure modes:
|
|
* 1) anonymous/numbered speakers assigned as action owners (fake accountability)
|
|
* 2) content-empty deictic quotes ("이렇게 이렇게") passed off as 근거
|
|
* 3) deadlines mentioned in risks/discussion not reflected in action 기한
|
|
* 4) speakers in the body who aren't in the attendee list
|
|
*
|
|
* Prompts can't be unit-tested for model behavior, but we CAN guard that the
|
|
* policy text doesn't silently get dropped in a future refactor.
|
|
*/
|
|
|
|
import {
|
|
buildMeetPrompt,
|
|
buildMeetExtractPrompt,
|
|
buildMeetReducePrompt,
|
|
} from '../src/features/datacollect/prompts/meetPrompt';
|
|
|
|
const transcript = '참석자 1: 이거는 이렇게 이렇게 해주세요. 효희 팀장: 네 수정할게요.';
|
|
const metadata = '회의명: 테스트';
|
|
|
|
describe('/meet prompt — accountability & grounding policy', () => {
|
|
// The OUTPUT_FORMAT block is shared by the single-shot and reduce paths, so
|
|
// both must carry the policy.
|
|
const sharedFormatBuilders: Array<[string, string]> = [
|
|
['buildMeetPrompt', buildMeetPrompt(transcript, metadata)],
|
|
['buildMeetReducePrompt', buildMeetReducePrompt('## 액션\n- [미지정] 작업', metadata)],
|
|
];
|
|
|
|
test.each(sharedFormatBuilders)('%s forbids assigning anonymous speakers as owners', (_name, prompt) => {
|
|
expect(prompt).toContain('[미지정-확인필요]');
|
|
expect(prompt).toMatch(/익명·번호 화자/);
|
|
});
|
|
|
|
test.each(sharedFormatBuilders)('%s rejects content-empty deictic quotes', (_name, prompt) => {
|
|
expect(prompt).toContain('[내용 확인필요]');
|
|
expect(prompt).toMatch(/이렇게 이렇게/);
|
|
});
|
|
|
|
test.each(sharedFormatBuilders)('%s cross-references deadlines into action 기한', (_name, prompt) => {
|
|
expect(prompt).toMatch(/기한 역참조/);
|
|
});
|
|
|
|
test.each(sharedFormatBuilders)('%s flags speakers missing from the attendee list', (_name, prompt) => {
|
|
expect(prompt).toMatch(/명단 외 화자|참석자 명단 정합성/);
|
|
});
|
|
|
|
test('extract (map) stage also avoids anonymous owner assignment & empty quotes', () => {
|
|
const p = buildMeetExtractPrompt('참석자 1: 이거 이렇게요', metadata, 1, 3);
|
|
expect(p).toContain('[미지정]');
|
|
expect(p).toContain('[내용 확인필요]');
|
|
});
|
|
|
|
test('final checklist references the new gates', () => {
|
|
const prompt = buildMeetPrompt(transcript, metadata);
|
|
expect(prompt).toMatch(/익명·번호 화자를 담당으로 확정하지 않고/);
|
|
expect(prompt).toMatch(/빈 인용을 근거로 달지 않았는가/);
|
|
});
|
|
});
|