feat(growth): Correction Loop — 정정 1회가 시스템 세 곳을 성장시키는 파이프라인 (v2.2.223)
self-evolving 고도화: 사용자 정정이 곧 Ground Truth — 정답지를 사람이 따로
만들지 않고, 태그 통계가 리포트에 머물지 않고 다음 턴의 행동을 바꾼다.
① 정정 감지·태깅 (correctionLoop.ts + agent.ts 훅, fire-and-forget):
- "아니야/틀렸어/~가 아니라" 류 정정 발화 감지 (보수적 — 추임새 "아니"는 제외)
- LLM 오류 분류 (사실오류/근거누락/맥락누락/추론오류/지시불이행/형식오류,
실패 시 휴리스틱 fallback) → error-tag frontmatter 레슨(lessons/) 저장
- 동시에 회귀 케이스 적립: .astra/eval/corrections.jsonl {질문, 틀린답, 정정}
② 주간 성장 사이클 확장 (1.5단계):
- 정정 회귀 테스트: 정정받은 질문을 두뇌 검색 컨텍스트와 함께 재실행 →
LLM-judge "같은 실수 반복?" 판정 → growth/regression-report.md (사이클당 ≤8건)
- 약점 프로필: 최근 60일 태그 통계 → growth/weakness-profile.json
③ 결핍의 행동화 (memoryContext):
- GROUNDING 약함 + agent scope 적용 중 → 전체 두뇌 1회 재검색 (scope 가
정답 문서를 가리는 경우 구제, 더 강한 근거일 때만 채택)
- 그래도 약함 → 학습 큐에 지식 공백 자동 proposed 등록 (질문 해시 중복 차단,
20건 폭주 방지, 승인은 사람 — Permission Based Learning 유지)
- 약점 프로필 → [자기검토] 블록 주입 (태그 2회 이상만): "너는 최근 X 정정을
N회 받았다 — <유형별 자기검토 지시>"
테스트 25건 추가 (감지 패턴·프로필 집계·큐 등록·영속화·fallback 분류).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,10 @@ import { runResearch, formatProposalMarkdown } from '../../intelligence/research
|
||||
import type { ExistingKnowledgeRef } from '../../intelligence/knowledgeValidation';
|
||||
import { loadQueue, saveQueue, mergeNeedsIntoQueue, formatQueueMarkdown, LEARNING_QUEUE_REL_PATH } from '../../intelligence/learningQueue';
|
||||
import { simpleChatCompletion } from '../../intelligence/llmCall';
|
||||
import {
|
||||
loadCorrectionCases, computeWeaknessProfile, saveWeaknessProfile,
|
||||
runRegressionCase, formatRegressionReport,
|
||||
} from '../../intelligence/correctionLoop';
|
||||
import { TelegramHttpClient } from '../../integrations/telegram/telegramClient';
|
||||
import { TELEGRAM_TOKEN_SECRET_KEY } from '../../extension/telegramCommands';
|
||||
|
||||
@@ -111,6 +115,49 @@ export async function runGrowthCycleOnce(context: vscode.ExtensionContext): Prom
|
||||
}
|
||||
} catch (e: any) { logError('성장 사이클: 검색 평가 실패.', { error: e?.message ?? String(e) }); }
|
||||
|
||||
// (1.5) Correction Loop — 정정 회귀 테스트 + 약점 프로필 갱신.
|
||||
// a. 약점 프로필: 최근 60일 정정 태그 통계 → weakness-profile.json
|
||||
// (memoryContext 가 다음 턴부터 자기검토 블록으로 주입 — 통계가 행동을 바꾼다)
|
||||
// b. 회귀: 최근 정정받은 질문을 두뇌 검색 컨텍스트와 함께 다시 풀어
|
||||
// "같은 실수 반복?" 을 LLM-judge 로 판정 — 정정이 곧 Ground Truth.
|
||||
try {
|
||||
const cases = loadCorrectionCases(brain.localBrainPath);
|
||||
if (cases.length > 0) {
|
||||
saveWeaknessProfile(brain.localBrainPath, computeWeaknessProfile(cases, now.getTime()));
|
||||
if (config.defaultModel && config.ollamaUrl) {
|
||||
const MAX_REGRESSION_PER_CYCLE = 8;
|
||||
const recent = cases.slice(-MAX_REGRESSION_PER_CYCLE);
|
||||
const orchestrator = new RetrievalOrchestrator();
|
||||
const allFiles = findBrainFiles(brain.localBrainPath);
|
||||
getBrainTokenIndex(brain.localBrainPath, allFiles);
|
||||
const llm = { baseUrl: config.ollamaUrl, model: config.defaultModel };
|
||||
const answerFn = async (question: string): Promise<string> => {
|
||||
// 실제 채팅과 동일하게 두뇌 근거를 주고 답하게 한다 (검색 없는 맨몸 답변은
|
||||
// 회귀 판정이 아니라 모델 암기 테스트가 되어버림).
|
||||
const refs = orchestrator.rankBrainForEval(question, brain, {
|
||||
limit: 5, chunkLevelRetrieval: config.chunkLevelRetrieval === true, chunkTargetChars: config.chunkTargetChars,
|
||||
}).slice(0, 5).map(r => {
|
||||
try { return `[${r.relativePath}]\n${fs.readFileSync(path.join(brain.localBrainPath, r.relativePath), 'utf8').slice(0, 1500)}`; }
|
||||
catch { return ''; }
|
||||
}).filter(Boolean).join('\n\n');
|
||||
return simpleChatCompletion(
|
||||
'두뇌 발췌를 근거로 간결히 답하라. 근거 없는 내용은 추정임을 명시하라.',
|
||||
`[두뇌 발췌]\n${refs || '(없음)'}\n\n[질문] ${question}`,
|
||||
{ ...llm, temperature: 0.2, maxTokens: 700, timeoutMs: 120000 },
|
||||
);
|
||||
};
|
||||
const results = [];
|
||||
for (const c of recent) results.push(await runRegressionCase(c, answerFn, llm));
|
||||
fs.writeFileSync(
|
||||
path.join(growthDir, 'regression-report.md'),
|
||||
formatRegressionReport(results, { dateStr: now.toLocaleString() }), 'utf8',
|
||||
);
|
||||
const repeated = results.filter(r => r.repeated === true).length;
|
||||
summary.push(`정정 회귀 ${results.length}건 중 재발 ${repeated}건${repeated > 0 ? ' ⚠️' : ''}`);
|
||||
}
|
||||
}
|
||||
} catch (e: any) { logError('성장 사이클: 정정 회귀/약점 프로필 실패.', { error: e?.message ?? String(e) }); }
|
||||
|
||||
// (2) 학습 큐 갱신 (Need Engine)
|
||||
let proposedCount = 0;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user