Skip to content

Playwright/passthrough 개선 + telemetry + reranker title-injection (v0.2.0 → next)#2

Merged
dongwhee merged 25 commits intomainfrom
develop
Apr 16, 2026
Merged

Playwright/passthrough 개선 + telemetry + reranker title-injection (v0.2.0 → next)#2
dongwhee merged 25 commits intomainfrom
develop

Conversation

@dongwhee
Copy link
Copy Markdown
Collaborator

요약

develop에 v0.2.0 이후 누적된 23 커밋을 main으로 머지. 세 갈래:

  1. Playwright + passthrough 긴급 수정 (최근 3커밋, 이번 PR 트리거)
  2. C4 telemetry (opt-in JSONL 수집)
  3. C3 리랭커 title injection (플래그 게이트)
  4. VLM prompt v3 (프로필 앵커 규칙 개선)

1. Playwright + passthrough 수정 (이번 PR 트리거)

컨테이너화된 trawl이 api.open-meteo.com/v1/forecast 같은 suffix 없는 JSON API에 호출하면 404로 보고되던 문제 조사 → 실제 원인은 베이스 이미지와 pip playwright 버전 불일치로 인한 Playwright 호출 전면 실패였음. 수정 세 건:

  • feat(passthrough) (29ad273): passthrough.probe(url) — fetch 이전 단계의 HEAD 프로브로 suffix 없는 JSON/XML API를 즉시 감지. pipeline._try_passthrough() 헬퍼로 URL suffix → HEAD 프로브를 통합. 단위 4개 + E2E 1개 테스트 추가.
  • fix(playwright) (b24072f): _BrowserHolder.ensure()chromium.launch() 실패 시 pw.stop() 호출로 좀비 greenlet dispatcher 정리. 이게 없으면 이후 호출이 "Sync API inside asyncio loop"라는 오진성 에러로 바뀌어 진짜 원인(바이너리 부재 등) 진단 불가.
  • chore: playwright 1.58 pin + image bump (9948739): playwright>=1.40playwright==1.58.0, Dockerfile 베이스 v1.47.0-jammyv1.58.0-jammy. 두 값이 반드시 동반 변경돼야 한다는 주석을 양쪽에 명시.

재빌드된 컨테이너 대상 mcp__trawl__fetch_page로 검증: fetcher_used=passthrough-probed, path=raw_passthrough, 정상 JSON 바디 반환 (fetch_ms=1232, 4710 chars).

2. C4 telemetry (10 커밋)

TRAWL_TELEMETRY=1 opt-in으로 fetch_relevant() 완료 시 ~/.cache/trawl/telemetry.jsonl에 JSONL 한 줄 추가. 64 MB single-generation rotation, 0600/0700 파일 권한, 스키마는 src/trawl/telemetry.py. 목적: notes/RESEARCH.md C4 결정의 선결 데이터 수집.

3. C3 reranker title injection (7 커밋)

TRAWL_RERANK_INCLUDE_TITLE 플래그로 리랭커 입력에 페이지 타이틀과 섹션 heading을 합쳐 보내는 경로 추가. extraction.extract_title() 헬퍼, pipeline.page_title 전파, PipelineResult.page_title 노출. 플래그-게이트라 기본 동작 변화 없음.

4. VLM 프로필 프롬프트 v3 (1 커밋)

profiles/prompts.py: "contiguous-run 앵커 규칙"으로 사이드바/네비 오인식 완화.

Test plan

  • tests/test_passthrough.py 18/18 pass (기존 14 + probe/HEAD 관련 4 신규)
  • 로컬 단위 테스트 전체: pytest tests/ --ignore=tests/test_pipeline.py --ignore=tests/test_youtube_fetcher.py → 116 pass (youtube는 PR 외 기존 실패, import 이슈)
  • Docker 재빌드 후 mcp__trawl__fetch_page로 open-meteo URL 검증 — 정상 JSON 수신
  • parity matrix (tests/test_pipeline.py) — 로컬 llama-server 의존, CI에서는 미실행
  • 배포 후 telemetry 파일 기록 확인 (opt-in 환경에서)

lyla and others added 25 commits April 15, 2026 10:37
RESEARCH.md was internal brainstorming that leaked into main with the
v0.2.0 release. Move to notes/ (gitignored) so future in-progress
write-ups stay local.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DeepQSE-inspired (title, query, chunk) format without fine-tuning.
Feature-flagged, accept/reject via 12-case parity matrix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
html.parser treats <b> inside <title> as text (per spec), so
get_text() alone returns the literal markup. Strip residual HTML
tags via regex after get_text() to guarantee clean title text.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Split document-string assembly into _build_documents() pure helper.
rerank() now accepts page_title keyword arg and gates title injection
on TRAWL_RERANK_INCLUDE_TITLE env var (default on).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
C4(Index-based extraction) 판단에 필요한 profile hit/miss/fallback
실측 데이터를 장기간 수집하기 위한 최소 구현 설계.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7개 task로 TDD 기반 구현. pipeline.fetch_relevant 래핑 +
단위 테스트 + 파리티 매트릭스 검증 + 문서 업데이트.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add geteuid() root guard to test_record_swallows_io_errors so it
  self-skips on containerized CI runners (plan fix carried over).
