feat: derive knowledge reorganization with entity-driven topic aggregation - #64
Merged
Merged
Conversation
…ntire archive Previously, if a ZIP contained any file with an extension not in allowedInnerExtensions (e.g. .jpg, .png), the entire ZIP was rejected with "extension not allowed". This made it impossible to upload ZIPs containing a mix of documents and non-document files. Now unsupported entries are silently skipped and only supported files are extracted and processed.
slugFromTopic (Go) and normalise_slug (Python) now produce a
deterministic "t-{sha256[:10]}" slug when the regex normalization
strips every character (e.g. pure Chinese topics like '定价').
Previously these topics were rejected with 'invalid slug'.
Two bugs caused a cascading failure where 126 out of 136 failed tasks were killed by 'breaker open' without ever reaching the LLM: 1. Extract phase treated circuit.ErrOpen as a regular failure, calling Nack and burning a retry attempt. The pipeline phase already handled this correctly by abandoning without Nack. Apply the same pattern to extract: abandon and let RecoverExpired requeue the task. 2. Dispatcher claimed maxConc tasks per tick in half-open state, but the breaker only allows one in-flight probe — the rest were instantly rejected. Limit claiming to 1 task when the breaker is half-open. Root cause: transient LLM outage (timeouts + connection errors) tripped the breaker; the 30s cooldown/half-open cycle then permanently killed queued tasks every cycle by burning all 3 retry attempts on instant ErrOpen rejections.
…eature: p1-feat-002)
…eature: p1-feat-003)
…ure: p3-feat-003)
- .gitignore: exclude .ph directory - server_daemon: hardcode reorganize=True in _handle_derive for validation - docs/assets: add derive-flow SVG diagrams (en + zh)
…ture: p5-feat-003)
…ture: p5-feat-002)
…ure: p5-feat-005)
…(Feature: p5-feat-004)
…mon (Feature: p13-feat-002)
_title_words() stripped all CJK characters, causing _relevance_score()
to return 0 for every Chinese title and dedup_create_new() to skip all
Chinese titles. Together these made the classifier create one wiki
article per source file instead of merging related content.
Add _tokenize() that produces character bigrams for CJK runs (standard
IR technique, comparable granularity to Latin words) and splits Latin
runs on non-alphanumeric boundaries. _title_words() and the inline
topic tokenization in _relevance_score() both delegate to it.
Side fix: hyphenated topic tags like 'sun-wukong' now split into
{'sun', 'wukong'} instead of collapsing to {'sunwukong'}, so they
match article titles correctly.
…ature: p14-feat-006)
…reshold Add filter_threshold (float64, range (0,1]) to the full config chain: TOML [derive] section, KAAS_DERIVE_FILTER_THRESHOLD env override, Go bridge DeriveRequest, Python CLI --filter-threshold, and the select_by_topic engine function. When set, the acceptance threshold becomes ceil(filter_rounds * filter_threshold). When unset (0), the existing supermajority formula ceil(2N/3) is preserved, so the change is fully backward-compatible. Examples with filter_rounds=5: filter_threshold=0.5 → threshold=3 (simple majority) filter_threshold=0.67 → threshold=4 (supermajority, current default) filter_threshold=1.0 → threshold=5 (unanimity)
lucasmaan
approved these changes
Sep 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The derive pipeline produces a flat, per-source-document wiki: one topic per
input file, with topic names echoing filenames. When a knowledge base is built
from hundreds of overlapping documents the resulting wiki contains redundant
articles, inconsistent naming, and no cross-document structure — users must
mentally reassemble the knowledge graph themselves.
Separately, the write phase hit reliability and UX issues at scale:
exceed the LLM context window, causing silent truncation or timeouts.
large KBs unnecessarily slow.
whitespace-only tokenizer that collapsed Chinese/Japanese text into single
mega-tokens, producing near-random relevance scores; multi-round voting was
unavailable, so borderline articles were decided by a single noisy call.
with no status tracking, detail inspection, or progress feedback in the UI.
mixed conversation history, making multi-KB workflows confusing.
Change
120 commits across Python AI engine, Go backend, and React frontend. The
changes group into seven areas:
1. Knowledge reorganization (
py/src/kb_ai/derive/_reorganize.py, new)An optional post-derive phase that restructures the flat topic list into a
coherent, cross-document knowledge graph:
orgs, products, concepts) and builds an inverted index mapping entities to
the topics that reference them.
variant spellings, abbreviations, and aliases into canonical forms (e.g.
"AWS", "Amazon Web Services", "亚马逊云" → one canonical entry).
classify-topicalprompt thatclusters topics by semantic similarity using the entity co-occurrence graph
as a signal, replacing the filename-echo naming.
topics into a coherent wiki structure, reviewed by a two-round feedback loop
(
aggregate-plan-feedbackprompt) before execution.derive_kb/compile_kbbehind areorganizeflag (default off, configurable per derive job via TOML configand API payload).
2. Oversized merge handling (
py/src/kb_ai/commands/compile.py)budget, items are packed into right-sized batches with iterative
merge-reduce, so no single LLM call receives an oversized prompt.
are split into coherent sub-articles at section boundaries.
_MAX_BATCH_BUDGET(20K tokens) prevents merge-pathtimeouts by capping per-batch input regardless of model context size.
3. Batch-parallel write (
py/src/kb_ai/commands/compile.py)_merge_batch_splitis refactored intoPhase A (classify, parallel), Phase B (merge, parallel per article), Phase C
(split check, sequential) with a configurable parallelism threshold
(
batch_parallel_threshold) and batch-count limit(
batch_parallel_batch_limit)._run_chain— a reusable async chain runner that fans out independentbatches with bounded concurrency via semaphore, with early-break on batch
count to avoid runaway parallelism on small items.
path, preserving determinism for small KBs.
4. Derive quality improvements
_filter.py) —select_by_topicnow runsconfigurable voting rounds (
filter_rounds, default 1) where each roundindependently scores article relevance; the final decision uses
majority-vote aggregation with a configurable acceptance threshold
(
filter_threshold).classify.py) — the relevance scorer now usescharacter bigrams for CJK text instead of whitespace splitting, fixing
near-random scores on Chinese/Japanese corpora.
directives so the LLM responds in the corpus language, not its default.
it appends a numeric suffix.
5. Builds page & build jobs (
web/,internal/)A full-stack Builds page replacing the old Tasks page:
BuildJobmodel, SQLite store, API handlers(
build_jobs.go), automaticBuildJobcreation on submit. PagedListDerivedJobsPagedandDeleteDerivedJobendpoints. Generic pagedquery builder extracted to
sqlite/paged.go.with
PagedJobTableshared component,StatsBarsummary,StatusStageBadge, master-detailSheetpanels(
BuildJobDetailSheet,TaskDetailSheet,DeriveJobDetailSheet),DeriveWikiPreviewSheetfor inline wiki preview, article table inderive detail, auto-polling via
useAutoPollinghook.replacing full-row click, unified 160px column widths, immediate
thinking indicator in chat.
6. Chat KB switcher (
web/src/features/chat/)Sessionmodel gainskb_slug; SQLite schema updated with migration.ChatKBSelectorcomponent lets users switch KB context mid-conversation.7. Bug fixes & code review hardening
fix(upload): skip unsupported files in ZIP instead of rejecting theentire archive.
fix(worker): don't burn retry attempts on circuit-breaker rejection.fix(derive): hash-based fallback slug for pure CJK topics.fix: enable TOC sidebar scrolling on overflow.fix: Windows-native compatibility (cherry-picked from fix: Windows-native compatibility for the derive-reorganization branch #63).refactor: structured slog logging, sharedformatDateutility,extracted
PagedJobTable, generic paged query builder, import groupingcleanup.
Config surface
derive.reorganizefalsederive.filter_rounds1derive.filter_threshold0.5batch_parallel_threshold3batch_parallel_batch_limit8Test plan
Python AI engine (35 new/updated test files, ~5,700 lines):
orchestrator integration —
test_reorganize_*.py(3 files, ~1,900 lines)infrastructure —
test_compile_batch_split.py(2,100+ lines)_create_systemlanguage directive regression testGo backend (8 new/updated test files, ~2,800 lines):
internal/config: DeriveConf parsing, validation, env overridesinternal/store/sqlite: build_jobs CRUD, derived paged queries,session kb_slug migration
internal/api: build job handlers, derive endpoints, sessionkb_slug filtering, submit BuildJob creation
internal/derive: runner reorganize config threadinginternal/bridge: daemon client reorganize parametersinternal/worker: circuit-breaker attempt-burn fixWeb frontend (18 new/updated test files, ~3,100 lines):
auto-polling hook, wiki preview sheet
go test ./...— all passcd py && uv run pytest tests/ -v— all passcd web && pnpm test— all passWindows compatibility verified via fix: Windows-native compatibility for the derive-reorganization branch #63 cherry-pick
🤖 Generated with Claude Code