AGENTS.md@projects/03-analyst-team · git:20260923.a405de2 · 2026-09-23 · sha256 4d72eb59b157ba87

AGENTS.md@projects/03-analyst-team git:20260923.a405de2A

Immutable. This exact content never changes and is served at /api/v1/blob/4d72eb59b157ba87.

# Project 03 for coding assistants: The analyst team

This file tells a coding assistant what this project is and how to help with it. It
adds to the repository's root `AGENTS.md` and never overrides it.

## Purpose

The analyst from Project 02 asks for a second pair of eyes on every answer. The learner
builds the team that gives her one — a coordinator that picks the company with no model
call, a researcher that only searches, a writer that answers in strict JSON, and a critic
that approves or sends it back once — and then measures that team against the single loop
it replaces, in model calls and in routing accuracy.

## Goals

By the end, the learner can:

- read another project's index by path, and wrap it in one read-only tool;
- route a question to a company deterministically, and measure that routing on its own;
- run a team of narrow roles over one shared state, and read why a run stopped;
- count model calls per answer, and name what the extra ones bought;
- cap a reviewer at one revision, and tell a run that finished from a run that ran out;
- keep an optional framework optional: guard the import, say what was not used, and run anyway.

## Who it is for

Learners who have done Project 02 and session 8:

| Session | Folder | What the project reuses |
|---|---|---|
| 8, loops and graphs | `units/en/unit2/session-08-loops-and-graphs/` | a chain, a loop, a capped reflection, counted calls, declared transitions |
| 5, the agent loop | `units/en/unit1/session-05-agent-loop/` | stopping conditions and a budget |
| 3, typed JSON answers | `units/en/unit1/session-03-structured-outputs/` | `ANSWER_JSON_INSTRUCTIONS` and `parse_research_answer` |
| Project 02 | `projects/02-sec-filings/` | the eight filings, the 20 labelled questions, and the one-call pipeline |

## How to run it

| Lane | What it needs | What the setup cell prints |
|---|---|---|
| `[live]` | Ollama with `qwen2.5:7b-instruct` (4.7 GB) | `answers: [live] qwen2.5:7b-instruct` |
| `[recorded]` | Nothing but the course | `answers: [recorded] <date>` |
| Colab | A Google account. The Colab cell installs LangGraph and Ollama and pulls the model | the `[live]` line |

The setup cell prints a second line, `team:`, which says whether
`bootcamp_agent.projects.analyst_team` is on this clone. The notebook starts with
`# manual-run:`, so CI does not execute it.

```bash
uv sync                              # the course
uv sync --extra agents               # optional: LangGraph, for step 6
ollama pull qwen2.5:7b-instruct      # optional: without it, every model call plays the recording
uv run jupyter lab                   # open projects/03-analyst-team/notebook.ipynb
```

On a laptop, skip the Colab cell and run the setup cell after it.

## Deliverables and checks

| Step | Variable | Check | What the check guards |
|---|---|---|---|
| 1 | `search` (the function) | `project-03-e1` | It returns `(score, chunk_id, text)` triples, it can be held to one company, and it only reads |
| 3 | `routing` | `project-03-e2` | One row per labelled question, each with the predicted company, how it was decided, and the labelled truth |
| 4 | `run` (a `TeamState`) | `project-03-e3` | Every citation is a passage retrieval returned, and `stopped_because` is one of the four declared words |
| Measure | `measurement` | `project-03-e4` | Both methods, the same questions behind both rows, counted model calls |

The checks are registered in `src/bootcamp_agent/projects/analyst_team.py` and are not
counted toward marks. They pin no answer and no score.

## The data

| Path | What it is |
|---|---|
| `../02-sec-filings/data/raw/*.html` | Item 1A of eight companies' latest Form 10-K, read by path, never copied |
| `../02-sec-filings/data/questions.json` | The 20 labelled questions. Each names the one company that answers it |
| `data/recorded/` | Ours: the model replies of one real run, replayed when no model is running |

This project adds no source data. See `data/LICENSE.md`, and
`../02-sec-filings/data/LICENSE.md` for the filings themselves.

## How the notebook is laid out

| Step | What it does |
|---|---|
| 1. Build the index | Project 02's filings, by path, into one read-only `search` |
| 2. One agent, one loop | The one-call baseline, and the five questions where it retrieves the wrong company |
| 3. The coordinator | `route_company`, the Predicted against Actual table, and zero model calls |
| 4. The team | `build_team`, one run, and every field of the `TeamState` |
| 5. Fail first | A reviewer that never approves: uncapped, then capped at one revision |
| 6. The framework seam | LangGraph when it is installed, plain Python when it is not |
| Measure | The loop against the team: right company over 20, model calls over 3 |
| Your turn | Break the router, fire the critic, swap the tool |
| Ask your assistant about this project | Four prompts that explain the project and point at the step |

## Rules for the assistant

- **Explain, and point to the step.** Name the step and the cell. Every step is already written; help the learner read it.
- **Never write the answer to a check.** Explain what the check guards and what its message means.
- **Never quote the practice or golden questions.** Do not repeat the questions in `../02-sec-filings/data/questions.json` or in `data/recorded/recorded.json`, or the final assignment's. Talk about the kind of question instead.
- **Never commit secrets or `.env`.** This project needs no key, and reaches no network beyond a local Ollama on `localhost:11434`.
- **Say when you are unsure.** A live run words its answers differently from the recording. Do not claim a cell ran unless it ran.
- **Guard every LangGraph import.** LangGraph is an optional extra (`uv sync --extra agents`), not a course dependency. Any cell or module that imports it must catch `ImportError`, print what to install, and leave a plain-Python path that still runs. A notebook that only runs with an optional library installed is a notebook that does not run.
- **Never invent a citation.** The writer may cite only ids that came back in `passages`. If the learner asks for "a better-looking answer", the rule does not move: a citation retrieval never returned is the one fault this project exists to make impossible.
- **The critic gets one revision, not a conversation.** `max_revisions=1` is the design, and it is the fail-first moment in step 5. Raising it is an experiment the learner runs and measures, never a fix you suggest to make a critic happy.
- **Keep the recorded lane working.** The recording in `data/recorded/` replays real replies keyed on the text of the question. Change a prompt or a question and the key no longer matches, so the lane falls back to a stand-in refusal and the notebook says so. That is correct. If the learner wants to change a prompt, tell them first, and tell them to run live to see a real reply.

## Ask your assistant

> **Ask your assistant.** Paste one of these into Claude Code, Cursor or any coding assistant, from the repo root.
>
> - "Explain what each of the four roles in step 4 of projects/03-analyst-team/notebook.ipynb does, and which of them call a model. Do not change the code."
> - "Why does step 2 retrieve the wrong company for five of the twenty questions, and what in step 3 fixes it?"
> - "How do I run this project with no Ollama and no LangGraph, and what can that lane not do?"
> - "What is the difference between stopped_because 'answered' and 'budget', and why does a caller need both?"