Agri & Rural Voice Agents

View as Markdown

Farmer-advisory bots (crop guidance, mandi/market prices, weather alerts, government scheme status) serve users who are frequently on 2G/3G connections, basic phones, and regional-language-only speech. Voice is often the only accessible interface here, not a convenience layer on top of an app. This vertical shares a lot with Government Services, the same accessibility bar and the same need to avoid stating unverified facts as certain, but adds connectivity constraints and domain content (crops, weather, mandi prices) that the other guides don’t cover.

There’s no dedicated example agent for this vertical yet in the cookbook, so this guide includes a full one, built on the same pattern as the Government Scheme Agent, which already covers PM-Kisan and other farmer-relevant schemes.

In short: design for a bad connection first, ground every price/weather answer with a live tool call instead of the model’s memory, and reuse the Government Services pattern for scheme questions instead of duplicating it.

Architecture

Farmer Audio (often PSTN/2G) → STT (streaming, 8kHz-tolerant) → LLM (simple language, tool-grounded facts) → TTS (streaming, mulaw for low-bandwidth) → Farmer
  • Tool calls from the LLM step: mandi price lookup, weather API, scheme status
1

Design for the connection you'll actually get

Assume PSTN or low-bandwidth mobile data, not a stable broadband WebRTC session. Use 8kHz telephony-friendly audio formats (mulaw/alaw for PSTN legs) and design conversations to survive occasional dropped audio: short turns and a willingness to repeat.

2

Ground time-sensitive facts with tools, not the model's memory

Mandi prices, weather, and pest advisories are exactly the kind of facts that are wrong the moment they’re not live. Use tool calling to hit a real prices/weather API rather than letting the LLM guess from training data. This matters even more here than in Government Services, because a number like today’s mandi price goes stale in hours, not months.

3

Keep the conversational turn short and concrete

Same principle as citizen-services bots: short, plain-language turns beat long explanations for a low-literacy, audio-only interface.

4

Cross-reference scheme content instead of duplicating it

Questions about PM-Kisan status or other scheme eligibility should route to the same grounded-answer pattern as Government Services rather than maintaining a second, possibly inconsistent copy of scheme knowledge.

LayerSettingWhy
STTsaaras:v3, mode="transcribe", language="unknown"Regional-language-first callers. Auto-detect avoids forcing Hindi on a Tamil- or Telugu-speaking farmer
LLMsarvam-30b, reasoning_effort=None for routine advisory turnsMost turns are lookup-and-explain (price, weather, basic crop guidance), not deep reasoning. Keep latency low over an already-slow connection
Tool callingWire mandi price and weather lookups as tools rather than prompting for “current” dataThe model has no live data access. Tool calling is the only way to get an actually-current mandi price into the response
TTSbulbul:v3, pace=0.85–0.9, warm speaker matched to the regional-language recommendationSame accessibility reasoning as Government Services: slower, warmer, and in the farmer’s own language beats a brisk, generic voice
TTS formatmulaw/alaw at 8kHz for PSTN/IVR delivery; fall back gracefully if the connection can’t sustain WebSocket streamingMatches Output Format Recommendations for telephony

Latency targets

The connection is usually the bottleneck here, not the model. Use streaming STT and TTS as the default, but design your retry/reconnect logic assuming the network will drop mid-call more often than in an urban contact-center deployment.

  • Keep tool-call round-trips (mandi price, weather) fast and cached where possible. For example, cache a mandi’s price for the trading day so a live lookup doesn’t stack latency on top of an already-slow connection.
  • Prefer sarvam-30b over sarvam-105b by default here, specifically because the added latency of a bigger model compounds with network latency in a way it doesn’t on a stable connection.

Pitfalls

This is the biggest risk in this vertical. A farmer acting on yesterday’s mandi price or a stale weather forecast has real financial consequences. Always ground these answers with a live tool call, and never let the LLM state a specific number from memory.

Test against 8kHz telephony audio and degraded network conditions, not just a WebRTC demo on office wifi. What works in a browser tab doesn’t necessarily survive a real rural PSTN call.

