Skip to content

Instantly share code, notes, and snippets.

@cbowdon
Created November 18, 2024 16:29
Show Gist options
  • Save cbowdon/0e23241564a0a674efbbced23ee563b1 to your computer and use it in GitHub Desktop.
Save cbowdon/0e23241564a0a674efbbced23ee563b1 to your computer and use it in GitHub Desktop.
Example of leaning on Bing search to do RAG
/**
* This file demonstrates how to use Bing search for RAG with Claude.
*
* It is a very simple solution for doing RAG-assisted chat responses about a particular website.
* Rather than having your own vector database etc., a quick-and-dirty approach is to call out to
* a search engine like Bing. This means you cede control of the retrieval process and rely on Bing's
* search result quality. In exchange for this, you can forget about embeddings, vector DBs, chunking,
* etc. and just worry about your chat interface.
*/
import Anthropic from "@anthropic-ai/sdk";
// First you will need to create a Bing custom search instance.
// https://learn.microsoft.com/en-gb/bing/search-apis/bing-custom-search/how-to/quick-start
interface BingKeys {
subscriptionKey: string;
customConfigId: string;
}
// Search results: you won't need all these fields, but FYI this is what's available
interface SearchResult {
id: string;
name: string;
url: string;
datePublished: string;
datePublishedDisplayText: string;
isFamilyFriendly: true;
displayUrl: string;
snippet: string;
dateLastCrawled: string;
openGraphImage: {
contentUrl: string;
width: number;
height: number;
};
fixedPosition: boolean;
language: "en";
isNavigational: boolean;
richCaptionGoBigHints: {
title: string;
publishDateDisplayText: string;
};
noCache: boolean;
siteName: string;
}
/**
*
* This is how we call the Bing custom search API.
*
*/
export async function retrieve(
{ customConfigId, subscriptionKey }: BingKeys,
query: string
): Promise<SearchResult[]> {
const res = await fetch(
`https://api.bing.microsoft.com/v7.0/custom/search?q=${query}&customconfig=${customConfigId}&mkt=en-GB&count=5`,
{
cache: "default",
credentials: "omit",
headers: {
Accept: "*/*",
"Accept-Language": "en-GB,en;q=0.9",
"Content-Type": "application/json",
"Ocp-Apim-Subscription-Key": subscriptionKey,
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.15",
},
method: "GET",
mode: "cors",
redirect: "follow",
referrerPolicy: "strict-origin-when-cross-origin",
}
);
const data = await res.json();
return data["webPages"]["value"] as SearchResult[];
}
/**
*
* This is how we generate responses with Claude.
*
*/
export async function generate(anthropic: Anthropic, prompt: string) {
const msg = await anthropic.messages.create({
model: "claude-3-5-sonnet-20241022",
max_tokens: 1024,
messages: [{ role: "user", content: prompt }],
});
return msg;
}
/**
*
* retrieve --> assemble prompt --> generate
*
*/
export async function retrieveAndGenerate(
anthropic: Anthropic,
bingKeys: BingKeys,
userQuery: string
) {
const searchResults = await retrieve(bingKeys, userQuery);
const formattedSearchResults = searchResults
.map(
({ name, snippet, url }) =>
`<page><name>${name}</name><snippet>${snippet}</snippet><url>${url}</url></page`
)
.join("\n");
const promptLines = [
`You are a customer support specialist for the stem cell charity Anthony Nolan.`,
`<instruction>Answer this user query using only pages from the Anthony Nolan website.</instruction>`,
`<query>${userQuery}</query>`,
`You can use this data (and ONLY this data) to answer the query:`,
`<data>`,
formattedSearchResults,
`</data>`,
`Format your answer like this:`,
`<formatting_example>`,
`ANSWER TO USER'S QUERY HERE`,
`Read more at: URL HERE`,
`</formatting_example>`,
];
return generate(anthropic, promptLines.join("\n"));
}
// EXAMPLES
const exampleBingSearchResults = [
{
id: "https://api.bing.microsoft.com/api/v7/#WebPages.0",
name: "Genetic blood disorders and other inherited conditions",
url: "https://www.anthonynolan.org/patients-and-families/blood-cancers-and-blood-disorders/what-a-blood-disorder/genetic-blood",
urlPingSuffix: "DevEx,5115.1",
datePublished: "2024-03-04T00:00:00.0000000",
datePublishedDisplayText: "4 Mar 2024",
isFamilyFriendly: true,
displayUrl:
"https://www.anthonynolan.org/patients-and-families/blood-cancers-and-blood-disorders/...",
snippet:
"People inherit these conditions through the genes they receive from their parents. They are often very rare and can sometimes be identified from a parent or newborn baby's blood sample. Some conditions don't develop until a little later in life.",
dateLastCrawled: "2024-11-18T08:20:00.0000000Z",
openGraphImage: {
contentUrl:
"https://themil.anthonynolan.org/transform/2f0a6dec-92a6-454d-bb4e-85f3fa7d0c0e/Patient-shoot?io=transform%3Afill%2Cwidth%3A1200%2Cheight%3A628&format=jpg",
width: 0,
height: 0,
},
fixedPosition: false,
language: "en",
isNavigational: false,
richCaptionGoBigHints: {
title: "anthonynolan.org",
publishDateDisplayText: "Mar 04, 2024",
},
noCache: false,
siteName: "Anthony Nolan",
},
{
id: "https://api.bing.microsoft.com/api/v7/#WebPages.1",
name: "FAQs - Anthony Nolan",
url: "https://www.anthonynolan.org/faqs",
urlPingSuffix: "DevEx,5141.1",
isFamilyFriendly: true,
displayUrl: "https://www.anthonynolan.org/faqs",
snippet:
"We are fully committed to using human tissues/cells and other in vitro (in the test tube) testing for our research where we can and finding new ways in which our findings can be validated without the use of animals.",
dateLastCrawled: "2024-11-15T04:49:00.0000000Z",
openGraphImage: {
contentUrl:
"https://themil.anthonynolan.org/transform/d8f45770-e38d-43b2-a3dd-3965f23e3b3c/Jacob_Hawley_donating?io=transform%3Afill%2Cwidth%3A1200%2Cheight%3A628&format=jpg",
width: 0,
height: 0,
},
fixedPosition: false,
language: "en",
isNavigational: false,
noCache: false,
siteName: "Anthony Nolan",
},
{
id: "https://api.bing.microsoft.com/api/v7/#WebPages.2",
name: "Haploidentical stem cell transplants | Anthony Nolan",
url: "https://www.anthonynolan.org/patients-and-families/understanding-stem-cell-transplants/haploidentical-stem-cell-transplants",
urlPingSuffix: "DevEx,5169.1",
datePublished: "2024-05-24T00:00:00.0000000",
datePublishedDisplayText: "24 May 2024",
isFamilyFriendly: true,
displayUrl:
"https://www.anthonynolan.org/patients-and-families/understanding-stem-cell-transplants/...",
snippet:
"Haploidentical stem cell transplants. If you’ve found out that you need a stem cell transplant, one of your options might be a haploidentical transplant, which uses stem cells from a relative. On this page we’ll explain what this means, how it works and why you might need one. Home.",
dateLastCrawled: "2024-11-14T02:05:00.0000000Z",
openGraphImage: {
contentUrl:
"https://themil.anthonynolan.org/transform/95101305-2186-4fb8-80f0-592ccc271578/Patient-Anie-and-her-dad?io=transform%3Afill%2Cwidth%3A1200%2Cheight%3A628&format=jpg",
width: 0,
height: 0,
},
fixedPosition: false,
language: "en",
isNavigational: false,
noCache: false,
siteName: "Anthony Nolan",
},
];
const anthropic = new Anthropic({
apiKey: "API KEY HERE",
});
const bingKeys = {
subscriptionKey: "SUB KEY HERE",
customConfigId: "CUSTOM CONFIG ID HERE",
};
const output = await retrieveAndGenerate(
anthropic,
bingKeys,
"How do I donate cash?"
);
console.log(output);
const exampleClaudeResponse = {
id: "msg_012Gvc998Vsfz3oJRBQQvzer",
type: "message",
role: "assistant",
model: "claude-3-5-sonnet-20241022",
content: [
{
type: "text",
text:
"There are several ways you can donate cash to Anthony Nolan:\n" +
"\n" +
"1. Make a card payment over the phone by calling 0303 303 3002\n" +
'2. Send a cheque payable to "Anthony Nolan" (include your contact details and information about your donation)\n' +
"3. Donate online at their website\n" +
"4. Set up a monthly donation (£5 a month helps build the stem cell register)\n" +
"\n" +
"Please note: Do not send cash through the post.\n" +
"\n" +
"Read more at: https://www.anthonynolan.org/help-save-a-life/donate-money",
},
],
stop_reason: "end_turn",
stop_sequence: null,
usage: { input_tokens: 725, output_tokens: 133 },
};
@cbowdon
Copy link
Author

cbowdon commented Nov 18, 2024

FYI those example search results aren't matched to the example response! Just some random dumps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment