fix: v2.2.203 — 기업모드 dev-impl 빈 깡통 99% 버그 (hollow check 기본 ON)

증상: 사용자가 기획서 + 폴더 주고 "여기 개발해줘" 요청 → ASTRA 가 파일 만들고
"개발 완료" 보고 → 실제 파일을 열면 class/함수 본문이 비어 있음
(def foo(): pass · 빈 class · imports only). 99% 확률 재발.

원인:
- 안전망 이미 존재 (selfReflectorHollow.ts 가 정규식으로 빈 깡통 감지)
- BUT 두 개 config 모두 OFF — selfReflectorEnabled (Phase A) +
  selfReflectorExternalEnabled (Phase B)
- Phase A 켜면 [Self-Reflector Check] 블록이 답변에 노출 (UX 부작용),
  Phase B 는 +1 LLM 호출 비용 — 부작용 때문에 기본 OFF
- 결과: 다수 사용자가 안전망 전혀 없는 상태로 코드 작성 → 빈 깡통 통과

Fix 3종:

1. Hollow check 를 selfReflector 와 분리 — 신규 설정 2개:
   - g1nation.hollowCheck.enabled (boolean, 기본 ON) — action-tag 있는 모든
     응답에 무조건 hollow 스캔. LLM 호출 0.
   - g1nation.hollowCheck.autoRetry (boolean, 기본 ON) — 검출 시 1회 자동
     재작업. Phase B 와 분리.
   - dispatcher.ts 게이트 조건 교체

2. dev-impl 프롬프트 강화 (pipelineTemplates.ts) — [빈 깡통 금지] 5개 규칙:
   - 파일은 하나씩 생성, 모든 함수 본문 완전 구현 후 다음 파일로
   - 금지 패턴 명시: pass · ... · NotImplementedError · # TODO · 빈 class
   - 인터페이스/추상 메서드만 빈 본문 OK
   - 각 파일 생성 직후 자가 검증
   - 최종 요약에 파일별 핵심 동작 한 줄씩

3. 기본값 변경 — 사용자 행동 없이 안전망 작동. 옛 selfReflector Phase A/B 는
   그대로 OFF (UX 부작용 보존).

예상 효과: ~70-85% stub 감소. 남은 ~15% (작은 모델 attention 한계 / 큰 프로젝트)
는 per-file 순차 생성으로 v2.2.204+ 검토.