A single hardcoded target_language_code will exclude farmers outside that language’s region. Auto-detect on STT and pick the TTS voice/language per caller, rather than shipping a single-language deployment and calling it done.

Pesticide/fertilizer names, scheme acronyms, and technical crop terminology need the same plain-language treatment as Government Services content. Don’t assume agricultural literacy any more than you’d assume digital literacy.

If your agri bot also answers PM-Kisan or scheme questions, reuse the grounded, verification-pointing pattern from the Government Scheme Agent rather than writing a second, possibly drifting version of the same facts.

Full example

There’s no published cookbook example for this vertical yet. Here’s a complete agent following the same LiveKit pattern as the Government Scheme Agent, with agri-specific instructions and a tool-calling stub for live mandi prices:

1import logging
2from dotenv import load_dotenv
3from livekit.agents import JobContext, WorkerOptions, cli, function_tool
4from livekit.agents.llm import RunContext
5from livekit.agents.voice import Agent, AgentSession
6from livekit.plugins import sarvam
7
8load_dotenv()
9
10logger = logging.getLogger("krishi-mitra")
11logger.setLevel(logging.INFO)
12
13
14class KrishiMitraAgent(Agent):
15 def __init__(self) -> None:
16 super().__init__(
17 instructions="""
18 You are Krishi Mitra, a voice assistant that helps Indian farmers with
19 crop advisory, mandi (market) prices, weather, and government scheme queries.
20
21 Your responsibilities:
22 - Answer questions about crop care, sowing/harvest timing, and common pest/disease symptoms in simple terms
23 - Use the get_mandi_price tool for any current price question. Never state a price from memory
24 - Use the get_weather_forecast tool for any weather question. Never guess conditions
25 - For government scheme questions (PM-Kisan, crop insurance, etc.), give general guidance and
26 always direct the farmer to their nearest Krishi Vigyan Kendra or the official portal to confirm details
27 - If you don't know something, say so plainly rather than guessing
28
29 Communication guidelines:
30 - Use simple, everyday language. Avoid technical agronomic or bureaucratic jargon
31 - Speak slowly and clearly. Be willing to repeat
32 - Be patient and respectful. Many callers may have limited formal education
33 - Keep answers short and concrete: a specific action, not a lecture
34
35 Start by greeting the farmer warmly and asking what they need help with today.
36 """,
37 stt=sarvam.STT(
38 language="unknown", # Auto-detect across regional languages
39 model="saaras:v3",
40 mode="transcribe",
41 ),
42 llm=sarvam.LLM(model="sarvam-30b"),
43 tts=sarvam.TTS(
44 target_language_code="hi-IN", # Set per deployment region
45 model="bulbul:v3",
46 speaker="simran",
47 pace=0.9,
48 ),
49 )
50
51 @function_tool
52 async def get_mandi_price(self, context: RunContext, crop: str, mandi: str) -> dict:
53 """Look up today's price for a crop at a given mandi. Replace with a real market-price API call."""
54 # TODO: call your mandi price provider (e.g. Agmarknet) here
55 return {"crop": crop, "mandi": mandi, "price_per_quintal": None, "as_of": "today"}
56
57 @function_tool
58 async def get_weather_forecast(self, context: RunContext, location: str) -> dict:
59 """Look up the weather forecast for a location. Replace with a real weather API call."""
60 # TODO: call your weather provider here
61 return {"location": location, "forecast": None}
62
63 async def on_enter(self):
64 self.session.generate_reply()
65
66
67async def entrypoint(ctx: JobContext):
68 logger.info(f"Farmer connected to room: {ctx.room.name}")
69 session = AgentSession()
70 await session.start(agent=KrishiMitraAgent(), room=ctx.room)
71
72
73if __name__ == "__main__":
74 cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))

get_mandi_price and get_weather_forecast above are stubs. Wiring them to real data sources is what makes this agent safe to deploy. Without that, don’t let the LLM answer price/weather questions from its own knowledge. See Tool Calling for the full request/response flow.

Install the same dependencies as the other LiveKit examples:

$pip install "livekit-agents[sarvam,silero]" python-dotenv

See also: Government Services for the scheme-awareness portion of this audience.