Skip to main content

Agent Lexicon

A deterministic terminology layer for AI agents
One canonical vocabulary across agents, branches, and tool calls.

CI PyPI Python License

Proof · Quickstart · Concepts · How it works

When many agents work a long coding session, each one quietly invents its own names. One branch writes accessToken, another authToken, a third bearer_token — all the same concept. By merge time the service speaks five dialects of itself. Agent Lexicon gives every agent a single canonical vocabulary to read from, resolves the words they actually use back to that canon, and flags terminology that drifted before it lands in main.

It is dependency-free, runs locally, and is deterministic by design: the same input always produces the same output, and every decision carries a reason you can audit.

As a command-line tool, install it with pipx so agent-lexicon and the short alex alias are available in every project:

pipx install agent-lexicon
pipx install "agent-lexicon[completion]"   # with shell tab-completion

To use it as a library inside a project, install it with pip into that project's environment instead:

pip install agent-lexicon

Requires Python 3.10+. Apache 2.0. Zero runtime dependencies.


Proof

Benchmark: less terminology drift

Paired synthetic benchmark: the same coding tasks run with and without an agent-lexicon context brief.

Setup: Claude Sonnet 4.6, temperature 0, 10 tasks × 3 repeats × 2 conditions = 60 runs. Scored by an independent regex scorer over raw model output.

Metric No lexicon With agent-lexicon
Exact canonical name (strict) 0% 60%
Canonical term inside compound (substring) 10% 100%

This is an early synthetic benchmark, not a universal claim. Harness, tasks, scorer, and raw results: SkeinRank/agent-lexicon-benchmark.

Live output

Real output from the shipped example lexicon (examples/customer_limits/lexicon.yaml). Two terms share the surface word limitbilling.credit_limit and api.rate_limit. This is exactly where an agent drifts and calls the wrong tool.

An ambiguous word stops the agent instead of guessing:

$ agent-lexicon resolve examples/customer_limits/lexicon.yaml "please raise the limit"
Status: ambiguous
Action: ask_clarification
Message: Found 2 possible canonical terms.
Lexicon snapshot: sha256:98b7c5324a20c58926ea8e3413f87851c6d8c354e93197a69c56f1e142ea962e
Candidates:
- api.rate_limit (rate limit) scopes=api matches='limit'
- billing.credit_limit (credit limit) scopes=billing matches='limit'

The same word, scoped, resolves cleanly:

$ agent-lexicon resolve examples/customer_limits/lexicon.yaml "please raise the limit" --scope billing
Status: resolved
Action: use_terms
Message: Resolved to billing.credit_limit.
Lexicon snapshot: sha256:98b7c5324a20c58926ea8e3413f87851c6d8c354e93197a69c56f1e142ea962e
Candidates:
- billing.credit_limit (credit limit) scopes=billing matches='limit'

A wrong tool call is blocked before it runs:

$ agent-lexicon guard examples/customer_limits/lexicon.yaml "raise the credit limit" --tool api.update_rate_limit
Status: blocked
Action: block
Allowed: no
Reason: Requested tool is not allowed for the resolved terminology.
Resolution: resolved
Lexicon snapshot: sha256:98b7c5324a20c58926ea8e3413f87851c6d8c354e93197a69c56f1e142ea962e
Matched terms:
- billing.credit_limit
Allowed tools:
- billing.update_credit_limit

No model was called. No embedding was computed. Run it again and you get the same answer, byte for byte.


Why this exists

The longer and wider an agent session runs, the more the shared vocabulary drifts. This is not a hallucination problem — the agents are not inventing facts. They are naming the same concept inconsistently, in branches that never see each other until merge. The result is a codebase where one idea lives under several names, and nobody decided that on purpose.

Existing tools do not close this gap:

  • Knowledge graphs model how concepts relate, but require pre-built structure and do not gate a tool call at runtime on raw text.
  • LLM or embedding similarity can guess that two names mean the same thing, but the guess is non-deterministic and cannot be reproduced or audited a year later.
  • Linters catch inconsistent identifiers in code, but have no notion of a canonical term and do not work on prose, comments, or tool-call text.

Agent Lexicon is a different layer: it takes raw text in, normalizes it, resolves it against a reviewed canonical vocabulary, and returns a structured, deterministic decision. Optional semantics sit on top — as a suggestion to a human, never as the thing that decides.


How it works

Three pieces, each doing one job.

