mirror of https://github.com/hwchase17/langchain
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
12 KiB
Python
346 lines
12 KiB
Python
"""Prompt template that contains few shot examples."""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List, Literal, Optional, Union
|
|
|
|
from langchain_core.messages import BaseMessage, get_buffer_string
|
|
from langchain_core.prompts.chat import (
|
|
BaseChatPromptTemplate,
|
|
BaseMessagePromptTemplate,
|
|
)
|
|
from langchain_core.prompts.prompt import PromptTemplate
|
|
from langchain_core.prompts.string import (
|
|
DEFAULT_FORMATTER_MAPPING,
|
|
StringPromptTemplate,
|
|
check_valid_template,
|
|
get_template_variables,
|
|
)
|
|
from langchain_core.pydantic_v1 import BaseModel, Extra, Field, root_validator
|
|
|
|
|
|
class _FewShotPromptTemplateMixin(BaseModel):
|
|
"""Prompt template that contains few shot examples."""
|
|
|
|
examples: Optional[List[dict]] = None
|
|
"""Examples to format into the prompt.
|
|
Either this or example_selector should be provided."""
|
|
|
|
example_selector: Any = None
|
|
"""ExampleSelector to choose the examples to format into the prompt.
|
|
Either this or examples should be provided."""
|
|
|
|
class Config:
|
|
"""Configuration for this pydantic object."""
|
|
|
|
extra = Extra.forbid
|
|
arbitrary_types_allowed = True
|
|
|
|
@root_validator(pre=True)
|
|
def check_examples_and_selector(cls, values: Dict) -> Dict:
|
|
"""Check that one and only one of examples/example_selector are provided."""
|
|
examples = values.get("examples", None)
|
|
example_selector = values.get("example_selector", None)
|
|
if examples and example_selector:
|
|
raise ValueError(
|
|
"Only one of 'examples' and 'example_selector' should be provided"
|
|
)
|
|
|
|
if examples is None and example_selector is None:
|
|
raise ValueError(
|
|
"One of 'examples' and 'example_selector' should be provided"
|
|
)
|
|
|
|
return values
|
|
|
|
def _get_examples(self, **kwargs: Any) -> List[dict]:
|
|
"""Get the examples to use for formatting the prompt.
|
|
|
|
Args:
|
|
**kwargs: Keyword arguments to be passed to the example selector.
|
|
|
|
Returns:
|
|
List of examples.
|
|
"""
|
|
if self.examples is not None:
|
|
return self.examples
|
|
elif self.example_selector is not None:
|
|
return self.example_selector.select_examples(kwargs)
|
|
else:
|
|
raise ValueError(
|
|
"One of 'examples' and 'example_selector' should be provided"
|
|
)
|
|
|
|
|
|
class FewShotPromptTemplate(_FewShotPromptTemplateMixin, StringPromptTemplate):
|
|
"""Prompt template that contains few shot examples."""
|
|
|
|
@classmethod
|
|
def is_lc_serializable(cls) -> bool:
|
|
"""Return whether or not the class is serializable."""
|
|
return False
|
|
|
|
validate_template: bool = False
|
|
"""Whether or not to try validating the template."""
|
|
|
|
input_variables: List[str]
|
|
"""A list of the names of the variables the prompt template expects."""
|
|
|
|
example_prompt: PromptTemplate
|
|
"""PromptTemplate used to format an individual example."""
|
|
|
|
suffix: str
|
|
"""A prompt template string to put after the examples."""
|
|
|
|
example_separator: str = "\n\n"
|
|
"""String separator used to join the prefix, the examples, and suffix."""
|
|
|
|
prefix: str = ""
|
|
"""A prompt template string to put before the examples."""
|
|
|
|
template_format: Literal["f-string", "jinja2"] = "f-string"
|
|
"""The format of the prompt template. Options are: 'f-string', 'jinja2'."""
|
|
|
|
@root_validator()
|
|
def template_is_valid(cls, values: Dict) -> Dict:
|
|
"""Check that prefix, suffix, and input variables are consistent."""
|
|
if values["validate_template"]:
|
|
check_valid_template(
|
|
values["prefix"] + values["suffix"],
|
|
values["template_format"],
|
|
values["input_variables"] + list(values["partial_variables"]),
|
|
)
|
|
elif values.get("template_format"):
|
|
values["input_variables"] = [
|
|
var
|
|
for var in get_template_variables(
|
|
values["prefix"] + values["suffix"], values["template_format"]
|
|
)
|
|
if var not in values["partial_variables"]
|
|
]
|
|
return values
|
|
|
|
class Config:
|
|
"""Configuration for this pydantic object."""
|
|
|
|
extra = Extra.forbid
|
|
arbitrary_types_allowed = True
|
|
|
|
def format(self, **kwargs: Any) -> str:
|
|
"""Format the prompt with the inputs.
|
|
|
|
Args:
|
|
**kwargs: Any arguments to be passed to the prompt template.
|
|
|
|
Returns:
|
|
A formatted string.
|
|
|
|
Example:
|
|
|
|
.. code-block:: python
|
|
|
|
prompt.format(variable1="foo")
|
|
"""
|
|
kwargs = self._merge_partial_and_user_variables(**kwargs)
|
|
# Get the examples to use.
|
|
examples = self._get_examples(**kwargs)
|
|
examples = [
|
|
{k: e[k] for k in self.example_prompt.input_variables} for e in examples
|
|
]
|
|
# Format the examples.
|
|
example_strings = [
|
|
self.example_prompt.format(**example) for example in examples
|
|
]
|
|
# Create the overall template.
|
|
pieces = [self.prefix, *example_strings, self.suffix]
|
|
template = self.example_separator.join([piece for piece in pieces if piece])
|
|
|
|
# Format the template with the input variables.
|
|
return DEFAULT_FORMATTER_MAPPING[self.template_format](template, **kwargs)
|
|
|
|
@property
|
|
def _prompt_type(self) -> str:
|
|
"""Return the prompt type key."""
|
|
return "few_shot"
|
|
|
|
def save(self, file_path: Union[Path, str]) -> None:
|
|
if self.example_selector:
|
|
raise ValueError("Saving an example selector is not currently supported")
|
|
return super().save(file_path)
|
|
|
|
|
|
class FewShotChatMessagePromptTemplate(
|
|
BaseChatPromptTemplate, _FewShotPromptTemplateMixin
|
|
):
|
|
"""Chat prompt template that supports few-shot examples.
|
|
|
|
The high level structure of produced by this prompt template is a list of messages
|
|
consisting of prefix message(s), example message(s), and suffix message(s).
|
|
|
|
This structure enables creating a conversation with intermediate examples like:
|
|
|
|
System: You are a helpful AI Assistant
|
|
Human: What is 2+2?
|
|
AI: 4
|
|
Human: What is 2+3?
|
|
AI: 5
|
|
Human: What is 4+4?
|
|
|
|
This prompt template can be used to generate a fixed list of examples or else
|
|
to dynamically select examples based on the input.
|
|
|
|
Examples:
|
|
|
|
Prompt template with a fixed list of examples (matching the sample
|
|
conversation above):
|
|
|
|
.. code-block:: python
|
|
|
|
from langchain_core.prompts import (
|
|
FewShotChatMessagePromptTemplate,
|
|
ChatPromptTemplate
|
|
)
|
|
|
|
examples = [
|
|
{"input": "2+2", "output": "4"},
|
|
{"input": "2+3", "output": "5"},
|
|
]
|
|
|
|
example_prompt = ChatPromptTemplate.from_messages(
|
|
[('human', '{input}'), ('ai', '{output}')]
|
|
)
|
|
|
|
few_shot_prompt = FewShotChatMessagePromptTemplate(
|
|
examples=examples,
|
|
# This is a prompt template used to format each individual example.
|
|
example_prompt=example_prompt,
|
|
)
|
|
|
|
final_prompt = ChatPromptTemplate.from_messages(
|
|
[
|
|
('system', 'You are a helpful AI Assistant'),
|
|
few_shot_prompt,
|
|
('human', '{input}'),
|
|
]
|
|
)
|
|
final_prompt.format(input="What is 4+4?")
|
|
|
|
Prompt template with dynamically selected examples:
|
|
|
|
.. code-block:: python
|
|
|
|
from langchain_core.prompts import SemanticSimilarityExampleSelector
|
|
from langchain_core.embeddings import OpenAIEmbeddings
|
|
from langchain_core.vectorstores import Chroma
|
|
|
|
examples = [
|
|
{"input": "2+2", "output": "4"},
|
|
{"input": "2+3", "output": "5"},
|
|
{"input": "2+4", "output": "6"},
|
|
# ...
|
|
]
|
|
|
|
to_vectorize = [
|
|
" ".join(example.values())
|
|
for example in examples
|
|
]
|
|
embeddings = OpenAIEmbeddings()
|
|
vectorstore = Chroma.from_texts(
|
|
to_vectorize, embeddings, metadatas=examples
|
|
)
|
|
example_selector = SemanticSimilarityExampleSelector(
|
|
vectorstore=vectorstore
|
|
)
|
|
|
|
from langchain_core import SystemMessage
|
|
from langchain_core.prompts import HumanMessagePromptTemplate
|
|
from langchain_core.prompts.few_shot import FewShotChatMessagePromptTemplate
|
|
|
|
few_shot_prompt = FewShotChatMessagePromptTemplate(
|
|
# Which variable(s) will be passed to the example selector.
|
|
input_variables=["input"],
|
|
example_selector=example_selector,
|
|
# Define how each example will be formatted.
|
|
# In this case, each example will become 2 messages:
|
|
# 1 human, and 1 AI
|
|
example_prompt=(
|
|
HumanMessagePromptTemplate.from_template("{input}")
|
|
+ AIMessagePromptTemplate.from_template("{output}")
|
|
),
|
|
)
|
|
# Define the overall prompt.
|
|
final_prompt = (
|
|
SystemMessagePromptTemplate.from_template(
|
|
"You are a helpful AI Assistant"
|
|
)
|
|
+ few_shot_prompt
|
|
+ HumanMessagePromptTemplate.from_template("{input}")
|
|
)
|
|
# Show the prompt
|
|
print(final_prompt.format_messages(input="What's 3+3?"))
|
|
|
|
# Use within an LLM
|
|
from langchain_core.chat_models import ChatAnthropic
|
|
chain = final_prompt | ChatAnthropic()
|
|
chain.invoke({"input": "What's 3+3?"})
|
|
"""
|
|
|
|
@classmethod
|
|
def is_lc_serializable(cls) -> bool:
|
|
"""Return whether or not the class is serializable."""
|
|
return False
|
|
|
|
input_variables: List[str] = Field(default_factory=list)
|
|
"""A list of the names of the variables the prompt template will use
|
|
to pass to the example_selector, if provided."""
|
|
example_prompt: Union[BaseMessagePromptTemplate, BaseChatPromptTemplate]
|
|
"""The class to format each example."""
|
|
|
|
class Config:
|
|
"""Configuration for this pydantic object."""
|
|
|
|
extra = Extra.forbid
|
|
arbitrary_types_allowed = True
|
|
|
|
def format_messages(self, **kwargs: Any) -> List[BaseMessage]:
|
|
"""Format kwargs into a list of messages.
|
|
|
|
Args:
|
|
**kwargs: keyword arguments to use for filling in templates in messages.
|
|
|
|
Returns:
|
|
A list of formatted messages with all template variables filled in.
|
|
"""
|
|
# Get the examples to use.
|
|
examples = self._get_examples(**kwargs)
|
|
examples = [
|
|
{k: e[k] for k in self.example_prompt.input_variables} for e in examples
|
|
]
|
|
# Format the examples.
|
|
messages = [
|
|
message
|
|
for example in examples
|
|
for message in self.example_prompt.format_messages(**example)
|
|
]
|
|
return messages
|
|
|
|
def format(self, **kwargs: Any) -> str:
|
|
"""Format the prompt with inputs generating a string.
|
|
|
|
Use this method to generate a string representation of a prompt consisting
|
|
of chat messages.
|
|
|
|
Useful for feeding into a string based completion language model or debugging.
|
|
|
|
Args:
|
|
**kwargs: keyword arguments to use for formatting.
|
|
|
|
Returns:
|
|
A string representation of the prompt
|
|
"""
|
|
messages = self.format_messages(**kwargs)
|
|
return get_buffer_string(messages)
|
|
|
|
def pretty_repr(self, html: bool = False) -> str:
|
|
raise NotImplementedError()
|