🌾 Inspiration Every farming season, the same story repeats across rural Zambia and most of smallholder Africa: a farmer watches yellow patches spread and curl across her maize leaves. The knowledge that could save her harvest exists — but it's written in English, published online, behind connectivity her village doesn't have. The nearest extension officer serves over 1,000 farmers. Her question is in Lozi, or Chewa, or Hausa. The answer is in English, on the internet.

UlimaAI exists for that farmer. "Ulima" is Swahili for farming. The goal is simple to state and hard to build: expert agricultural advice, in the farmer's own language, running completely offline on a cheap laptop — because the village may not have internet, but the school, the cooperative office, and the extension worker's bag increasingly have computers.

🤖 What it does UlimaAI is an offline agricultural advisor that speaks 21 African languages — from Swahili and Hausa to Lozi, Chewa and Zulu. A farmer types a question in their own language; UlimaAI detects it automatically, retrieves relevant local knowledge, and answers with specific, local, actionable advice: real varieties (SAMMAZ 40, OBA SUPER, Roma VF), real rates (Urea 50 kg/ha, not "apply some fertilizer"), real planting windows. It runs on CPU in ~2 GB of RAM — a $150 laptop with no internet, no API fees, no accounts.

🛠️ How I built it Model: Qwen2-1.5B-Instruct, GGUF Q4_K_M (~935 MB) on llama.cpp — the only runtime the ADTC evaluation accepts. The 1.5B size is deliberate: under the 8 GB laptop profile, 935 MB weights+4096-token context+OS leaves GBs of headroom.

The 21-language layer is pure Python, because offline means no API translation: weighted indicator-word detection (936 unique indicators, script detection for Arabic/Amharic, language-family disambiguation), native-language system prompts for 13 languages, and a Lozi↔English translation bridge — because a 1.5B model simply doesn't speak Silozi, but the pipeline can.

RAG without embeddings: a TF-IDF engine over 421 knowledge chunks from 29 local agricultural guides. No embedding model (RAM), no vector DB — but with a heading-title boost and a multilingual synonym map, a Hausa question like "Ta yaya zan kula da masara?" retrieves the right maize guide. The corpus is the grounding: the model answers from local agronomy, not from memory alone.

Measured, not guessed: every tuning claim comes from llama-bench runs — thread auto-detection, a native AVX512 build ( 44→107 t/s prompt processing, +142%), and rejected alternatives (below).

🧗 Challenges

  1. The bandwidth wall. I benchmarked flash-attention, q8_0 KV-cache, and re-quantization to Q4_0 and Q3_K_M — all flat. CPU generation is memory-bandwidth-bound: tok/s≈ model size RAM bandwidth ​

, and a 935 MB model at ~10.5 t/s already saturates this machine's ~9.4 GB/s. The clincher: Q4_0 is 5% smaller yet slower — proof it was never a compute problem. I documented it and moved on.

  1. A 1.5B model that hallucinates convincingly. Early answers invented chemistry: "DAP (Dihydroxyacetone phosphate)" — real acronym, wrong expansion — and even a "caesium deficiency." Real-looking, confident, wrong; dangerous for a farmer. The fix was a grounding rule in the prompt pipeline: use chemical and fertilizer names exactly as stated in the retrieved information — never expand or guess acronyms. Strict-rubric response score: 45 → 65.

  2. Swahili prompt fragility. Longer, more "helpful" system prompts made the model parrot the instructions back instead of answering. I iterated the Swahili prompt three times, watched each version fail differently, and learned that with small models, prompt engineering is often subtraction. The final template asks the question first and keeps instructions minimal.

  3. Bugs that hide in plain sight. The chat tool crashed on --rag (an import renamed in the rebrand). The word "in" was listed as a Hausa indicator — so a pure-English question about Nigeria got answered in Hausa. A hardcoded -t 4 was oversubscribing 2-vCPU hardware, silently costing 13% of throughput. None of these fail loudly; all of them fail users.

  4. Dead infrastructure. The model download URL was unreachable from half the networks we tested (fixed with a ModelScope mirror fallback), and a git submodule pointed at a deleted repository — which would have broken a judge's fresh clone on submission day.

📚 What I learned Constraints forced a better architecture. "No embeddings model, no internet" produced a TF-IDF RAG layer that's multilingual, instant, and costs ~0 RAM — better for this hardware than the fancy version. Benchmark before believing — including yourself. Our own score artifact claimed perfect throughput from a fast machine; honest re-measurement said ~70% of reference. I rebuilt every number from live runs. The submission now only contains numbers I can reproduce. The model is a distribution, not a number. Same prompt, same config, wildly different outputs. Quality at this scale is something you engineer and verify (grounding, routing, pinned samples, 72 tests), not something you assume.

🏆 Accomplishments & what's next Proud of: 21 languages with 55/55 detection tests on real farmer queries; +142% prompt processing (first-token latency halved); A/B-verified response quality gains (maize diagnosis 60→89, dry-season tomatoes 77→95, LLM-judged against the old pipeline with the real model) — all on hardware weaker than the target laptop.

Next: LoRA fine-tuning on our 583-entry multilingual Q&A set, and the voice interface that completes the tagline — offline speech-to-text and text-to-speech, because "Your Voice" should be literal.

Built With

Share this project:

Updates

Submission history