diff --git a/neo4j-example/pom.xml b/neo4j-example/pom.xml index 45402e6e..4609787a 100644 --- a/neo4j-example/pom.xml +++ b/neo4j-example/pom.xml @@ -6,7 +6,7 @@ dev.langchain4j neo4j-example - 1.0.0-beta4 + 1.1.0-beta7 org.springframework.boot spring-boot-starter-parent @@ -29,7 +29,7 @@ dev.langchain4j langchain4j-spring-boot-starter - 1.1.0-beta7 + ${project.version} dev.langchain4j @@ -64,6 +64,12 @@ langchain4j-open-ai 1.1.0 + + dev.langchain4j + langchain4j-embeddings-all-minilm-l6-v2-q + 1.1.0-beta7 + compile + diff --git a/neo4j-example/src/main/java/Neo4jRagAsAToolExample.java b/neo4j-example/src/main/java/Neo4jRagAsAToolExample.java new file mode 100644 index 00000000..b768f541 --- /dev/null +++ b/neo4j-example/src/main/java/Neo4jRagAsAToolExample.java @@ -0,0 +1,114 @@ +import dev.langchain4j.community.chain.RetrievalQAChain; +import dev.langchain4j.community.store.embedding.neo4j.Neo4jEmbeddingStoreIngestor; +import dev.langchain4j.community.store.embedding.neo4j.ParentChildGraphIngestor; +import dev.langchain4j.data.document.Document; +import dev.langchain4j.data.document.DocumentSplitter; +import dev.langchain4j.data.document.loader.FileSystemDocumentLoader; +import dev.langchain4j.data.document.splitter.DocumentByRegexSplitter; +import dev.langchain4j.model.embedding.onnx.allminilml6v2q.AllMiniLmL6V2QuantizedEmbeddingModel; +import dev.langchain4j.model.openai.OpenAiChatModel; +import dev.langchain4j.agent.tool.Tool; +import dev.langchain4j.rag.content.retriever.ContentRetriever; +import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever; +import dev.langchain4j.rag.query.Query; +import dev.langchain4j.service.AiServices; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.testcontainers.containers.Neo4jContainer; +import util.Utils; + +import java.time.Duration; + +import static dev.langchain4j.model.openai.OpenAiChatModelName.GPT_4_O_MINI; + +public class Neo4jRagAsAToolExample { + + public static class RagTools { + + private final Neo4jEmbeddingStoreIngestor ingestor; + private final RetrievalQAChain qaChain; + + public RagTools(Neo4jEmbeddingStoreIngestor ingestor, RetrievalQAChain qaChain) { + this.ingestor = ingestor; + this.qaChain = qaChain; + } + + @Tool("Ingest from document") + public String ingest(String text) { + Document document = FileSystemDocumentLoader.loadDocument(Utils.toPath(text)); + + ingestor.ingest(document); + return "Document ingested"; + } + + @Tool("Answer the question based only on the context provided from the ingested documents.") + public String ask(String question) { + return qaChain.execute(Query.from(question)); + } + } + + public static void main(String[] args) { + try (Neo4jContainer neo4j = new Neo4jContainer<>("neo4j:5.26").withAdminPassword("pass1234")) { + neo4j.start(); + // Setup OpenAI chat model + OpenAiChatModel chatModel = OpenAiChatModel.builder() + .baseUrl(System.getenv("OPENAI_BASE_URL")) + .apiKey(System.getenv("OPENAI_API_KEY")) + .modelName(GPT_4_O_MINI) + .timeout(Duration.ofSeconds(60)) + .build(); + final AllMiniLmL6V2QuantizedEmbeddingModel embeddingModel = new AllMiniLmL6V2QuantizedEmbeddingModel(); + + // MainDoc splitter splits on paragraphs (double newlines) + final String expectedQuery = "\\n\\n"; + int maxSegmentSize = 250; + DocumentSplitter parentSplitter = new DocumentByRegexSplitter(expectedQuery, expectedQuery, maxSegmentSize, 0); + + // Child splitter splits on periods (sentences) + final String expectedQueryChild = "\\. "; + DocumentSplitter childSplitter = + new DocumentByRegexSplitter(expectedQueryChild, expectedQuery, maxSegmentSize, 0); + + final Driver driver = GraphDatabase.driver(neo4j.getBoltUrl(), AuthTokens.basic("neo4j", "pass1234")); + + Neo4jEmbeddingStoreIngestor ingestor = ParentChildGraphIngestor.builder() + .driver(driver) + .documentSplitter(parentSplitter) + .documentSplitter(childSplitter) + .embeddingModel(embeddingModel) + .build(); + + // Retriever from Neo4j embeddings + ContentRetriever retriever = EmbeddingStoreContentRetriever.builder() + .embeddingStore(ingestor.getEmbeddingStore()) + .embeddingModel(embeddingModel) + .maxResults(5) + .minScore(0.4) + .build(); + + // Retrieval QA chain using retriever and LLM + RetrievalQAChain retrievalQAChain = RetrievalQAChain.builder() + .contentRetriever(retriever) + .chatModel(chatModel) + .build(); + + + RagTools tools = new RagTools(ingestor, retrievalQAChain); + + // Build assistant with ingestion tool and retrieval QA tool + Utils.Assistant assistant = AiServices.builder(Utils.Assistant.class) + .tools(tools) + .chatModel(chatModel) + .build(); + + + // Ask a question answered by retrieval QA chain + String chat = assistant.chat(""" + Ingest from document 'miles-of-smiles-terms-of-use.txt', and then return the answer for the question 'What is the cancellation policy?'"""); + System.out.println("ANSWER: " + chat); + // example output: + // `ANSWER: Reservations can be cancelled up to 7 days prior to the start of the booking period. + } + } +} diff --git a/neo4j-example/src/main/java/util/Utils.java b/neo4j-example/src/main/java/util/Utils.java new file mode 100644 index 00000000..ed8203bb --- /dev/null +++ b/neo4j-example/src/main/java/util/Utils.java @@ -0,0 +1,22 @@ +package util; + +import dev.langchain4j.service.UserMessage; + +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class Utils { + + public interface Assistant { + String chat(@UserMessage String userMessage); + } + + public static Path toPath(String fileName) { + try { + return Paths.get(Utils.class.getClassLoader().getResource(fileName).toURI()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } +} diff --git a/neo4j-example/src/main/resources/miles-of-smiles-terms-of-use.txt b/neo4j-example/src/main/resources/miles-of-smiles-terms-of-use.txt new file mode 100644 index 00000000..3e8acb00 --- /dev/null +++ b/neo4j-example/src/main/resources/miles-of-smiles-terms-of-use.txt @@ -0,0 +1,37 @@ +Miles of Smiles Car Rental Services Terms of Use + +1. Introduction +These Terms of Service (“Terms”) govern the access or use by you, an individual, from within any country in the world, of applications, websites, content, products, and services (“Services”) made available by Miles of Smiles Car Rental Services, a company registered in the United States of America. + +2. The Services +Miles of Smiles rents out vehicles to the end user. We reserve the right to temporarily or permanently discontinue the Services at any time and are not liable for any modification, suspension or discontinuation of the Services. + +3. Bookings +3.1 Users may make a booking through our website or mobile application. +3.2 You must provide accurate, current and complete information during the reservation process. You are responsible for all charges incurred under your account. +3.3 All bookings are subject to vehicle availability. + +4. Cancellation Policy +4.1 Reservations can be cancelled up to 7 days prior to the start of the booking period. +4.2 If the booking period is less than 3 days, cancellations are not permitted. + +5. Use of Vehicle +5.1 All cars rented from Miles of Smiles must not be used: +for any illegal purpose or in connection with any criminal offense. +for teaching someone to drive. +in any race, rally or contest. +while under the influence of alcohol or drugs. + +6. Liability +6.1 Users will be held liable for any damage, loss, or theft that occurs during the rental period. +6.2 We do not accept liability for any indirect or consequential loss, damage, or expense including but not limited to loss of profits. + +7. Governing Law +These terms will be governed by and construed in accordance with the laws of the United States of America, and any disputes relating to these terms will be subject to the exclusive jurisdiction of the courts of United States. + +8. Changes to These Terms +We may revise these terms of use at any time by amending this page. You are expected to check this page from time to time to take notice of any changes we made. + +9. Acceptance of These Terms +By using the Services, you acknowledge that you have read and understand these Terms and agree to be bound by them. +If you do not agree to these Terms, please do not use or access our Services. \ No newline at end of file