We build applications that read, classify, search and generate text, using language models where they help and simpler methods where they do not, with evaluation and data handling designed in from day one.
What we build
- Retrieval-augmented question answering. Assistants that answer from your documents, with citations back to the source passages, access control that mirrors your existing permissions, and a plan for stale content.
- Information extraction. Turning contracts, forms, emails and tickets into structured fields, validated against schemas so downstream systems get predictable data.
- Classification and routing. Tagging, triage and prioritization of text, using a small fine-tuned or classical model where it is cheaper and more predictable than a large one.
- Summarization and drafting tools that keep a person in the loop and make it clear what came from where.
- Tool-using agents for bounded workflows, with explicit permissions, step limits and logs of what the system did.
- Semantic search using embeddings, combined with keyword search where exact matches matter.
We work with hosted model APIs and with models you run yourself, and we keep the model behind an interface so it can be swapped as the options change.
How an engagement runs
- Define the task and the failure modes. What should the system do, what must it never do, and who reviews its output?
- Collect a test set first. Real or realistic examples with expected outcomes, written before the prompts. This becomes the evaluation set for the whole project, and the basis for the practices described under LLMOps.
- Build the simplest pipeline. Often retrieval plus a prompt, or a classical classifier, so there is a measured baseline.
- Improve against the evaluation. Chunking, retrieval, prompts, model choice and guardrails are changed one at a time and scored, with the changes recorded.
- Harden. Input validation, output checks, rate limits, timeouts, fallbacks, and handling of prompt injection from untrusted documents.
- Ship with tracing. Deployment with structured logging so behavior in production can be inspected, and a feedback path for corrections.
Privacy and data handling
Language model features move text around: prompts, retrieved passages, completions and traces. That text often includes personal data. We map where it flows, minimize what is sent to any model provider, set retention on logs, and design deletion so a request can actually be honored. Useful background lives in our guides on privacy in LLM applications, data mapping and discovery and data subject requests, and in these articles on personal data in prompts and traces, LLM vendor agreements and sub-processors, and how the EU AI Act and GDPR overlap. They are general information, not legal advice; your counsel should review your own obligations.
Typical stack
- Python and Node for application code; FastAPI or similar for services.
- A vector index such as pgvector or a managed vector database, alongside your existing search where useful.
- Hosted LLM APIs or self-hosted open-weight models served with an inference server, chosen to match your data residency needs.
- Pydantic or JSON Schema validation for structured outputs.
- Docker, Kubernetes or a serverless platform for deployment; GitHub Actions for tests and evaluation runs.
Deliverables
- Application code and infrastructure definitions in your repositories.
- An evaluation set, a scoring script, and a report showing results for each major change.
- Documentation of data flows, model providers used and retention settings.
- Dashboards or queries for tracing and cost, plus a runbook for operating the system.
Choosing between approaches
Text problems can be solved in several ways, and the right choice depends on volume, risk and how much the inputs vary. Rules and regular expressions are cheap, explainable and fast, and they work when the patterns are stable. Classical machine learning on text features, or a small fine-tuned model, suits high-volume classification where behavior must be predictable. Large language models handle varied, open-ended input and generation, at higher cost and with less predictable output, so they need evaluation and guardrails. Many real systems combine them: rules for the clear cases, a small model for routing and an LLM for the hard remainder.
We compare these options on your own examples early, before committing to an architecture. That comparison is usually quick and prevents the common mistake of building an elaborate LLM pipeline for a task a classifier handles better.
Frequently asked questions
Do we need an LLM for this?
Not always. If a rules-based or classical approach meets the need, we will say so. Language models earn their place on open-ended text, not on every task.
Can the system run without sending data to a third party?
Often yes, using models you host. There is a trade-off in capability, cost and operations, and we will lay it out for your case.
How do you handle wrong or invented answers?
By grounding answers in retrieved sources, requiring citations, testing against an evaluation set, and designing the interface so users can see and check the source. No design removes the risk completely.
Which model should we use?
That depends on quality needs, latency, cost and data constraints, and it changes quickly. We test candidates on your own evaluation set rather than relying on public rankings.
Request a quote
Describe the text task, the documents involved and any data constraints. We will propose a scope.
See all services.