Google Gemini
Give your Gemini app a memory of each of your users. Before Gemini answers, what is known about the user goes in the system instruction; after it answers, the exchange is saved, so the next conversation starts knowing it.
Install
pip install google-genai geniffynpm install @google/genai geniffySet GEMINI_API_KEY, and GENIFFY_API_KEY from API keys in the Geniffy app.
Remember each user
from geniffy import Geniffy
from google import genai
from google.genai import types
client = genai.Client() # reads GEMINI_API_KEY
geniffy = Geniffy() # reads GENIFFY_API_KEY
def chat(user_id: str, message: str, history: list) -> str:
"""history is the conversation so far, as types.Content."""
mem = geniffy.space(f"user_{user_id}")
context = mem.context(message) # what is known that bears on the message
system = f"You are a helpful assistant.\n\n<memory>\n{context}\n</memory>"
conversation = client.chats.create(
model="gemini-flash-latest",
config=types.GenerateContentConfig(system_instruction=system),
history=history,
)
reply = conversation.send_message(message).text
mem.memories.add(messages=[{"role": "user", "content": message},
{"role": "assistant", "content": reply}])
return replyimport { GoogleGenAI, type Content } from "@google/genai";
import { Geniffy } from "geniffy";
const ai = new GoogleGenAI({}); // reads GEMINI_API_KEY
const geniffy = new Geniffy(); // reads GENIFFY_API_KEY
// history is the conversation so far.
export async function chat(userId: string, message: string, history: Content[]) {
const mem = geniffy.space(`user_${userId}`);
const context = await mem.context(message); // what is known that bears on the message
const system = `You are a helpful assistant.\n\n<memory>\n${context}\n</memory>`;
const response = await ai.models.generateContent({
model: "gemini-flash-latest",
contents: [...history, { role: "user", parts: [{ text: message }] }],
config: { systemInstruction: system },
});
const reply = response.text ?? "";
await mem.memories.add({
messages: [
{ role: "user", content: message },
{ role: "assistant", content: reply },
],
});
return reply;
}Gemini now sees what is known about this user before every reply, each line with where it came from. When nothing is known, the block says so in one sentence, so Gemini says it doesn't know instead of guessing.
Let Gemini look things up
To let Gemini decide when to look something up, give it a recall function. In Python, pass the function
itself to a chat, and the SDK calls it for you until Gemini has its answer; in TypeScript, answer each call
yourself.
from geniffy import Geniffy
from google import genai
from google.genai import types
client = genai.Client()
geniffy = Geniffy()
INSTRUCTIONS = ("You are a helpful assistant. Use recall before answering anything "
"that depends on what the user said before.")
def chat(user_id: str, message: str) -> str:
mem = geniffy.space(f"user_{user_id}")
def recall(query: str) -> str:
"""Look up what is known about the user, with where it came from.
Args:
query: What to look up
"""
return mem.context(query)
conversation = client.chats.create(
model="gemini-flash-latest",
config=types.GenerateContentConfig(
system_instruction=INSTRUCTIONS, tools=[recall],
),
)
reply = conversation.send_message(message).text
mem.memories.add(messages=[{"role": "user", "content": message},
{"role": "assistant", "content": reply}])
return replyimport {
GoogleGenAI,
type Content,
type FunctionDeclaration,
type GenerateContentConfig,
} from "@google/genai";
import { Geniffy } from "geniffy";
const ai = new GoogleGenAI({});
const geniffy = new Geniffy();
const instructions =
"You are a helpful assistant. Use recall before answering anything " +
"that depends on what the user said before.";
const recall: FunctionDeclaration = {
name: "recall",
description: "Look up what is known about the user, with where it came from.",
parametersJsonSchema: {
type: "object",
properties: { query: { type: "string", description: "What to look up" } },
required: ["query"],
},
};
const config: GenerateContentConfig = {
systemInstruction: instructions,
tools: [{ functionDeclarations: [recall] }],
};
export async function chat(userId: string, message: string) {
const mem = geniffy.space(`user_${userId}`);
const contents: Content[] = [{ role: "user", parts: [{ text: message }] }];
for (;;) {
const response = await ai.models.generateContent({
model: "gemini-flash-latest",
contents,
config,
});
const calls = response.functionCalls ?? [];
if (calls.length === 0) {
const reply = response.text ?? "";
await mem.memories.add({
messages: [
{ role: "user", content: message },
{ role: "assistant", content: reply },
],
});
return reply;
}
const turn = response.candidates?.[0]?.content;
if (turn) contents.push(turn);
for (const call of calls) {
const result = await mem.context(String(call.args?.query ?? ""));
contents.push({
role: "user",
parts: [{ functionResponse: { name: call.name, response: { result } } }],
});
}
}
}The function answers with context(), so Gemini reads the same lines, with their sources, that the system
instruction would hold, and the same sentence when nothing is known.