panelagent is an LLM-free computation core backed by SQLite. SQLite is the
only runtime source of truth. Package seeds contain physical dye data and naming
aliases; instrument configuration and antibody inventories can be imported.
One database represents one laboratory and can hold any number of freely named
antibody libraries, such as Mouse, Human, Isotype, or Controls. Panel generation
and diagnosis always operate within exactly one selected library.
The package exports connect, ensure_schema, schema_version, Repo,
resolve_fluorochrome, find_valid_panels, generate_candidates, and
diagnose_conflicts. Repository instances receive an explicit SQLite
connection and keep no global state.
Repo also exposes list_libraries, list_instruments, and paginated
list_markers. Repo.antibodies accepts optional limit/offset keyword
arguments; existing callers remain unpaginated by default. Dye/channel queries,
fluorochrome resolution, generation, and diagnosis accept optional
instrument_id filtering. CLI and MCP resolve one instrument before calling
instrument-dependent APIs; direct Python calls without a filter retain their existing
behavior. find_valid_panels and generate_candidates accept an optional
max_search_nodes budget; exceeding it raises ValueError, rather than
returning an incomplete result as a completed search.
The schema contains instruments, channels, channel_mapping,
fluorochrome_spectra, fluorochrome_aliases,
fluorochrome_brightness, and antibodies. Quality state lives directly on an
antibody row in quality_flag, quality_notes, and quality_updated_at.
Writes are transactional, foreign keys are enabled, and databases use WAL.
The antibodies.library column identifies the owning library and participates
in the inventory uniqueness constraint.
Database path precedence is:
- CLI
--db PATH(accepted anywhere in the command line) PANELAGENT_DB~/.local/share/panelagent/panelagent.db
Write-capable CLI commands create parent directories automatically. MCP opens an existing initialized database read-only and never creates directories or schema.
Install and initialize (from a checkout, or the release wheel — see distribution.md):
pip install -e ".[mcp]" # repo 开发安装
pip install "panelagent-0.2.0-py3-none-any.whl[mcp]" # 发行 wheel(backend extra 另有)
pa init
pa init --config-dir config --csv Mouse=tests/fixtures/panel_inventory.csv
pa init --config-dir config --inventory-dir inventoryDiscovery and inspection:
pa library list --json
pa instrument list --json
pa marker list --library Mouse --limit 100 --offset 0 --json
pa db path
pa db stats --json
pa dye list --laser Violet --instrument-id 1
pa dye show AF488 --json
pa dye alias-add AF-488 "Alexa Fluor® 488"
pa channel list --laser Blue --instrument-id 1
pa antibody list --library Mouse --flag bad --limit 100 --offset 0
pa antibody search BioLegend --json
pa antibody annotate 1 --library Mouse --flag warn --note "Low signal"marker list 支持 --limit(1–500,默认 100)与 --offset(非负)分页。
antibody list/search 默认不分页;指定 --limit 后可配合 --offset 翻页。
dye/channel 查询支持 --instrument-id 过滤。
Pure panel computation:
pa panel generate --library Mouse --markers CD3,CD4,CD8 --max 10 --json
pa panel diagnose --library Mouse --markers pd1,ctla4,lag3 --jsonEvery subcommand accepts --json, including when it appears after nested
subcommands. Failures exit non-zero and print a machine-readable envelope
{"code": "...", "error": "..."}. Codes cover database access
(database_missing, database_error, database_unsupported), contract
validation (invalid_parameter, invalid_markers, duplicate_markers,
unknown_library, no_instruments, multiple_instruments,
unknown_instrument, antibody_not_found, target_exists) and entry-point
availability (backend_dependency_missing, web_unavailable, backend_unavailable,
skill_resources_missing). Zero stored instruments is rejected uniformly by
CLI and MCP (no_instruments). Panel generation excludes
quality_flag=bad by default; use --include-bad to override it. Warnings and
dyes with brightness level 1 or 2 are summarized in each candidate.
Service and distribution entry points (v0.2.0):
pa serve --host 127.0.0.1 --port 8000 # uvicorn 起 backend.app.main:app,注入 PANELAGENT_DB(需 backend extra)
pa web --web-dir /path/to/panelagent-web # 转调 panelagent.web,启动独立构建的 Next standalone 产物(需 [backend,mcp];库缺失时自动初始化)
pa skill install --dest ~/.agents/skills [--force] # 分发用户 skill 到 <dest>/panelagent-cli/The web bundle is built separately per platform (cd frontend && npm run build:bundle, producing dist/panelagent-web/); pa web never builds it.
Panel generation keeps the fewest-options-first marker order. At each
backtracking depth it sorts that marker's antibody options by the projected
laser utilization (current_load + 1) / capacity, with stable ID order for
ties. Loads are recalculated from the current assignments, including after
backtracking. This is a soft preference: no extra rejection rule is added, and
channel conflicts, quality filtering, brightness summaries, diagnosis, and the
maximum solution limit retain their existing behavior. It improves early
candidates without guaranteeing a globally optimal distribution.
Capacity comes from COUNT(DISTINCT channel) grouped by channels.laser,
excluding blocked channels and rows without laser metadata. With the bundled
CytoFLEX seed, usable capacities are B=2, R=3, V=4, Y=4: Violet has five stored
channels, but V4_V660 is blocked. Channel-to-laser assignments prefer database
metadata and fall back to B/R/V/Y/UV channel prefixes. Options with unknown
capacity remain eligible and receive no load penalty.
Each JSON candidate includes laser_distribution (used channel counts, for
example {"B": 2, "R": 2, "V": 2, "Y": 2}) and laser_capacity
({"B": 2, "R": 3, "V": 4, "Y": 4}). Text output shows
激光分布: B 2/2 · R 2/3 · V 2/4 · Y 2/4; unknown capacity is displayed as ?.
The MCP tools inherit these fields from the same core result.
Inventory-directory library inference recognizes filenames containing 小鼠,
人, or case-insensitive isotype. Unidentified filenames are skipped with a
warning. Use --csv Library=PATH for an explicit, freely named library.
CSV imports accept Excel-style UTF-8 BOM and CRLF files, silently discard fully
empty trailing rows, and ignore unnamed trailing columns. Rows are skipped with
a warning only when both target and fluorochrome are absent. A missing Target
falls back to Name; either target or fluorochrome may otherwise remain NULL.
Additional named columns such as Quantity are preserved in extra. Hidden
files and macOS ._* AppleDouble files are ignored. Re-imports use a NULL-safe
identity based on library, catalog number, clone, target, and fluorochrome, so
they update existing rows instead of accumulating duplicates.
Literal - placeholders in target, fluorochrome, and clone cells are imported
as NULL. Brightness seed names are normalized to spectrum canonical names at
seed time: exact spectrum names take precedence, followed by explicit aliases.
Unresolved brightness keys produce seed warnings. Full vendor spellings such as
PE/Cyanine7, PerCP/Cyanine5.5, and Brilliant Violet 421™ resolve through
the same aliases as their short forms. Known dyes without source data remain
unresolved rather than receiving guessed brightness values.
Run pa mcp --db /absolute/path/lab.db to start the optional stdio server using
the official Python SDK (mcp>=1.29.1,<2). Install the mcp extra and initialize
the selected database with pa init first. MCP does not initialize or modify
laboratory data. Each tool opens a short-lived read-only connection and closes it
after either success or failure. Stdout belongs exclusively to the MCP protocol;
diagnostics go to stderr. No LLM service or API key is needed to run the server.
The server exposes ten tools:
list_libraries,list_instruments,list_markerslist_fluorochromes,get_fluorochrome,list_channelssearch_antibodiesgenerate_panel,diagnose_paneldb_stats
All tools use core repository and computation functions. Instrument-dependent
tools accept instrument_id: one stored instrument is selected automatically;
multiple instruments require an explicit selection. Library names come from
list_libraries and must be selected explicitly for panel computation.
Search and marker discovery use limit (1–500, default 100) and offset
(nonnegative, default 0). Panel tools require 1–64 nonempty markers, trim their
names, and reject duplicates after normalization. Generation accepts
max_solutions from 1 to 100 (default 10) and has a 100,000-node search budget.
Budget exhaustion is a tool error, never evidence of infeasibility.
Tool failures set MCP isError; a completed search with no valid panel instead
returns a normal tool result with business status: "error", an empty candidate
list, and diagnosis. Channel-conflict-free candidates are deterministic
assignments, not an experimental optimization guarantee or an LLM evaluation.
See MCP setup and tool reference for installation, client configuration, response handling, and a complete discovery → generation → diagnosis workflow.
The editable authority remains config/. Files under
panelagent/data/seed/ are package snapshots and must be recopied or regenerated
after edits to config/spectral_data.json,
config/fluorochrome_brightness.json, or config/channel_mapping.json.
Underscore-prefixed metadata keys are omitted from spectra.json.
The 57 CytoFLEX dye mappings collapse to 14 unique physical channel rows under
the required (instrument_id, channel) primary key. AmCyan, V450, V500,
and Fixable Viability Stain 780 intentionally resolve without spectra.