Skip to content

Commit

Permalink
anthropic[minor]: add tool calling (langchain-ai#18554)
Browse files Browse the repository at this point in the history
  • Loading branch information
efriis authored and gkorland committed Mar 30, 2024
1 parent 2c0c823 commit 62be8e7
Show file tree
Hide file tree
Showing 8 changed files with 486 additions and 193 deletions.
205 changes: 33 additions & 172 deletions docs/docs/integrations/chat/anthropic_functions.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
"id": "5125a1e3",
"metadata": {},
"source": [
"# Anthropic Functions\n",
"# Anthropic Tools\n",
"\n",
"This notebook shows how to use an experimental wrapper around Anthropic that gives it the same API as OpenAI Functions."
"This notebook shows how to use an experimental wrapper around Anthropic that gives it tool calling and structured output capabilities. It follows Anthropic's guide [here](https://docs.anthropic.com/claude/docs/functions-external-tools)\n",
"\n",
"The wrapper is available from the `langchain-anthropic` package, and it also requires the optional dependency `defusedxml` for parsing XML output from the llm.\n",
"\n",
"Note: this is a beta feature that will be replaced by Anthropic's formal implementation of tool calling, but it is useful for testing and experimentation in the meantime."
]
},
{
Expand All @@ -17,225 +21,82 @@
"metadata": {},
"outputs": [],
"source": [
"from langchain_experimental.llms.anthropic_functions import AnthropicFunctions"
"%pip install -qU langchain-anthropic defusedxml\n",
"from langchain_anthropic.experimental import ChatAnthropicTools"
]
},
{
"cell_type": "markdown",
"id": "65499965",
"metadata": {},
"source": [
"## Initialize Model\n",
"\n",
"You can initialize this wrapper the same way you'd initialize ChatAnthropic"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e1d535f6",
"metadata": {},
"outputs": [],
"source": [
"model = AnthropicFunctions(model=\"claude-2\")"
]
},
{
"cell_type": "markdown",
"id": "fcc9eaf4",
"metadata": {},
"source": [
"## Passing in functions\n",
"## Tool Binding\n",
"\n",
"You can now pass in functions in a similar way"
"`ChatAnthropicTools` exposes a `bind_tools` method that allows you to pass in Pydantic models or BaseTools to the llm."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "0779c320",
"metadata": {},
"outputs": [],
"source": [
"functions = [\n",
" {\n",
" \"name\": \"get_current_weather\",\n",
" \"description\": \"Get the current weather in a given location\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"location\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"The city and state, e.g. San Francisco, CA\",\n",
" },\n",
" \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]},\n",
" },\n",
" \"required\": [\"location\"],\n",
" },\n",
" }\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "ad75a933",
"metadata": {},
"outputs": [],
"source": [
"from langchain_core.messages import HumanMessage"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "fc703085",
"metadata": {},
"outputs": [],
"source": [
"response = model.invoke(\n",
" [HumanMessage(content=\"whats the weater in boston?\")], functions=functions\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "04d7936a",
"id": "e1d535f6",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"AIMessage(content=' ', additional_kwargs={'function_call': {'name': 'get_current_weather', 'arguments': '{\"location\": \"Boston, MA\", \"unit\": \"fahrenheit\"}'}}, example=False)"
"AIMessage(content='', additional_kwargs={'tool_calls': [{'function': {'name': 'Person', 'arguments': '{\"name\": \"Erick\", \"age\": \"27\"}'}, 'type': 'function'}]})"
]
},
"execution_count": 7,
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"response"
]
},
{
"cell_type": "markdown",
"id": "0072fdba",
"metadata": {},
"source": [
"## Using for extraction\n",
"from langchain_core.pydantic_v1 import BaseModel\n",
"\n",
"You can now use this for extraction."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "7af5c567",
"metadata": {},
"outputs": [],
"source": [
"from langchain.chains import create_extraction_chain\n",
"\n",
"schema = {\n",
" \"properties\": {\n",
" \"name\": {\"type\": \"string\"},\n",
" \"height\": {\"type\": \"integer\"},\n",
" \"hair_color\": {\"type\": \"string\"},\n",
" },\n",
" \"required\": [\"name\", \"height\"],\n",
"}\n",
"inp = \"\"\"\n",
"Alex is 5 feet tall. Claudia is 1 feet taller Alex and jumps higher than him. Claudia is a brunette and Alex is blonde.\n",
" \"\"\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bd01082a",
"metadata": {},
"outputs": [],
"source": [
"chain = create_extraction_chain(schema, model)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b5a23e9f",
"metadata": {},
"outputs": [],
"source": [
"chain.invoke(inp)"
"class Person(BaseModel):\n",
" name: str\n",
" age: int\n",
"\n",
"\n",
"model = ChatAnthropicTools(model=\"claude-3-opus-20240229\").bind_tools(tools=[Person])\n",
"model.invoke(\"I am a 27 year old named Erick\")"
]
},
{
"cell_type": "markdown",
"id": "90ec959e",
"id": "fcc9eaf4",
"metadata": {},
"source": [
"## Using for tagging\n",
"## Structured Output\n",
"\n",
"You can now use this for tagging"
"`ChatAnthropicTools` also implements the [`with_structured_output` spec](/docs/guides/structured_output) for extracting values. Note: this may not be as stable as with models that explicitly offer tool calling."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "03c1eb0d",
"metadata": {},
"outputs": [],
"source": [
"from langchain.chains import create_tagging_chain"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "581c0ece",
"metadata": {},
"outputs": [],
"source": [
"schema = {\n",
" \"properties\": {\n",
" \"sentiment\": {\"type\": \"string\"},\n",
" \"aggressiveness\": {\"type\": \"integer\"},\n",
" \"language\": {\"type\": \"string\"},\n",
" }\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "d9a8570e",
"metadata": {},
"outputs": [],
"source": [
"chain = create_tagging_chain(schema, model)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "cf37d679",
"execution_count": 4,
"id": "0779c320",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'sentiment': 'positive', 'aggressiveness': '0', 'language': 'english'}"
"Person(name='Erick', age=27)"
]
},
"execution_count": 15,
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"chain.invoke(\"this is really cool\")"
"chain = ChatAnthropicTools(model=\"claude-3-opus-20240229\").with_structured_output(\n",
" Person\n",
")\n",
"chain.invoke(\"I am a 27 year old named Erick\")"
]
}
],
Expand All @@ -255,7 +116,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.0"
"version": "3.11.4"
}
},
"nbformat": 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
ChatResult,
)
from langchain_community.chat_models.anthropic import ChatAnthropic
from langchain_core._api.deprecation import deprecated
from langchain_core.language_models import BaseChatModel
from langchain_core.messages import (
AIMessage,
Expand Down Expand Up @@ -123,6 +124,11 @@ def _destrip(tool_input: Any) -> Any:
raise ValueError


@deprecated(
since="0.0.54",
removal="0.2",
alternative_import="langchain_anthropic.experimental.ChatAnthropicTools",
)
class AnthropicFunctions(BaseChatModel):
"""Chat model for interacting with Anthropic functions."""

Expand Down
23 changes: 16 additions & 7 deletions libs/partners/anthropic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,17 @@ This package contains the LangChain integration for Anthropic's generative model

## Chat Models

| API Model Name | Model Family |
| ------------------ | -------------- |
| claude-instant-1.2 | Claude Instant |
| claude-2.1 | Claude |
| claude-2.0 | Claude |
Anthropic recommends using their chat models over text completions.

You can see their recommended models [here](https://docs.anthropic.com/claude/docs/models-overview#model-recommendations).

To use, you should have an Anthropic API key configured. Initialize the model as:

```
from langchain_anthropic import ChatAnthropicMessages
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import AIMessage, HumanMessage
model = ChatAnthropicMessages(model="claude-2.1", temperature=0, max_tokens=1024)
model = ChatAnthropic(model="claude-3-opus-20240229", temperature=0, max_tokens=1024)
```

### Define the input message
Expand All @@ -32,3 +30,14 @@ model = ChatAnthropicMessages(model="claude-2.1", temperature=0, max_tokens=1024
`response = model.invoke([message])`

For a more detailed walkthrough see [here](https://python.langchain.com/docs/integrations/chat/anthropic).

## LLMs (Legacy)

You can use the Claude 2 models for text completions.

```python
from langchain_anthropic import AnthropicLLM

model = AnthropicLLM(model="claude-2.1", temperature=0, max_tokens=1024)
response = model.invoke("The best restaurant in San Francisco is: ")
```
22 changes: 10 additions & 12 deletions libs/partners/anthropic/langchain_anthropic/chat_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,14 @@ async def _astream(
await run_manager.on_llm_new_token(text, chunk=chunk)
yield chunk

def _format_output(self, data: Any) -> ChatResult:
return ChatResult(
generations=[
ChatGeneration(message=AIMessage(content=data.content[0].text))
],
llm_output=data,
)

def _generate(
self,
messages: List[BaseMessage],
Expand All @@ -265,12 +273,7 @@ def _generate(
) -> ChatResult:
params = self._format_params(messages=messages, stop=stop, **kwargs)
data = self._client.messages.create(**params)
return ChatResult(
generations=[
ChatGeneration(message=AIMessage(content=data.content[0].text))
],
llm_output=data,
)
return self._format_output(data, **kwargs)

async def _agenerate(
self,
Expand All @@ -281,12 +284,7 @@ async def _agenerate(
) -> ChatResult:
params = self._format_params(messages=messages, stop=stop, **kwargs)
data = await self._async_client.messages.create(**params)
return ChatResult(
generations=[
ChatGeneration(message=AIMessage(content=data.content[0].text))
],
llm_output=data,
)
return self._format_output(data, **kwargs)


@deprecated(since="0.1.0", removal="0.2.0", alternative="ChatAnthropic")
Expand Down

0 comments on commit 62be8e7

Please sign in to comment.