Files

15 KiB
Raw Permalink Blame History

English | Simplified Chinese

Development

This page covers local development: the environment, starting the app, the directory layout, dependencies and the lock file, tests, and the check scripts. It is for people who change the code. For why the system is designed this way, see Architecture and Design Decisions. The user interface is currently available in Simplified Chinese only.

Development environment

  • You need conda (miniforge is enough), Node 20+, and pnpm.
  • No Docker needed: the code sandbox is built on isolation primitives that ship with the operating system, with an optional upgrade to microVM (see Configuration).
  • You can try everything without an API key: the built-in Mock provider runs the whole orchestration path end to end (see Configuration).

Start

conda env create -f environment.yml   # first time: create the agentlab environment and install dependencies
./scripts/dev.sh                      # after that: one command starts the frontend and backend
  • Open http://localhost:5273 .
  • dev.sh finds the agentlab conda environment automatically and creates it from environment.yml when it doesn't exist. To use a different environment name, set AGENTLAB_CONDA_ENV.
  • Ports: the frontend defaults to 5273 and the backend to 8000; change them with AGENTLAB_WEB_PORT / AGENTLAB_PORT.
  • --host: the frontend listens on all network interfaces and prints the LAN address at startup, so phones and other computers on the same network can open it. The backend still listens only on 127.0.0.1, and the frontend dev server forwards the page's /api locally. Login is off by default, so only do this on a network you trust (see Login modes).
  • --postgres: use PostgreSQL for the app database; a development database is started in local docker (see Start together with the database).
  • Unrecognized arguments fail with an error; run ./scripts/dev.sh --help for usage.
  • dev.sh has hot reload on: changing a backend file means a service restart, and runs in progress show as interrupted with their checkpoints intact, ready to resume (see Resume from a checkpoint).

Directory layout

backend/                  FastAPI + LangGraph
  app/engine/             compiles and executes canvas JSON → StateGraph
    schema.py             graph definition and static validation
    compiler.py           compilation: node wrapping, events, retries, conditional routing
    runner.py             execution scheduling, event bus, interrupts and resumption
    issuance.py           issuance contract: number trace-back and the three-tier verdict
    evidence.py           clickable evidence: evidence ledger, citation markers, chunking and verifying report documents (pure functions)
    judge.py              claim-sentence judge: prefiltering, cost cap, writing verdicts back
    governance.py         publishing gates: governed lint and evidence gates G1–G5
    autofix.py            automatic pre-publish fixes; upgrade.py does the one-click upgrade to a traceable structure
    guards.py             agent guardrails: repeated calls, budgets, context compression
    review.py             output review: event-stream rule scan + model review
    expressions.py        template interpolation + AST-allowlist expression evaluation
    nodes/                executors for each node type (report.py is the report writer)
  app/data/               data source access: connections, SQL guard, schema introspection, spreadsheet import
  app/providers/          multi-model access and cost estimation
  app/sandbox/            four backends: microVM / Seatbelt / bubblewrap / bare subprocess
  app/tools/              tool registry, built-in tools, MCP, custom tools
  app/memory/             memory, knowledge base, inverted index, hybrid retrieval, document parsing
  app/api/                REST + WebSocket (the evidence API is in evidence.py)
frontend/                 React 19 + React Flow + Zustand
  src/canvas/nodeDefs.ts  node metadata: drives the properties panel, the node library, and the connection handles
  src/canvas/routing.ts   edge routing: staggered ports, corridor lanes, detours for long edges
  src/run/decode.ts       the single translation layer from the event stream to human-readable steps
  src/run/EvidenceDoc.tsx renders the report segment by segment: numbers, names, and quotes are all clickable fragments; EvidencePanel.tsx is the provenance chain
  src/store/studio.ts     canvas state + mapping from the event stream to highlights
environment.yml           conda environment definition
scripts/dev.sh            one-command startup
scripts/check-all.mjs     main entry for the frontend checks: runs every check-*.mjs in parallel lanes and summarizes them in one table

Dependencies and the lock file

  • backend/pyproject.toml specifies version ranges and backend/requirements.lock specifies exact versions: all backend dependencies (including transitive dependencies and all extras) are locked to the set the tests ran against, and the lock works across platforms.
  • environment.yml uses the lock file as a constraint when installing packages, so another machine gets the same set too, not whatever is newest that day.
  • The LangGraph / LangChain family is allowed only up to the next minor version in pyproject.toml: the engine depends directly on their checkpoint format, interrupt / resume, and streaming events, and these have changed behavior even in minor releases.
  • The checkpoint format itself lives in langgraph-checkpoint (langgraph only requires it to be <5, so it is pinned separately). Both checkpoint libraries, for SQLite and for PostgreSQL (langgraph-checkpoint-sqlite, langgraph-checkpoint-postgres), read and write according to it, and copy-to-postgres can move checkpoints only because both sides use the same format version.
  • From 3.1.3 on, langgraph-checkpoint-postgres requires langgraph-checkpoint 4.3, so it is locked at 3.1.2; upgrade the three together.
  • The same goes for aiosqlite and SQLAlchemy: db/base.py patches aiosqlite, and the connection pool runs into that patch when it closes connections. After the app database moves to PostgreSQL, the patch stays in place, because uploaded spreadsheets and manually registered SQLite data sources still go through aiosqlite.
  • [postgres] holds what the app database needs on PostgreSQL: on the SQLAlchemy side it goes through asyncpg (the same package [db] uses to connect to business databases), and the checkpoint library is the official langgraph-checkpoint-postgres, which only works with psycopg 3 and psycopg-pool. psycopg comes with [binary]: libpq is bundled in the wheel, so neither the image nor the local machine needs a separately installed PostgreSQL client.

Optional dependencies

environment.yml installs all of them by default. When installing extras separately, pass the lock file so the versions match what was tested:

pip install -e 'backend[docs]' -c backend/requirements.lock     # PDF / Word / PowerPoint / Excel parsing
pip install -e 'backend[db]' -c backend/requirements.lock       # MySQL / PostgreSQL / Oracle drivers
pip install -e 'backend[microvm]' -c backend/requirements.lock  # microVM sandbox
pip install -e 'backend[postgres]' -c backend/requirements.lock # PostgreSQL for the app database (asyncpg, psycopg 3, the official checkpoint library)
pip install -e 'backend[dev]' -c backend/requirements.lock      # pytest

Upgrading dependencies

Upgrading dependencies is something you do deliberately; after changing them, run the tests once before committing:

./scripts/lock-deps.sh --upgrade-package langgraph   # upgrade one package (plus whatever has to move with it)
./scripts/lock-deps.sh --upgrade                     # upgrade everything to the latest that pyproject allows
./scripts/lock-deps.sh                               # only added a new dependency: add it and leave the locked ones alone
pip install -e 'backend[microvm,dev,db,docs,postgres]' -c backend/requirements.lock   # install into the current environment

Regenerating the lock file requires uv; installing the dependencies themselves uses only pip.

Tests

# Backend: about 16000 tests; with -n auto they run in parallel in about 2–3 minutes
cd backend && env -u ANTHROPIC_API_KEY -u ANTHROPIC_BASE_URL -u ANTHROPIC_AUTH_TOKEN -u OPENAI_API_KEY \
  python -m pytest -n auto

# Frontend type check
cd frontend && npx tsc -b

# Run the same backend suite with the app database on PostgreSQL (starts a temporary postgres:16 container and deletes it afterwards)
PYTHON=$(conda run -n agentlab which python) ./scripts/test-postgres.sh
  • Don't leave out the env -u in front of pytest: providers without a configured key fall back to keys in environment variables, and the seed at startup would also use them to create a provider. With a real key left in the environment, the tests might really call the API.
  • For details on the PostgreSQL and multi-process tests, see PostgreSQL and Scaling.

A fake external system for acceptance tests

python scripts/fake-erp.py --port 8765

It can switch between "normal / time out after a successful write / 500 / 4xx", for acceptance-testing the different outcomes of writing to external systems; its API is described at the top of the script.

Frontend checks

# 43 items, 8 lanes in parallel by default. They connect to 5273 / 8000, so start ./scripts/dev.sh first
node scripts/check-all.mjs
# Start 2 extra "backend + vite" stacks used only by the checks, each with its own copy of the data, about 6.5 minutes; editing code while they run doesn't interrupt them
CHECK_DATA_SRC=<data-dir> node scripts/check-all.mjs --stacks 2 --lanes 12
# Run only some of the items (names without the check- prefix)
node scripts/check-all.mjs evidence publish
  • Full output is written to /tmp/agentlab-checks/<timestamp>/, and the terminal gets only the summary. Items that failed during the parallel run are rerun on their own at the end; ones that pass only on the rerun are listed separately as "Flaky".
  • With --stacks, each stack copies the data and picks free ports automatically; the backend runs without hot reload and vite doesn't watch files; everything is cleaned up after a finish, an error, or an interruption. --stacks supports SQLite only.
  • scripts/e2e-check.mjs really starts a run (Mock, costs nothing) and leaves records in the database, so it isn't part of check-all.
  • scripts/check-auth.mjs (login, navigation collapsed by role, 401 redirecting to login) has to run on its own against a separate service with local-account login turned on, so it isn't part of check-all either.

The 43 check-all items, in the order of ORDER in scripts/check-all.mjs:

Category Checks
No browser tokens (static design-token scan) copy (UI copy) trace (pure functions of the run-state core) decode (event translation layer)
Routing, components, and guardrails canvas-layout ui-kit guards
Shell, answer stream, and run states shell stream evidence (clickable evidence) run-states (node cards) canvas-fx (canvas surface layer)
Pages studio publish (pre-publish check and autofix) chat runs manage versions (uploaded spreadsheet versions) catalog (data catalog) catalog-loop (Table Copilot, review queue, export and import) catalog-profile (per-table data profiling jobs) catalog-changes (changes, the changes section in table details, scheduled maintenance, the background job bar) ui (end to end, with real data)
Review suite and exports schedules (schedules) compare (run comparison) verify (double-blind recomputation, adversarial review) inquiry (approval inquiry) challenge (adversarial review) bundle (verifiable report) whatif (what-if sandbox) report-export (report export)
Evaluation and caliber ledger evals (test suites, adding questions, starting and progress, results, comparison and A/B, caliber hits and repetition) ledger (the Caliber step in evidence, turn summaries, issuance and reconciliation, distillable questions)
App platform apps (app pages, stamps, clicking numbers to see provenance, entry validation, instance pages) inbox (joint sign-off progress, timeouts, the hash card for action confirmation, verification) app-editor (app editing, derivation, checks, choosing a disposition at publish time, Copilot) capabilities (capabilities, endpoints, identity, notifications, audit) access-keys (access keys) share (share links and the /s page) blueprint (creating apps, blueprint cards, trial runs, publish summaries) personal (personal apps) records (record list) files (file controls)

What the three verification layers cover

  • check-decode guards "is the translation right", check-stream guards "is it drawn right", and check-ui guards "does the whole path work". The three can each pass while the whole is broken: for example, every Step field is correct, yet the component renders it as a screen full of escaped JSON.
  • check-canvas-fx guards against "the events are right, but the UI didn't keep up": it pours scripted events into the store (going through the same applyEvent / NodeCard / FlowEdge the page uses; only the source of the events is a script) and asserts that the collaboration matrix really appears, the degree of parallelism is computed correctly, bar lengths are proportional to durations, the exit a branch took lights up, and light effects really aren't drawn when motion is turned off at the system level. Actually running a multi-agent graph needs a model connection and a wait of a dozen or more seconds, and still may not hit the moment when three members are running at once; a check like that would become a flaky failure.
  • check-canvas-layout alone guards the only computable part of "does the graph look good": it hands fixture graphs to the backend's /api/copilot/layout for layout, then directly calls import('/src/canvas/routing.ts') to compute paths with the very routing code running on the page, and makes geometric assertions: no two edges overlap on the same segment, no edge passes through the middle of someone else's card, and both ends of every edge attach to a node's border. When edges pile up in a tangle, check-ui and e2e-check won't say a word.

Documentation checks

python3 scripts/check-docs.py

It checks three things: README.md and docs/en/ must contain no Chinese characters or full-width symbols (code blocks included); relative links and anchors in README.md, README.zh-CN.md, every document under docs/, and deploy/README.md must all resolve; and docs/zh-CN/ and docs/en/ must have the same files, with the same heading levels in each pair. Run it after changing the docs.