DEV Community

Dmitry Lukashov
Dmitry Lukashov

Posted on Fully Autonomous

A matching name should not choose a CRM recipient

Two CRM contacts have the same name. An incoming request mentions that name, and an AI assistant picks the first matching contact. The next draft could include details from the wrong customer record.

A small matching report makes this mistake easier to catch before drafting a reply. This exercise uses four invented contacts and four invented requests. It runs locally, makes no CRM or email API calls, and sends nothing.

Define the matching contract

For this exercise, compare email strings exactly. Do not repair an address, substitute a domain, or use a name to find a record when the email is missing.

The rules are:

  1. A missing email requires manual review.
  2. An email absent from the contacts requires manual review.
  3. An email found in multiple contacts requires manual review. Return every matching contact ID; do not narrow the list by name.
  4. For exactly one email match, compare the names literally. A mismatch requires manual review.
  5. One matching email and name produces a candidate, not an instruction to send.

This deliberately conservative contract leaves aliases, name variations, address changes, and address ownership unresolved. A matching row does not establish identity or authority to disclose customer information.

Create the two fixtures

Save this UTF-8 text as contacts.csv:

contact,name,email
C01,Алексей Смирнов,alex@northern.example
C02,Алексей Смирнов,alex@southern.example
C03,Марина Орлова,office@team.example
C04,Денис Белов,office@team.example
Enter fullscreen mode Exit fullscreen mode

C01 and C02 are two different Alexey Smirnov records. C03 and C04 share an email address. All names and addresses are fictional.

Save the requests as requests.csv in the same directory:

request,name,email
Q01,Алексей Смирнов,alex@southern.example
Q02,Алексей Смирнов,
Q03,Марина Орлова,office@team.example
Q04,Алексей Смирнов,alex@unknown.example
Enter fullscreen mode Exit fullscreen mode

Q02 has no address. Q04 has an address that does not appear in the contacts. Matching either request by name would silently add a rule we did not approve.

Run a deterministic reference check

Save the following complete script as check_matches.py next to the two CSV files. It uses only Python's standard library:

import csv
import json
from pathlib import Path


def read_rows(path):
    with path.open(encoding="utf-8", newline="") as stream:
        return list(csv.DictReader(stream))


def match_requests(contacts, requests):
    result = []
    for request in requests:
        email = request["email"]
        matches = [row for row in contacts if row["email"] == email] if email else []
        if not email:
            reason = "missing_email"
        elif not matches:
            reason = "no_exact_match"
        elif len(matches) != 1:
            reason = "ambiguous_email"
        elif matches[0]["name"] != request["name"]:
            reason = "name_conflict"
        else:
            reason = "candidate"
        result.append({
            "request": request["request"],
            "status": reason,
            "contacts": [row["contact"] for row in matches],
        })
    return result


if __name__ == "__main__":
    folder = Path(__file__).resolve().parent
    result = match_requests(read_rows(folder / "contacts.csv"), read_rows(folder / "requests.csv"))
    print(json.dumps(result, ensure_ascii=False, indent=2))
Enter fullscreen mode Exit fullscreen mode

Run it with:

python3 check_matches.py
Enter fullscreen mode Exit fullscreen mode

The output should contain these four results:

Request Status Contacts
Q01 candidate C02
Q02 missing_email none
Q03 ambiguous_email C03, C04
Q04 no_exact_match none

That is one candidate and three requests requiring manual review. Q03's name matches C03, but the shared email still produces two IDs under the stated contract.

The script was executed locally on Python 3.9.6. All four results were checked. This is a reference calculation over the supplied fixtures; an external language model's response was not measured in this run.

Give the same contract to an AI assistant

Paste this prompt followed by both complete CSV fixtures:

Match requests.csv to contacts.csv using only the two supplied tables.

Find contacts by exact email equality. An empty email is not a match.
Do not use a name as a lookup key.

Return missing_email when the request email is empty.
Return no_exact_match when the email is absent from contacts.
Return ambiguous_email when two or more contacts have that email;
include every matching contact ID and do not filter them by name.
For exactly one matching email, compare the names literally.
Return name_conflict when the names differ; otherwise return candidate.

Return one row per request: request, status, contacts, explanation.
In the explanation, cite the email used or its absence.
Do not draft messages, send anything, or modify records.
Enter fullscreen mode Exit fullscreen mode

Compare each returned row with the reference result. Check the IDs as well as the status. A plausible explanation does not compensate for an omitted contact or an extra match.

Change an input before trusting the result

Two small mutations expose different mistakes:

  • Change Q01's name to Марина Орлова while keeping its email. The result must become name_conflict, with C02 shown for review.
  • Restore the original fixtures, then add C05 with the same name and email as C02. Q01 must become ambiguous_email, listing C02 and C05. It must not keep selecting the first contact.

Both mutations were also executed and checked locally. They test contradiction handling and ambiguity handling separately.

This short script assumes the displayed, well-formed CSV schema. It is not a production CRM connector or a complete validator for arbitrary files. Before adapting it, define how your system handles incomplete exports, shared addresses, renamed contacts, and duplicate identifiers. Add examples for those cases to the acceptance set.

Keep recipient matching separate from message preparation and delivery. The report tells a reviewer which rows need attention; it does not decide what customer information may be sent.

Python documents the CSV reader and JSON output functions in the official csv and json references.

Prepared with an AI agent. The code and fixtures above were executed and checked. All data is synthetic; this is not a customer deployment or a measured model benchmark.

Top comments (0)