BY Knowledge Graph
Persistent structured memory that turns isolated campaigns into a learning system. Each campaign writes outcomes, top designs, and failure modes into a JSON-backed graph so the next campaign benefits from prior art without re-running compute.
The store is intentionally minimal: append-only JSON files, no server process, no embeddings — keyword overlap is enough for the scale we operate at (hundreds to low thousands of campaigns). Six MCP tools wrap the storage layer so every agent in the BY suite reads and writes through the same contract.
When to Use This Skill
Use this skill when:
- ✅ Starting a new campaign — query prior campaigns and scaffold rankings before committing compute
- ✅ Finishing a campaign — record outcomes, top designs, and any failure patterns
- ✅ Diagnosing low pass rates — search for matching failure patterns from prior campaigns
- ✅ Pre-flight parameter selection — call
knowledge_get_recommendationsto seed defaults - ✅ Periodic maintenance — run
knowledge_consolidateafter every 20-30 campaigns - ✅ Cross-target analysis — compare hit rates of a scaffold across target classes
Don't use this skill for:
- ❌ Storing raw design files — those live in the campaign directory (FASTA, PDB, CIF)
- ❌ Storing every design from a campaign — record the top 10-20 only (selectivity matters)
- ❌ Replacing the research dossier —
by-researchwritesresearch/research.md; this skill stores the summary - ❌ Per-job telemetry or compute logs — use
by-campaign-managercheckpoints instead - ❌ Free-form notes that have no entity to attach to — write them to
.claude/memory/directly
The graph is a long-lived asset. Be selective on writes; aggressive on queries.
Quick Start
# 1. Query prior art at campaign start
result = mcp__by_knowledge__knowledge_query_similar(
target_description="TNF-alpha cytokine homotrimer autoimmune",
modality="VHH",
top_k=5,
)
# 2. Record outcomes at campaign end (top 10-20 designs, not all of them)
mcp__by_knowledge__knowledge_store_campaign(
target="TNF-alpha",
modality="VHH",
parameters={"scaffold": "caplacizumab", "seeds": 4, "temperature": 0.7},
outcomes={
"hit_rate": 0.23,
"best_ipsae": 0.78,
"best_iptm": 0.85,
"screening_pass_rate": 0.18,
},
notes="Iter-2 hotspot refinement converged on Y56/R113",
designs=[
{"design_id": "tnf_001", "scaffold": "caplacizumab", "ipsae": 0.78, "iptm": 0.85, "status": "PASS"},
# ... up to ~20 top designs
],
)
Expected runtime: <100 ms per call (no network, no DB engine). Storage is local
JSON; first call seeds ~/.by/knowledge/ if missing.
Installation
| Software | Version | License | Commercial Use | Installation |
|---|---|---|---|---|
| Python | >= 3.11 | PSF | ✅ Permitted | Pre-installed in BY env |
mcp SDK |
>= 1.0.0 | MIT | ✅ Permitted | pip install mcp |
jsonschema (optional, for migration) |
>= 4.0 | MIT | ✅ Permitted | pip install jsonschema |
No database, no server process, no compute. The MCP server starts cold in under one second. License Compliance: All packages permit commercial use.
Storage directory resolution (priority order):
KNOWLEDGE_DIRenvironment variable (explicit override)$BY_PROJECT_ROOT/.by/knowledge/(project-local)~/.by/knowledge/(home directory fallback)
Inputs
Required for knowledge_store_campaign:
target(str): Target name. Use lowercase-hyphenated form for stability (e.g."tnf-alpha","pd-l1").modality(str): One of"antibody","nanobody","VHH","scFv","de_novo","binder".parameters(dict): Scaffold, seeds, temperature, MSA mode, etc.outcomes(dict): Must includehit_rate,best_ipsae,best_iptm,screening_pass_ratewhere available.
Optional:
notes(str): Free-text, indexed for keyword search.designs(list[dict]): Top 10-20 designs withdesign_id,scaffold,ipsae,iptm,status(PASS/FAIL).
Required for knowledge_store_failure:
campaign_id(str): Stable identifier (use the campaign directory name).description(str): What broke.root_cause(str): Underlying cause from post-mortem.target(str): Target the campaign was for.
See references/graph-schema.md for the full JSON Schema of every entity and relationship type.
Outputs
All write tools return a JSON envelope:
{
"status": "stored",
"id": "campaign_a1b2c3d4e5f6",
"document": "Campaign targeting TNF-alpha using VHH modality. Scaffold: caplacizumab. Hit rate: 0.23. Best ipSAE: 0.78.",
"metadata": { /* echoed back for confirmation */ }
}
All read tools return result lists with similarity scores:
{
"results": [
{
"id": "campaign_a1b2c3d4e5f6",
"similarity": 0.83,
"document": "Campaign targeting TNF-alpha...",
"metadata": { "target": "...", "outcomes": {...}, "parameters": {...} }
}
],
"query": "TNF-alpha"
}
Persisted files in ~/.by/knowledge/ (or override path):
| File | Format | Contents |
|---|---|---|
campaigns.json |
JSON array | Campaign records with parameters, outcomes, notes, optional designs |
failures.json |
JSON array | Failure records with description, root_cause, target |
For NDJSON entity dumps (migration / external analytics), use the
migrate_knowledge.py script in scripts/ — it normalizes the on-disk arrays
into the schema in references/graph-schema.md.
Clarification Questions
⚠️ CRITICAL: ASK THIS FIRST — Always confirm there is a campaign or outcome to record before invoking write tools.
- Storage scope (ASK THIS FIRST): Do we have a completed campaign with screening outcomes ready to record? Or are we querying for a new campaign that hasn't started? Writing without outcomes pollutes the graph.
- Target normalization: Has the target been recorded before under a different name? (e.g.
TNF,TNF-alpha,TNF_alpha,tumor necrosis factor). Decide canonical form before writing. - Design selectivity: How many designs should be recorded? Default is top 10-20 by composite score. Recording all hundreds bloats the graph and slows query.
- Failure boundaries: Is this a recurring failure pattern worth recording, or a one-off bug? Only patterns that may repeat across campaigns belong in
failures.json. - Maintenance cadence: When was
knowledge_consolidatelast run? After 20-30 new campaigns, dedup + prune keeps query speed up. - Storage location: Is
KNOWLEDGE_DIRset, or are we using the default~/.by/knowledge/? Project-local stores (under$BY_PROJECT_ROOT/.by/knowledge/) keep teams from cross-contaminating data. - Migration intent: Are we upgrading schema version, migrating between machines, or just backing up? Different paths in
migrate_knowledge.py.
See references/query-patterns.md for query template selection guidance per question.
Standard Workflow
🚨 MANDATORY: USE THE MCP TOOLS — DO NOT WRITE DIRECTLY TO JSON FILES 🚨
Direct file writes bypass the atomic-rename safety, skip validation, and break concurrent-access guarantees.
At campaign start (read-only)
Query similar past campaigns:
mcp__by_knowledge__knowledge_query_similar( target_description="<target name + organism + class>", modality="<VHH|antibody|de_novo>", top_k=5, )✅ VERIFICATION: Result
resultsarray length matchestop_k(or fewer if graph is small).Get scaffold rankings for target class:
mcp__by_knowledge__knowledge_scaffold_rankings(target_class="cytokine")Pull all-in-one recommendations:
mcp__by_knowledge__knowledge_get_recommendations( target="TNF-alpha", modality="VHH", )Returns
similar_campaigns,recommended_scaffolds,warnings(from failures), andsuggested_parameters.
At campaign end (writes)
Record campaign outcomes:
mcp__by_knowledge__knowledge_store_campaign( target=..., modality=..., parameters={...}, outcomes={...}, designs=[...] )✅ VERIFICATION: Response
status == "stored"andidmatchescampaign_*pattern.Record any failure patterns:
mcp__by_knowledge__knowledge_store_failure( campaign_id=..., description=..., root_cause=..., target=..., )
Periodic maintenance
- Every 20-30 campaigns, consolidate:
Dedups (same target+modality+scaffold) and prunes (>90 days, <3 accesses).mcp__by_knowledge__knowledge_consolidate()
❌ DON'T:
- Write to
~/.by/knowledge/campaigns.jsonwithopen(..., "w")— bypasses atomic rename - Record every design from a campaign — graph bloat slows every subsequent query
- Use varying target names (
TNFvsTNF-alpha) — fragments the data - Skip the
designsarray thinking it's optional decoration — downstream agents key off it
When Scripts Fail
Script Failure Hierarchy:
- Fix and Retry (90%) —
pip install mcp jsonschema, ensureKNOWLEDGE_DIRexists and is writable, re-run. - Modify Script (5%) —
migrate_knowledge.pyis editable. Adjust validation rules for non-standard fields, then rerun. - Use as Reference (4%) — Read
knowledge_query_examples.py, adapt query template for an unusual filter. - Write from Scratch (1%) — Only if the entity model has fundamentally diverged; first update references/graph-schema.md.
Common failure: Permission denied on ~/.by/knowledge/ — set KNOWLEDGE_DIR
to a writable location, or chmod -R u+w ~/.by/knowledge/.
Common failure: JSON file corrupt mid-write — the server uses atomic rename
(write to .tmp, then rename), so corruption is rare. If you see a .tmp file
leftover, the previous write was interrupted; safe to delete after backing up.
Decision Points
When to record a design in the designs array:
- ✅ Top 10-20 by composite score
- ✅ Any design with notable liability (e.g. potential glycosylation site near hotspot)
- ✅ Any design used downstream (verification, lab submission)
- ❌ Mid-tier designs without distinguishing features
- ❌ FAIL designs unless they exemplify a failure pattern
When to record a failure:
- ✅ Pattern repeats across 2+ designs in the same campaign
- ✅ Recurring across campaigns (e.g. "VHH+caplacizumab on glycosylated epitopes consistently misfolds")
- ❌ One-off bug in the toolchain (file in by-debug instead)
- ❌ User error (e.g. wrong PDB chain) — not a learning opportunity for the graph
When to consolidate:
- ✅ After every 20-30 new campaigns
- ✅ Before exporting / migrating
- ❌ During active campaigns (consolidation is non-destructive but generates noise)
See references/query-patterns.md for the full decision tree per query type.
Common Issues
| Issue | Cause | Solution | Details |
|---|---|---|---|
Empty results from knowledge_query_similar |
Graph is empty or target_description has no shared keywords | Lower threshold or broaden description; run a few campaigns first | See query-patterns.md#empty-results |
| Same target appears multiple times | Inconsistent naming (TNF vs TNF-alpha) |
Normalize at write time; run knowledge_consolidate to dedup |
graph-schema.md#target |
Permission denied on storage |
~/.by/knowledge/ not writable |
chmod -R u+w ~/.by/knowledge/ or set KNOWLEDGE_DIR |
— |
| Scaffold rankings empty for target class | Substring match too narrow | Use broader target_class (e.g. cytokine not IL-23p19) |
query-patterns.md#scaffold-rankings |
knowledge_consolidate removed entries I wanted |
Pruned >90 days + <3 accesses |
Restore from backup; raise access_count by querying before next consolidate | graph-schema.md#access-count |
| Failures not surfacing in recommendations | Keyword overlap below 0.2 threshold | Re-record failure with target name + modality in description | graph-schema.md#failure |
.tmp files in storage directory |
Previous write interrupted | Safe to delete after confirming .json file is intact |
— |
KeyError: 'scaffold' in scaffold rankings |
Campaign stored without parameters.scaffold |
Always include scaffold in parameters dict |
graph-schema.md#campaign |
| Migration script rejects entries | Schema version mismatch | Run migrate_knowledge.py --upgrade to apply version transforms |
graph-schema.md#versioning |
| Slow queries (>1s) | Graph has thousands of entries | Run knowledge_consolidate, then re-query |
— |
| Two machines have divergent graphs | No sync layer | Export with migrate_knowledge.py --export, merge manually, re-import |
— |
| Designs missing from query response | Top-level field, not in metadata.designs | metadata.designs_count shows count; query record by id for full list |
graph-schema.md#design |
Best Practices
- 🚨 CRITICAL: Always normalize target names at write time. Use lowercase-hyphenated form (
tnf-alpha, notTNF-alphaorTNF alpha). - ✅ REQUIRED: Record the top 10-20 designs per campaign — never all of them. Graph bloat is the #1 cause of slow queries.
- ✅ REQUIRED: Include
composite_score,ipsae,iptm, andstatusin every design entry. Downstream agents key off these fields. - ✅ Query before designing —
knowledge_get_recommendationsis the cheapest way to avoid repeating a failed campaign. - ✅ Use
notesfield for non-structured context (e.g. "iter-2 hotspot refinement"). It's indexed for keyword search. - ✅ Record failures as patterns, not incidents. "VHH+glycosylated epitope misfolds" is useful; "ran out of disk on day 3" is not.
- ✅ Run
knowledge_consolidateafter every 20-30 campaigns to dedup and prune. - ✨ Optional: Set
KNOWLEDGE_DIRto a Dropbox/iCloud path to share the graph across machines. - ❌ DON'T: Write to
campaigns.jsonorfailures.jsondirectly. Bypasses atomic-rename safety. - ❌ DON'T: Re-query the graph mid-campaign on every screening result. Query at start, query at end, that's it.
Suggested Next Steps
After storing campaign outcomes, invoke these skills:
- by-campaign-manager — Update the campaign state file with knowledge entries written. Closes the campaign lifecycle.
- by-research — On the next new campaign,
by-researchwill consumeknowledge_query_similarresults as prior art context. - by-failure-diagnosis — If
failures.jsongrew during the campaign, run this skill to root-cause the patterns. - by-campaign-optimizer — Reads scaffold rankings to recommend the next campaign's parameter sweep.
Why this chain works: every BY skill reads from and writes to the same JSON store. Each campaign therefore improves the recommendations for the next, with zero manual curation step.
Related Skills
Upstream (run before this skill):
by-screening— Produces the screening results that get summarized into outcomes.by-campaign-manager— Provides the campaign_id and parameters dict.
Downstream (run after this skill):
by-research— Consumes prior campaigns as context for new target research.by-failure-diagnosis— Consumes failure entries for root-cause clustering.by-campaign-optimizer— Consumes scaffold rankings for active learning.
Alternative / complementary:
by-display— Formats query results for human review.
References
Detailed documentation (in references/):
- references/graph-schema.md — Full JSON Schema for every entity and relationship type, with field-level documentation and a property index.
- references/query-patterns.md — Common query templates, expected output shapes, and performance notes.
Scripts (in scripts/):
- scripts/knowledge_query_examples.py — 6-8 runnable query examples that exercise the patterns documented in
query-patterns.md. - scripts/migrate_knowledge.py — Migration utility for NDJSON entity dumps; validates against schema and handles schema-version upgrades.
MCP server source: templates/.claude/mcp_servers/knowledge/server.py —
canonical implementation of the six MCP tools.
License: BY (Blatant-Why) project — commercial use permitted under project terms.