Files
homeclaw/tests/web-comparison-split.test.ts
T
kimandClaude Opus 5 de5407b701 feat: 비교 검색에서 스펙이 실린 페이지 본문을 자동으로 가져온다
쪼개기(9f31d76)로 좋은 출처가 결과 목록엔 들어왔는데도 답변이 여전히
일반론이었다. 원인은 모델이 결과를 안 펼쳐본다는 것이다 — 프로덕션
로그 3,300줄에서 web_fetch 호출은 딱 1번, 그것도 기상청 페이지였고
GPU 스펙 질문에선 0번이다. 도구 설명에 "결과 URL을 web_fetch로 읽어라"라고
써 있는데도 그렇다. 스니펫엔 제품 이름만 있고 수치는 본문에 있으니,
스니펫만 보고 쓰면 "80 라인업이 60 라인업보다 빠르다"에서 멈춘다.

사용자 질문("스펙을 제조사에서 찾기가 어려운가?")에 답하려고 상위 세
출처를 실제로 가져와 재보니, 제조사 공식 페이지가 셋 중 가장 나빴다:

  nvidia.com    6016자, 스펙 키워드 0개 — 표가 JS 렌더링이라 본문은
                "This site requires Javascript" + "Game Changer" 홍보 문구
  techpowerup    275자, 스펙 키워드 0개 — 봇 차단
  nanoreview    Cores/TMUs/Boost Clock/Bandwidth/TFLOPS 전부 있음

그래서 가져오되 거르는 구조로 했다. hasSpecContent가 통과시킨 첫 페이지
하나만 대상별로 붙인다 — 이게 없으면 "Game Changer" 6KB가 컨텍스트를
차지하고, 여러 장을 붙이면 모델이 읽고 지나가야 할 양만 늘어난다.

실측(같은 쿼리): nanoreview 4080·5060 본문 2장 자동 확보, 9.4초,
RTX 4080 Cores 9728 / 게이밍 76 vs RTX 5060 Cores 3840 / 게이밍 43 —
"얼마나 빠른지"에 실제로 답할 수 있는 수치가 처음으로 들어왔다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 13:47:42 +09:00

118 lines
5.2 KiB
TypeScript