Resolve — Given a span of text, find the canonical terms and aliases inside it. Matching uses a dependency-free Aho-Corasick trie, so it is fast and works on prose, comments, and code-style identifiers (accessToken, access_token, ACCESS_TOKEN all resolve to the same term). Input is Unicode-normalized first, so invisible separators, full-width characters, and bidi-control tricks cannot slip a different term past the matcher.

Guard — Given resolved terminology and a tool the agent wants to call, decide whether that call is allowed. Ambiguous terminology returns ask_clarification. A tool that is not permitted for the resolved term returns block. Bidi-control characters in the triggering text are surfaced as a high-risk finding and block by default.

Drift detection at merge — Read the added lines between two git refs and classify every identifier: already known, a likely alias of an existing term, or a genuinely new term that nobody reviewed. The dangerous class — a coined name with no canonical neighbour — is what surfaces by default.

$ agent-lexicon check-merge --root . --base main --head feature-branch --include 'src/**'
Git merge terminology check: 1 files, 6 added lines
Range: main...feature-branch
Lexicon: lexicon/lexicon.yaml
Lexicon snapshot: sha256:98b7c5324a20c58926ea8e3413f87851c6d8c354e93197a69c56f1e142ea962e
Summary: known=2, likely_alias=0, likely_new_term=3, unresolved_unknown=0, hidden_unresolved=1
Known terminology:
- auth.py:2 'authToken' -> auth.access_token (access token) scopes=auth
New terminology candidates:
- auth.py:3 'credentialBlob' unknown; possible new term
- auth.py:4 'sessionKey' unknown; possible new term
- auth.py:5 'quuxHandle' unknown; possible new term
Hidden unresolved identifiers: 1. Use --include-unresolved-unknowns to inspect low-signal identifiers.

Add --fail-on-review to make this a blocking CI check that returns a non-zero exit code when unreviewed drift appears.


Three ways to use it

Command line — the full local loop, no code required. Every command is also available under the short alias alex, so alex resolve … works the same as agent-lexicon resolve ….

agent-lexicon init                      # create lexicon/, workspace, policy, and scan config
agent-lexicon scan                      # discover candidate terms from configured paths
agent-lexicon scan README.md docs src   # or override paths explicitly
agent-lexicon review                    # open the local web inbox to accept/reject
agent-lexicon publish --update-lexicon  # publish accepted decisions and update lexicon.yaml
agent-lexicon resolve <lexicon> "text"  # resolve terminology in any text
agent-lexicon guard   <lexicon> "text" --tool <name>   # gate a tool call
agent-lexicon context <lexicon>         # print the canonical vocabulary brief for an agent
agent-lexicon lint-diff --stdin         # lint a working diff for terminology drift
agent-lexicon check-merge --base main --head <branch>  # detect drift at merge
agent-lexicon check-merge --base main --head <branch> --semantic-check  # CI-style pass/fail

In an agent workflow

Two of these commands are built for wrapping an AI coding agent:

Before a task — hand the agent the project's canonical vocabulary so it starts with the right language:

agent-lexicon context lexicon/lexicon.yaml
# Use these canonical terms:
# - ContextSpace
# - RuntimeSnapshot
#
# Avoid:
# - WorkspaceScope (use "ContextSpace" instead)

During a task — check a working diff for terminology drift before it is committed, with layered severity:

git diff | agent-lexicon lint-diff --stdin
# Terminology lint: 2 files, 18 added lines
#
# Deprecated terms (fail):
# - src/session.py:14 WorkspaceScope -> use "ContextSpace"
#
# Possible typos / near-misses (warn):
# - docs/api.md:7 ContextSapce -> did you mean "ContextSpace"?
#
# New project terms (info):
# - src/memory.py:22 TaskMemoryProfile
# (exit code 1: a deprecated term was used)

Level 1 (declared deprecated terms) fails the check. Level 2 (lexical near-misses) warns, or fails under --strict. Level 3 (unknown project terms) is reported for awareness only. An optional --semantic flag adds probabilistic suggestions but never changes the exit code — enforcement stays deterministic.

At merge / PR — a deterministic terminology gate alongside your other CI checks:

agent-lexicon check-merge --base main --head HEAD --semantic-check
# Terminology check: 3 files, 42 added lines
# Semantic conflicts detected (1):
# - customer cap vs credit limit (use "credit limit")
# (exit code 1, so CI fails)

Both are deterministic: they flag terms the lexicon already declares (deprecated aliases and near-misses to canonical terms), never guesses.

