From e404fd39dda34180626b916df69daf93054fb14a Mon Sep 17 00:00:00 2001 From: Harrison Chase Date: Mon, 18 Sep 2023 11:10:44 -0700 Subject: [PATCH] add anthropic page (#10666) --- .../integrations/platforms/anthropic.mdx | 165 ++++++++++++++++++ .../langchain/chat_models/anthropic.py | 4 + libs/langchain/langchain/llms/anthropic.py | 4 + 3 files changed, 173 insertions(+) create mode 100644 docs/extras/integrations/platforms/anthropic.mdx diff --git a/docs/extras/integrations/platforms/anthropic.mdx b/docs/extras/integrations/platforms/anthropic.mdx new file mode 100644 index 000000000..a2ff69b83 --- /dev/null +++ b/docs/extras/integrations/platforms/anthropic.mdx @@ -0,0 +1,165 @@ +# Anthropic + +All functionality related to Anthropic models. + +[Anthropic](https://www.anthropic.com/) is an AI safety and research company, and is the creator of Claude. +This page covers all integrations between Anthropic models and LangChain. + +## Prompting Overview + +Claude is chat-based model, meaning it is trained on conversation data. +However, it is a text based API, meaning it takes in single string. +It expects this string to be in a particular format. +This means that it is up the user to ensure that is the case. +LangChain provides several utilities and helper functions to make sure prompts that you write - +whether formatted as a string or as a list of messages - end up formatted correctly. + +Specifically, Claude is trained to fill in text for the Assistant role as part of an ongoing dialogue +between a human user (`Human:`) and an AI assistant (`Assistant:`). Prompts sent via the API must contain +`\n\nHuman:` and `\n\nAssistant:` as the signals of who's speaking. +The final turn must always be `\n\nAssistant:` - the input string cannot have `\n\nHuman:` as the final role. + +Because Claude is chat-based but accepts a string as input, it can be treated as either a LangChain `ChatModel` or `LLM`. +This means there are two wrappers in LangChain - `ChatAnthropic` and `Anthropic`. +It is generally recommended to use the `ChatAnthropic` wrapper, and format your prompts as `ChatMessage`s (we will show examples of this below). +This is because it keeps your prompt in a general format that you can easily then also use with other models (should you want to). +However, if you want more fine-grained control over the prompt, you can use the `Anthropic` wrapper - we will show and example of this as well. +The `Anthropic` wrapper however is deprecated, as all functionality can be achieved in a more generic way using `ChatAnthropic`. + +## Prompting Best Practices + +Anthropic models have several prompting best practices compared to OpenAI models. + +**No System Messages** + +Anthropic models are not trained on the concept of a "system message". +We have worked with the Anthropic team to handle them somewhat appropriately (a Human message with an `admin` tag) +but this is largely a hack and it is recommended that you do not use system messages. + +**AI Messages Can Continue** + +A completion from Claude is a continuation of the last text in the string which allows you further control over Claude's output. +For example, putting words in Claude's mouth in a prompt like this: + +`\n\nHuman: Tell me a joke about bears\n\nAssistant: What do you call a bear with no teeth?` + +This will return a completion like this `A gummy bear!` instead of a whole new assistant message with a different random bear joke. + + +## `ChatAnthropic` + +`ChatAnthropic` is a subclass of LangChain's `ChatModel`, meaning it works best with `ChatPromptTemplate`. +You can import this wrapper with the following code: + +``` +from langchain.chat_models import ChatAnthropic +model = ChatAnthropic() +``` + +When working with ChatModels, it is preferred that you design your prompts as `ChatPromptTemplate`s. +Here is an example below of doing that: + +``` +from langchain.prompts import ChatPromptTemplate + +prompt = ChatPromptTemplate.from_messages([ + ("system", "You are a helpful chatbot"), + ("human", "Tell me a joke about {topic}"), +]) +``` + +You can then use this in a chain as follows: + +``` +chain = prompt | model +chain.invoke({"topic": "bears"}) +``` + +How is the prompt actually being formatted under the hood? We can see that by running the following code + +``` +prompt_value = prompt.format_prompt(topic="bears") +model.convert_prompt(prompt_value) +``` + +This produces the following formatted string: + +``` +'\n\nHuman: You are a helpful chatbot\n\nHuman: Tell me a joke about bears\n\nAssistant:' +``` + +We can see that under the hood LangChain is representing `SystemMessage`s with `Human: ...`, +and is appending an assistant message to the end IF the last message is NOT already an assistant message. + +If you decide instead to use a normal PromptTemplate (one that just works on a single string) let's take a look at +what happens: + +``` +from langchain.prompts import PromptTemplate + +prompt = PromptTemplate.from_template("Tell me a joke about {topic}") +prompt_value = prompt.format_prompt(topic="bears") +model.convert_prompt(prompt_value) +``` + +This produces the following formatted string: + +``` +'\n\nHuman: Tell me a joke about bears\n\nAssistant:' +``` + +We can see that it automatically adds the Human and Assistant tags. +What is happening under the hood? +First: the string gets converted to a single human message. This happens generically (because we are using a subclass of `ChatModel`). +Then, similarly to the above example, an empty Assistant message is getting appended. +This is Anthropic specific. + +## [Deprecated] `Anthropic` + +This `Anthropic` wrapper is subclassed from `LLM`. +We can import it with: + +``` +from langchain.llms import Anthropic +model = Anthropic() +``` + +This model class is designed to work with normal PromptTemplates. An example of that is below: + +``` +prompt = PromptTemplate.from_template("Tell me a joke about {topic}") +chain = prompt | model +chain.invoke({"topic": "bears"}) +``` + +Let's see what is going on with the prompt templating under the hood! + +``` +prompt_value = prompt.format_prompt(topic="bears") +model.convert_prompt(prompt_value) +``` + +This outputs the following + +``` +'\n\nHuman: Tell me a joke about bears\n\nAssistant: Sure, here you go:\n' +``` + +Notice that it adds the Human tag at the start of the string, and then finishes it with `\n\nAssistant: Sure, here you go:`. +The extra `Sure, here you go` was added on purpose by the Anthropic team. + +What happens if we have those symbols in the prompt directly? + +``` +prompt = PromptTemplate.from_template("Human: Tell me a joke about {topic}") +prompt_value = prompt.format_prompt(topic="bears") +model.convert_prompt(prompt_value) +``` + +This outputs: + +``` +'\n\nHuman: Tell me a joke about bears' +``` + +We can see that we detect that the user is trying to use the special tokens, and so we don't do any formatting. diff --git a/libs/langchain/langchain/chat_models/anthropic.py b/libs/langchain/langchain/chat_models/anthropic.py index a944ab170..a5c04dde6 100644 --- a/libs/langchain/langchain/chat_models/anthropic.py +++ b/libs/langchain/langchain/chat_models/anthropic.py @@ -15,6 +15,7 @@ from langchain.schema.messages import ( SystemMessage, ) from langchain.schema.output import ChatGeneration, ChatGenerationChunk, ChatResult +from langchain.schema.prompt import PromptValue def _convert_one_message_to_text( @@ -112,6 +113,9 @@ class ChatAnthropic(BaseChatModel, _AnthropicCommon): prompt_params["ai_prompt"] = self.AI_PROMPT return convert_messages_to_prompt_anthropic(messages=messages, **prompt_params) + def convert_prompt(self, prompt: PromptValue) -> str: + return self._convert_messages_to_prompt(prompt.to_messages()) + def _stream( self, messages: List[BaseMessage], diff --git a/libs/langchain/langchain/llms/anthropic.py b/libs/langchain/langchain/llms/anthropic.py index f7ee0ab57..78de75091 100644 --- a/libs/langchain/langchain/llms/anthropic.py +++ b/libs/langchain/langchain/llms/anthropic.py @@ -10,6 +10,7 @@ from langchain.llms.base import LLM from langchain.pydantic_v1 import Field, root_validator from langchain.schema.language_model import BaseLanguageModel from langchain.schema.output import GenerationChunk +from langchain.schema.prompt import PromptValue from langchain.utils import ( check_package_version, get_from_dict_or_env, @@ -234,6 +235,9 @@ class Anthropic(LLM, _AnthropicCommon): ) return response.completion + def convert_prompt(self, prompt: PromptValue) -> str: + return self._wrap_prompt(prompt.to_string()) + async def _acall( self, prompt: str,