feat(growth): 자기 지식 자동화 + 회귀 경보 + 충돌 스캔 + Critic 게이트 확장 (v2.2.225)

[근본 수정 — 자가검증 구식 정보 버그]
ASTRA 자기 지식이 사람이 쓴 스냅샷(selfIdentity 블록·아키텍처 위키 문서)에
의존해 릴리스마다 구식이 됐고, 자기 개선 제안에서 이미 있는 기능을 신규
제안하는 오류가 반복됨. 수정:
- featureInventory.ts: 활성화 시 package.json(contributes.commands/configuration)
  + POST_ANSWER_HOOKS 레지스트리에서 "ASTRA 기능 인벤토리" 문서를 두뇌에
  기계 생성 (버전 변경 시 자동 재생성 — 사람이 갱신을 잊을 수 없는 구조).
- selfIdentity: "자기 기능 평가·제안 전 인벤토리와 대조, 기억 의존 서술 금지" 규칙.

[검증-피드백-재설계 파이프라인 보강 — 의견 검토 후 역제안 3건]
- A-1 골든셋 회귀 경보: 주간 사이클이 metrics-history.jsonl 적립 + 직전 대비
  recall@1 -10%p 또는 MRR -0.08 하락 시 ⚠️ + 그 기간 추가된 문서를 용의자로
  제시(regression-alert.md). 자동 롤백 없음 — 판단은 사람.
- A-2 신규 지식 충돌 스캔(conflictScan.ts): 일일(사전 소화와 같은 슬롯) 신규/변경
  문서를 기존 유사 top-2와 LLM 모순 비교 → 충돌 시 conflict-report.md +
  "기존 A vs 신규 B" 알림. 쓰기 주체(Datacollect/수동/Research) 무관 포착.
  런당 비교 ≤5건·최초 실행 24h 한정 (폭주 방지).
- A-3 criticLoop 게이트 확장: 업무 turn 외에도 "근거 약함(top<0.25) + 단정
  표현(수치·날짜·확언)" 트리거 추가. 전 답변 강제 2-pass 는 기각 — intrinsic
  self-correction 은 외부 신호 없이 효과 없음(arXiv 2310.01798).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 11:44:58 +09:00
