From 5d02a8a56f4a63500d5b7baaa9a7fb1f0941b6e4 Mon Sep 17 00:00:00 2001 From: g1nation Date: Fri, 19 Jun 2026 18:19:20 +0900 Subject: [PATCH] =?UTF-8?q?v2.2.257:=20/meet=20=ED=9A=8C=EC=9D=98=EB=A1=9D?= =?UTF-8?q?=20=EC=B1=85=EC=9E=84=20=EC=86=8C=EC=9E=AC=C2=B7=EA=B7=BC?= =?UTF-8?q?=EA=B1=B0=20=EC=A0=95=EC=B1=85=204=EC=A2=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 실제 회의록 산출물 분석에서 드러난 4개 결함을 프롬프트 정책으로 차단. 공유 출력 형식(단일샷+reduce)과 추출(map) 단계 모두에 적용. - 익명·번호 화자("참석자 1")를 액션 담당으로 확정 금지 → [미지정-확인필요]. 한 익명에 액션 몰리면 "(동일 화자 여부 확인)" — 가짜 책임 배정 차단. - 내용 없는 인용("이렇게 이렇게/그거") 근거 금지 → [내용 확인필요]. - 기한 역참조: 리스크·논의의 마감("다음 주 수요일")을 결부된 액션 기한에 반영. - 참석자 명단 정합성: 본문 화자가 명단에 없으면 "(명단 외 화자 — 확인필요)". §4 지시문·최종 점검 체크리스트 반영. 회귀 가드 +10건, 전체 694 통과. Co-Authored-By: Claude Opus 4.8 (1M context) --- PATCHNOTES.md | 9 +++ package.json | 2 +- .../datacollect/prompts/meetPrompt.ts | 11 +++- tests/meetPrompt.test.ts | 60 +++++++++++++++++++ 4 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 tests/meetPrompt.test.ts diff --git a/PATCHNOTES.md b/PATCHNOTES.md index a4d45c3..1d85bc3 100644 --- a/PATCHNOTES.md +++ b/PATCHNOTES.md @@ -1,5 +1,14 @@ # Astra Patch Notes +## v2.2.257 (2026-06-19) +### 📝 `/meet` 회의록 — 책임 소재·근거 정책 4종 (익명 담당·빈 인용·기한·명단 정합) +실제 회의록 산출물 분석에서 드러난 4개 결함을 프롬프트 정책으로 차단. 공유 출력 형식(단일샷+reduce)과 추출(map) 단계 모두에 적용. +- **익명·번호 화자 책임 배정 금지**: "참석자 1" 같은 실명 미확인 화자를 액션 담당으로 확정하지 않고 `[미지정-확인필요]` 로 둠. 한 익명에 액션이 몰리면 "(동일 화자 여부 확인)" 표기 — 화자분리 오류로 인한 **가짜 책임 배정** 차단. +- **내용 없는 인용 금지**: "이렇게 이렇게/그거/이거" 같은 지시대명사뿐인 발화는 결정·액션 근거로 인용 불가 → 핵심 내용을 `[내용 확인필요]` 로 표기. +- **기한 역참조**: 리스크·논의에 등장한 마감("다음 주 수요일")을 결부된 액션 기한 칸에 연결 — 액션은 전부 "미정"인데 본문엔 마감이 떠 있는 모순 제거. +- **참석자 명단 정합성**: 본문 화자가 회의 개요 참석자에 없으면 "(명단 외 화자 — 확인필요)" 표기. +- §4 액션 지시문·최종 점검 체크리스트에 게이트 반영. 회귀 가드 테스트 +10건([meetPrompt.test.ts](tests/meetPrompt.test.ts)), 전체 694 통과. 코어 채팅 경로(v2.2.256)는 불변. + ## v2.2.256 (2026-06-19) ### 🧩 코어 채팅 경로 — 큰 입력 청킹·통합 + 실제 컨텍스트 창 정렬 + 모델 핸들 race 수정 큰 입력을 넣으면 `Failed to acquire LM Studio model handle … Operation canceled` 로 턴 전체가 죽던 문제를 3계층으로 해결. `/meet`·`/review` 와 달리 **일반 채팅(코어 경로)** 은 그동안 단일 예산 호출이라 약한 모델·큰 입력에서 무너졌다 — 그 갭을 메움. diff --git a/package.json b/package.json index 976ddf1..9174a6b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "astra", "displayName": "Astra", "description": "The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making.", - "version": "2.2.256", + "version": "2.2.257", "publisher": "g1nation", "license": "MIT", "icon": "assets/icon.png", diff --git a/src/features/datacollect/prompts/meetPrompt.ts b/src/features/datacollect/prompts/meetPrompt.ts index 6837f9b..d71b5c2 100644 --- a/src/features/datacollect/prompts/meetPrompt.ts +++ b/src/features/datacollect/prompts/meetPrompt.ts @@ -72,6 +72,10 @@ const OUTPUT_FORMAT = `# 작성 원칙 (회의록 품질 — 출력 전 반드 - **결과 중심**: 발언 순서·토론 과정·"누가 무슨 말을 했는지"를 나열하지 말고 *결과*를 적는다. (예: "PlayCanvas를 논의함"❌ → "PlayCanvas/Babylon.js 비교 검토 진행 예정"✅) 책임 소재나 입장 차이가 핵심일 때만 발언자를 밝힌다. - **담당자는 개인 우선**: "개발팀/QA팀" 같은 조직 단위가 아니라 개인 이름(예: 송병준, 김원일 PD)으로 적는다. 실제로 개인이 안 정해졌으면 "개발팀 (담당자 지정 필요)" 형태로 표기. - **증빙 보존**: 결정되지 않은 사항이라도 중요한 논의·리스크는 빠짐없이 기록한다(향후 이력·분쟁 근거). +- **익명·번호 화자에 책임 배정 금지(최우선)**: "참석자 1", "발언자 A", "(주체 불명확)"처럼 *실명이 확인 안 된 화자*를 액션 아이템 담당으로 확정하지 말 것. 담당 칸은 \`[미지정-확인필요]\`로 두고 근거 발언만 남긴다 — 익명에게 가짜 책임을 붙이는 것이 회의록 신뢰도를 가장 크게 해친다. 또한 **한 익명 화자에게 액션이 여러 건 몰리면** 화자분리 오류일 수 있으니 그 항목들 작업 상세 끝에 "(동일 화자 여부 확인)"을 덧붙인다. +- **내용 없는 인용 금지**: "이렇게 이렇게", "그거", "이거", "저렇게" 처럼 지시대명사뿐이라 *내용이 비어 있는* 발화는 결정·액션의 근거로 인용하지 말 것. 그런 발화만 근거인 항목은 핵심 내용을 \`[내용 확인필요]\`로 표기한다(빈 인용을 근거인 척 달지 말 것). 표기 오타와는 다른, *내용 공백* 문제다. +- **기한 역참조**: 리스크·논의·녹취 어디든 등장한 날짜·마감("다음 주 수요일", "이달 말" 등)은 *직접 결부된 게 분명한* 액션의 기한 칸에 연결한다. 결부가 불확실하면 기한 칸에 "(기한 후보: …)"로 보수적으로 표기한다 — 액션 기한을 전부 "미정"으로 비우면서 본문엔 마감이 떠 있는 모순을 막는다. +- **참석자 명단 정합성**: 본문(액션·결정·논의)에 등장한 발언자가 회의 개요 '참석자'에 없으면 그 항목 옆에 "(명단 외 화자 — 확인필요)"를 붙인다. # 출력 형식 (Output Format — 정확히 이 구조를 유지) @@ -91,7 +95,7 @@ const OUTPUT_FORMAT = `# 작성 원칙 (회의록 품질 — 출력 전 반드 (이번 회의에서 확정된 결정이 없으면 "이번 회의에서 확정된 결정사항 없음"이라고 명시한다 — 빈칸·생략 금지.) ## 4. 액션 아이템 -회의 후 수행할 업무를 **담당·작업·기한·산출물 4요소**가 모두 드러나게 정리한다. 각 행은 반드시 녹취록 근거로 작성한다. 담당자는 개인 우선(없으면 "OO팀 (담당자 지정 필요)"). 회의에서 기한·산출물이 안 나왔으면 해당 칸에 "확인 필요"라고 적는다. 표 셀 안에서는 줄바꿈과 \`|\` 문자를 쓰지 말 것. +회의 후 수행할 업무를 **담당·작업·기한·산출물 4요소**가 모두 드러나게 정리한다. 각 행은 반드시 녹취록 근거로 작성한다. 담당자는 개인 우선(없으면 "OO팀 (담당자 지정 필요)"). **실명이 확인 안 된 번호·익명 화자는 담당으로 확정하지 말고 \`[미지정-확인필요]\`로 둔다**(위 작성 원칙 참조). 회의에서 기한·산출물이 안 나왔으면 해당 칸에 "확인 필요"라고 적되, **본문에 마감 날짜가 언급됐고 이 작업에 결부되면 그 날짜를 기한 칸에 반영**한다. 표 셀 안에서는 줄바꿈과 \`|\` 문자를 쓰지 말 것. | 담당 | 작업 내용 | 작업 상세 | 산출물 | 기한 | 상태 | | --- | --- | --- | --- | --- | --- | @@ -125,7 +129,7 @@ const OUTPUT_FORMAT = `# 작성 원칙 (회의록 품질 — 출력 전 반드 - 핵심 논점 한 줄 # 최종 점검 (출력 전 내부 확인 — 체크 로그는 출력하지 말 것) -□ 결정사항에 확정된 것만 있는가(검토 필요 항목이 섞이지 않았나) □ 액션 아이템에 담당(개인)·작업·기한·산출물 4요소가 있는가 □ 오픈 이슈가 한곳에 모였는가 □ 리스크가 영향·대응방안과 함께 정리됐는가 □ 논의사항이 주제별 bullet로 간결한가 □ 요약이 결과 중심인가 □ 결정·액션에 근거 인용이 붙어 있는가 +□ 결정사항에 확정된 것만 있는가(검토 필요 항목이 섞이지 않았나) □ 액션 아이템에 담당(개인)·작업·기한·산출물 4요소가 있는가 □ 익명·번호 화자를 담당으로 확정하지 않고 [미지정-확인필요]로 뒀는가 □ "이렇게/그거" 같은 빈 인용을 근거로 달지 않았는가([내용 확인필요] 처리) □ 본문에 뜬 마감 날짜를 결부된 액션 기한에 반영했는가 □ 명단 외 화자에 (확인필요) 표시를 했는가 □ 오픈 이슈가 한곳에 모였는가 □ 리스크가 영향·대응방안과 함께 정리됐는가 □ 논의사항이 주제별 bullet로 간결한가 □ 요약이 결과 중심인가 □ 결정·액션에 근거 인용이 붙어 있는가 위 형식을 정확히 따르고, 모든 내용은 한국어로 작성하시오.`; @@ -143,7 +147,8 @@ export function buildMeetExtractPrompt(segment: string, metadata: string, segInd # 규칙 (할루시네이션 방지) - 이 조각에 없는 사실·수치·결정을 만들지 말 것. 발언 주체가 불명확하면 "(주체 불명확)"으로 표기. - STT 오타는 문맥과 메타데이터(용어집 역할)로 정규화하되, 없는 사실을 지어내는 것은 금지. -- 각 항목 끝에 근거 발언 원문 일부(20자 내외)를 \`근거: "…"\` 로 붙인다. +- 각 항목 끝에 근거 발언 원문 일부(20자 내외)를 \`근거: "…"\` 로 붙인다. 단, "이렇게/그거/이거" 같은 지시대명사뿐인 *내용 공백* 발화는 근거로 쓰지 말고 핵심 내용을 "[내용 확인필요]"로 표기한다. +- 액션 담당이 실명으로 확인 안 된 화자(번호·익명)면 \`[담당]\`에 "[미지정]"으로 적고 임의 배정하지 말 것. - 조각 경계에서 잘린 문장은 무리하게 해석하지 말고 "(조각 경계에서 잘림)"으로 표기. [메타데이터] diff --git a/tests/meetPrompt.test.ts b/tests/meetPrompt.test.ts new file mode 100644 index 0000000..d9a748a --- /dev/null +++ b/tests/meetPrompt.test.ts @@ -0,0 +1,60 @@ +/** + * 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(/빈 인용을 근거로 달지 않았는가/); + }); +});