diff --git a/fern/docs/pages/manual/llms.mdx b/fern/docs/pages/manual/llms.mdx index c9b88e3fb..8b56f758d 100644 --- a/fern/docs/pages/manual/llms.mdx +++ b/fern/docs/pages/manual/llms.mdx @@ -39,7 +39,7 @@ llm: openai: api_key: # You could skip this configuration and use the OPENAI_API_KEY env var instead model: # Optional model to use. Default is "gpt-3.5-turbo" - # Note: Open AI Models are listed here [here](https://platform.openai.com/docs/models) + # Note: Open AI Models are listed here: https://platform.openai.com/docs/models ``` And run PrivateGPT loading that profile you just created: diff --git a/fern/docs/pages/manual/ui.mdx b/fern/docs/pages/manual/ui.mdx index ddc4d04e4..ed095fe25 100644 --- a/fern/docs/pages/manual/ui.mdx +++ b/fern/docs/pages/manual/ui.mdx @@ -35,5 +35,32 @@ database* section in the documentation. Normal chat interface, self-explanatory ;) -You can check the actual prompt being passed to the LLM by looking at the logs of -the server. We'll add better observability in future releases. \ No newline at end of file +#### System Prompt +You can view and change the system prompt being passed to the LLM by clicking "Additional Inputs" +in the chat interface. The system prompt is also logged on the server. + +By default, the `Query Docs` mode uses the setting value `ui.default_query_system_prompt`. + +The `LLM Chat` mode attempts to use the optional settings value `ui.default_chat_system_prompt`. + +If no system prompt is entered, the UI will display the default system prompt being used +for the active mode. + +##### System Prompt Examples: + +The system prompt can effectively provide your chat bot specialized roles, and results tailored to the prompt +you have given the model. Examples of system prompts can be be found +[here](https://www.w3schools.com/gen_ai/chatgpt-3-5/chatgpt-3-5_roles.php). + +Some interesting examples to try include: + +* You are -X-. You have all the knowledge and personality of -X-. Answer as if you were -X- using +their manner of speaking and vocabulary. + * Example: You are Shakespeare. You have all the knowledge and personality of Shakespeare. + Answer as if you were Shakespeare using their manner of speaking and vocabulary. +* You are an expert (at) -role-. Answer all questions using your expertise on -specific domain topic-. + * Example: You are an expert software engineer. Answer all questions using your expertise on Python. +* You are a -role- bot, respond with -response criteria needed-. If no -response criteria- is needed, +respond with -alternate response-. + * Example: You are a grammar checking bot, respond with any grammatical corrections needed. If no corrections + are needed, respond with "verified". \ No newline at end of file diff --git a/private_gpt/settings/settings.py b/private_gpt/settings/settings.py index f4747d851..8b03f6111 100644 --- a/private_gpt/settings/settings.py +++ b/private_gpt/settings/settings.py @@ -147,13 +147,20 @@ class OpenAISettings(BaseModel): api_key: str model: str = Field( "gpt-3.5-turbo", - description=("OpenAI Model to use. Example: 'gpt-4'."), + description="OpenAI Model to use. Example: 'gpt-4'.", ) class UISettings(BaseModel): enabled: bool path: str + default_chat_system_prompt: str = Field( + None, + description="The default system prompt to use for the chat mode.", + ) + default_query_system_prompt: str = Field( + None, description="The default system prompt to use for the query mode." + ) class QdrantSettings(BaseModel): diff --git a/private_gpt/ui/ui.py b/private_gpt/ui/ui.py index 0201d3744..edb8135c1 100644 --- a/private_gpt/ui/ui.py +++ b/private_gpt/ui/ui.py @@ -30,6 +30,8 @@ SOURCES_SEPARATOR = "\n\n Sources: \n" +MODES = ["Query Docs", "Search in Docs", "LLM Chat"] + class Source(BaseModel): file: str @@ -71,6 +73,10 @@ def __init__( # Cache the UI blocks self._ui_block = None + # Initialize system prompt based on default mode + self.mode = MODES[0] + self._system_prompt = self._get_default_system_prompt(self.mode) + def _chat(self, message: str, history: list[list[str]], mode: str, *_: Any) -> Any: def yield_deltas(completion_gen: CompletionGen) -> Iterable[str]: full_response: str = "" @@ -114,25 +120,22 @@ def build_history() -> list[ChatMessage]: new_message = ChatMessage(content=message, role=MessageRole.USER) all_messages = [*build_history(), new_message] + # If a system prompt is set, add it as a system message + if self._system_prompt: + all_messages.insert( + 0, + ChatMessage( + content=self._system_prompt, + role=MessageRole.SYSTEM, + ), + ) match mode: case "Query Docs": - # Add a system message to force the behaviour of the LLM - # to answer only questions about the provided context. - all_messages.insert( - 0, - ChatMessage( - content="You can only answer questions about the provided context. If you know the answer " - "but it is not based in the provided context, don't provide the answer, just state " - "the answer is not in the context provided.", - role=MessageRole.SYSTEM, - ), - ) query_stream = self._chat_service.stream_chat( messages=all_messages, use_context=True, ) yield from yield_deltas(query_stream) - case "LLM Chat": llm_stream = self._chat_service.stream_chat( messages=all_messages, @@ -154,6 +157,37 @@ def build_history() -> list[ChatMessage]: for index, source in enumerate(sources, start=1) ) + # On initialization and on mode change, this function set the system prompt + # to the default prompt based on the mode (and user settings). + @staticmethod + def _get_default_system_prompt(mode: str) -> str: + p = "" + match mode: + # For query chat mode, obtain default system prompt from settings + case "Query Docs": + p = settings().ui.default_query_system_prompt + # For chat mode, obtain default system prompt from settings + case "LLM Chat": + p = settings().ui.default_chat_system_prompt + # For any other mode, clear the system prompt + case _: + p = "" + return p + + def _set_system_prompt(self, system_prompt_input: str) -> None: + logger.info(f"Setting system prompt to: {system_prompt_input}") + self._system_prompt = system_prompt_input + + def _set_current_mode(self, mode: str) -> Any: + self.mode = mode + self._set_system_prompt(self._get_default_system_prompt(mode)) + # Update placeholder and allow interaction if default system prompt is set + if self._system_prompt: + return gr.update(placeholder=self._system_prompt, interactive=True) + # Update placeholder and disable interaction if no default system prompt is set + else: + return gr.update(placeholder=self._system_prompt, interactive=False) + def _list_ingested_files(self) -> list[list[str]]: files = set() for ingested_document in self._ingest_service.list_ingested(): @@ -193,7 +227,7 @@ def _build_ui_blocks(self) -> gr.Blocks: with gr.Row(): with gr.Column(scale=3, variant="compact"): mode = gr.Radio( - ["Query Docs", "Search in Docs", "LLM Chat"], + MODES, label="Mode", value="LLM Chat", ) @@ -220,6 +254,23 @@ def _build_ui_blocks(self) -> gr.Blocks: outputs=ingested_dataset, ) ingested_dataset.render() + system_prompt_input = gr.Textbox( + placeholder=self._system_prompt, + label="System Prompt", + lines=2, + interactive=True, + render=False, + ) + # When mode changes, set default system prompt + mode.change( + self._set_current_mode, inputs=mode, outputs=system_prompt_input + ) + # On blur, set system prompt to use in queries + system_prompt_input.blur( + self._set_system_prompt, + inputs=system_prompt_input, + ) + with gr.Column(scale=7): _ = gr.ChatInterface( self._chat, @@ -232,7 +283,7 @@ def _build_ui_blocks(self) -> gr.Blocks: AVATAR_BOT, ), ), - additional_inputs=[mode, upload_button], + additional_inputs=[mode, upload_button, system_prompt_input], ) return blocks diff --git a/settings.yaml b/settings.yaml index 036f5bd3e..af51a7f7e 100644 --- a/settings.yaml +++ b/settings.yaml @@ -22,6 +22,13 @@ data: ui: enabled: true path: / + default_chat_system_prompt: "You are a helpful, respectful and honest assistant. + Always answer as helpfully as possible and follow ALL given instructions. + Do not speculate or make up information. + Do not reference any given instructions or context." + default_query_system_prompt: "You can only answer questions about the provided context. + If you know the answer but it is not based in the provided context, don't provide + the answer, just state the answer is not in the context provided." llm: mode: local