모델 한계 vs 로직 fix: 대부분 로직 fix 가능. 매우 작은 모델 (≤4B) 은 한계 더
빠름 — 더 큰 모델 (gemma-12b, qwen-32b) 권장.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 11:52:12 +09:00
parent c27cd823a9
commit ebfce17b03
14 changed files with 120 additions and 39 deletions
+18
View File
@@ -229,6 +229,22 @@ export interface IAgentConfig {
* 느껴진다면 꺼둘 수 있다.
*/
selfReflectorEnabled: boolean;
/**
* Hollow Code Check — `<create_file>` 등 action-tag 로 생성된 파일이 *빈 깡통*
* (empty class, stub-only function body, imports-only file 등) 인지 정규식으로
* 스캔. LLM 호출 0 — 휴리스틱 only.
*
* 옛 구조: selfReflector Phase A 가 켜져 있어야 동작 → 다수 사용자가 안전망 부재.
* 새 구조: 독립 toggle, *기본 ON*. action-tag 가 있는 응답에 무조건 실행. 검출 시
* actionReport 에 "⚠️ 빈 깡통" 라인 추가 (LLM 콜 없음 — 비용 0).
*/
hollowCheckEnabled: boolean;
/**
* Hollow 감지 시 1회 자동 재작업. Phase B (selfReflectorExternalEnabled) 와 분리 —
* dev-impl 같은 stage 당 정해진 단발 추가 호출 (예측 가능). 기본 ON.
* 재작업 결과도 hollow 한 번 더 검사 → 재차 hollow 면 사용자 경고 (무한 루프 방지).
*/
hollowCheckAutoRetry: boolean;
/**
* Self-Reflector Phase B — 회사 모드 specialist 응답 직후 *분리된 콘텍스트*
* 에서 LLM 한 번 더 호출해 외부 시각으로 검증. 실패 시 1회 retry. 비용
@@ -461,6 +477,8 @@ export function getConfig(): IAgentConfig {
// 둘이 어긋나면 안 되므로 변경 시 양쪽 같이 갱신.
companyIntentAlignmentMaxRounds: Math.max(1, Math.min(5, cfg.get<number>('company.intentAlignmentMaxRounds', 3))),
selfReflectorEnabled: cfg.get<boolean>('selfReflector.enabled', false),
hollowCheckEnabled: cfg.get<boolean>('hollowCheck.enabled', true),
hollowCheckAutoRetry: cfg.get<boolean>('hollowCheck.autoRetry', true),
selfReflectorExternalEnabled: cfg.get<boolean>('selfReflector.externalVerification', false),
selfReflectorExecutionEnabled: cfg.get<boolean>('selfReflector.executionVerification', false),
companyPixelOfficeEnabled: cfg.get<boolean>('company.pixelOffice.enabled', true),
+12 -13
View File
@@ -769,24 +769,23 @@ async function _dispatchOne(
});
}
// ── Self-Reflector Hollow Code Check (휴리스틱, LLM 콜 0) ──
// ── Hollow Code Check (휴리스틱, LLM 콜 0) ──
// Phase C(syntax)가 잡지 못하는 *빈 깡통* 패턴을 정규식으로 잡는다.
// hollow 발견 → 1) actionReport에 ❌ 라인 추가 2) verifierIssues에
// 합류시켜 Phase B retry 트리거 (혹은 Phase B OFF면 사용자에게
// 경고만 표시). 작은 LLM이 가장 자주 만드는 실패 패턴이라
// selfReflectorEnabled가 켜져 있으면 *조건부 자동 활성화*.
// v2.2.203: Phase A(selfReflectorEnabled) 와 분리 — 독립 toggle
// (hollowCheck.enabled, 기본 ON). action-tag 있는 모든 응답에 무조건 실행.
// Retry 도 Phase B 와 분리 (hollowCheck.autoRetry, 기본 ON) — 다수 사용자가
// 자동 안전망 받도록.
try {
const cfgRuntime = getDispatcherConfig();
if (cfgRuntime.selfReflectorEnabled && actionReport.length > 0) {
if (cfgRuntime.hollowCheckEnabled && actionReport.length > 0) {
const projectRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || '';
if (projectRoot) {
const hollowRes = verifyHollow(actionReport, projectRoot);
if (hollowRes.hasHollow) {
actionReport = [...actionReport, ...hollowRes.extraLines];
// verifier가 켜져 있고 아직 retry 안 했다면 hollow를 issue로
// 격상해서 자동 재작업 트리거. 켜져 있지 않으면 사용자에게
// 경고만 노출(이미 actionReport에 들어감).
if (cfgRuntime.selfReflectorExternalEnabled && verifierIssues.length === 0) {
// hollowCheck.autoRetry 가 켜져 있고 아직 retry 안 했다면 hollow를
// 자동 재작업 트리거. (Phase B 와 무관 — 빈 깡통은 늘 잡아야)
if (cfgRuntime.hollowCheckAutoRetry && verifierIssues.length === 0) {
verifierIssues = hollowRes.hollowReasons.map((r) => `빈 깡통: ${r}`);
verifierSummary = `Hollow code 감지 — 자동 재시도 트리거`;
// 같은 specialist 1회 retry: 빈 깡통 지적을 task 앞에 prepend.
@@ -815,9 +814,9 @@ async function _dispatchOne(
agentId, error: e?.message ?? String(e),
});
}
} else if (!cfgRuntime.selfReflectorExternalEnabled) {
// verifier OFF — 사용자에게 경고만.
verifierSummary = `⚠️ Hollow code 감지 — externalVerification 켜면 자동 재시도`;
} else if (!cfgRuntime.hollowCheckAutoRetry) {
// auto-retry OFF — 사용자에게 경고만.
verifierSummary = `⚠️ Hollow code 감지 — hollowCheck.autoRetry 켜면 자동 재시도`;
}
}
}
+8 -1
View File
@@ -166,7 +166,14 @@ const FULL_PRODUCT_DEV: PipelineTemplate = {
instructionTemplate:
'설계: {{stage.dev-design}}\n기획서: {{stage.plan-final}}\n\n' +
'설계대로 *실제 코드를 작성*하세요. 반드시 ConnectAI 액션 태그(`<create_file>`, `<edit_file>`, `<run_command>`)를 사용해 디스크에 떨어지도록.\n' +
'코드 블록만 보여주고 "생성 완료"라고 말하면 디스크엔 아무것도 안 만들어집니다. 작성 후 자가 검증 한 줄.',
'코드 블록만 보여주고 "생성 완료"라고 말하면 디스크엔 아무것도 안 만들어집니다.\n\n' +
'[빈 깡통 금지 — 가장 자주 발생하는 실패 패턴]\n' +
'1. 파일은 **하나씩** 생성. 한 파일 안의 **모든 함수·메서드·클래스 본문을 완전히 구현** 후 다음 파일로.\n' +
'2. **금지 패턴**: `def foo(): pass` · `def foo(): ...` · `def foo(): raise NotImplementedError` · `def foo(): # TODO` · 빈 class 본문 (`class Foo: pass`) · imports 만 있는 파일.\n' +
'3. 함수 본문이 정말 비어도 되는 경우는 *인터페이스/추상 메서드* 만. 나머지는 **무조건 동작하는 코드** 작성. 단순화는 OK, 빈 깡통은 NO.\n' +
'4. 각 파일 생성 직후 한 줄 자가 검증: "이 파일이 정말 동작하나? 빈 본문 없나?". 없으면 보강 후 `<edit_file>` 로 재기록.\n' +
'5. 모든 파일 생성 끝나면 *최종 요약* 에 (a) 생성된 파일 수 (b) 각 파일의 핵심 함수 동작 한 줄씩 명시.\n\n' +
'⚠️ "구조만 잡고 추후 구현" 패턴은 즉시 검출돼 자동 재작업이 트리거됩니다. 처음부터 본문까지 완전 구현하세요.',
},
{
id: 'qa',