- Move hashlib_sha1_prefix helper to module top alongside _sample_result.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename the existing fetch_relevant body to _fetch_relevant_impl and add
a thin public wrapper that calls _fetch_relevant_impl then
telemetry.record(result). Telemetry failures never propagate. Also adds
the integration test test_fetch_relevant_records_telemetry which drives
the error-return path (empty query, no profile) to verify one JSONL line
is written without requiring a network or embedding server.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
기존 trawl 캐시(~/.cache/trawl/profiles, visits.json)와 동일 디렉터리에
텔레메트리도 저장하도록 기본값 변경. 장점:

- 프로젝트의 XDG 캐시 관례와 일관.
- 도커(mija 스택 등)가 이미 ~/.trawl:/root/.cache/trawl을 마운트하는
  경우 TRAWL_TELEMETRY=1 하나만 추가하면 동작, 경로 오버라이드 불필요.

이제 부모 디렉터리를 다른 캐시와 공유하므로, 기존의 부모 chmod 0o700
로직은 제거 (파일 자체의 0o600은 유지).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CLAUDE.md에 TRAWL_TELEMETRY 옵트인 기능 한 줄 요약 추가
  (향후 세션이 존재를 인지).
- 스펙에서 부모 디렉터리 chmod 0o700 문구 제거 - 공유 캐시
  디렉터리이므로 권한을 건드리지 않는 현 구현과 일치.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
KBO schedule 페이지에서 mapper가 결정적으로 실패(0/8)하던 문제 수정.
VLM이 "KT vs NC score 6 to 0", "broadcast info included" 같은 여러
DOM 요소를 합성한 문구/요약 문장을 앵커로 뽑아 DOM 매칭이 전부
실패했음. 규칙 (1)에 "CONTIGUOUS text run" 요구와 구체적인 bad/good
예시, 규칙 (2)에 "summary phrase 금지"를 추가.

검증:
- KBO 격리 실험 N=8: 0% → 100%.
- profile_eval 36 케이스: 33/36 → 34/36 (94%). wiki_kr_hangul,
  nyt_article 수정. react_usestate 1건 회귀 (inline code가 섞인
  긴 문장을 VLM이 한 줄로 뽑지만 DOM은 <code>로 쪼개져 있음).
- parity matrix 12/12 유지.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`api.open-meteo.com/v1/forecast`처럼 URL suffix 없는 JSON API가 여태
Playwright 경로로 빠진 뒤 Content-Type 사후 감지 단계에서 겨우 복구되던
걸, fetch 이전 단계의 짧은 HEAD 프로브로 즉시 식별하도록 변경.

- passthrough.probe(url, timeout_s=3.0): HEAD 응답 CT가 passthrough
  eligible이면 CT 반환, 아니면 None
- pipeline._try_passthrough(): URL suffix → HEAD 프로브 순서로 통합.
  기존 두 곳의 인라인 passthrough 블록을 이 헬퍼로 교체

테스트: probe 단위 4개 + fetch_relevant HEAD-프로브 E2E 1개 추가,
기존 post-detection 테스트는 HEAD 405로 설정해 그 경로를 계속 검증.
_BrowserHolder.ensure()가 chromium.launch() 실패 후에도 self._pw를
살려두던 버그 수정. 예외 시 pw.stop()으로 context를 닫고 re-raise.

재현: 베이스 이미지에 Playwright 바이너리가 없는 상태에서 첫 호출은
"Executable doesn't exist" 에러를 내지만, 이후 호출은 좀비 greenlet
dispatcher + event loop 때문에 "Sync API inside asyncio loop"라는
오진성 에러로 뒤바뀜. 이 변경으로 매 호출이 동일한 원인 에러를
반환하게 되어 실제 문제(이미지/버전 불일치 등) 진단이 가능해짐.
베이스 이미지(v1.47.0-jammy)에 박힌 Chromium과 pip가 설치하는
playwright Python 패키지(언핀 `>=1.40` → 1.58 자동 선택) 사이에
버전 드리프트가 생겨 컨테이너에서 모든 Playwright 호출이 실패.

- pyproject.toml: playwright>=1.40 → playwright==1.58.0
- Dockerfile: FROM v1.47.0-jammy → v1.58.0-jammy
- 두 값은 반드시 같이 bump되어야 한다는 주석을 양쪽에 명시

이후 이미지 재빌드 시 `playwright install chromium` 같은 런타임
재설치 없이 베이스 이미지의 /ms-playwright가 그대로 재사용됨.
v0.2.0 이후 develop에 누적된 ruff 위반 21건을 정리. CI의 lint job이
main 이전부터 실패 상태였고, 새 PR도 그 상태를 물려받고 있어 지금
같이 정리.

- I001 (import sorting): extraction.py, telemetry.py, reranking/wcxb
  테스트들 — ruff --fix로 자동 정렬
- E402 (module-level import not at top): passthrough.py의 time/dataclass/httpx,
  test_passthrough.py / test_wcxb_runner.py의 mid-file import를 파일 상단으로 호이스팅
- E741 (ambiguous l): test_wcxb_aggregate.py의 `for l in`을 `for loss in`으로 개명
@dongwhee dongwhee merged commit bce8cb1 into main Apr 16, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant