Research Worker
El specialist de research del lab. Opera el Research Framework end-to-end: parsea la query, corre el phase DAG, persiste los artifacts y devuelve el report.
Status
Implemented. El Research Worker drive las 8 phases del framework contra search adapters reales y produce typed artifacts que el report final agrega. El worker es el entry point primario del lab para research questions no triviales.
Role
El job del Research Worker es convertir una research question en un report estructurado y auditable. Dada una query, el worker:
- Inicializa una mission en el framework (
tool-a-research-init). - Corre la phase de candidate-gathering (
tool-b-hotel-researchpara missions de hotel, o la domain tool apropiada). - Corre las paralelas de review, price y YouTube phases.
- Corre la Perplexity synthesis phase (P3) cuando está habilitada.
- Agrega todo a través del report generator (
tool-h-report-generator). - Opcionalmente entrega el report por email (
tool-g-email-sender).
El worker no razona sobre los internals del framework. Delega cada step a una domain tool. Las tools mismas hacen el trabajo; el worker las coordina y maneja el lifecycle (checkpointing, retry, recovery).
Architecture
El Research Worker es un thin runtime que:
- Recibe una research request del Coordinator.
- Envuelve la request en una mission (
mission_id+query). - Llama al CLI del framework (
research_cli.pyorun_research.py) para ejecutar la mission. - Observa el state de la mission y el artifact store.
- Surface el resultado (un fichero
report.md) al Coordinator.
El worker es intencionalmente pequeño. Toda la complejidad vive en el framework y las domain tools. El valor del worker es su lifecycle management: sabe cuándo empezar, cuándo esperar, cuándo reintentar, cuándo pausar y cuándo rendirse.
Domain tools
El worker invoca 8 domain tools del framework. Cada
tool es un package Python autocontenido con su propio
src/, tests/, skills/, y docs/.
tool-a-research-init
Inicializa una research run, escribe task.json,
maneja el checkpoint state.
| Aspect | Detail |
|---|---|
| Trigger | El primer step de cada mission. |
| Inputs | research_id, mission (la query parseada). |
| Outputs | <run_dir>/task.json, el run directory, el initial checkpoint.json. |
| Files | src/init.py, src/checkpoint.py, src/schema.py. |
| Tests | tests/test_init.py. |
| Status | Implemented. |
La tool expone tres funciones:
init_research(research_id, mission): crea el run directory y escribetask.json.save_checkpoint(research_id, phase, progress): persiste el current state acheckpoint.json.load_checkpoint(research_id): lee el current state.
El checkpoint format es CheckpointState (Pydantic) con
fields: research_id, current_phase, completed_phases,
next_action, progress, errors, updated_at.
El skill de la tool (.agents/skills/research-init/SKILL.md)
documenta el workflow: initialize, write task, write
candidates, create hotels/, create sources/, create
notes/. Cada step escribe un checkpoint; el failure
escribe un error checkpoint con la next recovery
action.
tool-b-hotel-research
Hotel research end-to-end: gather, normalize, rank, write.
| Aspect | Detail |
|---|---|
| Trigger | Hotel research missions. |
| Inputs | research_id, search terms (multi-language). |
| Outputs | candidates.json, hotels/ directory, sources/ directory. |
| Files | src/search.py, src/gather.py, src/report.py. |
| Tests | tests/test_hotel.py. |
| Status | Implemented. |
El SKILL de la tool describe un workflow de 3 phases:
- Search → candidates. Usar
web_searchcon terms como "[destination] 5 star hotel spa booking.com" o "hoteles [destination] 5 estrellas spa". Usarweb_fetchen sites agregadores. Save acandidates.json. - Normalize → hotels/. Para cada candidate, build un normalized hotel record con name, address, rating, price range, amenities y source URLs.
- Rank → report. Rankear los candidates por score (rating, price, amenities match) y escribir un comparison report.
La tool soporta búsqueda multi-language (English y Spanish como mínimo) y está diseñada para travel-planning queries.
tool-c-review-research
Trae y rankea reviews para una lista de candidates.
| Aspect | Detail |
|---|---|
| Trigger | Cuando una mission necesita review aggregation. |
| Inputs | research_id, lista de candidate IDs. |
| Outputs | reviews.json, ranked per candidate. |
| Files | src/reviewer.py, src/analyzer.py, src/ranker.py. |
| Tests | tests/test_review.py. |
| Status | Implemented. |
La tool trae reviews de sources configuradas, agrega distribuciones de sentiment y topic, y rankea candidates por review quality.
tool-d-photo-research
Descarga y organiza fotos para los candidates.
| Aspect | Detail |
|---|---|
| Trigger | Cuando una mission necesita visual assets. |
| Inputs | research_id, lista de candidate IDs. |
| Outputs | photos/ directory, organizado per candidate. |
| Files | src/download.py, src/cache.py, src/organizer.py. |
| Tests | tests/test_photo.py. |
| Status | Implemented. |
La tool descarga fotos, las cachea localmente, y las organiza en una directory structure estable keyed por candidate ID.
tool-e-price-analysis
Compara prices entre providers y descuento datos históricos.
| Aspect | Detail |
|---|---|
| Trigger | Cuando una mission necesita price comparison. |
| Inputs | research_id, lista de candidate IDs, date range. |
| Outputs | prices.json, comparison matrix, historical chart. |
| Files | src/comparator.py, src/discounter.py, src/historian.py. |
| Tests | tests/test_price.py. |
| Status | Implemented. |
La tool trae prices actuales, calcula estadísticas históricas (min, max, median, trend) y produce una comparison matrix que el report puede renderizar como tabla.
tool-f-video-scrape
Scrapea y extrae video metadata y transcripts.
| Aspect | Detail |
|---|---|
| Trigger | Cuando una mission necesita video coverage. |
| Inputs | research_id, lista de candidate IDs. |
| Outputs | youtube_search.json, transcripts.json. |
| Files | src/scraper.py, src/metadata.py, src/downloader.py. |
| Tests | tests/test_video.py. |
| Status | Implemented. |
La tool envuelve youtube_transcript_api y yt-dlp.
Encuentra videos relevantes, extrae transcripts y los
chunkea para el report.
tool-g-email-sender
Envía un email templated (report delivery, alerts).
| Aspect | Detail |
|---|---|
| Trigger | Cuando el usuario pide entregar un report por email. |
| Inputs | Recipient list, report path, template. |
| Outputs | Email sent, queue updated. |
| Files | src/sender.py, src/queue.py, src/templates.py. |
| Tests | tests/test_email.py. |
| Status | Implemented. |
El SKILL de la tool describe el workflow: confirm recipients, confirm report metadata, render el body desde un template, adjuntar los report files, enviar por la queue. La queue soporta retries y rate limiting.
tool-h-report-generator
Formatea el report final desde los artifacts.
| Aspect | Detail |
|---|---|
| Trigger | Cuando la mission tiene todos los artifacts que necesita. |
| Inputs | research_id, output directory, format. |
| Outputs | report.html, report.md, o report.json. |
| Files | src/generator.py, src/formatter.py, src/exporter.py. |
| Tests | tests/test_generator.py. |
| Status | Implemented. |
La main class de la tool es ReportGenerator. Hace:
- Carga los compiled candidate data desde
<research_root>/<research_id>/candidates.json. - Llama a
compile_report_datapara mergear todos los artifacts. - Renderiza el report en el format pedido
(
html,md, ojson). - Escribe el report al output directory.
Los formats soportados se exponen a través de la
constant SUPPORTED_FORMATS.
Mission lifecycle desde la perspectiva del worker
El worker es responsable del lifecycle de la mission. Internamente, el worker sigue esta state machine:
Cada transition escribe un checkpoint. Se le puede
preguntar al worker en cualquier momento "¿cuál es el
state de la mission X?" y leerá phase.json para
responder.
Checkpointing
Cada domain tool escribe un checkpoint después de cada
step. La checkpointing layer es checkpoint.py de
tool-a-research-init:
save_checkpoint(research_id, phase, progress): append el current step acheckpoint.json.load_checkpoint(research_id): lee el current state.- En failure, escribe un
errorcheckpoint con el fieldnext_actionpoblado con el recovery step.
El worker usa el checkpoint para:
- Resumir una mission pausada (leer state, retomar donde lo dejó).
- Debuggear un failure (leer el último step exitoso y el error message).
- Reportar progress al Coordinator.
El checkpoint format está documentado en Mission Lifecycle → Checkpointing.
Retry and recovery
El worker aplica una default retry policy a cada tool invocation. La policy es:
- 3 attempts total (1 original + 2 retries).
- Exponential backoff con full jitter: 1s, 2s, 4s, capado a 30s.
- Retry en 429 y 5xx; no retry en 4xx (excepto 429).
- No retry en tool-internal errors (un Pydantic validation failure no es retryable; es un bug).
Si se agotan todos los retries, el worker marca la
mission como failed_retryable y surface el error al
Coordinator. El Coordinator puede pedirle al worker que
reintente la mission con parámetros distintos o aceptar
el failure.
Parallelism
El worker corre domain tools independientes en paralelo cuando la mission lo permite. El parallel set es:
- F2 (reviews) + F4 (price) + Y1 (YouTube search) — todos
en
parallel_group=1.
El worker usa un ThreadPoolExecutor con un worker
count configurable (SCOUTE_PARALLEL_WORKERS, default
3). El runner coordina para que Y2 espere a Y1, y F6
espere a todo.
El worker no empieza una mission nueva hasta que la actual está en un terminal state. Esto es by design: una mission a la vez mantiene simple la state machine del worker.
Configuration
El worker se configura a través de environment variables. Las relevantes son:
| Variable | Default | Purpose |
|---|---|---|
SCOUTE_RESEARCH_ROOT | /tmp/scoute/research | Dónde se crean los run directories. |
SCOUTE_RUNS_DIR | /tmp/scoute/runs | Dónde viven mission state y artifacts. |
SCOUTE_PARALLEL_WORKERS | 3 | Max concurrent parallel phases. |
SCOUTE_DEFAULT_RETRY | 2 | Default retry count. |
SCOUTE_LOG_LEVEL | INFO | El log level del worker. |
La lista completa está en Environment Variables.
Performance
Una mission típica con las 8 phases tarda 2-5 minutos wall-clock. El bottleneck suele ser la parallel phase de review-and-price (depende de adapter latency y rate limits). La YouTube phase puede tardar más cuando participan muchos candidates.
El worker reporta un progress percentage cada 30 segundos al Coordinator. El Coordinator surface el progress al usuario.
Failure modes
| Failure | Worker response |
|---|---|
tool-a fails to initialize | Devolver un error al Coordinator; no se arrancó nada. |
| Una domain tool fails terminally | Marcar la mission failed_retryable; el Coordinator puede reintentar. |
| Una parallel phase fails parcialmente | Continuar con las otras parallel phases; marcar el report como partial. |
| Checkpoint write fails | Loguear el error; continuar (el próximo checkpoint reintentará). |
| Report generation fails | Devolver los partial artifacts en lugar de un report. |
| Email send fails | Devolver el report path; el usuario puede entregarlo manualmente. |
| All tokens exhausted (P3 only) | Saltar P3; continuar con el resto del report. |
El failure catalog está en Failure Catalog.
Relationship with the Coordinator
El Coordinator forward las research requests al worker y
recibe el resultado (un report.md path o un summary).
El worker no inicia trabajo de forma independiente.
El handoff es one-way: Coordinator → Worker → result. El worker no le hace clarifying questions al usuario; el Coordinator aclara el intent antes de enviar la request.
El worker también reporta el mission state al
Coordinator on demand. El Coordinator puede preguntar
"¿cuál es el state de la mission X?" y el worker
devolverá el contenido de phase.json más un short
summary.
Relationship with the Media Agent
El Media Agent y el Research Worker se prototiparon inicialmente en un shared runtime. La architecture los modela como logical agents separados porque sus responsibilities, tools y risk profiles difieren. Ver ADR-001.
Los dos agents no comparten memoria. El Media Agent no tiene persistent memory layer; el Research Worker tiene su propia workspace memory para mission artifacts.
Future work
- Multi-mission parallelism. Permitir que varias
missions corran concurrentemente, aisladas por
research_id. - Streaming reports. Stream el report según se genera, en lugar de esperar a que F6 termine.
- Cross-mission artifacts. Permitir que una mission use los artifacts de otra mission como inputs.
- Per-tool rate limits. Hacer configurables los rate limits per tool, no solo per adapter.
- Result caching. Cachear resultados entre missions con la misma query.