6c9c7fe7f3
CI / integration tests (3.13) (push) Failing after 1s
Commit lint / pull request title (push) Has been skipped
Docs / links (push) Failing after 1s
CI / unit tests (3.13) (push) Failing after 1s
CI / lint (push) Failing after 1s
CI / integration tests (push) Failing after 1s
CI / package build (push) Failing after 1s
Commit lint / commit messages (push) Failing after 1s
CI / unit tests (push) Failing after 1s
664 lines
20 KiB
Markdown
664 lines
20 KiB
Markdown
# Knowledge Base
|
||
|
||
The Knowledge module turns unstructured documents (Markdown, PDF, DOCX, …)
|
||
into a searchable topic library. Upload a file, and EverOS extracts a
|
||
structured topic tree via LLM, indexes it for keyword + vector search,
|
||
and keeps the original file for reference.
|
||
|
||
## Quick start
|
||
|
||
> The examples below assume EverOS is running on the default port 8000.
|
||
> See [README](../README.md) or [QUICKSTART](../QUICKSTART.md) to start
|
||
> the server.
|
||
|
||
```bash
|
||
# Upload a document
|
||
curl -s -X POST http://localhost:8000/api/v1/knowledge/documents \
|
||
-F "file=@my-report.pdf" \
|
||
-F "title=Q1 Engineering Report" \
|
||
| jq .data
|
||
# → { "doc_id": "d_a1b2c3d4e5f6", "category_id": "Technology", "topic_count": 8, ... }
|
||
|
||
# Search
|
||
curl -s -X POST http://localhost:8000/api/v1/knowledge/search \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"query": "performance bottleneck", "method": "hybrid"}' \
|
||
| jq '.data.hits[:3] | .[] | {topic_name, score}'
|
||
```
|
||
|
||
## Three-tier hierarchy
|
||
|
||
Knowledge is organized into three levels, from broadest to most granular:
|
||
|
||
```
|
||
L0 Category ← taxonomy bucket (e.g., "Technology", "Finance")
|
||
L1 Document ← one uploaded file = one document
|
||
L2 Topic ← LLM-extracted section with content
|
||
```
|
||
|
||
Each level corresponds to a different granularity of API:
|
||
|
||
| Level | Endpoint | Returns |
|
||
|-------|----------|---------|
|
||
| L0 | `GET /categories` | `category_id`, `description`, `document_count` |
|
||
| L1 | `GET /documents` | `doc_id`, `title`, `category_id`, `topic_count`, `created_at` |
|
||
| L1 (detail) | `GET /documents/{id}` | Full detail: summary, source info, original file path, topic list |
|
||
| L2 | `GET /topics/{id}` | Full topic: content, labels, tree position |
|
||
|
||
## Storage layout
|
||
|
||
Every document is a self-contained directory. Markdown files are the
|
||
single source of truth; SQLite and LanceDB are derived indexes built
|
||
automatically by the cascade daemon.
|
||
|
||
```
|
||
~/.everos/<app>/<project>/knowledge/
|
||
├── .taxonomy.md ← category definitions (YAML)
|
||
├── Technology/
|
||
│ └── Q1_Engineering_Report_d_a1b2c3d4e5f6/
|
||
│ ├── index.md ← document metadata + summary
|
||
│ ├── 1_Performance_Analysis.md ← topic with full content
|
||
│ ├── 2_Infrastructure_Costs.md
|
||
│ ├── 3_Team_Velocity.md
|
||
│ └── _original/ ← original uploaded file
|
||
│ └── my-report.pdf
|
||
└── Finance/
|
||
└── Budget_Review_d_f6e5d4c3b2a1/
|
||
├── index.md
|
||
├── 1_Revenue.md
|
||
└── _original/
|
||
└── budget.xlsx
|
||
```
|
||
|
||
### Storage roles
|
||
|
||
```
|
||
Markdown (source of truth) + SQLite (structured state) + LanceDB (vector + BM25 index)
|
||
```
|
||
|
||
| Store | What it holds | Role |
|
||
|-------|---------------|------|
|
||
| Markdown | Document metadata, summaries, topic content, original files | Single source of truth; human-readable and editable |
|
||
| SQLite | Document rows, topic rows (with content), change queue | Structured queries, paginated lists, count aggregation |
|
||
| LanceDB | Topic vectors, BM25 tokens, scalar fields | Search index (fully rebuildable from Markdown) |
|
||
|
||
Even if SQLite and LanceDB data is corrupted, as long as the Markdown
|
||
files are intact, the indexes can be fully rebuilt via the cascade daemon.
|
||
|
||
### Markdown format
|
||
|
||
**index.md** (document root):
|
||
|
||
```yaml
|
||
---
|
||
type: knowledge_document
|
||
id: d_a1b2c3d4e5f6
|
||
doc_id: d_a1b2c3d4e5f6
|
||
category_id: Technology
|
||
title: Q1 Engineering Report
|
||
schema_version: 1
|
||
source_name: my-report.pdf
|
||
source_type: file
|
||
---
|
||
This report covers Q1 engineering outcomes including performance
|
||
analysis, infrastructure costs, and team velocity metrics.
|
||
```
|
||
|
||
The body is an LLM-generated summary of the entire document.
|
||
|
||
**Topic files** (e.g., `1_Performance_Analysis.md`):
|
||
|
||
```yaml
|
||
---
|
||
type: knowledge_topic
|
||
id: d_a1b2c3d4e5f6_1
|
||
node_id: d_a1b2c3d4e5f6_1
|
||
doc_id: d_a1b2c3d4e5f6
|
||
category_id: Technology
|
||
topic_index: 1
|
||
topic_name: Performance Analysis
|
||
topic_path: Q1 Engineering Report > Performance Analysis
|
||
summary: Analysis of API latency, database query times, and caching hit rates.
|
||
depth: 1
|
||
parent_node_id: d_a1b2c3d4e5f6_0
|
||
children_node_ids: []
|
||
content_labels: ["performance", "latency", "caching"]
|
||
schema_version: 1
|
||
---
|
||
The P99 API latency dropped from 450ms to 120ms after the Redis
|
||
caching layer was deployed in week 6. Database query times improved
|
||
by 40% following the index optimization sprint...
|
||
```
|
||
|
||
The body is the full extracted content for this topic.
|
||
|
||
> The taxonomy file uses `kind` (not `type`) in its frontmatter to
|
||
> distinguish it from document and topic files, which use `type`.
|
||
|
||
### Original file preservation
|
||
|
||
The `_original/` subdirectory stores the uploaded binary file unchanged.
|
||
Users can locate the original via the `original_file_path` field returned
|
||
by `GET /documents/{doc_id}`.
|
||
|
||
The underscore prefix follows the Jekyll/Eleventy convention for
|
||
non-content directories that the cascade daemon should skip.
|
||
|
||
Lifecycle:
|
||
- **POST** (create) — writes `_original/<filename>`
|
||
- **PUT** (replace) — clears the old directory and writes the new file
|
||
- **DELETE** — removes the entire document directory including `_original/`
|
||
- **PATCH** (category change) — moves the whole directory; `_original/` follows
|
||
|
||
## Taxonomy
|
||
|
||
Categories are defined in `.taxonomy.md` at the knowledge root. EverOS
|
||
ships with 20 default categories:
|
||
|
||
| Category | Description |
|
||
|----------|-------------|
|
||
| Technology | CS, software, AI/ML, cloud, cybersecurity |
|
||
| Science | Physics, chemistry, biology, astronomy |
|
||
| Medical | Clinical medicine, drugs, public health |
|
||
| Finance | Securities, banking, accounting, fintech |
|
||
| Legal | Laws, contracts, compliance, IP |
|
||
| Education | Teaching, curriculum, e-learning |
|
||
| Business | Strategy, marketing, operations, HR |
|
||
| Engineering | Mechanical, civil, electrical engineering |
|
||
| Arts | Visual arts, music, literature, film |
|
||
| Sports | Athletics, fitness, sports science |
|
||
| Travel | Tourism, hospitality, transportation |
|
||
| Food | Culinary, nutrition, food safety |
|
||
| Environment | Climate, ecology, sustainability |
|
||
| Politics | Government, international relations, policy |
|
||
| History | Historical events, civilizations, historiography |
|
||
| Psychology | Cognitive science, behavioral psychology, mental health |
|
||
| Agriculture | Farming, crop science, agribusiness |
|
||
| RealEstate | Property development, urban planning, housing |
|
||
| Media | Journalism, social media, PR |
|
||
| Others | Fallback for unclassified documents |
|
||
|
||
### Customization
|
||
|
||
Edit `.taxonomy.md` directly to add, remove, or rename categories:
|
||
|
||
```yaml
|
||
---
|
||
kind: knowledge_taxonomy
|
||
categories:
|
||
- id: Technology
|
||
description: Computer science, software engineering, AI/ML.
|
||
- id: InternalOps
|
||
description: Company-specific operational procedures and runbooks.
|
||
- id: CustomerSuccess
|
||
description: Customer onboarding, support playbooks, case studies.
|
||
---
|
||
```
|
||
|
||
Taxonomy changes are **hot-reloaded** — no server restart needed. The
|
||
system reads `.taxonomy.md` from disk on every upload and category list
|
||
request, so edits take effect immediately.
|
||
|
||
When a document is uploaded, the LLM selects the best-matching category
|
||
from this list. If no category matches, the document falls back to
|
||
`Others`. You can also specify `category_id` explicitly in the upload
|
||
request to bypass LLM classification.
|
||
|
||
## API reference
|
||
|
||
All endpoints are under `/api/v1/knowledge`. Responses use the envelope
|
||
format `{"request_id": "...", "data": {...}}`. The `request_id` field is
|
||
omitted from examples below for brevity.
|
||
|
||
### Upload a document
|
||
|
||
```
|
||
POST /documents
|
||
Content-Type: multipart/form-data
|
||
```
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `file` | file | yes | The document to upload |
|
||
| `title` | string | yes | Human-readable title |
|
||
| `source_type` | string | no | Provenance type (`"file"`, `"url"`, …) |
|
||
| `category_id` | string | no | Skip LLM classification; use this category |
|
||
| `app_id` | string | no | Tenant app (default: `"default"`) |
|
||
| `project_id` | string | no | Tenant project (default: `"default"`) |
|
||
|
||
**Response** (201):
|
||
|
||
```json
|
||
{
|
||
"data": {
|
||
"doc_id": "d_a1b2c3d4e5f6",
|
||
"category_id": "Technology",
|
||
"topic_count": 8,
|
||
"source_name": "my-report.pdf",
|
||
"md_path": "/home/user/.everos/default_app/default_project/knowledge/Technology/Q1_Report_d_a1b2c3d4e5f6",
|
||
"original_file_path": "/home/user/.everos/.../Q1_Report_d_a1b2c3d4e5f6/_original/my-report.pdf"
|
||
}
|
||
}
|
||
```
|
||
|
||
`original_file_path` is the absolute path to the preserved upload, or `null`
|
||
when no binary was stored (e.g. an empty filename).
|
||
|
||
**Example — Python**:
|
||
|
||
```python
|
||
from pathlib import Path
|
||
|
||
import httpx
|
||
|
||
|
||
async def upload_document(file_path: str, title: str) -> dict:
|
||
async with httpx.AsyncClient(base_url="http://localhost:8000") as client:
|
||
with open(file_path, "rb") as f:
|
||
resp = await client.post(
|
||
"/api/v1/knowledge/documents",
|
||
files={"file": (Path(file_path).name, f)},
|
||
data={"title": title},
|
||
)
|
||
resp.raise_for_status()
|
||
return resp.json()["data"]
|
||
```
|
||
|
||
**Example — curl**:
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8000/api/v1/knowledge/documents \
|
||
-F "file=@report.pdf" \
|
||
-F "title=Quarterly Report" \
|
||
-F "category_id=Finance"
|
||
```
|
||
|
||
### Replace a document
|
||
|
||
```
|
||
PUT /documents/{doc_id}
|
||
Content-Type: multipart/form-data
|
||
```
|
||
|
||
Same fields as POST. Returns 404 if `doc_id` does not exist; on success
|
||
returns 200 (not 201). Atomic operation: if extraction fails, the old
|
||
document is restored from backup.
|
||
|
||
### Update metadata
|
||
|
||
```
|
||
PATCH /documents/{doc_id}
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"title": "Updated Title",
|
||
"category_id": "Finance"
|
||
}
|
||
```
|
||
|
||
Returns `doc_id`, `updated_at`, and `updated_fields: ["title", "category_id"]`.
|
||
Changing `category_id` moves the document directory to the new category folder.
|
||
|
||
### Delete a document
|
||
|
||
```
|
||
DELETE /documents/{doc_id}
|
||
```
|
||
|
||
Returns 204 when no topics were removed (document absent or present with zero
|
||
topics); 200 with `doc_id` + `deleted_topics` otherwise.
|
||
|
||
### List documents
|
||
|
||
```
|
||
GET /documents?page=1&page_size=20&sort_by=created_at&sort_order=desc
|
||
```
|
||
|
||
Optional filter: `category_id=Technology`. `sort_by` accepts `created_at`
|
||
(default), `updated_at`, or `title`; `sort_order` is `asc` or `desc`.
|
||
|
||
```json
|
||
{
|
||
"data": {
|
||
"documents": [
|
||
{
|
||
"doc_id": "d_a1b2c3d4e5f6",
|
||
"category_id": "Technology",
|
||
"title": "Q1 Engineering Report",
|
||
"topic_count": 8,
|
||
"created_at": "2026-06-24T10:00:00Z"
|
||
}
|
||
],
|
||
"total": 42,
|
||
"page": 1,
|
||
"page_size": 20
|
||
}
|
||
}
|
||
```
|
||
|
||
### Get document detail
|
||
|
||
```
|
||
GET /documents/{doc_id}
|
||
```
|
||
|
||
Returns full metadata, summary, original file path, and topic overview list.
|
||
|
||
```json
|
||
{
|
||
"data": {
|
||
"doc_id": "d_a1b2c3d4e5f6",
|
||
"category_id": "Technology",
|
||
"title": "Q1 Engineering Report",
|
||
"summary": "This report covers Q1 engineering outcomes...",
|
||
"source_name": "my-report.pdf",
|
||
"source_type": "file",
|
||
"original_file_path": "/home/user/.everos/.../Q1_Report_d_a1b2c3d4e5f6/_original/my-report.pdf",
|
||
"topics": [
|
||
{
|
||
"topic_id": "d_a1b2c3d4e5f6_1",
|
||
"topic_name": "Performance Analysis",
|
||
"topic_path": "Q1 Engineering Report > Performance Analysis",
|
||
"depth": 1,
|
||
"summary": "Analysis of API latency..."
|
||
}
|
||
],
|
||
"created_at": "2026-06-24T10:00:00Z",
|
||
"updated_at": "2026-06-24T10:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
`original_file_path` is `null` for documents created before the original
|
||
file preservation feature, or when no file was attached.
|
||
|
||
### Get topic detail
|
||
|
||
```
|
||
GET /topics/{topic_id}
|
||
```
|
||
|
||
Returns the full topic content, tree structure, and labels.
|
||
|
||
```json
|
||
{
|
||
"data": {
|
||
"topic_id": "d_a1b2c3d4e5f6_1",
|
||
"doc_id": "d_a1b2c3d4e5f6",
|
||
"category_id": "Technology",
|
||
"topic_name": "Performance Analysis",
|
||
"topic_path": "Q1 Engineering Report > Performance Analysis",
|
||
"depth": 1,
|
||
"summary": "Analysis of API latency, database query times...",
|
||
"content": "The P99 API latency dropped from 450ms to 120ms...",
|
||
"content_labels": ["performance", "latency", "caching"],
|
||
"parent_topic_id": "d_a1b2c3d4e5f6_0",
|
||
"children_topic_ids": [],
|
||
"created_at": "2026-06-24T10:00:00Z",
|
||
"updated_at": "2026-06-24T10:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
### Search
|
||
|
||
```
|
||
POST /search
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"query": "performance bottleneck",
|
||
"method": "hybrid",
|
||
"top_k": 10,
|
||
"include_content": true,
|
||
"score_threshold": 0.5
|
||
}
|
||
```
|
||
|
||
| Field | Type | Default | Description |
|
||
|-------|------|---------|-------------|
|
||
| `query` | string | — | Search query (required, 1–2000 chars) |
|
||
| `method` | string | `"hybrid"` | `"keyword"`, `"vector"`, or `"hybrid"` |
|
||
| `top_k` | int | 10 | Max results (1–100) |
|
||
| `include_content` | bool | false | Include full topic content in results |
|
||
| `score_threshold` | float | null | Drop results below this score |
|
||
|
||
**Search methods**:
|
||
|
||
- **keyword** — BM25 sparse retrieval over tokenized summary + content
|
||
- **vector** — Dense ANN over embedded summary vectors (requires embedding provider)
|
||
- **hybrid** — Parallel keyword + vector, fused with Reciprocal Rank Fusion (RRF), then cross-encoder reranking
|
||
|
||
All three methods embed the query and apply cross-encoder reranking, so
|
||
knowledge search requires **both** an embedding and a rerank provider —
|
||
there is no provider-free fallback (this is by design: no silent
|
||
degradation). The two failure modes map to distinct status codes:
|
||
|
||
- **Provider not configured** → `500 CONFIGURATION_ERROR` (a required
|
||
setting is missing; retrying will not help — set `EVEROS_EMBEDDING__*` /
|
||
`EVEROS_RERANK__*`).
|
||
- **Provider configured but failing/timing out at call time** →
|
||
`503 EXTERNAL_SERVICE_UNAVAILABLE` (transient; retryable).
|
||
|
||
**Response**:
|
||
|
||
```json
|
||
{
|
||
"data": {
|
||
"hits": [
|
||
{
|
||
"topic_id": "d_a1b2c3d4e5f6_1",
|
||
"category_id": "Technology",
|
||
"topic_name": "Performance Analysis",
|
||
"topic_path": "Q1 Engineering Report > Performance Analysis",
|
||
"depth": 1,
|
||
"summary": "Analysis of API latency...",
|
||
"content": "The P99 API latency dropped...",
|
||
"score": 0.92,
|
||
"retrieval_method": "hybrid",
|
||
"source": null,
|
||
"document": {
|
||
"doc_id": "d_a1b2c3d4e5f6",
|
||
"title": "Q1 Engineering Report",
|
||
"summary": "This report covers..."
|
||
}
|
||
}
|
||
],
|
||
"total": 3,
|
||
"took_ms": 245.6
|
||
}
|
||
}
|
||
```
|
||
|
||
### List categories
|
||
|
||
```
|
||
GET /categories
|
||
```
|
||
|
||
```json
|
||
{
|
||
"data": {
|
||
"categories": [
|
||
{"category_id": "Technology", "description": "Computer science...", "document_count": 12},
|
||
{"category_id": "Finance", "description": "Securities...", "document_count": 5},
|
||
{"category_id": "Others", "description": "Fallback...", "document_count": 0}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
## Search pipeline
|
||
|
||
```
|
||
query ─→ embed ─→ keyword (BM25) ─┐
|
||
vector (ANN) ──┤─→ RRF fusion ─→ rerank ─→ top_k
|
||
```
|
||
|
||
1. **Embed** — the query is embedded using the configured embedding provider
|
||
2. **Recall** — dual-channel retrieval from LanceDB:
|
||
- BM25 channel: keyword matching on `summary_tokens` + `content_tokens`
|
||
- ANN channel: nearest-neighbor search on the `vector` column
|
||
- In `hybrid` mode, both channels run in parallel
|
||
3. **Fuse** — Reciprocal Rank Fusion merges the two candidate lists
|
||
4. **Rerank** — cross-encoder reranker rescores the top candidates
|
||
5. **Filter** — drop results below `score_threshold` and limit to `top_k`
|
||
|
||
### Configuration
|
||
|
||
Search tuning parameters in `src/everos/config/default.toml`:
|
||
|
||
```toml
|
||
[knowledge.search]
|
||
recall_n = 200 # initial recall pool size per channel
|
||
rerank_n = 50 # candidates sent to reranker
|
||
mass_top_m = 50 # category-aware retrieve pool
|
||
lambda = 0.1 # category boost weight
|
||
top_k_cap = 100 # hard cap on returned results
|
||
```
|
||
|
||
Override via environment variables:
|
||
|
||
```bash
|
||
export EVEROS_KNOWLEDGE__SEARCH__RECALL_N=500
|
||
export EVEROS_KNOWLEDGE__SEARCH__RERANK_N=100
|
||
```
|
||
|
||
## Cascade sync
|
||
|
||
The cascade daemon watches the knowledge Markdown directory for file
|
||
changes and keeps SQLite + LanceDB in sync.
|
||
|
||
```
|
||
md file written
|
||
→ FSEvents / watchdog detects change
|
||
→ worker picks up from queue (≤1s poll interval)
|
||
→ handler dispatched by file type:
|
||
index.md → KnowledgeDocumentHandler → SQLite upsert (metadata)
|
||
N_topic.md → KnowledgeTopicHandler → tokenize + embed + SQLite + LanceDB upsert
|
||
```
|
||
|
||
The topic handler uses a SHA-256 content digest to skip unchanged files —
|
||
re-embedding only happens when the content actually changes.
|
||
|
||
Typical latency from file write to search availability: **1–3 seconds**.
|
||
|
||
## Supported file formats
|
||
|
||
EverOS accepts text-based files natively. Binary formats require the
|
||
`everos[multimodal]` extra (depends on LibreOffice for document conversion).
|
||
|
||
| Category | Formats | Requires `[multimodal]` |
|
||
|----------|---------|:-----------------------:|
|
||
| Text | `.txt`, `.md`, `.csv`, `.tsv`, `.vtt` | No |
|
||
| Documents | `.pdf`, `.docx`, `.doc`, `.rtf`, `.odt`, `.pages` | Yes |
|
||
| Spreadsheets | `.xlsx`, `.xls`, `.ods`, `.numbers` | Yes |
|
||
| Presentations | `.pptx`, `.ppt`, `.odp`, `.key` | Yes |
|
||
| Web | `.html`, `.htm`, `.eml` | Yes |
|
||
| Images (OCR) | `.png`, `.jpg`, `.webp`, `.tiff`, `.bmp`, `.svg` | Yes |
|
||
| Audio (transcription) | `.mp3`, `.wav`, `.m4a`, `.amr`, `.aiff`, `.aac`, `.ogg`, `.flac` | Yes |
|
||
|
||
```bash
|
||
pip install everos[multimodal]
|
||
```
|
||
|
||
## Error handling
|
||
|
||
| HTTP | Error code | Scenario |
|
||
|------|-----------|----------|
|
||
| 404 | `NOT_FOUND` | Document or topic does not exist |
|
||
| 409 | `CONFLICT` | `doc_id` already exists (use PUT to replace) |
|
||
| 415 | `UNSUPPORTED_FORMAT` | File format not parseable |
|
||
| 422 | `INVALID_INPUT` | Empty/oversized query, empty title, invalid ID format |
|
||
| 500 | `CONFIGURATION_ERROR` | Embedding or rerank provider not configured |
|
||
| 503 | `EXTERNAL_SERVICE_UNAVAILABLE` | Configured embedding/rerank provider failing at call time |
|
||
| 422 | `EXTRACTION_EMPTY` | Document parsed but extractor produced no topics |
|
||
| 503 | `CAPABILITY_UNAVAILABLE` | `everos[multimodal]` not installed |
|
||
|
||
All error responses use the standard error envelope — see
|
||
[api.md → Errors](api.md#errors).
|
||
|
||
## Multi-tenancy
|
||
|
||
All endpoints accept `app_id` and `project_id` parameters (default:
|
||
`"default"`). Data is fully isolated per tenant pair:
|
||
|
||
```bash
|
||
# Tenant A uploads
|
||
curl -X POST .../documents -F "file=@a.pdf" -F "title=A" \
|
||
-F "app_id=tenant_a" -F "project_id=proj_1"
|
||
|
||
# Tenant B cannot see Tenant A's data
|
||
curl .../documents?app_id=tenant_b&project_id=proj_1
|
||
# → { "documents": [], "total": 0 }
|
||
```
|
||
|
||
Storage paths, SQLite rows, and LanceDB indexes are all scoped by
|
||
`app_id` + `project_id`.
|
||
|
||
## End-to-end walkthrough
|
||
|
||
A complete workflow from upload to search:
|
||
|
||
```bash
|
||
BASE=http://localhost:8000/api/v1/knowledge
|
||
|
||
# 1. List available categories
|
||
curl -s "$BASE/categories" | jq '[.data.categories[] | .category_id]'
|
||
# → ["Technology", "Science", "Medical", ..., "Others"]
|
||
|
||
# 2. Upload a document
|
||
DOC_ID=$(curl -s -X POST "$BASE/documents" \
|
||
-F "file=@architecture-guide.md" \
|
||
-F "title=System Architecture Guide" \
|
||
| jq -r .data.doc_id)
|
||
echo "Created: $DOC_ID"
|
||
|
||
# 3. View document detail (with topic list)
|
||
curl -s "$BASE/documents/$DOC_ID" | jq '{
|
||
title: .data.title,
|
||
category: .data.category_id,
|
||
topics: [.data.topics[] | .topic_name],
|
||
original: .data.original_file_path
|
||
}'
|
||
|
||
# 4. Read a topic — pick the first from the detail response
|
||
TOPIC_ID=$(curl -s "$BASE/documents/$DOC_ID" \
|
||
| jq -r '.data.topics[0].topic_id')
|
||
curl -s "$BASE/topics/$TOPIC_ID" | jq '{
|
||
name: .data.topic_name,
|
||
path: .data.topic_path,
|
||
content: .data.content[:200],
|
||
labels: .data.content_labels
|
||
}'
|
||
|
||
# 5. Search (index is typically ready within 1–3 seconds)
|
||
sleep 3
|
||
curl -s -X POST "$BASE/search" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"query": "how to handle service failures",
|
||
"method": "hybrid",
|
||
"top_k": 5,
|
||
"include_content": true
|
||
}' | jq '.data | {
|
||
total,
|
||
took_ms,
|
||
top_hit: .hits[0] | {topic_name, score, content: .content[:100]}
|
||
}'
|
||
|
||
# 6. Move document to a different category
|
||
curl -s -X PATCH "$BASE/documents/$DOC_ID" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"category_id": "Engineering"}' \
|
||
| jq .data.updated_fields
|
||
# → ["category_id"]
|
||
|
||
# 7. Clean up
|
||
curl -s -X DELETE "$BASE/documents/$DOC_ID" | jq .
|
||
```
|