|
1 | 1 | # CLAUDE.md |
2 | 2 |
|
3 | | -Vectorless is a Document Understanding Engine for AI written in Rust. |
| 3 | +Vectorless is a Document Understanding Engine for AI. See `README.md` for the project's public positioning. |
4 | 4 |
|
5 | | -## Principles |
| 5 | +## Positioning: a standard + a reference implementation |
6 | 6 |
|
7 | | -- **Reason, don't vector.** Retrieval is a reasoning act, not a similarity computation. |
8 | | -- **Model fails, we fail.** No heuristic fallbacks, no silent degradation. |
9 | | -- **No thought, no answer.** Only reasoned output counts as an answer. |
| 7 | +Vectorless is split into two layers that are **independently consumable**: |
| 8 | + |
| 9 | +1. **The Rust side defines two standards.** |
| 10 | + - **The IR** (`vectorless-document::Document`) — a single, versioned, serializable artifact that fully represents an understood document. Produced by the compile pipeline; can be persisted, transmitted, and re-loaded. |
| 11 | + - **The navigation primitives** (`vectorless-primitives::DocumentNavigator`) — a fixed set of shell-style operations (`ls`, `cd`, `cat`, `grep`, `find`, `head`, `wc`, `pwd`, `back`, plus inspection and reasoning-index queries) that any agent uses to read an IR. Exposed to Python via PyO3. |
| 12 | + |
| 13 | + These two together are **the standard**. Anything that produces a valid `Document` and consumes it via `DocumentNavigator` is a conforming participant. |
| 14 | + |
| 15 | +2. **The Python side is one reference agent implementation.** `vectorless/ask/` is a multi-agent system (Orchestrator + Workers + reasoning + verify) that ships in the box. It is the default `engine.ask()` — but it is not load-bearing for the standard. Developers can: |
| 16 | + - Replace `ask/` entirely with their own Python agent built on the same `Document` / `DocumentNavigator` primitives. |
| 17 | + - Skip Python and consume the IR + primitives directly from Rust. |
| 18 | + - Produce IRs from a non-Rust source (the IR is serializable JSON) and feed them into any compatible navigator/agent. |
| 19 | + |
| 20 | +The reason this matters: the value of Vectorless is the **IR + primitives contract**, not the bundled reasoning loop. The reference implementation is a courtesy, not a lock-in. |
10 | 21 |
|
11 | 22 | ## Project Structure |
12 | 23 |
|
13 | | -Cargo workspace with 17 fine-grained Rust crates + pure Python SDK: |
| 24 | +``` |
| 25 | +crates/ Rust — the standard |
| 26 | +├── vectorless-error/ Result / Error |
| 27 | +├── vectorless-document/ ★ IR types: Document, DocumentTree, NavigationIndex, ReasoningIndex, |
| 28 | +│ QueryRoutingTable, ChainIndex, ContentOverlapMap, EvidenceScoreMap |
| 29 | +├── vectorless-primitives/ ★ DocumentNavigator (the navigation API) |
| 30 | +├── vectorless-config/ Configuration hub |
| 31 | +├── vectorless-utils/ Fingerprinting, token counting, keyword extraction |
| 32 | +├── vectorless-graph/ Cross-document relationship graph |
| 33 | +├── vectorless-events/ Progress event bus |
| 34 | +├── vectorless-metrics/ Metrics collection |
| 35 | +├── vectorless-llm/ LLM client (pool, throttle, retry) |
| 36 | +├── vectorless-storage/ Workspace persistence (file backend, LRU cache) |
| 37 | +├── vectorless-compiler/ Compile pipeline (frontend → transform → analysis → backend) |
| 38 | +├── vectorless-engine/ Facade: Engine, EngineBuilder, compile() / forget() / load_document() |
| 39 | +└── vectorless-py/ PyO3 bindings → produces the `_vectorless` native module |
| 40 | +
|
| 41 | +vectorless/ Python — the reference multi-agent implementation |
| 42 | +├── ask/ Orchestrator + Worker + reasoning + verify (the default agent) |
| 43 | +├── rerank/ Dedup + quality scoring + synthesis |
| 44 | +├── _internal/ Wrappers around the PyO3 native module |
| 45 | +├── engine.py User-facing Engine (delegates compile to Rust, ask to ask/) |
| 46 | +└── cli/ CLI entrypoint |
| 47 | +
|
| 48 | +examples/ Python examples |
| 49 | +docs/ Docusaurus documentation site |
| 50 | +``` |
| 51 | + |
| 52 | +The two crates marked ★ are the **standard surface**. Everything else in `crates/` is implementation detail that produces and serves that standard. |
| 53 | + |
| 54 | +### Dependency Layers (Rust) |
14 | 55 |
|
15 | 56 | ``` |
16 | | -crates/ |
17 | | -├── vectorless-error/ # Error types (Result, Error enum) |
18 | | -├── vectorless-document/ # Document types (Document, Tree, NavigationIndex, ReasoningIndex) |
19 | | -├── vectorless-config/ # Configuration hub (aggregates all config types) |
20 | | -├── vectorless-utils/ # Utilities (fingerprinting, token counting, validation) |
21 | | -├── vectorless-scoring/ # Scoring (BM25, keyword extraction) |
22 | | -├── vectorless-graph/ # Cross-document relationship graph |
23 | | -├── vectorless-events/ # Event system for progress monitoring |
24 | | -├── vectorless-metrics/ # Metrics collection and reporting |
25 | | -├── vectorless-llm/ # LLM client (pool, memo/cache, throttle, fallback) |
26 | | -├── vectorless-storage/ # Persistence (Workspace, LRU cache, file/memory backends) |
27 | | -├── vectorless-query/ # Query understanding (intent classification, rewrite) |
28 | | -├── vectorless-compiler/ # Compile pipeline (15-pass, checkpointing, incremental update) |
29 | | -├── vectorless-agent/ # Retrieval execution (Worker navigation + Orchestrator fusion) |
30 | | -├── vectorless-retrieval/ # Retrieval dispatch layer (dispatcher, cache, streaming) |
31 | | -├── vectorless-rerank/ # Result reranking (dedup, BM25 scoring, fusion) |
32 | | -├── vectorless-engine/ # Facade (Engine, EngineBuilder) — re-exports public API |
33 | | -└── vectorless-py/ # PyO3 bindings (compiled into Python native module) |
| 57 | +Layer 0: error · document (no workspace deps) |
| 58 | +Layer 1: utils · graph · events (depends on Layer 0) |
| 59 | +Layer 2: config · metrics (depends on Layer 0–1) |
| 60 | +Layer 3: llm · storage · primitives (depends on Layer 0–2) |
| 61 | +Layer 4: compiler (depends on Layer 0–3) |
| 62 | +Layer 5: engine (depends on Layer 0–4) |
| 63 | +Layer 6: vectorless-py (PyO3 bindings) (engine + document + primitives) |
34 | 64 | ``` |
35 | 65 |
|
36 | | -- `vectorless/` - Pure Python SDK (high-level wrappers, CLI, config loading, integrations) |
37 | | -- `examples/` - Python examples (primary, for Python ecosystem) |
38 | | -- `docs/` - Docusaurus documentation site |
| 66 | +`vectorless-document` and `vectorless-primitives` have no dependency on `compiler`, `llm`, `storage`, or `engine` — by design, so the standard can be consumed without dragging in the pipeline. |
39 | 67 |
|
40 | | -### Dependency Layers |
| 68 | +### Compile Pipeline (the IR producer) |
| 69 | + |
| 70 | +The compiler runs documents through four stage groups (`crates/vectorless-compiler/src/passes/`): |
41 | 71 |
|
42 | 72 | ``` |
43 | | -Layer 0: error · document · utils · scoring (no workspace deps) |
44 | | -Layer 1: graph · events · config · metrics (depends on Layer 0) |
45 | | -Layer 2: llm · storage (depends on Layer 0–1) |
46 | | -Layer 3: query (depends on Layer 0–2) |
47 | | -Layer 4: compiler · agent (depends on Layer 0–3) |
48 | | -Layer 5: retrieval · rerank (depends on Layer 0–4) |
49 | | -Layer 6: engine (facade) · vectorless-py (bindings) (depends on all) |
| 73 | +frontend (parse, build) raw bytes → DocumentTree |
| 74 | +transform (split, enrich) chunking + section enrichment |
| 75 | +analysis (validate, enhance) structure checks + augmentation |
| 76 | +backend (route, concept, navigation, |
| 77 | + chain, overlap, reasoning, |
| 78 | + score, optimize, verify) retrieval-acceleration artifacts |
50 | 79 | ``` |
51 | 80 |
|
| 81 | +Every backend pass attaches an optional acceleration field to the `Document`. The IR is valid without any of them (an agent can navigate using only `tree` + `nav_index`); the acceleration fields exist so well-equipped agents can short-circuit reasoning. |
| 82 | + |
52 | 83 | ### Compilation Isolation |
53 | 84 |
|
54 | | -改一个模块只重编译该 crate + 上游 facade: |
55 | | -- 改 `agent` → agent, retrieval, rerank, engine, py 重编译;index/llm/storage 不动 |
56 | | -- 改 `llm` → llm 及其上层重编译;index/agent/stage 不重编译 |
57 | | -- 改 `document` → 全部重编译(核心类型,预期行为) |
| 85 | +Changing one module only recompiles that crate + upstream facades: |
58 | 86 |
|
59 | | -### Retrieval Call Flow |
| 87 | +- Change `llm` → recompiles llm, compiler, engine, py; storage/graph untouched |
| 88 | +- Change `compiler` → recompiles compiler, engine, py; llm/storage untouched |
| 89 | +- Change `document` or `primitives` → recompiles everything (standard surface change, expected) |
| 90 | +- Change Python `ask/` / `rerank/` → no Rust recompile |
60 | 91 |
|
| 92 | +### How third parties consume the standard |
| 93 | + |
| 94 | +Anyone building a custom agent works against two types and two crates only: |
| 95 | + |
| 96 | +```rust |
| 97 | +use vectorless_document::Document; // The IR |
| 98 | +use vectorless_primitives::DocumentNavigator; // The primitive API |
| 99 | + |
| 100 | +let doc: Document = load_ir_from_disk()?; // produced by our compiler, or yours |
| 101 | +let mut nav = DocumentNavigator::new(doc); |
| 102 | +let children = nav.ls().await; |
| 103 | +nav.cd("n3").await?; |
| 104 | +let body = nav.cat(None).await?; |
61 | 105 | ``` |
62 | | -Engine.ask() |
63 | | - → retrieval/dispatcher |
64 | | - → query/understand() → QueryPlan (LLM intent + concepts + strategy) |
65 | | - → Orchestrator (always, single or multi-doc) |
66 | | - → analyze(QueryPlan) → dispatch plan |
67 | | - → supervisor loop: |
68 | | - dispatch Workers → evaluate() → |
69 | | - if insufficient → replan() → loop |
70 | | - → rerank/ (dedup → BM25 score → synthesis/fusion) |
71 | | -``` |
| 106 | + |
| 107 | +The Python reference agent (`vectorless/ask/`) is just one consumer of this same surface. |
72 | 108 |
|
73 | 109 | ## Build Commands |
74 | 110 |
|
75 | 111 | ```bash |
76 | | -# Build (workspace) |
| 112 | +# Rust workspace |
77 | 113 | cargo build # Build all crates |
78 | | -cargo test # Run tests (488 tests across all crates) |
| 114 | +cargo test # Run workspace tests |
79 | 115 | cargo clippy # Lint |
80 | | -cargo fmt # Format code |
| 116 | +cargo fmt # Format |
81 | 117 |
|
82 | | -# Build specific crate (fast — only that crate + dependents) |
83 | | -cargo build -p vectorless-agent |
| 118 | +# Build a single crate (fast — only that crate + dependents) |
| 119 | +cargo build -p vectorless-compiler |
84 | 120 |
|
85 | | -# Python SDK |
86 | | -pip install -e . # Install in editable mode (from project root, uses maturin) |
| 121 | +# Python SDK (uses maturin under the hood) |
| 122 | +pip install -e . # Editable install from project root |
87 | 123 |
|
88 | 124 | # Docs site |
89 | 125 | cd docs |
90 | | -pnpm install # Install dependencies |
91 | | -pnpm build # Build static site |
| 126 | +pnpm install |
| 127 | +pnpm build |
92 | 128 | ``` |
93 | 129 |
|
94 | 130 | ## Code Conventions |
95 | 131 |
|
96 | | -- Follow Rust standard naming (snake_case for functions/variables, PascalCase for types) |
97 | | -- Use `thiserror` for error handling |
98 | | -- Use `tracing` for logging |
99 | | -- Public APIs require documentation comments |
| 132 | +- Rust: snake_case for functions/variables, PascalCase for types; `thiserror` for errors; `tracing` for logging; doc comments on public APIs. |
| 133 | +- Python: type-annotated; `pydantic` for models; `litellm` + `instructor` for LLM calls. |
| 134 | +- Commit messages: `type(scope): description` (Conventional Commits). |
100 | 135 |
|
101 | 136 | --- |
102 | 137 |
|
@@ -170,9 +205,10 @@ When uncertain whether an operation is safe, **default to asking user confirmati |
170 | 205 |
|
171 | 206 | ## Common Development Workflow |
172 | 207 |
|
173 | | -1. **Adding features**: Implement in the appropriate `crates/vectorless-*/` crate, add tests |
174 | | -2. **Fixing bugs**: Add failing test case first, fix and ensure tests pass |
175 | | -3. **Adding crates**: New modules get their own crate under `crates/`, add to workspace Cargo.toml |
176 | | -4. **Python bindings**: Update `crates/vectorless-py/src/lib.rs` (PyO3) when Rust APIs change |
177 | | -5. **Python SDK**: Update `vectorless/` when API surface changes |
178 | | -6. **Committing code**: Use semantic commit messages, format: `type(scope): description` |
| 208 | +1. **Touching the standard (`document` or `primitives`):** this is a public-contract change. Bump `CURRENT_SCHEMA_VERSION` if the IR's serialized shape changes; document the new field; add tests. |
| 209 | +2. **Adding a compile-pipeline pass:** implement under `crates/vectorless-compiler/src/passes/` (frontend/transform/analysis/backend), wire into the pipeline, attach output to `Document` as an `Option<...>` so old IRs still load. |
| 210 | +3. **Adding/modifying reasoning behavior:** implement under `vectorless/ask/` (Python). This is reference-implementation work, not standard work — it should not require Rust changes. |
| 211 | +4. **Fixing bugs:** write a failing test first, then fix. |
| 212 | +5. **Adding crates:** new modules get their own crate under `crates/`, registered in workspace `Cargo.toml`. |
| 213 | +6. **PyO3 bindings:** update `crates/vectorless-py/src/lib.rs` when Rust types cross the FFI boundary; corresponding Python wrappers in `vectorless/_internal/`. |
| 214 | +7. **Committing:** Conventional Commits — `feat(compiler): ...`, `fix(ask): ...`, `refactor(primitives): ...`. |
0 commit comments