Python library — call the same logic inline.

from agent_lexicon import load_lexicon, resolve_text, guard_tool_call

lexicon = load_lexicon("lexicon/lexicon.yaml")

decision = resolve_text(lexicon, "please raise the limit", scopes=["billing"])
print(decision.status)        # ResolutionStatus.RESOLVED
print(decision.action)        # ResolutionAction.USE_TERMS

guard = guard_tool_call(
    lexicon,
    "raise the credit limit",
    tool_name="api.update_rate_limit",
)
print(guard.status)           # ToolGuardStatus.BLOCKED

MCP server — expose the lexicon to any MCP-compatible agent over stdio.

agent-lexicon mcp serve --root . --lexicon lexicon/lexicon.yaml

The server exposes six tools: resolve_term, check_language, guard_tool_call, find_evidence, submit_proposal, and get_snapshot. List their full definitions with agent-lexicon mcp tools.

Repository scan config

agent-lexicon init creates .agent-lexicon/config.yaml so common repository scans do not need long CLI commands. By default, agent-lexicon scan starts from documentation and common source roots, applies language-aware include globs for popular stacks, and respects the repository .gitignore.

scan:
  paths:
    - README.md
    - docs
    - src
    - app
    - packages
    - lib
    - services
  include:
    - "docs/**/*.md"
    - "docs/**/*.txt"
    - "**/*.py"
    - "**/*.ts"
    - "**/*.tsx"
    - "**/*.go"
    - "**/*.rs"
    - "**/*.java"
    - "**/*.kt"
    - "**/*.cs"
    - "**/*.sql"
    - "**/*.yaml"
  exclude:
    - ".venv/**"
    - "node_modules/**"
    - "dist/**"
    - "**/generated/**"
  respect_gitignore: true
  max_file_bytes: 1000000

.gitignore is treated as the first line of repository-specific ignore behavior. Use scan.exclude for Agent Lexicon-specific rules such as generated fixtures that are still tracked in Git.

CLI flags still win when you need a one-off run:

agent-lexicon scan docs src --include "src/**/*.py" --exclude "src/generated/**"
agent-lexicon scan --no-gitignore
agent-lexicon check-merge --base main --head HEAD --exclude "docs/generated/**"

GitHub Actions workflow

The repository includes a terminology review workflow for pull requests. It validates the tracked lexicon and runs merge-time drift detection against the PR diff:

name: Agent Lexicon Terminology Review

on:
  pull_request:
    branches: [main]

jobs:
  terminology-review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - run: python -m pip install --upgrade poetry==2.1.1
      - run: poetry install --with dev
      - run: poetry run agent-lexicon validate lexicon/lexicon.yaml --lint --strict-lint
      - run: poetry run agent-lexicon check-merge --root . --base origin/${{ github.base_ref }} --head HEAD

The checked-in workflow is review-first by default: it prints terminology drift without blocking every PR. Set base_ref, head_ref, and fail_on_review=true for an on-demand blocking run, or add --fail-on-review when your team is ready to make terminology review a required merge gate.


The dictionary is code

The canonical vocabulary lives in a git-tracked YAML file. A term has a canonical form, aliases, the scopes it belongs to, and optionally the tools that are allowed to act on it.

version: 1
scopes:
  - id: billing
    label: Billing
  - id: api
    label: API
terms:
  - id: billing.credit_limit
    canonical: credit limit
    scopes: [billing]
    tools: [billing.update_credit_limit]
    aliases:
      - surface: customer cap
      - surface: account limit
  - id: api.rate_limit
    canonical: rate limit
    scopes: [api]
    tools: [api.update_rate_limit]
    aliases:
      - surface: requests per minute

Because it is just a file in the repo, the vocabulary versions, diffs, and reviews the same way your code does. Runtime decisions also carry a content-addressed snapshot reference (sha256:<digest>), so the same text can be replayed later against the exact same vocabulary content. A built-in linter warns when a surface is broad enough to over-trigger or to affect a guard decision:

$ agent-lexicon lint lexicon/lexicon.yaml
Lexicon lint: warnings (1 warning)
[warning] tool_broad_surface: tool-routed term uses a broad surface that can
  affect guard decisions (term=data.primary_key; surface='PK'). Hint: Use
  explicit tool-facing aliases and avoid bare words on terms with tools.

Review and publish decisions are kept as workspace provenance records and can be exported as JSONL for audit or handoff:

