diff --git a/lib/langchain/vectorsearch/base.rb b/lib/langchain/vectorsearch/base.rb index f58199a63..5cc054e38 100644 --- a/lib/langchain/vectorsearch/base.rb +++ b/lib/langchain/vectorsearch/base.rb @@ -168,6 +168,27 @@ def generate_hyde_prompt(question:) prompt_template.format(question: question) end + # Ask a question and return the answer + # @param question [String] The question to ask + # @param context [Array] The context generated by RAG + # @param system_prompt [String] Content of the prompt to send as "system" + # @yield [String] Stream responses back one String at a time + # @return [String] The answer to the question + def generate_messages_and_chat(question: , context: , system_prompt: nil, &block) + context = context.join("\n---\n") + + prompt = generate_rag_prompt(question: question, context: context) + + messages = [ + system_prompt ? {role: 'system', content: system_prompt} : nil, + {role: "user", content: prompt} + ].compact + response = llm.chat(messages: messages, &block) + + response.context = context + response + end + # Retrieval Augmented Generation (RAG) # # @param question [String] User's question diff --git a/lib/langchain/vectorsearch/chroma.rb b/lib/langchain/vectorsearch/chroma.rb index abd3f585b..afc6c538f 100644 --- a/lib/langchain/vectorsearch/chroma.rb +++ b/lib/langchain/vectorsearch/chroma.rb @@ -123,24 +123,17 @@ def similarity_search_by_vector( # Ask a question and return the answer # @param question [String] The question to ask # @param k [Integer] The number of results to have in context + # @param system_prompt [String] Content of the prompt to send as "system" # @yield [String] Stream responses back one String at a time # @return [String] The answer to the question - def ask(question:, k: 4, &block) + def ask(question:, k: 4, system_prompt: nil, &block) search_results = similarity_search(query: question, k: k) context = search_results.map do |result| result.document end - context = context.join("\n---\n") - - prompt = generate_rag_prompt(question: question, context: context) - - messages = [{role: "user", content: prompt}] - response = llm.chat(messages: messages, &block) - - response.context = context - response + generate_messages_and_chat(question: question, context: context, system_prompt: system_prompt, &block) end private diff --git a/lib/langchain/vectorsearch/elasticsearch.rb b/lib/langchain/vectorsearch/elasticsearch.rb index f9f5401bd..19b6501ba 100644 --- a/lib/langchain/vectorsearch/elasticsearch.rb +++ b/lib/langchain/vectorsearch/elasticsearch.rb @@ -141,22 +141,17 @@ def default_query(query_vector) # Ask a question and return the answer # @param question [String] The question to ask # @param k [Integer] The number of results to have in context + # @param system_prompt [String] Content of the prompt to send as "system" # @yield [String] Stream responses back one String at a time # @return [String] The answer to the question - def ask(question:, k: 4, &block) + def ask(question:, k: 4, system_prompt: nil, &block) search_results = similarity_search(query: question, k: k) context = search_results.map do |result| result[:input] - end.join("\n---\n") - - prompt = generate_rag_prompt(question: question, context: context) - - messages = [{role: "user", content: prompt}] - response = llm.chat(messages: messages, &block) + end - response.context = context - response + generate_messages_and_chat(question: question, context: context, system_prompt: system_prompt, &block) end # Search for similar texts diff --git a/lib/langchain/vectorsearch/epsilla.rb b/lib/langchain/vectorsearch/epsilla.rb index a0b2857cc..3b91916bf 100644 --- a/lib/langchain/vectorsearch/epsilla.rb +++ b/lib/langchain/vectorsearch/epsilla.rb @@ -125,23 +125,17 @@ def similarity_search_by_vector(embedding:, k: 4) # Ask a question and return the answer # @param question [String] The question to ask # @param k [Integer] The number of results to have in context + # @param system_prompt [String] Content of the prompt to send as "system" # @yield [String] Stream responses back one String at a time # @return [String] The answer to the question - def ask(question:, k: 4, &block) + def ask(question:, k: 4, system_prompt: nil, &block) search_results = similarity_search(query: question, k: k) context = search_results.map do |result| result.to_s end - context = context.join("\n---\n") - prompt = generate_rag_prompt(question: question, context: context) - - messages = [{role: "user", content: prompt}] - response = llm.chat(messages: messages, &block) - - response.context = context - response + generate_messages_and_chat(question: question, context: context, system_prompt: system_prompt, &block) end end end diff --git a/lib/langchain/vectorsearch/milvus.rb b/lib/langchain/vectorsearch/milvus.rb index a575085ff..f5d1131de 100644 --- a/lib/langchain/vectorsearch/milvus.rb +++ b/lib/langchain/vectorsearch/milvus.rb @@ -139,22 +139,15 @@ def similarity_search_by_vector(embedding:, k: 4) # Ask a question and return the answer # @param question [String] The question to ask # @param k [Integer] The number of results to have in context + # @param system_prompt [String] Content of the prompt to send as "system" # @yield [String] Stream responses back one String at a time # @return [String] The answer to the question - def ask(question:, k: 4, &block) + def ask(question:, k: 4, system_prompt: nil, &block) search_results = similarity_search(query: question, k: k) - content_data = search_results.dig("data").map { |result| result.dig("content") } + context = search_results.dig("data").map { |result| result.dig("content") } - context = content_data.join("\n---\n") - - prompt = generate_rag_prompt(question: question, context: context) - - messages = [{role: "user", content: prompt}] - response = llm.chat(messages: messages, &block) - - response.context = context - response + generate_messages_and_chat(question: question, context: context, system_prompt: system_prompt, &block) end end end diff --git a/lib/langchain/vectorsearch/pgvector.rb b/lib/langchain/vectorsearch/pgvector.rb index de29224cc..122286aec 100644 --- a/lib/langchain/vectorsearch/pgvector.rb +++ b/lib/langchain/vectorsearch/pgvector.rb @@ -144,23 +144,17 @@ def similarity_search_by_vector(embedding:, k: 4) # Ask a question and return the answer # @param question [String] The question to ask # @param k [Integer] The number of results to have in context + # @param system_prompt [String] Content of the prompt to send as "system" # @yield [String] Stream responses back one String at a time # @return [String] The answer to the question - def ask(question:, k: 4, &block) + def ask(question:, k: 4, system_prompt: nil, &block) search_results = similarity_search(query: question, k: k) context = search_results.map do |result| result.content.to_s end - context = context.join("\n---\n") - prompt = generate_rag_prompt(question: question, context: context) - - messages = [{role: "user", content: prompt}] - response = llm.chat(messages: messages, &block) - - response.context = context - response + generate_messages_and_chat(question: question, context: context, system_prompt: system_prompt, &block) end end end diff --git a/lib/langchain/vectorsearch/pinecone.rb b/lib/langchain/vectorsearch/pinecone.rb index 6cbec2569..67b1d4b8d 100644 --- a/lib/langchain/vectorsearch/pinecone.rb +++ b/lib/langchain/vectorsearch/pinecone.rb @@ -168,24 +168,18 @@ def similarity_search_by_vector(embedding:, k: 4, namespace: "", filter: nil) # @param question [String] The question to ask # @param namespace [String] The namespace to search in # @param k [Integer] The number of results to have in context + # @param system_prompt [String] Content of the prompt to send as "system" # @param filter [String] The filter to use # @yield [String] Stream responses back one String at a time # @return [String] The answer to the question - def ask(question:, namespace: "", filter: nil, k: 4, &block) + def ask(question:, namespace: "", filter: nil, k: 4, system_prompt: nil, &block) search_results = similarity_search(query: question, namespace: namespace, filter: filter, k: k) context = search_results.map do |result| result.dig("metadata").to_s end - context = context.join("\n---\n") - prompt = generate_rag_prompt(question: question, context: context) - - messages = [{role: "user", content: prompt}] - response = llm.chat(messages: messages, &block) - - response.context = context - response + generate_messages_and_chat(question: question, context: context, system_prompt: system_prompt, &block) end # Pinecone index diff --git a/lib/langchain/vectorsearch/qdrant.rb b/lib/langchain/vectorsearch/qdrant.rb index ade772f8a..8e1ba228b 100644 --- a/lib/langchain/vectorsearch/qdrant.rb +++ b/lib/langchain/vectorsearch/qdrant.rb @@ -136,23 +136,17 @@ def similarity_search_by_vector( # Ask a question and return the answer # @param question [String] The question to ask # @param k [Integer] The number of results to have in context + # @param system_prompt [String] Content of the prompt to send as "system" # @yield [String] Stream responses back one String at a time # @return [String] The answer to the question - def ask(question:, k: 4, &block) + def ask(question:, k: 4, system_prompt: nil, &block) search_results = similarity_search(query: question, k: k) context = search_results.map do |result| result.dig("payload").to_s end - context = context.join("\n---\n") - prompt = generate_rag_prompt(question: question, context: context) - - messages = [{role: "user", content: prompt}] - response = llm.chat(messages: messages, &block) - - response.context = context - response + generate_messages_and_chat(question: question, context: context, system_prompt: system_prompt, &block) end end end diff --git a/lib/langchain/vectorsearch/weaviate.rb b/lib/langchain/vectorsearch/weaviate.rb index 53440f04d..6e8e3f363 100644 --- a/lib/langchain/vectorsearch/weaviate.rb +++ b/lib/langchain/vectorsearch/weaviate.rb @@ -142,23 +142,17 @@ def similarity_search_by_vector(embedding:, k: 4) # Ask a question and return the answer # @param question [String] The question to ask # @param k [Integer] The number of results to have in context + # @param system_prompt [String] Content of the prompt to send as "system" # @yield [String] Stream responses back one String at a time # @return [Hash] The answer - def ask(question:, k: 4, &block) + def ask(question:, k: 4, system_prompt: nil, &block) search_results = similarity_search(query: question, k: k) context = search_results.map do |result| result.dig("content").to_s end - context = context.join("\n---\n") - prompt = generate_rag_prompt(question: question, context: context) - - messages = [{role: "user", content: prompt}] - response = llm.chat(messages: messages, &block) - - response.context = context - response + generate_messages_and_chat(question: question, context: context, system_prompt: system_prompt, &block) end private diff --git a/spec/langchain/vectorsearch/chroma_spec.rb b/spec/langchain/vectorsearch/chroma_spec.rb index 6f6dbe681..eb21c1832 100644 --- a/spec/langchain/vectorsearch/chroma_spec.rb +++ b/spec/langchain/vectorsearch/chroma_spec.rb @@ -166,6 +166,18 @@ end end + context 'with system prompt' do + let(:system_prompt) { 'You are a helpful assistant' } + before do + allow(subject.llm).to receive(:chat).with(messages: [{role: 'system', content: system_prompt}, *messages]).and_return(response) + expect(response).to receive(:context=).with(text) + end + + it "asks a question and returns the answer" do + expect(subject.ask(question: question, system_prompt: system_prompt).completion).to eq(answer) + end + end + context "with block" do let(:block) { proc { |chunk| puts "Received chunk: #{chunk}" } } diff --git a/spec/langchain/vectorsearch/elasticsearch_spec.rb b/spec/langchain/vectorsearch/elasticsearch_spec.rb index 72a753239..13b13137d 100644 --- a/spec/langchain/vectorsearch/elasticsearch_spec.rb +++ b/spec/langchain/vectorsearch/elasticsearch_spec.rb @@ -274,6 +274,18 @@ end end + context 'with system prompt' do + let(:system_prompt) { 'You are a helpful assistant' } + before do + allow(subject.llm).to receive(:chat).with(messages: [{role: 'system', content: system_prompt}, *messages]).and_return(response) + expect(response).to receive(:context=).with(text) + end + + it "asks a question and returns the answer" do + expect(subject.ask(question: question, k: 4, system_prompt: system_prompt).completion).to eq(answer) + end + end + context "with block" do let(:block) { proc { |chunk| puts "Received chunk: #{chunk}" } } diff --git a/spec/langchain/vectorsearch/epsilla_spec.rb b/spec/langchain/vectorsearch/epsilla_spec.rb index f02b1f88a..9348fcf15 100644 --- a/spec/langchain/vectorsearch/epsilla_spec.rb +++ b/spec/langchain/vectorsearch/epsilla_spec.rb @@ -236,6 +236,18 @@ end end + context 'with system prompt' do + let(:system_prompt) { 'You are a helpful assistant' } + before do + allow(subject.llm).to receive(:chat).with(messages: [{role: 'system', content: system_prompt}, *messages]).and_return(response) + expect(response).to receive(:context=).with(text) + end + + it "asks a question and returns the answer" do + expect(subject.ask(question: question, k: k, system_prompt: system_prompt).completion).to eq(answer) + end + end + context "with block" do let(:block) { proc { |chunk| puts "Received chunk: #{chunk}" } } diff --git a/spec/langchain/vectorsearch/milvus_spec.rb b/spec/langchain/vectorsearch/milvus_spec.rb index 0713930d5..9fd78a501 100644 --- a/spec/langchain/vectorsearch/milvus_spec.rb +++ b/spec/langchain/vectorsearch/milvus_spec.rb @@ -141,6 +141,18 @@ end end + context 'with system prompt' do + let(:system_prompt) { 'You are a helpful assistant' } + before do + allow(subject.llm).to receive(:chat).with(messages: [{role: 'system', content: system_prompt}, *messages]).and_return(response) + expect(response).to receive(:context=).with(text) + end + + it "asks a question and returns the answer" do + expect(subject.ask(question: question, system_prompt: system_prompt).completion).to eq(answer) + end + end + context "with block" do let(:block) { proc { |chunk| puts "Received chunk: #{chunk}" } } diff --git a/spec/langchain/vectorsearch/pgvector_spec.rb b/spec/langchain/vectorsearch/pgvector_spec.rb index 5b3d6a725..9237538e8 100644 --- a/spec/langchain/vectorsearch/pgvector_spec.rb +++ b/spec/langchain/vectorsearch/pgvector_spec.rb @@ -234,6 +234,17 @@ end end + context 'with system prompt' do + before do + allow(subject.llm).to receive(:chat).with(messages: messages).and_return(response) + expect(response).to receive(:context=).with(text) + end + + it "asks a question and returns the answer" do + expect(subject.ask(question: question, k: k, system_prompt: 'You are a helpful assistant').completion).to eq(answer) + end + end + context "with block" do let(:block) { proc { |chunk| puts "Received chunk: #{chunk}" } } diff --git a/spec/langchain/vectorsearch/pinecone_spec.rb b/spec/langchain/vectorsearch/pinecone_spec.rb index 4ef45205b..9f17d578c 100644 --- a/spec/langchain/vectorsearch/pinecone_spec.rb +++ b/spec/langchain/vectorsearch/pinecone_spec.rb @@ -410,6 +410,22 @@ end end + describe "with system prompt" do + let(:system_prompt) { 'You are a helpful assistant' } + + before do + allow(subject).to receive(:similarity_search).with( + query: question, namespace: "", filter: nil, k: k + ).and_return(matches) + allow(subject.llm).to receive(:chat).with(messages: [{role: 'system', content: system_prompt}, *messages]).and_return(response) + expect(response).to receive(:context=).with(metadata.to_s) + end + + it "asks a question" do + expect(subject.ask(question: question, system_prompt: system_prompt).completion).to eq(answer) + end + end + describe "with block" do let(:block) { proc { |chunk| puts "Received chunk: #{chunk}" } } diff --git a/spec/langchain/vectorsearch/qdrant_spec.rb b/spec/langchain/vectorsearch/qdrant_spec.rb index 68e25965d..737632e9a 100644 --- a/spec/langchain/vectorsearch/qdrant_spec.rb +++ b/spec/langchain/vectorsearch/qdrant_spec.rb @@ -157,6 +157,18 @@ end end + context 'with system prompt' do + let(:system_prompt) { 'You are a helpful assistant' } + before do + allow(subject.llm).to receive(:chat).with(messages: [{role: 'system', content: system_prompt}, *messages]).and_return(response) + expect(response).to receive(:context=).with(text) + end + + it "asks a question and returns the answer" do + expect(subject.ask(question: question, k: k, system_prompt: system_prompt).completion).to eq(answer) + end + end + context "with block" do let(:block) { proc { |chunk| puts "Received chunk: #{chunk}" } } diff --git a/spec/langchain/vectorsearch/weaviate_spec.rb b/spec/langchain/vectorsearch/weaviate_spec.rb index c09a74fb7..0467f0f4b 100644 --- a/spec/langchain/vectorsearch/weaviate_spec.rb +++ b/spec/langchain/vectorsearch/weaviate_spec.rb @@ -214,6 +214,18 @@ def stub(id) end end + context 'with system prompt' do + let(:system_prompt) { 'You are a helpful assistant' } + before do + allow(subject.llm).to receive(:chat).with(messages: [{role: 'system', content: system_prompt}, *messages]).and_return(response) + expect(response).to receive(:context=).with(matches[0]["content"]) + end + + it "asks a question and returns the answer" do + expect(subject.ask(question: question, k: k, system_prompt: system_prompt).completion).to eq(answer) + end + end + context "with block" do let(:block) { proc { |chunk| puts "Received chunk: #{chunk}" } }