Skip to content

Commit 727b849

Browse files
authored
Merge pull request #67 from vectorlessflow/dev
Dev
2 parents 111a7e4 + 4a81365 commit 727b849

36 files changed

Lines changed: 2001 additions & 50 deletions

docs/docs/architecture.mdx

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
sidebar_position: 3
3+
---
4+
5+
# Architecture
6+
7+
Vectorless transforms documents into hierarchical semantic trees and uses LLM-powered reasoning to navigate them. This page describes the end-to-end pipeline.
8+
9+
## High-Level Flow
10+
11+
```text
12+
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
13+
│ Document │────▶│ Index │────▶│ Storage │
14+
│ (PDF/MD) │ │ Pipeline │ │ (Disk) │
15+
└──────────────┘ └──────────────┘ └──────┬───────┘
16+
17+
┌──────────────┐ ┌──────▼───────┐
18+
│ Result │◀────│ Retrieval │
19+
│ (Answer) │ │ Pipeline │
20+
└──────────────┘ └──────────────┘
21+
```
22+
23+
## Index Pipeline
24+
25+
The indexing pipeline processes documents through ordered stages:
26+
27+
| Stage | Priority | Description |
28+
|-------|----------|-------------|
29+
| **Parse** | 10 | Parse document into raw nodes (Markdown headings, PDF pages) |
30+
| **Build** | 20 | Construct arena-based tree with thinning and content merge |
31+
| **Validate** | 22 | Tree integrity checks |
32+
| **Split** | 25 | Split oversized leaf nodes (>4000 tokens) |
33+
| **Enhance** | 30 | Generate LLM summaries (Full, Selective, or Lazy strategy) |
34+
| **Enrich** | 40 | Calculate metadata, page ranges, resolve cross-references |
35+
| **Reasoning Index** | 45 | Build keyword-to-node mappings, synonym expansion, summary shortcuts |
36+
| **Optimize** | 60 | Final tree optimization |
37+
38+
Each stage is independently configurable. The pipeline supports incremental re-indexing via content fingerprinting.
39+
40+
## Tree Structure
41+
42+
Each node in the tree contains:
43+
44+
```text
45+
TreeNode
46+
├── title — Section heading
47+
├── content — Raw text (leaf nodes)
48+
├── summary — LLM-generated summary
49+
├── structure — Hierarchical index (e.g., "1.2.3")
50+
├── depth — Tree depth (root = 0)
51+
├── references[] — Resolved cross-references ("see Section 2.1" → NodeId)
52+
├── token_count — Estimated token count
53+
└── page_range — Start/end page (PDF)
54+
```
55+
56+
## Retrieval Pipeline
57+
58+
The retrieval pipeline consists of four phases:
59+
60+
1. **Analyze** — Detect query complexity, extract keywords, decompose complex queries
61+
2. **Plan** — Select retrieval strategy and search algorithm
62+
3. **Search** — Execute tree traversal with Pilot guidance
63+
4. **Evaluate** — Score, deduplicate, and aggregate results
64+
65+
### Pilot
66+
67+
The Pilot is the core intelligence component. It provides LLM-guided navigation at key decision points:
68+
69+
- **Fork points** — When multiple children exist, Pilot evaluates which path to follow
70+
- **Backtracking** — When a path yields insufficient results, Pilot suggests alternatives
71+
- **Binary pruning** — Quick relevance filter for nodes with many children
72+
73+
### Search Algorithms
74+
75+
| Algorithm | Description | Use Case |
76+
|-----------|-------------|----------|
77+
| **Beam Search** | Explores multiple paths with backtracking | General purpose (recommended) |
78+
| **MCTS** | Monte Carlo Tree Search with UCT selection | Complex multi-hop queries |
79+
| **Pure Pilot** | Greedy single-path, Pilot at every level | High-accuracy, higher token cost |
80+
| **ToC Navigator** | Table-of-contents based location | Broad queries ("what is this about?") |
81+
82+
## Cross-Document Graph
83+
84+
When multiple documents are indexed, Vectorless automatically builds a relationship graph based on shared keywords and Jaccard similarity. This graph enables cross-document retrieval with score boosting.
85+
86+
## Zero Infrastructure
87+
88+
The entire system requires only an LLM API key. No vector database, no embedding models, no additional infrastructure. Trees and metadata are persisted to the local filesystem in the workspace directory.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
---
2+
sidebar_position: 3
3+
---
4+
5+
# Batch Indexing
6+
7+
Index multiple documents efficiently with progress tracking and error handling.
8+
9+
## Python
10+
11+
```python
12+
import asyncio
13+
from vectorless import Engine, IndexContext, IndexOptions
14+
15+
async def main():
16+
engine = Engine(
17+
workspace="./workspace",
18+
api_key="sk-...",
19+
model="gpt-4o",
20+
)
21+
22+
# Index a directory of documents
23+
result = await engine.index(
24+
IndexContext.from_dir("./documents/")
25+
)
26+
27+
print(f"Indexed {len(result.items)} documents")
28+
print(f"Failures: {len(result.failed)}")
29+
30+
for item in result.items:
31+
print(f"{item.name} ({item.format}) → {item.doc_id}")
32+
if item.metrics:
33+
m = item.metrics
34+
print(f" Nodes: {m.nodes_processed}, "
35+
f"Summaries: {m.summaries_generated}, "
36+
f"Time: {m.total_time_ms}ms")
37+
38+
for fail in result.failed:
39+
print(f"{fail.source}: {fail.error}")
40+
41+
# List all indexed documents
42+
docs = await engine.list()
43+
print(f"\nTotal indexed: {len(docs)} documents")
44+
45+
asyncio.run(main())
46+
```
47+
48+
## Rust
49+
50+
```rust
51+
use vectorless::client::{Engine, EngineBuilder, IndexContext};
52+
53+
#[tokio::main]
54+
async fn main() -> vectorless::Result<()> {
55+
let engine = EngineBuilder::new()
56+
.with_workspace("./workspace")
57+
.with_key("sk-...")
58+
.with_model("gpt-4o")
59+
.build()
60+
.await?;
61+
62+
// Index a directory
63+
let result = engine.index(IndexContext::from_dir("./documents/")).await?;
64+
65+
println!("Indexed {} documents", result.items.len());
66+
println!("Failures: {}", result.failed.len());
67+
68+
for item in &result.items {
69+
println!(" ✓ {} ({:?}) → {}", item.name, item.format, item.doc_id);
70+
}
71+
72+
// List all documents
73+
let docs = engine.list().await?;
74+
println!("Total indexed: {} documents", docs.len());
75+
76+
Ok(())
77+
}
78+
```
79+
80+
## Error Handling
81+
82+
Each item in the result is either successful or failed. Failures don't prevent other documents from being indexed:
83+
84+
```python
85+
result = await engine.index(IndexContext.from_paths(mixed_paths))
86+
87+
# Successful items
88+
for item in result.items:
89+
process(item)
90+
91+
# Failed items — handle gracefully
92+
for fail in result.failed:
93+
print(f"Failed: {fail.source}{fail.error}")
94+
```
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
sidebar_position: 2
3+
---
4+
5+
# Multi-Document Retrieval
6+
7+
Query across multiple indexed documents using the cross-document strategy with graph-based score boosting.
8+
9+
## Python
10+
11+
```python
12+
import asyncio
13+
from vectorless import (
14+
Engine, IndexContext, QueryContext,
15+
IndexOptions, StrategyPreference
16+
)
17+
18+
async def main():
19+
engine = Engine(
20+
workspace="./workspace",
21+
api_key="sk-...",
22+
model="gpt-4o",
23+
)
24+
25+
# Index multiple documents
26+
docs = ["./report-q1.pdf", "./report-q2.pdf", "./report-q3.pdf"]
27+
doc_ids = []
28+
29+
for path in docs:
30+
result = await engine.index(IndexContext.from_path(path))
31+
doc_ids.append(result.doc_id)
32+
print(f"Indexed: {path}{result.doc_id}")
33+
34+
# Check the cross-document graph
35+
graph = await engine.get_graph()
36+
if graph:
37+
print(f"\nGraph: {graph.node_count()} docs, {graph.edge_count()} edges")
38+
for doc_id in doc_ids:
39+
neighbors = graph.get_neighbors(doc_id)
40+
for edge in neighbors:
41+
print(f" {doc_id[:8]}... → {edge.target_doc_id[:8]}... ({edge.weight:.2f})")
42+
43+
# Query across all documents
44+
result = await engine.query(
45+
QueryContext("Compare quarterly revenue trends")
46+
.with_doc_ids(doc_ids)
47+
.with_strategy(StrategyPreference.CROSS_DOCUMENT)
48+
)
49+
50+
for item in result.items:
51+
print(f"\n[{item.doc_id[:8]}...] Score: {item.score:.2f}")
52+
print(item.content[:300])
53+
54+
# Or query entire workspace
55+
result = await engine.query(
56+
QueryContext("What documents discuss risk factors?")
57+
.with_workspace()
58+
)
59+
60+
print(f"\nFound in {len(result.items)} document(s)")
61+
62+
# Cleanup
63+
for doc_id in doc_ids:
64+
await engine.remove(doc_id)
65+
66+
asyncio.run(main())
67+
```
68+
69+
## Key Concepts
70+
71+
### Document Graph
72+
73+
After indexing, documents are connected in a graph based on shared keywords. The graph enables:
74+
75+
- **Score boosting** — High-confidence results in one document boost neighbor documents
76+
- **Relationship discovery** — Automatically find related documents
77+
- **Cross-referencing** — Results from connected documents are surfaced together
78+
79+
### Merge Strategies
80+
81+
The cross-document strategy supports multiple merge modes:
82+
83+
| Strategy | Description |
84+
|----------|-------------|
85+
| **TopK** | Return top-K results across all documents |
86+
| **BestPerDocument** | Best result from each document |
87+
| **WeightedByRelevance** | Weight by each document's best score |
88+
| **GraphBoosted** | Use graph connections to boost scores |

