Industrial AI, robotics conversion and adoption, Industry 4.0 automation and test automation, and the software those systems run inside: enterprise systems (ERP, HRMS, CRM, inventory and production management), web applications, AI agents and automation, and native mobile apps for iOS and Android.
A focused first release typically ships in 8–12 weeks. Multi-module enterprise platforms run 4–9 months, delivered module by module so you are using working software long before the final release rather than waiting on a single big-bang launch.
Both. A well-defined scope gets a fixed price against a signed specification. Longer or evolving builds run as a dedicated team on a monthly retainer. Either way you get a written estimate with every assumption listed before a contract exists, so you can see exactly what moves the number.
Discovery and scoping, then UI/UX design, then two-week build sprints that each end in a working demo. You get a staging environment from the first week, and QA, security review and deployment run inside the sprint instead of being bolted on at the end.
You do, in full, on final payment. Source code, design files, infrastructure and documentation are handed over in your own repositories and cloud accounts. We keep no licence over your product and there is no lock-in to our hosting.
Yes. We connect to MES and the equipment already on the line, accounting, HR and CRM platforms, payment gateways, WhatsApp Business, and legacy in-house databases. Where no API exists we build a sync layer and migrate historical records with reconciliation reports your finance team can check line by line.
Every project closes with a handover: documentation, team training, and a warranty period covering defects at no cost. After that you can run support in-house or keep us on a monthly plan covering monitoring, security patches, and an agreed block of enhancement hours.
We sign your NDA before the first technical conversation. Builds ship with role-based access, encryption in transit and at rest, and audit logging as standard. Production data stays inside your infrastructure and access is limited to the engineers assigned to your project.
Start a project
Didn’t find what you were looking for?
Tell us what you are building. You get scope, a timeline and a realistic cost — not a brochure.
Tools
The stack we build on
We design, build, and evaluate with a modern AI stack—LLMs, vector search, orchestration, and observability—so your features are fast, reliable, and secure.
A model can answer from your documents. Whether it answers correctly is decided before it reads a word.
Retrieval-augmented generation — RAG — means searching your own material for the passages that bear on a question, then asking a language model to answer from those passages and cite them. The generation half is the part that demonstrates well. The retrieval half is the part that decides whether the answer is right, because a model can only work with what is put in front of it. This is the whole journey, stage by stage, in plain terms.
A question typed into a box travels through six pieces of machinery before anything is written back. Five of them happen before the language model is involved at all, and those five are where accuracy is won or lost.
Getting your documents into a form a machine can read, without losing the fact that they had a shape. A contract is not a stream of words: it has clause numbers, a schedule at the back, a table of rates in the middle and a footer on every page. Layout-aware ingestion keeps that structure — it reads a two-column page in the right order, keeps a table as a table rather than a run of numbers, and knows that a heading governs the paragraphs beneath it. Where pages are scans rather than text, OCR — optical character recognition, the step that turns a picture of words into words — runs first, and its output is checked rather than trusted. The cost is that this stage is slow and unglamorous, and it is usually the largest share of the build.
The rate table dissolves into a line of digits with no column headings attached, page footers get quoted back as though they were clauses, and every scanned document in the archive is silently invisible to the system.
02
Chunking, semantic
Semantic chunking on structural boundaries, with overlap and inherited headings
Cutting each document into passages small enough to search and hand to a model. The naive method cuts every five hundred words and moves on. Semantic chunking cuts on the boundaries the document itself already has — a clause, a section, a procedure step, a row group — so that a retrieved passage is a complete unit of meaning. Each chunk carries the headings above it, so a passage that says 'this does not apply' still knows which rule it is qualifying, and consecutive chunks overlap slightly so a sentence spanning the cut is not lost. The cost is that every document family needs its own rules: contracts do not chunk like maintenance manuals, and manuals do not chunk like email.
A rule gets cut in half. The system retrieves the half that states the obligation and never sees the half that lists the exemptions, and the answer is confidently, precisely wrong.
03
Embedding
Embedding model, vector representation, similarity in vector space
Turning each passage into a list of numbers — a vector, also called an embedding — that stands for its meaning. Passages about the same thing end up with similar numbers even when they share no words, which is what lets a search for 'can we get out of this early' find a clause headed 'termination for convenience'. That is the whole trick, and it is genuinely useful. It is also the source of the technique's main weakness: meaning-similarity is approximate by design, so it is good at paraphrase and poor at exactness. The numbers for part 4471-B and part 4471-D are near-identical, because the meanings are near-identical, and to your engineer they are two different parts.
Search only finds passages that reuse the questioner's words, so anyone who asks in their own vocabulary rather than the document's gets nothing back and concludes the system is empty.
04
Hybrid search
Dense vector search plus lexical keyword search (BM25), fused into one ranked candidate set
Running two searches over the same question and merging the results. The vector search finds passages that mean something similar. The keyword search — the older, exact kind, matching the literal string — finds passages containing the precise token: the part number, the site code, the policy reference, the surname. Each one fails where the other is strong, so the sensible answer is to run both and fuse the two ranked lists into one candidate set, typically a few dozen passages. The cost is a second index to build, keep in step and pay for.
You get a system that is articulate about themes and useless about specifics — it will discuss your warranty policy at length and fail to find the one clause that names the model number in the question.
05
Reranking
Cross-encoder rerank over the candidate set; top-k selection against a relevance floor
Reading the shortlist properly before choosing what to use. The first search is fast and shallow: it compares the question and each passage separately, which is why it can rank a near-miss above the right answer. A reranker is a second, slower model that looks at the question and one passage together and scores how well that passage actually answers this question. It reorders the candidates and keeps the best few — the top-k, k being simply how many passages you pass on, usually three to eight. It also enforces a floor: if nothing scores above the threshold, the honest output is that the answer is not in the corpus. The cost is latency and a second model call on every query.
The model is handed the five passages that were merely nearest, including two about a different site and one from a superseded version, and it writes a fluent answer out of the wrong material.
06
Generation with citation
Grounded generation constrained to the retrieved passages, with span-level source attribution
Only now does the language model write. It is given the question, the handful of passages that survived reranking, and an instruction to answer from those and nothing else — and to say so when they do not contain the answer. Every claim in the reply carries a citation back to the document, page and clause it came from, which is what turns an answer into something a person can check in ten seconds rather than take on faith. Grounding reduces invention; it does not eliminate it, which is why the citation matters more than the fluency. The cost here is real but modest, and it is the smallest engineering problem on this page.
An answer with no source is an assertion. Nobody can verify it, so either it gets trusted when it should not be or it gets checked in full — and a system that has to be checked in full has saved nobody any time.
Worked cases
Two corpora, two questions, followed all the way through.
The stages above, running on real material. Note in both cases how much of the work happens before the model is asked anything.
A lease portfolio nobody has read end to end since it was signed
What arrivesA question from an asset manager: can we exit unit 4 early, and at what cost
What leavesAn answer with the break clause quoted, and the lease, page and clause number beside it
01IngestFour hundred leases, most of them scanned PDFs of signed originals with initials in the margins. OCR reads them; layout-aware parsing keeps the schedules at the back attached to the clauses at the front that refer to them, and flags the twelve documents whose scan quality is too poor to trust to a person rather than passing them through.
02ChunkCut on clause boundaries, not on word counts, so a break clause arrives whole — conditions, notice period and penalty in one passage rather than spread across three. Each chunk inherits its lease reference and section heading, so a passage always knows which building it belongs to.
03SearchThe question says 'exit early'; the document says 'determination'. Vector search bridges that gap. The keyword half of the hybrid search simultaneously pins the result to unit 4 rather than to the nineteen other units with similar clauses.
04RerankThe shortlist contains the right clause, a superseded amendment to it, and a near-identical clause from the adjoining unit. The reranker reads all three against the question and keeps two: the clause and the amendment that modifies it.
05AnswerThe reply states the notice period and the penalty, quotes both passages, and names the lease, page and clause. The asset manager verifies it against the source in under a minute, which is the point — the system's job is to end the search, not to end the judgement.
Twenty years of service manuals, and an engineer standing in front of the machine
What arrivesA fault code and a serial number, typed on a phone from the factory floor
What leavesThe procedure for that machine's revision, with the manual and page it came from
01IngestManuals across four revisions of the same equipment family, full of exploded diagrams, torque tables and part numbers that differ by one character. Tables are preserved as tables; the revision and issue date of every manual is captured as metadata on every passage it produces.
02ChunkCut on procedure steps, so a passage is a complete instruction with its prerequisites and its warning, never step four of nine on its own. Torque tables stay intact rather than being sliced mid-column.
03SearchThe keyword half of the search matches the fault code and the part numbers exactly — this is precisely where embeddings alone fail, because 4471-B and 4471-D mean almost the same thing to a vector and different things to a fitter. The vector half handles the engineer's description of the symptom, which uses none of the manual's vocabulary.
04Filter and rerankThe serial number resolves to a revision, and passages from the other three revisions are filtered out before ranking rather than argued with afterwards. The reranker then orders what remains by how directly it answers this fault.
05AnswerThe procedure comes back with its warnings intact and the manual, revision and page cited. Where the fault code appears in no manual for that revision, the reply says so and offers the nearest documented fault rather than improvising a repair.
Where it breaks
The demo works on ten documents. The failure starts at ten thousand.
Naive RAG — cut everything into equal blocks, embed them, take the nearest five, ask the model — can be assembled in an afternoon and will look convincing. What follows is what happens when it meets a corpus that was written by people over twenty years.
The first failure is the cut. Fixed-length chunking has no idea what a document is; it counts words and cuts. Sooner or later it cuts through the middle of a rule, and the two halves go into the index as unrelated passages. The half that says the charge applies is retrieved. The half beginning 'this clause shall not apply where' is four hundred words downstream and is never seen. Nothing in the system registers that anything is missing, because from the machine's point of view nothing is: it found a relevant passage and it used it. The answer is specific, well-written and the opposite of the truth.
The second failure is exactness. Embeddings encode meaning, and meaning is exactly the wrong lens for an identifier. Part 4471-B and part 4471-D are, semantically, the same thing — same family, same function, one character apart — so their vectors sit almost on top of each other. So do two site codes, two policy references, two employees with the same surname. A meaning-only search will happily return the wrong one and rank it first. This is not a tuning problem to be solved with a better embedding model; it is what embeddings are. The fix is to run a keyword search alongside them, which is the whole reason hybrid search exists.
The third failure is the top-k. Ask for the five nearest passages and you will always get five passages, because 'nearest' is a relative measure and something is always nearest. There is no natural point at which a vector search returns nothing. So a question whose answer is genuinely not in your corpus produces five near-misses: the right topic, the wrong site; the right clause, the superseded version; the right procedure for a different revision. Without a reranker to score how well each passage actually answers this question, and a relevance floor beneath which the system declines to answer, those five near-misses go forward as though they were evidence.
The fourth failure is the model, and it is the one people find hardest to accept. Handed five irrelevant passages and a question, a language model will not refuse. It will write a coherent, plausible, well-structured answer from the material it was given, in the same confident register it uses when it is right. There is no visible difference between a grounded answer and a badly-grounded one unless the citations are there for a human to check. This is why the generation stage is the last place to spend your effort: it is doing exactly what it was asked to do, with whatever it was handed.
There is also the case where none of this is the right tool at all. If the question is 'how many leases expire in March', that is a query against structured data, not a search over prose — retrieval will return three lease documents that mention March and count nothing. Aggregation, arithmetic over a whole corpus, anything that needs every matching record rather than the most relevant few: these belong in a database, and a well-built system routes them there. Retrieval answers questions whose answer is written down somewhere. It does not answer questions whose answer has to be computed across everything.
The model is not the part that fails. It writes the same confident answer from the right five passages and the wrong five, which is why everything upstream of it is the actual engineering.
How to tell them apart
A retrieval layer that holds up. A demo that will not survive your corpus.
Both look the same in a fifteen-minute presentation. These are the questions that separate them, and they are worth asking of anyone selling you one — including us.
A retrieval layer that holds up
Chunks on the document's own boundaries — clause, section, procedure step — and can show you what a retrieved passage looks like before the model ever sees it
Runs hybrid search, so part numbers, policy references and site codes are matched exactly rather than approximately
Reranks the shortlist and enforces a relevance floor, so 'I don't know' is a real output and not a thing the system is incapable of saying
Cites document, page and clause on every claim, which makes verification a ten-second job instead of an act of faith
Is measured against a fixed set of real questions with known answers, so a change to chunking or the embedding model can be shown to have helped rather than assumed to have
A demo that will not survive your corpus
Cuts every document into equal blocks regardless of what the document is, and has never been shown a scanned page
Searches only by meaning, and has no answer to what happens when two part numbers differ by one character
Always returns k passages, always answers, and has no threshold below which it declines — so it has never once said it does not know
Answers without citations, or cites the document but not the place in it, which is the same as not citing it
Was evaluated by someone asking it questions they already knew it could answer, with no held-out set and no record of what it got wrong
Asked often
The questions that come up once your own documents are involved.
Do our documents leave our tenancy?
That is a decision you make, not a property of the technique. The pipeline can run entirely inside your own cloud tenancy or on your own hardware, with an open-weights model doing the generation, in which case nothing crosses the boundary. Where a hosted model is used, only the retrieved passages and the question travel — not the corpus — and the terms under which that provider retains or trains on data are a contract to read rather than a detail to assume. The honest trade is that fully self-hosted costs more and is usually a little behind on model quality; for regulated material that is often the right price to pay.
What about PDFs that are scans rather than text?
They need OCR — optical character recognition — before anything else can happen, and OCR quality varies enormously with the source. A clean scan of a typed page is close to solved. A twenty-year-old fax of a signed contract with handwritten amendments in the margin is not, and no honest supplier will tell you otherwise. What a well-built pipeline does is measure confidence per page and route the poor pages to a person instead of quietly indexing rubbish. Budget for that: on an old archive, ingestion is routinely the largest single line in the build.
How do we keep it current?
Re-indexing, and it should be triggered by the systems where documents actually change rather than by someone remembering. When a document is superseded, the old version's passages come out of the index or get marked superseded so they can still be cited historically without being offered as current — this matters more than it sounds, because a confidently-cited obsolete policy is worse than no answer. The recurring cost is small but it is not zero: an index that nobody owns drifts out of date within a quarter, and nothing about the system's behaviour will tell you that it has.
What happens when it doesn't know?
It should say so, and that behaviour has to be built deliberately, because the default is the opposite. The mechanism is the relevance floor at the reranking stage: if no passage scores above the threshold, the system returns that it has nothing rather than passing weak material to the model. In practice this is the single feature that decides whether people come to trust the thing. A system that answers everything gets checked every time, which saves nobody anything; a system that declines a small share of questions gets believed on the rest.
Do we need a vector database?
You need somewhere to store embeddings and search them quickly, which several things you may already run can do — Postgres with the pgvector extension, or the search engine already indexing your content. A dedicated vector database earns its place at large scale, with heavy filtering by metadata, or where query volume is high. At a few hundred thousand chunks it is usually a preference rather than a requirement. It is also the least consequential decision on this page: the choice of store has almost never been the reason a retrieval layer worked or failed, and the chunking has almost always been.
In practice
Judge it on what reaches the model. Not on what the model writes.
If you are evaluating a retrieval system — ours or anyone's — ask to see the passages it retrieved before you read the answer it wrote. The answer will read well either way. The passages will tell you whether the machine found the right rule, the whole rule, and the current version of it, which is the only thing that determines whether the answer was true.
The work that makes that happen is unglamorous: reading your documents properly, cutting them where they were meant to be cut, searching two ways at once, and building the willingness to say nothing when there is nothing to say. It is also most of the project. If you have a corpus and a question you want answered from it, that is a conversation worth having before anything gets built.