Structured Modeling and Validation of Person Knowledge Entries

    This article presents an engineering approach for turning biography-style person entries into maintainable structured data. It covers the real data problems, a layered data model, an extraction pipeline, normalization examples, validation rules, storage and retrieval design, and operational quality gates for sustainable production.

    1. The Real Problem: From a Paragraph to Maintainable Data

    Consider a typical person entry. The following sample has been anonymized: a 20th-century French writer and critic, who also worked as a screenwriter and actor, served for a long time in the private secretariat of a senior official, wrote film and book reviews for the literary supplement of a major newspaper, and produced work across four directions: literary criticism, novels, diary-style works, and film/TV scripts. At the end of the entry, there is also a table showing “year — work — participation role.”

    This kind of text is very common in information extraction engineering. It mixes four completely different types of data into one piece of natural language:

    • Entity attributes: name in multiple languages, nationality, birthplace, birth and death dates, and occupation set.
    • Event timeline: employment periods, editorship terms, years of performance and screenwriting, death date, and burial location.
    • Work set: many-to-many relationships between works and people, with explicit role labels. The same work may involve a person both as a screenwriter and as an actor.
    • Person relationships: kinship, social relationships, and collaboration relationships.

    Storing all of this as one large text field may work in the short term, but it will fail in the long term: the fields cannot be counted, timelines cannot be compared, people with the same name cannot be disambiguated, and errors cannot be traced back to the source text.

    Therefore, the engineering goal should be clear: decompose biography text into four layers — entities, attributes, typed events, and role-bearing relationships — and ensure that every assertion can be traced back to a source text snippet and a source identifier.

    2. Data Model: Three-Layer Separation Instead of One Wide Table

    2.1 Layering Principles

    1. The main table should only contain stable attributes.
      Frequently changing content, such as summaries, tags, and popularity metrics, should not be placed in the main table. This avoids version conflicts.

    2. The timeline should be an independent table.
      One person corresponds to N events. These N events are the foundation for consistency validation and timeline visualization.

    3. Relationships should be independent and role-based.
      Work relationships must explicitly record the role, such as screenwriter, actor, or author. Otherwise, vague expressions like “participated in a film” will be misused downstream.

    4. Keep the original text traceable.
      Every derived data item should be attached to raw_text and a source identifier. This is the prerequisite for review and correction, and it is also the most reliable way to prevent hallucinations when working with large language model extraction.

    2.2 Schema Sketch

    1. -- Person master table: stable attributes only
    2. CREATE TABLE person (
    3. person_id VARCHAR(32) PRIMARY KEY,
    4. name_zh VARCHAR(128) NOT NULL,
    5. name_native VARCHAR(256), -- original name, multilingual
    6. gender CHAR(1) NOT NULL, -- M/F/U
    7. birth_place VARCHAR(256),
    8. birth_date DATE,
    9. death_date DATE,
    10. occupation_set VARCHAR(512), -- normalized occupation code set
    11. version_no INT NOT NULL DEFAULT 1,
    12. status VARCHAR(16) NOT NULL, -- draft/reviewing/published/offline
    13. updated_at TIMESTAMP
    14. );
    15. -- Timeline event table: appointments, awards, publications, performances, death, etc.
    16. CREATE TABLE person_event (
    17. event_id BIGSERIAL PRIMARY KEY,
    18. person_id VARCHAR(32) NOT NULL,
    19. event_type VARCHAR(32) NOT NULL, -- controlled enumeration
    20. title VARCHAR(256),
    21. org_name VARCHAR(256),
    22. start_date DATE, -- if only year is known, normalize to first day of the year and mark precision
    23. end_date DATE, -- NULL if ongoing
    24. date_prec VARCHAR(8), -- day/month/year/decade
    25. raw_text TEXT,
    26. source_id VARCHAR(256) NOT NULL
    27. );
    28. -- Work and relationship table: role-based many-to-many
    29. CREATE TABLE person_work (
    30. person_id VARCHAR(32) NOT NULL,
    31. work_name VARCHAR(256) NOT NULL,
    32. work_type VARCHAR(32) NOT NULL, -- literary essay/novel/diary-style work/film
    33. role VARCHAR(32) NOT NULL, -- screenwriter/lead actor/author/co-director
    34. work_year SMALLINT,
    35. award_name VARCHAR(128), -- award information, nullable
    36. PRIMARY KEY (person_id, work_name, role)
    37. );

    One often-overlooked detail is the date_prec field. In biography text, “late 1950s,” “1962,” and “March 22, 1996” have very different precision levels. If all of them are stored as plain DATE values, downstream sorting and statistics will suffer from systematic bias.

    3. Extraction Pipeline: Rules First, Models as Fallback, Human Review for Edge Cases

    3.1 Five Stages

    1. Chunking and field locating
      Preferably split content according to the inherent layout of encyclopedia entries: abstract paragraph, experience section, family section, works section, and tables. Layout priors can significantly improve accuracy and are more stable than extracting everything from the whole document without structure.

    2. Temporal expression normalization
      Convert expressions such as “served as editor-in-chief of a journal from 1949 to 1953” or “late 1950s” into ISO 8601 intervals with precision markers.

    3. Occupation and organization normalization
      Map free-text occupations to a controlled enumeration. Normalize organization names, including abbreviations, full names, and historical names.

    4. Table structuring
      Person work tables often contain merged cells and cross-row notes. First restore the two-dimensional grid, then parse it according to three semantic columns: year, work name list, and role.

    5. Entity linking and deduplication
      Disambiguate people with the same name using birth and death years, birthplace, and kinship relationships. If external authoritative identifiers are available, prioritize alignment by those identifiers.

    3.2 Normalization Example

    1. {
    2. "person": {
    3. "name_native": "<native name>",
    4. "birth_year": 1914,
    5. "death_year": 1996,
    6. "nationality": "FR",
    7. "occupations": ["WRITER", "CRITIC", "SCREENWRITER", "ACTOR"]
    8. },
    9. "events": [
    10. {"type": "GRADUATE_DEGREE", "field": "LAW", "start": "1938-01-01", "prec": "year"},
    11. {"type": "PRIVATE_SECRETARY", "org": "<senior official's office>", "start": "1944-01-01",
    12. "end": "1949-12-31", "prec": "year", "evidence": "worked in the private secretariat of ... from 1944 to 1949"},
    13. {"type": "EDITOR_IN_CHIEF", "org": "<monthly magazine>", "start": "1949-01-01",
    14. "end": "1953-12-31", "prec": "year"},
    15. {"type": "COLUMNIST", "org": "<newspaper literary supplement>", "start": "1954-01-01", "prec": "decade"},
    16. {"type": "DEATH", "start": "1996-03-22", "prec": "day"}
    17. ],
    18. "works": [
    19. {"name": "<literary essay>", "type": "ESSAY", "year": 1945, "role": "AUTHOR"},
    20. {"name": "<novel>", "type": "NOVEL", "year": 1959, "role": "AUTHOR",
    21. "award": "<literary award>"},
    22. {"name": "<film A>", "type": "FILM", "year": 1950, "role": "ACTOR"},
    23. {"name": "<film B>", "type": "FILM", "year": 1962, "role": "SCREENWRITER"}
    24. ]
    25. }

    The most important design in this structure is the evidence field. It allows an assertion, such as “served as editor-in-chief from 1949 to 1953,” to be traced back word for word. Reviewers can locate disputed points without rereading the entire text.

    4. Validation Rules: From “Looks Correct” to “Machine-Verified Correct”

    The most frequent errors in biography data are not missing extractions. They are extractions that are present but mutually contradictory. At minimum, the following categories of validation should be implemented.

    1. import datetime as dt
    2. def validate_person(p, events, works):
    3. errs = []
    4. # 1) Basic format and logic: birth/death order, plausible lifespan
    5. if p["birth_date"] and p["death_date"]:
    6. if p["birth_date"] > p["death_date"]:
    7. errs.append(("E_BIRTH_AFTER_DEATH", p["person_id"]))
    8. elif (p["death_date"] - p["birth_date"]).days > 365 * 130:
    9. errs.append(("E_AGE_OUT_OF_RANGE", p["person_id"]))
    10. # 2) Self-consistency of event intervals
    11. for e in events:
    12. if e["start_date"] and e["end_date"] and e["start_date"] > e["end_date"]:
    13. errs.append(("E_EVENT_RANGE_REVERSED", e["event_id"]))
    14. # 3) Events cannot start before birth year or after death year, except award or honorary events
    15. for e in events:
    16. if e["event_type"] in ("PRIVATE_SECRETARY", "EDITOR_IN_CHIEF", "COLUMNIST"):
    17. if p["birth_date"] and e["start_date"] < p["birth_date"].replace(year=p["birth_date"].year + 14):
    18. errs.append(("E_EVENT_TOO_EARLY", e["event_id"]))
    19. # 4) Mutually exclusive roles of the same type at the same organization should not overlap
    20. by_org = {}
    21. for e in events:
    22. if e["event_type"] == "EDITOR_IN_CHIEF":
    23. by_org.setdefault(e["org_name"], []).append(e)
    24. for org, lst in by_org.items():
    25. lst.sort(key=lambda x: x["start_date"] or dt.date.min)
    26. for a, b in zip(lst, lst[1:]):
    27. if a["end_date"] and b["start_date"] and a["end_date"] >= b["start_date"]:
    28. errs.append(("E_OVERLAP_SAME_ORG", org, a["event_id"], b["event_id"]))
    29. # 5) Work year must fall within the person's plausible activity period
    30. for w in works:
    31. if w["year"] and p["birth_date"] and w["year"] < p["birth_date"].year + 15:
    32. errs.append(("E_WORK_YEAR_IMPOSSIBLE", w["name"]))
    33. # 6) Required fields and enumerations
    34. if not p["occupations"]:
    35. errs.append(("E_OCCUPATION_EMPTY", p["person_id"]))
    36. if p["gender"] not in ("M", "F", "U"):
    37. errs.append(("E_GENDER_ENUM", p["person_id"]))
    38. return errs

    In addition to machine rules, three human intervention strategies are needed:

    • Low-confidence extraction: fields whose model confidence score is below a threshold should be forced into a review queue.
    • Cross-source conflicts: if two sources provide different values for the same attribute, the item should require human review.
    • High-impact entities: high-traffic people should be prioritized for careful review.

    Everything outside these three categories should be published automatically. Otherwise, review costs will grow linearly with the number of entries.

    5. Storage and Retrieval: Structured Querying and Semantic Recall Together

    Person knowledge entries usually serve two access patterns:

    • Precise condition filtering, such as “French writers who served as editor-in-chief between 1940 and 1960.”
    • Fuzzy semantic querying, such as “writers who created diary-style works and participated in film screenwriting.”

    A single storage solution is difficult to satisfy both needs. A practical design includes:

    • Structured storage: use a relational database for the main table, event table, and relationship table. This ensures transactional consistency and makes validation rules executable.
    • Retrieval layer: write summaries, work names, and event descriptions into a search service. Build inverted indexes to support keyword search and range filtering.
    • Semantic layer: generate embeddings for summaries and key snippets. Use hybrid retrieval with keyword recall, vector recall, and fusion ranking, such as Reciprocal Rank Fusion, to handle cases like “diary-style works” that may not match literally but are semantically related.
    • Raw and derived separation: store raw snapshots in object storage and store derived structured data in the database. Link the two through source identifiers so the pipeline can be rerun.
    • Observability: connect extraction latency, validation failure rate, and index latency to logging and monitoring systems. A sudden drop in rule hit rate is often the first signal of upstream page layout changes.

    6. Operational Loop: Turning One-Off Extraction into a Sustainable Pipeline

    The recommended workflow is a six-stage loop: discovery, extraction, validation, review, publication, and feedback. Quantifiable quality gates should be defined.

    Metric Definition Target
    Field completeness rate Non-empty required fields / total required fields ≥ 98%
    Validation pass rate Percentage of entries passing validation on first attempt ≥ 95%
    Extraction precision Percentage of correct assertions in sampled review ≥ 97%
    Manual rejection rate Percentage of rejected entries in the review queue ≤ 5%
    Deduplication accuracy Percentage of correct merge operations ≥ 99%
    Traceability coverage Percentage of assertions carrying evidence 100%

    Three practical conclusions are worth emphasizing.

    First, do layout parsing before semantic extraction. This is more controllable than a single end-to-end extraction pass. When errors occur, the localization cost is much lower.

    Second, define controlled vocabularies early. Once occupations, organizations, and work types are allowed to become free text, downstream statistics and aggregation will fail.

    Third, every assertion must carry a source text snippet and source identifier. This is essential for review efficiency, content compliance, and dispute resolution.

    With this model and workflow, a biography text of several thousand words can be reliably reduced to dozens of structured assertions. These assertions can then support downstream capabilities such as timeline visualization, relationship graphs, condition-based filtering, and semantic question answering, while keeping the marginal cost of adding new entries close to zero.