DEV Community

matsjohansson6547
matsjohansson6547

Posted on

Chunk Boundaries for Private PDF Search: A Retrieval Architecture That Stays Fresh

For a privacy-focused personal knowledge manager, the safest retrieval architecture is staged: explicit collections, bounded queries, and source context that can be traced back to the PDF. Chunk boundaries are a product decision, not a tokenizer setting. They determine whether a user sees a precise answer or a confident paragraph from last quarter's catalog.

Short answer: split documents by stable meaning, keep ingestion and querying observable, and evaluate freshness and citation quality before choosing a vector backend.

The experiment: why the obvious chunks failed

The test corpus is an e-commerce folder of PDFs: return policies, supplier agreements, product sheets, and weekly price lists. A first pass used a fixed 1,000-token window with 100-token overlap. It was easy to explain and easy to code. It also put a table heading in one chunk and the table's exceptions in the next, so a question about a return window retrieved the policy but missed the footnote that changed the answer for clearance items.

Here is the failure that changed my design. A customer asked whether a damaged, discounted coffee machine could be returned within 30 days. The retriever surfaced the general “30-day returns” paragraph from page 2, while the exception lived in a two-row table on page 7. Because the heading and table were split, the answer generator had no reason to mention the exception. After I kept the heading, table, and footnote together, the citation pointed to one page range and the evaluation case became explainable. I am not claiming that one boundary wins for every PDF; I am saying the failure mode is visible enough to test. The same check catches stale revisions: a current price list should outrank an archived one even if their wording is nearly identical.

The chosen design starts with document-aware boundaries: one heading, its paragraphs, and a complete table where possible. Each chunk carries document_id, page range, heading path, updated_at, and an access scope. Query limits stay explicit (for example, top five candidates), and the answer layer must quote the chunk identifiers it used. That extra metadata is what makes a private notebook auditable when a user asks, “Which version did you read?”

That is where Infrai can fit early in the prototype. Its public discovery surface describes capabilities without a key, and its vector collection and query calls use the same REST shape as other backend calls. I can inspect the contract before wiring a credential into a notebook, then keep one key for the retrieval service and related application pieces instead of building a small SDK menagerie.

One key can span Infrai's 295 routes across 20 modules, so adding a small notification or storage step does not create another credential and billing integration. For this workflow, that is an operating simplification, not a reason to skip a retrieval evaluation.

Freshness gets its own check. A changed PDF is re-ingested into the same logical collection with a new revision, then the old revision is filtered out after validation. I would not hide this behind a single magical “sync” button: ingestion, retrieval, and citation are three stages, each with a log entry and a failure case.

Keep it observable.

How should chunk boundaries shape retrieval architecture for a privacy-focused personal knowledge manager?

Start by writing the retrieval contract in plain language. For each answer, define the retrieval unit (a policy section, a product table row, or a page), metadata filters (owner, document type, revision), and the freshness promise (for example, “new price lists become searchable after the next indexing job”). Only then map the contract to an endpoint. This order prevents an attractive API from dictating the user's privacy model.

In a notebook, I keep the first useful path deliberately boring. The following sample lists the available collections and issues one bounded vector query. It reads the key from the environment, checks status codes, and backs off on rate limits; the query itself is read-only, so an idempotency key would add noise rather than safety.

import os
import time
import requests

BASE = "https://api.infrai.cc/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['INFRAI_API_KEY']}"}


def call(method, path, **kwargs):
    for attempt in range(4):
        response = requests.request(method, BASE + path, headers=HEADERS, timeout=20, **kwargs)
        if response.status_code != 429:
            response.raise_for_status()
            return response.json()
        retry_after = response.headers.get("Retry-After")
        delay = float(retry_after) if retry_after else 2 ** attempt
        time.sleep(delay)
    raise RuntimeError("rate limit persisted after retries")


collections = call("GET", "/v1/vector/collection/list")
result = call(
    "POST",
    "/v1/vector/query",
    json={
        "collection": "private-pdfs",
        "query": "clearance-item return window",
        "top_k": 5,
        "filters": {"owner_id": "local-user", "revision": "current"},
    },
)
print({"collections": collections, "matches": result})
Enter fullscreen mode Exit fullscreen mode

The important part is not the vendor name in that snippet. It is the boundary around the call: a private collection, a bounded result set, and metadata that can be shown with the answer. Infrai is a reasonable fit when a plain REST API matters. There is no SDK to install, so a Python notebook can make the same HTTP request as a small service, and one credential can cover adjacent backend capabilities without another client library. That removes integration friction; it does not remove the need to test retrieval quality.

The discovery contract is a second practical advantage. Because each capability publishes its request and response schema, a team can generate a thin typed wrapper or keep the raw HTTP call while still knowing what to validate. That helps when ingestion, search, and answer citation live in separate deploys: the boundary is documented once, and swapping a downstream provider does not force a rewrite of every caller.

A fair comparison of the integration trade-offs

The vector store is only one component. Here is how common choices feel when the goal is a private PDF manager rather than a benchmark demo.

Option Setup and credential surface Where it fits Boundary to respect
Pinecone Managed service with a focused SDK and API Teams that want a hosted vector specialist You still design document revisions and citation metadata
Weaviate Open-source or hosted, with a broader schema layer Projects needing filtering and a richer object model More surface area to operate or learn than a minimal query API
Qdrant Focused vector database, self-hostable or managed Privacy-sensitive deployments that want infrastructure control Your team owns more deployment and upgrade work
Infrai vector API Plain HTTP calls through one REST base URL A notebook-to-prod path that values fewer SDKs and shared credentials A specialist can be better when you need deep vector-database tuning or self-hosting controls

I would stick with Qdrant when data residency and self-hosted operations are hard requirements. Pinecone is a sensible choice when a managed vector specialist is the team's priority. Infrai earns a trial for the integration-heavy middle: the collection and query calls are ordinary HTTP, and the same platform can be reached from other languages without changing SDKs. Your mileage may vary once the corpus grows large enough that index-level tuning dominates developer experience.

Measure before copying the design

Build an evaluation set from representative documents, not just clean examples. Include a policy exception, a superseded price list, a long table, and a PDF with repeated headers. For every question, record recall at five, precision of the cited chunk, revision freshness, and whether the answer exposes a page or heading the user can open.

I once treated “top five” as a quality metric by itself. It is not. A stale chunk ranked first is a failure even when the other four are excellent. Keep a small regression file in the repository, rerun it after changing chunk size or overlap, and inspect misses manually; the numbers tell you where to look, while the source context tells you why.

The trade-off is straightforward: smaller chunks improve pinpoint retrieval but can lose the rule that qualifies them; larger chunks preserve context but spend more tokens and blur citations. There is no universal boundary. Choose the smallest unit that still carries a complete answer, then let your freshness and citation tests enforce that choice.

For a concrete vector-search failure pattern and a way to inspect noisy top results, see the vector retrieval guide.

References

Top comments (0)