fix: proactive context compression for LM Studio small models - compress BEFORE fetch not after error
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"result": "Final report with inconsistencies. This should be long enough to pass validation.",
|
"result": "Final report with inconsistencies. This should be long enough to pass validation.",
|
||||||
"createdAt": 1778136474568,
|
"createdAt": 1778137049532,
|
||||||
"modelVersion": "unknown"
|
"modelVersion": "unknown"
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
"result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
||||||
"createdAt": 1778136474566,
|
"createdAt": 1778137049529,
|
||||||
"modelVersion": "unknown"
|
"modelVersion": "unknown"
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"result": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
|
"result": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
|
||||||
"createdAt": 1778136474564,
|
"createdAt": 1778137049527,
|
||||||
"modelVersion": "unknown"
|
"modelVersion": "unknown"
|
||||||
}
|
}
|
||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"result": "---\nid: stress_conflict_1778136474544\ndate: 2026-05-07T06:47:54.569Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\nFinal report with inconsistencies. This should be long enough to pass validation.\n\nFinal report with inconsistencies. This should be long enough to pass validation.\n\n---\n## 💡 Astra의 선제적 제안 (Proactive Next Actions)\nFinal report with inconsistencies. This should be long enough to pass validation.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `60/100` | ⚠️ Medium |\n| **Fallbacks Used** | `0` | ✅ None |\n| **Auto Retries** | `0` | ✅ Stable |\n| **Deduplication** | `0` | Standard |\n| **Processing Time** | `0.0s` | ✅ Fast |\n\n### 🔍 Decision Audit Trail\n- **[PLANNER]** 전략 수립 중... (18ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (3ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (2ms)\n",
|
"result": "---\nid: stress_conflict_1778137049510\ndate: 2026-05-07T06:57:29.533Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\nFinal report with inconsistencies. This should be long enough to pass validation.\n\nFinal report with inconsistencies. This should be long enough to pass validation.\n\n---\n## 💡 Astra의 선제적 제안 (Proactive Next Actions)\nFinal report with inconsistencies. This should be long enough to pass validation.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `60/100` | ⚠️ Medium |\n| **Fallbacks Used** | `0` | ✅ None |\n| **Auto Retries** | `0` | ✅ Stable |\n| **Deduplication** | `0` | Standard |\n| **Processing Time** | `0.0s` | ✅ Fast |\n\n### 🔍 Decision Audit Trail\n- **[PLANNER]** 전략 수립 중... (16ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (2ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (3ms)\n",
|
||||||
"createdAt": 1778136474569,
|
"createdAt": 1778137049533,
|
||||||
"modelVersion": "unknown"
|
"modelVersion": "unknown"
|
||||||
}
|
}
|
||||||
+10
-10
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"missionId": "stress_conflict_1778136474544",
|
"missionId": "stress_conflict_1778137049510",
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"startTime": "2026-05-07T06:47:54.544Z",
|
"startTime": "2026-05-07T06:57:29.510Z",
|
||||||
"totalElapsedMs": 26,
|
"totalElapsedMs": 24,
|
||||||
"results": {
|
"results": {
|
||||||
"planner": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
|
"planner": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
|
||||||
"researcher": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
"researcher": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
||||||
@@ -16,30 +16,30 @@
|
|||||||
{
|
{
|
||||||
"from": "idle",
|
"from": "idle",
|
||||||
"to": "planner",
|
"to": "planner",
|
||||||
"durationMs": 18,
|
"durationMs": 16,
|
||||||
"message": "전략 수립 중...",
|
"message": "전략 수립 중...",
|
||||||
"ts": "2026-05-07T06:47:54.562Z"
|
"ts": "2026-05-07T06:57:29.526Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "planner",
|
"from": "planner",
|
||||||
"to": "researcher",
|
"to": "researcher",
|
||||||
"durationMs": 3,
|
"durationMs": 2,
|
||||||
"message": "핵심 정보 수집 및 분석 중...",
|
"message": "핵심 정보 수집 및 분석 중...",
|
||||||
"ts": "2026-05-07T06:47:54.565Z"
|
"ts": "2026-05-07T06:57:29.528Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "researcher",
|
"from": "researcher",
|
||||||
"to": "writer",
|
"to": "writer",
|
||||||
"durationMs": 2,
|
"durationMs": 3,
|
||||||
"message": "최종 리포트 작성 및 편집 중...",
|
"message": "최종 리포트 작성 및 편집 중...",
|
||||||
"ts": "2026-05-07T06:47:54.567Z"
|
"ts": "2026-05-07T06:57:29.531Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "writer",
|
"from": "writer",
|
||||||
"to": "completed",
|
"to": "completed",
|
||||||
"durationMs": 3,
|
"durationMs": 3,
|
||||||
"message": "미션 완료",
|
"message": "미션 완료",
|
||||||
"ts": "2026-05-07T06:47:54.570Z"
|
"ts": "2026-05-07T06:57:29.534Z"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"resilienceMetrics": {
|
"resilienceMetrics": {
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"name": "astra",
|
"name": "astra",
|
||||||
"displayName": "Astra",
|
"displayName": "Astra",
|
||||||
"description": "The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making.",
|
"description": "The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making.",
|
||||||
"version": "2.80.14",
|
"version": "2.80.15",
|
||||||
"publisher": "g1nation",
|
"publisher": "g1nation",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"icon": "assets/icon.png",
|
"icon": "assets/icon.png",
|
||||||
|
|||||||
+70
-3
@@ -2022,16 +2022,83 @@ export class AgentExecutor {
|
|||||||
|
|
||||||
for (const candidateModel of modelCandidates) {
|
for (const candidateModel of modelCandidates) {
|
||||||
for (const variant of messageVariants) {
|
for (const variant of messageVariants) {
|
||||||
// 실제 전송할 메시지 (n_ctx 재시도 시 수정됨)
|
// 실제 전송할 메시지
|
||||||
let finalMessages = variant.messages;
|
let finalMessages = variant.messages;
|
||||||
|
|
||||||
|
// ── LM Studio 선제적 컨텍스트 압축 ──
|
||||||
|
// 소형 모델(4B 등)은 GPU 메모리 부족으로 n_ctx가 설정값보다 크게 줄어들 수 있고,
|
||||||
|
// 이때 LM Studio는 에러 대신 200 OK + 빈 스트림을 반환하여 재시도 불가.
|
||||||
|
// 따라서 전송 전에 선제적으로 메시지를 n_ctx에 맞게 압축합니다.
|
||||||
|
if (engine === 'lmstudio') {
|
||||||
|
const totalCharsRaw = finalMessages.reduce((acc, m) => acc + String(m.content || '').length, 0);
|
||||||
|
const estimatedTokensRaw = Math.ceil(totalCharsRaw / 4);
|
||||||
|
const LM_CTX_SAFE_LIMIT = 3500; // 4096 n_ctx 기준 안전 마진
|
||||||
|
|
||||||
|
if (estimatedTokensRaw > LM_CTX_SAFE_LIMIT) {
|
||||||
|
logInfo('LM Studio proactive compression triggered.', {
|
||||||
|
estimatedTokens: estimatedTokensRaw,
|
||||||
|
limit: LM_CTX_SAFE_LIMIT,
|
||||||
|
originalMessageCount: finalMessages.length
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1. system 메시지에서 [CONTEXT] 이후 부분을 우선 제거
|
||||||
|
const sysIdx = finalMessages.findIndex(m => m.role === 'system');
|
||||||
|
if (sysIdx >= 0) {
|
||||||
|
const sysContent = String(finalMessages[sysIdx].content || '');
|
||||||
|
const contextSplit = sysContent.indexOf('[CONTEXT]');
|
||||||
|
if (contextSplit > 0) {
|
||||||
|
// [CONTEXT] 이전까지만 유지 (기본 시스템 프롬프트 + 핵심 지시)
|
||||||
|
const trimmedSys = sysContent.slice(0, contextSplit).trimEnd();
|
||||||
|
finalMessages = finalMessages.map((m, i) =>
|
||||||
|
i === sysIdx ? { ...m, content: trimmedSys + '\n[Context omitted: model context limit]' } : m
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 그래도 크면 시스템 프롬프트를 max 글자로 강제 잘라냄
|
||||||
|
const afterTrimChars = finalMessages.reduce((acc, m) => acc + String(m.content || '').length, 0);
|
||||||
|
const afterTrimTokens = Math.ceil(afterTrimChars / 4);
|
||||||
|
if (afterTrimTokens > LM_CTX_SAFE_LIMIT && sysIdx >= 0) {
|
||||||
|
// 유저 메시지 토큰 계산
|
||||||
|
const nonSysTokens = finalMessages
|
||||||
|
.filter((_, i) => i !== sysIdx)
|
||||||
|
.reduce((acc, m) => acc + String(m.content || '').length, 0) / 4;
|
||||||
|
const maxSysChars = Math.max(2000, (LM_CTX_SAFE_LIMIT - Math.ceil(nonSysTokens) - 512)) * 4;
|
||||||
|
const sysContent = String(finalMessages[sysIdx].content || '');
|
||||||
|
if (sysContent.length > maxSysChars) {
|
||||||
|
finalMessages = finalMessages.map((m, i) =>
|
||||||
|
i === sysIdx ? { ...m, content: sysContent.slice(0, maxSysChars) + '\n[Truncated for model context limit]' } : m
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 히스토리 메시지 정리: system + 마지막 user만 유지
|
||||||
|
const finalCheck = finalMessages.reduce((acc, m) => acc + String(m.content || '').length, 0) / 4;
|
||||||
|
if (finalCheck > LM_CTX_SAFE_LIMIT) {
|
||||||
|
const sysMsg = finalMessages.find(m => m.role === 'system');
|
||||||
|
const lastUserMsg = [...finalMessages].reverse().find(m => m.role === 'user');
|
||||||
|
finalMessages = [
|
||||||
|
...(sysMsg ? [sysMsg] : []),
|
||||||
|
...(lastUserMsg ? [lastUserMsg] : [])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo('LM Studio compression result.', {
|
||||||
|
originalTokens: estimatedTokensRaw,
|
||||||
|
compressedTokens: Math.ceil(finalMessages.reduce((a, m) => a + String(m.content || '').length, 0) / 4),
|
||||||
|
messageCount: finalMessages.length
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const totalChars = finalMessages.reduce((acc, m) => acc + String(m.content || '').length, 0);
|
const totalChars = finalMessages.reduce((acc, m) => acc + String(m.content || '').length, 0);
|
||||||
const estimatedTokens = Math.ceil(totalChars / 4);
|
const estimatedTokens = Math.ceil(totalChars / 4);
|
||||||
const streamBody = {
|
const streamBody = {
|
||||||
model: candidateModel,
|
model: candidateModel,
|
||||||
messages: finalMessages,
|
messages: finalMessages.map(m => ({ role: m.role, content: m.content })),
|
||||||
stream: true,
|
stream: true,
|
||||||
...(engine === 'lmstudio'
|
...(engine === 'lmstudio'
|
||||||
? { max_tokens: 4096, temperature }
|
? { max_tokens: Math.min(4096, Math.max(256, 3500 - estimatedTokens)), temperature }
|
||||||
: { options: { num_ctx: 32768, num_predict: 4096, temperature } }),
|
: { options: { num_ctx: 32768, num_predict: 4096, temperature } }),
|
||||||
};
|
};
|
||||||
logInfo('AI streaming request started.', {
|
logInfo('AI streaming request started.', {
|
||||||
|
|||||||
Reference in New Issue
Block a user