feat: v2.2.83 → v2.2.91 — info prompt 강화 + 사용자 노출 설정 + 답변 포맷 정리
[v2.2.83] /youtube info 프롬프트 강화 - 비유 방향 보존 룰 (Hugging Face=자료실 같은 짝 뒤집기 방지) - 신뢰도 라벨 4종 ([근거 명시] / [화자 주장] / [가정] / [정리자 추론]) - 타임스탬프 fail 룰 (인용·구간 요약 모두 mm:ss 필수) - "정리자 노트" 별도 섹션으로 추론 격리 [v2.2.85] polishPersona self-check 5가지 - 정리·리뷰·요약 답변 출력 직전 머릿속 체크: (1) 사실 오류 (2) 없는 내용 추가 (3) 뉘앙스 유지 (4) 중요도 비례 (5) 중복 제거 [v2.2.86] chunkedSwitchTokens 절대 임계값 게이트 - 입력 < 50k 토큰이면 키워드·길이 트리거 무시하고 단일 호출 - 큰 컨텍스트 모델(131k+)에서 chunked 과잉 발동 방지 [v2.2.87] MAX_SECTIONS 5→3 cap - 총 호출 7회 → 5회 (outline + 3 section + polish) - 사용자 피드백 "6+회는 과하다" [v2.2.88] 이모지 사용 금지 룰 - polishPersona / directPersona / sectionPersona 모두 적용 - 사용자 피드백 "이모지는 시각 노이즈" [v2.2.89] 사용자 노출 설정 두 항목 - chunkedMaxSections config 신규 (default 3, 1~10 clamp) - MAX_SECTIONS_HARD_CEILING (10) 으로 안전망 격상 - Astra Settings 패널 "고급" 섹션에 두 슬라이더 노출 [v2.2.90] 가이드 문구 단순화 - "작은 모델은 낮추라" 문구 빼고 일관되게 50000 권장으로 [v2.2.91] 답변 포맷 가독성 fix - persona 의 "TL;DR" 표현 전부 "한 줄 요약" 으로 단일화 - stripMarkdownFormatting 에 헤더 후 빈 줄 강제 삽입 (marked.parse 가 라벨·본문을 별도 단락으로 인식 → 시각 분리) [테스트] 400/400 통과 (resilience_stress + chunked flow + MAX_SECTIONS cap 등) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -3,15 +3,15 @@
|
||||
<!-- ASTRA:AUTO-START -->
|
||||
|
||||
## Snapshot
|
||||
- **Workspace**: `ConnectAI` `v2.2.73` _(absolute path varies by environment; resolved from the active VS Code workspace)_
|
||||
- **Workspace**: `ConnectAI` `v2.2.90` _(absolute path varies by environment; resolved from the active VS Code workspace)_
|
||||
- **Description**: The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making.
|
||||
- **Stack**: TypeScript, Node.js, VS Code Extension, LM Studio SDK, Test runner
|
||||
- **Stats**: 285 source files, ~56,679 lines across 5 top-level modules.
|
||||
- **Stats**: 286 source files, ~57,087 lines across 5 top-level modules.
|
||||
|
||||
## Last Refresh
|
||||
- **Time**: 2026-05-23T06:46:38.895Z
|
||||
- **Files newly analysed**: 5
|
||||
- **Files reused from cache**: 280
|
||||
- **Time**: 2026-05-24T04:49:04.938Z
|
||||
- **Files newly analysed**: 3
|
||||
- **Files reused from cache**: 283
|
||||
|
||||
## Directory Map
|
||||
```mermaid
|
||||
@@ -37,7 +37,7 @@ mindmap
|
||||
> Arrows: which top-level module imports from which.
|
||||
```mermaid
|
||||
flowchart LR
|
||||
src["src/<br/>139 files"]
|
||||
src["src/<br/>140 files"]
|
||||
media["media/<br/>6 files"]
|
||||
tests["tests/<br/>35 files"]
|
||||
core_py["core_py/<br/>6 files"]
|
||||
@@ -59,44 +59,44 @@ flowchart LR
|
||||
- `src/core/services.ts` — referenced by **10** files
|
||||
- `src/lib/paths.ts` — referenced by **10** files
|
||||
- `src/agent.ts` — referenced by **7** files
|
||||
- `src/retrieval/lessonHelpers.ts` — referenced by **6** files · Lesson / Experience Memory — pure helpers (no vscode dependency) "Lesson" = a markdown file in the active brain that captures a past mistake/risk and how to avoid repeating it. Identified by a lessons
|
||||
- `src/sidebarProvider.ts` — referenced by **7** files
|
||||
- `src/skills/agentKnowledgeMap.ts` — referenced by **6** files
|
||||
|
||||
## Modules
|
||||
|
||||
### `src/` — 139 files, ~39,180 lines
|
||||
### `src/` — 140 files, ~39,922 lines
|
||||
|
||||
**Sub-directories**
|
||||
- `src/features/` (66) — Astra Office — public API. 다음 세션에서 추가될 OfficeSnapshot presenter / schema 도 같은 entry 로 노출 예정. 현재 노출: full webview panel H
|
||||
- `src/features/` (67) — Astra Office — public API. 다음 세션에서 추가될 OfficeSnapshot presenter / schema 도 같은 entry 로 노출 예정. 현재 노출: full webview panel H
|
||||
- `src/core/` (15) — Astra Path Resolver (경로 해결기) Astra의 모든 데이터 파일(.astra 디렉토리)의 경로를 중앙에서 관리합니다. 확장 프로그램의 설치 경로(extensionUri) 기반으로 .astra 디렉토
|
||||
- `src/memory/` (8) — Episodic Memory (일화 기억) 과거 대화/회의/결정의 맥락 흐름을 저장합니다. 세션 종료 시 자동으로 에피소드를 요약하여 저장합니다. "왜 이렇게 결정했는지", "어떤 흐름으로 진행했는지" 기록. 저장
|
||||
- `src/retrieval/` (8) — Brain Index — persistent, mtime-keyed tokenized cache of the Second Brain RAG 검색은 매 질의마다 브레인의 모든 .md 파일을 읽고 토크나이즈해서 TF-I
|
||||
- `src/docs/` (6) — src Chronicle Records
|
||||
- `src/lib/` (6) — Context Manager (컨텍스트 한계 관리) "context length = 132k" 는 "답변을 132k 토큰까지 생성해도 된다" 가 아닙니다. 시스템 프롬프트 + 대화 기록 + 입력 문서 + 생성될 답변
|
||||
- `src/sidebar/` (5) — 5 files (.ts)
|
||||
- `src/integrations/` (4) — Per-chat conversation history for the Telegram bot. Why this exists: the previous bot was stateless — every inbound mess
|
||||
- `src/lmstudio/` (4) — 4 files (.ts)
|
||||
- `src/sidebar/` (4) — 4 files (.ts)
|
||||
- `src/skills/` (4) — 4 files (.ts)
|
||||
- `src/agents/` (3) — Reflection → Lesson persistence Take the Reflector agent's structured critique and persist any substantive findings as a
|
||||
- `src/agents/` (2) — 2 files (.ts)
|
||||
- `src/scaffolder/` (2) — Scaffolder template catalog. Templates are pure data — (projectName) => { [relativePath]: contents }. New templates are
|
||||
|
||||
**Key files**
|
||||
- `src/utils.ts` (448 lines)
|
||||
- `src/config.ts` (394 lines)
|
||||
- `src/config.ts` (406 lines)
|
||||
- `src/features/company/types.ts` (446 lines) — Type definitions for the 1인 기업 (One-Person Company) mode. The mode turns the user into a virtual CEO that dispatches work to a roster of specialist agents. Each turn produces a session directory conta
|
||||
- `src/core/services.ts` (164 lines)
|
||||
- `src/core/services.ts` (176 lines)
|
||||
- `src/sidebarProvider.ts` (4340 lines)
|
||||
- `src/lib/paths.ts` (151 lines)
|
||||
- `src/features/company/companyConfig.ts` (896 lines) — State + config plumbing for 1인 기업 모드. Two surfaces: - CompanyState (runtime data: enabled flag, company name, which agents are active, per-agent model overrides). Persisted in VS Code's globalState so
|
||||
- `src/sidebarProvider.ts` (4327 lines)
|
||||
- `src/memory/types.ts` (126 lines) — Memory Type Definitions (메모리 타입 정의) Astra의 5-Layer Cognitive Memory System의 모든 타입을 정의합니다. ① Short-Term ② Long-Term ③ Project ④ Procedural ⑤ Episodic
|
||||
- `src/retrieval/scoring.ts` (536 lines) — Scoring Engine — TF-IDF + Bilingual Tokenizer 단순 includes() 키워드 매칭을 넘어서, TF-IDF 가중치 기반의 문서 스코어링을 제공합니다. 한국어/영어 양국어 토크나이저를 포함합니다.
|
||||
- `src/retrieval/scoring.ts` (541 lines) — Scoring Engine — TF-IDF + Bilingual Tokenizer 단순 includes() 키워드 매칭을 넘어서, TF-IDF 가중치 기반의 문서 스코어링을 제공합니다. 한국어/영어 양국어 토크나이저를 포함합니다.
|
||||
- `src/skills/agentKnowledgeMap.ts` (374 lines)
|
||||
- `src/retrieval/lessonHelpers.ts` (325 lines) — Lesson / Experience Memory — pure helpers (no vscode dependency) "Lesson" = a markdown file in the active brain that captures a past mistake/risk and how to avoid repeating it. Identified by a lessons
|
||||
- `src/agent.ts` (4076 lines)
|
||||
- `src/agent.ts` (4105 lines)
|
||||
- `src/features/providers/types.ts` (63 lines) — Cloud LLM provider routing — model id prefix → provider id 매핑. Prefix 규칙: openrouter:anthropic/claude-3.5-sonnet → { provider: 'openrouter', model: 'anthropic/claude-3.5-sonnet' } anthropic:claude-3-5
|
||||
- `src/lib/engine.ts` (940 lines)
|
||||
- `src/lib/engine.ts` (1103 lines)
|
||||
- `src/retrieval/brainIndex.ts` (325 lines) — Brain Index — persistent, mtime-keyed tokenized cache of the Second Brain RAG 검색은 매 질의마다 브레인의 모든 .md 파일을 읽고 토크나이즈해서 TF-IDF 점수를 계산했습니다 — 파일 수가 많아지면 그게 병목입니다. 이 모듈은 <brainPath>/.astra/brain-index.json 에
|
||||
- `src/features/company/dispatcher.ts` (1435 lines) — Sequential dispatcher for 1인 기업 모드. Drives one company "turn": user prompt → CEO planner (JSON {brief, tasks}) → for each task in plan: dispatch one specialist (sequentially) - build specialist prompt
|
||||
- `src/retrieval/lessonHelpers.ts` (325 lines) — Lesson / Experience Memory — pure helpers (no vscode dependency) "Lesson" = a markdown file in the active brain that captures a past mistake/risk and how to avoid repeating it. Identified by a lessons
|
||||
- `src/features/company/dispatcher.ts` (1442 lines) — Sequential dispatcher for 1인 기업 모드. Drives one company "turn": user prompt → CEO planner (JSON {brief, tasks}) → for each task in plan: dispatch one specialist (sequentially) - build specialist prompt
|
||||
- `src/features/providers/providerConfig.ts` (78 lines) — Provider 별 API key + enable 토글 저장소. 설계: - API key 자체는 vscode.SecretStorage (secrets) 에 — settings.json / Settings Sync 침범 안 받음. - enabled 토글은 일반 settings (g1nation.providers.<id>.enabled) — 사용자가 패널에서
|
||||
- `src/features/approval/approvalQueue.ts` (129 lines)
|
||||
- `src/integrations/telegram/telegramClient.ts` (154 lines)
|
||||
@@ -107,24 +107,24 @@ flowchart LR
|
||||
- `src/features/projectArchitecture/scanner.ts` (644 lines) — Deep static analyser for the Project Architecture Context generator. Walks the project tree (skipping the usual nodemodules / out / dist noise), pulls the role of each interesting file from its leadin
|
||||
- `src/lib/contextManager.ts` (278 lines) — Context Manager (컨텍스트 한계 관리) "context length = 132k" 는 "답변을 132k 토큰까지 생성해도 된다" 가 아닙니다. 시스템 프롬프트 + 대화 기록 + 입력 문서 + 생성될 답변 + 여유분 ≤ context length 이 모듈은 요청을 보내기 전에 입력 토큰을 추정하고, - 동적으로 출력 상한(maxTokens)을 계
|
||||
|
||||
### `media/` — 6 files, ~7,455 lines
|
||||
### `media/` — 6 files, ~7,484 lines
|
||||
|
||||
**Key files**
|
||||
- `media/sidebar.css` (2068 lines) — Stylesheet
|
||||
- `media/sidebar.js` (3807 lines)
|
||||
- `media/sidebar.html` (538 lines) — Astra
|
||||
- `media/settings-panel.html` (381 lines) — Astra Settings
|
||||
- `media/settings-panel.html` (398 lines) — Astra Settings
|
||||
- `media/settings-panel.css` (210 lines) — Stylesheet
|
||||
- `media/settings-panel.js` (451 lines)
|
||||
- `media/settings-panel.js` (463 lines)
|
||||
|
||||
### `tests/` — 35 files, ~6,004 lines
|
||||
### `tests/` — 35 files, ~5,641 lines
|
||||
*Depends on*: `src/`
|
||||
|
||||
**Sub-directories**
|
||||
- `tests/mocks/` (1) — 1 files (.js)
|
||||
|
||||
**Key files**
|
||||
- `tests/agentEngine.test.ts` (782 lines) — AgentEngine Integration Tests & Performance Benchmarks 검증 대상: 1. ErrorClassifier — 오류 유형(Transient/Permanent/Abort) 자동 분류 2. ErrorRecoveryMatrix — 각 규칙이 의도한 대응 전략으로 매핑되는지 검증 3. resilientExecute — 지수 백
|
||||
- `tests/agentEngine.test.ts` (405 lines) — AgentEngine Tests — Chunked Writer Architecture 예전 buildup(planner → researcher → reflector → writer → synthesizer)을 단일 ChunkedWriter 의 outline → section[N] → polish 로 교체한 뒤의 회귀 테스트. 다루는 범위: 1. ErrorC
|
||||
- `tests/lmStudioLifecycle.test.ts` (326 lines) — Unit tests for ModelLifecycleManager. Strategy: inject mock ILMStudioClient and a simple in-memory IActivityTracker. No real LM Studio or SDK is touched — the manager file does not import the SDK dire
|
||||
- `tests/telegramBot.test.ts` (363 lines) — Unit tests for TelegramBot + truncateForTelegram. Strategy: - TelegramBot is driven by an injected ITelegramClient stub. We script getUpdates to return queued batches and assert that: - the offset cur
|
||||
- `tests/lmStudioStreamer.test.ts` (222 lines) — Unit tests for LMStudioStreamer. Strategy: inject a fake ILMStudioClient that returns a fake model handle whose respond() yields a controllable async iterable. No real SDK or WebSocket touched.
|
||||
@@ -132,7 +132,7 @@ flowchart LR
|
||||
- `tests/secondBrainTrace.test.ts` (407 lines)
|
||||
- `tests/approvalQueue.test.ts` (164 lines) — Unit tests for ApprovalQueue. Strategy: drive enqueue → approve / reject / clear / pre-empt directly, confirm the onChange event fires at the right moments and callbacks fire exactly once.
|
||||
- `tests/projectScaffolder.test.ts` (135 lines) — Unit tests for FileSystemProjectScaffolder. Drives against a real temp directory so end-to-end file IO + path-traversal defenses are exercised.
|
||||
- `tests/resilience_stress.test.ts` (183 lines) — Resilience & Boundary Stress Test Suite (v2.77.3) 이 테스트는 ConnectAI 엔진이 극한의 환경(인증 실패, 네트워크 차단, 타임아웃 등)에서 얼마나 안정적으로 복구되고, 신뢰성 지표(Resilience Metrics)를 정확히 기록하는지 검증합니다.
|
||||
- `tests/resilience_stress.test.ts` (197 lines) — Resilience & Boundary Stress Test Suite (v2.77.3) 이 테스트는 ConnectAI 엔진이 극한의 환경(인증 실패, 네트워크 차단, 타임아웃 등)에서 얼마나 안정적으로 복구되고, 신뢰성 지표(Resilience Metrics)를 정확히 기록하는지 검증합니다.
|
||||
- `tests/skillInjectionService.test.ts` (172 lines) — Unit tests for FileSystemSkillInjectionService. Strategy: drive the service against a real temp directory so path-traversal defenses and writeFileSync paths are exercised end-to-end. The service accep
|
||||
- `tests/dataProcessor.test.ts` (87 lines) — / <reference types="jest" />
|
||||
- `tests/findBrainFilesCache.test.ts` (80 lines) — Unit tests for findBrainFiles TTL cache.
|
||||
@@ -196,7 +196,7 @@ flowchart LR
|
||||
## VS Code Extension Surface
|
||||
- **Extension ID**: `g1nation.astra`
|
||||
- **Activation events**: `onStartupFinished`
|
||||
- **Commands** (28):
|
||||
- **Commands** (29):
|
||||
- `g1nation.newChat` — Astra: New Chat
|
||||
- `g1nation.exportChat` — Astra: Export Chat as Markdown
|
||||
- `g1nation.explainSelection` — Astra: Explain Selected Code
|
||||
@@ -210,6 +210,7 @@ flowchart LR
|
||||
- `g1nation.settings.focus` — Astra: Open Settings Panel
|
||||
- `g1nation.skills.editKnowledgeMap` — Astra: Edit Agent ↔ Knowledge Map
|
||||
- `g1nation.openChat` — Astra: Open Chat (Editor Column)
|
||||
- `g1nation.setupDatacollect` — Astra: Setup Datacollect Dependencies (yt-dlp, youtube-transcript-api)
|
||||
- `g1nation.lesson.create` — Astra: New Lesson (Experience Memory)
|
||||
- `g1nation.lesson.fromConversation` — Astra: New Lesson from Current Conversation
|
||||
- `g1nation.lesson.manage` — Astra: Browse / Manage Lessons
|
||||
@@ -225,7 +226,7 @@ flowchart LR
|
||||
- `g1nation.calendar.refresh` — Astra: Google Calendar 새로고침 📅
|
||||
- `g1nation.calendar.connectOAuth` — Astra: Google Calendar OAuth 연결 (쓰기) 🔐
|
||||
- `g1nation.devilAgent.toggle` — Astra: Toggle Devil Agent 🎭
|
||||
- **Configuration** (87 settings):
|
||||
- **Configuration** (86 settings):
|
||||
- `g1nation.multiAgentEnabled` *(boolean)* _(default: `false`)_ — Enable Multi-Agent Workflow (Planner -> Researcher -> Writer) for complex tasks.
|
||||
- `g1nation.datacollectBridgeUrl` *(string)* _(default: `"http://127.0.0.1:3002"`)_ — Wiki/Datacollect MCP Bridge URL. /research, /benchmark, /youtube chat slash commands route here. The Bridge must be running (`npm run bridge` in the Datacollect project).
|
||||
- `g1nation.datacollectSavePath` *(string)* _(default: `""`)_
|
||||
@@ -282,11 +283,11 @@ flowchart LR
|
||||
- `g1nation.embeddingModel` *(string)* _(default: `""`)_ — Embedding model registered in LM Studio / Ollama (e.g. 'text-embedding-bge-small-en-v1.5', 'nomic-embed-text', 'multilingual-e5-small'). When empty, Astra uses TF-IDF only. When set, the brain is embe
|
||||
- `g1nation.embeddingBlendAlpha` *(number)* _(default: `0.5`)_ — Hybrid score blend: 0 = pure TF-IDF (sparse / keyword), 1 = pure embedding cosine (dense / semantic), 0.5 = balanced. Only used when g1nation.embeddingModel is set. Default 0.5.
|
||||
- `g1nation.knowledgeMix.secondBrainWeight` *(number)* _(default: `50`)_ — Knowledge Mix (0–100): how heavily the assistant should lean on Second Brain evidence vs. its own general knowledge. 0 = Second Brain disabled (model knowledge only). 50 = balanced (legacy default). 1
|
||||
- `g1nation.enableReflection` *(boolean)* _(default: `true`)_ — Insert a Self-Reflection (Reflector) stage between Researcher and Writer in the multi-agent workflow. The Reflector critically reviews the plan and research output (gaps, contradictions, unsupported c
|
||||
- `g1nation.autoLessonFromReflection` *(boolean)* _(default: `true`)_ — Persist substantive Reflector critiques to the active brain as lesson cards under `lessons/auto-reflector/`. Future missions automatically retrieve these cards (via the existing Experience-Memory pipe
|
||||
- `g1nation.workflow.synthesizerEnabled` *(boolean)* _(default: `true`)_
|
||||
- `g1nation.workflow.multiAgentMode` *(string)* _(default: `"auto"`)_
|
||||
- _…and 27 more_
|
||||
- `g1nation.workflow.autoCtxFractionThreshold` *(number)* _(default: `0.3`)_
|
||||
- `g1nation.chunkedSwitchTokens` *(number)* _(default: `50000`)_
|
||||
- `g1nation.chunkedMaxSections` *(number)* _(default: `3`)_
|
||||
- _…and 26 more_
|
||||
|
||||
## Dependencies
|
||||
- **Runtime** (2): `@lmstudio/sdk`, `pdf-parse`
|
||||
@@ -334,7 +335,7 @@ Astra는 대표님의 명시적인 승인 하에 로컬 시스템의 강력한
|
||||
**Designed for High-Performance Decision Making.**
|
||||
Copyright (C) **g1nation**. All rights reserved.
|
||||
|
||||
_Last auto-scan: 2026-05-23T06:46:38.895Z · signature `457ea57e`_
|
||||
_Last auto-scan: 2026-05-24T04:49:04.938Z · signature `932fe655`_
|
||||
<!-- ASTRA:AUTO-END -->
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"version": 1,
|
||||
"generatedAt": "2026-05-23T06:46:38.906Z",
|
||||
"generatedAt": "2026-05-24T04:49:04.948Z",
|
||||
"files": {
|
||||
"src/agent.ts": {
|
||||
"mtimeMs": 1779517702000,
|
||||
"size": 232696,
|
||||
"lines": 4076,
|
||||
"mtimeMs": 1779584933000,
|
||||
"size": 233704,
|
||||
"lines": 4105,
|
||||
"role": "",
|
||||
"imports": [
|
||||
"src/utils",
|
||||
@@ -13,7 +13,6 @@
|
||||
"src/security",
|
||||
"src/core/transaction",
|
||||
"src/core/session",
|
||||
"src/agents/factory",
|
||||
"src/agents/AgentWorkflowManager",
|
||||
"src/core/errorHandler",
|
||||
"src/core/events",
|
||||
@@ -39,36 +38,25 @@
|
||||
]
|
||||
},
|
||||
"src/agents/AgentWorkflowManager.ts": {
|
||||
"mtimeMs": 1779509768000,
|
||||
"size": 2783,
|
||||
"lines": 66,
|
||||
"mtimeMs": 1779534441000,
|
||||
"size": 2315,
|
||||
"lines": 62,
|
||||
"role": "",
|
||||
"imports": [
|
||||
"src/agents/factory",
|
||||
"src/lib/engine",
|
||||
"src/config"
|
||||
"src/lib/engine"
|
||||
]
|
||||
},
|
||||
"src/agents/factory.ts": {
|
||||
"mtimeMs": 1779513282000,
|
||||
"size": 15847,
|
||||
"lines": 273,
|
||||
"mtimeMs": 1779597040000,
|
||||
"size": 17711,
|
||||
"lines": 288,
|
||||
"role": "",
|
||||
"imports": [
|
||||
"src/config",
|
||||
"src/lib/engine"
|
||||
]
|
||||
},
|
||||
"src/agents/reflectionPersister.ts": {
|
||||
"mtimeMs": 1778763469000,
|
||||
"size": 11986,
|
||||
"lines": 308,
|
||||
"role": "Reflection → Lesson persistence Take the Reflector agent's structured critique and persist any substantive findings as a lesson card in <brainDir>/lessons/auto-reflector/. The existing brain retrieval",
|
||||
"imports": [
|
||||
"src/utils",
|
||||
"src/retrieval/lessonHelpers"
|
||||
]
|
||||
},
|
||||
"src/bridge.ts": {
|
||||
"mtimeMs": 1779492050000,
|
||||
"size": 10526,
|
||||
@@ -83,9 +71,9 @@
|
||||
]
|
||||
},
|
||||
"src/config.ts": {
|
||||
"mtimeMs": 1779517494000,
|
||||
"size": 20523,
|
||||
"lines": 394,
|
||||
"mtimeMs": 1779598116000,
|
||||
"size": 21266,
|
||||
"lines": 406,
|
||||
"role": "",
|
||||
"imports": []
|
||||
},
|
||||
@@ -171,9 +159,9 @@
|
||||
]
|
||||
},
|
||||
"src/core/services.ts": {
|
||||
"mtimeMs": 1778421200000,
|
||||
"size": 6692,
|
||||
"lines": 164,
|
||||
"mtimeMs": 1779535877000,
|
||||
"size": 7446,
|
||||
"lines": 176,
|
||||
"role": "",
|
||||
"imports": [
|
||||
"src/config",
|
||||
@@ -260,9 +248,9 @@
|
||||
"imports": []
|
||||
},
|
||||
"src/extension.ts": {
|
||||
"mtimeMs": 1779518653000,
|
||||
"size": 65568,
|
||||
"lines": 1247,
|
||||
"mtimeMs": 1779543586000,
|
||||
"size": 66247,
|
||||
"lines": 1251,
|
||||
"role": "",
|
||||
"imports": [
|
||||
"src/utils",
|
||||
@@ -291,6 +279,7 @@
|
||||
"src/retrieval",
|
||||
"src/retrieval/lessonHelpers",
|
||||
"src/skills/scopedBrainRetriever",
|
||||
"src/features/setup/datacollectSetup",
|
||||
"src/integrations/telegram/conversationHistory",
|
||||
"src/features/calendar",
|
||||
"src/features/devilAgent"
|
||||
@@ -450,9 +439,9 @@
|
||||
]
|
||||
},
|
||||
"src/features/company/ceoPlanner.ts": {
|
||||
"mtimeMs": 1778902489000,
|
||||
"size": 11781,
|
||||
"lines": 269,
|
||||
"mtimeMs": 1779535966000,
|
||||
"size": 11839,
|
||||
"lines": 270,
|
||||
"role": "CEO planner — turns a user prompt into a CompanyTaskPlan. Lifecycle of one planner call: 1. Build the planner system prompt (template + active-agent list). 2. Hit the AI service with the user prompt a",
|
||||
"imports": [
|
||||
"src/core/services",
|
||||
@@ -464,9 +453,9 @@
|
||||
]
|
||||
},
|
||||
"src/features/company/ceoReporter.ts": {
|
||||
"mtimeMs": 1778762677000,
|
||||
"size": 4895,
|
||||
"lines": 122,
|
||||
"mtimeMs": 1779535985000,
|
||||
"size": 4953,
|
||||
"lines": 123,
|
||||
"role": "CEO synthesis pass — runs after all specialists have finished. Given the per-agent outputs, this asks the CEO model to produce the final markdown report (✅ 완료 / 🚀 다음 / 💡 인사이트) that the user actually",
|
||||
"imports": [
|
||||
"src/core/services",
|
||||
@@ -487,9 +476,9 @@
|
||||
]
|
||||
},
|
||||
"src/features/company/dispatcher.ts": {
|
||||
"mtimeMs": 1778936524000,
|
||||
"size": 73113,
|
||||
"lines": 1435,
|
||||
"mtimeMs": 1779536000000,
|
||||
"size": 73381,
|
||||
"lines": 1442,
|
||||
"role": "Sequential dispatcher for 1인 기업 모드. Drives one company \"turn\": user prompt → CEO planner (JSON {brief, tasks}) → for each task in plan: dispatch one specialist (sequentially) - build specialist prompt",
|
||||
"imports": [
|
||||
"src/core/services",
|
||||
@@ -533,9 +522,9 @@
|
||||
]
|
||||
},
|
||||
"src/features/company/intentAlignment.ts": {
|
||||
"mtimeMs": 1778902489000,
|
||||
"size": 15285,
|
||||
"lines": 334,
|
||||
"mtimeMs": 1779536494000,
|
||||
"size": 15705,
|
||||
"lines": 343,
|
||||
"role": "Intent Alignment — 사용자의 자연어 요청을 실행 가능한 작업 조건으로 변환. 사용자는 자기 의도와 배경지식이 에이전트에게 충분히 전달되었다고 착각하는 경향이 있다 (투명성의 착각·지식의 저주·공통 기반 부족). 그래서 에이전트가 즉시 작업에 돌입하면 사용자가 머릿속에 가진 것과 다른 결과를 만들어 낸다. 이 모듈은 그 격차를 메꾸는 한 단계 ",
|
||||
"imports": [
|
||||
"src/core/services",
|
||||
@@ -652,21 +641,22 @@
|
||||
"imports": []
|
||||
},
|
||||
"src/features/datacollect/bridgeClient.ts": {
|
||||
"mtimeMs": 1779277308000,
|
||||
"size": 3518,
|
||||
"lines": 86,
|
||||
"mtimeMs": 1779542176000,
|
||||
"size": 5949,
|
||||
"lines": 129,
|
||||
"role": "",
|
||||
"imports": []
|
||||
},
|
||||
"src/features/datacollect/slashRouter.ts": {
|
||||
"mtimeMs": 1779518658000,
|
||||
"size": 73164,
|
||||
"lines": 1413,
|
||||
"mtimeMs": 1779548421000,
|
||||
"size": 97168,
|
||||
"lines": 1797,
|
||||
"role": "",
|
||||
"imports": [
|
||||
"src/utils",
|
||||
"src/features/datacollect/bridgeClient",
|
||||
"src/features/calendar"
|
||||
"src/features/calendar",
|
||||
"src/features/setup/datacollectSetup"
|
||||
]
|
||||
},
|
||||
"src/features/devilAgent/devilPrompt.ts": {
|
||||
@@ -889,9 +879,9 @@
|
||||
]
|
||||
},
|
||||
"src/features/settings/settingsPanelProvider.ts": {
|
||||
"mtimeMs": 1779492050000,
|
||||
"size": 31952,
|
||||
"lines": 691,
|
||||
"mtimeMs": 1779597205000,
|
||||
"size": 32670,
|
||||
"lines": 701,
|
||||
"role": "",
|
||||
"imports": [
|
||||
"src/integrations/telegram/telegramClient",
|
||||
@@ -903,6 +893,15 @@
|
||||
"src/features/providers"
|
||||
]
|
||||
},
|
||||
"src/features/setup/datacollectSetup.ts": {
|
||||
"mtimeMs": 1779543560000,
|
||||
"size": 12422,
|
||||
"lines": 267,
|
||||
"role": "Datacollect 의존성(Python 패키지) 자동 설치/검증 모듈. 의도: Astra extension 만 깔고 끝나면 /youtube, /research 같은 datacollect 슬래시 명령은 bridge 의 Python 의존성 (yt-dlp, youtube-transcript-api) 이 없어서 실패한다. 사용자가 그걸 매번 수동으로 깔아야 하는",
|
||||
"imports": [
|
||||
"src/utils"
|
||||
]
|
||||
},
|
||||
"src/features/sheets/index.ts": {
|
||||
"mtimeMs": 1778935930000,
|
||||
"size": 237,
|
||||
@@ -982,9 +981,9 @@
|
||||
"imports": []
|
||||
},
|
||||
"src/lib/diagnostics.ts": {
|
||||
"mtimeMs": 1777978189000,
|
||||
"size": 8389,
|
||||
"lines": 210,
|
||||
"mtimeMs": 1779534648000,
|
||||
"size": 8992,
|
||||
"lines": 220,
|
||||
"role": "",
|
||||
"imports": [
|
||||
"src/utils"
|
||||
@@ -1000,9 +999,9 @@
|
||||
]
|
||||
},
|
||||
"src/lib/engine.ts": {
|
||||
"mtimeMs": 1779509734000,
|
||||
"size": 45131,
|
||||
"lines": 940,
|
||||
"mtimeMs": 1779597103000,
|
||||
"size": 51410,
|
||||
"lines": 1103,
|
||||
"role": "",
|
||||
"imports": [
|
||||
"src/core/lock",
|
||||
@@ -1011,8 +1010,7 @@
|
||||
"src/lib/diagnostics",
|
||||
"src/lib/formatter",
|
||||
"src/types/interfaces",
|
||||
"src/config",
|
||||
"src/agents/reflectionPersister"
|
||||
"src/config"
|
||||
]
|
||||
},
|
||||
"src/lib/formatter.ts": {
|
||||
@@ -1048,9 +1046,9 @@
|
||||
]
|
||||
},
|
||||
"src/lmstudio/lifecycleManager.ts": {
|
||||
"mtimeMs": 1779517553000,
|
||||
"size": 11372,
|
||||
"lines": 300,
|
||||
"mtimeMs": 1779536619000,
|
||||
"size": 12733,
|
||||
"lines": 315,
|
||||
"role": "",
|
||||
"imports": [
|
||||
"src/lmstudio/client",
|
||||
@@ -1161,8 +1159,8 @@
|
||||
]
|
||||
},
|
||||
"src/retrieval/contextBudget.ts": {
|
||||
"mtimeMs": 1778674336000,
|
||||
"size": 5143,
|
||||
"mtimeMs": 1779536715000,
|
||||
"size": 5136,
|
||||
"lines": 140,
|
||||
"role": "Context Budget Manager (컨텍스트 예산 관리) 시스템 프롬프트의 토큰 예산을 관리하여 로컬 모델의 context window를 효율적으로 활용합니다.",
|
||||
"imports": [
|
||||
@@ -1179,8 +1177,8 @@
|
||||
]
|
||||
},
|
||||
"src/retrieval/index.ts": {
|
||||
"mtimeMs": 1778676255000,
|
||||
"size": 23764,
|
||||
"mtimeMs": 1779536701000,
|
||||
"size": 23754,
|
||||
"lines": 514,
|
||||
"role": "RetrievalOrchestrator — Unified RAG Pipeline Astra의 모든 검색 소스를 통합 관리하는 오케스트레이터입니다. 검색 흐름: ① Query Planning — 의도 분류 + 검색 전략 결정 ② Parallel Search — Brain + Memory + Project + Episode 동시 검색 ③ Result Fusio",
|
||||
"imports": [
|
||||
@@ -1216,15 +1214,15 @@
|
||||
]
|
||||
},
|
||||
"src/retrieval/scoring.ts": {
|
||||
"mtimeMs": 1779492050000,
|
||||
"size": 21751,
|
||||
"lines": 536,
|
||||
"mtimeMs": 1779536686000,
|
||||
"size": 22097,
|
||||
"lines": 541,
|
||||
"role": "Scoring Engine — TF-IDF + Bilingual Tokenizer 단순 includes() 키워드 매칭을 넘어서, TF-IDF 가중치 기반의 문서 스코어링을 제공합니다. 한국어/영어 양국어 토크나이저를 포함합니다.",
|
||||
"imports": []
|
||||
},
|
||||
"src/retrieval/types.ts": {
|
||||
"mtimeMs": 1778674336000,
|
||||
"size": 2424,
|
||||
"mtimeMs": 1779536706000,
|
||||
"size": 2419,
|
||||
"lines": 66,
|
||||
"role": "Retrieval Types (검색 결과 통합 타입) 모든 검색 소스(Brain, Memory, Project, Episode)의 결과를 통합 인터페이스로 정의합니다.",
|
||||
"imports": []
|
||||
@@ -1275,9 +1273,9 @@
|
||||
]
|
||||
},
|
||||
"src/sidebar/chatHandlers.ts": {
|
||||
"mtimeMs": 1779518648000,
|
||||
"size": 39607,
|
||||
"lines": 710,
|
||||
"mtimeMs": 1779536404000,
|
||||
"size": 23810,
|
||||
"lines": 417,
|
||||
"role": "",
|
||||
"imports": [
|
||||
"src/sidebarProvider",
|
||||
@@ -1287,7 +1285,7 @@
|
||||
"src/config",
|
||||
"src/features/company",
|
||||
"src/core/services",
|
||||
"src/features/company/resumeStore"
|
||||
"src/sidebar/companyHandlers"
|
||||
]
|
||||
},
|
||||
"src/sidebar/chronicleHandlers.ts": {
|
||||
@@ -1299,10 +1297,21 @@
|
||||
"src/sidebarProvider"
|
||||
]
|
||||
},
|
||||
"src/sidebar/companyHandlers.ts": {
|
||||
"mtimeMs": 1779536364000,
|
||||
"size": 18106,
|
||||
"lines": 347,
|
||||
"role": "",
|
||||
"imports": [
|
||||
"src/sidebarProvider",
|
||||
"src/features/company",
|
||||
"src/features/company/resumeStore"
|
||||
]
|
||||
},
|
||||
"src/sidebarProvider.ts": {
|
||||
"mtimeMs": 1779518686000,
|
||||
"size": 200699,
|
||||
"lines": 4327,
|
||||
"mtimeMs": 1779536519000,
|
||||
"size": 201315,
|
||||
"lines": 4340,
|
||||
"role": "",
|
||||
"imports": [
|
||||
"src/utils",
|
||||
@@ -1403,16 +1412,16 @@
|
||||
"imports": []
|
||||
},
|
||||
"media/settings-panel.html": {
|
||||
"mtimeMs": 1779492050000,
|
||||
"size": 21043,
|
||||
"lines": 381,
|
||||
"mtimeMs": 1779598086000,
|
||||
"size": 22426,
|
||||
"lines": 398,
|
||||
"role": "Astra Settings",
|
||||
"imports": []
|
||||
},
|
||||
"media/settings-panel.js": {
|
||||
"mtimeMs": 1779492050000,
|
||||
"size": 21104,
|
||||
"lines": 451,
|
||||
"mtimeMs": 1779597166000,
|
||||
"size": 21773,
|
||||
"lines": 463,
|
||||
"role": "",
|
||||
"imports": []
|
||||
},
|
||||
@@ -1438,12 +1447,13 @@
|
||||
"imports": []
|
||||
},
|
||||
"tests/agentEngine.test.ts": {
|
||||
"mtimeMs": 1778690770000,
|
||||
"size": 33921,
|
||||
"lines": 782,
|
||||
"role": "AgentEngine Integration Tests & Performance Benchmarks 검증 대상: 1. ErrorClassifier — 오류 유형(Transient/Permanent/Abort) 자동 분류 2. ErrorRecoveryMatrix — 각 규칙이 의도한 대응 전략으로 매핑되는지 검증 3. resilientExecute — 지수 백",
|
||||
"mtimeMs": 1779597242000,
|
||||
"size": 18323,
|
||||
"lines": 405,
|
||||
"role": "AgentEngine Tests — Chunked Writer Architecture 예전 buildup(planner → researcher → reflector → writer → synthesizer)을 단일 ChunkedWriter 의 outline → section[N] → polish 로 교체한 뒤의 회귀 테스트. 다루는 범위: 1. ErrorC",
|
||||
"imports": [
|
||||
"src/lib/engine"
|
||||
"src/lib/engine",
|
||||
"src/config"
|
||||
]
|
||||
},
|
||||
"tests/approvalQueue.test.ts": {
|
||||
@@ -1520,8 +1530,8 @@
|
||||
]
|
||||
},
|
||||
"tests/integration_retrieval.test.ts": {
|
||||
"mtimeMs": 1777949141000,
|
||||
"size": 4017,
|
||||
"mtimeMs": 1779536730000,
|
||||
"size": 4013,
|
||||
"lines": 91,
|
||||
"role": "",
|
||||
"imports": [
|
||||
@@ -1642,9 +1652,9 @@
|
||||
]
|
||||
},
|
||||
"tests/resilience_stress.test.ts": {
|
||||
"mtimeMs": 1777968922000,
|
||||
"size": 6981,
|
||||
"lines": 183,
|
||||
"mtimeMs": 1779534568000,
|
||||
"size": 8413,
|
||||
"lines": 197,
|
||||
"role": "Resilience & Boundary Stress Test Suite (v2.77.3) 이 테스트는 ConnectAI 엔진이 극한의 환경(인증 실패, 네트워크 차단, 타임아웃 등)에서 얼마나 안정적으로 복구되고, 신뢰성 지표(Resilience Metrics)를 정확히 기록하는지 검증합니다.",
|
||||
"imports": [
|
||||
"src/lib/engine"
|
||||
@@ -1998,7 +2008,7 @@
|
||||
"imports": []
|
||||
},
|
||||
"docs/records/ConnectAI/chronicle.config.json": {
|
||||
"mtimeMs": 1779518029000,
|
||||
"mtimeMs": 1779598044000,
|
||||
"size": 416,
|
||||
"lines": 11,
|
||||
"role": "JSON configuration",
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"result": "직답 결과 — single-pass mock 응답입니다.",
|
||||
"createdAt": 1779544958019,
|
||||
"createdAt": 1779598841531,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"result": "---\nid: wiki_on\ndate: 2026-05-23T14:02:38.020Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\n직답 결과 — single-pass mock 응답입니다.\n\n직답 결과 — single-pass mock 응답입니다.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `0/100` | ✅ Low |\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- **[DIRECT]** 답변 작성 중... (단일 호출 fast-path) (11ms)\n",
|
||||
"createdAt": 1779544958020,
|
||||
"result": "---\nid: wiki_on\ndate: 2026-05-24T05:00:41.532Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\n직답 결과 — single-pass mock 응답입니다.\n\n직답 결과 — single-pass mock 응답입니다.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `0/100` | ✅ Low |\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- **[DIRECT]** 답변 작성 중... (단일 호출 fast-path) (12ms)\n",
|
||||
"createdAt": 1779598841532,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"missionId": "wiki_on",
|
||||
"status": "completed",
|
||||
"startTime": "2026-05-23T14:02:38.006Z",
|
||||
"startTime": "2026-05-24T05:00:41.519Z",
|
||||
"totalElapsedMs": 14,
|
||||
"results": {
|
||||
"direct": "직답 결과 — single-pass mock 응답입니다."
|
||||
@@ -12,16 +12,16 @@
|
||||
{
|
||||
"from": "idle",
|
||||
"to": "direct",
|
||||
"durationMs": 11,
|
||||
"durationMs": 12,
|
||||
"message": "답변 작성 중... (단일 호출 fast-path)",
|
||||
"ts": "2026-05-23T14:02:38.017Z"
|
||||
"ts": "2026-05-24T05:00:41.531Z"
|
||||
},
|
||||
{
|
||||
"from": "direct",
|
||||
"to": "completed",
|
||||
"durationMs": 3,
|
||||
"durationMs": 2,
|
||||
"message": "미션 완료",
|
||||
"ts": "2026-05-23T14:02:38.020Z"
|
||||
"ts": "2026-05-24T05:00:41.533Z"
|
||||
}
|
||||
],
|
||||
"resilienceMetrics": {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"result": "Final report with inconsistencies. This should be long enough to pass validation.",
|
||||
"createdAt": 1779544964863,
|
||||
"createdAt": 1779598848393,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"result": "Final report with inconsistencies. This should be long enough to pass validation.",
|
||||
"createdAt": 1779544964863,
|
||||
"createdAt": 1779598848393,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"result": "[{\"heading\":\"본문\",\"scope\":\"전체 답변\"}]",
|
||||
"createdAt": 1779544964855,
|
||||
"createdAt": 1779598848392,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
||||
"createdAt": 1779544964859,
|
||||
"createdAt": 1779598848393,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
+10
-10
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"missionId": "stress_conflict_1779544964841",
|
||||
"missionId": "stress_conflict_1779598848381",
|
||||
"status": "completed",
|
||||
"startTime": "2026-05-23T14:02:44.841Z",
|
||||
"totalElapsedMs": 22,
|
||||
"startTime": "2026-05-24T05:00:48.381Z",
|
||||
"totalElapsedMs": 13,
|
||||
"results": {
|
||||
"outline": "[{\"heading\":\"본문\",\"scope\":\"전체 답변\"}]",
|
||||
"section_0": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
||||
@@ -16,28 +16,28 @@
|
||||
"to": "outline",
|
||||
"durationMs": 11,
|
||||
"message": "답변 구조 잡는 중...",
|
||||
"ts": "2026-05-23T14:02:44.852Z"
|
||||
"ts": "2026-05-24T05:00:48.392Z"
|
||||
},
|
||||
{
|
||||
"from": "outline",
|
||||
"to": "section",
|
||||
"durationMs": 4,
|
||||
"durationMs": 1,
|
||||
"message": "본문 작성 중...",
|
||||
"ts": "2026-05-23T14:02:44.856Z"
|
||||
"ts": "2026-05-24T05:00:48.393Z"
|
||||
},
|
||||
{
|
||||
"from": "section",
|
||||
"to": "polish",
|
||||
"durationMs": 3,
|
||||
"durationMs": 0,
|
||||
"message": "최종 다듬기 중...",
|
||||
"ts": "2026-05-23T14:02:44.859Z"
|
||||
"ts": "2026-05-24T05:00:48.393Z"
|
||||
},
|
||||
{
|
||||
"from": "polish",
|
||||
"to": "completed",
|
||||
"durationMs": 4,
|
||||
"durationMs": 1,
|
||||
"message": "미션 완료",
|
||||
"ts": "2026-05-23T14:02:44.863Z"
|
||||
"ts": "2026-05-24T05:00:48.394Z"
|
||||
}
|
||||
],
|
||||
"resilienceMetrics": {
|
||||
@@ -7,5 +7,5 @@
|
||||
"corePurpose": "",
|
||||
"detailLevel": "standard",
|
||||
"createdAt": "2026-05-23T03:51:11.620Z",
|
||||
"updatedAt": "2026-05-23T06:48:11.444Z"
|
||||
"updatedAt": "2026-05-24T04:50:03.783Z"
|
||||
}
|
||||
|
||||
@@ -373,6 +373,23 @@
|
||||
</div>
|
||||
<small class="hint">채팅 응답 생성의 temperature. 낮을수록(0.2~0.3) 한국어 오타·깨진 토큰이 줄고 안정적입니다. 기본 0.3 권장.</small>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="advChunkedSwitch">Chunked 진입 토큰 임계값 (chunkedSwitchTokens)</label>
|
||||
<div class="input-group narrow">
|
||||
<input id="advChunkedSwitch" type="number" min="1000" step="1000" />
|
||||
<button data-save="advanced.chunkedSwitchTokens">저장</button>
|
||||
</div>
|
||||
<small class="hint">입력 prompt 가 이 토큰 수 *미만* 이면 chunked 파이프라인 발동 안 함 — 단일 호출로 답변. 기본 50000 권장 (대부분의 모델에 적합). 매우 작은 모델로 큰 입력 처리 시 OOM 가능성 있으면 이 값을 낮추면 됨.</small>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label for="advChunkedMax">Chunked 최대 섹션 수 (chunkedMaxSections)</label>
|
||||
<div class="input-group narrow">
|
||||
<input id="advChunkedMax" type="number" min="1" max="10" step="1" />
|
||||
<button data-save="advanced.chunkedMaxSections">저장</button>
|
||||
</div>
|
||||
<small class="hint">Chunked 가 답변을 쪼갤 수 있는 최대 섹션 수. 실제 LLM 호출 = `2 + N` 회 (outline 1 + section N + polish 1). 기본 3 (총 5회). 빨리 받고 싶으면 2 (총 4회), 답변을 더 세분화하려면 5 (총 7회).</small>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
const advAutoSteps = $('advAutoSteps');
|
||||
const advCtxSize = $('advCtxSize');
|
||||
const advChatTemp = $('advChatTemp');
|
||||
const advChunkedSwitch = $('advChunkedSwitch');
|
||||
const advChunkedMax = $('advChunkedMax');
|
||||
|
||||
// ---- Google (Calendar + Sheets) ----
|
||||
const gClientId = $('gClientId');
|
||||
@@ -241,6 +243,14 @@
|
||||
vscode.postMessage({ type: 'advanced.update', chatTemperature: Number(advChatTemp.value) })
|
||||
);
|
||||
|
||||
document.querySelector('[data-save="advanced.chunkedSwitchTokens"]').addEventListener('click', () =>
|
||||
vscode.postMessage({ type: 'advanced.update', chunkedSwitchTokens: Number(advChunkedSwitch.value) })
|
||||
);
|
||||
|
||||
document.querySelector('[data-save="advanced.chunkedMaxSections"]').addEventListener('click', () =>
|
||||
vscode.postMessage({ type: 'advanced.update', chunkedMaxSections: Number(advChunkedMax.value) })
|
||||
);
|
||||
|
||||
// ---- Header ----
|
||||
$('openVscodeSettings').addEventListener('click', () =>
|
||||
vscode.postMessage({ type: 'openVscodeSettings' })
|
||||
@@ -397,6 +407,8 @@
|
||||
setIfNotFocused(advAutoSteps, adv.maxAutoSteps);
|
||||
setIfNotFocused(advCtxSize, adv.maxContextSize);
|
||||
setIfNotFocused(advChatTemp, adv.chatTemperature);
|
||||
setIfNotFocused(advChunkedSwitch, adv.chunkedSwitchTokens);
|
||||
setIfNotFocused(advChunkedMax, adv.chunkedMaxSections);
|
||||
|
||||
// ---- Google (Calendar + Sheets) ----
|
||||
const g = state.google;
|
||||
|
||||
+14
-1
@@ -2,7 +2,7 @@
|
||||
"name": "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.",
|
||||
"version": "2.2.82",
|
||||
"version": "2.2.91",
|
||||
"publisher": "g1nation",
|
||||
"license": "MIT",
|
||||
"icon": "assets/icon.png",
|
||||
@@ -560,6 +560,19 @@
|
||||
"maximum": 0.95,
|
||||
"markdownDescription": "`workflow.multiAgentMode = auto` 일 때, prompt 토큰이 효과적 context window 의 이 비율(0~1)을 넘으면 5단계 파이프라인을 강제 발동. 기본 0.30 — 작은 모델이 input으로 컨텍스트의 30% 이상을 먹기 시작하면 한 번에 답하려다 EOS/잘림이 잘 발생한다."
|
||||
},
|
||||
"g1nation.chunkedSwitchTokens": {
|
||||
"type": "number",
|
||||
"default": 50000,
|
||||
"minimum": 1000,
|
||||
"markdownDescription": "**입력 prompt 가 이 토큰 수 *미만* 이면 Multi-Agent(chunked) 파이프라인 발동 안 함** — 모델이 단일 호출로 처리.\n\n키워드(\"요약\", \"리뷰\", \"보고서\" 등) 나 길이(>240자) 같은 트리거가 있어도 입력이 이 임계값 미만이면 무시되고 한 번에 답변 → 답변 속도 크게 향상.\n\n기본 **50000** — 대부분의 사용 환경에 적합. 매우 작은 컨텍스트 모델로 큰 입력을 자주 다룬다면 OOM 방지 차원에서 이 값을 낮출 수 있음."
|
||||
},
|
||||
"g1nation.chunkedMaxSections": {
|
||||
"type": "number",
|
||||
"default": 3,
|
||||
"minimum": 1,
|
||||
"maximum": 10,
|
||||
"markdownDescription": "**Chunked 파이프라인이 답변을 쪼갤 수 있는 최대 섹션 수.**\n\n실제 LLM 호출 횟수 = `1 (outline) + N (sections) + 1 (polish)` = **2 + N 회**.\n- `1` → 총 3회 (가장 빠름, 답변이 단순할 때만 적합)\n- `3` (기본) → 총 5회\n- `5` → 총 7회 (세분화 필요할 때만)\n\n작을수록 답변 속도 빠름, 클수록 답변이 더 세분화돼서 복잡한 보고서·기획서에 유리. 기본 3 — 일반 채팅에 적합."
|
||||
},
|
||||
"g1nation.liveStreamTokens": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
|
||||
@@ -2112,6 +2112,21 @@ export class AgentExecutor {
|
||||
const paramB = estimateModelParamsB(cfg.defaultModel);
|
||||
if (paramB !== null && paramB <= 4) return true;
|
||||
|
||||
// ── 절대 임계값 게이트 (사용자 명시 요청) ────────────────────────────
|
||||
// 입력 prompt 가 `chunkedSwitchTokens` 미만이면 *키워드·길이 트리거 모두 무시*
|
||||
// 하고 단일 LLM 호출. 큰 컨텍스트 모델(131k 등)에서 "요약/리뷰" 같은 키워드만
|
||||
// 써도 chunked 가 강제 발동해 답변이 느려지던 문제 해결.
|
||||
//
|
||||
// ⚠️ 이 게이트는 fraction 안전 체크보다 *먼저* 평가됨 — 사용자가 절대 임계값을
|
||||
// 명시한 의도(50k 미만은 한 번에 처리)를 fraction 이 뒤집지 못하게. 작은
|
||||
// 컨텍스트 모델 사용자는 config 에서 이 값을 모델 윈도우의 ~30% 로 낮춰야 함.
|
||||
try {
|
||||
const promptTokensForGate = estimateTokens(prompt);
|
||||
if (promptTokensForGate < cfg.chunkedSwitchTokens) {
|
||||
return false;
|
||||
}
|
||||
} catch { /* fall through — 안전 측 fraction/keyword 체크가 처리 */ }
|
||||
|
||||
try {
|
||||
const effectiveCtx = cfg.smallModelContextCap > 0 && paramB !== null && paramB <= 4
|
||||
? cfg.smallModelContextCap
|
||||
|
||||
+59
-18
@@ -138,11 +138,15 @@ export interface SectionOutline {
|
||||
* prompt picked here based on `options.config.role`.
|
||||
*/
|
||||
export class ChunkedWriter extends BaseAgent {
|
||||
/** Hard cap on section count regardless of what the outline model returns. */
|
||||
static readonly MAX_SECTIONS = 5;
|
||||
/**
|
||||
* Hard ceiling — *사용자 config 가 어떤 값이든 이걸 넘을 수 없다*. 안전망 의미.
|
||||
* 실제 사용 상한은 `getConfig().chunkedMaxSections` (default 3). 사용자가
|
||||
* Astra Settings 에서 1~10 사이 조정 가능, 이 상수가 그 위 절대 한도.
|
||||
*/
|
||||
static readonly MAX_SECTIONS_HARD_CEILING = 10;
|
||||
|
||||
private readonly outlinePersona = `You are a concise editor planning the structure of a Korean answer.
|
||||
Decide how many sections the answer needs (0..${ChunkedWriter.MAX_SECTIONS}). Pick the *smallest* number that still covers the user's request well — a short factual question should be 0-1 section, a meaty analysis 3-5.
|
||||
Decide how many sections the answer needs. The exact upper bound (MAX_N) is given in the user message below — never exceed it. Pick the *smallest* count that still covers the request well — a short factual question should be 0-1 section, a meaty analysis up to MAX_N.
|
||||
|
||||
Output STRICTLY a JSON array of objects: \`[{"heading": "...", "scope": "..."}]\`. No prose, no fences, no leading text.
|
||||
- 🟢 **빈 배열 \`[]\`** = "쪼갤 필요 없음". 사용자 질문이 간단해서 단일 LLM 호출로 즉답이 더 빠르고 자연스러울 때 (예: 단순 사실 질문, 짧은 코드 한 줄, 정의 묻기). 시스템이 이걸 받으면 outline·section 단계 건너뛰고 1회 직답으로 처리한다.
|
||||
@@ -151,7 +155,7 @@ Output STRICTLY a JSON array of objects: \`[{"heading": "...", "scope": "..."}]\
|
||||
|
||||
판단 기준:
|
||||
- 답변이 한 단락 (대략 3~5문장) 이내로 완결 가능 → \`[]\`
|
||||
- 본문 분석·여러 측면 비교·구조화된 보고서가 필요 → N개 섹션
|
||||
- 본문 분석·여러 측면 비교·구조화된 보고서가 필요 → N개 섹션 (단, MAX_N 절대 초과 금지)
|
||||
|
||||
If the user attached source content (article/code/log) the sections must cover *that content*, not analysis methodology.`;
|
||||
|
||||
@@ -164,24 +168,52 @@ If the user attached source content (article/code/log) the sections must cover *
|
||||
Rules:
|
||||
- Stay strictly inside this section's scope. Do NOT cover other outline entries.
|
||||
- Korean, plain markdown (no top-level "#" — the heading will be added by the joiner).
|
||||
- 이모지 / 이모티콘 사용 금지 (📌 🎯 💡 ✅ 등). 사용자 명시 피드백.
|
||||
- Pack facts. Avoid filler / executive summaries / closing remarks (the polish pass adds those).
|
||||
- If the user attached source content, cite from it; do not invent facts.
|
||||
- Do NOT output the heading itself — only the body of this section.`;
|
||||
|
||||
private readonly polishPersona = `You are the final editor producing the user-facing Korean answer from a sectioned draft.
|
||||
|
||||
Job:
|
||||
[Job]
|
||||
1. Fix typos, broken markdown, inconsistent terminology.
|
||||
2. Remove unsupported claims / hallucinations: if a sentence asserts a fact that isn't grounded in the user's request (or the earlier sections themselves), delete it. Better to be short than wrong.
|
||||
3. Smooth section transitions and remove duplicated information across sections.
|
||||
4. Open with the conclusion / key takeaway in the first sentence (no "분석해보겠습니다", no preamble).
|
||||
5. Preserve every factually grounded claim from the draft. Don't invent new facts.
|
||||
4. Preserve every factually grounded claim from the draft. Don't invent new facts.
|
||||
|
||||
Output rules:
|
||||
- Korean. Plain markdown. Section labels as plain text on their own line — no "#", "##".
|
||||
- Bullets with "- " only. No tables, no HTML, no triple-bar separators.
|
||||
- If the draft already has a sensible structure, keep it; only refactor when sections clearly overlap or contradict.
|
||||
- DO NOT emit hidden reasoning (<think>, "Thinking:", etc.).`;
|
||||
[정리·리뷰·요약 self-check — 출력 직전에 반드시 머릿속으로 통과]
|
||||
사용자가 원본을 첨부했거나 draft 가 원본 자료를 다루고 있을 때, 답변 출력 전에 다음 5가지를
|
||||
스스로 점검. 어기면 그 부분 삭제·수정 후 출력.
|
||||
(1) **사실 오류** — 원본의 고유명사·수치·비유·대응 관계가 정확히 옮겨졌나? 비유는
|
||||
방향이 뒤집히기 쉬움 (예: "A=자료실, B=공부방" 을 "B=자료실, A=공부방" 으로 뒤집기).
|
||||
(2) **없는 내용 추가 금지** — 원본에 없는 *인과·순서·단계 구분* 을 만들지 말 것. "따라서",
|
||||
"그러므로", "단계별로", "A → B → C 순으로" 같은 표현이 답변에 들어가려 하면, 원본에
|
||||
그 흐름이 *명시* 돼 있는지 확인. 없으면 그 표현 빼거나 "(정리자 추론)" 로 라벨링.
|
||||
(3) **원본 뉘앙스 유지** — "A 와 B 를 *동시에* 하라" 를 "A 후 B *순서로*" 로 바꾸는 식의
|
||||
양상(동시/순차/선택/필수) 변형 금지. 원본 표현 그대로 따옴표 인용 권장.
|
||||
(4) **중요도 비례** — 원본의 핵심이 답변에서도 부각되고, 부가 디테일은 그에 비례한 분량만.
|
||||
본문 길이가 아니라 중요도에 비례해서 요약.
|
||||
(5) **중복 제거** — 마지막 단락에서 앞 내용을 다시 요약·반복하지 말 것. 한 줄 요약이 있으면
|
||||
그 역할은 거기서 끝.
|
||||
|
||||
[답변 포맷 — Readability / Visibility]
|
||||
사용자가 명시 피드백을 줘서 다음 포맷을 따른다. 답변 *복잡도* 에 따라 두 분기:
|
||||
|
||||
A. **본문이 길거나 여러 단위의 정보를 다룰 때** (대략 본문 250자 이상 / 비교·분석·계획·리뷰 등):
|
||||
1. 답변 첫 섹션 헤더는 정확히 \`## 한 줄 요약\` 으로 시작 (한국어 사용자 친화 — "TL;DR", "Summary", "요약" 같은 다른 표현 금지). 결론·핵심을 1~3문장으로 압축. 사용자가 본문을 다 안 읽어도 take-away 가 잡혀야 함. **헤더에 이모지 절대 사용 금지**.
|
||||
2. 본문은 \`##\` 또는 \`###\` subheading 으로 시각 분할. 한 덩어리 prose 금지.
|
||||
3. 비교 가능한 정보(장단점·옵션·항목별 평가)는 마크다운 표로. 순서·체크리스트는 \`- \` 불릿.
|
||||
4. 첫 문장 자체가 결론이어야 한다는 룰은 유지 — 한 줄 요약 안에서 첫 문장이 결론.
|
||||
|
||||
B. **짧은 직답 (1~3문장 정도로 충분한 경우)**:
|
||||
1. 한 줄 요약 / subheading 강제 안 함. 그냥 결론으로 직답.
|
||||
2. 인사·서문 없이 첫 문장이 답. ("좋은 질문입니다" "분석해보겠습니다" 금지)
|
||||
|
||||
[공통 규칙]
|
||||
- 한국어 마크다운. 코드 블록은 실제 코드일 때만 (\`\`\`).
|
||||
- **이모지·이모티콘 절대 사용 금지** — 헤더든 본문이든 📌 🎯 💡 ✅ ⚠️ 🚀 ❓ 🧩 등 모두 금지. 사용자가 시각 노이즈로 느낀다고 명시 피드백. 정보는 텍스트·표·불릿으로만 전달.
|
||||
- 추론 과정·\`<think>\`·"Thinking Process:" 같은 hidden reasoning 절대 노출 금지.
|
||||
- 본문 분기를 LLM 자신이 판단 — 사용자가 모드 명시 안 함.`;
|
||||
|
||||
/**
|
||||
* Single-pass 직답 persona. 짧은 질문·정의 묻기·간단한 사실 확인처럼
|
||||
@@ -192,16 +224,25 @@ Output rules:
|
||||
|
||||
Rules:
|
||||
- 첫 문장이 결론 / 직답이다. "분석해보겠습니다" "좋은 질문입니다" 같은 서문 금지.
|
||||
- Korean. Plain markdown — "#", "##" 같은 헤더 금지, "- " bullet 만 허용. No tables, no HTML.
|
||||
- 짧은 질문엔 짧은 답. 한 문장으로 충분하면 한 문장.
|
||||
- Korean. Plain markdown.
|
||||
- **이모지 / 이모티콘 사용 금지** (📌 🎯 💡 ✅ ⚠️ 등 전부). 사용자 명시 피드백.
|
||||
- 짧은 질문엔 짧은 답. 한 문장으로 충분하면 한 문장. 1~3문장 직답이면 헤더·표 없이 그냥 prose 로.
|
||||
- 만약 답이 *예상보다 길어지거나* 여러 정보 단위를 다루게 되면 \`## 한 줄 요약\` 후 \`##\` subheading 으로 분할 (사용자가 Readability 위해 요청한 룰). 표·불릿도 활용. 헤더에 이모지 사용 금지.
|
||||
- 사용자가 본문(코드·기사·로그)을 첨부했으면 그 본문에서 인용. 본문에 없는 사실 지어내지 말 것.
|
||||
- 추론 과정·"Thinking:"·<think> 노출 금지.`;
|
||||
|
||||
async execute(input: string, context?: string, signal?: AbortSignal, options?: AgentExecuteOptions): Promise<string> {
|
||||
const role = (options?.config?.role as string | undefined) || 'section';
|
||||
switch (role) {
|
||||
case 'outline':
|
||||
return this.callLLM(this.outlinePersona, this.buildOutlinePrompt(input, context), signal);
|
||||
case 'outline': {
|
||||
// 호출자(AgentEngine)가 사용자 config 의 chunkedMaxSections 값을
|
||||
// options.config.maxSections 로 박아 넘긴다. 없으면 hard ceiling 사용
|
||||
// (실행 안 되어야 할 코드 경로 — 안전망).
|
||||
const maxN = (typeof options?.config?.maxSections === 'number' && options.config.maxSections > 0)
|
||||
? Math.min(ChunkedWriter.MAX_SECTIONS_HARD_CEILING, Math.floor(options.config.maxSections as number))
|
||||
: ChunkedWriter.MAX_SECTIONS_HARD_CEILING;
|
||||
return this.callLLM(this.outlinePersona, this.buildOutlinePrompt(input, context, maxN), signal);
|
||||
}
|
||||
case 'polish':
|
||||
return this.callLLM(this.polishPersona, this.buildPolishPrompt(input, options), signal);
|
||||
case 'direct':
|
||||
@@ -212,11 +253,11 @@ Rules:
|
||||
}
|
||||
}
|
||||
|
||||
private buildOutlinePrompt(userRequest: string, brainContext?: string): string {
|
||||
private buildOutlinePrompt(userRequest: string, brainContext?: string, maxN: number = ChunkedWriter.MAX_SECTIONS_HARD_CEILING): string {
|
||||
const ctx = brainContext && brainContext.trim().length > 0
|
||||
? `\n\n[보조 지식 컨텍스트 — 답변에 직접 인용하기보단 분할 결정에만 참고]\n${brainContext.substring(0, 1200)}`
|
||||
: '';
|
||||
return `[사용자 요청 — 본문이 포함돼 있다면 그게 1차 자료입니다]\n${userRequest}${ctx}\n\n위 요청에 대한 답변을 ${ChunkedWriter.MAX_SECTIONS}개 이내의 섹션으로 어떻게 나눌지 JSON 배열로만 출력하세요.`;
|
||||
return `[사용자 요청 — 본문이 포함돼 있다면 그게 1차 자료입니다]\n${userRequest}${ctx}\n\n[제약]\nMAX_N = ${maxN} — 절대 ${maxN}개 초과 금지.\n\n위 요청에 대한 답변을 ${maxN}개 이내의 섹션으로 어떻게 나눌지 JSON 배열로만 출력하세요.`;
|
||||
}
|
||||
|
||||
private buildSectionPrompt(input: string, brainContext?: string, options?: AgentExecuteOptions): string {
|
||||
|
||||
@@ -147,6 +147,27 @@ export interface IAgentConfig {
|
||||
* 기본 0.30 — 작은 모델이 30% 이상을 input으로 먹기 시작하면 한 번에 끝내려는 시도가 위험.
|
||||
*/
|
||||
workflowAutoCtxFractionThreshold: number;
|
||||
/**
|
||||
* 절대 토큰 임계값 — 입력 prompt 가 이 값 *미만* 이면 Multi-Agent 파이프라인 발동
|
||||
* 안 함 (키워드·길이 트리거 무시). 모델이 단일 호출로 처리.
|
||||
*
|
||||
* 의도: 사용자가 "요약/리뷰" 같은 키워드만 써도 chunked 가 강제로 발동해
|
||||
* LLM 여러 번 호출되며 답변이 느려지는 문제 해결. 입력이 모델 윈도우 대비
|
||||
* 충분히 작으면 한 번에 답하는 게 합리적.
|
||||
*
|
||||
* 기본 50000 — 대부분의 사용 환경에 적합. 매우 작은 컨텍스트 모델로 큰 입력을
|
||||
* 자주 다룬다면 OOM 방지 차원에서 사용자가 직접 낮출 수 있음 (Astra Settings 패널).
|
||||
*/
|
||||
chunkedSwitchTokens: number;
|
||||
/**
|
||||
* Chunked 파이프라인 진입 시 outline 이 만들 수 있는 *최대 섹션 수*.
|
||||
* 실제 LLM 호출 횟수 = 1(outline) + N(section) + 1(polish) = 2 + N.
|
||||
* 따라서 이 값이 3이면 최대 5회, 4이면 최대 6회.
|
||||
*
|
||||
* 작을수록 답변 속도 빠름, 클수록 답변이 더 세분화. 기본 3 — 사용자
|
||||
* 피드백("6회 이상은 과하다") 반영. 1~10 범위 clamp.
|
||||
*/
|
||||
chunkedMaxSections: number;
|
||||
// ─── Stream 표시 ───
|
||||
/**
|
||||
* 모델 토큰을 받는 즉시 채팅 버블에 흘려보낼지 여부.
|
||||
@@ -301,6 +322,8 @@ export function getConfig(): IAgentConfig {
|
||||
workflowAutoCtxFractionThreshold: Math.max(0.05, Math.min(0.95,
|
||||
cfg.get<number>('workflow.autoCtxFractionThreshold', 0.30)
|
||||
)),
|
||||
chunkedSwitchTokens: Math.max(1000, cfg.get<number>('chunkedSwitchTokens', 50000)),
|
||||
chunkedMaxSections: Math.max(1, Math.min(10, cfg.get<number>('chunkedMaxSections', 3))),
|
||||
liveStreamTokens: cfg.get<boolean>('liveStreamTokens', true),
|
||||
outputFormat: ((): 'plain' | 'markdown' => {
|
||||
const v = (cfg.get<string>('outputFormat', 'plain') || 'plain').trim().toLowerCase();
|
||||
|
||||
@@ -260,16 +260,33 @@ export function stripMarkdownFormatting(text: string): string {
|
||||
});
|
||||
|
||||
// 3. 줄 단위 정리.
|
||||
src = src.split('\n').map((rawLine) => {
|
||||
// 헤더가 strip 되면 라벨 텍스트만 남는데, 다음 본문 줄과 시각적으로 *분리* 되어야
|
||||
// marked.parse 가 별도 단락으로 인식. 그래서 strip 시점에 *후속 빈 줄 보장* 플래그.
|
||||
const stripped: string[] = [];
|
||||
let pendingEnsureBlankAfter = false;
|
||||
for (const rawLine of src.split('\n')) {
|
||||
let line = rawLine;
|
||||
let wasHeader = false;
|
||||
// 줄 시작 헤더 마커 제거 ("## 핵심 요약" → "핵심 요약")
|
||||
const headerHit = /^\s{0,3}#{1,6}\s+/.test(line);
|
||||
if (headerHit) {
|
||||
line = line.replace(/^\s{0,3}#{1,6}\s+/, '');
|
||||
wasHeader = true;
|
||||
}
|
||||
// 줄 시작 blockquote 제거
|
||||
line = line.replace(/^\s{0,3}>\s?/, '');
|
||||
// 줄 시작 `* ` 또는 `+ ` 불릿 → `- ` 로 통일
|
||||
line = line.replace(/^(\s*)[*+]\s+/, '$1- ');
|
||||
return line;
|
||||
}).join('\n');
|
||||
|
||||
// 직전 줄이 strip 된 헤더였고, 지금 줄이 *빈 줄이 아니면* 그 사이에 빈 줄 1개 강제 삽입.
|
||||
// marked.parse 는 빈 줄을 단락 구분으로 해석하므로 헤더가 본문과 시각 분리됨.
|
||||
if (pendingEnsureBlankAfter && line.trim().length > 0) {
|
||||
stripped.push('');
|
||||
}
|
||||
stripped.push(line);
|
||||
pendingEnsureBlankAfter = wasHeader;
|
||||
}
|
||||
src = stripped.join('\n');
|
||||
|
||||
// 4. 강조 마커 제거.
|
||||
src = src.replace(/\*\*(.+?)\*\*/g, '$1'); // **bold**
|
||||
|
||||
@@ -800,13 +800,27 @@ function buildInfoExtractionPrompt(video: any, userContent: string): string {
|
||||
이 영상을 다시 보지 않고도 핵심 정보를 그대로 활용할 수 있도록, 영상이 *말한 것*
|
||||
(주장·사실·근거·결론)을 구조화해서 정리하세요.
|
||||
|
||||
[분석 원칙]
|
||||
1. 영상 본문(자막)에 *명시된 것* 만 인용. 추측·일반론·외부 지식 보강 금지.
|
||||
2. 자막에 없는 사실은 "본문에 명시되지 않음" 이라고 표시. 채워 넣지 말 것.
|
||||
3. 정보의 신뢰도 단계 표기: \`[근거 명시]\` (구체 출처·수치·인용)·\`[화자 주장]\`
|
||||
(출처 없는 단정)·\`[가정]\` (조건부 표현). 모든 핵심 주장에 라벨링.
|
||||
4. 타임스탬프는 mm:ss 형식으로 인용 직후 괄호에. 예: "…라고 말한다 (12:34)".
|
||||
5. 한국어 마크다운. 표·불릿 자유롭게.
|
||||
[분석 원칙 — 모두 반드시 준수]
|
||||
1. **출처 분리** — 영상 본문(자막)에 *명시된 것* 만 핵심 섹션에 넣음. 정리자의 추론·외부
|
||||
지식·자기 해석은 별도 \`## 🧩 정리자 노트\` 섹션에만. 두 줄 섞지 말 것.
|
||||
2. **빈 곳 채우지 말 것** — 자막에 없는 사실은 "본문에 명시되지 않음" 또는 "해당 사례 없음".
|
||||
3. **신뢰도 라벨 필수** — 모든 핵심 주장 앞에 다음 중 하나:
|
||||
- \`[근거 명시]\` 구체 출처·수치·인용이 본문에 있음
|
||||
- \`[화자 주장]\` 출처 없는 단정 (디노가 그렇게 말함)
|
||||
- \`[가정]\` 조건부·"~인 것 같다" 표현
|
||||
- \`[정리자 추론]\` 본문에 없지만 정리자가 추가 (이건 정리자 노트 섹션 전용)
|
||||
4. **타임스탬프 필수** — 본문 인용·구간 요약·발언 따옴표는 끝에 \`(mm:ss)\` 무조건 붙임.
|
||||
이걸 빠뜨리면 fail. "(시점 미상)" 도 허용 안 함 — 모르면 인용 자체 빼기.
|
||||
5. **화자 한 줄 비유 보존 + 방향 보존** — 영상에 비유·은유·"X 는 Y 같은 것" 식 압축 표현이
|
||||
있으면 반드시 별도 섹션 \`## 💡 화자 한 줄 비유\` 에 보존. 영상의 결정적 요약이 거기
|
||||
들어 있을 가능성 큼. 없으면 "본문에 명시된 한 줄 비유 없음" 명시.
|
||||
⚠️ **비유는 방향이 뒤집히기 쉬움** — 화자가 "Hugging Face = 자료실, Reddit = 공부방"
|
||||
이라 했으면 정확히 그 짝(어느 쪽이 자료실이고 어느 쪽이 공부방인지)을 그대로 따옴표
|
||||
인용으로 보존. 정리자가 단어 위치를 바꾸거나 뜻을 의역하면 안 됨. 고유명사·수치·
|
||||
대응 관계도 마찬가지 — 본문 그대로.
|
||||
6. **순서·단계 발명 금지** — 화자가 "A → B → C 순서로" 라고 명시하지 *않았으면* "단계적
|
||||
학습 순서" 같은 흐름을 정리자가 만들지 말 것. 굳이 필요하면 정리자 노트로.
|
||||
7. 한국어 마크다운. 표·불릿 자유롭게.
|
||||
|
||||
[영상 메타데이터]
|
||||
\`\`\`json
|
||||
@@ -816,17 +830,24 @@ ${JSON.stringify(slim, null, 2)}
|
||||
[자막 본문]
|
||||
${trimmed}${userBlock}
|
||||
|
||||
[필수 출력 형식 — 정확히 이 구조. 아래 6개 섹션 외 추가 금지]
|
||||
[필수 출력 형식 — 정확히 이 구조. 아래 8개 섹션 외 추가 금지]
|
||||
|
||||
# ${slim.title || video.title} — 정보 추출 카드
|
||||
|
||||
> **영상 URL**: ${slim.url} · **분석 일자**: ${today} · **길이**: ${slim.durationHms || (slim.durationSec ? formatHms(slim.durationSec) : '?')} · **채널**: ${slim.channel || '?'}
|
||||
|
||||
## 🎯 한 줄 요약 (TL;DR)
|
||||
(영상의 핵심 메시지 한 문장. "무엇이 누구에게 왜 중요한가" 를 압축. 제목 그대로 베끼지 말고 본문 기준으로 다시 쓸 것)
|
||||
(영상의 핵심 메시지 한 문장. "무엇이 누구에게 왜 중요한가" 를 압축. 제목 그대로 베끼지
|
||||
말고 본문 기준으로 다시 쓸 것. 정리자의 해석은 금지 — 화자의 말 그대로 압축)
|
||||
|
||||
## 💡 화자 한 줄 비유 (Anchor Metaphor)
|
||||
영상에서 화자가 *전체 메시지를 한 줄로 압축한 비유·은유* 가 있으면 그대로 따옴표로
|
||||
보존. 영상 마무리부에 자주 등장. 예: "Hugging Face = 자료실, Reddit = 공부방,
|
||||
유튜브 = 복습실" 같은 식. 없으면 "본문에 명시된 한 줄 비유 없음".
|
||||
|
||||
## 📌 핵심 주장 3~5개
|
||||
영상이 제시한 *주요 결론·주장* 만. 각 항목 한 줄 + 신뢰도 라벨 + 본문 인용 (mm:ss).
|
||||
영상이 *명시한* 주요 결론·주장만. 정리자 추론은 여기 들어오면 안 됨 (그건 🧩 섹션).
|
||||
각 항목 한 줄 + 신뢰도 라벨 + 본문 인용 (mm:ss).
|
||||
- **[근거 명시]** "주장 한 줄" — 본문 인용 (mm:ss)
|
||||
- **[화자 주장]** "주장 한 줄" — 본문 인용 (mm:ss)
|
||||
- …
|
||||
@@ -842,9 +863,16 @@ ${trimmed}${userBlock}
|
||||
데이터가 없는 영상이면 "본문에 명시된 구체 수치·출처 없음" 한 줄.
|
||||
|
||||
## 🧭 구조 요약 (Sectioned Summary)
|
||||
영상을 chapters (있으면) 또는 30초 버킷으로 구간 나눠 각 구간의 *내용 요약*. 1~2문장씩.
|
||||
- **[00:00–02:30]** 도입부에서 다룬 내용 한 문장 요약
|
||||
- **[02:30–05:00]** 본론 첫 부분…
|
||||
영상을 chapters (메타데이터에 있으면 그것 사용) 또는 30초 버킷으로 구간 나눠 각 구간의
|
||||
*내용 요약*. 1~2문장씩. 각 항목 끝에 타임스탬프 범위 필수.
|
||||
- **[00:00–02:30]** 도입부에서 다룬 내용 한 문장 요약 (mm:ss–mm:ss)
|
||||
- **[02:30–05:00]** 본론 첫 부분… (mm:ss–mm:ss)
|
||||
- …
|
||||
|
||||
## 🔗 인용용 한 줄 카드 (Citation Snippets)
|
||||
영상의 *결정적 발언* 을 그대로 따옴표로 보존. 사장님이 글·발표·메모에 인용할 때 복붙용.
|
||||
3~5개. 길이는 한 문장. 타임스탬프 필수.
|
||||
- "직접 인용 한 문장" — ${slim.title || video.title}, ${slim.channel || '?'} (mm:ss)
|
||||
- …
|
||||
|
||||
## ❓ 더 파고들 질문 (Open Questions)
|
||||
@@ -853,11 +881,15 @@ ${trimmed}${userBlock}
|
||||
- "본문에서 X 가 Y 라고 했지만 Z 데이터 출처는 명시 안 됨 — 원 데이터 찾아볼 것"
|
||||
- …
|
||||
|
||||
## 🔗 인용용 한 줄 카드 (Citation Snippets)
|
||||
영상의 *결정적 발언* 을 그대로 따옴표로 보존. 사장님이 글·발표·메모에 인용할 때 복붙용.
|
||||
3~5개. 길이는 한 문장.
|
||||
- "직접 인용 한 문장" — ${slim.title || video.title}, ${slim.channel || '?'} (mm:ss)
|
||||
- …`;
|
||||
## 🧩 정리자 노트 (원본 보강) — 선택
|
||||
*본문에 없지만* 정리자가 추가로 짚고 싶은 맥락·해석·연결·경고. 위 6개 핵심 섹션과
|
||||
구조적으로 격리되어, 독자가 "이건 화자가 말한 게 아니라 LLM 이 추론한 거" 라고
|
||||
명확히 인지하도록. 모든 항목은 \`[정리자 추론]\` 라벨로 시작.
|
||||
- **[정리자 추론]** 화자가 "여러 채널을 동시 시청" 하라 했지만, 입문자 페이스를 고려하면
|
||||
먼저 한 채널을 깊게 따라가는 것도 한 가지 시작점이 될 수 있음.
|
||||
- …
|
||||
|
||||
특별히 보강할 게 없으면 이 섹션 통째로 "정리자 추가 노트 없음 — 본문 그대로가 명확함" 한 줄.`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -83,6 +83,8 @@ interface SettingsState {
|
||||
maxAutoSteps: number;
|
||||
maxContextSize: number;
|
||||
chatTemperature: number;
|
||||
chunkedSwitchTokens: number;
|
||||
chunkedMaxSections: number;
|
||||
};
|
||||
datacollect: {
|
||||
bridgeUrl: string;
|
||||
@@ -585,6 +587,12 @@ export class SettingsPanelProvider implements vscode.WebviewViewProvider {
|
||||
if (typeof msg.chatTemperature === 'number' && Number.isFinite(msg.chatTemperature)) {
|
||||
await this._safeConfigUpdate('chatTemperature', Math.max(0, Math.min(2, msg.chatTemperature)));
|
||||
}
|
||||
if (typeof msg.chunkedSwitchTokens === 'number' && Number.isFinite(msg.chunkedSwitchTokens)) {
|
||||
await this._safeConfigUpdate('chunkedSwitchTokens', Math.max(1000, Math.floor(msg.chunkedSwitchTokens)));
|
||||
}
|
||||
if (typeof msg.chunkedMaxSections === 'number' && Number.isFinite(msg.chunkedMaxSections)) {
|
||||
await this._safeConfigUpdate('chunkedMaxSections', Math.max(1, Math.min(10, Math.floor(msg.chunkedMaxSections))));
|
||||
}
|
||||
}
|
||||
|
||||
// ────────────── Datacollect (slash 명령) ──────────────
|
||||
@@ -657,6 +665,8 @@ export class SettingsPanelProvider implements vscode.WebviewViewProvider {
|
||||
maxAutoSteps: cfg.get<number>('maxAutoSteps', 50) ?? 50,
|
||||
maxContextSize: cfg.get<number>('maxContextSize', 32000) ?? 32000,
|
||||
chatTemperature: cfg.get<number>('chatTemperature', 0.3) ?? 0.3,
|
||||
chunkedSwitchTokens: cfg.get<number>('chunkedSwitchTokens', 50000) ?? 50000,
|
||||
chunkedMaxSections: cfg.get<number>('chunkedMaxSections', 3) ?? 3,
|
||||
},
|
||||
datacollect: {
|
||||
bridgeUrl: cfg.get<string>('datacollectBridgeUrl', '') || '',
|
||||
|
||||
+28
-6
@@ -456,8 +456,14 @@ export class CacheManager {
|
||||
* - Error Recovery Matrix 기반의 Transient/Permanent 오류 자동 분류 및 복구
|
||||
*/
|
||||
export class AgentEngine {
|
||||
/** Outline LLM이 제안한 N을 강제로 1..MAX_SECTIONS 로 clamp 한다. */
|
||||
static readonly MAX_SECTIONS = 5;
|
||||
/**
|
||||
* Hard ceiling — *사용자 config 가 어떤 값이든 절대 넘을 수 없다*. 안전망.
|
||||
* 실제 사용 상한은 `getConfig().chunkedMaxSections` (default 3). 사용자가
|
||||
* Astra Settings 에서 1~10 사이 조정.
|
||||
*
|
||||
* factory.ts ChunkedWriter.MAX_SECTIONS_HARD_CEILING 와 일치.
|
||||
*/
|
||||
static readonly MAX_SECTIONS_HARD_CEILING = 10;
|
||||
|
||||
/**
|
||||
* 단일 writer agent — 같은 모델이 outline / section / polish 역할을 번갈아
|
||||
@@ -526,18 +532,28 @@ export class AgentEngine {
|
||||
|
||||
// --- Phase 1: Outline ---
|
||||
// 1번의 LLM 호출로 답변을 몇 개 섹션으로 쪼갤지 결정. JSON 배열 반환.
|
||||
// 사용자 config 의 chunkedMaxSections 를 outline persona 에 전달 — outline
|
||||
// LLM 이 그 상한을 지키도록 prompt 에 박힘. parseOutline 의 cap 도 같은
|
||||
// 값 사용해서 LLM 이 룰 어겨도 강제로 자름.
|
||||
const cfgMaxSections = (() => {
|
||||
try {
|
||||
const { getConfig } = require('../config') as typeof import('../config');
|
||||
const v = getConfig().chunkedMaxSections;
|
||||
return Math.max(1, Math.min(AgentEngine.MAX_SECTIONS_HARD_CEILING, v ?? 3));
|
||||
} catch { return 3; } // 안전 fallback
|
||||
})();
|
||||
const outlineRaw = await this.executeStep(
|
||||
state, 'outline', '답변 구조 잡는 중...',
|
||||
() => this.resilientExecute(state, this.writer, 'Outline', prompt, brainContext, signal, onProgress, {
|
||||
...options,
|
||||
context: brainContext,
|
||||
signal,
|
||||
config: { ...options?.config, role: 'outline' },
|
||||
config: { ...options?.config, role: 'outline', maxSections: cfgMaxSections },
|
||||
}),
|
||||
`outline::${prompt}`, brainContext, signal, onProgress
|
||||
);
|
||||
|
||||
const outline = this.parseOutline(outlineRaw);
|
||||
const outline = this.parseOutline(outlineRaw, cfgMaxSections);
|
||||
const sections = outline.sections;
|
||||
|
||||
// outline 이 빈 배열(`reason === 'empty'`)을 반환했다면 LLM 이
|
||||
@@ -920,10 +936,16 @@ export class AgentEngine {
|
||||
* (옛 버전엔 길이로만 구분이 안 돼서 empty 와 fallback 이 혼동돼
|
||||
* parse 실패가 우발적 single-pass 전환을 일으켰음).
|
||||
*/
|
||||
private parseOutline(raw: string): {
|
||||
private parseOutline(raw: string, cap?: number): {
|
||||
sections: Array<{ heading: string; scope: string }>;
|
||||
reason: 'ok' | 'empty' | 'fallback';
|
||||
} {
|
||||
// cap 미지정 시 hard ceiling 으로 안전 보호. 정상 호출 경로에선 호출자가 사용자
|
||||
// config 값 (chunkedMaxSections) 을 전달함.
|
||||
const effectiveCap = Math.max(1, Math.min(
|
||||
AgentEngine.MAX_SECTIONS_HARD_CEILING,
|
||||
cap ?? AgentEngine.MAX_SECTIONS_HARD_CEILING,
|
||||
));
|
||||
const fallbackSections = [{ heading: '본문', scope: '사용자 요청 전체를 다루는 단일 섹션' }];
|
||||
if (!raw || !raw.trim()) {
|
||||
return { sections: fallbackSections, reason: 'fallback' };
|
||||
@@ -949,7 +971,7 @@ export class AgentEngine {
|
||||
}))
|
||||
.filter((o) => o.heading.length > 0);
|
||||
if (cleaned.length === 0) return null;
|
||||
return { kind: 'sections', list: cleaned.slice(0, AgentEngine.MAX_SECTIONS) };
|
||||
return { kind: 'sections', list: cleaned.slice(0, effectiveCap) };
|
||||
} catch { return null; }
|
||||
};
|
||||
|
||||
|
||||
@@ -250,11 +250,15 @@ describe('AgentEngine — chunked flow', () => {
|
||||
expect(roles.filter(r => r === 'section')).toHaveLength(3);
|
||||
});
|
||||
|
||||
test('outline MAX_SECTIONS 초과 응답은 5개로 cap 된다', async () => {
|
||||
// 7개를 줘도 5개로 잘려야 함
|
||||
test('outline 가 config 의 chunkedMaxSections 보다 많이 반환해도 그 값으로 cap', async () => {
|
||||
// 사용자 config 의 chunkedMaxSections (default 3) 가 실제 상한.
|
||||
// outline LLM 이 더 많이 반환해도 parseOutline 에서 cap.
|
||||
const { getConfig } = require('../src/config') as typeof import('../src/config');
|
||||
const expectedCap = getConfig().chunkedMaxSections;
|
||||
const overshoot = expectedCap + 4;
|
||||
const writer = new MockChunkedWriter(
|
||||
JSON.stringify(
|
||||
Array.from({ length: 7 }, (_, i) => ({ heading: `H${i}`, scope: `s${i}` }))
|
||||
Array.from({ length: overshoot }, (_, i) => ({ heading: `H${i}`, scope: `s${i}` }))
|
||||
)
|
||||
);
|
||||
const engine = new AgentEngine(writer);
|
||||
@@ -262,7 +266,11 @@ describe('AgentEngine — chunked flow', () => {
|
||||
'chunked_cap', CHUNKED_PROMPT, 'ctx', createAbortSignal(), noopProgress,
|
||||
);
|
||||
const sectionCount = writer.calls.filter(c => c.role === 'section').length;
|
||||
expect(sectionCount).toBe(AgentEngine.MAX_SECTIONS);
|
||||
expect(sectionCount).toBe(expectedCap);
|
||||
});
|
||||
|
||||
test('Hard ceiling (10) 은 config 한도 위 보호 안전망 — 절대 초과 금지', () => {
|
||||
expect(AgentEngine.MAX_SECTIONS_HARD_CEILING).toBe(10);
|
||||
});
|
||||
|
||||
test('outline JSON 파싱 실패 시 단일 "본문" 섹션으로 폴백', async () => {
|
||||
|
||||
Reference in New Issue
Block a user