AI-Knowledge-Assistant

Agent Orchestration Layer

A tool-using agent built on top of the existing Ollama + Open WebUI stack — the local, self-hosted equivalent of what Voiceflow/Botpress-style “agent + RAG” builders do, without sending documents to a third-party cloud platform (see docs/01-system-readiness.md’s validation of that Instagram post: the pattern is legitimate, but those platforms are cloud SaaS).

This is a post-Gate-10 enhancement, per the master plan’s Section 27/39 rule to only add capability once the base gates pass and a real need justifies it — it does not replace or gate the base RAG assistant in docs/06-assistants.md.

Why “tool-using agent” over the alternatives

Two other shapes were considered and rejected for now:

Tool-calling — the model decides when to call a small, fixed set of local tools — gives real agentic behavior with a modest, auditable implementation: one Python module (frontend/agent.py, ~250 lines, stdlib only) plus one React screen.

Architecture

Browser (Agent screen)
   │  POST /agent/run  {model, kbIds, allowWeb, messages}
   ▼
frontend/server.py (loopback proxy, unchanged transport)
   │  forwards the browser's Open WebUI API key
   ▼
frontend/agent.py  run_agent()
   │
   ├── POST {ollama}/api/chat  { model, messages, tools, stream:false }
   │     Ollama's native tool-calling (verified against ollama/ollama
   │     docs/api.md) — NOT Open WebUI's chat/completions endpoint, so the
   │     tool-call loop is fully under this module's control.
   │
   ├── search_knowledge_base → POST {owui}/api/v1/retrieval/query/collection
   │     body: {collection_names: kbIds, query, k}
   │     (verified: open-webui/open-webui backend/open_webui/routers/
   │      retrieval.py, QueryCollectionsForm)
   │
   ├── read_document → GET {owui}/api/v1/files/{id}/data/content → {content}
   │     (verified: backend/open_webui/routers/files.py)
   │
   ├── get_current_datetime, calculate → local, deterministic, no network
   │
   └── web_search → DuckDuckGo HTML endpoint, OPT-IN ONLY (see below)

No new vector database, no new embedding model, no new LLM server — the tools call back into Open WebUI’s own REST API, reusing the single existing backend of truth (docs/00 Section “Do not over-engineer”).

The loop

run_agent() in frontend/agent.py:

  1. Build a system prompt listing the available tools and the filenames in the selected knowledge base(s) (fetched once via /api/v1/knowledge/).
  2. Call Ollama with the running message list + tool schema.
  3. If the response has no tool_calls, that’s the final answer — stop.
  4. Otherwise check each requested tool against the run’s dispatch allowlist, execute only permitted handlers, append a {"role":"tool", "tool_name", "content"} message per Ollama’s documented format, and loop.
  5. Hard cap: 6 steps. If exceeded, the last content is returned with truncated: true — never a silent infinite loop, never a fabricated “done” (matches the project’s “no silent caps” principle).

The full trace (every tool call, its arguments, and its result) is returned to the browser and rendered as a step-by-step card list before the final answer — more transparent than a black-box chat reply, and the closest analogue to Voiceflow/Botpress’s visible flow execution without building an actual flow canvas.

Tools

Tool What it does Network
search_knowledge_base RAG retrieval over the knowledge base(s) selected in the UI (the model cannot expand scope beyond what the user picked) Local (Open WebUI)
read_document Full extracted text of one named file, resolved by fuzzy filename match against the selected knowledge base(s) Local (Open WebUI)
get_current_datetime Current local date/time None
calculate Arithmetic via a restricted ast-based evaluator (+ - * / // % **, parentheses only — no names, no calls, so it cannot execute code) None
web_search DuckDuckGo HTML endpoint, no API key External — opt-in only

web_search — the one tool that leaves the machine

This directly implements the hybrid boundary described in docs/08-security-privacy.md and Section 38 of the master plan: private work stays local by default, and any external route is a deliberate, user-controlled exception — never automatic.

Security notes

Using it

Workspace → Agent in the frontend (frontend/README.md). Pick a tool-capable model (qwen3 is confirmed to support tool calling), select the knowledge base(s) it may search, leave “Allow web search” off unless you explicitly need it, and ask a question. Each tool call appears as its own card before the final answer.

Offline permission regression tests

Run python -m unittest discover -s frontend/tests -v from the repository root with Python 3.11 or 3.12. The standard-library tests use synthetic model responses and mock every service/search call; real HTTP requests are blocked and asserted absent. They cover disabled/enabled/unknown tool dispatch, strict request booleans, per-run isolation and selected knowledge-base scope. They do not validate live Ollama/Open WebUI APIs, Windows installation, document retrieval quality or backup restoration.