Skip to content

fix: classify cache hits by current CV, not the legacy assessment column - #16

Merged
sangowu merged 1 commit into
mainfrom
fix/prefilter-cache-hit-respects-cv
Aug 7, 2026
Merged

sangowu merged 1 commit into
mainfrom
fix/prefilter-cache-hit-respects-cv

Conversation

@sangowu

@sangowu sangowu commented Aug 7, 2026

Copy link
Copy Markdown
Owner

接续 #15#15 修的是 assess 命令的补跑路径,本 PR 修同源问题在搜索管道里的那一半。

问题

search_prefilter.py 的缓存命中判定完全不看 CV:

if cached_job.assessment is not None:
    if not cached_job.assessment.is_relevant:
        continue        # ← 旧 CV 的拒绝判定,被当成永久结论

assessment 是 JD × CV 的函数,却不记录 cv_hash。后果是被某个旧 CV 判为"不相关"的职位,会在 continue 处被直接丢弃,任何新 CV 都无法让它重进管道。本地 293 条缓存里有 95 条处于这个状态。

改动

新增 classify_cache_hit(cached_job, cv_hash, language),按当前 cv_hash + match_prompt_versionjob_matches

情况 判定
当前 CV 有 match,recommendation != "skip" reuse → 复用,跳过 LLM
当前 CV 有 match,recommendation == "skip" skip
当前 CV 无 match(换过 CV / prompt 版本变更 / 从未评过) reassess → 用缓存 JD 内容重评,不重新 fetch
cv_hash 退回 legacy 列判定,保持既有行为

prefilter_jobs 新增 cv_hash 参数(agent.py 两个调用点都已接上;cv_hash 本来就在作用域里,只是没往下传)。顺带恢复了被 del language 丢掉的 language 参数。

保留无 cv_hash 的降级路径是必要的:该分支下 flush_assessmentshas_cv 为假,若把这些职位塞进 patch_pending 会导致它们不进结果集。

验证

新增 tests/test_search_prefilter.py(9 个用例),覆盖四种判定、prompt 版本失效、legacy 降级路径,以及 prefilter_jobs 的集成行为。全量 233 passed

对正式缓存的只读验证(不调用 LLM),取旧 CV 拒绝过的真实职位:

被旧 CV 判为 is_relevant=False 的职位:
  progress partners|devops engineer      -> reassess
  doit|technical product owner           -> reassess
  exl service|ai platform engineer       -> reassess
  metlife|ml ops engineer                -> reassess
  metlife|data engineer                  -> reassess

with cv_hash   -> skipped=0  re-assess=5     ← 新行为
legacy (no cv) -> skipped=5  re-assess=0     ← 等价于旧行为

两条路径在同一份代码里对比,直接显示行为差异。

成本影响

换 CV 后的第一次搜索,命中缓存的职位会重新评估,不再静默复用旧 CV 的结论。这正是本 PR 要的语义,但不免费。

缓解:match_job_to_cvmatching.py:710)内部查 job_matchesreuse 分支不会重复调用 LLM;reassess 分支先走批量闸门(8 条/次)再精算,JD profile 也可复用。

已知残留:被闸门拒绝的职位不写 job_matches,因此每次搜索都会重新过一次闸门。批量闸门成本低,未在本 PR 处理。

🤖 Generated with Claude Code

https://claude.ai/code/session_012DbPx4FbswofbGn41anE1q

prefilter_jobs decided a cached job's fate from job_cache.assessment, a column
that records no cv_hash. A job rejected under an earlier CV was dropped by
`continue` before it could reach the pipeline, so no later CV could ever
reconsider it -- 95 of 293 jobs in the local cache were blocked this way.

Cache hits now go through classify_cache_hit, which looks up job_matches for
the current cv_hash and match prompt version:

  reuse    - match exists, recommendation != skip
  skip     - match exists, recommendation == skip
  reassess - no match for this CV (CV changed, prompt version bumped, or never
             scored); re-enters assessment on cached JD content, no re-fetch

Without a cv_hash the previous legacy-column behavior is kept, so CV-less runs
keep working and the no-CV path does not start dropping results.

Verified read-only against the real cache on jobs a previous CV had rejected:
passing the current cv_hash yields skipped=0 / re-assess=5, while the legacy
path on the same rows yields skipped=5 / re-assess=0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012DbPx4FbswofbGn41anE1q
@sangowu
sangowu merged commit a096c1d into main Aug 7, 2026
2 checks passed
@sangowu
sangowu deleted the fix/prefilter-cache-hit-respects-cv branch August 7, 2026 21:14
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