Skip to content
Back to home

Conversational AI · ERP

An AI assistant that lets a sales team talk to their ERP

Telegram bot that gives ~50 sales technicians a natural-language interface to the company ERP. Built around Claude with tool calling: the model decides which tools to invoke against the ERP, the bot executes them, and the technician gets answers in seconds instead of navigating six screens.

AI assistant

online

what tasks do I have today?

9:24

You have 4 pending tasks:

1. Call · ACME · today

2. Visit · Riverbend · tomorrow

3. Email · García & Sons · today

4. Sample · Northwind · tomorrow

9:24

stock of rice?

9:25

RICE (RM) — Total stock: 4

Warehouse 90: 2 · Warehouse 9: 2

Other articles matching "rice" are at 0.

9:25
Live conversation, real ERP behind it (data shown is illustrative).

Context

A distribution company with an in-house ERP. Sales technicians spend the day on the road visiting clients. The ERP has a mobile app but it requires several taps to answer simple questions: what tasks do I have today, what did this client buy last month, do we have stock of this article. The brief was: let me ask in Telegram what I would otherwise have to navigate to find.

Architecture

Four layers with stable interfaces and swappable engines. Channel, orchestrator, tools, and ERP access. Anything inside a layer can be replaced without rewriting the others.

Channel adapter Telegram (live) · WhatsApp (planned) normalised MessageEvent Orchestrator LLM (Claude Sonnet 4.6, swappable) · Tool runner Memory (20 turns / 30 min) · Identity (phone → tech id) tool_call(name, args) Tools (typed catalogue) 6 read tools live · write tools planned ERP access (REST) Token manager (system & action pools) In-process caches · Error sanitisation (3 layers)

Example conversations

Field data has been replaced with invented values. The shape of the answers, the formatting rules, and the tone are the real ones.

Technician

→ what tasks do I have today?

Bot

You have 4 pending tasks:
1. Call · Customer: Northwind Distributors · Due: today
2. Visit · Customer: ACME Foods · Due: tomorrow
3. Email · Customer: Garcia & Sons · Due: today
4. Sample drop · Customer: Riverbend Markets · Due: tomorrow

Technician

→ show me the last 3 orders of ACME

Bot

1. Order 600014 · 14/04/2026 · €270.00 · Pending
2. Order 600011 · 14/04/2026 · €428.92 · Pending
3. Order 600012 · 14/04/2026 · €1,088.41 · Pending

Technician

→ stock of rice?

Bot

RICE (RM) — Total stock: 4 · Warehouse 90: 2 · Warehouse 9: 2
Other articles matching "rice" are at 0.

Tool catalogue (read)

  • find_customer

    Search a customer by name or tax ID, return the best matches.

  • get_customer

    Full detail of one customer: addresses, contacts, last activity.

  • get_agenda

    Meetings and visits scheduled for the technician on a given date.

  • get_pending_tasks

    All open CRM actions assigned to the technician.

  • get_recent_orders

    Last N orders of a customer with status and total.

  • get_article

    Article detail including stock split by warehouse.

Key technical decisions

  1. 01

    Tool calling, not text parsing

    The LLM does not invent data and does not parse intents with regex. It is given a typed catalogue of tools and decides which to call. That makes the bot debuggable: every action has a name, arguments and a result that I can log.

  2. 02

    Identity by phone number, not chat user

    The first time a technician opens the bot, Telegram shares their contact with explicit consent. The phone number is cross-referenced against the company directory to find the technician id. This means writes can be attributed to a real person, not a chat handle.

  3. 03

    Two token pools, system and action

    Read calls run under a system identity (audit log says ai_assistant). Future write calls run under a token bound to the real technician. Auditors can tell apart what the bot did on behalf of the user from what the bot did on its own behalf.

  4. 04

    Errors never reach the user raw

    Three-layer error model: raw response → typed internal error → neutral message in the chat with a short trace_id. Technicians never see SQL or stack traces. Support uses the trace_id to find the full context in the logs.

  5. 05

    Caches in memory with TTL, not a separate cache server

    Customers, articles, orders and CRM catalogues are cached in process with short TTLs and refreshed lazily. Restarts repopulate from the source of truth. No Redis dependency for the pilot. Easy to migrate later if scale needs it.

  6. 06

    Confirmation pattern for writes

    Writes are not executed straight away. The bot summarises the action, asks for explicit yes, then commits with an idempotency key. If the user repeats the request the action runs once, not twice.

Stack

  • Python 3.12
  • Claude Sonnet 4.6 (Anthropic)
  • Telegram Bot API
  • httpx (async)
  • SQLAlchemy + Alembic
  • SQLite → Postgres
  • pytest
  • Docker

Where it stands

Sales technicians
~50
Reading tools
6
Tests
36
Channels wired
Telegram (live) · WhatsApp (planned)

The read-only pilot is shipping. Next phases on the roadmap: write tools with confirmation, voice transcription, proactive reminders, WhatsApp adapter, and deployment hardening.