docs/docs/examples/quick-query.mdx

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
sidebar_position: 1
3+
---
4+
5+
# Quick Query Example
6+
7+
This example demonstrates the basic index-and-query workflow with both Python and Rust.
8+
9+
## Python
10+
11+
```python
12+
import asyncio
13+
from vectorless import Engine, IndexContext, QueryContext, StrategyPreference
14+
15+
async def main():
16+
# 1. Create engine
17+
engine = Engine(
18+
workspace="./data",
19+
api_key="sk-...",
20+
model="gpt-4o",
21+
)
22+
23+
# 2. Index a document
24+
result = await engine.index(IndexContext.from_path("./report.pdf"))
25+
doc_id = result.doc_id
26+
print(f"Indexed document: {doc_id}")
27+
28+
# 3. Simple keyword query
29+
answer = await engine.query(
30+
QueryContext("revenue")
31+
.with_doc_id(doc_id)
32+
.with_strategy(StrategyPreference.KEYWORD)
33+
)
34+
print(f"Keyword result: {answer.single().content[:200]}")
35+
36+
# 4. Complex reasoning query
37+
answer = await engine.query(
38+
QueryContext("What are the main factors affecting performance?")
39+
.with_doc_id(doc_id)
40+
.with_strategy(StrategyPreference.HYBRID)
41+
)
42+
print(f"Score: {answer.single().score:.2f}")
43+
print(f"Hybrid result: {answer.single().content[:200]}")
44+
45+
# 5. Cleanup
46+
await engine.remove(doc_id)
47+
48+
asyncio.run(main())
49+
```
50+
51+
## Rust
52+
53+
```rust
54+
use vectorless::client::{Engine, EngineBuilder, IndexContext, QueryContext};
55+
use vectorless::StrategyPreference;
56+
57+
#[tokio::main]
58+
async fn main() -> vectorless::Result<()> {
59+
// 1. Create engine
60+
let engine = EngineBuilder::new()
61+
.with_workspace("./data")
62+
.with_key("sk-...")
63+
.with_model("gpt-4o")
64+
.build()
65+
.await?;
66+
67+
// 2. Index a document
68+
let result = engine.index(IndexContext::from_path("./report.pdf")).await?;
69+
let doc_id = result.doc_id().unwrap().to_string();
70+
println!("Indexed document: {}", doc_id);
71+
72+
// 3. Query with hybrid strategy
73+
let answer = engine.query(
74+
QueryContext::new("What are the main factors affecting performance?")
75+
.with_doc_id(&doc_id)
76+
).await?;
77+
78+
if let Some(item) = answer.single() {
79+
println!("Score: {:.2}", item.score);
80+
println!("{}", item.content);
81+
}
82+
83+
// 4. Cleanup
84+
engine.remove(&doc_id).await?;
85+
86+
Ok(())
87+
}
88+
```

0 commit comments

Comments
 (0)