library(readr)
library(dplyr)
library(tibble)
library(tidyr)
library(purrr)
library(stringr)
library(digest)
entities <- read_csv(
"data/riverton/riverton-entities.csv",
na = character(),
col_types = cols(
entity_id = col_character(),
canonical_name = col_character(),
entity_type = col_character(),
description = col_character()
)
) |>
select(entity_id, canonical_name, entity_type)
aliases <- read_csv(
"data/riverton/riverton-aliases.csv",
na = character(),
col_types = cols(
entity_id = col_character(),
alias = col_character()
)
) |>
left_join(entities, by = "entity_id")
metadata <- read_csv(
"data/riverton/riverton-reference-metadata.csv",
na = character(),
col_types = cols(
artifact = col_character(),
description = col_character(),
source = col_character(),
license = col_character(),
created_on = col_character(),
fingerprint = col_character()
)
)
entity_hash <- digest(
paste(read_lines("data/riverton/riverton-entities.csv"), collapse = "\n"),
algo = "sha256",
serialize = FALSE
)
alias_hash <- digest(
paste(read_lines("data/riverton/riverton-aliases.csv"), collapse = "\n"),
algo = "sha256",
serialize = FALSE
)Adding facts to a knowledge base
Keep provenance, conflicts, NIL records, and decisions from checks visible
Grace receives a week of short announcements about Riverton training programs. Some contain the same facts. Two give different deadlines for the same program. Another uses the name Riverton, which could mean two different places in the reference table.
Knowledge base population links document mentions to entities and adds typed facts to a knowledge base. Provenance records each factβs sources and derivation. A knowledge base structures claims, sources, conflicts, and decisions from checks. It does not establish truth by itself.
A NIL record represents a mention with no matching entity in the reference table. A provisional ID preserves that mention without assuming an existing identity.
The Riverton records in this lesson are fictional teaching examples about training programs, places, and organisations.
By the end of this lesson, you will be able to:
- distinguish entity linking from fact insertion
- declare single-valued and list-valued slots before merging
- keep document dates, evidence text, and offsets for every fact
- merge duplicate facts without losing provenance
- keep conflicting dated values instead of overwriting them
- route negated, hedged, ambiguous, NIL, and type-failed candidates to human checks.
Load the Riverton reference list
The existing knowledge base starts with entity IDs and aliases. The lesson reads local CSV files with fixed column types. It computes each fileβs SHA-256 fingerprint, which identifies the file contents. It compares that fingerprint with the recorded value from file creation. A mismatch stops processing before the code adds any fact.
| Entity ID | Canonical name | Type |
|---|---|---|
| ORG-0001 | Riverton Workforce Lab | organisation |
| ORG-0002 | Riverton Skills Centre | organisation |
| ORG-0003 | Marrow County Transit | organisation |
| LOC-0001 | Riverton | place |
| LOC-0002 | Riverton | place |
| LOC-0003 | Bellhaven | place |
| CRD-0001 | Forklift Operator Licence | credential |
| CRD-0002 | Data Support Certificate | credential |
| Artifact | Rows | Computed SHA-256 | Recorded SHA-256 | Match |
|---|---|---|---|---|
| riverton-entities.csv | 8 | df260574f1f6088b51735a4778646ef22e5338186f74db321a96802d9033fe57 | df260574f1f6088b51735a4778646ef22e5338186f74db321a96802d9033fe57 | TRUE |
| riverton-aliases.csv | 17 | 0a419b6a8a742ffab404b8c58c209eaa90b0247a0ee30dbbfc85a6e914a85c64 | 0a419b6a8a742ffab404b8c58c209eaa90b0247a0ee30dbbfc85a6e914a85c64 | TRUE |
The table prints names and types only. It omits the forklift credentialβs description because this lesson concerns provenance. That description is not evidence for a job-board claim.
Declare slot rules before merging
A slot is a field with a specified value type. Some slots can contain a list. Others are single-valued for a named time period, so a second dated value becomes a conflict to examine. Cardinality specifies how many values a slot can contain.
slot_schema <- tribble(
~slot, ~subject_type, ~object_type, ~cardinality, ~merge_rule,
"offers", "organisation", "credential", "list-valued",
"One fact per subject-object pair. Additional evidence adds provenance.",
"requires", "organisation", "credential", "list-valued",
"One fact per subject-object pair. Negated or hedged statements do not become facts.",
"application_deadline", "organisation", "date literal", "single-valued",
"Keep each dated value. Mark the conflict. Let a person resolve the conflict."
)| Slot | Subject type | Object type | Cardinality | Merge rule |
|---|---|---|---|---|
| offers | organisation | credential | list-valued | One fact per subject-object pair. Additional evidence adds provenance. |
| requires | organisation | credential | list-valued | One fact per subject-object pair. Negated or hedged statements do not become facts. |
| application_deadline | organisation | date literal | single-valued | Keep each dated value. Mark the conflict. Let a person resolve the conflict. |
The slot schema defines duplicates and conflicts. Without those rules, a pipeline might replace a deadline and omit the value recorded on 4 September. It might also incorrectly reject two offerings as a conflict.
Prepare dated announcements
The documents below are constructed announcements. They include one duplicate, one single-valued conflict, one negated statement, and one hedged statement that expresses uncertainty. They also include an ambiguous Riverton mention, one NIL organisation, one undeclared slot, and one visible type-check failure.
extractor_version <- "riverton-kbp-rules-1.0"
documents <- tribble(
~document_id, ~document_date, ~text,
"A001", as.Date("2026-09-01"), "Riverton Skills Centre offers the Data Support Certificate.",
"A002", as.Date("2026-09-03"), "The Skills Centre offers DATA SUPPORT CERTIFICATE.",
"A003", as.Date("2026-09-04"), "Riverton Skills Centre application deadline is October 15.",
"A004", as.Date("2026-09-06"), "Riverton Skills Centre application deadline is November 1.",
"A005", as.Date("2026-09-07"), "Marrow County Transit does not require the Forklift Operator Licence.",
"A006", as.Date("2026-09-08"), "Riverton will host the Data Support Certificate.",
"A007", as.Date("2026-09-09"), "Riverton Transit offers forklift certification.",
"A008", as.Date("2026-09-10"), "Bellhaven offers the Data Support Certificate.",
"A009", as.Date("2026-09-11"), "Riverton Skills Centre may offer the Forklift Operator Licence.",
"A010", as.Date("2026-09-12"), "Riverton Skills Centre promotes the Data Support Certificate."
)
candidate_plan <- tribble(
~candidate_id, ~document_id, ~slot, ~subject_mention, ~object_mention, ~status, ~expected_action,
"CAND-001", "A001", "offers", "Riverton Skills Centre", "Data Support Certificate", "asserted", "add fact",
"CAND-002", "A002", "offers", "Skills Centre", "DATA SUPPORT CERTIFICATE", "asserted", "merge duplicate",
"CAND-003", "A003", "application_deadline", "Riverton Skills Centre", "October 15", "asserted", "add fact",
"CAND-004", "A004", "application_deadline", "Riverton Skills Centre", "November 1", "asserted", "flag conflict",
"CAND-005", "A005", "requires", "Marrow County Transit", "Forklift Operator Licence", "negated", "reject negated",
"CAND-006", "A006", "offers", "Riverton", "Data Support Certificate", "asserted", "hold ambiguous",
"CAND-007", "A007", "offers", "Riverton Transit", "forklift certification", "asserted", "hold NIL",
"CAND-008", "A008", "offers", "Bellhaven", "Data Support Certificate", "asserted", "reject type",
"CAND-009", "A009", "offers", "Riverton Skills Centre", "Forklift Operator Licence", "hedged", "hold hedged",
"CAND-010", "A010", "promotes", "Riverton Skills Centre", "Data Support Certificate", "asserted", "reject undeclared slot"
)| Document ID | Document date | Evidence text |
|---|---|---|
| A001 | 2026-09-01 | Riverton Skills Centre offers the Data Support Certificate. |
| A002 | 2026-09-03 | The Skills Centre offers DATA SUPPORT CERTIFICATE. |
| A003 | 2026-09-04 | Riverton Skills Centre application deadline is October 15. |
| A004 | 2026-09-06 | Riverton Skills Centre application deadline is November 1. |
| A005 | 2026-09-07 | Marrow County Transit does not require the Forklift Operator Licence. |
| A006 | 2026-09-08 | Riverton will host the Data Support Certificate. |
| A007 | 2026-09-09 | Riverton Transit offers forklift certification. |
| A008 | 2026-09-10 | Bellhaven offers the Data Support Certificate. |
| A009 | 2026-09-11 | Riverton Skills Centre may offer the Forklift Operator Licence. |
| A010 | 2026-09-12 | Riverton Skills Centre promotes the Data Support Certificate. |
| Candidate ID | Document ID | Slot | Subject mention | Object mention | Status | Expected action |
|---|---|---|---|---|---|---|
| CAND-001 | A001 | offers | Riverton Skills Centre | Data Support Certificate | asserted | add fact |
| CAND-002 | A002 | offers | Skills Centre | DATA SUPPORT CERTIFICATE | asserted | merge duplicate |
| CAND-003 | A003 | application_deadline | Riverton Skills Centre | October 15 | asserted | add fact |
| CAND-004 | A004 | application_deadline | Riverton Skills Centre | November 1 | asserted | flag conflict |
| CAND-005 | A005 | requires | Marrow County Transit | Forklift Operator Licence | negated | reject negated |
| CAND-006 | A006 | offers | Riverton | Data Support Certificate | asserted | hold ambiguous |
| CAND-007 | A007 | offers | Riverton Transit | forklift certification | asserted | hold NIL |
| CAND-008 | A008 | offers | Bellhaven | Data Support Certificate | asserted | reject type |
| CAND-009 | A009 | offers | Riverton Skills Centre | Forklift Operator Licence | hedged | hold hedged |
| CAND-010 | A010 | promotes | Riverton Skills Centre | Data Support Certificate | asserted | reject undeclared slot |
These expected actions are not a benchmark. The author wrote both the examples and the teaching pipelineβs checklist. The author also supplied candidates manually, in place of output from an extractor such as lesson 69βs relation extractor. The code links their mentions, examines types and status, and applies population rules.
Link mentions and keep offsets
The linker accepts an alias only when it has one candidate entity. An absent subject mention receives a provisional NIL ID. An absent object mention requires a human check. An alias with several candidate entities also requires a human check. The linker does not guess among similar names.
Show the mention-linking code
normalize_text <- function(value) {
str_to_lower(value, locale = "en")
}
alias_lookup <- aliases |>
mutate(normalized_alias = normalize_text(alias)) |>
distinct(normalized_alias, entity_id, canonical_name, entity_type)
link_mention <- function(mention) {
rows <- alias_lookup |>
filter(normalized_alias == normalize_text(mention))
if (nrow(rows) == 0L) {
return(tibble(
linked_id = NA_character_,
linked_type = NA_character_,
link_status = "NIL"
))
}
if (n_distinct(rows$entity_id) > 1L) {
return(tibble(
linked_id = paste(sort(unique(rows$entity_id)), collapse = "; "),
linked_type = paste(sort(unique(rows$entity_type)), collapse = "; "),
link_status = "ambiguous"
))
}
tibble(
linked_id = rows$entity_id[[1]],
linked_type = rows$entity_type[[1]],
link_status = "linked"
)
}
locate_mention <- function(text, mention) {
location <- str_locate(
text,
regex(escape_regex(mention), ignore_case = TRUE)
)
tibble(
start = as.integer(location[1, 1]),
end = as.integer(location[1, 2])
)
}
escape_regex <- function(value) {
str_replace_all(value, "([.\\\\|()\\[\\]{}^$*+?])", "\\\\\\1")
}
nil_mentions <- candidate_plan |>
distinct(subject_mention) |>
mutate(link = map(subject_mention, link_mention)) |>
unnest(link) |>
filter(link_status == "NIL") |>
mutate(provisional_id = sprintf("NIL-%04d", row_number()))
candidates <- candidate_plan |>
left_join(documents, by = "document_id") |>
mutate(
subject_link = map(subject_mention, link_mention),
object_link = map2(
slot,
object_mention,
\(current_slot, current_mention) {
if (current_slot == "application_deadline") {
tibble(
linked_id = current_mention,
linked_type = "date literal",
link_status = "literal value (not linked)"
)
} else {
link_mention(current_mention)
}
}
),
subject_offsets = map2(text, subject_mention, locate_mention),
object_offsets = map2(text, object_mention, locate_mention)
) |>
unnest(subject_link) |>
rename(
subject_id = linked_id,
subject_type = linked_type,
subject_link_status = link_status
) |>
unnest(object_link) |>
rename(
object_id = linked_id,
object_type = linked_type,
object_link_status = link_status
) |>
unnest(subject_offsets) |>
rename(subject_start = start, subject_end = end) |>
unnest(object_offsets) |>
rename(object_start = start, object_end = end) |>
left_join(
nil_mentions |>
select(subject_mention, provisional_id),
by = "subject_mention"
) |>
mutate(
subject_id = if_else(subject_link_status == "NIL", provisional_id, subject_id),
subject_type = if_else(subject_link_status == "NIL", "organisation", subject_type),
object_value = case_when(
slot == "application_deadline" ~ object_mention,
TRUE ~ object_id
)
)
link_table <- candidates |>
select(
candidate_id,
subject_mention,
subject_id,
subject_type,
subject_link_status,
object_mention,
object_id,
object_type,
object_link_status
)| Candidate ID | Subject mention | Subject ID | Subject type | Subject link status | Object mention | Object ID | Object type | Object link status |
|---|---|---|---|---|---|---|---|---|
| CAND-001 | Riverton Skills Centre | ORG-0002 | organisation | linked | Data Support Certificate | CRD-0002 | credential | linked |
| CAND-002 | Skills Centre | ORG-0002 | organisation | linked | DATA SUPPORT CERTIFICATE | CRD-0002 | credential | linked |
| CAND-003 | Riverton Skills Centre | ORG-0002 | organisation | linked | October 15 | October 15 | date literal | literal value (not linked) |
| CAND-004 | Riverton Skills Centre | ORG-0002 | organisation | linked | November 1 | November 1 | date literal | literal value (not linked) |
| CAND-005 | Marrow County Transit | ORG-0003 | organisation | linked | Forklift Operator Licence | CRD-0001 | credential | linked |
| CAND-006 | Riverton | LOC-0001; LOC-0002 | place | ambiguous | Data Support Certificate | CRD-0002 | credential | linked |
| CAND-007 | Riverton Transit | NIL-0001 | organisation | NIL | forklift certification | CRD-0001 | credential | linked |
| CAND-008 | Bellhaven | LOC-0003 | place | linked | Data Support Certificate | CRD-0002 | credential | linked |
| CAND-009 | Riverton Skills Centre | ORG-0002 | organisation | linked | Forklift Operator Licence | CRD-0001 | credential | linked |
| CAND-010 | Riverton Skills Centre | ORG-0002 | organisation | linked | Data Support Certificate | CRD-0002 | credential | linked |
The Riverton row keeps both candidate IDs in the check record. The linker does not select either place. It gives Riverton Transit a provisional NIL ID instead of merging that name into Marrow County Transit.
Apply slot, status, and type decisions
The action table is the evaluation for this lesson. It shows whether the pipeline made the expected decision for each candidate. It is not precision and recall.
Show the slot and type checks
schema_checks <- candidates |>
left_join(slot_schema, by = "slot") |>
mutate(
declared_slot = !is.na(subject_type.y),
subject_type_ok = declared_slot & subject_type.x == subject_type.y,
object_type_ok = case_when(
!declared_slot ~ FALSE,
object_type.y == "date literal" ~ str_detect(object_mention, "^[A-Z][a-z]+ [0-9]+$"),
TRUE ~ object_type.x == object_type.y
),
type_check = subject_type_ok & object_type_ok,
fact_key = paste(slot, subject_id, object_value, sep = "|")
)
first_seen <- schema_checks |>
filter(status == "asserted", subject_link_status == "linked", type_check) |>
arrange(document_date, candidate_id) |>
distinct(fact_key, .keep_all = TRUE) |>
transmute(first_candidate_id = candidate_id, fact_key)
single_value_seen <- schema_checks |>
filter(status == "asserted", subject_link_status == "linked", type_check) |>
filter(cardinality == "single-valued") |>
count(slot, subject_id, name = "single_value_count")
first_single_seen <- schema_checks |>
filter(status == "asserted", subject_link_status == "linked", type_check) |>
filter(cardinality == "single-valued") |>
arrange(document_date, candidate_id) |>
group_by(slot, subject_id) |>
slice_head(n = 1) |>
ungroup() |>
transmute(slot, subject_id, first_single_candidate_id = candidate_id)
candidate_actions <- schema_checks |>
left_join(first_seen, by = "fact_key") |>
left_join(single_value_seen, by = c("slot", "subject_id")) |>
left_join(first_single_seen, by = c("slot", "subject_id")) |>
mutate(
single_value_count = coalesce(single_value_count, 0L),
actual_action = case_when(
status == "negated" ~ "reject negated",
status == "hedged" ~ "hold hedged",
subject_link_status == "ambiguous" ~ "hold ambiguous",
subject_link_status == "NIL" ~ "hold NIL",
!declared_slot ~ "reject undeclared slot",
object_link_status == "NIL" | is.na(type_check) ~ "hold NIL",
!type_check ~ "reject type",
candidate_id != first_candidate_id ~ "merge duplicate",
cardinality == "single-valued" &
single_value_count > 1L &
candidate_id != first_single_candidate_id ~ "flag conflict",
TRUE ~ "add fact"
),
action_matches = actual_action == expected_action,
action_reason = case_when(
status == "negated" ~ "negated statements never become facts",
status == "hedged" ~ "hedged statements require human checks",
subject_link_status == "ambiguous" ~ "alias maps to more than one ID",
subject_link_status == "NIL" ~ "new name receives a provisional ID for human checks",
!declared_slot ~ "slot is not declared in the schema",
object_link_status == "NIL" | is.na(type_check) ~ "object mention is not linked",
!type_check ~ "slot type check failed",
candidate_id != first_candidate_id ~ "same fact already exists. Keep provenance.",
cardinality == "single-valued" &
single_value_count > 1L &
candidate_id != first_single_candidate_id ~ "single-valued slot has dated conflict",
TRUE ~ "asserted, linked, and type-compatible"
)
)| Candidate ID | Expected action | Pipeline action | Match | Reason |
|---|---|---|---|---|
| CAND-001 | add fact | add fact | TRUE | asserted, linked, and type-compatible |
| CAND-002 | merge duplicate | merge duplicate | TRUE | same fact already exists. Keep provenance. |
| CAND-003 | add fact | add fact | TRUE | asserted, linked, and type-compatible |
| CAND-004 | flag conflict | flag conflict | TRUE | single-valued slot has dated conflict |
| CAND-005 | reject negated | reject negated | TRUE | negated statements never become facts |
| CAND-006 | hold ambiguous | hold ambiguous | TRUE | alias maps to more than one ID |
| CAND-007 | hold NIL | hold NIL | TRUE | new name receives a provisional ID for human checks |
| CAND-008 | reject type | reject type | TRUE | slot type check failed |
| CAND-009 | hold hedged | hold hedged | TRUE | hedged statements require human checks |
| CAND-010 | reject undeclared slot | reject undeclared slot | TRUE | slot is not declared in the schema |
The type failure is visible: Bellhaven is a place ID, not an organisation. The pipeline rejects the negated requirement row. It neither stores a negative fact nor deletes an existing fact.
Build facts and provenance
The final fact table keeps one fact for the duplicate offer and two dated deadline values for the conflict. Provenance stays in a separate table so one fact can point to several source records.
accepted_candidates <- candidate_actions |>
filter(actual_action %in% c("add fact", "merge duplicate", "flag conflict"))
fact_keys <- accepted_candidates |>
distinct(slot, subject_id, object_value, fact_key) |>
arrange(slot, subject_id, object_value) |>
mutate(fact_id = sprintf("FACT-%03d", row_number()))
populated_facts <- accepted_candidates |>
inner_join(fact_keys, by = c("slot", "subject_id", "object_value", "fact_key")) |>
group_by(fact_id, slot, subject_id, object_value) |>
summarise(
provenance_records = n(),
conflict_flag = first(cardinality) == "single-valued" &
n_distinct(candidate_actions$object_value[
candidate_actions$slot == first(slot) &
candidate_actions$subject_id == first(subject_id) &
candidate_actions$actual_action %in% c("add fact", "flag conflict")
]) > 1L,
.groups = "drop"
) |>
arrange(fact_id)
fact_provenance <- accepted_candidates |>
inner_join(fact_keys, by = c("slot", "subject_id", "object_value", "fact_key")) |>
transmute(
fact_id,
candidate_id,
document_id,
document_date,
evidence_text = text,
subject_start,
subject_end,
object_start,
object_end,
candidate_source = "hand-supplied candidate",
pipeline_rules_version = extractor_version
) |>
arrange(fact_id, document_id)
review_queue <- candidate_actions |>
filter(!actual_action %in% c("add fact", "merge duplicate", "flag conflict")) |>
transmute(
candidate_id,
document_id,
subject_mention,
object_mention,
provisional_or_candidate_id = subject_id,
review_reason = action_reason
)| Fact ID | Slot | Subject ID | Object value | Provenance records | Conflict flagged |
|---|---|---|---|---|---|
| FACT-001 | application_deadline | ORG-0002 | November 1 | 1 | TRUE |
| FACT-002 | application_deadline | ORG-0002 | October 15 | 1 | TRUE |
| FACT-003 | offers | ORG-0002 | CRD-0002 | 2 | FALSE |
| Fact ID | Candidate ID | Document ID | Document date | Evidence text | Subject start | Subject end | Object start | Object end | Candidate source | Pipeline rules version |
|---|---|---|---|---|---|---|---|---|---|---|
| FACT-001 | CAND-004 | A004 | 2026-09-06 | Riverton Skills Centre application deadline is November 1. | 1 | 22 | 48 | 57 | hand-supplied candidate | riverton-kbp-rules-1.0 |
| FACT-002 | CAND-003 | A003 | 2026-09-04 | Riverton Skills Centre application deadline is October 15. | 1 | 22 | 48 | 57 | hand-supplied candidate | riverton-kbp-rules-1.0 |
| FACT-003 | CAND-001 | A001 | 2026-09-01 | Riverton Skills Centre offers the Data Support Certificate. | 1 | 22 | 35 | 58 | hand-supplied candidate | riverton-kbp-rules-1.0 |
| FACT-003 | CAND-002 | A002 | 2026-09-03 | The Skills Centre offers DATA SUPPORT CERTIFICATE. | 5 | 17 | 26 | 49 | hand-supplied candidate | riverton-kbp-rules-1.0 |
| Candidate ID | Document ID | Subject mention | Object mention | Provisional or candidate ID | Reason for human checks |
|---|---|---|---|---|---|
| CAND-005 | A005 | Marrow County Transit | Forklift Operator Licence | ORG-0003 | negated statements never become facts |
| CAND-006 | A006 | Riverton | Data Support Certificate | LOC-0001; LOC-0002 | alias maps to more than one ID |
| CAND-007 | A007 | Riverton Transit | forklift certification | NIL-0001 | new name receives a provisional ID for human checks |
| CAND-008 | A008 | Bellhaven | Data Support Certificate | LOC-0003 | slot type check failed |
| CAND-009 | A009 | Riverton Skills Centre | Forklift Operator Licence | ORG-0002 | hedged statements require human checks |
| CAND-010 | A010 | Riverton Skills Centre | Data Support Certificate | ORG-0002 | slot is not declared in the schema |
One offer fact has two provenance records. Both deadline facts remain and require human checks. A later document might contain an update, an error, or information about a different session. After applying the check rules, the pipeline adds 3 populated facts. The table records the conflict but does not resolve it.
Rendering creates the FACT- display IDs. A real knowledge base keeps fact IDs stable after publication.
Prepare the graph facts used in the visualization lesson
Lesson 81 redraws a small graph from the same IDs. The table below is a compact edge view of the populated facts. The two Riverton place IDs stay separate because entity IDs, not labels, are the keys.
graph_facts <- populated_facts |>
left_join(
fact_provenance |>
group_by(fact_id) |>
summarise(document_ids = paste(document_id, collapse = "; "), .groups = "drop"),
by = "fact_id"
) |>
transmute(
subject_id,
relation = slot,
object_value,
document_ids,
source = "populated fact"
)| Subject ID | Relation | Object value | Document IDs | Source |
|---|---|---|---|---|
| ORG-0002 | application_deadline | November 1 | A004 | populated fact |
| ORG-0002 | application_deadline | October 15 | A003 | populated fact |
| ORG-0002 | offers | CRD-0002 | A001; A002 | populated fact |
These graph edges are still KB records, not proof about the real world. The source column names the constructed population step. Document IDs refer to the provenance table above. Both Riverton place IDs remain in the entity table. Their shared label is a naming note for the visualization lesson, not a factual relation.
What the knowledge base does not know
A missing edge does not establish that a relation is false. Under this open-world approach, the KB records only claims accepted under its current policy. Records may be incomplete or outdated. A later document may contradict them. Provenance helps readers examine claims, but it does not certify truth.
What to remember
- Knowledge base population combines linking and slot filling.
- Declare slot cardinality before merging facts.
- Duplicate facts can share one fact ID with several provenance records.
- Single-valued conflicts keep both dated values until a human examines them.
- Negated and hedged statements do not become facts.
Rivertonstays ambiguous, and a NIL organisation receives a provisional ID.
Graceβs resulting knowledge base contains fewer automatic facts than candidate statements. The population step records additions, candidates requiring human checks, and the reasons for each decision.