$ agent-lexicon workspace export-decision-log --root . --action review_decision_saved
{"action":"review_decision_saved","actor":"local","rule_id":"human_review",...}

Optional semantics, kept honest

When the deterministic heuristics are confident, they decide alone. When a new identifier lands in a gray zone — close to an existing term but not a clear match — an optional semantic reranker can suggest the most likely canonical neighbour, so a reviewer sees "authToken might be your access token" instead of an unsorted pile of unknowns.

pip install "agent-lexicon[oov]"        # tokenizer-backed out-of-vocabulary scoring
pip install "agent-lexicon[semantic]"   # semantic near-miss reranking

This is deliberately a suggestion to a human, marked as non-deterministic, never an autonomous decision. The semantic layer never commits a term on its own. The thing that decides stays deterministic and auditable; the thing that suggests is allowed to be smart. That boundary is the point — it is what keeps every committed decision reproducible.


Design guarantees

These hold on the deterministic runtime and local review paths:

  • Deterministic. The same text against the same immutable lexicon snapshot always produces the same decision. No model, no embedding, no randomness on the resolve and guard paths.
  • Reproducible. Runtime and merge reports include a content-addressed lexicon_snapshot_ref (sha256:<digest>), so a decision can be replayed later against the exact same vocabulary content.
  • Auditable. Every runtime decision reports its reason — which surface matched, at which span, in which scope, and why a tool was allowed or blocked. Local review and publish decisions are also written to an append-only provenance log with actor, action, rule, result, and lexicon snapshot metadata.
  • Dependency-free core. The resolver and matcher have zero runtime dependencies and run entirely in memory. Optional extras are opt-in and never touch the hot path.
  • Safe by construction. Local writes are atomic (a reader sees a complete file or none), and the workspace database is configured for concurrent access without torn reads.
  • Storage boundary. The local workspace is SQLite-backed by default, but workflow code depends on a small WorkspaceStore boundary so future shared storage can be added without changing the deterministic runtime.

Documentation

  • Quickstart — local setup, scan, review, publish, and runtime usage.
  • Concepts — terms, aliases, scopes, resolution, guard decisions, and merge-time drift detection.
  • Python API referenceload_lexicon, resolve_text, guard_tool_call, and the decision and enum types they return.
  • MCP server reference — the six MCP tools, their arguments, and their return values.

Contributing, security, and community:


Status

Agent Lexicon is an early, actively developed project (0.7.x). The core — resolve, guard, near-miss, dictionary-as-code, and merge-time drift detection — is well tested (326 passing tests) and used through the CLI, the Python API, and the local MCP server. Scaling it across many processes or a networked deployment is on the roadmap, not yet proven in production.

If terminology consistency across long, multi-agent sessions is a real cost for you — especially in regulated domains where decisions must be reproducible and auditable — this is built for exactly that.


License

Apache 2.0. Free for commercial use.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agent_lexicon-0.9.0.tar.gz (331.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agent_lexicon-0.9.0-py3-none-any.whl (248.8 kB view details)

Uploaded Python 3

File details

Details for the file agent_lexicon-0.9.0.tar.gz.

File metadata

  • Download URL: agent_lexicon-0.9.0.tar.gz
  • Upload date:
  • Size: 331.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for agent_lexicon-0.9.0.tar.gz
Algorithm Hash digest
SHA256 3a8cf695494359a3f2182263289b86499cb02c0c58b16f2fef7401396e41cbc6
MD5 fea66e024b5f9e09e99b70492db97241
BLAKE2b-256 c10709f167c93253f4b79dddc596260be9989d921843ee1c4d1b6bf6e42d5aaf

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_lexicon-0.9.0.tar.gz:

Publisher: publish.yml on SkeinRank/agent-lexicon

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file agent_lexicon-0.9.0-py3-none-any.whl.

File metadata

  • Download URL: agent_lexicon-0.9.0-py3-none-any.whl
  • Upload date:
  • Size: 248.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for agent_lexicon-0.9.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a32a82c36f093648e94e2d0ea73fae50ec707993bcbacaae5e47d83a98ff2e42
MD5 3ac48d5f1ec22eb42d0a39e10ddb84d5
BLAKE2b-256 35ad43a7d86bbc15ea3f4adc8f55d2dcf6f5863642bc50c9c03d03d21d4d49ea

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_lexicon-0.9.0-py3-none-any.whl:

Publisher: publish.yml on SkeinRank/agent-lexicon

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page