sdr_code_audit_2026_03_09
SDR Code Audit — 09 марта 2026
Ветка: eval (c10422d)
Аудитор: Feanor worker 09:00
Цель: проверить pipeline после 4 фиксов (QueryParser + reasoning + retry + anti-hallucination)
Зафиксированные фиксы (eval branch)
| Commit | Fix | Статус |
|---|---|---|
| e542890 | QueryParser prefix bug + FindingsExtraction.reasoning default | ✅ Корректно |
| 8f9f171 | 429 retry с exponential backoff (1/2/4s, max 3 попытки) | ✅ Корректно |
| c10422d | Anti-hallucination rules 7-8 в extraction prompt | ✅ Корректно |
| e684b91 | Benchmark config с llama-3.1-8b-instant | ✅ Корректно |
Детали аудита кода
1. FindingsExtraction schema (state.py)
reasoning: str = Field(default="")— default добавлен ✅ (fix e542890)is_relevant: bool = Field(default=True)— правильный default ✅findings: list[ResearchFinding] = Field(default_factory=list)— ✅
Старые parse ошибки из logs/session_2026-03-08_12-05-18.log (13 записей типа “❌ Source N extraction failed”) были вызваны отсутствием required field reasoning в JSON от llama-3.1-8b. Фикс e542890 добавил default="" → теперь пустой reasoning не ломает парсинг.
2. Retry logic (micro_researcher.py)
for attempt in range(3):
if "429" in err_str or "rate_limit" in err_str:
await asyncio.sleep(2 ** attempt) # 1s, 2s, 4s
continue
Корректно. Exponential backoff реализован правильно. ✅
Есть TWO-LEVEL retry:
- Внешний: extractor_model.with_retry(stop_after_attempt=N) в ExtractionOrchestrator
- Внутренний: 3-retry loop в MicroResearcherExecutor для 429
Это не баг, но может привести к длительным задержкам при массовых rate limits.
3. is_relevant logic (micro_researcher.py lines 110-112)
if result and hasattr(result, "is_relevant") and not result.is_relevant:
logger.info(f"⏭️ Source N: Marked as NOT RELEVANT, skipping")
return []
Логика корректна. Если is_relevant=False → пустой список (не галлюцинации). ✅
4. OpenAlex content handling (coordinator.py + openalex_search.py)
- coordinator использует
result.get("raw_content") or result.get("content", "")✅ - openalex_search возвращает
raw_content = "Title + meta + Abstract"когда аннотация есть - При отсутствии аннотации: только title + meta (~100-150 chars)
- В таком случае rule 8 корректно помечает is_relevant=False ✅
НЕТ БАГА: поведение “is_relevant:false при пустом контенте” является ПРАВИЛЬНЫМ. Лучше пропустить источник без аннотации, чем галлюцинировать.
5. StandardExtractor fallback (extraction.py)
При ошибке парсинга (не token limit): retry 3 раза без изменения контекста.
Это немного неэффективно, но не критично.
6. Extraction path routing (ExtractionOrchestrator)
- При наличии
coordinator_sources→ MicroResearcherOrchestrator (по-источнично) - Иначе → StandardExtractor (весь контекст сразу)
Оба пути корректны. Coordinator path — основной для production. ✅
Итоговая оценка
Статус pipeline: ✅ Логически корректен после 4 фиксов
Ожидаемый рост качества (при смене модели на claude-haiku):
- Нет parsing failures (haiku надёжнее llama-8b в structured output)
- Лучший is_relevant классификатор (меньше false negatives)
- Более точные findings (меньше галлюцинаций даже без rule 8)
- Прогноз: с 8.7/30 → 18-22/30 (ориентир из аналогичных SDR систем)
Блокеры к production eval:
1. ANTHROPIC_API_KEY для claude-haiku (ask Даниила)
2. Merge eval→main (propose)
Рекомендация
Мержить eval→main с текущими 4 фиксами безопасно. Код проверен.
После merge — запустить re-eval с claude-haiku для честного сравнения.