Skip to content

feat(knowledge): detect potential-conflict pairs (agents' KB #4, detection half)#224

Merged
mkreyman merged 1 commit into
masterfrom
knowledge-conflict-detection
Jun 30, 2026
Merged

feat(knowledge): detect potential-conflict pairs (agents' KB #4, detection half)#224
mkreyman merged 1 commit into
masterfrom
knowledge-conflict-detection

Conversation

@mkreyman

Copy link
Copy Markdown
Owner

Route-the-findings: turn the nightly lint from a passive report into action + routed escalations. This PR is the DETECTION half; surfacing-at-retrieval is the next PR.

Principle

The KB only flags "too similar to comfortably coexist" pairs — it does not judge whether they're a redundancy to merge or a real contradiction. That judgment needs live context the nightly job lacks, so it belongs to the consuming agent. (An LLM contradiction-judge here would be the killed self-evolving-memory-compiler pattern.) Hence a new mechanical :potential_conflict link type, distinct from :contradicts (which asserts a known disagreement).

Changes

  • ArticleLink — new :potential_conflict relationship_type (string column → no migration).
  • ArticleLinkingWorker (forward, incremental) — per article it already runs nearest; now also creates a :potential_conflict link for any neighbor ≥ the conflict threshold (:knowledge_conflict_threshold, default 0.93), alongside the ambient :relates_to. Type-aware dedup so the two kinds don't crowd each other out.
  • KnowledgeLintWorker (existing-corpus backstop) — a bounded nightly sweep promotes pre-existing :relates_to links whose stored similarity_score ≥ the threshold to also carry :potential_conflict. No new embedding calls; cycles the corpus over nights (cap :knowledge_lint_max_conflict_promotions, default 500); idempotent. Reports conflicts_promoted in the lint audit event.

#1's gate keeps the published corpus de-duplicated going forward, so these flagged pairs are mostly shrinking legacy — low noise when surfaced at retrieval (next PR).

Tests (+5)

Forward detection (flag ≥ threshold, none below the threshold); promotion sweep (promote ≥ threshold, leave below alone, idempotent). Full gate green: format, credo, dialyzer, 3042 tests, 0 failures.

…ction half)

Route-the-findings: turn the nightly lint from a passive report into action +
routed escalations. This half adds DETECTION; surfacing-at-retrieval is the next PR.

The KB only FLAGS 'too similar to comfortably coexist' pairs — it does NOT judge
whether they're a redundancy to merge or a real contradiction. That judgment needs
live context the nightly job lacks, so it belongs to the consuming agent (the
established principle; and an LLM contradiction-judge here would be the killed
self-evolving-memory-compiler pattern). Hence a new mechanical :potential_conflict
link type, distinct from :contradicts (which asserts a known disagreement).

- ArticleLink: + :potential_conflict relationship_type (string column, no migration).
- ArticleLinkingWorker (forward, incremental): per article it already runs nearest;
  now ALSO creates a :potential_conflict link for any neighbor >= the conflict
  threshold (config :knowledge_conflict_threshold, default 0.93), alongside the
  ambient :relates_to. Type-aware dedup so the two link kinds don't crowd each other.
- KnowledgeLintWorker (existing-corpus backstop): a bounded nightly sweep promotes
  pre-existing :relates_to links whose stored similarity_score >= the threshold to
  also carry :potential_conflict — no new embedding calls, cycles the corpus over
  nights (cap :knowledge_lint_max_conflict_promotions, default 500), idempotent.
  Reports conflicts_promoted in the lint audit event.

#1's gate keeps the published corpus de-duplicated going forward, so these flagged
pairs are mostly shrinking legacy — low noise when surfaced at retrieval (next PR).

Tests (+5): forward detection (flag >= threshold, none below); promotion sweep
(promote >= threshold, leave below, idempotent). Full gate green (3042 tests).
@mkreyman mkreyman merged commit 37b3200 into master Jun 30, 2026
9 checks passed
@mkreyman mkreyman deleted the knowledge-conflict-detection branch June 30, 2026 23:01
mkreyman added a commit that referenced this pull request Jun 30, 2026
…s' KB #4, surfacing half) (#225)

The detection half (#224) creates :potential_conflict links; this makes them
travel with the search result so the consuming agent — the one with live context —
trips over them and resolves them.

- get_article JSON: + a flattened, actionable `potential_conflicts` field per article
  (peer article_id/title/similarity), derived from the already-loaded link graph.
  An agent reading an article now sees its 'too similar to coexist' peers directly,
  instead of digging through incoming/outgoing_links. Empty list when none.
- Knowledge.list_potential_conflicts/2: the tenant-wide review queue — every flagged
  pair, highest-overlap first (most likely a true duplicate), paginated, tenant-scoped.
- API: GET /api/v1/knowledge/conflicts (agent+), so a consuming agent or human can
  pull the whole queue and act.
- MCP v2.26.0: knowledge_conflicts tool (agent key); knowledge_get now carries
  potential_conflicts automatically (flows through the API). Also fixed a stale
  knowledge_drafts doc that still claimed an over-max limit is rejected-400 (it has
  clamped since the pagination work).

The KB only flags; every tool/field repeats that the caller decides redundancy-vs-
contradiction. Tests (+6 Elixir, +2 MCP): list ordering/pagination/isolation, the
get field (populated + empty), the endpoint, and the MCP plumbing. Full gate green
(3048 tests, dialyzer, credo; 52 MCP tests).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant