Coding Agents for Historical Research

Python tools, project workflows, and delegated research

Bas Machielsen

What is a coding agent?

Accessing a model from Python

Models can be accessed through a browser or an application programming interface (API). An API lets a Python program send prompts and receive responses. chatlas handles this communication and maintains the conversation.

from chatlas import ChatAnthropic

chat = ChatAnthropic()
chat.chat("Explain what a company director does.")
chat.chat("How does that differ from a shareholder?")

Python program

Provider’s API

Model

Response

Requires an API credential. Setup instructions are in the reference slides.

Giving the model a function

A tool is a function made available to the model. The model requests a call and supplies its arguments. The surrounding software executes the function and returns its result.

def multiply(x: int, y: int) -> int:
    """Multiply two integers exactly."""
    return x * y

chat.register_tool(multiply)
chat.chat("Use multiply to calculate 20598162 × 83106206.")

Illustrative tool display; product checked with Python

Tool request: multiply(20598162, 83106206)
Tool result: 1711835094393372

Giving the model access to files

The same mechanism gives a model access to project files. Reading supplies information; writing creates a saved research output.

from pathlib import Path

def read_file(path: str) -> str:
    """Read a UTF-8 text file."""
    return Path(path).read_text(encoding="utf-8")

def write_file(path: str, content: str) -> str:
    """Save text to a UTF-8 file."""
    Path(path).write_text(content, encoding="utf-8")
    return f"Saved {path}"

chat.register_tool(read_file)
chat.register_tool(write_file)

From tool calls to an agent

chat.chat(
    "Read directors.csv. Summarize the directors, firms, "
    "and periods. Save the summary to director_summary.md."
)

Illustrative execution sequence

  1. Model requests read_file("directors.csv").
  2. Python returns the file contents.
  3. Model requests write_file(...) with the summary.
  4. Python saves it; the model reports completion.

Prepared output preview

# Director roster

## Directors and firms
F.H. Fentener van Vlissingen: SHV.

## Period covered
Service dates are not supplied.

An agent uses tools repeatedly, choosing its next action from the results. chatlas handles the tool-calling loop.

Tools for coding and research

Coding agents supply tools for inspecting files, editing code, and running commands. Web tools let them find sources and inspect their contents.

Tool Example action
Read / list / search files Locate and inspect directors.csv
Write / edit files Create a Python retrieval script
Run a command Execute the script and inspect its output
Web search Find biographies or genealogy records
Web fetch / browser Open a source and examine its contents

Write Python

Run it

Inspect the result

Choose the next action

The agent’s working environment

A terminal inside a project

A terminal accepts text commands. The shell interprets them and runs programs. Commands operate from a working directory, which determines where relative paths begin.

director_research/
├── directors.csv
├── director_summary.md
├── sources/
├── code/
└── outputs/
pwd                    # working directory
cd director_research   # enter the project
ls                     # list its contents
ls sources             # list source documents

sources/biography.md starts inside the working directory. .. refers to its parent directory. Launching an agent here gives it this workspace.

Running commands and saving output

An agent’s command tool can run the same programs as a person at the terminal. It can inspect output, identify a failure, and decide what to do next.

cat directors.csv
python code/summarize_directors.py
python code/summarize_directors.py > outputs/run.log

python is the program; the script path is an argument. > saves printed output to a file, replacing its previous contents.

Give the agent the task

Read directors.csv. Write a Python script that counts directors by firm. Run it, inspect the result, and save the script and a summary under code/ and outputs/.

Why plain text and Markdown?

Find the director’s birth date and spouse in the biography. Save the evidence and add a section on unresolved identities.

Working document What the agent needs to do
Scanned PDF Recognize text from page images; check names and dates
DOCX or PPTX Extract text from the document structure; check its order
Markdown Read and search the text directly; edit the relevant section

The editable note

# Director research
## Identity
## Evidence
## Unresolved identities

Plain text exposes the contents directly to file tools. Markdown expresses headings, links, and code within that text. Changes can be inspected line by line.

Converting documents into Markdown

Convert PDF, DOCX, and PPTX material into Markdown for the project’s working documentation. Check names, dates, tables, and reading order against the original.

anydoc sources/biography.pdf -o sources/biography.md
anydoc sources/notes.docx -o sources/notes.md
anydoc sources/slides.pptx -o sources/slides.md

Convert the source documents to Markdown with anydoc. Check the passages containing names and dates against the originals. Identify documents requiring OCR and record extraction problems.

Keep originals for verification. Scanned PDFs require OCR; anydoc reports this by default.

