Vectorising support conversations
Semantic search over support history is genuinely useful: finding the precedent for an
unusual case, grounding an AI agent, or locating every conversation about a topic without
guessing keywords.
Two things make this harder than the usual document-embedding pipeline, and both need
deciding before the first vector is written.
Conversations are not documents. They are multi-party, interleaved, and the useful unit is
almost never the whole thread or the single message.
An embedding is a copy. Deleting a conversation and leaving its vectors in an index means
the content is still retrievable, so erasure and retention have to reach the index too — and
an index built by an external API means the content went to that API.
Chunk on turn boundaries, not on character counts
Fixed-size chunking is the default in most tooling and it is wrong here. Splitting mid-turn
produces chunks containing half of the customer's problem and half of the agent's answer,
which retrieve badly and read worse.
- The customer's opening turn is the highest-value chunk for most retrieval, because it is
the closest thing to the query someone will type. Index it separately and consider
weighting it.
- Group a customer turn with the agent's response to it. Question and answer together
is the unit that answers "how did we handle this".
- Never split a turn across chunks. Where a turn exceeds your model's window, split it on
sentence boundaries and overlap.
- Keep an unchunked summary per conversation, embedded separately, for
find-me-similar-cases retrieval. Chunk-level and conversation-level retrieval answer
different questions.
- Drop the noise before embedding: signature blocks, quoted reply chains, legal
disclaimers, and automated acknowledgements. Quoted history in particular means the same
text is embedded once per reply, which floods retrieval with near-duplicates of the same
conversation.
- State-change events are not content. Assignment and status records have no semantic value
and dilute the index.
Attach the metadata that makes retrieval usable
An embedding index over conversations with no filters is much less useful than it looks —
almost every real query is scoped.
Store alongside each vector: conversation id, source, channel, language, date, status,
resolution, product or contact-driver category, and whether the turn was internal.
Then filter before or alongside the vector search. "Similar cases resolved in the last six
months, on email, in German, excluding internal notes" is the actual query, and a pure
similarity search cannot express it.
Two metadata decisions worth making explicitly:
- Internal notes must be separable. Grounding a customer-facing bot on internal notes will
surface internal reasoning to customers. Either exclude them from the index or make the
flag mandatory in every query path.
- Language. Multilingual embedding quality varies by language, and mixing languages in one
index without a language filter produces confident cross-language matches that are
frequently wrong.
Do the PII work before embedding, not after
The decisions, in order:
- Should this conversation be in the index at all? Exclude special-category content,
anything under legal hold or an open dispute, anything outside retention, and anyone who
has objected or requested erasure.
- Redact before embedding. A vector computed from unredacted text carries that content —
and while it is not straightforwardly readable, treating an embedding as anonymous is a
mistake. Redaction also improves retrieval, since names and account numbers are noise for
semantic matching.
- Do not store the raw text in the index if you can resolve it from the source at read
time. An index holding both the vector and the full transcript is a second complete copy of
your support history with its own access model.
- Know where the embedding is computed. An external embedding API means every indexed
conversation was transmitted to that provider. Check the provider's retention and
training-use terms as configured, not as advertised.
Whether the whole arrangement is lawful — purpose, basis, transfer — is a determination for
whoever owns data protection. Raise it before building, not after.
Deletion has to reach the index
The failure that will find you later:
- Erasure and retention deletion must propagate to the index. Deleting a conversation in
the helpdesk while its vectors remain leaves it retrievable, which is a defensible thing to
be asked about and an indefensible thing to have overlooked.
- Keep the conversation id on every chunk so deletion is a targeted operation rather than
a rebuild.
- Test the deletion path — delete a conversation, then confirm its chunks are gone and it
no longer retrieves. Assert this in a test rather than assuming the delete call worked.
- Backfill and rebuild carefully. A rebuild from a source snapshot taken before a deletion
reintroduces deleted content. Rebuild from live source, or reapply the deletion list after.
Freshness and drift
- Conversations are mutable. Reopened, added to, redacted. An index built once diverges
from the source; decide the reindex trigger — updated-at based, event-driven, or periodic.
- Redaction after indexing must trigger a reindex of that conversation, or the pre-redaction
content stays retrievable. This is the mutation people forget.
- Changing the embedding model invalidates the whole index. Vectors from two models are not
comparable, so a model change is a full rebuild, not an incremental migration. Version the
index by model and plan the cutover.
Evaluate retrieval, or you will not know it is bad
An embedding index always returns something, which is why bad ones survive.
- Build a small labelled set: real queries with the conversations that should be found.
Draw the queries from what people actually search for, and from the questions agents ask.
- Measure recall at the k you actually use, per language and per channel. A single
aggregate number hides that one language is unusable.
- Include hard negatives — conversations that look similar and are not the answer.
- Re-evaluate after any change to chunking, model, or filtering. Chunking changes have
larger effects than model changes, and are usually made casually.
Present results to the user
- The PII decisions, first: exclusions, redaction before embedding, whether raw text is
stored, and where embeddings are computed — with the lawfulness question routed.
- The chunking strategy, on turn boundaries, with the noise removed and the reasoning for
the unit chosen.
- The metadata schema, and the filters every query path will use — internal-note
separation and language specifically.
- The deletion path, with the propagation test.
- The reindex triggers, including redaction-after-indexing and the model-change rebuild.
- The retrieval evaluation — labelled set, recall at your k, per language and channel.
- What is deliberately excluded from the index, and the retrieval consequence of excluding
it.
1---2name: cx-conversation-embedding-pipeline3description: Use to design a pipeline that vectorises support conversations for semantic search or retrieval, with the chunking, PII and deletion decisions made before anything is embedded. Trigger for "make our support conversations searchable", "build a vector index over tickets", "semantic search across transcripts", RAG over support data, chunking transcripts, or an embedding index that returns unhelpful matches.4---56# Vectorising support conversations78Semantic search over support history is genuinely useful: finding the precedent for an9unusual case, grounding an AI agent, or locating every conversation about a topic without10guessing keywords.1112Two things make this harder than the usual document-embedding pipeline, and both need13deciding before the first vector is written.1415**Conversations are not documents.** They are multi-party, interleaved, and the useful unit is16almost never the whole thread or the single message.1718**An embedding is a copy.** Deleting a conversation and leaving its vectors in an index means19the content is still retrievable, so erasure and retention have to reach the index too — and20an index built by an external API means the content went to that API.2122## Chunk on turn boundaries, not on character counts2324Fixed-size chunking is the default in most tooling and it is wrong here. Splitting mid-turn25produces chunks containing half of the customer's problem and half of the agent's answer,26which retrieve badly and read worse.2728- **The customer's opening turn is the highest-value chunk** for most retrieval, because it is29 the closest thing to the query someone will type. Index it separately and consider30 weighting it.31- **Group a customer turn with the agent's response** to it. Question and answer together32 is the unit that answers "how did we handle this".33- **Never split a turn across chunks.** Where a turn exceeds your model's window, split it on34 sentence boundaries and overlap.35- **Keep an unchunked summary** per conversation, embedded separately, for36 find-me-similar-cases retrieval. Chunk-level and conversation-level retrieval answer37 different questions.38- **Drop the noise before embedding**: signature blocks, quoted reply chains, legal39 disclaimers, and automated acknowledgements. Quoted history in particular means the same40 text is embedded once per reply, which floods retrieval with near-duplicates of the same41 conversation.42- **State-change events are not content.** Assignment and status records have no semantic value43 and dilute the index.4445## Attach the metadata that makes retrieval usable4647An embedding index over conversations with no filters is much less useful than it looks —48almost every real query is scoped.4950Store alongside each vector: conversation id, source, channel, language, date, status,51resolution, product or contact-driver category, and whether the turn was internal.5253Then **filter before or alongside the vector search**. "Similar cases resolved in the last six54months, on email, in German, excluding internal notes" is the actual query, and a pure55similarity search cannot express it.5657Two metadata decisions worth making explicitly:5859- **Internal notes must be separable.** Grounding a customer-facing bot on internal notes will60 surface internal reasoning to customers. Either exclude them from the index or make the61 flag mandatory in every query path.62- **Language.** Multilingual embedding quality varies by language, and mixing languages in one63 index without a language filter produces confident cross-language matches that are64 frequently wrong.6566## Do the PII work before embedding, not after6768The decisions, in order:69701. **Should this conversation be in the index at all?** Exclude special-category content,71 anything under legal hold or an open dispute, anything outside retention, and anyone who72 has objected or requested erasure.732. **Redact before embedding.** A vector computed from unredacted text carries that content —74 and while it is not straightforwardly readable, treating an embedding as anonymous is a75 mistake. Redaction also improves retrieval, since names and account numbers are noise for76 semantic matching.773. **Do not store the raw text in the index** if you can resolve it from the source at read78 time. An index holding both the vector and the full transcript is a second complete copy of79 your support history with its own access model.804. **Know where the embedding is computed.** An external embedding API means every indexed81 conversation was transmitted to that provider. Check the provider's retention and82 training-use terms **as configured**, not as advertised.8384Whether the whole arrangement is lawful — purpose, basis, transfer — is a determination for85whoever owns data protection. Raise it before building, not after.8687## Deletion has to reach the index8889The failure that will find you later:9091- **Erasure and retention deletion must propagate to the index.** Deleting a conversation in92 the helpdesk while its vectors remain leaves it retrievable, which is a defensible thing to93 be asked about and an indefensible thing to have overlooked.94- **Keep the conversation id on every chunk** so deletion is a targeted operation rather than95 a rebuild.96- **Test the deletion path** — delete a conversation, then confirm its chunks are gone and it97 no longer retrieves. Assert this in a test rather than assuming the delete call worked.98- **Backfill and rebuild carefully.** A rebuild from a source snapshot taken before a deletion99 reintroduces deleted content. Rebuild from live source, or reapply the deletion list after.100101## Freshness and drift102103- **Conversations are mutable.** Reopened, added to, redacted. An index built once diverges104 from the source; decide the reindex trigger — updated-at based, event-driven, or periodic.105- **Redaction after indexing** must trigger a reindex of that conversation, or the pre-redaction106 content stays retrievable. This is the mutation people forget.107- **Changing the embedding model invalidates the whole index.** Vectors from two models are not108 comparable, so a model change is a full rebuild, not an incremental migration. Version the109 index by model and plan the cutover.110111## Evaluate retrieval, or you will not know it is bad112113An embedding index always returns something, which is why bad ones survive.114115- **Build a small labelled set**: real queries with the conversations that should be found.116 Draw the queries from what people actually search for, and from the questions agents ask.117- **Measure recall at the k you actually use**, per language and per channel. A single118 aggregate number hides that one language is unusable.119- **Include hard negatives** — conversations that look similar and are not the answer.120- **Re-evaluate after any change** to chunking, model, or filtering. Chunking changes have121 larger effects than model changes, and are usually made casually.122123## Present results to the user1241251. **The PII decisions**, first: exclusions, redaction before embedding, whether raw text is126 stored, and where embeddings are computed — with the lawfulness question routed.1272. **The chunking strategy**, on turn boundaries, with the noise removed and the reasoning for128 the unit chosen.1293. **The metadata schema**, and the filters every query path will use — internal-note130 separation and language specifically.1314. **The deletion path**, with the propagation test.1325. **The reindex triggers**, including redaction-after-indexing and the model-change rebuild.1336. **The retrieval evaluation** — labelled set, recall at your k, per language and channel.1347. **What is deliberately excluded** from the index, and the retrieval consequence of excluding135 it.