A privacy-first agentic AI system that queries IoT irrigation sensor data through natural language — powered by a local LLM (Ollama + qwen2.5:7b) and LangChain 1.x. Zero cloud calls, zero API keys, zero data leakage.
Modern LLM applications typically send every user query to a cloud API (OpenAI, Anthropic, Google), which is a non-starter for privacy-sensitive fields like agriculture, health, and industrial IoT. This project shows how to build a fully local agentic AI system for a real-world use case:
- A LangChain 1.x agent runs on a local llama-class LLM (qwen2.5:7b via Ollama) — no cloud, no API keys.
- The agent uses three custom tools to query irrigation sensor data (soil humidity, air temperature, air humidity, pump state).
- Users interact through a Streamlit web interface — dashboard on the right, natural-language chat on the left.
- The agent handles multilingual input (English or French), multi-tool orchestration, and honest failure (says "I can't" instead of hallucinating).
Streamlit interface: sensor dashboard on the right, natural-language agent on the left. Everything runs on the user's machine.
Example interaction: the agent orchestrates multiple tools to answer a comparative question in French, even when asked in English.
Agriculture, healthcare, and industrial IoT increasingly need conversational AI on top of their sensor data — but sending every query to a cloud LLM raises three practical problems:
- Privacy & compliance. Farm data, patient telemetry, and industrial signals often can't leave the site legally or contractually.
- Latency. Real-time control loops don't tolerate a 500 ms cloud round-trip.
- Cost. Per-token API pricing doesn't scale to always-on monitoring.
Local LLMs solve all three at once — and open-source models like qwen2.5:7b are now good enough for structured tool-calling on a laptop CPU.
This project is a concrete, reproducible proof of concept: a working agent, a working UI, a working data pipeline, all running on a commodity machine with no cloud dependencies.
┌────────────────────────────────────┐ ┌────────────────────┐
│ User (Streamlit web interface) │────────▶│ LangChain agent │
│ "How often was the pump active?" │ │ (create_agent) │
└────────────────────────────────────┘ └─────────┬──────────┘
│
│ tool selection
▼
┌────────────────────────────────────┐
│ Local LLM (qwen2.5:7b via Ollama) │
│ runs on user's CPU │
└─────────────────┬──────────────────┘
│ chooses tool + args
▼
┌────────────────────────────────────────────┐
│ Three custom tools (Python + @tool) │
│ ├── get_derniere_mesure │
│ ├── get_stats_periode │
│ └── conseil_arrosage │
└─────────────────┬──────────────────────────┘
│ reads
▼
┌───────────────────────┐
│ donnees_irrigation │
│ .csv (sensor data) │
└───────────────────────┘
Key design choices:
- Digital twin, not real hardware yet. The CSV is generated by
generer_donnees.py— a physically plausible simulator of 7 days of sensor readings (temperature follows a sinusoidal day/night cycle, soil humidity drops during the day, the pump activates when humidity drops below 30% and stops above 50%). - The LLM never writes to actuators. It only reads sensor data and recommends. Any real-world irrigation pump would remain under human control.
- Prompt engineering matters. The system prompt explicitly routes questions with words like "often", "average", "compare" to
get_stats_periode, avoiding the classic pitfall where the LLM only calls the first plausible tool.
The agent handles three families of questions, in French or English:
| Category | Example question | Tool called |
|---|---|---|
| Current state | "What is the latest sensor measurement?" | get_derniere_mesure |
| Historical / statistical | "How often has the pump been activated this week?" | get_stats_periode |
| Actionable advice | "Do I need to water the plants now?" | conseil_arrosage |
| Comparative (multi-tool) | "Compare today's humidity to the weekly average." | Both get_derniere_mesure and get_stats_periode |
| Out-of-scope | "What's the weather forecast for tomorrow?" | None — the agent honestly declines and suggests a weather app. |
The soil humidity curve makes the simulator's control logic visible: humidity drops steadily during the day, the pump kicks in when it hits the 30% threshold (red dashed line), and humidity jumps back up. The temperature curve shows a clean sinusoidal day/night cycle around ~22°C. Over 7 days, the pump activated 10 times — enough signal for the agent to answer historical questions meaningfully.
- Agentic AI — multi-tool routing, multi-step reasoning, orchestration
- LangChain 1.x — the modern
create_agentAPI (LangGraph-based), not the deprecatedcreate_react_agent - Local LLM deployment — Ollama + qwen2.5:7b, ~4.7 GB, runs on CPU
- NLP / LLM-based reasoning — natural-language question understanding, multilingual (EN input → FR output)
- Prompt engineering — explicit routing rules, hallucination avoidance
- Tool design — three
@tool-decorated Python functions with clear docstrings the LLM can rely on - Data simulation — realistic physics-inspired sensor time series (
generer_donnees.py) - Web UI (Streamlit) — dashboard, KPI cards, matplotlib charts, chat history
- Edge AI mindset — zero cloud dependency, privacy-first architecture
Local_LLM_Agent_Irrigation/
├── README.md
├── LICENSE # MIT
├── requirements.txt
├── .gitignore
├── app.py # Streamlit web interface (entry point)
│
├── src/
│ ├── agent.py # LangChain agent (terminal version)
│ ├── outils.py # the three @tool-decorated functions
│ └── generer_donnees.py # sensor data simulator (digital twin)
│
├── data/
│ └── donnees_irrigation.csv # 7 days of simulated sensor readings
│
└── figures/
├── screenshot_dashboard.png
├── screenshot_conversation.png
└── screenshot_graphs.png
# Install Ollama from https://ollama.com/download
ollama pull qwen2.5:7bqwen2.5:7b is ~4.7 GB. It runs on CPU on any reasonably modern laptop; a GPU speeds inference up considerably but is not required.
git clone https://github.com/HaneneRBH/Local_LLM_Agent_Irrigation.git
cd Local_LLM_Agent_Irrigation
pip install -r requirements.txtpython src/generer_donnees.pyThis regenerates data/donnees_irrigation.csv — 336 half-hourly readings over 7 days. A version is already committed so you can skip this step.
python -m streamlit run app.pyYour browser opens at http://localhost:8501. The first question takes ~1-2 min (the LLM loads into memory); subsequent questions are faster.
python src/agent.pyRuns six example questions in sequence and prints the agent's answers. Useful for a quick sanity check without the web UI.
- Small local model on purpose. qwen2.5:7b was chosen after benchmarking against llama3.1:8b on this specific task — qwen2.5 is markedly more reliable at strict tool-calling in French, which is what matters here. Bigger models (Mixtral, Qwen 72B) would work too but defeat the "runs on any laptop" purpose.
- CSV, not a database. For a portfolio project the goal is transparency, not scalability. Moving to PostgreSQL is a natural v2 step (see roadmap).
- French system prompt with explicit routing rules. Small LLMs don't reason as fluently about which tool to call as GPT-4 does. Explicit keyword-to-tool mappings ("compare", "often", "average" → stats tool) makes the agent's behavior predictable.
- The agent never writes to actuators. Recommending is the whole point; commanding would require a completely different safety story.
Concrete extensions, in order of impact:
- RAG on historical data. Move from "read whole CSV" to vector search over months of readings (ChromaDB + LangChain retrievers). Would let the agent answer questions like "when was the last drought episode?" with real specificity.
- PostgreSQL + SQL agent. Replace the CSV with a real database and give the agent a text-to-SQL tool.
- FastAPI wrapper. Expose the agent as a REST endpoint so it can be called from mobile apps, embedded systems, or other services.
- Docker Compose stack. Package Ollama + agent + Streamlit + Postgres in one
docker-compose up. - Real IoT integration. Connect an ESP32 running the sensor firmware, streaming live data via MQTT into the agent's data source.
- Raspberry Pi deployment. Move the whole stack (LLM included) to a Raspberry Pi 5 for a true edge-AI appliance.
Hanene Rouabeh — PhD in Computer Systems Engineering 15 years of experience across embedded systems (STM32, FPGA/VHDL, PCB design), signal and image processing, control systems, and applied machine learning (CNN, Transformer, ESN) with PyTorch. Six international journal publications, one patent, and a strong preference for reproducible, well-documented research code.
- GitHub: @HaneneRBH
- Email: hanene.rouabeh@gmail.com
- Companion repo: Lora_rffi_robuste_pytorch — a PyTorch CNN2D + Transformer for channel-robust RF fingerprint identification
Released under the MIT License. Do whatever you want with it, just keep the copyright notice.


