How WeKnora's Architecture Is Layered: Three-Process Core and Go Backend Tiers
WeKnora implements a three-process core architecture consisting of a Vue3 frontend, a Go-based core service, and a Python document reader, with the Go backend further stratified into Router, Handler, Service, Repository, and Infrastructure layers wired together via the uber-dig dependency injection container.
Tencent/WeKnora is an open-source knowledge management and RAG (Retrieval-Augmented Generation) platform. Understanding how WeKnora's architecture is layered reveals a modular design that separates client presentation, business logic, and document processing into distinct processes while maintaining clean internal tiers within the Go backend. The architecture supports multiple deployment shapes—from standard Docker-Compose to single-binary Lite mode—without changing the core codebase.
The Three-Process Core Architecture
WeKnora operates as a three-process core surrounded by persistence and optional infrastructure services. These processes communicate through well-defined protocols:
- Client Layer: Browser (Vue3 SPA), Miniprogram, CLI/Go SDK, MCP client, and IM platforms issue HTTP/HTTPS (REST + SSE) or webhook requests
- Frontend Layer: Nginx serves Vue3 static assets and reverse-proxies
/api/*routes to the Go service (frontend/Dockerfile) - Core Service (Go): The
appcontainer (wechatopenai/weknora-app) provides REST APIs, RAG retrieval, Agent engine, asynchronous task workers, and channel adapters (cmd/server/main.go) - DocReader (Python): The
docreadercontainer parses PDFs, DOCX, Excel, and EPUB files via gRPC (docreader/main.py) - Persistence: PostgreSQL (ParadeDB) stores business data with BM25 text indexing and
pgvectorembeddings; Redis hosts the Asynq task queue, SSE stream manager, Pub/Sub, and rate-limiting - Optional Components: Enabled via Docker-Compose profiles, including
searxng(web-search),neo4j(knowledge-graph),minio(object storage), vector stores (qdrant,milvus,weaviate,doris), andlangfuse(LLM observability)
Internal Go Backend Layering
The Go codebase follows a classic Handler → Service → Repository → Database four-tier design. Each layer is decoupled via interfaces defined in internal/types/interfaces/ and assembled through the uber-dig DI container (internal/container/container.go).
Router and Middleware Layer
The internal/router/ and internal/middleware/ packages handle Gin route registration, authentication, RBAC, rate-limiting, logging, and error handling. This layer acts as the entry point for all HTTP traffic before delegation to handlers.
Handler Layer
Located in internal/handler/, this layer parses request DTOs, invokes Service methods, and writes JSON responses. Handlers remain thin, containing no business logic—only translation between HTTP and domain objects.
Service Layer
The internal/application/service/ directory contains business orchestration logic for knowledge bases, chat pipelines, agents, tenants, and models. Services coordinate between repositories, external adapters, and domain logic. For example, internal/application/service/knowledge_service.go handles document processing workflows.
Repository Layer
Found in internal/application/repository/, this layer provides GORM-based data access with separate implementations for each retrieval engine under repository/retriever/*. Repositories abstract PostgreSQL interactions and vector store operations behind interface contracts.
Infrastructure Layer
The internal/infrastructure/, internal/models/, internal/stream/, internal/mcp/, internal/im/, and internal/sandbox/ packages contain adapters to external systems. These include the DocReader gRPC client, LLM provider integrations, vector store drivers, and sandbox runtimes for executing user code in isolated containers.
Inter-Process Communication Patterns
The layered architecture relies on specific protocols for cross-process communication:
| Path | Protocol | Implementation Details |
|---|---|---|
| Browser → Frontend → App | HTTP/HTTPS (REST + SSE) | Nginx reverse-proxies /api to the Go service; SSE managed via Redis |
| App → DocReader | gRPC (ReadStream) | Streams large files with optional TLS/mTLS |
| App ↔ PostgreSQL | PostgreSQL wire (GORM/pgx) | Business data, BM25 full-text search, pgvector embeddings |
| App ↔ Redis | RESP | Asynq queue, SSE stream manager, Pub/Sub, rate limiting |
| App ↔ Neo4j | Bolt | Optional GraphRAG storage |
| App ↔ External Services | Native SDK / HTTP / gRPC | Selected dynamically by RETRIEVE_DRIVER configuration |
| App ↔ Sandboxes | Docker Engine API / Cube / E2B | Isolated execution of user code |
Deployment Shapes and Flexibility
WeKnora's layered architecture supports multiple deployment configurations through environment variables and Compose profiles:
- Standard Docker-Compose: Launches all core and optional services with full functionality
- Lite Mode: Runs as a single binary with
DB_DRIVER=sqlite, no Redis dependency, and embedded frontend assets—falling back to an in-process synchronous task executor when Redis is unavailable - Desktop: Wails-based client in
cmd/desktop/wrapping the Go backend - Kubernetes: Production deployments using the Helm chart in
helm/ - Bare-Metal: Systemd units in
deploy/for traditional server deployments
All shapes share identical Go source code; only the orchestration and environment configuration differ.
Implementation Examples
Querying Knowledge Bases via Go SDK
The client/knowledgebase.go file implements the SDK interface for external applications:
package main
import (
"context"
"fmt"
"github.com/Tencent/WeKnora/main/client"
)
func main() {
cli := client.NewClient("http://localhost:8080")
cli.SetAPIKey("YOUR_API_KEY")
kbs, err := cli.KnowledgeBase.List(context.Background())
if err != nil {
panic(err)
}
for _, kb := range kbs {
fmt.Printf("KB %s – %s (tenant %d)\n", kb.ID, kb.Name, kb.TenantID)
}
}
Asynchronous Document Processing
The Service layer enqueues tasks for the DocReader via Asynq (configured in internal/container/container.go):
func (s *KnowledgeService) CreateKnowledgeFromFile(ctx context.Context, kbID string, filePath string) error {
if err := s.repo.CreateKnowledge(ctx, kbID, filePath); err != nil {
return err
}
return s.taskEnqueuer.Enqueue(ctx, task.TypeDocumentProcess, task.Payload{
KnowledgeID: newlyCreatedID,
})
}
Source: internal/application/service/knowledge_service.go and internal/application/repository/knowledge_repository.go.
RAG Chat Pipeline Usage
sess, _ := cli.Session.Create(context.Background(), client.SessionCreateRequest{
KnowledgeBaseID: "kb-uuid",
})
msg, _ := cli.Chat.Send(context.Background(), sess.ID, client.ChatMessage{
Role: "user",
Content: "What is the summary of the uploaded PDF?",
})
fmt.Println(msg.RenderedContent)
Source: client/message.go calls the pipeline orchestrated by internal/application/service/chat_service.go.
CLI Document Upload
weknora knowledge upload \
--kb-id 123e4567-e89b-12d3-a456-426614174000 \
--file ./sample.pdf \
--title "Sample PDF"
The CLI wraps the same HTTP API defined in client/resource_urls.go and documented in cli/README.md.
Summary
- WeKnora's architecture is layered into three distinct processes (Frontend, Go App, DocReader) with clean separation of concerns
- The Go backend implements five internal tiers: Router/Middleware, Handler, Service, Repository, and Infrastructure, connected via uber-dig dependency injection (internal/container/container.go)
- Communication protocols are protocol-specific: HTTP/REST for clients, gRPC for document processing, PostgreSQL wire for persistence, and RESP for Redis caching and queues
- Deployment flexibility allows the same codebase to run in Docker-Compose, Lite single-binary mode, Desktop (Wails), Kubernetes, or bare-metal configurations
- Optional components (vector stores, knowledge graphs, observability) integrate through driver interfaces without modifying core business logic
Frequently Asked Questions
What are the three core processes in WeKnora's architecture?
The three core processes are the Frontend (Nginx serving Vue3 static assets), the App (Go service handling REST APIs and business logic), and the DocReader (Python gRPC service for document parsing). These communicate via HTTP/HTTPS and gRPC respectively, forming the minimal runtime requirement for standard deployments.
How does the Go backend handle dependency injection?
The Go backend uses uber-dig to wire layers together in internal/container/container.go. The container conditionally provides either a real Asynq task executor when Redis is available or falls back to an in-process synchronous executor for Lite mode, allowing the same Service layer code to run in both high-availability and lightweight configurations.
What storage engines does WeKnora support for vector embeddings?
WeKnora supports multiple vector stores configurable via the RETRIEVE_DRIVER environment variable, including pgvector (embedded in PostgreSQL), Qdrant, Milvus, Weaviate, and Doris. The Repository layer abstracts these through interface definitions in internal/application/repository/retriever/.
Can WeKnora run without Docker?
Yes. WeKnora supports a Lite mode that compiles to a single binary using SQLite instead of PostgreSQL and an in-process task executor instead of Redis. This mode embeds frontend assets directly into the binary, making it suitable for desktop deployments via Wails or traditional bare-metal installations using the systemd units provided in deploy/.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →