Multilingual Worker Support · AfifAI
Case study · AI support platform · 2025–2026

A multilingual AI help desk for foreign workers

Foreign workers employed through Israeli manpower corporations can now get answers in their own language. They follow guided scripts, ask free-text questions to a RAG assistant, or send a voice note. When the bot can't help, it opens a ticket that the Hebrew-speaking area manager reads in Hebrew, and the manager's reply reaches the worker translated back.

.NET 8 / ASP.NET CoreEF Core · SQL ServerAzure OpenAIAzure AI SearchReact · TypeScriptWhisper
Client
Israeli company serving manpower corporations and their foreign workers (confidential)
My role
Team lead & architect (hands-on)
Timeline
Aug 2025 – Feb 2026 · empty repos to dev / QA / stage / prod
Status
In production
At a glance
400 to 8LLM translation calls when 1 of 50 FAQs is edited. Unchanged content is skipped by hash.
≈1 sto save an FAQ set. Translation and re-indexing moved off the request path.
up to 10×faster bulk indexing: embeddings run 10 in parallel, then one batch upload
6worker languages, plus Hebrew for managers. Tickets are translated in both directions.
N to 2database round trips per cloned script (previously one per node)
1 to 10 MBattachment limit, and voice notes are transcribed automatically
135REST endpoints across 12 controllers for 6 roles
3 appsAPI, worker web app and admin console, built over 7 months
The problem

Workers and managers don't share a language

Manpower corporations employ foreign workers who speak Thai, Hindi, Sinhala, Russian or English. Their area managers work in Hebrew. Simple questions about a payslip, housing, insurance or a visa went by phone or messaging app. Nothing tracked them, and someone often had to translate by hand. The client wanted a self-service channel that answers the common questions in the worker's language and escalates the rest as a ticket that stays traceable in both languages.

What I owned

Architecture to production, hands-on

  • Designed the solution: a .NET 8 API, a worker web app, an admin console, SQL Server, Azure OpenAI and Azure AI Search.
  • Built the RAG answerer, the write-time translation pipeline and the voice-note transcription myself.
  • Built the conversation-script engine and its drag-and-drop tree editor, with master scripts cloned per corporation.
  • Built the ticket flow: chat transcript into a ticket, translated comments, SMS alerts to area managers, and live updates in the chat.
  • Led the team through four environments, defined the role model (6 roles) and ran the performance clean-up.
Architecture

One API with four engines, all language-aware

The diagram is wider than the screen: swipe sideways to see all of it →

Every translated entity is stored once per language, together with a hash of its source text. The read path never calls the LLM, so a Thai worker opening a script gets stored Thai text with no added latency.

How a question flows

From a tap on a script to a manager's reply

Step through one worker conversation. The content is illustrative and contains no client data. The pseudo-calls show what each stage does.

Design decisions

Translate once, answer honestly, keep the request fast

Grounded answer first, labelled fallback second

The model may answer only from the retrieved snippets, or reply with an exact __NOT_FOUND__ sentinel. Only then does a general-knowledge answer run, and it is stored with source=llm. Every answer logs its source, top score, retrievals, citations, tokens and latency. Trade-off: two LLM calls on a miss.

Translate at write time, keyed by a content hash

Scripts, nodes and FAQs are translated when an admin saves them and stored per language with a SHA-256 of the source. Items whose hash hasn't changed are skipped. Reading costs no LLM calls. Trade-off: more rows, and a few seconds before a new edit shows up in every language.

Heavy work after the response

Saving FAQs returns as soon as the database commits. Indexing and translation run in their own DI scope in the background, with capped parallelism (10 embeddings, 5 translations). A failed job writes nothing, so the next save retries it naturally because the hash still differs.

Master content, cloned per corporation

A system-level master script and FAQ set is cloned into each new corporation, translations included, and tracked by an activation table. Corporations can customise their copy. Trade-off: a sync path is needed when the master changes.

HTML-aware, filter-safe translation

Rich-text answers are translated in an HTML mode that keeps every tag. Plain titles go through a text mode that strips stray markup. If the content filter blocks a text, the original is kept and the save still succeeds.

Engineering log

What broke, and how it was fixed

Nov 2025

Semantic ranking wasn't available

The first retrieval design relied on semantic re-ranking, but the search tier in use didn't support it, so queries failed.

Switched to hybrid retrieval: a simple keyword query plus an HNSW vector query, top-8. The semantic configuration stays in the index for when the tier allows it.

Nov 2025

Every save re-translated everything

Editing one FAQ in a list of 50 re-translated all 50 into every language: 400 LLM calls for a one-line change.

Added a SourceHash per entity and language. Unchanged items are skipped, so the same edit now costs 8 calls.

Dec 2025

Saving FAQs blocked on the LLM

The save request waited for every translation and embedding to finish, and the admin UI hung while it did.

Batched the database upsert into one call, then moved indexing and translation into scoped background tasks with semaphores. The response returns in about a second.

Dec 2025

The model rejected a parameter

After a deployment change, chat calls failed on an explicit temperature setting that the model no longer accepted.

Removed per-call sampling settings and made the deployment name the only model switch. Translation consistency now comes from strict prompts.

Jan 2026

Times were off by hours

Chat and ticket times showed in the wrong time zone. EF Core returned DateTimeKind.Unspecified, and an earlier switch to local time made it worse.

The server stores UTC and marks it explicitly in DTOs. The client parser accepts strings without a Z and formats them per locale.

Jan 2026

Cloning a script tree was slow and fragile

Onboarding a corporation copied the master script one node at a time, and parent links had to point at IDs that didn't exist yet.

Rewrote the copy in two passes: insert all nodes in one batch, build an old-to-new ID map, then fix parents in a second batch. That is 2 round trips per script, with translations copied too.

Jan 2026

Workers who can't type

Some workers send voice notes instead of text. The manager saw an audio file with no context.

Added recording in the web app and Whisper transcription on the server. The ticket view interleaves each audio file with its transcript at the right point in the conversation.

Outcome

Where it landed

  • Workers ask in their own language. Answers come from the corporation's own FAQs and scripts, and an honest fallback runs when nothing matches.
  • Each escalation becomes a ticket with the full chat transcript. Managers read it in Hebrew and reply in Hebrew, and the worker gets the reply in their language. Manual translation per ticket went from ≈5 minutes to none.
  • Area managers get an SMS for new tickets and follow-ups. Workers see replies in the chat within about 5 seconds, because the chat polls for new comments only.
  • Content edits re-translate only what changed (400 → 8 calls in the 50-FAQ case). Saves return in about a second.
  • New corporations start with a ready, translated master script and FAQ set instead of an empty account.
  • Admin dashboards show tickets by subject, by employee and by hour and weekday, plus headcount per corporation. Employees are imported from Excel or CSV with row-level validation.
Stack

Technology stack

Backend
.NET 8, ASP.NET Core, EF Core, SQL Server, AutoMapper, NLog
AI
Azure OpenAI (gpt-4o-mini, embeddings, Whisper), Azure AI Search hybrid retrieval
Front ends
React 18, TypeScript, Redux Toolkit, Vite, Ant Design, i18next, dnd-kit, Quill, Chart.js
Integrations
AWS S3 file storage, SMS gateway, JWT + OTP sign-in
Delivery
Docker images, dev / QA / stage / prod branches, SQL compare scripts
Languages
Hebrew (RTL), English, Russian, Thai, Hindi, Sinhala; Arabic in progress

Free scoping call · 30 minutes · No commitment

Bring one process. Leave with a plan and a number to beat.

Write a few lines: the process or system, what hurts today (time, errors, cost or an AI pilot that stalled) and who uses it. After a short call you get a written plan and a fixed quote within ≈3 days.

Get a free scoping callafif.mansour@gmail.com

Opens a pre-filled email · reply within one business day

The model reads. The code counts.

All projects