fix: v2.2.202 — 기업모드 Intent Alignment 가 일반 채팅 컨텍스트 무시하던 버그
증상: 일반 채팅에서 프로젝트·요구사항을 충분히 논의한 뒤 기업모드 전환 후 후속 작업을 요청하면 "추가 정보 필요 — 맥락/목표/기준/형식" 화면이 떠 사용자에게 *방금 말한 내용을 다시 묻는* 느낌을 줌. 원인: - Intent Classifier 는 prior chat 컨텍스트(previousBrief/Tail) 받음 → follow-up 분기 정확 - Intent Alignment (clarification 화면 만드는 분석기) 는 IntentAnalysisInput 인터페이스에 chat history 필드가 없음 → 오직 현재 사용자 메시지만 봄 - 결과: 모드 전환 직후 첫 라운드 분석기는 사용자가 이전에 일반 채팅에서 한 모든 설명을 못 봄 → context 빈칸 → openQuestions 에 "맥락은?" 추가 Fix: - IntentAnalysisInput 에 priorChatSummary?: string 필드 추가 - 시스템 프롬프트에 *모드 전환 시 context 우선 추출* 규칙 추가 — 일반 채팅에서 명시된 항목은 추측이 아니라 명시된 사실로 취급 - _buildUserMessage() 가 [모드 전환 직전 일반 채팅 요약] 블록을 user message 상단에 주입 - sidebarProvider.ts 호출 지점에서 this._agent.getHistory() → 최근 10 turn (!internal) 추출 → "role: content" 한 줄씩, content 200자 cap - 후속 라운드 (previousContract 있음) 면 history 중복 첨부 안 함 — 이미 contract 에 흡수됨 효과: 일반 채팅 → 기업모드 전환 시 분석기가 prior chat 의 context/goal/criteria 를 직접 추출. redundant "맥락/목표/기준/형식 다시 말해 주세요" 질문 사라짐. 첫 라운드부터 confidence=high 가능 → 바로 본 작업 진행. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,16 @@ export interface IntentAnalysisInput {
|
||||
* goal/format을 그쪽 능력에 맞춰 추출할 수 있다.
|
||||
*/
|
||||
availableRoleCategories?: string[];
|
||||
/**
|
||||
* 모드 전환 *직전* 의 일반 채팅 히스토리 요약. 사용자가 일반 채팅에서
|
||||
* 프로젝트·맥락·요구를 충분히 논의한 뒤 기업모드로 전환해 *후속 작업* 을
|
||||
* 요청한 경우, 분석기가 이를 보면 context/goal/criteria 를 이미 도출
|
||||
* 가능 — 중복 질문(맥락/목표/기준/형식) 을 안 던진다.
|
||||
*
|
||||
* 형식: 최근 N(기본 10) 턴의 `role: content` 한 줄씩, 각 content 200자 cap.
|
||||
* 없으면 undefined (첫 진입 / 모드 토글 없는 케이스).
|
||||
*/
|
||||
priorChatSummary?: string;
|
||||
}
|
||||
|
||||
const SYSTEM_PROMPT = `당신은 "1인 기업 모드"의 *요청 분석가*입니다. 사용자의 자연어 요청을 받아 그것을 실행 가능한 작업 조건 5가지(C-G-C-F-Q)로 정리합니다.
|
||||
@@ -73,6 +83,8 @@ const SYSTEM_PROMPT = `당신은 "1인 기업 모드"의 *요청 분석가*입
|
||||
|
||||
⚠️ 추측 금지. 사용자의 한 줄 + 컨텍스트에서 *직접 추론*되지 않는 정보는 채우지 마세요. 빈 칸은 그대로 두고 그 자리에 대응하는 질문을 openQuestions에 넣으세요.
|
||||
|
||||
⚠️ **[모드 전환 시 context 우선 추출]**: 입력에 \`[모드 전환 직전 일반 채팅 요약]\` 블록이 있으면, 그것을 **사용자의 한 줄과 같은 권위로** 취급하세요. 거기서 context/goal/criteria/format 을 *직접 추출* 한 뒤, 그래도 빠진 항목만 openQuestions 에 넣으세요. 사용자가 이미 일반 채팅에서 충분히 설명한 내용을 다시 물어보면 안 됩니다 — 일반 채팅에서 *명시적으로 언급* 된 항목은 추측이 아니라 **명시된 사실** 입니다.
|
||||
|
||||
confidence는 다음 기준으로 자체 판정:
|
||||
- "high" : C·G·C·F 4개 모두 prompt에서 직접 추론 가능. openQuestions = [] 가능.
|
||||
- "medium" : 대체로 명확하지만 1~2개 항목에서 합리적 가정 필요. 추가 질문 1~2개.
|
||||
@@ -95,6 +107,18 @@ function _buildUserMessage(input: IntentAnalysisInput): string {
|
||||
const lines: string[] = [];
|
||||
lines.push('[사용자 원본 요청]');
|
||||
lines.push(input.userOriginalPrompt);
|
||||
// 모드 전환 직전 일반 채팅 요약 — 분석기가 context/goal/criteria 를 *여기서 먼저 추출*.
|
||||
// 사용자가 일반 채팅에서 이미 설명한 항목을 openQuestions 에 다시 넣지 못하게 막음.
|
||||
if (input.priorChatSummary && input.priorChatSummary.trim()) {
|
||||
lines.push('');
|
||||
lines.push('[모드 전환 직전 일반 채팅 요약]');
|
||||
lines.push('아래는 사용자가 *기업모드 전환 전* 일반 채팅에서 같은 주제로 나눈 대화입니다.');
|
||||
lines.push('여기에 명시된 context/goal/criteria/format 은 *사용자가 이미 말한 사실* 로 취급하여');
|
||||
lines.push('contract 의 해당 슬롯을 채우고, 다시 묻지 마세요.');
|
||||
lines.push('---');
|
||||
lines.push(input.priorChatSummary);
|
||||
lines.push('---');
|
||||
}
|
||||
if (input.activePipelineName) {
|
||||
lines.push('');
|
||||
lines.push(`(활성 파이프라인) "${input.activePipelineName}"`);
|
||||
|
||||
@@ -1890,6 +1890,25 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
|
||||
// Pixel Office: 분석 시작 표시 (LLM 콜 직전).
|
||||
try { this.pixelOfficeOnAlignmentStart(opts.userPrompt); } catch { /* noop */ }
|
||||
|
||||
// 모드 전환 직전 일반 채팅 요약 — Intent Alignment 가 *이미 논의된 맥락* 을
|
||||
// 재질문하지 않도록. 후속 라운드(previousContract 있음) 면 chatHistory 가
|
||||
// 이미 contract 에 흡수됐으므로 중복 첨부 안 함.
|
||||
let priorChatSummary: string | undefined;
|
||||
if (!opts.previousContract) {
|
||||
try {
|
||||
const history = this._agent.getHistory();
|
||||
const visible = history.filter((m) => !m.internal && (m.role === 'user' || m.role === 'assistant'));
|
||||
// 마지막 N=10 턴, content 200자 cap — 토큰 폭주 방지.
|
||||
const recent = visible.slice(-10);
|
||||
if (recent.length > 0) {
|
||||
priorChatSummary = recent
|
||||
.map((m) => `${m.role}: ${String(m.content || '').replace(/\s+/g, ' ').trim().slice(0, 200)}`)
|
||||
.join('\n');
|
||||
}
|
||||
} catch { /* history 못 가져와도 alignment 자체는 동작 */ }
|
||||
}
|
||||
|
||||
const analysis = await analyzeIntent(
|
||||
new AIService(),
|
||||
{
|
||||
@@ -1898,6 +1917,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
previousContract: opts.previousContract,
|
||||
activePipelineName: activePipeline?.name,
|
||||
availableRoleCategories: extractActiveRoleCategories(state),
|
||||
priorChatSummary,
|
||||
},
|
||||
// 분류기와 같은 작은 모델을 재사용 — 이 단계도 빠르고 가벼워야 함.
|
||||
{ model: cfg.companyIntentClassifierModel || cfg.defaultModel },
|
||||
|
||||
Reference in New Issue
Block a user