Knowledge Tools
Give your agents persistent memory with store, query, and delete operations.
Knowledge tools vs. Database tools
Connic provides two storage systems for agents. They serve different purposes and are often used together.
Stores text and finds it by meaning. Content is turned into vector embeddings, so a query for "cancellation rules" can surface a document titled "return and refund policy".
Use when:
- Searching unstructured text (FAQs, docs, notes)
- Results should be ranked by relevance, not filtered exactly
- The agent needs long-term memory it can recall naturally
- You don't know the exact query in advance
Stores structured data and finds it by exact field values. Query with operators like $gt, $in, or $andagainst any field in any collection.
Use when:
- Data is structured (orders, users, events, records)
- You need exact lookups, counts, or filters by field
- The agent creates, reads, updates, or deletes records
- Data volume or structure calls for real querying
Knowledge tools let your agents store and retrieve information that persists across runs. Store documents, FAQs, or any text, then query it naturally. The system finds relevant content based on meaning, not just keywords.
Storing knowledge is asynchronous: uploads are accepted first, processed as ingestion jobs in the background, and become retrievable after indexing completes.
Namespaces let you organize knowledge into a hierarchy using dot-separated names (e.g., "policies.hr.leave", "products.pricing"). Querying a parent namespace also searches all sub-namespaces. Entry IDs are unique within a namespace. Max depth is 10 levels.
You can view, search, and manage all stored knowledge from the Knowledge page in your project dashboard.
Custom tool wrappers
A common pattern is to wrap the knowledge tools in domain-specific functions so the agent callsremember or recall instead of working with namespaces and entry IDs directly. The routing logic is encoded once in the wrapper, keeping the agent focused on its task. You can also expose the tools directly in YAML for simpler agents. It's up to you.
from connic.tools import store_knowledge, query_knowledge, delete_knowledge
async def remember(content: str, topic: str) -> dict:
"""Store information under a topic."""
return await store_knowledge(content=content, namespace=topic)
async def recall(question: str, topic: str | None = None) -> list:
"""Search stored knowledge for relevant information."""
result = await query_knowledge(question, namespace=topic)
return result["results"]
async def forget(entry_id: str, topic: str) -> dict:
"""Remove an entry from a topic."""
return await delete_knowledge(entry_id=entry_id, namespace=topic)Use the custom tools in the agent YAML:
name: my-agent
model: gemini/gemini-2.5-flash
tools:
- memory.remember
- memory.recall
- memory.forgetParameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| query | string | required | The search query. Be specific for best results |
| namespace | string? | null | Filter results to a specific namespace |
| min_score | float | 0.7 | Minimum relevance score (0.0 to 1.0) |
| max_results | int | 3 | Maximum number of results to return |
| metadata_filter | dict? | null | MongoDB-style filter applied to entry metadata — same operators as db_find ($eq, $ne, $in, $gt, $exists, $or, …). Dot-notation for nested keys |
Return Value
Returns matching results with: content, entry_id, score (relevance 0-1), namespace, metadata
Examples
# Simple query
result = await query_knowledge("What is the refund policy?")
# Results contain matching content with relevance scores
for item in result["results"]:
print(f"[{item['score']:.0%}] {item['content'][:100]}...")# Filter by namespace
result = await query_knowledge(
query="How do I reset my password?",
namespace="support"
)
# Filter by metadata — same MongoDB-style operators as the database tools
# (\$eq, \$ne, \$gt, \$gte, \$lt, \$lte, \$in, \$nin, \$exists,
# \$regex, \$contains, \$elemMatch, \$and, \$or, \$nor, \$not).
# Bare values are equality shorthand.
result = await query_knowledge(
query="latest changes",
namespace="products",
metadata_filter={
"product_id": "X",
"status": {"$in": ["active", "pending"]},
},
)
# Adjust score threshold and result count
result = await query_knowledge(
query="pricing information",
min_score=0.5, # Lower threshold = more results
max_results=10 # Return up to 10 results
)Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| content | string | required | The text content to store, queued for asynchronous indexing |
| entry_id | string? | auto | Custom ID for the entry (UUID generated if omitted) |
| namespace | string? | null | Category for organizing knowledge |
| metadata | dict? | null | Additional key-value data to store |
Return Value
Returns: entry_id, job_id, status, queued, and success. Storage is asynchronous, so the entry becomes searchable after the background job completes.
Examples
# Simple store (auto-generated ID)
result = await store_knowledge(
content="The company refund policy allows returns within 30 days."
)
# Returns immediately with a queued job
# {"entry_id": "abc123...", "job_id": "...", "status": "pending", "queued": true, "success": true}# Store with custom ID for later updates
result = await store_knowledge(
content="Q1 sales target is $1M with focus on enterprise.",
entry_id="q1-sales-target",
namespace="planning",
metadata={"quarter": "Q1", "year": "2024"}
)
# Store user preferences
result = await store_knowledge(
content="User prefers dark mode and metric units.",
entry_id="user-preferences",
namespace="user_data"
)Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| entry_id | string? | null | ID of a single entry to delete. Omit when bulk-deleting by namespace |
| namespace | string? | null | Namespace to scope the deletion. Required for metadata-filter deletes; sub-namespaces are included |
| metadata_filter | dict? | null | MongoDB-style filter applied to entry metadata — same operators as db_find ($eq, $ne, $in, $or, …). Requires namespace |
Return Value
Returns: ok (success), deleted_chunks (number of underlying chunks removed). Provide either entry_id, or a namespace (optionally with metadata_filter). Entry IDs are unique per namespace, so specify the namespace if the same ID exists in multiple.
Examples
# Delete a single entry by id
result = await delete_knowledge(entry_id="old-product-info")
# Delete a single entry scoped to a namespace
result = await delete_knowledge(
entry_id="q1-sales-target",
namespace="planning",
)
# Bulk delete by metadata — uses the same MongoDB-style filter syntax
# as the database tools, so operators like \$ne / \$in / \$not work
result = await delete_knowledge(
namespace="products",
metadata_filter={"product_id": "X"},
)
# Orphan cleanup pattern: re-ingest a source, then delete every entry
# in scope from previous runs (everything that isn't the current run_id)
result = await delete_knowledge(
namespace="confluence",
metadata_filter={
"root_page_id": page_id,
"run_id": {"$ne": current_run_id},
},
)
# Wipe an entire namespace subtree (sub-namespaces included)
result = await delete_knowledge(namespace="meetings")Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| parent | string? | null | Parent namespace to list children of. If omitted, lists top-level namespaces |
| depth | int | 1 | How many levels deep to list (1 = direct children, 0 = all descendants, max 10) |
Return Value
Without parent: returns a list of namespace objects with name, entry_count, total_entry_count, has_children.
With parent: returns parent (info about the parent) and namespaces (list of children).
Examples
# List top-level namespaces
result = await kb_list_namespaces()
# Returns: [{"name": "policies", "entry_count": 5, "total_entry_count": 12, "has_children": true}, ...]
# Drill into a specific namespace
result = await kb_list_namespaces(parent="policies")
# Returns: {"parent": {...}, "namespaces": [{"name": "policies.hr", ...}, ...]}
# List all namespaces at all depths
result = await kb_list_namespaces(depth=0)Complete Agent Example
version: "1.0"
name: knowledge-agent
model: gemini/gemini-2.5-pro
description: "Agent with persistent memory"
system_prompt: |
You are an assistant with access to a knowledge base.
Always search the knowledge base first before answering.
tools:
- query_knowledge
- store_knowledge
- delete_knowledge
- kb_list_namespaces- Use descriptive entry IDs so you can update entries by ID later
- Use namespaces to scope searches and avoid unrelated results
- Query before answering so the agent uses stored knowledge
- Test queries directly in the Knowledge dashboard