feat(retrieval): 청크 검색 기본 켬(+62.5%p recall@1) + 확신도 전역화 (v2.2.218)
P1 — 섹션 청크 검색 기본 활성화: - 골든셋 24질의 A/B 측정: 파일 단위 → 섹션 청크에서 recall@1 12.5%→75.0% · recall@3 33.3%→83.3% · recall@5 37.5%→87.5% · MRR 0.217→0.802. 18질의 개선·악화 0건. - Phase 1-가 구현은 완성돼 있었으나 chunkLevelRetrieval 기본값이 false 라 실전 채팅이 열등한 파일 모드로 동작 — package.json·config 기본값 true 로. - tests/retrievalEvalCompare.test.ts: 환경변수(ASTRA_EVAL_BRAIN) 게이트형 A/B 회귀 측정 도구 (평소 skip — CI/패키징 무영향). P2 — 확신도 전역화 (/meet 원칙을 모든 대화로): - memoryContext 에 [GROUNDING] 블록 — 두뇌 근거 강도(강/보통/약)를 점수로 평가해 답변 정책 주입: 약함 → "⚠️ 두뇌 근거 약함" 표기+단정 금지, 강함 → 근거 문서 제목 인용, 보통 → 사실/추론 구분 서술. P3 — 회의 용어집 자동화 + 출력 위생: - /meet 실행마다 담당자 이름·사용자 메타데이터 용어를 .astra/meet_glossary.json 에 누적, 다음 실행 때 자동 주입 (STT 보정 용어집 — 반복 회의 표기 일관성). - selfIdentity 블록에 한·영 혼합 깨진 표기 금지 규칙 (전 대화, 무비용). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,7 @@ import {
|
||||
transcriptHash, taskKey, loadRegisteredKeys, markRegistered,
|
||||
savePending, loadPending, clearPending, classifyAction,
|
||||
registerAction, buildNotes, parseConfirmArgs, renderPendingQuestion,
|
||||
loadGlossaryTerms, updateGlossary, extractGlossaryCandidates,
|
||||
type PendingItem, type PendingFile,
|
||||
} from './scheduling/meetRegistration';
|
||||
import {
|
||||
@@ -554,6 +555,15 @@ async function runMeet(arg: string, view: Webview | undefined, context?: vscode.
|
||||
}
|
||||
// 중복 방지 키 — 동일 녹취 재실행 시 이미 등록된 액션을 건너뛰기 위한 해시 (원본 전체 기준).
|
||||
const tHash = transcriptHash(transcript);
|
||||
const userMetadata = metadata; // 용어집 후보 추출용 — 자동 보강 전 원본 보존
|
||||
// [자동 용어집] 이전 /meet 들에서 누적된 인명·용어를 메타데이터에 보강 —
|
||||
// meetPrompt 가 메타데이터를 STT 보정 용어집으로 쓰므로 반복 회의의 표기
|
||||
// 일관성이 자동으로 좋아진다. 사용자 입력 메타데이터가 항상 우선(앞에 배치).
|
||||
const glossaryTerms = loadGlossaryTerms();
|
||||
if (glossaryTerms.length) {
|
||||
metadata = `${metadata ? metadata + '\n' : ''}[자동 용어집 — 이전 회의에서 누적된 인명·용어 표기] ${glossaryTerms.join(', ')}`;
|
||||
chunk(view, `📚 자동 용어집 ${glossaryTerms.length}개 용어 주입\n`);
|
||||
}
|
||||
// v2.2.211: 60K 하드 자르기 폐지 → 세그먼트 추출(Map) + 병합(Reduce).
|
||||
// 단일샷 60K 는 로컬 32K 컨텍스트에서 잘리거나 lost-in-the-middle 로 중간
|
||||
// 안건이 증발하던 원인. 12K 조각별 추출은 입력이 짧아 누락·날조 둘 다 준다.
|
||||
@@ -657,6 +667,11 @@ async function runMeet(arg: string, view: Webview | undefined, context?: vscode.
|
||||
chunk(view, `\nℹ️ 캘린더 자동 등록을 건너뜁니다 — Google Calendar OAuth(쓰기)가 연결되지 않았습니다. (Astra Settings → Google 섹션에서 연결)\n`);
|
||||
} else {
|
||||
const tasks = parseActionItems(report);
|
||||
// [자동 용어집 누적] 이번 회의의 담당자 이름 + 사용자가 입력한 메타데이터
|
||||
// 용어를 워크스페이스 용어집에 저장 — 다음 /meet 의 STT 보정에 자동 사용.
|
||||
try {
|
||||
updateGlossary([...tasks.map(t => t.owner), ...extractGlossaryCandidates(userMetadata)]);
|
||||
} catch { /* 용어집 실패는 본 흐름에 영향 없음 */ }
|
||||
if (tasks.length === 0) {
|
||||
chunk(view, `\nℹ️ 액션 아이템이 없어 캘린더에 등록할 항목이 없습니다.\n`);
|
||||
} else {
|
||||
|
||||
@@ -251,3 +251,38 @@ export function renderPendingQuestion(p: PendingFile): string {
|
||||
export function logMeetRegistration(event: string, data: Record<string, unknown>): void {
|
||||
logInfo(`/meet 등록 게이트: ${event}`, data);
|
||||
}
|
||||
|
||||
// ── 회의 용어집 (반복 회의의 STT 보정 정확도용) ─────────────────────────────
|
||||
// meetPrompt 는 메타데이터를 "용어집 역할"로 쓴다 — 매번 수동 입력하는 대신,
|
||||
// 이전 /meet 실행에서 나온 인명(담당)·사용자 입력 메타데이터 용어를 워크스페이스에
|
||||
// 누적하고 다음 실행 때 자동 주입한다. (.astra/meet_glossary.json)
|
||||
const GLOSSARY_REL = 'meet_glossary.json';
|
||||
const GLOSSARY_MAX = 120;
|
||||
type Glossary = { terms: string[]; updatedAt: string };
|
||||
|
||||
export function loadGlossaryTerms(): string[] {
|
||||
return (readJson<Glossary>(GLOSSARY_REL)?.terms || []).filter(t => typeof t === 'string' && t.trim());
|
||||
}
|
||||
|
||||
/** 담당자 이름·메타데이터에서 뽑은 용어를 용어집에 누적 (중복 제거, 상한 유지). */
|
||||
export function updateGlossary(newTerms: string[]): void {
|
||||
const cleaned = newTerms
|
||||
.map(t => (t || '').trim())
|
||||
.filter(t => t.length >= 2 && t.length <= 30 && !/미지정|없음|확인|불명/.test(t));
|
||||
if (!cleaned.length) return;
|
||||
const cur = loadGlossaryTerms();
|
||||
const set = new Set(cur);
|
||||
for (const t of cleaned) set.add(t);
|
||||
// 상한 초과 시 오래된 것부터 제거 (Set 삽입 순서 = 누적 순서)
|
||||
const all = [...set];
|
||||
const terms = all.length > GLOSSARY_MAX ? all.slice(all.length - GLOSSARY_MAX) : all;
|
||||
writeJson(GLOSSARY_REL, { terms, updatedAt: new Date().toISOString() } satisfies Glossary);
|
||||
}
|
||||
|
||||
/** 사용자 메타데이터 입력에서 용어 후보 추출 — 쉼표/슬래시/공백 구분 토큰 중 고유명사형. */
|
||||
export function extractGlossaryCandidates(metadata: string): string[] {
|
||||
return (metadata || '')
|
||||
.split(/[,,/·;\n]+/)
|
||||
.map(t => t.replace(/^[\s::\-–·]+|[\s::\-–·]+$/g, ''))
|
||||
.filter(t => t.length >= 2 && t.length <= 30 && !/^\d+$/.test(t));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user