v2.2.308~310: Standing Rules + 조사 파이프라인 + LM Studio 추론 노출 수리
v2.2.308 — Standing Rules (Correction Loop ④): 행동/스타일 교정의 세션 영속화
- 행동 지적 감지("또 ~하네", "하지 말라고 했잖아" 등 5종) → LLM 이 명령형 규칙으로
정규화 → .astra/growth/standing-rules.json 즉시 저장 → 매 턴 보호 구역 주입.
self-reflection 이 컨텍스트 창(단기 기억)에 갇혀 새 세션에서 증발하던 문제 해결.
- 중복 지적은 hits 증가(토큰 자카드), 상한 10개 초과 시 자동 은퇴, 재지적 시 부활.
- "Astra: 상시 행동 규칙" 커맨드 — 활성/은퇴 규칙 + 실제 주입 블록 열람(도달 증거).
v2.2.309 — 조사 파이프라인: 파일명만 보고 상상하는 헛조사 4중 차단
- <investigate_files path focus> 액션: 폴더의 텍스트 파일을 코드가 강제 정독해
파일별 노트 주입(Map-Reduce). 내용 해시 캐시(.astra/cache/investigate)로 재조사
시 재사용 — 온디맨드 지식화.
- 조사 증거 게이트(행동 제약) + Hollow Investigation 감지(list만 하고 read 0회로
파일 서술 시 경고 footer — continuation 경로 포함) + actionStats turn 추적.
- g1nation.investigationModel: 조사형 요청만 상위 모델(예: claude-code:sonnet) 라우팅.
v2.2.310 — LM Studio LSEP 합성 추론 구분자 처리
- 일부 모델 조합에서 추론이 여는 마커 없이 content 앞에 흐르고
__LM_STUDIO_INTERNAL_LSEP_SYNTHETIC_REASONING_END_<hex>__ 로만 경계 표시 →
생각 과정 전체가 채팅에 노출되던 문제.
- LiveReasoningFilter: 마커 감지 시 streamReplace 신호로 화면 즉시 리셋(토큰 분절
대응 holdback 포함), sanitizeAssistantContent: 마커 이전 추론 최종 제거(이중 방어).
기타: tests/wikiSave.test.ts 상대경로 테스트 Windows 호환 수정(드라이브 문자).
검증: 전체 테스트 926건 통과 (신규 39건: standingRules 15 + investigationPipeline 16 + LSEP 8).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -63,3 +63,74 @@ describe('LiveReasoningFilter — 스트리밍 중 추론 구간 실시간 차
|
||||
expect(run(['답변 끝 <'])).toBe('답변 끝 <');
|
||||
});
|
||||
});
|
||||
|
||||
describe('[v2.2.310] LM Studio LSEP 합성 추론 구분자', () => {
|
||||
const MARKER = '__LM_STUDIO_INTERNAL_LSEP_SYNTHETIC_REASONING_END_f4e9a8d2c6b14d0c9e5f3a7b8c1d2e6a__';
|
||||
|
||||
/** run + separator 신호 관찰 버전. */
|
||||
function runWithSignal(tokens: string[]): { out: string; replacedAt: number[] } {
|
||||
const f = new LiveReasoningFilter();
|
||||
let out = '';
|
||||
const replacedAt: number[] = [];
|
||||
tokens.forEach((t, i) => {
|
||||
const visible = f.push(t);
|
||||
if (f.consumeSeparatorSignal()) {
|
||||
out = visible; // streamReplace 시뮬레이션 — 화면 리셋
|
||||
replacedAt.push(i);
|
||||
} else {
|
||||
out += visible;
|
||||
}
|
||||
});
|
||||
out += f.flush();
|
||||
return { out, replacedAt };
|
||||
}
|
||||
|
||||
test('END 마커 뒤 텍스트만 최종 화면에 남는다 (실사례 재현)', () => {
|
||||
const { out, replacedAt } = runWithSignal([
|
||||
'User message: "안녕, 잘지내?" 분석... Option 1... Final choice...',
|
||||
MARKER + '잘 지내고 있습니다. 별일 없으셨나요?',
|
||||
]);
|
||||
expect(out).toBe('잘 지내고 있습니다. 별일 없으셨나요?');
|
||||
expect(replacedAt).toEqual([1]); // 마커 도착 시점에 streamReplace 신호
|
||||
});
|
||||
|
||||
test('마커가 토큰 경계에 잘게 쪼개져도 잡는다', () => {
|
||||
const { out } = runWithSignal([
|
||||
'추론 텍스트 어쩌고 ',
|
||||
'__LM_STUDIO_INTERNAL_LSEP',
|
||||
'_SYNTHETIC_REASONING_END_f4e9a8d2c6b14d0c',
|
||||
'9e5f3a7b8c1d2e6a__',
|
||||
'실제 답변',
|
||||
]);
|
||||
expect(out).toBe('실제 답변');
|
||||
});
|
||||
|
||||
test('START 변형이 오면 END 까지 통째로 숨긴다', () => {
|
||||
const start = '__LM_STUDIO_INTERNAL_LSEP_SYNTHETIC_REASONING_START_abc123def456__';
|
||||
const { out } = runWithSignal(['답변 앞부분 ', start + '숨길 추론 ' + MARKER, '답변 뒷부분']);
|
||||
expect(out).toBe('답변 앞부분 답변 뒷부분');
|
||||
});
|
||||
|
||||
test('마커 없는 일반 밑줄 텍스트는 그대로 통과 (오탐 없음)', () => {
|
||||
const { out, replacedAt } = runWithSignal(['snake_case_variable 와 __init__ 메서드', ' 설명입니다']);
|
||||
expect(out).toBe('snake_case_variable 와 __init__ 메서드 설명입니다');
|
||||
expect(replacedAt).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[v2.2.310] sanitizeAssistantContent — LSEP 최종 정리 (이중 방어)', () => {
|
||||
const { sanitizeAssistantContent } = require('../src/lib/contextBuilders/outputSanitization');
|
||||
const MARKER = '__LM_STUDIO_INTERNAL_LSEP_SYNTHETIC_REASONING_END_f4e9a8d2c6b14d0c9e5f3a7b8c1d2e6a__';
|
||||
|
||||
test('마커 앞 추론 전체 제거, 답변만 남긴다', () => {
|
||||
const raw = 'User message 분석... Option 1... Final choice...' + MARKER + '잘 지내고 있습니다. 별일 없으셨나요?';
|
||||
expect(sanitizeAssistantContent(raw)).toBe('잘 지내고 있습니다. 별일 없으셨나요?');
|
||||
});
|
||||
test('마커가 여러 번이면 마지막 마커 뒤만', () => {
|
||||
const raw = '추론1' + MARKER + '중간' + MARKER + '최종 답변';
|
||||
expect(sanitizeAssistantContent(raw)).toBe('최종 답변');
|
||||
});
|
||||
test('마커 없으면 기존 동작 그대로', () => {
|
||||
expect(sanitizeAssistantContent('일반 답변입니다.')).toBe('일반 답변입니다.');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user