/** * [v2.2.315] 웹 실접속 결정론 표시 — "사이트 분석 요청에 실접속 없이 일반론 답변" * (koritips.com 실사례) 재발 방지. 접속 실패는 답변 상단 경고, 성공은 하단 출처. */ import { buildWebFetchNotice, isUrlContextSuccessBlock } from '../src/lib/contextBuilders/urlContext'; import { shouldUseMultiAgentWorkflow } from '../src/lib/contextBuilders/multiAgentRouting'; describe('buildWebFetchNotice', () => { test('전부 실패 → 상단 경고 (실측 아님 명시)', () => { const n = buildWebFetchNotice({ urls: ['https://koritips.com'], succeeded: [], failed: [{ url: 'https://koritips.com', reason: 'HTTP 403' }] }); expect(n.top).toContain('사이트 실접속 실패'); expect(n.top).toContain('koritips.com'); expect(n.top).toContain('HTTP 403'); expect(n.bottom).toBe(''); }); test('성공 → 하단 실접속 출처', () => { const n = buildWebFetchNotice({ urls: ['https://koritips.com'], succeeded: ['https://koritips.com'], failed: [] }); expect(n.top).toBe(''); expect(n.bottom).toContain('실접속 확인'); }); test('URL 없는 턴은 무표시', () => { expect(buildWebFetchNotice(null)).toEqual({ top: '', bottom: '' }); expect(buildWebFetchNotice({ urls: [], succeeded: [], failed: [] })).toEqual({ top: '', bottom: '' }); }); test('성공 블록 판별', () => { expect(isUrlContextSuccessBlock('[URL CONTENT — 실데이터 · https://x.com]\n...')).toBe(true); expect(isUrlContextSuccessBlock('[URL CONTENT — https://x.com]\n상태: 접근 실패 — ...')).toBe(false); }); }); describe('shouldUseMultiAgentWorkflow — URL 은 단일 턴 강제', () => { test('URL 포함 조사 요청은 워크플로우로 빠지지 않는다 (웹 접근 경로가 없으므로)', () => { expect(shouldUseMultiAgentWorkflow('HTTPS://KORITIPS.COM 사이트 조사해서 보고서 작성해줘', true)).toBe(false); expect(shouldUseMultiAgentWorkflow('https://example.com 리서치해줘', false)).toBe(false); }); }); // [v2.2.317] 본문 불일치 검증 — 접속 성공인데 실데이터 미반영 일반론 답변 감지. import { detectWebContentMismatch, formatWebContentMismatchFooter, extractSuccessMeta } from '../src/lib/contextBuilders/urlContext'; describe('detectWebContentMismatch', () => { const stats = { urls: ['https://www.koritips.com'], succeeded: ['https://www.koritips.com'], failed: [], succeededDetail: [{ url: 'https://www.koritips.com', title: "Kori's Tips - Lived. Learned. Shared.", sample: 'Blogging Tips 카테고리의 글 모음. 블로그 운영, 애드센스 수익, 워드프레스 세팅, 검색 노출 가이드를 다룹니다. blogging adsense wordpress guide', }], }; const hallucinated = '이 사이트는 IT 엔지니어 및 개발자를 위한 전문 지식 공유 플랫폼으로 판단됩니다. 기술 기획자와 테크프리너가 핵심 구독층이며, 아키텍처 관점의 심층 콘텐츠를 제공합니다. '.repeat(4); test('실데이터 표현이 전혀 반영 안 된 일반론 = 불일치', () => { const r = detectWebContentMismatch(hallucinated, stats); expect(r.mismatch).toBe(true); }); test('본문 표현을 인용한 답변은 통과', () => { const grounded = `이 블로그는 Blogging Tips 카테고리 중심으로 블로그 운영과 애드센스 수익, 워드프레스 세팅을 다룹니다. ${'상세 설명. '.repeat(30)}`; expect(detectWebContentMismatch(grounded, stats).mismatch).toBe(false); }); test('접속 실패 턴은 판정 대상 아님 (실패는 상단 경고가 담당)', () => { expect(detectWebContentMismatch(hallucinated, { urls: ['https://x.com'], succeeded: [], failed: [{ url: 'https://x.com', reason: 'x' }] }).mismatch).toBe(false); }); test('footer 에 재요청 안내 포함', () => { expect(formatWebContentMismatchFooter()).toContain('본문 불일치'); }); }); describe('extractSuccessMeta', () => { test('성공 블록에서 제목·본문 표본 추출', () => { const block = '[URL CONTENT — 실데이터 · https://x.com]\n제목: 테스트 제목\n아래 본문만 근거로 답하라.\n\n본문 내용입니다.'; const m = extractSuccessMeta('https://x.com', block); expect(m.title).toBe('테스트 제목'); expect(m.sample).toContain('본문 내용'); }); });