I recently wrote about using OpenAI as I was exploring doing some tagging of content based on product or article information. After seeing the cost it would be for a large number of requests, I looked into what might have a more generous free tier and found Google Gemini to be quite useful for development and low-volume usage.
To recap the sceanrio I presented in the other article:
- I want to be able to search and list products or articles by a tag
- This means tagging content in the database with tags that aren't there yet
- There is too much data to manually curate
- I want to control the taxonomy to a very specific list.
- I want each element in the database tagged with the most applicable tags from that specified list.
I started looking at some of my article content and playing around with tag-based listings of articles. I wanted to be able to separate some of my fantasy fiction content from my developer tutorials and videos, or at least be able to tag them that way. This would prove out the model of auto-tagging to a specified list of tags.
In this article I'll break down what I learned about doing this with Google Gemini (if OpenAI is your thing that's in the other article). If you just want to see the code: 🧑💻Full example script on GitHub
The Tech Stack Used:
- Google Generative AI 0.24.0 for Gemini client
- React 18.0.25 and Typescript 5.7 for app development
- Next Sanity 9.9.5 for Sanity client
- npm 11.2 for package management
- tsnode 10.9.2 for running scripts
- dotenv 16.4.7 for loading environment variables
Getting Started with a Google Gemini Account and the Client
TODO: My first attempt was using OpenAI. There isn't a free tier to do this type of work, so I needed to put at least $5 onto a credit card to start making requests. Once I did, the requests cost about half a penny (I made 16 requests for 8 cents during my first test run).
In my example, I'm using TypeScript in a Next.js application, but you can probably use similar code in your application. The following steps will get you connecting to OpenAI.
1. Install Google Gemini Package
The first step was to get the Google Gemini package installed in the application. I used npm for my installation.
npm install @google/generative-ai
2. Import Google Gemini and Initialize Client
Initializing the Google Gemini client is very straightforward. All you need is a constructor and an API key. You can get the API key from the Google AI Studio (https://aistudio.google.com/app/apikey) Once you have your API key, you can then initialize your client and make calls. In my code example here, I’m putting my API Key in an environment file.
import { GoogleGenerativeAI } from '@google/generative-ai';
const gemini = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!); //Make sure to add the API Key to your ENV file.
3. Monitor Usage
Google AI has quotas for the free tier based on different activities and models being used. The default limit for the free tier that will impact you the most is the 30 requests per minute (for the 2.0 Flash Lite model) and the 1500 requests per day for most models. You can monitor how much you are using of a model on the Google Cloud API & Services dashboard. There is a section for quotas: https://console.cloud.google.com/apis/api/generativelanguage.googleapis.com/quotas
I found this a good console to keep an eye on to see how I was doing in terms of my quota usage while I was running test runs to make sure I wasn't getting to close to the limit.
Each model has its own quota, so while it is limited to 1500 per day, you can swap models to get more usage. For this tagging purpose, all of the models I tried were up to the task to get good enough tagging. I swapped between gemini-2.0-flash-lite, gemini-2.0-flash, and gemini-1.5-flash, which allowed me to tag up to 4500 records every day.
Using Google Gemini as Your Tagging Recommendation Engine
Once the Gemini client is set up and able to make requests, it's time to start building the logic to add tags onto content.
1. Define Your Taxonomy
The goal is to create a collection of acceptable tags that should be applied to content. This makes sure that the AI doesn't create tags that we don't need or want at this time.
// Define the set of tags to check
const selectedTags = ["fiction", "fantasy", "developer", "tutorial", "video", "event"];
2. Retrieve Data to be Tagged
In my other example with OpenAI, I was using a Sanity client to query for data and we're going to run the same thing here so that we are working with the same data, but running through Google Gemini.
// Create a client to connect to your data source
const sanityClient = createClient({
projectId,
dataset,
token,
apiVersion: '2024-03-19',
useCdn: false,
});
// Fetch all posts from the data source, pulling back the data needed
const query = `*[_type == "post"] {
_id,
title,
body,
tags
}`;
const posts = await sanityClient.fetch(query);
3. Use Google Gemini to Check Tags Against Your Content
In my scenario, I did a loop over all the data that was fetched and contacted Google Gemini for each one. Every time, I requested the Google Gemini API to check if any of the tags I have in my taxonomy are applicable to that article. There are ??? parts of this function that are important:
- Specify the model to use. I used "gemini-2.0-flash-lite" because it is a more recent model but has the same free limit as some of the older models. It does seem like the older models (gemini-1.5-flash, for example) worked just as well and seem to have distinct quotas. I didn't push it hard to see what would happen but it seems like if all the models are working relatively well for your task you could build failover logic to run on a model until you hit the limit and then fail over to another model.
- Temperature. This parameter on the request controls how creative the LLM should be. I used a setting of 0.3 to try to keep the model from being too random.
- Max Output Tokens. This parameter tells Gemini how large of a response we want back. By keeping this at a small number (100) it gives the context to the LLM to try to keep its response very short and not try to add a bunch of filler content.
- System Instruction. This prompt context to the model sets Gemini up as to how it should process the incoming request. The example here sets it up for content analysis for article content.
- Prompt. The prompt will have all the context information about the specific article in this example. If you have products, this might be a product description and title. For my articles, I was using a title and the article content. This requests the tags, based on the provided content.
- Tag extraction. The data on the response comes back as a string and we need to extract it from the response and then split it up to get the array of strings, one for each tag that the LLM suggested.
// New function to check applicable tags
async function checkApplicableTags(title: string, content: string, targetTags: string[]): Promise<string[]> {
try {
const model = gemini.getGenerativeModel({
model: "gemini-2.0-flash-lite",
generationConfig: {
temperature: 0.3,
maxOutputTokens: 100,
},
systemInstruction: "You are a helpful AI assistant that specializes in content analysis. Your task is to determine which of the specified tags are applicable to the provided article content. If none are applicable, return an empty array. Do not force tags that are not applicable."
});
const prompt = `Given the following article:\n\nTitle: ${title}\n\nContent: ${content}\n\nPlease analyze the content and determine which of the following tags are applicable: ${targetTags.join(', ')}. Provide only the applicable tags in a comma-separated list with no additional text or explanation.`;
const result = await model.generateContent(prompt);
// Extract applicable tags from the response
const applicableTags = result.response.text() || '';
return applicableTags.split(',').map(tag => tag.trim()).filter(tag => tag.length > 0);
} catch (error) {
console.error('Error checking applicable tags from Gemini:', error);
return [];
}
}
Saving Your Progress
Once you have results back from the Gemini API, you'll want to save these new tags onto the record before processing the next one. During testing, you might skip this and just output the results to the Console to see what type of results you are getting and validate your prompts.
In this example, I'm saving back to Sanity with the new tags which are a collection of the previous tags plus the new tags.
// Save the updated tags back to the Sanity document
if (updatedTags.length > 0) {
console.log(`Saving updated tags for "${post.title}" to Sanity...`);
await sanityClient.patch(post._id) // Document ID
.set({ tags: updatedTags }) // Update the tags field
.commit() // Commit the changes
.then(() => {
console.log(`Successfully updated tags for "${post.title}".`);
})
.catch((error) => {
console.error(`Error updating tags for "${post.title}":`, error);
});
}
While this was a simple scenario, I found Gemini very easy to work with, even a bit easier than OpenAI, though I found the UI for the quotas not as easy as working with the OpenAI dashboard. I used the approach shown here on a similar scenario with a set of products (about eight thousand) and it worked rather well, though it still suffers from occasionally tagging things too much and putting an extra tag based on a loose association.
One thing I did run into was that if you are re-running the script you often have to determine which records have previously been processed. I started including a "Gemini-tagged" tag on my records to mark that they had been processed by the LLM. This allowed me to skip over records I had already worked on. However, as I evolve my pattern I feel like storing it in the tags is not good enough. I probably need something like a 'last tagged' date field so I can do a refresh on tags that haven't been done in a long time.
📖Additional Resources
There are a few things that I found helpful as you get started with Google Gemini:
- Google Gemini (github.com): Official Node.js/Typescript library with all sorts of examples.
- Get a Gemini API Key (ai.google.dev): Official docs for setting up your key and triggering a request.
- Credentials (console.cloud.google.com): API & Service credentials management page for Google Cloud.
🤖 AI Disclosure
- AI-Generated Code: Most of the code samples above were generated by AI using Cursor IDE and a variety of models. While they were reviewed, edited, and tested by myself, I did not artisanally create this logic. They are part of working code that is in my GitHub repo.
Top comments (0)