---
id: financeiros/ia-texto-nao-estruturado
industry: Financial services
usage: widely used
effort: medium
grain: Conversation or group x cutoff date; CRM interaction; contact; case x extracted field for accuracy
---

# AI over unstructured text

Claim conversations, WhatsApp groups with partners, CRM notes and case documents become fields you can add up and audit: question, pending item, urgency, decision maker and extracted value, with accuracy measured against an answer key.

## Executive summary

**For:** Insurance brokers and insurtechs, lenders, pension and benefits platforms and asset managers that pile up text in conversations, notes and documents.

**What it solves**

- The most frequent questions and pending items come from real conversations, with an auditable count.
- Each conversation, interaction or contact gets closed-list fields you can filter and add up.
- AI extraction has its accuracy measured field by field against manual work.

**Why it matters:** In insurance, lending and pensions what matters to the customer is written down: the pending item on the claim, the HR objection, the line item in the court decision. Without structure, only whoever opens the conversation reads it. With AI labeling on fixed rules, the leader sees where operations get stuck, sales sees who decides and the pipeline automates extraction knowing how often it is right.

**Business questions it answers**

- What are the 10 most frequent partner questions in the WhatsApp groups this month?
- Which claims have an open pending item from operations or from the customer?
- Which groups have gone longest without a reply and have high urgency?
- Which companies in the book raised a price objection in recent interactions?
- Who is the decision maker at each account we are negotiating with?
- What is the accuracy of AI extraction against the manual calculation, by field?

**What you need**

- The text source connected to Nekt: platform database, WhatsApp logs, CRM or documents
- The list of who belongs to operations (ids, roles or phones) and the closed categories for each field
- The AI provider key stored as a secret, never in the code

**What you get**

- A table per conversation or interaction with AI fields and processing status
- A ranking of questions and pending items with counts computed in SQL
- An accuracy report by field against the answer key
- An AI agent that answers with the labels and the source excerpt

## Detailed

### The problem

The claim conversation, the group with the real estate agency and the meeting note stay as free text, and the answer to what the most common questions are depends on someone reading everything. Whoever sends raw text to the AI mixes staff messages with customer messages, gets labels that change on every run and stores a failed call as a calm conversation. Without an answer key, nobody knows whether the automatic extraction is right.

### Business rules

1. **Separate who speaks before sending to the AI.** Classify each message as operations or customer by the sender's id or role (claims analyst, brokerage admin) or by a table of team phones. Questions come only from customer messages; operations pending items come only from messages operations sent. An unknown sender is a customer, and this is stated. (Avoid: Sending the whole conversation and asking the AI for the customers' questions.)
2. **Clean and filter with rules before the model.** Replace base64 images and files embedded in the text with [attached image] and [attached file]. Normalize case and spaces. For questions, keep only messages with a question mark, more than 15 characters and that are not a short greeting. For income in bank transactions, first apply a dictionary of patterns (payroll, benefits, delivery apps) and send only what is left to the model. (Avoid: Sending the model the whole history, with base64 attachments and greetings.)
3. **The AI labels, SQL counts.** Group identical texts with their occurrence count. Ask the AI only for the label of each text (or each group), returned as a text-to-label mapping. The total per label is the sum of the counts in SQL. Limit the number of texts per call (very large prompts came back empty) and split into batches. (Avoid: Asking the AI for the top 10 with occurrence totals and storing the number it returned.)
4. **Closed list and validated JSON.** Each field has fixed values: sentiment (positive, neutral, negative), conversation status (open, closed, awaiting reply), urgency (low, medium, high), intent, topic and stage signal in the CRM, contact's decision power (decision maker, influencer, user). Validate the JSON, normalize keys (accents, case, keys in Portuguese or English) and send anything outside the list to "other". (Avoid: Accepting any text the AI returns in each field.)
5. **A failure is a failure, never neutral.** When the call fails, the JSON does not come or the quota runs out, store an error status, the error message and null fields. Never fill neutral sentiment and low urgency as default values. Retry with increasing waits and reprocess only the failures. (Avoid: Returning neutral and low when the API fails.)
6. **Process only what is new.** Store, per conversation or interaction, the hash of the text sent and the prompt version. Reprocess when a new message arrives or the prompt changes. For the groups queue, show those open and awaiting reply plus those closed within the last 2 hours, and count how many times each group was checked while open. (Avoid: Sending every conversation again on each run.)
7. **Measure accuracy against an answer key.** For document extraction (initial data and line items of a court case, for example), compare by case and field the AI's value with the manual work. The universe is only cases that have an answer key, with the latest version on each side. Person names: equality without accents and case. Company names: strong normalization (no legal suffix, punctuation or branch) and a matching ladder: equal, equal without spaces, prefix of 10 or more characters. Dates exactly equal. Amounts equal to 2 decimals. A missing side is "no data to compare", not an error. Items not comparable by value leave both sides. (Avoid: Judging the AI by reading a few cases, or counting as an error a field the answer key does not have.)
8. **Secrets and personal data stay out of the code.** The provider key is a Nekt secret, never written in the notebook (it appeared in plain text in more than one workspace). Mask CPF (individual tax ID), phone and email before sending when the label does not depend on them. Cut the text to a maximum length per call (for example, 3,000 characters per interaction) to control cost. (Avoid: API key and the customer's CPF inside the prompt and the code.)
9. **No test sample in production.** A 10-row limit for testing is useful, but it has to go before scheduling. Record on each run how many items were eligible, how many were sent, how many came back valid and how many failed. (Avoid: Scheduling the transformation with the test limit still in the code.)

### What to extract from each kind of text

Closed-list fields real operations extracted. The AI labels; counting and rules stay in SQL.

- **Claim conversation** → Operations pending item, customer question, last speaker (`sender by user role`)
- **WhatsApp group with partners** → Status, sentiment, urgency, action needed (`last 15 messages of the group`)
- **Customer messages in customer service** → Top process questions, with a descriptive label (`only messages with ? and no greeting`)
- **Sales interaction in the CRM** → Intent, topic, stage signal, objections, decision makers (`notes, calls, emails and meetings`)
- **Contact and job title** → Seniority and decision power (`decision maker, influencer, user`)
- **Case document** → Extracted fields, compared with the answer key (`per case and field`)
- **Bank transaction name** → Income source and frequency (`dictionary first, model for the rest`)
- **Notes and recorded meetings** → Text prepared for semantic search (`source record id on every chunk`)

### Definitions

| Metric | Definition |
|---|---|
| Eligible item | conversation, interaction, contact or document with new text (hash different from the last processed) or a new prompt version, on the run |
| Valid processing rate | items with valid JSON and all fields inside the list / items sent, per run |
| Question occurrences | sum of the counts of customer texts the AI mapped to the same label, in the period |
| Group awaiting reply | group or conversation whose last customer message is later than the last operations message; wait = now minus that message |
| Field accuracy | comparisons equal under the field-type rule / comparisons with both sides filled, per field and per month, only for cases with an answer key |
| Extraction coverage | cases with the field filled by the AI / cases with the field in the answer key |
| Topic share | interactions with the topic / interactions classified in the period, per book or company |

### Pitfalls

- **A failure became a calm conversation.** When the AI call failed, the function returned neutral sentiment and low urgency, and groups with problems vanished from the attention list. How to avoid: Error status and null fields, with reprocessing of failures.
- **A total made up by the AI.** The questions ranking stored the occurrence sum the model itself returned, and the number did not match the messages. How to avoid: Ask only for the text-to-label mapping and sum the counts in SQL.
- **A large prompt comes back empty.** Sending thousands of message types in one call made the AI return an empty list, and the notebook crashed out of memory when collecting everything. How to avoid: Cap on items per call, batches and a bounded collect.
- **API key in the notebook.** AI provider keys appeared written in transformation code, including as the default value when the environment variable did not exist. How to avoid: A Nekt secret and an explicit failure when the key is not configured.
- **Ten rows in production.** Contact and interaction classification ran on a schedule with the 10-row test limit, and the CRM looked enriched. How to avoid: Remove the limit and check eligible against sent on every run.
- **Company names never matched.** Comparing the extraction with the answer key flagged equal names written with LTDA, branch or abbreviation as errors, and the rule stopped working when the engine moved to Spark. How to avoid: Strong normalization with a matching ladder, tested on the transformation engine.

### Acceptance test

- [ ] No message from operations ids, roles or phones enters the customer questions ranking.
- [ ] For each label in the ranking, the total equals the sum of the counts of the texts mapped to it.
- [ ] Every item sent has a processing status (valid or error) and no error appears with sentiment or urgency filled in.
- [ ] Running the transformation again with no new messages sends no item to the AI.
- [ ] In the accuracy report, every case and field row has a status: equal, different, answer key only, AI only or no data to compare.

### Context document

```text
AI over unstructured text
- AI fields are in <table>, one record per <conversation | interaction | contact | document>, with status_processamento (processing status) and versao_prompt (prompt version).
- Consider only records with a valid processing status. Records with an error are not neutral: say they were not classified.
- Questions come only from customer messages. Operations pending items come only from operations messages (senders in <operations_table>).
- Categories: sentiment (positive, neutral, negative), status (open, closed, awaiting reply), urgency (low, medium, high), topic <list>, intent <list>. Outside the list = other.
- Counts and rankings come from SQL over the text-to-label mapping, never from totals generated by the AI.
- Group awaiting reply: the customer's last message is after the last operations message. Say how long it has been waiting.
- Extraction accuracy: use <accuracy_table>, only cases with an answer key; "no data to compare" is not an error.
- When citing a classification, bring the excerpt or the id of the source record.
Exceptions per client:
- <client>: operations identified by roles <roles>; all other senders are customers.
- <client>: line items <codes> are not comparable by value and stay out of accuracy.
```

### Variations

- **Summary per company:** An executive summary of the relationship per account, generated over the already classified interactions and refreshed only when a new interaction arrives.
- **Semantic search:** Notes, meetings and bios prepared for embedding with the source record id, so the agent can cite the source.
- **Extraction with human review:** Fields with low historical accuracy go to a review queue before moving on in the pipeline.
