feat(core): 자기지식 접지·웹 접근·환경 자가점검 — 할루시네이션 방어 3중화 (v2.2.247)

- Alignment Self-Learning: 자가 조사(질문 전 두뇌 검색)·사용자 답변 두뇌 저장·핵심메시지/프로젝트 컨텍스트 주입 (alignmentResearch.ts 신규)
- 웹 접근: Bridge 폴백 직접 fetch(webFetch.ts 신규)·<fetch_url> 액션 태그·기업 모드 URL/아키텍처 컨텍스트 주입·bare 도메인 인식
- 트리거 버그 수정: startsWith('/') 가 절대경로를 슬래시 명령으로 오인 — 분석 지시·URL 주입 전멸 원인 (회귀 테스트 고정)
- 자기지식 접지: 기능 인벤토리 lazy 재생성·학습 메커니즘 정본 섹션·[인벤토리 대조] 태그 의무화·결정론적 재구현 제안 정정 훅(featureConceptMap.ts 신규)
- 환경 자가점검: HealthCheckMonitor 에 Bridge/두뇌 볼륨/git 자격증명/확장 버전 검사 4종 + readyBar ⚠ 표시
- 두뇌 동기화: 원격 미설정 시 로컬 새로고침 모드·staged 기준 commit 판정·인증 부재 안내
- 기타: outputFormat 기본 markdown(제목 렌더 복구)·레슨/행동제약 truncation 보호 구역 이동·[CONTEXT] 절단 우선순위 재정렬

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
g1nation
2026-06-12 23:46:07 +09:00
parent 553aa0b134
commit a114d968b0
42 changed files with 4178 additions and 2088 deletions
+47 -16
View File
@@ -215,6 +215,30 @@ export interface DispatcherDeps {
* 'off'였던 경우.
*/
requirementContract?: RequirementContract;
/**
* 현재 워크스페이스의 아키텍처 컨텍스트 (architecture.md 요약, 호출자가
* 절단해 전달). 일반 챗은 이 컨텍스트를 자동 주입받지만 기업 모드는 빠져
* 있던 공백 수정 — specialist 가 "이 프로젝트가 뭐냐"를 추측하지 않게.
* contract 와 같은 4개 지점(planner/specialist/verifier/inspector)에 prepend.
*/
architectureContextBlock?: string;
/**
* 사용자 프롬프트에 포함된 URL 의 pre-fetch 결과 ([URL CONTENT] 블록).
* 기업 모드에는 continuation loop 가 없어 LLM 주도 fetch 결과를 재분석할
* 기회가 없으므로, dispatch 전에 호출자가 가져와 모든 에이전트에게 배포.
*/
webContextBlock?: string;
}
/**
* deps 의 자동 수집 컨텍스트(architecture + web)를 한 prefix 문자열로 합성.
* 둘 다 없으면 빈 문자열 — 기존 동작과 100% 동일.
*/
function buildExtraContextPrefix(deps: DispatcherDeps): string {
const blocks = [deps.architectureContextBlock, deps.webContextBlock]
.map((b) => (b || '').trim())
.filter(Boolean);
return blocks.length > 0 ? blocks.join('\n\n') : '';
}
/**
@@ -353,11 +377,13 @@ export async function runCompanyTurn(
};
} else {
const ceoModel = modelForAgent(state, 'ceo', deps.defaultModel);
const plannerExtraPrefix = buildExtraContextPrefix(deps);
const plannerContract = deps.requirementContract
? formatContractForPrompt(deps.requirementContract)
: undefined;
const plannerResult = await runCeoPlanner(deps.ai, userPrompt, state, {
model: ceoModel,
contractBlock: deps.requirementContract
? formatContractForPrompt(deps.requirementContract)
: undefined,
contractBlock: [plannerExtraPrefix, plannerContract].filter(Boolean).join('\n\n') || undefined,
signal: deps.signal,
});
plan = plannerResult.plan;
@@ -666,11 +692,13 @@ async function _dispatchOne(
peerOutputs,
brainContext, // injected as `[SECOND BRAIN CONTEXT]` block
knowledgeMixPolicy: policyBlock, // injected as `[KNOWLEDGE MIX POLICY]` block
// alignment 단계에서 도출된 contract가 deps에 있으면 모든 specialist의
// system 프롬프트에 같은 ground truth로 prepend된다. 추측 방지.
contractBlock: deps.requirementContract
? formatContractForPrompt(deps.requirementContract)
: undefined,
// alignment 단계에서 도출된 contract + 자동 수집 컨텍스트(architecture/web)가
// deps에 있으면 모든 specialist의 system 프롬프트에 같은 ground truth로
// prepend된다. 추측 방지.
contractBlock: [
buildExtraContextPrefix(deps),
deps.requirementContract ? formatContractForPrompt(deps.requirementContract) : '',
].filter(Boolean).join('\n\n') || undefined,
});
// 우선순위: stage > agent > global default.
const model = (stageModelOverride && stageModelOverride.trim())
@@ -696,9 +724,10 @@ async function _dispatchOne(
// 옛 dynamic import 8회 → 정적 import 로 promote (파일 상단). 모듈 자체 cyclic 없음.
const cfgRuntime = getDispatcherConfig();
if (cfgRuntime.selfReflectorExternalEnabled && rawResponse) {
const contractBlock = deps.requirementContract
? formatContractForPrompt(deps.requirementContract)
: undefined;
const contractBlock = [
buildExtraContextPrefix(deps),
deps.requirementContract ? formatContractForPrompt(deps.requirementContract) : '',
].filter(Boolean).join('\n\n') || undefined;
const verdict = await verifyResponse(deps.ai, {
task,
response: rawResponse,
@@ -1048,11 +1077,13 @@ async function _runReviewCycle(args: {
return { verdict: 'aborted', rounds: round - 1 };
}
const startedAt = Date.now();
// contract가 있으면 검수자/CEO 모두에게 같은 ground truth를 prepend —
// 검수 기준이 contract와 일치하는지를 정확히 평가할 수 있다.
const contractPrefix = deps.requirementContract
? formatContractForPrompt(deps.requirementContract) + '\n\n'
: '';
// contract + 자동 수집 컨텍스트가 있으면 검수자/CEO 모두에게 같은 ground
// truth를 prepend — 검수 기준이 contract와 일치하는지를 정확히 평가할 수 있다.
const contractPrefixParts = [
buildExtraContextPrefix(deps),
deps.requirementContract ? formatContractForPrompt(deps.requirementContract) : '',
].filter(Boolean).join('\n\n');
const contractPrefix = contractPrefixParts ? contractPrefixParts + '\n\n' : '';
// ── 1) 검수자 LLM 콜 ──
const inspectorSystem = contractPrefix + '당신은 산출물 *감리*입니다. 작업자의 결과물을 객관적으로 검토하고 한국어 마크다운으로 응답하세요.\n\n반드시 첫 줄을 다음 둘 중 하나로 시작:\n - ✅ 통과 — 산출물이 task 요구 + 위 contract의 criteria를 모두 충족하면.\n - ❌ 보완 필요: <구체 항목 한 줄> — contract 기준 누락·오류·약점이 있으면.\n\n그 다음 줄들에 *구체적인* 피드백 또는 칭찬 1~3줄. 모호한 일반론 금지.';