fix: 시점 표현("최근/지금/현재")을 화제 무관 라이브데이터 신호로 일반화
세션 papa@ca71bc1c(로그 12804행): "마두로 미국에 잡혀가지 않았나?"에 세 턴
연속 도구 호출 없이 "체포되지 않았다"고 확신에 차 정정했는데, 실제로는
사용자가 맞았다(이미 미국에 체포·압송된 상태). 직전 커밋에서 체포/사임 등
개별 어휘를 isLiveDataRequest에 추가해 이 사고는 막았지만, 그 방식 자체가
화이트홀이었다 — 다음번엔 태풍도 체포도 아닌 제3의 화제에서 또 뚫린다.
화제별 어휘 목록 대신 "최근/지금/현재"가 들어가면 화제를 안 가리고 걸리도록
일반화했다. 근거는 실사용 로그 403건 전수조사: 이 세 표현이 들어간 66건
(16.4%) 중 오탐 0건 — 비트코인 시세·현직 대통령·최신 모델 버전 등 전부
정당한 실시간 조회였다. 새 레이어가 추가로 잡아낸 32건 중 31건도 마찬가지로
필요한 검색이었고, "지금 몇 시야?"만 이미 시스템 프롬프트에 있는 시각을
다시 검색시키는 사소한 낭비였다(오답은 아님). "요즘"은 실사용 예가 없어
후보에서 뺐다 — "요즘 어떻게 지내?" 같은 잡담과 부딪힐 위험을 검증 없이
감수하지 않는다.
넓히는 과정에서 진짜 버그 두 개가 드러났다:
- isLiveDataRequest가 isExemptFromVerification(지서버/클로서버 등 개인
인프라 예외)을 애초에 한 번도 거치지 않고 있었다. 파일 상단 주석은 "모든
검증 가드가 이 예외를 자동으로 적용받는다"고 주장했지만 사실이 아니었다.
시점 표현을 넓게 걸자 "지서버 최근에 왜 이렇게 느려"가 웹검색 리마인더에
걸릴 뻔하면서 드러났다.
- "시어엔진"(SearXNG 별칭)이 예외 목록에 없었다. 실사용 로그의 "현재
시어엔진 상태 어때?"는 웹검색으로 검증 불가능한 로컬 인프라 질문이다.
둘 다 함께 고쳤다. 부수 효과로, 오타("잡ㅎ간") 때문에 이전 커밋의 체포 어휘
목록이 못 잡던 마두로 사건의 첫 턴도 "현재" 표현으로 이제 잡힌다.
코딩/실행형 요청(isExecutionLikeRequest)과 인사말(isGreetingLikeMessage)은
계속 제외한다 — "지금 이 코드 확인해줘"에 검색 리마인더가 끼어들면 안 된다.
검증: tests/prompt-gates.test.ts 6개 추가, 196개 전부 통과. 실제 채팅에서
"비트코인 현재 가격이 얼마야?" → web_search 호출 확인, "지서버 지금 몇 개
모델 로드돼있나" → 웹검색 없이 로컬 처리 확인.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,10 @@
|
|||||||
// Add new private-only terms here — every verification-forcing gate picks it up automatically.
|
// Add new private-only terms here — every verification-forcing gate picks it up automatically.
|
||||||
const EXEMPT_FROM_VERIFICATION = [
|
const EXEMPT_FROM_VERIFICATION = [
|
||||||
/지서버|클로서버|z-server|claw-server/i,
|
/지서버|클로서버|z-server|claw-server/i,
|
||||||
|
// SearXNG의 사용자 별칭([[feedback_searxng_nickname]]). 2026-08-09 "현재 시어엔진 상태 어때?"가
|
||||||
|
// 실사용 로그에 나왔는데 이 목록에 없어서, 시점표현 net을 달면 웹검색 불가능한 로컬 인프라
|
||||||
|
// 질문에까지 "검색부터 하라"는 리마인더가 걸릴 뻔했다.
|
||||||
|
/시어엔진/i,
|
||||||
];
|
];
|
||||||
|
|
||||||
export function isExemptFromVerification(message: string): boolean {
|
export function isExemptFromVerification(message: string): boolean {
|
||||||
@@ -64,12 +68,31 @@ export function isNewsRequest(message: string): boolean {
|
|||||||
|
|
||||||
export function isLiveDataRequest(message: string): boolean {
|
export function isLiveDataRequest(message: string): boolean {
|
||||||
const m = String(message || '');
|
const m = String(message || '');
|
||||||
|
// 지서버/클로서버/시어엔진 같은 개인 인프라는 웹검색으로 검증할 수 없다. 이 함수는 지금까지
|
||||||
|
// 이 예외를 전혀 거치지 않았다 — "체포/최근" 같은 넓은 패턴을 새로 추가하면서 "지서버 최근에
|
||||||
|
// 왜 이렇게 느려" 같은 문장이 웹검색 리마인더에 걸릴 뻔했다.
|
||||||
|
if (isExemptFromVerification(m)) return false;
|
||||||
return isNewsRequest(m)
|
return isNewsRequest(m)
|
||||||
// 태풍/지진 등 재해 현황이 빠져 있었다. 2026-08-09 "지금 태풍 돌핀 어디 있지?"가 이 게이트를
|
// 태풍/지진 등 재해 현황이 빠져 있었다. 2026-08-09 "지금 태풍 돌핀 어디 있지?"가 이 게이트를
|
||||||
// 그냥 통과해 검색 없이 답변됐고(로그에 TOOL 호출 0건), 모델이 일주일 전 대화에서 쓴 것과
|
// 그냥 통과해 검색 없이 답변됐고(로그에 TOOL 호출 0건), 모델이 일주일 전 대화에서 쓴 것과
|
||||||
// 똑같은 좌표·기압·풍속을 그대로 다시 뱉었다. 날씨만큼이나 시시각각 변하는 값들이다.
|
// 똑같은 좌표·기압·풍속을 그대로 다시 뱉었다. 날씨만큼이나 시시각각 변하는 값들이다.
|
||||||
|| /(날씨|기온|예보|미세먼지|황사|환율|주가|금리|시세|장마)/.test(m)
|
|| /(날씨|기온|예보|미세먼지|황사|환율|주가|금리|시세|장마)/.test(m)
|
||||||
|| /(태풍|호우|폭우|폭설|한파|폭염|지진|해일|쓰나미|산불|홍수|가뭄|열대저압부)/.test(m)
|
|| /(태풍|호우|폭우|폭설|한파|폭염|지진|해일|쓰나미|산불|홍수|가뭄|열대저압부)/.test(m)
|
||||||
|
// 공인의 신상 변동(체포/실각 등)도 같은 실패 패턴을 냈다. 2026-08-09 "마두로 잡혀간 이후
|
||||||
|
// 어때?"에 세 턴 연속 도구 호출 없이 "체포되지 않았다"고 확신에 차 답했는데, 실제로는
|
||||||
|
// 사용자가 맞았다(마두로는 미국에 체포·압송된 상태). 다음 턴에 검색을 하긴 했지만 카테고리만
|
||||||
|
// 훑어 인물명으로는 안 찾았고, 사용자가 뉴스 URL을 직접 붙여준 뒤에야 정정했다. 체포·사임
|
||||||
|
// 같은 사건은 정치인/유명인이 아니어도 검색 비용이 크지 않으므로 넓게 잡는다.
|
||||||
|
// "구속"만 예외 — DB/코딩에서 "제약조건"(구속조건, 구속받지 않고) 뜻으로도 흔히 쓰여, 체포
|
||||||
|
// 맥락을 나타내는 활용형(구속됐/구속영장/구속 상태 등)일 때만 걸리게 좁혔다.
|
||||||
|
|| /(체포|구속\s*(?:됐|되었|돼|당했|영장|기소|수사|송치|상태)|수감|압송|기소|사임|하야|퇴진|실각|타계|서거|피살|암살|쿠데타|잡혀가|잡혀갔|잡힌)/.test(m)
|
||||||
|
// 위 목록들은 결국 화이트홀 문제다 — 다음번엔 태풍도 체포도 아닌 제3의 화제에서 또 뚫린다.
|
||||||
|
// 2026-08-09 실사용 로그 403건 중 "최근/지금/현재"가 들어간 66건(16.4%)을 전수 확인한 결과
|
||||||
|
// 전부 정당한 실시간 조회 요청이었다(비트코인 시세, 대통령이 누구인지, 서울 날씨 등) — 0건의
|
||||||
|
// 오탐. "요즘"은 실사용 예가 없어 후보에서 뺐다(잡담 "요즘 어떻게 지내?"와 부딪힐 위험을
|
||||||
|
// 검증 없이 감수하지 않는다, [[feedback_calibrate_on_real_data]]). 코딩/파일 등 실행형
|
||||||
|
// 요청과 인사말은 계속 제외한다 — "지금 이 코드 확인해줘"에 검색 리마인더가 끼어들면 안 된다.
|
||||||
|
|| (/(최근|지금|현재)/.test(m) && !isExecutionLikeRequest(m) && !isGreetingLikeMessage(m))
|
||||||
|| /\b(weather|forecast|stock price|exchange rate)\b/i.test(m);
|
|| /\b(weather|forecast|stock price|exchange rate)\b/i.test(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,13 @@ describe('isExemptFromVerification — 개인 인프라 별칭', () => {
|
|||||||
test('일반 제품 질문은 면제 아님', () => {
|
test('일반 제품 질문은 면제 아님', () => {
|
||||||
assert.equal(isExemptFromVerification('RTX 5090 스펙 알려줘'), false);
|
assert.equal(isExemptFromVerification('RTX 5090 스펙 알려줘'), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// [2026-08-09] "현재 시어엔진 상태 어때?"가 실사용 로그에 나왔는데 이 목록에 없었다.
|
||||||
|
// SearXNG의 사용자 별칭([[feedback_searxng_nickname]]) — 지서버/클로서버와 같은 이유로
|
||||||
|
// 웹검색이 검증할 수 없는 로컬 인프라다.
|
||||||
|
test('시어엔진(SearXNG 별칭)도 면제', () => {
|
||||||
|
assert.equal(isExemptFromVerification('현재 시어엔진 상태 어때?'), true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isFactualInfoRequest — 검증이 필요한 질문 분류', () => {
|
describe('isFactualInfoRequest — 검증이 필요한 질문 분류', () => {
|
||||||
@@ -186,6 +193,49 @@ describe('isNewsRequest / isLiveDataRequest', () => {
|
|||||||
assert.equal(isLiveDataRequest('방금 지진 났어?'), true);
|
assert.equal(isLiveDataRequest('방금 지진 났어?'), true);
|
||||||
assert.equal(isLiveDataRequest('내일 호우 예보 있나'), true);
|
assert.equal(isLiveDataRequest('내일 호우 예보 있나'), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// [2026-08-09] 실제 사고(로그 12804행 세션 papa@ca71bc1c): "마두로 미국에 잡혀가지
|
||||||
|
// 않았나?"에 세 턴 연속 도구 호출 없이 "체포되지 않았다"고 확신에 차 정정했는데, 실제로는
|
||||||
|
// 사용자가 맞았다(마두로는 이미 미국에 체포·압송된 상태). 공인의 체포/사임/사망 같은 신상
|
||||||
|
// 변동도 날씨·재해만큼 시시각각 변하는데 아무 가드에도 안 걸렸다.
|
||||||
|
test('체포·사임 등 공인 신상 변동도 라이브 데이터다', () => {
|
||||||
|
assert.equal(isLiveDataRequest('마두로 미국에 잡혀가지 않았나?'), true);
|
||||||
|
assert.equal(isLiveDataRequest('얼마전 미국에 잡혀가 구속 상태자너?'), true);
|
||||||
|
assert.equal(isLiveDataRequest('그 배우 구속됐대?'), true);
|
||||||
|
assert.equal(isLiveDataRequest('대통령 하야했다는게 사실이야?'), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// "구속"은 DB/코딩에서 "제약조건"(구속조건, ~에 구속받지 않고) 뜻으로도 흔히 쓰인다.
|
||||||
|
// 체포 맥락의 활용형(구속됐/구속영장/구속 상태 등)일 때만 걸려야, 정상적인 설계·코딩
|
||||||
|
// 질문이 "지금 답하지 말고 검색부터 하라"는 리마인더에 걸려 방해받지 않는다.
|
||||||
|
test('"구속"의 비체포 의미(제약조건)는 라이브 데이터로 안 잡는다', () => {
|
||||||
|
assert.equal(isLiveDataRequest('이 조건에 구속받지 않고 자유롭게 설계해줘'), false);
|
||||||
|
assert.equal(isLiveDataRequest('변수 스코프 구속 범위가 어떻게 되지'), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 날씨/재해/체포 같은 화제별 목록은 결국 화이트홀이다 — 다음번엔 제3의 화제에서 또 뚫린다.
|
||||||
|
// "최근/지금/현재"가 들어가면 화제를 안 가리고 걸리도록 일반화했다. 근거는 실사용 로그
|
||||||
|
// 403건 전수조사: 이 세 표현이 들어간 66건(16.4%) 중 오탐 0건 — 전부 비트코인 시세, 대통령이
|
||||||
|
// 누구인지, 서울 날씨 등 정당한 실시간 조회였다. "요즘"은 실사용 예가 없어 후보에서 뺐다.
|
||||||
|
test('[2026-08-09] "최근/지금/현재"는 화제 무관하게 라이브 데이터로 본다', () => {
|
||||||
|
assert.equal(isLiveDataRequest('비트코인 현재 가격이 얼마야?'), true);
|
||||||
|
assert.equal(isLiveDataRequest('지금 한국 대통령이 누구야?'), true);
|
||||||
|
assert.equal(isLiveDataRequest('최근에 나온 라마(Llama) 모델 버전이 뭐야?'), true);
|
||||||
|
assert.equal(isLiveDataRequest('베네수엘라는 지금 어때?'), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('시점표현이 있어도 코딩/실행형 요청은 라이브 데이터가 아니다', () => {
|
||||||
|
// "지금 이 코드 확인해줘"에 "검색부터 하라"는 리마인더가 끼어들면 안 된다.
|
||||||
|
assert.equal(isLiveDataRequest('지금 이 코드 확인해줘'), false);
|
||||||
|
assert.equal(isLiveDataRequest('현재 파일 구조 만들어줘'), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('시점표현이 있어도 개인 인프라 질문은 라이브 데이터가 아니다', () => {
|
||||||
|
// 지서버/시어엔진은 웹검색으로 검증 불가능하다 — isLiveDataRequest가 이 예외를 아예
|
||||||
|
// 거치지 않던 기존 버그도 함께 고쳐졌다.
|
||||||
|
assert.equal(isLiveDataRequest('지서버 최근에 왜 이렇게 느려'), false);
|
||||||
|
assert.equal(isLiveDataRequest('현재 시어엔진 상태 어때?'), false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isUsableGroundingResult — 빈 검색결과 판별', () => {
|
describe('isUsableGroundingResult — 빈 검색결과 판별', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user