refactor: v2.2.195-201 — slashRouter god-file 해체 (–95%) + 인프라 5개 추출
아키텍처 감사 결과 HIGH 2건 + MED 2건 + LOW 1건 — 7 라운드 정리 시리즈.
기능 변경 없음, 순수 구조 정리.
**slashRouter.ts: 4,174 → 201줄 (–3,973, –95%)**
**agent.ts: 1,617 → 1,551줄 (–66, –4%)**
v2.2.195: eventSourcedStore + SystemPromptBlock registry
- createEventStore<E>(opts) — 4 store (customers/hire/runway/feedback) I/O 240줄 중복 제거
- _turnCtx 5 named string field → 1 Map<string, string> (새 verification block 추가 25곳→1곳)
- buildAstraModeSystemPrompt: 5 ternary gate + 5 위치 → 1 for-loop join
v2.2.196: trackers cluster split
- src/features/teamops/handlers/_shared.ts (fmtKrw/parseAmount/daysUntil/parseTaskOwner/stageEmoji/STAGE_ORDER/TERMINAL_STAGES)
- src/features/teamops/handlers/trackers.ts (runway/customers/hire)
- src/features/teamops/handlers/index.ts (barrel)
- extension.ts 에 side-effect import (순환 import 회피)
v2.2.197: mtimeFileCache + PostAnswerHook registry
- src/lib/mtimeFileCache.ts — createMtimeFileCache<T>(name, parse) (terminologyBlock + termValidator 2-cache invariant 자동화)
- src/agent/postAnswerHooks/{types,index}.ts — Devil/SelfCheck/TermValidator 3 _maybeX method → 1 runPostAnswerHooks(ctx) loop
- agent.ts –66줄
v2.2.198: dashboards cluster split
- src/features/teamops/handlers/dashboards.ts (morning/evening/cohort/weekly)
v2.2.199: coordination + communication clusters split
- src/features/teamops/handlers/coordination.ts (task/decisions/onesie/blocked/standup)
- src/features/teamops/handlers/communication.ts (draft/feedback)
- callLmSynthesis export 노출 (communication 이 사용)
- 옛 parseTaskOwner local 정의 삭제 (_shared.ts 사용)
v2.2.200: system cluster split
- src/features/system/handlers.ts (memory/glossary/help)
v2.2.201: datacollect cluster split + LLM 인프라 추출
- src/features/datacollect/handlers.ts (research/benchmark/youtube/blog/wikify/meet)
- src/features/datacollect/llm.ts (callLmSynthesis + repairKoreanGlitches + bridgeErrorRemedy)
- slashRouter import 4개로 축소: vscode/logInfo/getBridgeBaseUrl/bridgeErrorRemedy
**최종 slashRouter (201줄):**
- REGISTRY Map + registerSlashCommand/listSlashCommands/isSlashCommand
- handleSlashCommand (dispatcher + 에러 처리)
- Webview interface + chunk helper
- getRecentSlashCommands ring buffer (actionability scoring 용)
**미래 부담 감소 metrics:**
- 새 슬래시 명령: god-file 끝에 함수 + register → 1 파일 + 1 register call
- 새 verification block: 5곳 편집 → 1 set call
- 새 event store: 60줄 boilerplate → createEventStore 한 줄
- 새 post-answer hook: 3 step → 1 push
- 새 mtime cache: Map + invariant 관리 → createMtimeFileCache 한 줄
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+24
-106
@@ -164,8 +164,7 @@ import { buildAgentModeSystemPrompt } from './agent/handlePrompt/buildAgentModeS
|
||||
import { buildAstraModeSystemPrompt } from './agent/handlePrompt/buildAstraModeSystemPrompt';
|
||||
import { computeBudgetedRequest } from './agent/handlePrompt/computeBudgetedRequest';
|
||||
import { processFinalAnswer } from './agent/handlePrompt/processFinalAnswer';
|
||||
import { postHocSelfCheck, formatSelfCheckFooter, DEFAULT_SELF_CHECK_OPTIONS } from './agent/postHocSelfCheck';
|
||||
import { validateTermUsage, formatTermValidatorFooter } from './agent/termValidator';
|
||||
import { runPostAnswerHooks } from './agent/postAnswerHooks';
|
||||
import { applyAutoContinuation } from './agent/handlePrompt/applyAutoContinuation';
|
||||
|
||||
export interface ChatMessage {
|
||||
@@ -287,28 +286,26 @@ export class AgentExecutor {
|
||||
lessons: string[];
|
||||
/** 이번 turn 에 결정된 Knowledge Mix — scope footer 표시용. */
|
||||
knowledgeMix: ResolvedKnowledgeMix | null;
|
||||
/** [CONFLICT WARNINGS] 시스템 프롬프트 블록 — 검색된 출처에서 충돌 신호 감지 시. */
|
||||
conflictWarnings: string;
|
||||
/** [VERIFICATION CHECKLIST] Chain-of-Verification 블록 — 그라운딩 자기 검증 지시. */
|
||||
coveChecklist: string;
|
||||
/** [INTENT CLARIFICATION GUIDANCE] — 모호 질의 감지 시 역질문 우선 지시. */
|
||||
intentClarification: string;
|
||||
/** [CITATION TRACE] — 답변 끝에 사용 출처 한 줄 정리 지시. */
|
||||
citationTrace: string;
|
||||
/**
|
||||
* 동적 시스템 프롬프트 블록 레지스트리 — turn 마다 memoryContext 가 채우고
|
||||
* buildAstraModeSystemPrompt 가 iterate 해서 prompt 에 주입.
|
||||
*
|
||||
* 옛 구조: conflictWarnings/coveChecklist/intentClarification/citationTrace/terminology
|
||||
* 5개 named field + 5개 reset + 5개 named param + 5개 ternary gate (총 25곳 edit).
|
||||
* 새 구조: 1 Map. 새 블록 추가 = 1 set call.
|
||||
*
|
||||
* Key 는 디버그·재정의용 id (예: 'conflict-warnings'). Value 는 이미 빌드된
|
||||
* 블록 본문 — 빈 문자열이면 주입 안 함. casual mode 게이팅은 호출자가 처리.
|
||||
*/
|
||||
dynamicBlocks: Map<string, string>;
|
||||
/** Self-check 용 — selected chunks 의 (title, content) 요약. memoryContext 가 채움. */
|
||||
selfCheckSources: Array<{ title: string; excerpt: string }>;
|
||||
/** [TERMINOLOGY DICTIONARY] — 사용자 편집 글로서리 + Term Check 지침. */
|
||||
terminology: string;
|
||||
} = {
|
||||
retrieval: null,
|
||||
lessons: [],
|
||||
knowledgeMix: null,
|
||||
conflictWarnings: '',
|
||||
coveChecklist: '',
|
||||
intentClarification: '',
|
||||
citationTrace: '',
|
||||
dynamicBlocks: new Map(),
|
||||
selfCheckSources: [],
|
||||
terminology: '',
|
||||
};
|
||||
|
||||
/** Per-turn state 일괄 정리. turn 시작/abort/load session 시 호출. */
|
||||
@@ -316,12 +313,8 @@ export class AgentExecutor {
|
||||
this._turnCtx.retrieval = null;
|
||||
this._turnCtx.lessons = [];
|
||||
this._turnCtx.knowledgeMix = null;
|
||||
this._turnCtx.conflictWarnings = '';
|
||||
this._turnCtx.coveChecklist = '';
|
||||
this._turnCtx.intentClarification = '';
|
||||
this._turnCtx.citationTrace = '';
|
||||
this._turnCtx.dynamicBlocks.clear();
|
||||
this._turnCtx.selfCheckSources = [];
|
||||
this._turnCtx.terminology = '';
|
||||
}
|
||||
|
||||
private readonly options: AgentExecutorOptions;
|
||||
@@ -673,11 +666,7 @@ export class AgentExecutor {
|
||||
isCasualConversation,
|
||||
localPathContext,
|
||||
knowledgeMix: this._turnCtx.knowledgeMix,
|
||||
conflictWarningsCtx: this._turnCtx.conflictWarnings,
|
||||
coveChecklistCtx: this._turnCtx.coveChecklist,
|
||||
intentClarificationCtx: this._turnCtx.intentClarification,
|
||||
citationTraceCtx: this._turnCtx.citationTrace,
|
||||
terminologyCtx: this._turnCtx.terminology,
|
||||
dynamicBlocks: this._turnCtx.dynamicBlocks,
|
||||
});
|
||||
// Context budget computation → src/agent/handlePrompt/computeBudgetedRequest.ts
|
||||
const imageCount = (reqMessages as any[])
|
||||
@@ -1221,25 +1210,21 @@ export class AgentExecutor {
|
||||
memoryLayers: this._turnCtx.retrieval?.usedMemoryLayers ?? [],
|
||||
note: `continuations=${continuationCount} historyDropped=${reqMessages.length - budgetedHistory.length}`,
|
||||
});
|
||||
// ── Devil Agent (도현) — 비활성 시 silent skip. 활성 시 별도 LLM 호출로 반박 카드 emit. ──
|
||||
// 비동기 — main turn 완료에 영향 없음. 실패해도 main 답변은 보존됨.
|
||||
void this._maybeEmitDevilRebuttal({
|
||||
// ── Post-answer hooks (v2.2.197) — Devil + SelfCheck + TermValidator 통합 레지스트리. ──
|
||||
// 새 hook 추가 = `src/agent/postAnswerHooks/index.ts` 에 한 객체 push.
|
||||
// 안전 fallback 내장 — 한 hook 실패가 다른 hook / main turn 영향 없음.
|
||||
runPostAnswerHooks({
|
||||
userPrompt: prompt || '',
|
||||
assistantAnswer: finalAssistantContent,
|
||||
baseUrl: ollamaUrl,
|
||||
modelName: actualModel,
|
||||
contextLength: ctxLimits.contextLength,
|
||||
engine,
|
||||
selfCheckSources: this._turnCtx.selfCheckSources,
|
||||
callNonStreaming: (p) => this.callNonStreaming(p),
|
||||
getAbortSignal: () => this.abortController?.signal,
|
||||
getWebview: () => this.webview,
|
||||
});
|
||||
// ── Post-hoc Self-Check (v2.2.191) — 별도 LLM 호출 1회로 답변 사후 검증. ──
|
||||
// 비동기 — Devil 과 동일 패턴. 결과를 footer 한 줄로 append.
|
||||
void this._maybePostHocSelfCheck({
|
||||
userPrompt: prompt || '',
|
||||
assistantAnswer: finalAssistantContent,
|
||||
});
|
||||
// ── Term Validator (v2.2.194) — 결정론적 정규식 스캔. LLM 호출 없음, 즉시 실행. ──
|
||||
// 글로서리 forbidden 단어가 답변에 등장 시 footer flag. 위반 없으면 ✓.
|
||||
this._maybeRunTermValidator(finalAssistantContent);
|
||||
} else {
|
||||
this.webview.postMessage({ type: 'streamChunk', value: finalAssistantContent });
|
||||
}
|
||||
@@ -1394,73 +1379,6 @@ export class AgentExecutor {
|
||||
* "lock() request could not be registered" error this method is helping
|
||||
* to avoid.
|
||||
*/
|
||||
/**
|
||||
* Devil Agent 반박 emit — main turn 완료 직후 호출 (fire-and-forget).
|
||||
* 비활성 시 즉시 return. 활성 시 별도 LLM 호출 (callNonStreaming 재사용) 로 짧은 비판 생성.
|
||||
* 성공 시 webview 에 'devilRebuttal' 메시지 전송 → UI 가 카드로 렌더.
|
||||
*/
|
||||
private async _maybeEmitDevilRebuttal(opts: {
|
||||
userPrompt: string;
|
||||
assistantAnswer: string;
|
||||
baseUrl: string;
|
||||
modelName: string;
|
||||
contextLength: number;
|
||||
engine: 'lmstudio' | 'ollama';
|
||||
}): Promise<void> {
|
||||
return maybeEmitDevilRebuttalFn({
|
||||
getAbortSignal: () => this.abortController?.signal,
|
||||
callNonStreaming: (p) => this.callNonStreaming(p),
|
||||
getWebview: () => this.webview,
|
||||
}, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-hoc Self-Check — 답변 *완료 후* LLM 1회 호출로 3가지 평가
|
||||
* (사용자 질의 직접 답함 / 출처 그라운딩 / 논리 모순). 비동기 — main turn 에 영향 없음.
|
||||
* 기본 OFF (g1nation.selfCheckEnabled). 결과는 footer 한 줄로 streamChunk append.
|
||||
*/
|
||||
private async _maybePostHocSelfCheck(opts: {
|
||||
userPrompt: string;
|
||||
assistantAnswer: string;
|
||||
}): Promise<void> {
|
||||
try {
|
||||
const cfg = getConfig();
|
||||
if (!cfg.selfCheckEnabled) return;
|
||||
if (!opts.userPrompt.trim() || !opts.assistantAnswer.trim()) return;
|
||||
const model = (cfg.selfCheckModel || '').trim() || cfg.defaultModel;
|
||||
if (!model || !cfg.ollamaUrl) return;
|
||||
const sources = this._turnCtx.selfCheckSources || [];
|
||||
|
||||
const result = await postHocSelfCheck(opts.userPrompt, opts.assistantAnswer, sources, {
|
||||
ollamaUrl: cfg.ollamaUrl,
|
||||
model,
|
||||
timeoutMs: (cfg.selfCheckTimeoutSec ?? 6) * 1000,
|
||||
excerptLength: DEFAULT_SELF_CHECK_OPTIONS.excerptLength,
|
||||
maxSources: DEFAULT_SELF_CHECK_OPTIONS.maxSources,
|
||||
});
|
||||
|
||||
// 성공 실패 모두 footer 표시 — 사용자가 self-check 가 *돌고 있는지* 알 수 있게.
|
||||
// 실패 시 흐릿한 한 줄, 성공 시 평가 한 줄.
|
||||
const footer = formatSelfCheckFooter(result, model);
|
||||
this.webview?.postMessage({ type: 'streamChunk', value: footer });
|
||||
} catch { /* swallow — self-check never breaks the turn */ }
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-gen Term Validator — 글로서리 forbidden 단어가 답변에 등장하는지 결정론적 스캔.
|
||||
* LLM 호출 없음, 동기 실행 (수 ms). 글로서리 없거나 disabled 면 silent no-op.
|
||||
*/
|
||||
private _maybeRunTermValidator(answer: string): void {
|
||||
try {
|
||||
const cfg = getConfig();
|
||||
if (cfg.termValidatorEnabled === false) return;
|
||||
if (!answer || !answer.trim()) return;
|
||||
const result = validateTermUsage(answer, cfg.glossaryPath || '.astra/glossary.md');
|
||||
if (!result.ran || result.dictionarySize === 0) return; // 글로서리 없거나 비어 있음 → 표시 안 함
|
||||
const footer = formatTermValidatorFooter(result);
|
||||
if (footer) this.webview?.postMessage({ type: 'streamChunk', value: footer });
|
||||
} catch { /* swallow — validator never breaks the turn */ }
|
||||
}
|
||||
|
||||
private async callNonStreaming(params: {
|
||||
baseUrl: string;
|
||||
|
||||
Reference in New Issue
Block a user