Generative Search - OpenAI
In short
- The Generative OpenAI (
generative-openai
) module is a Weaviate module for generating responses based on the data stored in your Weaviate instance. - The module can generate a response for each returned object, or a single response for a group of objects.
- The module adds a
generate {}
parameter to the GraphQL_additional {}
property of theGet {}
queries - Added in Weaviate
v1.17.3
Introduction
generative-openai
is a Weaviate module for generating text based on fields returned by Weaviate queries.
The module works in two steps:
- (Weaviate) Run a search query in Weaviate to find relevant objects.
- (OpenAI) Use OpenAI to generate a response based on the results (from the previous step) and the provided prompt or task.
You can use the Generative OpenAI module with any other modules. For example, you could use text2vec-cohere
or text2vec-huggingface
to vectorize and query your data, but then rely on the generative-openai
module to generate a response.
The generative module can provide results for:
- each returned object -
singleResult{ prompt }
- the group of all results together –
groupedResult{ task }
You need to input both a query and a prompt (for individual responses) or a task (for all responses).
OpenAI API key
generative-openai
requires an OpenAI API key to perform the generation task.
Providing the key to Weaviate
You can provide your OpenAI API key in two ways:
During the configuration of your Docker instance, by adding
OPENAI_APIKEY
underenvironment
to yourdocker-compose
file, like this:environment:
OPENAI_APIKEY: 'your-key-goes-here'
...At run-time (recommended), by providing
"X-OpenAI-Api-Key"
to the Weaviate client, like this:
- Python
- JavaScript
- Go
- Java
import weaviate
client = weaviate.Client(
url = "https://some-endpoint.weaviate.network/",
additional_headers = {
"X-OpenAI-Api-Key": "<THE-KEY>" # Replace with your API key
}
)
const weaviate = require("weaviate-client");
const client = weaviate.client({
scheme: 'https',
host: 'some-endpoint.weaviate.network',
// Replace with your API key
headers: {'X-OpenAI-Api-Key': '<THE-KEY>'},
});
package main
import (
"context"
"fmt"
"github.com/weaviate/weaviate-go-client/v4/weaviate"
"github.com/weaviate/weaviate/entities/models"
)
func main() {
cfg := weaviate.Config{
Host: "some-endpoint.weaviate.network/", // Replace with your endpoint
Scheme: "https",
// Replace with your API key
Headers: map[string]string{"X-OpenAI-Api-Key": "<THE-KEY>"}
}
client := weaviate.New(cfg)
}
package io.weaviate;
import java.util.ArrayList;
import io.weaviate.client.Config;
import io.weaviate.client.WeaviateClient;
import io.weaviate.client.base.Result;
public class App {
public static void main(String[] args) {
Map<String, String> headers = new HashMap<String, String>() { {
// Replace with your API key
put("X-OpenAI-Api-Key", "<THE-KEY>");
} };
Config config = new Config("https", "some-endpoint.weaviate.network/", headers);
WeaviateClient client = new WeaviateClient(config);
}
}
Enabling the module
Your Weaviate instance must be on 1.17.3
or newer.
If your instance is older than 1.17.3
then you need to migrate or upgrade it to a newer version.
WCS
The Generative OpenAI
module is enabled by default in the Weaviate Cloud Services (WCS). If your instance version is on 1.17.3
or newer, then the module is ready to go.
Local deployment with Docker
To enable the Generative OpenAI module with your local deployment of Weaviate, you need to configure your docker-compose
file. Add the generative-openai
module (alongside any other module you may need) to the ENABLE_MODULES
property, like this:
ENABLE_MODULES: 'text2vec-openai,generative-openai'
Here is a full example of a Docker configuration, which uses the generative-openai
module in combination with text2vec-openai
:
---
version: '3.4'
services:
weaviate:
command:
- --host
- 0.0.0.0
- --port
- '8080'
- --scheme
- http
image:
semitechnologies/weaviate:1.18.2
ports:
- 8080:8080
restart: on-failure:0
environment:
QUERY_DEFAULTS_LIMIT: 25
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true'
PERSISTENCE_DATA_PATH: '/var/lib/weaviate'
DEFAULT_VECTORIZER_MODULE: 'text2vec-openai'
ENABLE_MODULES: 'text2vec-openai,generative-openai'
OPENAI_APIKEY: sk-foobar # this parameter is optional, as you can also provide it through the client
CLUSTER_HOSTNAME: 'node1'
Schema configuration
The Generative module doesn't require a specific schema configuration.
You need a schema to run queries on your data, so that the module can use the results to generate a response.
New to Weaviate Schemas?
If you are new to Weaviate, check out the Weaviate schema tutorial.
How to use
This module extends the _additional {...}
property with a generate
operator.
generate
takes the following arguments:
Field | Data Type | Required | Example | Description |
---|---|---|---|---|
singleResult {prompt} | string | no | Summarize the following in a tweet: {summary} | Generates a response for each individual search result. You need to include at least one result field in the prompt, between braces. |
groupedResult {task} | string | no | Explain why these results are similar to each other | Generates a single response for all search results |
Currently, you can't provide your OpenAI key in the Weaviate console. That means you can't use the GraphQL
examples with your WCS instances, but if you provide your API key in the Docker configuration, then this should work.
Example of properties in the prompt
When piping the results to the prompt, at least one field returned by the query must be added to the prompt. If you don't add any fields, Weaviate will throw an error.
For example, assume your schema looks like this:
{
Article {
title
summary
}
}
You can add both title
and summary
to the prompt by enclosing them in curly brackets:
{
Get {
Article {
title
summary
_additional {
generate(
singleResult: {
prompt: """
Summarize the following in a tweet:
{title} - {summary}
"""
}
) {
singleResult
error
}
}
}
}
}
Example - single result
Here is an example of a query where:
- we run a vector search (with
nearText
) to find articles about "Italian food" - then we ask the generator module to describe each result as a Facebook ad.
- the query asks for the
summary
field, which it then includes in theprompt
argument of thegenerate
operator.
- the query asks for the
- GraphQL
- Python
- JavaScript
- Go
- Java
- Curl
{
Get {
Article(
nearText: {
concepts: ["Italian food"]
}
limit: 1
) {
title
summary
_additional {
generate(
singleResult: {
prompt: """
Describe the following as a Facebook Ad: {summary}
"""
}
) {
singleResult
error
}
}
}
}
}
import weaviate
client = weaviate.Client(
url = "https://some-endpoint.weaviate.network/",
additional_headers={
"X-OpenAI-Api-Key": "<THE-KEY>"
}
)
# instruction for the generative module
generatePrompt = "Describe the following as a Facebook Ad: {summary}"
result = (
client.query
.get("Article", ["title", "summary"])
.with_generate(single_prompt=generatePrompt)
.with_near_text({
"concepts": ["Italian food"]
})
.with_limit(5)
).do()
print(result)
const weaviate = require('weaviate-client');
const client = weaviate.client({
scheme: 'https',
host: 'some-endpoint.weaviate.network',
headers: {'X-OpenAI-Api-Key': '<THE-KEY>'},
});
// instruction for the generative module
const generatePrompt = 'Describe the following as a Facebook Ad: {summary}';
client.graphql
.get()
.withClassName('Article')
.withFields(
'title summary ' +
`_additional {generate(singleResult:{ prompt: ${generatePrompt} }) { singleResult }}`
)
.withNearText({
concepts: ['Italian food']
})
.withLimit(5)
.do()
.then(res => {
console.log(res)
})
.catch(err => {
console.error(err)
});
package main
import (
"context"
"fmt"
"github.com/weaviate/weaviate-go-client/v4/weaviate"
"github.com/weaviate/weaviate-go-client/v4/weaviate/graphql"
)
func main() {
cfg := weaviate.Config{
Host: "some-endpoint.weaviate.network",
Scheme: "https",
Headers: map[string]string{"X-OpenAI-Api-Key": "<THE-KEY>"},
}
client := weaviate.New(cfg)
// instruction for the generative module
generatePrompt := "\"Describe the following as a Facebook Ad: {summary}\""
fields := []graphql.Field{
{Name: "title"},
{Name: "summary"},
{
Name: "_additional",
Fields: []graphql.Field{
{
Name: "generate(singleResult:{ prompt: " + generatePrompt + " })",
Fields: []graphql.Field{
{Name: "singleResult"},
},
},
},
},
}
concepts := []string{"Italian food"}
nearText := client.GraphQL().NearTextArgBuilder().
WithConcepts(concepts)
ctx := context.Background()
result, err := client.GraphQL().Get().
WithClassName("Article").
WithFields(fields...).
WithNearText(nearText).
WithLimit(5).
Do(ctx)
if err != nil {
panic(err)
}
fmt.Printf("%v", result)
}
package io.weaviate;
import java.util.HashMap;
import java.util.Map;
import io.weaviate.client.Config;
import io.weaviate.client.WeaviateClient;
import io.weaviate.client.base.Result;
import io.weaviate.client.v1.graphql.model.GraphQLResponse;
import io.weaviate.client.v1.graphql.query.argument.NearTextArgument;
import io.weaviate.client.v1.graphql.query.fields.Field;
public class App {
public static void main(String[] args) {
Map<String, String> headers = new HashMap<String, String>() {
{put("X-OpenAI-Api-Key", "<THE-KEY>");}
};
Config config = new Config("https", "some-endpoint.weaviate.network", headers);
WeaviateClient client = new WeaviateClient(config);
// instruction for the generative module
String generatePrompt = "\"Describe the following as a Facebook Ad: {summary}\""
Field title = Field.builder().name("title").build();
Field summary = Field.builder().name("summary").build();
Field generate = Field.builder()
.name("generate(singleResult:{ prompt: " + generatePrompt + " } )")
.fields(new Field[]{ Field.builder().name("singleResult").build() })
.build();
Field _additional = Field.builder()
.name("_additional")
.fields(new Field[]{ generate })
.build();
NearTextArgument nearText = client.graphQL().arguments().nearTextArgBuilder()
.concepts(new String[]{ "Italian food" })
.build();
Result<GraphQLResponse> result = client.graphQL().get()
.withClassName("Article")
.withFields(title, summary, _additional)
.withNearText(nearText)
.withLimit(5)
.run();
if (result.hasErrors()) {
System.out.println(result.getError());
return;
}
System.out.println(result.getResult());
}
}
$ echo '{
"query": "{
Get {
Article(
nearText: {
concepts: [\"Italian food\"]
}
limit: 5
) {
title
summary
_additional {
generate(
singleResult: {
prompt: \"\"\"
Describe the following as a Facebook Ad: {summary}
\"\"\"
}
) {
singleResult
error
}
}
}
}
}
"
}' | tr -d "\n" | curl \
-X POST \
-H 'Content-Type: application/json' \
-H "X-OpenAI-Api-Key: <THE-KEY>" \
-d @- \
https://some-endpoint.weaviate.network/v1/graphql
Example response - single result
{
"data": {
"Get": {
"Article": [
{
"_additional": {
"generate": {
"error": null,
"singleResult": "This Facebook Ad will explore the fascinating history of Italian food and how it has evolved over time. Learn from Dr Eva Del Soldato and Diego Zancani, two experts in Italian food history, about how even the emoji for pasta isn't just pasta -- it's a steaming plate of spaghetti heaped with tomato sauce on top. Discover how Italy's complex history has shaped the Italian food we know and love today."
}
},
"summary": "Even the emoji for pasta isn't just pasta -- it's a steaming plate of spaghetti heaped with tomato sauce on top. But while today we think of tomatoes as inextricably linked to Italian food, that hasn't always been the case. \"People tend to think Italian food was always as it is now -- that Dante was eating pizza,\" says Dr Eva Del Soldato , associate professor of romance languages at the University of Pennsylvania, who leads courses on Italian food history. In fact, she says, Italy's complex history -- it wasn't unified until 1861 -- means that what we think of Italian food is, for the most part, a relatively modern concept. Diego Zancani, emeritus professor of medieval and modern languages at Oxford University and author of \"How We Fell in Love with Italian Food,\" agrees.",
"title": "How this fruit became the star of Italian cooking"
}
]
}
}
}
Example - grouped result
Here is an example of a query where:
- we run a vector search (with
nearText
) to find publications about finance, - then we ask the generator module to explain why these articles are about finance.
- GraphQL
- Python
- JavaScript
- Go
- Java
- Curl
{
Get {
Publication(
nearText: {
concepts: ["magazine or newspaper about finance"]
certainty: 0.75
}
) {
name
_additional {
generate(
groupedResult: {
task: "Explain why these magazines or newspapers are about finance"
}
) {
groupedResult
error
}
}
}
}
}
import weaviate
client = weaviate.Client(
url = "https://some-endpoint.weaviate.network/",
additional_headers={
"X-OpenAI-Api-Key": "<THE-KEY>"
}
)
# instruction for the generative module
generateTask = "Explain why these magazines or newspapers are about finance"
result = (
client.query
.get("Publication", ["name"])
.with_generate(grouped_task=generateTask)
.with_near_text({
"concepts": ["magazine or newspaper about finance"]
})
.with_limit(5)
).do()
print(result)
const weaviate = require('weaviate-client');
const client = weaviate.client({
scheme: 'https',
host: 'some-endpoint.weaviate.network',
headers: {'X-OpenAI-Api-Key': '<THE-KEY>'},
});
// instruction for the generative module
const generateTask = 'Explain why these magazines or newspapers are about finance';
client.graphql
.get()
.withClassName('Article')
.withFields(
'name ' +
`_additional {generate(groupedResult:{ task: ${generateTask} }) { groupedResult }}`
)
.withNearText({
concepts: ['magazine or newspaper about finance']
})
.withLimit(5)
.do()
.then(res => {
console.log(res)
})
.catch(err => {
console.error(err)
});
package main
import (
"context"
"fmt"
"github.com/weaviate/weaviate-go-client/v4/weaviate"
"github.com/weaviate/weaviate-go-client/v4/weaviate/graphql"
)
func main() {
cfg := weaviate.Config{
Host: "some-endpoint.weaviate.network",
Scheme: "https",
Headers: map[string]string{"X-OpenAI-Api-Key": "<THE-KEY>"},
}
client := weaviate.New(cfg)
// instruction for the generative module
generateTask := "\"Explain why these magazines or newspapers are about finance\""
fields := []graphql.Field{
{Name: "name"},
{
Name: "_additional",
Fields: []graphql.Field{
{
Name: "generate(groupedResult:{ task: " + generateTask + " })",
Fields: []graphql.Field{
{Name: "groupedResult"},
},
},
},
},
}
concepts := []string{"magazine or newspaper about finance"}
nearText := client.GraphQL().NearTextArgBuilder().
WithConcepts(concepts)
ctx := context.Background()
result, err := client.GraphQL().Get().
WithClassName("Publication").
WithFields(fields...).
WithNearText(nearText).
WithLimit(5).
Do(ctx)
if err != nil {
panic(err)
}
fmt.Printf("%v", result)
}
package io.weaviate;
import java.util.HashMap;
import java.util.Map;
import io.weaviate.client.Config;
import io.weaviate.client.WeaviateClient;
import io.weaviate.client.base.Result;
import io.weaviate.client.v1.graphql.model.GraphQLResponse;
import io.weaviate.client.v1.graphql.query.argument.NearTextArgument;
import io.weaviate.client.v1.graphql.query.fields.Field;
public class App {
public static void main(String[] args) {
Map<String, String> headers = new HashMap<String, String>() { {
put("X-OpenAI-Api-Key", "<THE-KEY>");
} };
Config config = new Config("https", "some-endpoint.weaviate.network", headers);
WeaviateClient client = new WeaviateClient(config);
// instruction for the generative module
String generateTask = "\"Explain why these magazines or newspapers are about finance\"";
Field name = Field.builder().name("name").build();
Field generate = Field.builder()
.name("generate(groupedResult:{ task: " + generateTask + " } )")
.fields(new Field[]{ Field.builder().name("groupedResult").build() })
.build();
Field _additional = Field.builder()
.name("_additional")
.fields(new Field[]{ generate })
.build();
NearTextArgument nearText = client.graphQL().arguments().nearTextArgBuilder()
.concepts(new String[]{ "magazine or newspaper about finance" })
.build();
Result<GraphQLResponse> result = client.graphQL().get()
.withClassName("Publication")
.withFields(name, _additional)
.withNearText(nearText)
.withLimit(5)
.run();
if (result.hasErrors()) {
System.out.println(result.getError());
return;
}
System.out.println(result.getResult());
}
}
$ echo '{
"query": "{
Get {
Publication(
nearText: {
concepts: [\"magazine or newspaper about finance\"]
}
limit: 5
) {
name
_additional {
generate(
groupedResult: {
task: \"Explain why these magazines or newspapers are about finance\"
}
) {
groupedResult
error
}
}
}
}
}
"
}' | tr -d "\n" | curl \
-X POST \
-H 'Content-Type: application/json' \
-H "X-OpenAI-Api-Key: <THE-KEY>" \
-d @- \
https://some-endpoint.weaviate.network/v1/graphql
Example response - grouped result
{
"data": {
"Get": {
"Publication": [
{
"_additional": {
"generate": {
"error": null,
"groupedResult": "The Financial Times, Wall Street Journal, and The New York Times Company are all about finance because they provide news and analysis on the latest financial markets, economic trends, and business developments. They also provide advice and commentary on personal finance, investments, and other financial topics."
}
},
"name": "Financial Times"
},
{
"_additional": {
"generate": null
},
"name": "Wall Street Journal"
},
{
"_additional": {
"generate": null
},
"name": "The New York Times Company"
}
]
}
}
}
Additional information
Available models
OpenAI has one model available to generate answers based on the prompt.
More resources
If you can't find the answer to your question here, please look at the:
- Frequently Asked Questions. Or,
- Knowledge base of old issues. Or,
- For questions: Stackoverflow. Or,
- For issues: GitHub. Or,
- Ask your question in the Slack channel: Slack.