Commands checked against local anydoc --help, 30 September 2026.

Working with an agent

Launching an agent

Start the agent from the project directory. Claude Code and Codex provide interactive sessions in which an agent can inspect files, run commands, and save work.

Claude Code

cd director_research
claude

Codex

cd director_research
codex

Enter a task in the session. Read the response, inspect the files it creates, and continue with follow-up instructions.

Specifying the task and output

Read directors.csv. Summarize the directors, firms, and periods represented in the file. Save the summary to outputs/director_summary.md. Report missing information explicitly and list the files created.

A task specifies the input, objective, and required output. Naming a file makes the work available outside the conversation.

Structure of the saved file

# Director roster
## Directors and firms
## Period covered
## Missing information

Working interactively

Initial instruction

Summarize the director roster and save it to outputs/director_summary.md.

Follow-up

The period column describes service at the firm, not the person’s lifespan. Revise the summary to make that distinction explicit.

Further instruction

Add a section listing the information needed to distinguish directors with similar names. Base it on the columns actually present in the roster.

Follow-up instructions can correct an interpretation, change the task, or request further work. The agent can revise the saved file in the same session.

Persistent project instructions

CLAUDE.md and AGENTS.md provide project instructions across sessions: commands, research conventions, and output locations.

# Director research
## Commands
Run scripts with Python.
## Evidence
Compare names, dates, places, and family.
Save source URLs and identity decisions.
Record unresolved cases.
## Outputs
Save notes and tables under outputs/.

When AGENTS.md does not exist

ln -s CLAUDE.md AGENTS.md

The symbolic link gives both filenames the same contents.

Other coding agents, including Cursor, also exist. Their instruction conventions differ.

Running a saved prompt

Save this task as prompts/summarize.md

Read directors.csv.
Summarize the directors, firms, and service periods.
Write outputs/director_summary.md.
Report missing information and list the files created.

Claude Code

mkdir -p outputs
claude -p < prompts/summarize.md \
  > outputs/claude-summary.md

Codex

codex exec --sandbox workspace-write \
  -o outputs/codex-summary.md - \
  < prompts/summarize.md

The requested research file and the agent’s final message are separate outputs.

A research workflow

The research task

Starting from a director roster, identify each director’s parents, children, and spouse. Research those relatives’ firm affiliations and record the sources.

Worked row from a demonstration roster

Director Birth year Firm
Frederik Hendrik Fentener van Vlissingen 1849 SHV

Family relationships

Firm affiliations

Supporting sources

Save separate tables with identities, roles, dates, and source links.

Director and family details. Workflow adapted from the genealogical research in my paper.

How the agent approaches the task

Director roster

Source records

Relatives

Affiliations

The agent can write Python for repeated retrieval and table construction. It can also inspect the evidence, change a query, choose another source, and assess identities.

Repeated operation Decision after inspecting results
Query an API for each director Which name variant should be tried next?
Retrieve linked family records Does this concern the director or his son?
Combine affiliation rows Does the source establish a directorship or ownership?

Writing and running a source query

Write Python to search OpenArchieven for the director’s full name. Run it and inspect the candidate records.

import requests

def search_records(name: str) -> dict:
    response = requests.get(
        "https://api.openarchieven.nl/1.1/"
        "records/search.json",
        params={"name": name, "number_show": 5},
        timeout=30,
    )
    response.raise_for_status()
    return response.json()["response"]

result = search_records(
    "Frederik Hendrik Fentener van Vlissingen"
)

Saved API response: 71 person mentions

Event year Role
1962 Deceased
1962 Father
1918 Deceased

A person mention is not a distinct individual.

OpenArchieven API query, retrieved 30 September 2026.

Responding to the results

  1. The full-name query returns different people and record roles.
  2. The attempted Geni page is inaccessible; research continues in GenealogieOnline.
  3. The family tree supplies a spouse and marriage date as leads.
  4. An OpenArchieven marriage record identifies the groom born in 1849, his spouse, and parents.

Distinguish the director born in 1849 from his son. Compare roles, dates, spouse, and parents. Open the records and save the identity decision with its reasons.

Accepted anchor: marriage in 1879; groom born on 21 September 1849.

Retrieving the family

Join people to their marriage-record roles

people = {
    person["@pid"]: person
    for person in record["Person"]
}
roles = {
    relation["RelationType"]:
        people[relation["PersonKeyRef"]]
    for relation in record["RelationEP"]
}
father = roles["Vader van de bruidegom"]
mother = roles["Moeder van de bruidegom"]
spouse = roles["Bruid"]

Simon Paulus + Anna Maria Veldhuijs

F.H., born 1849 + Lijntje Roosenburg

F.H. (Frits), born 1882

Python extracts recorded relationships. The agent checks identities and investigates conflicting or incomplete links before expanding the family.

Researching affiliations

Research F.H. Fentener van Vlissingen, born in 1882. Try his full name, Frits, and F.H. with company and role terms. Open the sources. Record supported firms, roles, and dates. Use newly found firms for follow-up searches and keep unresolved questions explicit.

"Frits Fentener van Vlissingen" commissaris
"F.H. Fentener van Vlissingen" Hoogovens
"Frederik Hendrik Fentener van Vlissingen" KLM

Biography: paraphrased evidence

Hoogovens: delegated supervisory director, 1918–1945; chairman of the supervisory board, 1945–1953.

A biography supplies firms and roles that guide subsequent searches. The same task repeats for the other identified relatives.

The resulting research files

Supported affiliations of the director’s son, born in 1882

Firm Role Period
Hoogovens Delegated supervisory director 1918–1945
Hoogovens Chairman of the supervisory board 1945–1953
outputs/
├── identities.md
├── family.csv
├── affiliations.csv
└── unresolved.md

Python combines the saved rows. Notes retain identity decisions and sources.

Relatives whose affiliations have not been established remain unresolved.

Both roles: Biografisch Woordenboek van Nederland. Links are retained with the exported rows.

Delegating research tasks

Assigning a task, leaving the steps open

A research worker receives an objective, input information, and an output location. It chooses searches and follow-up actions from the evidence it finds.

Research the firm affiliations of F.H. Fentener van Vlissingen, born in 1882. Use the identified family record to distinguish namesakes. Choose suitable queries and follow source leads. Save firms, roles, periods, and URLs to outputs/people/fhfv-1882/affiliations.csv. Save evidence and unresolved questions alongside it.

Inspect evidence

Choose a search

Read a source

Assess and save

Spawning research agents

The main agent can delegate separate people to subagents. Each worker conducts its investigation and returns findings to the coordinator.

Spawn two research subagents. Assign one the director’s son, born in 1882, and the other his spouse, Lijntje Roosenburg. Give each the identified person record and affiliation-research task. Each saves files in a separate person folder. Wait for both, then summarize findings and unresolved questions.

Coordinator

Son’s research worker

Spouse’s research worker

Coordinator collects results

Reviewing and combining their work

Read both workers’ notes and rows. Check the assigned identities and whether sources support the roles and periods. Request further evidence where necessary. Use Python to combine reviewed rows into outputs/affiliations.csv and collect unresolved questions in outputs/unresolved.md.

outputs/
├── people/
│   ├── fhfv-1882/
│   └── roosenburg-1860/
├── affiliations.csv
└── unresolved.md

The coordinator reviews and combines the investigations. The researcher decides whether the evidence is sufficient for the analysis.

Coding agents can produce repeatable code and conduct searches that adapt to results. Saved code, evidence, and decisions make that work inspectable.

Reference material

Installation and sign-in

Claude Code: macOS, Linux, or WSL

curl -fsSL https://claude.ai/install.sh | bash
claude auth login
claude

Codex: npm installation

npm install -g @openai/codex
codex login
codex

Run the installed agent inside the project. For web research in Codex, use codex --search. Check the installed version’s tool and permission settings before running saved prompts unattended.

Claude setup; Codex CLI. Commands checked 30 September 2026.

Python API setup and accompanying examples

Install chatlas and its Anthropic provider dependency:

python -m pip install chatlas anthropic
export ANTHROPIC_API_KEY="your-api-key"

Offline research replay: from the website repository

python presentations/coding_agents/demo.py

Optional API example: from the prepared workspace

cd presentations/coding_agents/demo-workspace
python code/chatlas_example.py

The replay needs only Python’s standard library. The optional API example makes model calls; rendering the presentation does not.

Conversion and OCR

anydoc --help
anydoc sources/biography.pdf -o sources/biography.md

# For a scanned PDF: hosted OCR
anydoc sources/scan.pdf --ocr hosted -o sources/scan.md

Hosted OCR sends the document to the configured service. Set up its API access as described by the installed tool. Check the result against the source images.

Exit code Meaning
0 Conversion completed
1 Reading or conversion failed
2 Invalid invocation
3 PDF pages need OCR under default handling

Verified with local anydoc --help, 30 September 2026.

Sources: agents and command-line work

Sources: the worked family

Evidence inspected on 30 September 2026. Geni access failed. The replay uses saved archive responses and curated decisions; it is not an exhaustive affiliation survey.