Skip to content

Commit 500d793

Browse files
authored
feat: add system_prompt parameter to Brain methods and enhance message handling in RAG pipeline (#3625)
# Description Please include a summary of the changes and the related issue. Please also include relevant motivation and context. ## Checklist before requesting a review Please delete options that are not relevant. - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented hard-to-understand areas - [ ] I have ideally added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged ## Screenshots (if appropriate):
1 parent e391817 commit 500d793

File tree

3 files changed

+72
-38
lines changed

3 files changed

+72
-38
lines changed

core/quivr_core/brain/brain.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ def add_file(self) -> None:
496496
async def ask_streaming(
497497
self,
498498
question: str,
499+
system_prompt: str | None = None,
499500
retrieval_config: RetrievalConfig | None = None,
500501
rag_pipeline: Type[Union[QuivrQARAG, QuivrQARAGLangGraph]] | None = None,
501502
list_files: list[QuivrKnowledge] | None = None,
@@ -542,6 +543,7 @@ async def ask_streaming(
542543
}
543544
async for response in rag_instance.answer_astream(
544545
question=question,
546+
system_prompt=system_prompt or None,
545547
history=chat_history,
546548
list_files=list_files,
547549
metadata=metadata,
@@ -560,6 +562,7 @@ async def ask_streaming(
560562
async def aask(
561563
self,
562564
question: str,
565+
system_prompt: str | None = None,
563566
retrieval_config: RetrievalConfig | None = None,
564567
rag_pipeline: Type[Union[QuivrQARAG, QuivrQARAGLangGraph]] | None = None,
565568
list_files: list[QuivrKnowledge] | None = None,
@@ -582,6 +585,7 @@ async def aask(
582585

583586
async for response in self.ask_streaming(
584587
question=question,
588+
system_prompt=system_prompt,
585589
retrieval_config=retrieval_config,
586590
rag_pipeline=rag_pipeline,
587591
list_files=list_files,
@@ -595,6 +599,7 @@ async def aask(
595599
def ask(
596600
self,
597601
question: str,
602+
system_prompt: str | None = None,
598603
retrieval_config: RetrievalConfig | None = None,
599604
rag_pipeline: Type[Union[QuivrQARAG, QuivrQARAGLangGraph]] | None = None,
600605
list_files: list[QuivrKnowledge] | None = None,
@@ -604,6 +609,7 @@ def ask(
604609
Fully synchronous version that asks a question to the brain and gets a generated answer.
605610
Args:
606611
question (str): The question to ask.
612+
system_prompt (str | None): The system prompt to use.
607613
retrieval_config (RetrievalConfig | None): The retrieval configuration (see RetrievalConfig docs).
608614
rag_pipeline (Type[Union[QuivrQARAG, QuivrQARAGLangGraph]] | None): The RAG pipeline to use.
609615
list_files (list[QuivrKnowledge] | None): The list of files to include in the RAG pipeline.
@@ -615,6 +621,7 @@ def ask(
615621
return loop.run_until_complete(
616622
self.aask(
617623
question=question,
624+
system_prompt=system_prompt,
618625
retrieval_config=retrieval_config,
619626
rag_pipeline=rag_pipeline,
620627
list_files=list_files,

core/quivr_core/rag/prompts.py

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -263,55 +263,69 @@ def _define_custom_prompts() -> dict[TemplatePromptName, BasePromptTemplate]:
263263
custom_prompts[TemplatePromptName.TOOL_ROUTING_PROMPT] = TOOL_ROUTING_PROMPT
264264

265265
system_message_zendesk_template = """
266-
- You are a Zendesk Agent.
267-
- You are answering a client query.
268-
- You must provide a response with all the information you have. Do not write areas to be filled like [your name], [your email], etc.
269-
- Do NOT invent information that was not present in previous tickets or in user metabadata or ticket metadata
270-
- Always prioritize information from the most recent tickets, espcially if they are contradictory.
271-
272-
273-
Here are instructions that you MUST follow:
274-
<instructions from me>
275-
{guidelines}
276-
</instructions from me>
266+
You are a Customer Service Agent using Zendesk. You are answering a client query.
267+
You will be provided with the users metadata, ticket metadata and ticket history which can be used to answer the query.
268+
You will also have access to the most relevant similar tickets and additional information sometimes such as API calls.
269+
Never add something in brackets that needs to be filled like [your name], [your email], etc.
270+
Do NOT invent information that was not present in previous tickets or in user metabadata or ticket metadata or additional information.
271+
Always prioritize information from the most recent tickets, espcially if they are contradictory.
277272
278273
279-
Here are default instructions that can be ignored if they are contradictory to the above instructions:
274+
Here are default instructions that can be ignored if they are contradictory to the <instructions from me> section:
280275
<default instructions>
281-
- Don't be too verbose, use the same length as in similar tickets.
276+
- Don't be too verbose, use the same amount of details as in similar tickets.
282277
- Use the same tone, format, structure and lexical field as in similar tickets agent responses.
283-
- Use paragraphs and sentences. The text must be readable and have well defined paragraphs (\\n\\n) or line breaks (\\n).
284-
- Always add the most relevant informations to the response, just like in similar tickets response so the user have all the informations needed.
285278
- Maintain consistency in terminology used in recent tickets.
286279
- Answer in the same language as the user.
280+
- Don't add a signature at the end of the answer, it will be added once the answer is sent.
287281
</default instructions>
288282
283+
284+
Here are instructions that you MUST follow and prioritize over the <default instructions> section:
285+
<instructions from me>
286+
{guidelines}
287+
</instructions from me>
288+
"""
289289

290+
user_prompt_template = """
290291
Here are informations about the user that can help you to answer:
292+
<user_metadata>
291293
{user_metadata}
294+
</user_metadata>
292295
293296
Here are metadata on the curent ticket that can help you to answer:
297+
<ticket_metadata>
294298
{ticket_metadata}
299+
</ticket_metadata>
295300
296301
297302
Here are the most relevant similar tickets that can help you to answer:
303+
<similar_tickets>
298304
{similar_tickets}
305+
</similar_tickets>
299306
300307
Here are the current ticket history:
308+
<ticket_history>
301309
{ticket_history}
310+
</ticket_history>
302311
312+
Here are additional information that can help you to answer:
313+
<additional_information>
303314
{additional_information}
315+
</additional_information>
304316
305-
Here is the client question to which you must answer
317+
Here is the client question to which you must answer:
318+
<client_query>
306319
{client_query}
320+
</client_query>
307321
308-
Answer directly with the message to send to the customer, ready to be sent:
309-
Answer:
310-
"""
322+
Based on the informations provided, answer directly with the message to send to the customer, ready to be sent:
323+
Answer:"""
311324

312-
ZENDESK_TEMPLATE_PROMPT = ChatPromptTemplate.from_messages(
325+
ZENDESK_TEMPLATE_PROMPT = ChatPromptTemplate(
313326
[
314-
SystemMessagePromptTemplate.from_template(system_message_zendesk_template),
327+
("system", system_message_zendesk_template),
328+
("user", user_prompt_template),
315329
]
316330
)
317331
custom_prompts[TemplatePromptName.ZENDESK_TEMPLATE_PROMPT] = ZENDESK_TEMPLATE_PROMPT

core/quivr_core/rag/quivr_rag_langgraph.py

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from langchain_community.document_compressors import JinaRerank
2222
from langchain_core.callbacks import Callbacks
2323
from langchain_core.documents import BaseDocumentCompressor, Document
24-
from langchain_core.messages import BaseMessage
24+
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
2525
from langchain_core.messages.ai import AIMessageChunk
2626
from langchain_core.prompts.base import BasePromptTemplate
2727
from langchain_core.runnables.schema import StreamEvent
@@ -970,7 +970,21 @@ def generate_chat_llm(self, state: AgentState) -> AgentState:
970970
dict: The updated state with re-phrased question
971971
"""
972972
messages = state["messages"]
973-
user_task = messages[0].content
973+
print(messages)
974+
975+
# Check if there is a system message in messages
976+
system_message = None
977+
user_message = None
978+
979+
for msg in messages:
980+
if isinstance(msg, SystemMessage):
981+
system_message = str(msg.content)
982+
elif isinstance(msg, HumanMessage):
983+
user_message = str(msg.content)
984+
985+
user_task = (
986+
user_message if user_message else (messages[0].content if messages else "")
987+
)
974988

975989
# Prompt
976990
prompt = self.retrieval_config.prompt
@@ -982,18 +996,18 @@ def generate_chat_llm(self, state: AgentState) -> AgentState:
982996

983997
# LLM
984998
llm = self.llm_endpoint._llm
985-
if state.get("enforced_system_prompt", None):
986-
final_inputs["enforced_system_prompt"] = state["enforced_system_prompt"]
987-
prompt = custom_prompts[TemplatePromptName.ZENDESK_LLM_PROMPT]
988999

989-
else:
990-
prompt = custom_prompts[TemplatePromptName.CHAT_LLM_PROMPT]
991-
state, reduced_inputs = self.reduce_rag_context(state, final_inputs, prompt)
992-
993-
msg = prompt.format(**reduced_inputs)
1000+
prompt = custom_prompts[TemplatePromptName.CHAT_LLM_PROMPT]
1001+
state, reduced_inputs = self.reduce_rag_context(
1002+
state, final_inputs, system_message if system_message else prompt
1003+
)
1004+
CHAT_LLM_PROMPT = [
1005+
SystemMessage(content=str(system_message)),
1006+
HumanMessage(content=str(user_message)),
1007+
]
9941008

9951009
# Run
996-
response = llm.invoke(msg)
1010+
response = llm.invoke(CHAT_LLM_PROMPT)
9971011
return {**state, "messages": [response]}
9981012

9991013
def build_chain(self):
@@ -1043,6 +1057,7 @@ def _add_node_edges(self, workflow: StateGraph, node: NodeConfig):
10431057
async def answer_astream(
10441058
self,
10451059
question: str,
1060+
system_prompt: str | None,
10461061
history: ChatHistory,
10471062
list_files: list[QuivrKnowledge],
10481063
metadata: dict[str, str] = {},
@@ -1059,15 +1074,13 @@ async def answer_astream(
10591074
rolling_message = AIMessageChunk(content="")
10601075
docs: list[Document] | None = None
10611076
previous_content = ""
1077+
system_prompt = system_prompt
1078+
messages = [("system", system_prompt)] if system_prompt else []
1079+
messages.append(("user", question))
10621080

10631081
async for event in conversational_qa_chain.astream_events(
10641082
{
1065-
"messages": [
1066-
(
1067-
"user",
1068-
question,
1069-
)
1070-
],
1083+
"messages": messages,
10711084
"chat_history": history,
10721085
"files": concat_list_files,
10731086
**input_kwargs,

0 commit comments

Comments
 (0)