I built a research-mode toggle for my LINE bot that actually
The prompt (copy-paste ready)
You are a research analyst. The user provides a URL or topic.
1. Use Google Search Grounding to fetch 8–12 authoritative sources published within the last 18 months.
2. Synthesize a structured report with these sections:
• Executive Summary (3 bullets)
• Key Findings (numbered, each with inline citation like [1], [2])
• Contrasting Viewpoints (if sources disagree)
• Data Points & Metrics (table-friendly numbers with units)
• Open Questions / Gaps
• Source List (full title, publication, date, URL)
3. Tone: professional, neutral, no fluff.
4. Output language: match the user's input language.
5. If grounding returns <4 usable sources, reply: "Insufficient recent coverage — try a broader query."Why this prompt works
Explicit source count & recency — "8–12 sources, last 18 months" stops the model from hallucinating old blog posts.
Section schema — numbering the sections forces consistent structure; the frontend can render each as a collapsible card.
Inline citation contract — [1] style tags map 1:1 to the Source List, so the LINE flex message can make them tap-to-open.
Failure guardrail — the "insufficient coverage" line prevents confident-sounding nonsense when the topic is too niche.
Wiring it into the LINE webhook (Node sketch)
// webhook handler (express)
app.post('/webhook', line.middleware(config), async (req, res) => {
await Promise.all(req.body.events.map(handleEvent));
res.sendStatus(200);
});
async function handleEvent(event) {
if (event.type !== 'message' || event.message.type !== 'text') return;
const text = event.message.text.trim();
// user taps the "Research Report" quick-reply button → payload starts with "research:"
if (text.startsWith('research:')) {
const query = text.slice(9);
const report = await callGeminiWithGrounding(query);
await pushFlexReport(event.source.userId, report);
}
}callGeminiWithGrounding hits generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro:generateContent with tools: [{ googleSearchRetrieval: {} }] and the prompt above in systemInstruction. The response's groundingMetadata.groundingChunks gives you the citation URLs — map those to the [n] markers before you build the flex JSON.
Cost & latency reality check
- Tokens: ~2.2k input / 1.8k output per report (Gemini 1.5 Pro)
- Latency: 6–9 s end-to-end (grounding adds ~3 s)
- Bill: ~$0.018 per report at current pricing — cheap enough for a freemium bot tier
One gotcha
Grounding sometimes returns paywalled PDFs. I filter groundingChunks for web.uri containing .pdf and drop them unless the domain is arxiv.org or pubmed.ncbi.nlm.nih.gov. Keeps the "tap to open" experience honest.
Ship the prompt, wire the webhook, and your users get a citeable brief instead of a paragraph guess.