parent 7584c6bbc1
commit b03a49bfc3
10 changed files with 369 additions and 8 deletions
+95
View File
@@ -0,0 +1,95 @@
/**
* ASTRA 기능 인벤토리 자동 생성 — 자기 지식 구식화의 근본 수정.
*
* 문제 (반복 발생한 심각 버그): ASTRA 의 자기 지식이 *사람이 쓴 스냅샷*
* (selfIdentity 블록, "ASTRA 자기 아키텍처" 위키 문서)에 의존했다. 스냅샷은
* 작성 시점에 박제되므로 릴리스마다 구식이 되고, ASTRA 는 자기 평가·개선 제안에서
* 이미 있는 기능을 "신규 제안"하거나(자가검증 구식 정보 사용) 없는 기능을 주장한다.
*
* 수정: 소스 오브 트루스(package.json 의 contributes.commands / configuration +
* POST_ANSWER_HOOKS 레지스트리)에서 인벤토리 문서를 **활성화 시점에 기계 생성**해
* 두뇌에 쓴다. 버전이 바뀌면 자동 재생성 — 사람이 갱신을 잊을 수 없는 구조.
* RAG 가 이 문서를 검색하므로 자기 기능 질문·자기 개선 제안의 근거가 항상 현행이다.
*/
import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';
import { getActiveBrainProfile, logError, logInfo } from '../utils';
import { POST_ANSWER_HOOKS } from '../agent/postAnswerHooks';
export const INVENTORY_FILE = 'ASTRA 기능 인벤토리.md';
const STATE_KEY = 'astra.inventoryVersion';
/** 답변 후 훅 id → 한 줄 설명. 레지스트리에 새 훅이 추가되면 id 는 자동 노출되고
* 설명만 여기 한 줄 추가 (설명 누락 시에도 id 는 문서에 나타난다 — 침묵 누락 방지). */
const HOOK_DESCRIPTIONS: Record<string, string> = {
'devil-rebuttal': 'Devil Agent 반박 카드 (활성화 시)',
'self-check': '답변 검증 LLM 호출 — 검색 근거 대조 (opt-in)',
'term-validator': '글로서리 금지 용어 결정론적 검사',
'requirement-coverage': '업무 필수 요소 커버리지 결정론적 검사',
'confidence-escalation': '확신도 산출 + 인간 검토 에스컬레이션 + Reflection 기록',
'critic-loop': '문제 신호(요소 누락/저확신/근거 약함+단정) 턴만 Critic LLM 검수 1회',
};
function stripMd(s: string): string {
return (s || '').replace(/\*\*|`|\[|\]/g, '').replace(/\s+/g, ' ').trim();
}
/** package.json contributes 에서 인벤토리 마크다운 생성 (순수 — 테스트 가능). */
export function buildInventoryMarkdown(pkg: any, nowIso: string): string {
const version = String(pkg?.version || '?');
const commands: Array<{ command: string; title: string }> = pkg?.contributes?.commands || [];
const configProps: Record<string, any> = pkg?.contributes?.configuration?.properties || {};
const lines: string[] = [
'---',
'type: reference',
'title: "ASTRA 기능 인벤토리 (자동 생성)"',
`version: "${version}"`,
`generated_at: ${nowIso}`,
'aliases: ["ASTRA 기능 목록", "ASTRA 명령어", "내 기능", "ASTRA가 할 수 있는 것", "기능 인벤토리", "ASTRA capabilities"]',
'---',
'',
`# ASTRA 기능 인벤토리 — v${version} (자동 생성)`,
'',
'> ⚙️ 이 문서는 Astra 활성화 시 **소스 코드(package.json)에서 기계 생성**됩니다 — 수동 편집 금지 (버전 변경 시 덮어씀).',
'> 자기 기능에 대한 질문·자기 개선 제안은 이 문서가 **항상 현행** 근거입니다. 서사적 설명은 [[ASTRA 자기 아키텍처]] 참고.',
'',
`## 사용자 명령 (${commands.length}개)`,
...commands.map(c => `- ${stripMd(c.title)}`),
'',
`## 설정으로 제어되는 동작·자동화 (${Object.keys(configProps).length}개)`,
...Object.entries(configProps).map(([key, prop]: [string, any]) => {
const desc = stripMd(String(prop?.markdownDescription || prop?.description || ''));
const first = desc.split(/(?<=[.다음])\s/)[0] || desc;
return `- \`${key.replace('g1nation.', '')}\`${first.slice(0, 160)}`;
}),
'',
`## 답변 후 자동 검증 훅 (${POST_ANSWER_HOOKS.length}단계 — 매 답변 후 실행)`,
...POST_ANSWER_HOOKS.map(h => `- \`${h.id}\`${HOOK_DESCRIPTIONS[h.id] || '(설명 미등록 — 코드 참조)'}`),
'',
];
return lines.join('\n');
}
/**
* 버전이 바뀌었거나 문서가 없으면 두뇌에 인벤토리를 재생성. 활성화 시 1회 호출
* (fire-and-forget — 실패해도 turn 에 영향 없음).
*/
export async function ensureFeatureInventory(context: vscode.ExtensionContext): Promise<void> {
try {
const brain = getActiveBrainProfile();
if (!brain?.localBrainPath || !fs.existsSync(brain.localBrainPath)) return;
const pkg = vscode.extensions.getExtension('g1nation.astra')?.packageJSON;
if (!pkg) return;
const version = String(pkg.version || '?');
const file = path.join(brain.localBrainPath, INVENTORY_FILE);
if (context.globalState.get<string>(STATE_KEY) === version && fs.existsSync(file)) return;
fs.writeFileSync(file, buildInventoryMarkdown(pkg, new Date().toISOString()), 'utf8');
await context.globalState.update(STATE_KEY, version);
logInfo('기능 인벤토리 재생성.', { version, file });
} catch (e: any) {
logError('기능 인벤토리 생성 실패 (무시).', { error: e?.message ?? String(e) });
}
}