Context proxy for AI agents. Enriches LLM context from a knowledge graph, extracts knowledge from responses.
Project description
acervo
Context proxy for AI agents.
Enriches context before. Extracts knowledge after. Remembers everything.
Every conversation your AI agent has starts from scratch. Every context is forgotten. Your agent asks the same questions, loses the same insights, and has no idea who it's talking to.
Acervo fixes that. It sits between the user and the LLM as a context proxy — building a knowledge graph that grows with every conversation, and assembling only the relevant context before each LLM call.
How it works
User message
│
▼
acervo.prepare(message, history) ← Acervo enriches context from graph
│ topic detection → query planning → context assembly
│ returns: context_stack (ready for LLM) + plan (GRAPH / WEB_SEARCH / READY)
│
▼
Your app calls the LLM ← You control the LLM, streaming, tools
│
▼
acervo.process(message, response) ← Acervo extracts knowledge from response
│ entity extraction → relation detection → fact persistence
│
▼
Knowledge accumulates. Token usage stays flat.
Acervo does not call the LLM itself. Your app controls the model, streaming, and tool execution. Acervo only enriches context and extracts knowledge.
Installation
pip install acervo
Or install from source:
git clone https://github.com/sandyeveliz/acervo.git
cd acervo
pip install -e .
Requirements
- Python 3.11+
- An OpenAI-compatible LLM server (LM Studio, Ollama, OpenAI, etc.)
Quick start
1. Start your LLM server
Acervo needs a small utility model for extraction, planning, and topic detection. Any OpenAI-compatible endpoint works.
With LM Studio: Load qwen2.5-3b-instruct (or any 3B+ model) and start the server on port 1234.
With Ollama:
ollama run qwen2.5:3b
2. Use the prepare/process API
import asyncio
from acervo import Acervo, OpenAIClient
async def main():
# Connect to your LLM (any OpenAI-compatible endpoint)
llm = OpenAIClient(
base_url="http://localhost:1234/v1",
model="qwen2.5-3b-instruct",
api_key="lm-studio",
)
memory = Acervo(llm=llm, owner="Sandy")
history = [{"role": "system", "content": "You are a helpful assistant."}]
# --- Turn 1: user asks something ---
user_msg = "I work at Altovallestudio, we build software"
history.append({"role": "user", "content": user_msg})
# Acervo prepares context from graph
prep = await memory.prepare(user_msg, history)
# prep.context_stack → messages with injected graph context
# prep.plan.tool → "READY" (no search needed)
# prep.has_context → False (first time, nothing in graph yet)
# Your app calls the LLM (use your own client, streaming, etc.)
assistant_msg = "Nice! Tell me more about your projects."
history.append({"role": "assistant", "content": assistant_msg})
# Acervo extracts knowledge from the conversation
await memory.process(user_msg, assistant_msg)
# Graph now has: Altovallestudio (Organización), Sandy (Persona)
# --- Turn 2: ask about something stored ---
user_msg2 = "What do you know about my company?"
history.append({"role": "user", "content": user_msg2})
prep2 = await memory.prepare(user_msg2, history)
# prep2.has_context → True!
# prep2.context_stack includes: "Altovallestudio (Organización) - Sandy works here"
print("Context for LLM:", prep2.context_stack)
asyncio.run(main())
3. Use the low-level API
If you don't need the full pipeline (topic detection, planning), you can use commit() and materialize() directly:
# Store knowledge
await memory.commit(
"Batman was created by Bill Finger and Bob Kane in 1939",
"He first appeared in Detective Comics #27.",
)
# Retrieve relevant context
context = memory.materialize("Batman")
# Returns: "# Batman (Personaje)\nEl usuario mencionó:\n- was created by Bill Finger..."
Tested setup
This is the stack we use daily for development and testing:
| Component | Tool | Model |
|---|---|---|
| LLM server | LM Studio | unsloth/qwen3.5-9b (chat) + qwen2.5-3b-instruct (utility) |
| Embeddings | Ollama | qwen3-embedding (optional, for topic detection L2) |
| Client app | AVS-Agents | Python TUI with Textual |
Acervo works with any OpenAI-compatible endpoint. The LLMClient protocol is simple:
class LLMClient(Protocol):
async def chat(
self,
messages: list[dict[str, str]],
*,
temperature: float = 0.0,
max_tokens: int = 500,
) -> str: ...
Features
Knowledge graph with two layers
- UNIVERSAL — world knowledge (cities, characters, technologies). Shared across users.
- PERSONAL — user-specific context (projects, preferences). Trusted within that user's session.
Auto-registering ontology
Built-in types: Persona, Personaje, Organización, Lugar, Tecnología, Obra, Universo, Editorial
Built-in relations: IS_A, CREATED_BY, ALIAS_OF, PART_OF, SET_IN, DEBUTED_IN, PUBLISHED_BY
When the LLM extracts a type or relation that doesn't exist yet, Acervo registers it automatically. No code changes needed.
Pipeline components
All behind a single LLMClient protocol — one small model handles everything:
| Component | What it does |
|---|---|
| Topic detector | 3-level cascade: keywords → embeddings → LLM classification |
| Query planner | LLM decides: use graph, search web, or respond directly |
| Context index | 3-layer stack (system + warm graph + hot messages) with token budgeting |
| Extractor | Entities, semantic relations, and facts from conversations and web results |
| Synthesizer | Renders graph nodes into compact text for context injection |
Constant token usage
Without Acervo: turn 1 → 200tk | turn 50 → 9000tk | turn 100 → limit
With Acervo: turn 1 → 200tk | turn 50 → 400tk | turn 100 → 420tk
Project status
v0.1.0 — first public release.
| Feature | Status |
|---|---|
| Knowledge graph (JSON persistence) | ✅ Working |
| Two-layer architecture (UNIVERSAL / PERSONAL) | ✅ Working |
| prepare() / process() context proxy API | ✅ Working |
| Auto-registering ontology | ✅ Working |
| Semantic relations (IS_A, CREATED_BY, ALIAS_OF, etc.) | ✅ Working |
| Topic detector (keywords → embeddings → LLM) | ✅ Working |
| Query planner (GRAPH_ALL / WEB_SEARCH / READY) | ✅ Working |
| Context index with token budgeting | ✅ Working |
| Built-in OpenAIClient (zero external deps) | ✅ Working |
| LLMClient + Embedder protocols | ✅ Working |
| Unit tests (56 passing) | ✅ Working |
| MCP server | 📋 Planned |
REST API (acervo serve) |
📋 Planned |
PyPI package (pip install acervo) |
📋 Planned |
acervo init directory indexer |
📋 Planned |
| Community knowledge packs | 📋 Planned |
| Vector search | 📋 Planned |
Documentation
- Tutorial — build a chat with persistent memory in 5 minutes
- Getting Started — installation, quickstart, LLMClient protocol
- Configuration — SDK parameters, environment variables
- Knowledge Layers — UNIVERSAL vs PERSONAL, node lifecycle, ontology
Why "Acervo"?
In library science, an acervo is the complete collection of a library — every book, document, and record it holds, organized so anything can be found when needed.
An agent's memory should work like a well-run library: knowledge organized by subject, filed in the right place, and retrieved by a librarian who knows exactly which shelf to go to — not by someone who reads every book from cover to cover every time you ask a question.
Contributing
Acervo is open source under Apache 2.0. See CONTRIBUTING.md to get started.
License
Apache 2.0 — see LICENSE. Copyright 2026 Sandy Veliz.
Built by Sandy Veliz · AltoValleStudio
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file acervo-0.1.1.tar.gz.
File metadata
- Download URL: acervo-0.1.1.tar.gz
- Upload date:
- Size: 55.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a873107b18d71cf082457700707d832cff2f8aa06d39aa3f4455f99ae50a7ad6
|
|
| MD5 |
921775138e6ec96b2208bc7055eba3af
|
|
| BLAKE2b-256 |
34700e27aca716314a51a82f2e9195980445f05e09e6105161547edbd05989a8
|
File details
Details for the file acervo-0.1.1-py3-none-any.whl.
File metadata
- Download URL: acervo-0.1.1-py3-none-any.whl
- Upload date:
- Size: 42.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
980974720094767b015450e93d2378cdb3e699d377c630a7bf0544c12aa3a01b
|
|
| MD5 |
470b6418976b62db9e73ef360e6c9fe8
|
|
| BLAKE2b-256 |
e6f7781cdefd42cf6b87e5c6145635bd0aa5bd673b197a9621f0aae60327cace
|