/**
* splitComparisonQuery — 비교 질문을 대상별 검색으로 쪼개기
*
* 2026-08-10에 "A vs B로 합치지 말고 따로 검색하라"를 web_search 설명에 넣었는데, 08-11 프로덕션
* 로그에서 모델이 세 턴 연속으로 정확히 금지된 형태를 그대로 날렸다:
* web_search({"query":"RTX 4080 vs RTX 5060 performance comparison specs"})
* 매번 technical.city 링크가 죽어 있었고(2/5 dead) 답변은 수치 0건의 일반론이었다. 설명으로
* 강제가 안 되니 코드에서 쪼갠다.
*
* 이 테스트가 지키는 선: 쪼갤 만한 것만 쪼갠다. "Lakers vs Celtics"를 "Lakers"/"Celtics" 두
* 검색으로 만들면 원래 질문의 의미가 사라진다 — 오탐 쪽이 더 비싸다.
*/
import { test, describe } from 'node:test';
import assert from 'node:assert/strict';
import { splitComparisonQuery, hasSpecContent } from '../src/tools/web';
describe('쪼개야 하는 쿼리', () => {
test('실제 사고 쿼리 — 비교 동사는 버리고 속성어는 남긴다', () => {
const r = splitComparisonQuery('RTX 4080 vs RTX 5060 performance comparison specs');
assert.deepEqual(r, { a: 'RTX 4080 performance specs', b: 'RTX 5060 performance specs' });
});
test('속성어가 없어도 양쪽이 모델명 꼴이면 쪼갠다', () => {
const r = splitComparisonQuery('RTX 4080 vs RTX 5060');
assert.deepEqual(r, { a: 'RTX 4080 specs', b: 'RTX 5060 specs' });
});
test('어제 검증한 대역폭 케이스', () => {
const r = splitComparisonQuery('RTX 3090 vs RTX 4080 Super memory bandwidth');
assert.ok(r);
assert.ok(r!.a.startsWith('RTX 3090'));
assert.ok(r!.b.startsWith('RTX 4080 Super'));
assert.ok(r!.a.includes('bandwidth') && r!.b.includes('bandwidth'));
});
test('한국어 "와/과 ... 비교"도 쪼갠다', () => {
assert.deepEqual(
splitComparisonQuery('RTX 4080과 5060 성능 비교'),
{ a: 'RTX 4080 성능', b: '5060 성능' },
);
});
test('vs. / versus 표기도 인식한다', () => {
assert.ok(splitComparisonQuery('M4 Max vs. M3 Ultra benchmark'));
assert.ok(splitComparisonQuery('A100 versus H100 tflops'));
});
});
describe('쪼개면 안 되는 쿼리', () => {
test('스포츠 대진 — 속성어도 모델번호도 없다', () => {
assert.equal(splitComparisonQuery('Lakers vs Celtics'), null);
});
test('일반 검색은 건드리지 않는다', () => {
assert.equal(splitComparisonQuery('오늘 서울 날씨'), null);
assert.equal(splitComparisonQuery('RTX 4080 memory bandwidth'), null);
});
test('비교 대상이 셋이면 판단하지 않는다', () => {
assert.equal(splitComparisonQuery('A vs B vs C specs'), null);
});
test('"결과"처럼 과/와로 끝나는 단어에 잘못 걸리지 않는다', () => {
assert.equal(splitComparisonQuery('검색결과 스펙'), null);
assert.equal(splitComparisonQuery('효과 성능'), null);
});
test('빈 입력', () => {
assert.equal(splitComparisonQuery(''), null);
assert.equal(splitComparisonQuery(null as any), null);
});
});
/**
* hasSpecContent — 가져온 본문에 진짜 스펙이 실려 있는지
*
* 이 판정이 필요한 이유는 2026-08-11 실측이다. 비교 검색이 물어오는 상위 세 출처를 실제로
* 가져와 봤더니, 제조사 공식 페이지가 셋 중 가장 쓸모없었다:
* nvidia.com 6016자, 스펙 키워드 0개 (스펙 표가 JS 렌더링, 본문은 홍보 문구)
* techpowerup 275자, 스펙 키워드 0개 (봇 차단)
* nanoreview Cores/TMUs/Boost Clock/Bandwidth/TFLOPS 전부 있음
* 이 필터가 없으면 "Game Changer" 6KB가 모델 컨텍스트를 차지한다.
*/
describe('hasSpecContent — 본문에 수치가 실렸는지 판정', () => {
test('NVIDIA 공식 페이지 본문은 통과시키지 않는다 (실측 발췌)', () => {
assert.equal(hasSpecContent(
'GeForce RTX 5060 Family Graphics Cards | NVIDIA. This site requires Javascript in order to '
+ 'view all its content. Game Changer. The NVIDIA GeForce RTX 5060 Ti and RTX 5060, powered by '
+ 'the NVIDIA Blackwell architecture, enables game-changing AI capabilities.',
), false);
});
test('봇 차단 페이지도 통과시키지 않는다 (실측 발췌)', () => {
assert.equal(hasSpecContent(
'NVIDIA GeForce RTX 5060 Specs | TechPowerUp GPU Database. Automated bot check in progress. '
+ 'Your browser must support Javascript.',
), false);
});
test('nanoreview 본문은 통과시킨다 (실측 발췌)', () => {
assert.equal(hasSpecContent(
'GeForce RTX 5060. Graphics Processor: GB206-250. Cores: 3840. TMUs / ROPs: 120 / 48.',
), true);
});
test('대역폭·클럭 표기도 스펙으로 본다', () => {
assert.equal(hasSpecContent('Memory Bandwidth: 936 GB/s'), true);
assert.equal(hasSpecContent('Boost Clock: 2505 MHz'), true);
assert.equal(hasSpecContent('CUDA Cores: 9728'), true);
});
test('숫자 없는 일반 문장은 스펙이 아니다', () => {
assert.equal(hasSpecContent('이 카드는 게이밍 성능이 매우 뛰어납니다.'), false);
assert.equal(hasSpecContent(''), false);
});
});