Lab Notes
Agents

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:

  1. Inicializa una mission en el framework (tool-a-research-init).
  2. Corre la phase de candidate-gathering (tool-b-hotel-research para missions de hotel, o la domain tool apropiada).
  3. Corre las paralelas de review, price y YouTube phases.
  4. Corre la Perplexity synthesis phase (P3) cuando está habilitada.
  5. Agrega todo a través del report generator (tool-h-report-generator).
  6. 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.py o run_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.
Loading diagram…

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.

AspectDetail
TriggerEl primer step de cada mission.
Inputsresearch_id, mission (la query parseada).
Outputs<run_dir>/task.json, el run directory, el initial checkpoint.json.
Filessrc/init.py, src/checkpoint.py, src/schema.py.
Teststests/test_init.py.
StatusImplemented.

La tool expone tres funciones:

  • init_research(research_id, mission): crea el run directory y escribe task.json.
  • save_checkpoint(research_id, phase, progress): persiste el current state a checkpoint.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.

AspectDetail
TriggerHotel research missions.
Inputsresearch_id, search terms (multi-language).
Outputscandidates.json, hotels/ directory, sources/ directory.
Filessrc/search.py, src/gather.py, src/report.py.
Teststests/test_hotel.py.
StatusImplemented.

El SKILL de la tool describe un workflow de 3 phases:

  1. Search → candidates. Usar web_search con terms como "[destination] 5 star hotel spa booking.com" o "hoteles [destination] 5 estrellas spa". Usar web_fetch en sites agregadores. Save a candidates.json.
  2. Normalize → hotels/. Para cada candidate, build un normalized hotel record con name, address, rating, price range, amenities y source URLs.
  3. 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.

AspectDetail
TriggerCuando una mission necesita review aggregation.
Inputsresearch_id, lista de candidate IDs.
Outputsreviews.json, ranked per candidate.
Filessrc/reviewer.py, src/analyzer.py, src/ranker.py.
Teststests/test_review.py.
StatusImplemented.

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.

AspectDetail
TriggerCuando una mission necesita visual assets.
Inputsresearch_id, lista de candidate IDs.
Outputsphotos/ directory, organizado per candidate.
Filessrc/download.py, src/cache.py, src/organizer.py.
Teststests/test_photo.py.
StatusImplemented.

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.

AspectDetail
TriggerCuando una mission necesita price comparison.
Inputsresearch_id, lista de candidate IDs, date range.
Outputsprices.json, comparison matrix, historical chart.
Filessrc/comparator.py, src/discounter.py, src/historian.py.
Teststests/test_price.py.
StatusImplemented.

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.

AspectDetail
TriggerCuando una mission necesita video coverage.
Inputsresearch_id, lista de candidate IDs.
Outputsyoutube_search.json, transcripts.json.
Filessrc/scraper.py, src/metadata.py, src/downloader.py.
Teststests/test_video.py.
StatusImplemented.

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).

AspectDetail
TriggerCuando el usuario pide entregar un report por email.
InputsRecipient list, report path, template.
OutputsEmail sent, queue updated.
Filessrc/sender.py, src/queue.py, src/templates.py.
Teststests/test_email.py.
StatusImplemented.

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.

AspectDetail
TriggerCuando la mission tiene todos los artifacts que necesita.
Inputsresearch_id, output directory, format.
Outputsreport.html, report.md, o report.json.
Filessrc/generator.py, src/formatter.py, src/exporter.py.
Teststests/test_generator.py.
StatusImplemented.

La main class de la tool es ReportGenerator. Hace:

  1. Carga los compiled candidate data desde <research_root>/<research_id>/candidates.json.
  2. Llama a compile_report_data para mergear todos los artifacts.
  3. Renderiza el report en el format pedido (html, md, o json).
  4. 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:

Loading diagram…

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 a checkpoint.json.
  • load_checkpoint(research_id): lee el current state.
  • En failure, escribe un error checkpoint con el field next_action poblado 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:

VariableDefaultPurpose
SCOUTE_RESEARCH_ROOT/tmp/scoute/researchDónde se crean los run directories.
SCOUTE_RUNS_DIR/tmp/scoute/runsDónde viven mission state y artifacts.
SCOUTE_PARALLEL_WORKERS3Max concurrent parallel phases.
SCOUTE_DEFAULT_RETRY2Default retry count.
SCOUTE_LOG_LEVELINFOEl 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

FailureWorker response
tool-a fails to initializeDevolver un error al Coordinator; no se arrancó nada.
Una domain tool fails terminallyMarcar la mission failed_retryable; el Coordinator puede reintentar.
Una parallel phase fails parcialmenteContinuar con las otras parallel phases; marcar el report como partial.
Checkpoint write failsLoguear el error; continuar (el próximo checkpoint reintentará).
Report generation failsDevolver los partial artifacts en lugar de un report.
Email send failsDevolver 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.

See also