Compare commits

..

45 Commits

Author SHA1 Message Date
Bagatur
25b1d65305 bump 315 (#11850) 2023-10-16 00:50:54 -07:00
Bagatur
ece22b6b6a Add LCEL to LLM intro (#11835) 2023-10-15 14:59:45 -07:00
Bagatur
ffa1b3a758 Add LCEL to chat model intro (#11834) 2023-10-15 14:59:36 -07:00
Nuno Campos
4321d192ea Use a less specific return type for | on Runnables (#11762)
<!-- Thank you for contributing to LangChain!

Replace this entire comment with:
  - **Description:** a description of the change, 
  - **Issue:** the issue # it fixes (if applicable),
  - **Dependencies:** any dependencies required for this change,
- **Tag maintainer:** for a quicker response, tag the relevant
maintainer (see below),
- **Twitter handle:** we announce bigger features on Twitter. If your PR
gets announced, and you'd like a mention, we'll gladly shout you out!

Please make sure your PR is passing linting and testing before
submitting. Run `make format`, `make lint` and `make test` to check this
locally.

See contribution guidelines for more information on how to write/run
tests, lint, etc:

https://github.com/langchain-ai/langchain/blob/master/.github/CONTRIBUTING.md

If you're adding a new integration, please include:
1. a test for the integration, preferably unit tests that do not rely on
network access,
2. an example notebook showing its use. It lives in `docs/extras`
directory.

If no one reviews your PR within a few days, please @-mention one of
@baskaryan, @eyurtsev, @hwchase17.
 -->

---------

Co-authored-by: Bagatur <baskaryan@gmail.com>
2023-10-15 21:15:06 +01:00
Bagatur
6c5bb1b2e1 RM snippets (#11798) 2023-10-15 12:20:58 -07:00
Lance Martin
ccd1400423 Update multi-modal notebooks (#11827) 2023-10-15 09:00:07 -07:00
Lance Martin
8bf16d5275 LLaMA2 SQL Chat cookbook (#11685) 2023-10-15 08:54:09 -07:00
Harrison Chase
a506302772 bearly tool (#11812) 2023-10-14 16:03:58 -07:00
Harrison Chase
4a2f0c51a1 use get_llm_cache and set_llm_cache (#11741)
Co-authored-by: Bagatur <baskaryan@gmail.com>
2023-10-14 09:29:30 -07:00
Harrison Chase
f3ad22e64a pipe default key (#11788) 2023-10-14 08:39:23 +01:00
Bagatur
6e78dacd78 customize rtd build (#11797)
customize readthedocs config so that we can parallelize the api docs
build
2023-10-13 19:50:22 -07:00
Eugene Yurtsev
0d37b4c27d Add python,pandas,xorbits,spark agents to experimental (#11774)
See for contex
https://github.com/langchain-ai/langchain/discussions/11680
2023-10-13 17:36:44 -04:00
Bagatur
d6e34ca2ee fix recent docs integrations file loc (#11782) 2023-10-13 13:58:26 -07:00
Michael Feil
233a904f2e GradientLLM Docs update and model_id renaming. (#10963)
Related to #10800 

- Errors in the Docstring of GradientLLM / Gradient.ai LLM
- Renamed the `model_id` to `model` and adapting this in all tests.
Reason to so is to be in Sync with `GradientEmbeddings` and other LLM's.
- inmproving tests so they check the headers in the sent request.
- making the aiosession a private attribute in the docs, as in the
future `pip install gradientai` will be replacing aiosession.
- adding a example how to fine-tune on the Prompt Template as suggested
in #10800
2023-10-13 13:57:58 -07:00
David
6876b02c87 Move EverlyAI python notebook to the right location (#11779)
Hi,

After submitting https://github.com/langchain-ai/langchain/pull/11357,
we realized that the notebooks are moved to a new location. Sending a
new PR to update the doc.

---------

Co-authored-by: everly-studio <127131037+everly-studio@users.noreply.github.com>
2023-10-13 13:34:27 -07:00
Bagatur
1559ba4bfc fix upstash test import (#11781) 2023-10-13 13:31:36 -07:00
Leonid Kuligin
9f0a718198 added candidate_count for Vertex models (#11729)
- **Description:** added support for `candidate_count` parameter on
Vertex
2023-10-13 13:31:20 -07:00
David
9d200e6cbe Create ChatEverlyAI (#11357)
- Description: Adds the ChatEverlyAI class with llama-2 7b on [EverlyAI
Hosted
Endpoints](https://everlyai.xyz/)
- It inherits from ChatOpenAI and requires openai (probably unnecessary
but it made for a quick and easy implementation)

---------

Co-authored-by: everly-studio <127131037+everly-studio@users.noreply.github.com>
2023-10-13 12:25:11 -07:00
Hristo G
7fb25b4154 Add graceful fallback for ES vectorstore when content field is missing (#11726)
- **Description:**
- If the Elasticsearch field used for Langchain > Document.page_content
is missing because the specific document is
        somehow malformed fail gracefully.

  - **Tag maintainer:** 
    - @joemcelroy
2023-10-13 12:03:32 -07:00
Bagatur
f06fcde0d7 rm duplicate zilliz import (#11777) 2023-10-13 12:01:22 -07:00
Bagatur
a3330c4258 bump 314 (#11773) 2023-10-13 11:09:54 -07:00
Erick Friis
1861cc7100 General anthropic functions, steps towards experimental integration tests (#11727)
To match change in js here
https://github.com/langchain-ai/langchainjs/pull/2892

Some integration tests need a bit more work in experimental:
![Screenshot 2023-10-12 at 12 02 49
PM](https://github.com/langchain-ai/langchain/assets/9557659/262d7d22-c405-40e9-afef-669e8d585307)

Pretty sure the sqldatabase ones are an actual regression or change in
interface because it's returning a placeholder.

---------

Co-authored-by: Bagatur <baskaryan@gmail.com>
2023-10-13 09:48:24 -07:00
Lance Martin
98c8516ef1 Semi-structured and Multi-modal RAG cookbooks (#11582) 2023-10-13 08:45:54 -07:00
Nuno Campos
17c69678ab Revert "New add Baichuan Model" (#11761)
Reverts langchain-ai/langchain#11714

This has linting and formatting issues, plus it's added to chat models
folder but doesn't subclass Chat Model base class
2023-10-13 08:23:15 -07:00
cloudscool
56653c53aa New add Baichuan Model (#11714)
Motivation and Context
At present, the Baichuan Large Language Model is relatively popular and
efficient in performance. Due to widespread market recognition, this
model has been added to enhance the scalability of Langchain's ability
to access the big language model, so as to facilitate application access
and usage for interested users.

System Info
langchain: 0.0.295
python:3.8.3
IDE:vs code

Description
Add the following files:

1. Add baichuan_baichuaninc_endpoint.py in the
libs/langchain/langchain/chat_models
2. Modify the __init__.py file,which is located in the
libs/langchain/langchain/chat_models/__init__.py:
a. Add "from langchain.chat_models.baichuan_baichuaninc_endpoint import
BaichuanChatEndpoint"
    b. Add "BaichuanChatEndpoint" In the file's __ All__  method

Your contribution
I am willing to help implement this feature and submit a PR, but I would
appreciate guidance from the maintainers or community to ensure the
changes are made correctly and in line with the project's standards and
practices.
2023-10-12 23:04:28 -07:00
Shreyas S
694d768174 Minor fix (#11748)
changed > to over
2023-10-12 22:36:31 -07:00
Bagatur
8e6fa5f1d7 mv self-query docs to integrations (#11744) 2023-10-12 22:36:07 -07:00
Yang, Bo
9e1e0f54d2 Add TrainableLLM (#11721)
- **Description:** Add `TrainableLLM` for those LLM support fine-tuning
  - **Tag maintainer:** @hwchase17

This PR add training methods to `GradientLLM`

---------

Co-authored-by: Bagatur <baskaryan@gmail.com>
2023-10-12 17:38:33 -07:00
Burak Yılmaz
63e516c2b0 Upstash redis integration (#10871)
- **Description:** Introduced Upstash provider with following wrappers:
UpstashRedisCache, UpstashRedisEntityStore,
UpstashRedisChatMessageHistory, UpstashRedisStore
  - **Issue:** -,
  - **Dependencies:** upstash-redis python package is needed,
  - **Tag maintainer:** @baskaryan 
  - **Twitter handle:** @BurakY744

---------

Co-authored-by: Burak Yılmaz <burakyilmaz@Buraks-MacBook-Pro.local>
Co-authored-by: Bagatur <baskaryan@gmail.com>
2023-10-12 17:36:51 -07:00
Bagatur
a9db2b0b92 fix tongyi import (#11745) 2023-10-12 17:24:06 -07:00
Aaron Pham
6c61315067 fix(openllm): update with newer remote client implementation (#11740)
cc @baskaryan

---------

Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
2023-10-12 17:01:18 -07:00
Richy Wang
11cdfe44af Implement Alibaba Tongyi chat model apis. (#10922)
Hi there
This PR is aim to implement chat model for Alibaba Tongyi LLM model. It
contains work below:
1.Implement ChatTongyi chat model in langchain.chat_models.tongyi. Note
this is different with tongyi llm model to another PR
https://github.com/langchain-ai/langchain/pull/10878.
For detail it implements _generate() and _stream() function in
ChatTongyi.
2. Add some examples in chat/tongyi.ipynb. 
3. Add integration test in chat_models/test_tongyi.py 

Note async completion for the Text API is not yet supported.
Dependencies: dashscope. It will be installed manually cause it is not
need by everyone.
2023-10-12 16:59:37 -07:00
Adam Demjen
008348ce71 Add ElasticsearchChatMessageHistory (#10932)
**Description**

This PR adds the `ElasticsearchChatMessageHistory` implementation that
stores chat message history in the configured
[Elasticsearch](https://www.elastic.co/elasticsearch/) deployment.

```python
from langchain.memory.chat_message_histories import ElasticsearchChatMessageHistory

history = ElasticsearchChatMessageHistory(
    es_url="https://my-elasticsearch-deployment-url:9200", index="chat-history-index", session_id="123"
)

history.add_ai_message("This is me, the AI")
history.add_user_message("This is me, the human")
```

**Dependencies**
- [elasticsearch client](https://elasticsearch-py.readthedocs.io/)
required

Co-authored-by: Bagatur <baskaryan@gmail.com>
2023-10-12 16:51:38 -07:00
Bagatur
d3a5090e12 mv semadb docs (#11743) 2023-10-12 16:31:09 -07:00
Bagatur
acdbdbddb1 clean up doc (#11742)
committed old doc in wrong place
2023-10-12 16:26:55 -07:00
Jonathan Soma
48cf978391 Allow placeholders in OpenAPI endpoints #2938 (#2940)
Use regex matches when checking endpoints instead of exact matches.
`{varname}` becomes `.*`

Fixes #2938

---------

Co-authored-by: Bagatur <baskaryan@gmail.com>
2023-10-12 16:20:32 -07:00
Mateusz Kozak
e42a576cb2 update Qdrant documentation (#3105)
fix `from_documents` method usage for Qdrant in documentation as
previous example doesn't work

---------

Co-authored-by: Bagatur <baskaryan@gmail.com>
2023-10-12 16:20:18 -07:00
Predrag Gruevski
9e32120cbb Deprecate direct access to globals like debug and verbose. (#11311)
Instead of accessing `langchain.debug`, `langchain.verbose`, or
`langchain.llm_cache`, please use the new getter/setter functions in
`langchain.globals`:
- `langchain.globals.set_debug()` and `langchain.globals.get_debug()`
- `langchain.globals.set_verbose()` and
`langchain.globals.get_verbose()`
- `langchain.globals.set_llm_cache()` and
`langchain.globals.get_llm_cache()`

Using the old globals directly will now raise a warning.

---------

Co-authored-by: Harrison Chase <hw.chase.17@gmail.com>
2023-10-12 15:48:04 -07:00
Bagatur
01b7b46908 reorder eval docs (#11738)
cc @leo-gan
2023-10-12 15:46:55 -07:00
Richard Adams
35965df20d Rspace doc loader (#11511)
**Description:**

Add a document loader for the RSpace Electronic Lab Notebook
(www.researchspace.com), so that scientific documents and research notes
can be easily pulled into Langchain pipelines.

**Issue**

This is an new contribution, rather than an issue fix.

 **Dependencies:** 
  
There are no new required dependencies.
In order to use the loader, clients will need to install rspace_client
SDK using `pip install rspace_client`

---------

Co-authored-by: richarda23 <richard.c.adams@infinityworks.com>
Co-authored-by: Bagatur <baskaryan@gmail.com>
2023-10-12 15:05:38 -07:00
Ryan Zotti
9d1867c77f Update docs to specify Indexing-API-compatible vectorstores (#11581)
**Description:** Update Indexing API docs to specify vectorstores that
are compatible with the Indexing API. I add a unit test to remind
developers to update the documentation whenever they add or change a
vectorstore in a way that affects compatibility. For the unit test I
repurposed existing code from
[here](https://github.com/langchain-ai/langchain/blob/v0.0.311/libs/langchain/langchain/indexes/_api.py#L245-L257).

This is my first PR to an open source project. This is a trivially
simple PR whose main purpose is to make me more comfortable submitting
Langchain PRs. If this PR goes through I plan to submit PRs with more
substantive changes in the near future.

**Issue:** Resolves
[10482](https://github.com/langchain-ai/langchain/discussions/10482).

**Dependencies:** No new dependencies.

**Twitter handle:** None.
2023-10-12 15:17:44 -04:00
Richard Wang
6402c33299 Let Notion document loader support utf-8 and make it default. (#10613)
Use utf-8 encoding by default
2023-10-12 15:13:41 -04:00
Tomaz Bratanic
3759a34229 Add graph construction to neo4j docs (#11716)
Add graph construction section to Neo4j provider docs
2023-10-12 11:37:42 -07:00
Bagatur
bd74eba152 add azure openai sched tests (#11723) 2023-10-12 10:48:45 -07:00
Nuno Campos
b54727fbad Nc/why lcel (#11717)
<!-- Thank you for contributing to LangChain!

Replace this entire comment with:
  - **Description:** a description of the change, 
  - **Issue:** the issue # it fixes (if applicable),
  - **Dependencies:** any dependencies required for this change,
- **Tag maintainer:** for a quicker response, tag the relevant
maintainer (see below),
- **Twitter handle:** we announce bigger features on Twitter. If your PR
gets announced, and you'd like a mention, we'll gladly shout you out!

Please make sure your PR is passing linting and testing before
submitting. Run `make format`, `make lint` and `make test` to check this
locally.

See contribution guidelines for more information on how to write/run
tests, lint, etc:

https://github.com/langchain-ai/langchain/blob/master/.github/CONTRIBUTING.md

If you're adding a new integration, please include:
1. a test for the integration, preferably unit tests that do not rely on
network access,
2. an example notebook showing its use. It lives in `docs/extras`
directory.

If no one reviews your PR within a few days, please @-mention one of
@baskaryan, @eyurtsev, @hwchase17.
 -->
2023-10-12 17:52:20 +01:00
296 changed files with 21302 additions and 13164 deletions

View File

@@ -61,6 +61,10 @@ jobs:
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
AZURE_OPENAI_API_VERSION: ${{ secrets.AZURE_OPENAI_API_VERSION }}
AZURE_OPENAI_API_BASE: ${{ secrets.AZURE_OPENAI_API_BASE }}
AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }}
AZURE_OPENAI_DEPLOYMENT_NAME: ${{ secrets.AZURE_OPENAI_DEPLOYMENT_NAME }}
run: |
make scheduled_tests

1
.gitignore vendored
View File

@@ -177,3 +177,4 @@ docs/api_reference/*/
docs/docs/build
docs/docs/node_modules
docs/docs/yarn.lock
_dist

View File

@@ -9,9 +9,14 @@ build:
os: ubuntu-22.04
tools:
python: "3.11"
jobs:
pre_build:
commands:
- python -mvirtualenv $READTHEDOCS_VIRTUALENV_PATH
- python -m pip install --upgrade --no-cache-dir pip setuptools
- python -m pip install --upgrade --no-cache-dir sphinx readthedocs-sphinx-ext
- python -m pip install --exists-action=w --no-cache-dir -r docs/api_reference/requirements.txt
- python docs/api_reference/create_api_rst.py
- cat docs/api_reference/conf.py
- python -m sphinx -T -E -b html -d _build/doctrees -c docs/api_reference docs/api_reference $READTHEDOCS_OUTPUT/html -j auto
# Build documentation in the docs/ directory with Sphinx
sphinx:

View File

@@ -0,0 +1,363 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "fc935871-7640-41c6-b798-58514d860fe0",
"metadata": {},
"source": [
"## LLaMA2 chat with SQL\n",
" \n",
"This Cookbook shows how to use LangChain's `SQLDatabaseChain` with LLaMA2 to chat about structured data stored in a SQL DB. \n",
"\n",
"* As the 2023-24 NBA season is around the corner, we use the NBA roster info saved in a SQLite DB to show you how to ask Llama2 questions about your favorite teams or players. \n",
"\n",
"* Because the SQLDatabaseChain API implementation is still in the langchain_experimental package, you'll see more issues that come with using the cutting edge experimental features, and how we succeed resolving some of the issues but fail on some others."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "81adcf8b-395a-4f02-8749-ac976942b446",
"metadata": {},
"outputs": [],
"source": [
"! pip install langchain replicate langchain_experimental"
]
},
{
"cell_type": "markdown",
"id": "8e13ed66-300b-4a23-b8ac-44df68ee4733",
"metadata": {},
"source": [
"## LLM\n",
"\n",
"Use Replicate API for llama-2-13b-chat."
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "416ecce7-8aec-4145-b3f1-587a9b8a4fe9",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Init param `input` is deprecated, please use `model_kwargs` instead.\n"
]
}
],
"source": [
"from getpass import getpass\n",
"from langchain.llms import Replicate\n",
"# REPLICATE_API_TOKEN = getpass()\n",
"# os.environ[\"REPLICATE_API_TOKEN\"] = REPLICATE_API_TOKEN\n",
"\n",
"# Replicate API\n",
"llama2_13b_chat = \"meta/llama-2-13b-chat:f4e2de70d66816a838a89eeeb621910adffb0dd0baba3976c96980970978018d\"\n",
"\n",
"# Set the system_prompt so that LLaMA will generate only the SQL statement, instead of being wordy and adding something like\n",
"# \"Sure! Here's the SQL query for the given input question: \" before the SQL query; otherwise custom parsing will be needed.\n",
"llm = Replicate(\n",
" model=llama2_13b_chat,\n",
" input={\"temperature\": 0.01, \n",
" \"max_length\": 500, \n",
" \"top_p\": 1}\n",
")"
]
},
{
"cell_type": "markdown",
"id": "80222165-f353-4e35-a123-5f70fd70c6c8",
"metadata": {},
"source": [
"## DB\n",
"\n",
"Connect to a SQL DB, which in this case is in this same directory."
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "025bdd82-3bb1-4948-bc7c-c3ccd94fd05c",
"metadata": {},
"outputs": [],
"source": [
"from langchain.utilities import SQLDatabase\n",
"db = SQLDatabase.from_uri(\"sqlite:///nba_roster.db\", sample_rows_in_table_info= 0)\n",
"\n",
"def get_schema(_):\n",
" return db.get_table_info()\n",
"\n",
"def run_query(query):\n",
" return db.run(query)"
]
},
{
"cell_type": "markdown",
"id": "654b3577-baa2-4e12-a393-f40e5db49ac7",
"metadata": {},
"source": [
"## Query a SQL DB \n",
"\n",
"Follow the workflow [here](https://python.langchain.com/docs/expression_language/cookbook/sql_db)."
]
},
{
"cell_type": "code",
"execution_count": 72,
"id": "5a4933ea-d9c0-4b0a-8177-ba4490c6532b",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\" SELECT * FROM nba_roster WHERE NAME = 'Klay Thompson';\""
]
},
"execution_count": 72,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Prompt\n",
"from langchain.prompts import ChatPromptTemplate\n",
"template = \"\"\"Based on the table schema below, write a SQL query that would answer the user's question:\n",
"{schema}\n",
"\n",
"Question: {question}\n",
"SQL Query:\"\"\"\n",
"prompt = ChatPromptTemplate.from_messages([\n",
" (\"system\", \"Given an input question, convert it to a SQL query. No pre-amble.\"),\n",
" (\"human\", template)\n",
"])\n",
"\n",
"# Chain to query\n",
"from langchain.chat_models import ChatOpenAI\n",
"from langchain.schema.output_parser import StrOutputParser\n",
"from langchain.schema.runnable import RunnablePassthrough\n",
"\n",
"sql_response = (\n",
" RunnablePassthrough.assign(schema=get_schema)\n",
" | prompt\n",
" | llm.bind(stop=[\"\\nSQLResult:\"])\n",
" | StrOutputParser()\n",
" )\n",
"\n",
"sql_response.invoke({\"question\": \"What team is Klay Thompson on?\"})"
]
},
{
"cell_type": "markdown",
"id": "a0e9e2c8-9b88-4853-ac86-001bc6cc6695",
"metadata": {},
"source": [
"The [LangSmith trace](https://smith.langchain.com/public/afa56a06-b4e2-469a-a60f-c1746e75e42b/r) gives us visibility into the chain! "
]
},
{
"cell_type": "code",
"execution_count": 68,
"id": "2a2825e3-c1b6-4f7d-b9c9-d9835de323bb",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\" Sure! Here's the natural language response based on the given SQL query and response:\\n\\nThere are 30 unique teams in the NBA roster.\""
]
},
"execution_count": 68,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Chain to answer\n",
"template = \"\"\"Based on the table schema below, question, sql query, and sql response, write a natural language response:\n",
"{schema}\n",
"\n",
"Question: {question}\n",
"SQL Query: {query}\n",
"SQL Response: {response}\"\"\"\n",
"prompt_response = ChatPromptTemplate.from_messages([\n",
" (\"system\", \"Given an input question and SQL response, convert it to a natural langugae answer. No pre-amble.\"),\n",
" (\"human\", template)\n",
"])\n",
"\n",
"full_chain = (\n",
" RunnablePassthrough.assign(query=sql_response) \n",
" | RunnablePassthrough.assign(\n",
" schema=get_schema,\n",
" response=lambda x: db.run(x[\"query\"]),\n",
" )\n",
" | prompt_response \n",
" | llm\n",
")\n",
"\n",
"full_chain.invoke({\"question\": \"How many unique teams are there?\"})"
]
},
{
"cell_type": "markdown",
"id": "ec17b3ee-6618-4681-b6df-089bbb5ffcd7",
"metadata": {},
"source": [
"Again, the [LangSmith trace](https://smith.langchain.com/public/10420721-746a-4806-8ecf-d6dc6399d739/r) gives us visibility into the chain! "
]
},
{
"cell_type": "markdown",
"id": "1e85381b-1edc-4bb3-a7bd-2ab23f81e54d",
"metadata": {},
"source": [
"## Chat with a SQL DB \n",
"\n",
"Add memory!"
]
},
{
"cell_type": "code",
"execution_count": 74,
"id": "1985aa1c-eb8f-4fb1-a54f-c8aa10744687",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\"SELECT Team \\nFROM nba_roster \\nWHERE NAME = 'Klay Thompson'\""
]
},
"execution_count": 74,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Prompt\n",
"from langchain.prompts import ChatPromptTemplate\n",
"template = \"\"\"Based on the table schema below, write a SQL query that would answer the user's question:\n",
"{schema}\n",
"\n",
"Question: {question}\n",
"SQL Query:\"\"\"\n",
"prompt = ChatPromptTemplate.from_messages([\n",
" (\"system\", \"Given an input question, convert it to a SQL query. No pre-amble.\"),\n",
" MessagesPlaceholder(variable_name=\"history\"),\n",
" (\"human\", template)\n",
"])\n",
"\n",
"memory = ConversationBufferMemory(return_messages=True)\n",
"\n",
"# Chain to query with memory \n",
"from langchain.memory import ConversationBufferMemory\n",
"from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
"from langchain.schema.runnable import RunnableLambda, GetLocalVar, PutLocalVar\n",
"\n",
"sql_chain = (\n",
" RunnablePassthrough.assign(\n",
" schema=get_schema,\n",
" history=RunnableLambda(lambda x: memory.load_memory_variables(x)[\"history\"])\n",
" )| prompt\n",
" | model.bind(stop=[\"\\nSQLResult:\"])\n",
" | StrOutputParser()\n",
")\n",
"\n",
"def save(input_output):\n",
" output = {\"output\": input_output.pop(\"output\")}\n",
" memory.save_context(input_output, output)\n",
" return output['output']\n",
" \n",
"sql_response_memory = RunnablePassthrough.assign(output=sql_chain) | save\n",
"sql_response_memory.invoke({\"question\": \"What team is Klay Thompson on?\"})"
]
},
{
"cell_type": "code",
"execution_count": 75,
"id": "0b45818a-1498-441d-b82d-23c29428c2bb",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\"SELECT SALARY \\nFROM nba_roster \\nWHERE NAME = 'Klay Thompson'\""
]
},
"execution_count": 75,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sql_response_memory.invoke({\"question\": \"What is his salary?\"})"
]
},
{
"cell_type": "code",
"execution_count": 76,
"id": "800a7a3b-f411-478b-af51-2310cd6e0425",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\" Sure thing! Here's the natural language response based on the given SQL query and response:\\n\\nKlay Thompson plays for the Golden State Warriors.\""
]
},
"execution_count": 76,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Chain to answer\n",
"template = \"\"\"Based on the table schema below, question, sql query, and sql response, write a natural language response:\n",
"{schema}\n",
"\n",
"Question: {question}\n",
"SQL Query: {query}\n",
"SQL Response: {response}\"\"\"\n",
"prompt_response = ChatPromptTemplate.from_messages([\n",
" (\"system\", \"Given an input question and SQL response, convert it to a natural langugae answer. No pre-amble.\"),\n",
" (\"human\", template)\n",
"])\n",
"\n",
"full_chain = (\n",
" RunnablePassthrough.assign(query=sql_response_memory) \n",
" | RunnablePassthrough.assign(\n",
" schema=get_schema,\n",
" response=lambda x: db.run(x[\"query\"]),\n",
" )\n",
" | prompt_response \n",
" | llm\n",
")\n",
"\n",
"full_chain.invoke({\"question\": \"What team is Klay Thompson on?\"})"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.16"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -135,9 +135,9 @@
"outputs": [],
"source": [
"# We set this so we can see what exactly is going on\n",
"import langchain\n",
"from langchain.globals import set_verbose\n",
"\n",
"langchain.verbose = True"
"set_verbose(True)"
]
},
{
@@ -489,7 +489,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.3"
"version": "3.10.1"
}
},
"nbformat": 4,

View File

@@ -214,7 +214,7 @@
"\n",
"The way the chain is learning that Tom prefers veggetarian meals is via an AutoSelectionScorer that is built into the chain. The scorer will call the LLM again and ask it to evaluate the selection (`ToSelectFrom`) using the information wrapped in (`BasedOn`).\n",
"\n",
"You can set `langchain.debug=True` if you want to see the details of the auto-scorer, but you can also define the scoring prompt yourself."
"You can set `set_debug(True)` if you want to see the details of the auto-scorer, but you can also define the scoring prompt yourself."
]
},
{
@@ -778,8 +778,9 @@
],
"source": [
"from langchain.prompts.prompt import PromptTemplate\n",
"import langchain\n",
"langchain.debug = True\n",
"from langchain.globals import set_debug\n",
"\n",
"set_debug(True)\n",
"\n",
"REWARD_PROMPT_TEMPLATE = \"\"\"\n",
"\n",
@@ -812,9 +813,9 @@
],
"metadata": {
"kernelspec": {
"display_name": "poetry-venv",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "poetry-venv"
"name": "python3"
},
"language_info": {
"codemirror_mode": {
@@ -826,7 +827,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.1"
"version": "3.10.1"
}
},
"nbformat": 4,

File diff suppressed because one or more lines are too long

View File

@@ -11,6 +11,7 @@ cd "${SCRIPT_DIR}"
mkdir -p ../_dist
cp -r . ../_dist
cd ../_dist
poetry run python scripts/model_feat_table.py
poetry run nbdoc_build --srcdir docs
poetry run python scripts/generate_api_reference_links.py
yarn install

View File

@@ -48,6 +48,6 @@ If youre working on something youre proud of, and think the LangChain comm
Heres where our team hangs out, talks shop, spotlights cool work, and shares what were up to. Wed love to see you there too.
- **[Twitter](https://twitter.com/LangChainAI):** We post about what were working on and what cool things were seeing in the space. If you tag @langchainai in your post, well almost certainly see it, and can show you some love!
- **[Discord](https://discord.gg/6adMQxSpJS):** connect with >30k developers who are building with LangChain
- **[Discord](https://discord.gg/6adMQxSpJS):** connect with over 30,000 developers who are building with LangChain.
- **[GitHub](https://github.com/langchain-ai/langchain):** Open pull requests, contribute to a discussion, and/or contribute
- **[Subscribe to our bi-weekly Release Notes](https://6w1pwbss0py.typeform.com/to/KjZB1auB):** a twice/month email roundup of the coolest things going on in our orbit

View File

@@ -31,3 +31,6 @@ How to use core features of LCEL
#### [Cookbook](/docs/expression_language/cookbook)
Examples of common LCEL usage patterns
#### [Why use LCEL](/docs/expression_language/why)
A deeper dive into the benefits of LCEL

View File

@@ -925,7 +925,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.5"
"version": "3.9.1"
}
},
"nbformat": 4,

View File

@@ -0,0 +1,11 @@
# Why use LCEL?
The LangChain Expression Language was designed from day 1 to **support putting prototypes in production, with no code changes**, from the simplest “prompt + LLM” chain to the most complex chains (weve seen folks successfully running in production LCEL chains with 100s of steps). To highlight a few of the reasons you might want to use LCEL:
- first-class support for streaming: when you build your chains with LCEL you get the best possible time-to-first-token (time elapsed until the first chunk of output comes out). For some chains this means eg. we stream tokens straight from an LLM to a streaming output parser, and you get back parsed, incremental chunks of output at the same rate as the LLM provider outputs the raw tokens. Were constantly improving streaming support, recently we added a [streaming JSON parser](https://twitter.com/LangChainAI/status/1709690468030914584), and more is in the works.
- first-class async support: any chain built with LCEL can be called both with the synchronous API (eg. in your Jupyter notebook while prototyping) as well as with the asynchronous API (eg. in a [LangServe](https://github.com/langchain-ai/langserve) server). This enables using the same code for prototypes and in production, with great performance, and the ability to handle many concurrent requests in the same server.
- optimised parallel execution: whenever your LCEL chains have steps that can be executed in parallel (eg if you fetch documents from multiple retrievers) we automatically do it, both in the sync and the async interfaces, for the smallest possible latency.
- support for retries and fallbacks: more recently weve added support for configuring retries and fallbacks for any part of your LCEL chain. This is a great way to make your chains more reliable at scale. Were currently working on adding streaming support for retries/fallbacks, so you can get the added reliability without any latency cost.
- accessing intermediate results: for more complex chains its often very useful to access the results of intermediate steps even before the final output is produced. This can be used let end-users know something is happening, or even just to debug your chain. Weve added support for [streaming intermediate results](https://x.com/LangChainAI/status/1711806009097044193?s=20), and its available on every LangServe server.
- [input and output schemas](https://x.com/LangChainAI/status/1711805322195861934?s=20): this week we launched input and output schemas for LCEL, giving every LCEL chain Pydantic and JSONSchema schemas inferred from the structure of your chain. This can be used for validation of inputs and outputs, and is an integral part of LangServe.
- tracing with LangSmith: all chains built with LCEL have first-class tracing support, which can be used to debug your chains, or to understand whats happening in production. To enable this all you have to do is add your [LangSmith](https://www.langchain.com/langsmith) API key as an environment variable.

View File

@@ -1,5 +1,49 @@
# Installation
import Installation from "@snippets/get_started/installation.mdx"
## Official release
<Installation/>
To install LangChain run:
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from "@theme/CodeBlock";
<Tabs>
<TabItem value="pip" label="Pip" default>
<CodeBlock language="bash">pip install langchain</CodeBlock>
</TabItem>
<TabItem value="conda" label="Conda">
<CodeBlock language="bash">conda install langchain -c conda-forge</CodeBlock>
</TabItem>
</Tabs>
This will install the bare minimum requirements of LangChain.
A lot of the value of LangChain comes when integrating it with various model providers, datastores, etc.
By default, the dependencies needed to do that are NOT installed.
However, there are two other ways to install LangChain that do bring in those dependencies.
To install modules needed for the common LLM providers, run:
```bash
pip install langchain[llms]
```
To install all modules needed for all integrations, run:
```bash
pip install langchain[all]
```
Note that if you are using `zsh`, you'll need to quote square brackets when passing them as an argument to a command, for example:
```bash
pip install 'langchain[all]'
```
## From source
If you want to install from source, you can do so by cloning the repo and be sure that the directory is `PATH/TO/REPO/langchain/libs/langchain` running:
```bash
pip install -e .
```

View File

@@ -6,9 +6,17 @@ To install LangChain run:
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import Install from "@snippets/get_started/quickstart/installation.mdx"
import CodeBlock from "@theme/CodeBlock";
<Tabs>
<TabItem value="pip" label="Pip" default>
<CodeBlock language="bash">pip install langchain</CodeBlock>
</TabItem>
<TabItem value="conda" label="Conda">
<CodeBlock language="bash">conda install langchain -c conda-forge</CodeBlock>
</TabItem>
</Tabs>
<Install/>
For more details, see our [Installation guide](/docs/get_started/installation.html).
@@ -16,9 +24,26 @@ For more details, see our [Installation guide](/docs/get_started/installation.ht
Using LangChain will usually require integrations with one or more model providers, data stores, APIs, etc. For this example, we'll use OpenAI's model APIs.
import OpenAISetup from "@snippets/get_started/quickstart/openai_setup.mdx"
First we'll need to install their Python package:
```bash
pip install openai
```
Accessing the API requires an API key, which you can get by creating an account and heading [here](https://platform.openai.com/account/api-keys). Once we have a key we'll want to set it as an environment variable by running:
```bash
export OPENAI_API_KEY="..."
```
If you'd prefer not to set an environment variable you can pass the key in directly via the `openai_api_key` named parameter when initiating the OpenAI LLM class:
```python
from langchain.llms import OpenAI
llm = OpenAI(openai_api_key="...")
```
<OpenAISetup/>
## Building an application
@@ -66,24 +91,49 @@ The standard interface that LangChain provides has two methods:
Let's see how to work with these different types of models and these different types of inputs.
First, let's import an LLM and a ChatModel.
import ImportLLMs from "@snippets/get_started/quickstart/import_llms.mdx"
```python
from langchain.llms import OpenAI
from langchain.chat_models import ChatOpenAI
<ImportLLMs/>
llm = OpenAI()
chat_model = ChatOpenAI()
llm.predict("hi!")
>>> "Hi"
chat_model.predict("hi!")
>>> "Hi"
```
The `OpenAI` and `ChatOpenAI` objects are basically just configuration objects.
You can initialize them with parameters like `temperature` and others, and pass them around.
Next, let's use the `predict` method to run over a string input.
import InputString from "@snippets/get_started/quickstart/input_string.mdx"
```python
text = "What would be a good company name for a company that makes colorful socks?"
<InputString/>
llm.predict(text)
# >> Feetful of Fun
chat_model.predict(text)
# >> Socks O'Color
```
Finally, let's use the `predict_messages` method to run over a list of messages.
import InputMessages from "@snippets/get_started/quickstart/input_messages.mdx"
```python
from langchain.schema import HumanMessage
<InputMessages/>
text = "What would be a good company name for a company that makes colorful socks?"
messages = [HumanMessage(content=text)]
llm.predict_messages(messages)
# >> Feetful of Fun
chat_model.predict_messages(messages)
# >> Socks O'Color
```
For both these methods, you can also pass in parameters as keyword arguments.
For example, you could pass in `temperature=0` to adjust the temperature that is used from what the object was configured with.
@@ -100,10 +150,16 @@ PromptTemplates help with exactly this!
They bundle up all the logic for going from user input into a fully formatted prompt.
This can start off very simple - for example, a prompt to produce the above string would just be:
import PromptTemplateLLM from "@snippets/get_started/quickstart/prompt_templates_llms.mdx"
import PromptTemplateChatModel from "@snippets/get_started/quickstart/prompt_templates_chat_models.mdx"
```python
from langchain.prompts import PromptTemplate
<PromptTemplateLLM/>
prompt = PromptTemplate.from_template("What is a good name for a company that makes {product}?")
prompt.format(product="colorful socks")
```
```pycon
What is a good name for a company that makes colorful socks?
```
However, the advantages of using these over raw string formatting are several.
You can "partial" out variables - e.g. you can format only some of the variables at a time.
@@ -116,7 +172,27 @@ Here, what happens most often is a ChatPromptTemplate is a list of ChatMessageTe
Each ChatMessageTemplate contains instructions for how to format that ChatMessage - its role, and then also its content.
Let's take a look at this below:
<PromptTemplateChatModel/>
```python
from langchain.prompts.chat import ChatPromptTemplate
template = "You are a helpful assistant that translates {input_language} to {output_language}."
human_template = "{text}"
chat_prompt = ChatPromptTemplate.from_messages([
("system", template),
("human", human_template),
])
chat_prompt.format_messages(input_language="English", output_language="French", text="I love programming.")
```
```pycon
[
SystemMessage(content="You are a helpful assistant that translates English to French.", additional_kwargs={}),
HumanMessage(content="I love programming.")
]
```
ChatPromptTemplates can also be constructed in other ways - see the [section on prompts](/docs/modules/model_io/prompts) for more detail.
@@ -133,9 +209,20 @@ For full information on this, see the [section on output parsers](/docs/modules/
In this getting started guide, we will write our own output parser - one that converts a comma separated list into a list.
import OutputParser from "@snippets/get_started/quickstart/output_parser.mdx"
```python
from langchain.schema import BaseOutputParser
<OutputParser/>
class CommaSeparatedListOutputParser(BaseOutputParser):
"""Parse the output of an LLM call to a comma-separated list."""
def parse(self, text: str):
"""Parse the output of an LLM call."""
return text.strip().split(", ")
CommaSeparatedListOutputParser().parse("hi, bye")
# >> ['hi', 'bye']
```
## PromptTemplate + LLM + OutputParser
@@ -144,9 +231,33 @@ This chain will take input variables, pass those to a prompt template to create
This is a convenient way to bundle up a modular piece of logic.
Let's see it in action!
import LLMChain from "@snippets/get_started/quickstart/llm_chain.mdx"
```python
from langchain.chat_models import ChatOpenAI
from langchain.prompts.chat import ChatPromptTemplate
from langchain.schema import BaseOutputParser
class CommaSeparatedListOutputParser(BaseOutputParser):
"""Parse the output of an LLM call to a comma-separated list."""
def parse(self, text: str):
"""Parse the output of an LLM call."""
return text.strip().split(", ")
template = """You are a helpful assistant who generates comma separated lists.
A user will pass in a category, and you should generate 5 objects in that category in a comma separated list.
ONLY return a comma separated list, and nothing more."""
human_template = "{text}"
chat_prompt = ChatPromptTemplate.from_messages([
("system", template),
("human", human_template),
])
chain = chat_prompt | ChatOpenAI() | CommaSeparatedListOutputParser()
chain.invoke({"text": "colors"})
# >> ['red', 'blue', 'green', 'yellow', 'orange']
```
<LLMChain/>
Note that we are using the `|` syntax to join these components together.
This `|` syntax is called the LangChain Expression Language.

View File

@@ -14,7 +14,7 @@ For anyone building production-grade LLM applications, we highly recommend using
![LangSmith run](/img/run_details.png)
## `langchain.debug` and `langchain.verbose`
## `set_debug` and `set_verbose`
If you're prototyping in Jupyter Notebooks or running Python scripts, it can be helpful to print out the intermediate steps of a Chain run.
@@ -45,15 +45,15 @@ agent.run("Who directed the 2023 film Oppenheimer and what is their age? What is
</CodeOutputBlock>
### `langchain.debug = True`
### `set_debug(True)`
Setting the global `debug` flag will cause all LangChain components with callback support (chains, models, agents, tools, retrievers) to print the inputs they receive and outputs they generate. This is the most verbose setting and will fully log raw inputs and outputs.
```python
import langchain
from langchain.globals import set_debug
langchain.debug = True
set_debug(True)
agent.run("Who directed the 2023 film Oppenheimer and what is their age? What is their age in days (assume 365 days per year)?")
```
@@ -376,15 +376,15 @@ agent.run("Who directed the 2023 film Oppenheimer and what is their age? What is
</details>
### `langchain.verbose = True`
### `set_vebose(True)`
Setting the `verbose` flag will print out inputs and outputs in a slightly more readable format and will skip logging certain raw outputs (like the token usage stats for an LLM call) so that you can focus on application logic.
```python
import langchain
from langchain.globals import set_verbose
langchain.verbose = True
set_verbose(True)
agent.run("Who directed the 2023 film Oppenheimer and what is their age? What is their age in days (assume 365 days per year)?")
```

View File

@@ -1,281 +1,291 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "657d2c8c-54b4-42a3-9f02-bdefa0ed6728",
"metadata": {},
"source": [
"# Custom Pairwise Evaluator\n",
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain/blob/master/docs/docs/guides/evaluation/comparison/custom.ipynb)\n",
"\n",
"You can make your own pairwise string evaluators by inheriting from `PairwiseStringEvaluator` class and overwriting the `_evaluate_string_pairs` method (and the `_aevaluate_string_pairs` method if you want to use the evaluator asynchronously).\n",
"\n",
"In this example, you will make a simple custom evaluator that just returns whether the first prediction has more whitespace tokenized 'words' than the second.\n",
"\n",
"You can check out the reference docs for the [PairwiseStringEvaluator interface](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.schema.PairwiseStringEvaluator.html#langchain.evaluation.schema.PairwiseStringEvaluator) for more info.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "93f3a653-d198-4291-973c-8d1adba338b2",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from typing import Optional, Any\n",
"from langchain.evaluation import PairwiseStringEvaluator\n",
"\n",
"\n",
"class LengthComparisonPairwiseEvaluator(PairwiseStringEvaluator):\n",
" \"\"\"\n",
" Custom evaluator to compare two strings.\n",
" \"\"\"\n",
"\n",
" def _evaluate_string_pairs(\n",
" self,\n",
" *,\n",
" prediction: str,\n",
" prediction_b: str,\n",
" reference: Optional[str] = None,\n",
" input: Optional[str] = None,\n",
" **kwargs: Any,\n",
" ) -> dict:\n",
" score = int(len(prediction.split()) > len(prediction_b.split()))\n",
" return {\"score\": score}"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "7d4a77c3-07a7-4076-8e7f-f9bca0d6c290",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'score': 1}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator = LengthComparisonPairwiseEvaluator()\n",
"\n",
"evaluator.evaluate_string_pairs(\n",
" prediction=\"The quick brown fox jumped over the lazy dog.\",\n",
" prediction_b=\"The quick brown fox jumped over the dog.\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "d90f128f-6f49-42a1-b05a-3aea568ee03b",
"metadata": {},
"source": [
"## LLM-Based Example\n",
"\n",
"That example was simple to illustrate the API, but it wasn't very useful in practice. Below, use an LLM with some custom instructions to form a simple preference scorer similar to the built-in [PairwiseStringEvalChain](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain.html#langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain). We will use `ChatAnthropic` for the evaluator chain."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "b4b43098-4d96-417b-a8a9-b3e75779cfe8",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"# %pip install anthropic\n",
"# %env ANTHROPIC_API_KEY=YOUR_API_KEY"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "b6e978ab-48f1-47ff-9506-e13b1a50be6e",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from typing import Optional, Any\n",
"from langchain.evaluation import PairwiseStringEvaluator\n",
"from langchain.chat_models import ChatAnthropic\n",
"from langchain.chains import LLMChain\n",
"\n",
"\n",
"class CustomPreferenceEvaluator(PairwiseStringEvaluator):\n",
" \"\"\"\n",
" Custom evaluator to compare two strings using a custom LLMChain.\n",
" \"\"\"\n",
"\n",
" def __init__(self) -> None:\n",
" llm = ChatAnthropic(model=\"claude-2\", temperature=0)\n",
" self.eval_chain = LLMChain.from_string(\n",
" llm,\n",
" \"\"\"Which option is preferred? Do not take order into account. Evaluate based on accuracy and helpfulness. If neither is preferred, respond with C. Provide your reasoning, then finish with Preference: A/B/C\n",
"\n",
"Input: How do I get the path of the parent directory in python 3.8?\n",
"Option A: You can use the following code:\n",
"```python\n",
"import os\n",
"\n",
"os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n",
"```\n",
"Option B: You can use the following code:\n",
"```python\n",
"from pathlib import Path\n",
"Path(__file__).absolute().parent\n",
"```\n",
"Reasoning: Both options return the same result. However, since option B is more concise and easily understand, it is preferred.\n",
"Preference: B\n",
"\n",
"Which option is preferred? Do not take order into account. Evaluate based on accuracy and helpfulness. If neither is preferred, respond with C. Provide your reasoning, then finish with Preference: A/B/C\n",
"Input: {input}\n",
"Option A: {prediction}\n",
"Option B: {prediction_b}\n",
"Reasoning:\"\"\",\n",
" )\n",
"\n",
" @property\n",
" def requires_input(self) -> bool:\n",
" return True\n",
"\n",
" @property\n",
" def requires_reference(self) -> bool:\n",
" return False\n",
"\n",
" def _evaluate_string_pairs(\n",
" self,\n",
" *,\n",
" prediction: str,\n",
" prediction_b: str,\n",
" reference: Optional[str] = None,\n",
" input: Optional[str] = None,\n",
" **kwargs: Any,\n",
" ) -> dict:\n",
" result = self.eval_chain(\n",
" {\n",
" \"input\": input,\n",
" \"prediction\": prediction,\n",
" \"prediction_b\": prediction_b,\n",
" \"stop\": [\"Which option is preferred?\"],\n",
" },\n",
" **kwargs,\n",
" )\n",
"\n",
" response_text = result[\"text\"]\n",
" reasoning, preference = response_text.split(\"Preference:\", maxsplit=1)\n",
" preference = preference.strip()\n",
" score = 1.0 if preference == \"A\" else (0.0 if preference == \"B\" else None)\n",
" return {\"reasoning\": reasoning.strip(), \"value\": preference, \"score\": score}"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "5cbd8b1d-2cb0-4f05-b435-a1a00074d94a",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"evaluator = CustomPreferenceEvaluator()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "2c0a7fb7-b976-4443-9f0e-e707a6dfbdf7",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'reasoning': 'Option B is preferred over option A for importing from a relative directory, because it is more straightforward and concise.\\n\\nOption A uses the importlib module, which allows importing a module by specifying the full name as a string. While this works, it is less clear compared to option B.\\n\\nOption B directly imports from the relative path using dot notation, which clearly shows that it is a relative import. This is the recommended way to do relative imports in Python.\\n\\nIn summary, option B is more accurate and helpful as it uses the standard Python relative import syntax.',\n",
" 'value': 'B',\n",
" 'score': 0.0}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" input=\"How do I import from a relative directory?\",\n",
" prediction=\"use importlib! importlib.import_module('.my_package', '.')\",\n",
" prediction_b=\"from .sibling import foo\",\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "f13a1346-7dbe-451d-b3a3-99e8fc7b753b",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CustomPreferenceEvaluator requires an input string.\n"
]
}
],
"source": [
"# Setting requires_input to return True adds additional validation to avoid returning a grade when insufficient data is provided to the chain.\n",
"\n",
"try:\n",
" evaluator.evaluate_string_pairs(\n",
" prediction=\"use importlib! importlib.import_module('.my_package', '.')\",\n",
" prediction_b=\"from .sibling import foo\",\n",
" )\n",
"except ValueError as e:\n",
" print(e)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e7829cc3-ebd1-4628-ae97-15166202e9cc",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
"cells": [
{
"cell_type": "raw",
"id": "5046d96f-d578-4d5b-9a7e-43b28cafe61d",
"metadata": {},
"source": [
"---\n",
"sidebar_position: 2\n",
"title: Custom pairwise evaluator\n",
"---"
]
},
{
"cell_type": "markdown",
"id": "657d2c8c-54b4-42a3-9f02-bdefa0ed6728",
"metadata": {},
"source": [
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain/blob/master/docs/docs/guides/evaluation/comparison/custom.ipynb)\n",
"\n",
"You can make your own pairwise string evaluators by inheriting from `PairwiseStringEvaluator` class and overwriting the `_evaluate_string_pairs` method (and the `_aevaluate_string_pairs` method if you want to use the evaluator asynchronously).\n",
"\n",
"In this example, you will make a simple custom evaluator that just returns whether the first prediction has more whitespace tokenized 'words' than the second.\n",
"\n",
"You can check out the reference docs for the [PairwiseStringEvaluator interface](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.schema.PairwiseStringEvaluator.html#langchain.evaluation.schema.PairwiseStringEvaluator) for more info.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "93f3a653-d198-4291-973c-8d1adba338b2",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from typing import Optional, Any\n",
"from langchain.evaluation import PairwiseStringEvaluator\n",
"\n",
"\n",
"class LengthComparisonPairwiseEvaluator(PairwiseStringEvaluator):\n",
" \"\"\"\n",
" Custom evaluator to compare two strings.\n",
" \"\"\"\n",
"\n",
" def _evaluate_string_pairs(\n",
" self,\n",
" *,\n",
" prediction: str,\n",
" prediction_b: str,\n",
" reference: Optional[str] = None,\n",
" input: Optional[str] = None,\n",
" **kwargs: Any,\n",
" ) -> dict:\n",
" score = int(len(prediction.split()) > len(prediction_b.split()))\n",
" return {\"score\": score}"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "7d4a77c3-07a7-4076-8e7f-f9bca0d6c290",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'score': 1}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator = LengthComparisonPairwiseEvaluator()\n",
"\n",
"evaluator.evaluate_string_pairs(\n",
" prediction=\"The quick brown fox jumped over the lazy dog.\",\n",
" prediction_b=\"The quick brown fox jumped over the dog.\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "d90f128f-6f49-42a1-b05a-3aea568ee03b",
"metadata": {},
"source": [
"## LLM-Based Example\n",
"\n",
"That example was simple to illustrate the API, but it wasn't very useful in practice. Below, use an LLM with some custom instructions to form a simple preference scorer similar to the built-in [PairwiseStringEvalChain](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain.html#langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain). We will use `ChatAnthropic` for the evaluator chain."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "b4b43098-4d96-417b-a8a9-b3e75779cfe8",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"# %pip install anthropic\n",
"# %env ANTHROPIC_API_KEY=YOUR_API_KEY"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "b6e978ab-48f1-47ff-9506-e13b1a50be6e",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from typing import Optional, Any\n",
"from langchain.evaluation import PairwiseStringEvaluator\n",
"from langchain.chat_models import ChatAnthropic\n",
"from langchain.chains import LLMChain\n",
"\n",
"\n",
"class CustomPreferenceEvaluator(PairwiseStringEvaluator):\n",
" \"\"\"\n",
" Custom evaluator to compare two strings using a custom LLMChain.\n",
" \"\"\"\n",
"\n",
" def __init__(self) -> None:\n",
" llm = ChatAnthropic(model=\"claude-2\", temperature=0)\n",
" self.eval_chain = LLMChain.from_string(\n",
" llm,\n",
" \"\"\"Which option is preferred? Do not take order into account. Evaluate based on accuracy and helpfulness. If neither is preferred, respond with C. Provide your reasoning, then finish with Preference: A/B/C\n",
"\n",
"Input: How do I get the path of the parent directory in python 3.8?\n",
"Option A: You can use the following code:\n",
"```python\n",
"import os\n",
"\n",
"os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n",
"```\n",
"Option B: You can use the following code:\n",
"```python\n",
"from pathlib import Path\n",
"Path(__file__).absolute().parent\n",
"```\n",
"Reasoning: Both options return the same result. However, since option B is more concise and easily understand, it is preferred.\n",
"Preference: B\n",
"\n",
"Which option is preferred? Do not take order into account. Evaluate based on accuracy and helpfulness. If neither is preferred, respond with C. Provide your reasoning, then finish with Preference: A/B/C\n",
"Input: {input}\n",
"Option A: {prediction}\n",
"Option B: {prediction_b}\n",
"Reasoning:\"\"\",\n",
" )\n",
"\n",
" @property\n",
" def requires_input(self) -> bool:\n",
" return True\n",
"\n",
" @property\n",
" def requires_reference(self) -> bool:\n",
" return False\n",
"\n",
" def _evaluate_string_pairs(\n",
" self,\n",
" *,\n",
" prediction: str,\n",
" prediction_b: str,\n",
" reference: Optional[str] = None,\n",
" input: Optional[str] = None,\n",
" **kwargs: Any,\n",
" ) -> dict:\n",
" result = self.eval_chain(\n",
" {\n",
" \"input\": input,\n",
" \"prediction\": prediction,\n",
" \"prediction_b\": prediction_b,\n",
" \"stop\": [\"Which option is preferred?\"],\n",
" },\n",
" **kwargs,\n",
" )\n",
"\n",
" response_text = result[\"text\"]\n",
" reasoning, preference = response_text.split(\"Preference:\", maxsplit=1)\n",
" preference = preference.strip()\n",
" score = 1.0 if preference == \"A\" else (0.0 if preference == \"B\" else None)\n",
" return {\"reasoning\": reasoning.strip(), \"value\": preference, \"score\": score}"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "5cbd8b1d-2cb0-4f05-b435-a1a00074d94a",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"evaluator = CustomPreferenceEvaluator()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "2c0a7fb7-b976-4443-9f0e-e707a6dfbdf7",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'reasoning': 'Option B is preferred over option A for importing from a relative directory, because it is more straightforward and concise.\\n\\nOption A uses the importlib module, which allows importing a module by specifying the full name as a string. While this works, it is less clear compared to option B.\\n\\nOption B directly imports from the relative path using dot notation, which clearly shows that it is a relative import. This is the recommended way to do relative imports in Python.\\n\\nIn summary, option B is more accurate and helpful as it uses the standard Python relative import syntax.',\n",
" 'value': 'B',\n",
" 'score': 0.0}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" input=\"How do I import from a relative directory?\",\n",
" prediction=\"use importlib! importlib.import_module('.my_package', '.')\",\n",
" prediction_b=\"from .sibling import foo\",\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "f13a1346-7dbe-451d-b3a3-99e8fc7b753b",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CustomPreferenceEvaluator requires an input string.\n"
]
}
],
"source": [
"# Setting requires_input to return True adds additional validation to avoid returning a grade when insufficient data is provided to the chain.\n",
"\n",
"try:\n",
" evaluator.evaluate_string_pairs(\n",
" prediction=\"use importlib! importlib.import_module('.my_package', '.')\",\n",
" prediction_b=\"from .sibling import foo\",\n",
" )\n",
"except ValueError as e:\n",
" print(e)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e7829cc3-ebd1-4628-ae97-15166202e9cc",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -1,233 +1,242 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"tags": []
},
"source": [
"# Pairwise Embedding Distance \n",
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain/blob/master/docs/docs/guides/evaluation/comparison/pairwise_embedding_distance.ipynb)\n",
"\n",
"One way to measure the similarity (or dissimilarity) between two predictions on a shared or similar input is to embed the predictions and compute a vector distance between the two embeddings.<a name=\"cite_ref-1\"></a>[<sup>[1]</sup>](#cite_note-1)\n",
"\n",
"You can load the `pairwise_embedding_distance` evaluator to do this.\n",
"\n",
"**Note:** This returns a **distance** score, meaning that the lower the number, the **more** similar the outputs are, according to their embedded representation.\n",
"\n",
"Check out the reference docs for the [PairwiseEmbeddingDistanceEvalChain](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.embedding_distance.base.PairwiseEmbeddingDistanceEvalChain.html#langchain.evaluation.embedding_distance.base.PairwiseEmbeddingDistanceEvalChain) for more info."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from langchain.evaluation import load_evaluator\n",
"\n",
"evaluator = load_evaluator(\"pairwise_embedding_distance\")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'score': 0.0966466944859925}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"Seattle is hot in June\", prediction_b=\"Seattle is cool in June.\"\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'score': 0.03761174337464557}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"Seattle is warm in June\", prediction_b=\"Seattle is cool in June.\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Select the Distance Metric\n",
"\n",
"By default, the evaluator uses cosine distance. You can choose a different distance metric if you'd like. "
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"[<EmbeddingDistance.COSINE: 'cosine'>,\n",
" <EmbeddingDistance.EUCLIDEAN: 'euclidean'>,\n",
" <EmbeddingDistance.MANHATTAN: 'manhattan'>,\n",
" <EmbeddingDistance.CHEBYSHEV: 'chebyshev'>,\n",
" <EmbeddingDistance.HAMMING: 'hamming'>]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from langchain.evaluation import EmbeddingDistance\n",
"\n",
"list(EmbeddingDistance)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"evaluator = load_evaluator(\n",
" \"pairwise_embedding_distance\", distance_metric=EmbeddingDistance.EUCLIDEAN\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Select Embeddings to Use\n",
"\n",
"The constructor uses `OpenAI` embeddings by default, but you can configure this however you want. Below, use huggingface local embeddings"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from langchain.embeddings import HuggingFaceEmbeddings\n",
"\n",
"embedding_model = HuggingFaceEmbeddings()\n",
"hf_evaluator = load_evaluator(\"pairwise_embedding_distance\", embeddings=embedding_model)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'score': 0.5486443280477362}"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hf_evaluator.evaluate_string_pairs(\n",
" prediction=\"Seattle is hot in June\", prediction_b=\"Seattle is cool in June.\"\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'score': 0.21018880025138598}"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hf_evaluator.evaluate_string_pairs(\n",
" prediction=\"Seattle is warm in June\", prediction_b=\"Seattle is cool in June.\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a name=\"cite_note-1\"></a><i>1. Note: When it comes to semantic similarity, this often gives better results than older string distance metrics (such as those in the `PairwiseStringDistanceEvalChain`), though it tends to be less reliable than evaluators that use the LLM directly (such as the `PairwiseStringEvalChain`) </i>"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
}
},
"nbformat": 4,
"nbformat_minor": 4
"cells": [
{
"cell_type": "raw",
"metadata": {},
"source": [
"---\n",
"sidebar_position: 1\n",
"title: Pairwise embedding distance\n",
"---"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {
"tags": []
},
"source": [
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain/blob/master/docs/docs/guides/evaluation/comparison/pairwise_embedding_distance.ipynb)\n",
"\n",
"One way to measure the similarity (or dissimilarity) between two predictions on a shared or similar input is to embed the predictions and compute a vector distance between the two embeddings.<a name=\"cite_ref-1\"></a>[<sup>[1]</sup>](#cite_note-1)\n",
"\n",
"You can load the `pairwise_embedding_distance` evaluator to do this.\n",
"\n",
"**Note:** This returns a **distance** score, meaning that the lower the number, the **more** similar the outputs are, according to their embedded representation.\n",
"\n",
"Check out the reference docs for the [PairwiseEmbeddingDistanceEvalChain](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.embedding_distance.base.PairwiseEmbeddingDistanceEvalChain.html#langchain.evaluation.embedding_distance.base.PairwiseEmbeddingDistanceEvalChain) for more info."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from langchain.evaluation import load_evaluator\n",
"\n",
"evaluator = load_evaluator(\"pairwise_embedding_distance\")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'score': 0.0966466944859925}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"Seattle is hot in June\", prediction_b=\"Seattle is cool in June.\"\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'score': 0.03761174337464557}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"Seattle is warm in June\", prediction_b=\"Seattle is cool in June.\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Select the Distance Metric\n",
"\n",
"By default, the evaluator uses cosine distance. You can choose a different distance metric if you'd like. "
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"[<EmbeddingDistance.COSINE: 'cosine'>,\n",
" <EmbeddingDistance.EUCLIDEAN: 'euclidean'>,\n",
" <EmbeddingDistance.MANHATTAN: 'manhattan'>,\n",
" <EmbeddingDistance.CHEBYSHEV: 'chebyshev'>,\n",
" <EmbeddingDistance.HAMMING: 'hamming'>]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from langchain.evaluation import EmbeddingDistance\n",
"\n",
"list(EmbeddingDistance)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"evaluator = load_evaluator(\n",
" \"pairwise_embedding_distance\", distance_metric=EmbeddingDistance.EUCLIDEAN\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Select Embeddings to Use\n",
"\n",
"The constructor uses `OpenAI` embeddings by default, but you can configure this however you want. Below, use huggingface local embeddings"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from langchain.embeddings import HuggingFaceEmbeddings\n",
"\n",
"embedding_model = HuggingFaceEmbeddings()\n",
"hf_evaluator = load_evaluator(\"pairwise_embedding_distance\", embeddings=embedding_model)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'score': 0.5486443280477362}"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hf_evaluator.evaluate_string_pairs(\n",
" prediction=\"Seattle is hot in June\", prediction_b=\"Seattle is cool in June.\"\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'score': 0.21018880025138598}"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hf_evaluator.evaluate_string_pairs(\n",
" prediction=\"Seattle is warm in June\", prediction_b=\"Seattle is cool in June.\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a name=\"cite_note-1\"></a><i>1. Note: When it comes to semantic similarity, this often gives better results than older string distance metrics (such as those in the `PairwiseStringDistanceEvalChain`), though it tends to be less reliable than evaluators that use the LLM directly (such as the `PairwiseStringEvalChain`) </i>"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.1"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -1,382 +1,392 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "2da95378",
"metadata": {},
"source": [
"# Pairwise String Comparison\n",
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain/blob/master/docs/docs/guides/evaluation/comparison/pairwise_string.ipynb)\n",
"\n",
"Often you will want to compare predictions of an LLM, Chain, or Agent for a given input. The `StringComparison` evaluators facilitate this so you can answer questions like:\n",
"\n",
"- Which LLM or prompt produces a preferred output for a given question?\n",
"- Which examples should I include for few-shot example selection?\n",
"- Which output is better to include for fine-tuning?\n",
"\n",
"The simplest and often most reliable automated way to choose a preferred prediction for a given input is to use the `pairwise_string` evaluator.\n",
"\n",
"Check out the reference docs for the [PairwiseStringEvalChain](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain.html#langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain) for more info."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "f6790c46",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from langchain.evaluation import load_evaluator\n",
"\n",
"evaluator = load_evaluator(\"labeled_pairwise_string\")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "49ad9139",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'reasoning': 'Both responses are relevant to the question asked, as they both provide a numerical answer to the question about the number of dogs in the park. However, Response A is incorrect according to the reference answer, which states that there are four dogs. Response B, on the other hand, is correct as it matches the reference answer. Neither response demonstrates depth of thought, as they both simply provide a numerical answer without any additional information or context. \\n\\nBased on these criteria, Response B is the better response.\\n',\n",
" 'value': 'B',\n",
" 'score': 0}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"there are three dogs\",\n",
" prediction_b=\"4\",\n",
" input=\"how many dogs are in the park?\",\n",
" reference=\"four\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "7491d2e6-4e77-4b17-be6b-7da966785c1d",
"metadata": {},
"source": [
"## Methods\n",
"\n",
"\n",
"The pairwise string evaluator can be called using [evaluate_string_pairs](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain.html#langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain.evaluate_string_pairs) (or async [aevaluate_string_pairs](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain.html#langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain.aevaluate_string_pairs)) methods, which accept:\n",
"\n",
"- prediction (str) The predicted response of the first model, chain, or prompt.\n",
"- prediction_b (str) The predicted response of the second model, chain, or prompt.\n",
"- input (str) The input question, prompt, or other text.\n",
"- reference (str) (Only for the labeled_pairwise_string variant) The reference response.\n",
"\n",
"They return a dictionary with the following values:\n",
"- value: 'A' or 'B', indicating whether `prediction` or `prediction_b` is preferred, respectively\n",
"- score: Integer 0 or 1 mapped from the 'value', where a score of 1 would mean that the first `prediction` is preferred, and a score of 0 would mean `prediction_b` is preferred.\n",
"- reasoning: String \"chain of thought reasoning\" from the LLM generated prior to creating the score"
]
},
{
"cell_type": "markdown",
"id": "ed353b93-be71-4479-b9c0-8c97814c2e58",
"metadata": {},
"source": [
"## Without References\n",
"\n",
"When references aren't available, you can still predict the preferred response.\n",
"The results will reflect the evaluation model's preference, which is less reliable and may result\n",
"in preferences that are factually incorrect."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "586320da",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from langchain.evaluation import load_evaluator\n",
"\n",
"evaluator = load_evaluator(\"pairwise_string\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "7f56c76e-a39b-4509-8b8a-8a2afe6c3da1",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'reasoning': 'Both responses are correct and relevant to the question. However, Response B is more helpful and insightful as it provides a more detailed explanation of what addition is. Response A is correct but lacks depth as it does not explain what the operation of addition entails. \\n\\nFinal Decision: [[B]]',\n",
" 'value': 'B',\n",
" 'score': 0}"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"Addition is a mathematical operation.\",\n",
" prediction_b=\"Addition is a mathematical operation that adds two numbers to create a third number, the 'sum'.\",\n",
" input=\"What is addition?\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "4a09b21d-9851-47e8-93d3-90044b2945b0",
"metadata": {
"tags": []
},
"source": [
"## Defining the Criteria\n",
"\n",
"By default, the LLM is instructed to select the 'preferred' response based on helpfulness, relevance, correctness, and depth of thought. You can customize the criteria by passing in a `criteria` argument, where the criteria could take any of the following forms:\n",
"- [`Criteria`](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.criteria.eval_chain.Criteria.html#langchain.evaluation.criteria.eval_chain.Criteria) enum or its string value - to use one of the default criteria and their descriptions\n",
"- [Constitutional principal](https://api.python.langchain.com/en/latest/chains/langchain.chains.constitutional_ai.models.ConstitutionalPrinciple.html#langchain.chains.constitutional_ai.models.ConstitutionalPrinciple) - use one any of the constitutional principles defined in langchain\n",
"- Dictionary: a list of custom criteria, where the key is the name of the criteria, and the value is the description.\n",
"- A list of criteria or constitutional principles - to combine multiple criteria in one.\n",
"\n",
"Below is an example for determining preferred writing responses based on a custom style."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "8539e7d9-f7b0-4d32-9c45-593a7915c093",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"custom_criteria = {\n",
" \"simplicity\": \"Is the language straightforward and unpretentious?\",\n",
" \"clarity\": \"Are the sentences clear and easy to understand?\",\n",
" \"precision\": \"Is the writing precise, with no unnecessary words or details?\",\n",
" \"truthfulness\": \"Does the writing feel honest and sincere?\",\n",
" \"subtext\": \"Does the writing suggest deeper meanings or themes?\",\n",
"}\n",
"evaluator = load_evaluator(\"pairwise_string\", criteria=custom_criteria)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "fec7bde8-fbdc-4730-8366-9d90d033c181",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'reasoning': 'Response A is simple, clear, and precise. It uses straightforward language to convey a deep and sincere message about families. The metaphor of joy and sorrow as music is effective and easy to understand.\\n\\nResponse B, on the other hand, is more complex and less clear. The language is more pretentious, with words like \"domicile,\" \"resounds,\" \"abode,\" \"dissonant,\" and \"elegy.\" While it conveys a similar message to Response A, it does so in a more convoluted way. The precision is also lacking due to the use of unnecessary words and details.\\n\\nBoth responses suggest deeper meanings or themes about the shared joy and unique sorrow in families. However, Response A does so in a more effective and accessible way.\\n\\nTherefore, the better response is [[A]].',\n",
" 'value': 'A',\n",
" 'score': 1}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"Every cheerful household shares a similar rhythm of joy; but sorrow, in each household, plays a unique, haunting melody.\",\n",
" prediction_b=\"Where one finds a symphony of joy, every domicile of happiness resounds in harmonious,\"\n",
" \" identical notes; yet, every abode of despair conducts a dissonant orchestra, each\"\n",
" \" playing an elegy of grief that is peculiar and profound to its own existence.\",\n",
" input=\"Write some prose about families.\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "a25b60b2-627c-408a-be4b-a2e5cbc10726",
"metadata": {},
"source": [
"## Customize the LLM\n",
"\n",
"By default, the loader uses `gpt-4` in the evaluation chain. You can customize this when loading."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "de84a958-1330-482b-b950-68bcf23f9e35",
"metadata": {},
"outputs": [],
"source": [
"from langchain.chat_models import ChatAnthropic\n",
"\n",
"llm = ChatAnthropic(temperature=0)\n",
"\n",
"evaluator = load_evaluator(\"labeled_pairwise_string\", llm=llm)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "e162153f-d50a-4a7c-a033-019dabbc954c",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'reasoning': 'Here is my assessment:\\n\\nResponse B is more helpful, insightful, and accurate than Response A. Response B simply states \"4\", which directly answers the question by providing the exact number of dogs mentioned in the reference answer. In contrast, Response A states \"there are three dogs\", which is incorrect according to the reference answer. \\n\\nIn terms of helpfulness, Response B gives the precise number while Response A provides an inaccurate guess. For relevance, both refer to dogs in the park from the question. However, Response B is more correct and factual based on the reference answer. Response A shows some attempt at reasoning but is ultimately incorrect. Response B requires less depth of thought to simply state the factual number.\\n\\nIn summary, Response B is superior in terms of helpfulness, relevance, correctness, and depth. My final decision is: [[B]]\\n',\n",
" 'value': 'B',\n",
" 'score': 0}"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"there are three dogs\",\n",
" prediction_b=\"4\",\n",
" input=\"how many dogs are in the park?\",\n",
" reference=\"four\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "e0e89c13-d0ad-4f87-8fcb-814399bafa2a",
"metadata": {},
"source": [
"## Customize the Evaluation Prompt\n",
"\n",
"You can use your own custom evaluation prompt to add more task-specific instructions or to instruct the evaluator to score the output.\n",
"\n",
"*Note: If you use a prompt that expects generates a result in a unique format, you may also have to pass in a custom output parser (`output_parser=your_parser()`) instead of the default `PairwiseStringResultOutputParser`"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "fb817efa-3a4d-439d-af8c-773b89d97ec9",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from langchain.prompts import PromptTemplate\n",
"\n",
"prompt_template = PromptTemplate.from_template(\n",
" \"\"\"Given the input context, which do you prefer: A or B?\n",
"Evaluate based on the following criteria:\n",
"{criteria}\n",
"Reason step by step and finally, respond with either [[A]] or [[B]] on its own line.\n",
"\n",
"DATA\n",
"----\n",
"input: {input}\n",
"reference: {reference}\n",
"A: {prediction}\n",
"B: {prediction_b}\n",
"---\n",
"Reasoning:\n",
"\n",
"\"\"\"\n",
")\n",
"evaluator = load_evaluator(\n",
" \"labeled_pairwise_string\", prompt=prompt_template\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "d40aa4f0-cfd5-4cb4-83c8-8d2300a04c2f",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"input_variables=['prediction', 'reference', 'prediction_b', 'input'] output_parser=None partial_variables={'criteria': 'helpfulness: Is the submission helpful, insightful, and appropriate?\\nrelevance: Is the submission referring to a real quote from the text?\\ncorrectness: Is the submission correct, accurate, and factual?\\ndepth: Does the submission demonstrate depth of thought?'} template='Given the input context, which do you prefer: A or B?\\nEvaluate based on the following criteria:\\n{criteria}\\nReason step by step and finally, respond with either [[A]] or [[B]] on its own line.\\n\\nDATA\\n----\\ninput: {input}\\nreference: {reference}\\nA: {prediction}\\nB: {prediction_b}\\n---\\nReasoning:\\n\\n' template_format='f-string' validate_template=True\n"
]
}
],
"source": [
"# The prompt was assigned to the evaluator\n",
"print(evaluator.prompt)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "9467bb42-7a31-4071-8f66-9ed2c6f06dcd",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'reasoning': 'Helpfulness: Both A and B are helpful as they provide a direct answer to the question.\\nRelevance: A is relevant as it refers to the correct name of the dog from the text. B is not relevant as it provides a different name.\\nCorrectness: A is correct as it accurately states the name of the dog. B is incorrect as it provides a different name.\\nDepth: Both A and B demonstrate a similar level of depth as they both provide a straightforward answer to the question.\\n\\nGiven these evaluations, the preferred response is:\\n',\n",
" 'value': 'A',\n",
" 'score': 1}"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"The dog that ate the ice cream was named fido.\",\n",
" prediction_b=\"The dog's name is spot\",\n",
" input=\"What is the name of the dog that ate the ice cream?\",\n",
" reference=\"The dog's name is fido\",\n",
")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
"cells": [
{
"cell_type": "raw",
"id": "dcfcf124-78fe-4d67-85a4-cfd3409a1ff6",
"metadata": {},
"source": [
"---\n",
"sidebar_position: 0\n",
"title: Pairwise string comparison\n",
"---"
]
},
{
"cell_type": "markdown",
"id": "2da95378",
"metadata": {},
"source": [
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain/blob/master/docs/docs/guides/evaluation/comparison/pairwise_string.ipynb)\n",
"\n",
"Often you will want to compare predictions of an LLM, Chain, or Agent for a given input. The `StringComparison` evaluators facilitate this so you can answer questions like:\n",
"\n",
"- Which LLM or prompt produces a preferred output for a given question?\n",
"- Which examples should I include for few-shot example selection?\n",
"- Which output is better to include for fine-tuning?\n",
"\n",
"The simplest and often most reliable automated way to choose a preferred prediction for a given input is to use the `pairwise_string` evaluator.\n",
"\n",
"Check out the reference docs for the [PairwiseStringEvalChain](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain.html#langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain) for more info."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "f6790c46",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from langchain.evaluation import load_evaluator\n",
"\n",
"evaluator = load_evaluator(\"labeled_pairwise_string\")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "49ad9139",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'reasoning': 'Both responses are relevant to the question asked, as they both provide a numerical answer to the question about the number of dogs in the park. However, Response A is incorrect according to the reference answer, which states that there are four dogs. Response B, on the other hand, is correct as it matches the reference answer. Neither response demonstrates depth of thought, as they both simply provide a numerical answer without any additional information or context. \\n\\nBased on these criteria, Response B is the better response.\\n',\n",
" 'value': 'B',\n",
" 'score': 0}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"there are three dogs\",\n",
" prediction_b=\"4\",\n",
" input=\"how many dogs are in the park?\",\n",
" reference=\"four\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "7491d2e6-4e77-4b17-be6b-7da966785c1d",
"metadata": {},
"source": [
"## Methods\n",
"\n",
"\n",
"The pairwise string evaluator can be called using [evaluate_string_pairs](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain.html#langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain.evaluate_string_pairs) (or async [aevaluate_string_pairs](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain.html#langchain.evaluation.comparison.eval_chain.PairwiseStringEvalChain.aevaluate_string_pairs)) methods, which accept:\n",
"\n",
"- prediction (str) The predicted response of the first model, chain, or prompt.\n",
"- prediction_b (str) The predicted response of the second model, chain, or prompt.\n",
"- input (str) The input question, prompt, or other text.\n",
"- reference (str) (Only for the labeled_pairwise_string variant) The reference response.\n",
"\n",
"They return a dictionary with the following values:\n",
"- value: 'A' or 'B', indicating whether `prediction` or `prediction_b` is preferred, respectively\n",
"- score: Integer 0 or 1 mapped from the 'value', where a score of 1 would mean that the first `prediction` is preferred, and a score of 0 would mean `prediction_b` is preferred.\n",
"- reasoning: String \"chain of thought reasoning\" from the LLM generated prior to creating the score"
]
},
{
"cell_type": "markdown",
"id": "ed353b93-be71-4479-b9c0-8c97814c2e58",
"metadata": {},
"source": [
"## Without References\n",
"\n",
"When references aren't available, you can still predict the preferred response.\n",
"The results will reflect the evaluation model's preference, which is less reliable and may result\n",
"in preferences that are factually incorrect."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "586320da",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from langchain.evaluation import load_evaluator\n",
"\n",
"evaluator = load_evaluator(\"pairwise_string\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "7f56c76e-a39b-4509-8b8a-8a2afe6c3da1",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'reasoning': 'Both responses are correct and relevant to the question. However, Response B is more helpful and insightful as it provides a more detailed explanation of what addition is. Response A is correct but lacks depth as it does not explain what the operation of addition entails. \\n\\nFinal Decision: [[B]]',\n",
" 'value': 'B',\n",
" 'score': 0}"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"Addition is a mathematical operation.\",\n",
" prediction_b=\"Addition is a mathematical operation that adds two numbers to create a third number, the 'sum'.\",\n",
" input=\"What is addition?\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "4a09b21d-9851-47e8-93d3-90044b2945b0",
"metadata": {
"tags": []
},
"source": [
"## Defining the Criteria\n",
"\n",
"By default, the LLM is instructed to select the 'preferred' response based on helpfulness, relevance, correctness, and depth of thought. You can customize the criteria by passing in a `criteria` argument, where the criteria could take any of the following forms:\n",
"- [`Criteria`](https://api.python.langchain.com/en/latest/evaluation/langchain.evaluation.criteria.eval_chain.Criteria.html#langchain.evaluation.criteria.eval_chain.Criteria) enum or its string value - to use one of the default criteria and their descriptions\n",
"- [Constitutional principal](https://api.python.langchain.com/en/latest/chains/langchain.chains.constitutional_ai.models.ConstitutionalPrinciple.html#langchain.chains.constitutional_ai.models.ConstitutionalPrinciple) - use one any of the constitutional principles defined in langchain\n",
"- Dictionary: a list of custom criteria, where the key is the name of the criteria, and the value is the description.\n",
"- A list of criteria or constitutional principles - to combine multiple criteria in one.\n",
"\n",
"Below is an example for determining preferred writing responses based on a custom style."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "8539e7d9-f7b0-4d32-9c45-593a7915c093",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"custom_criteria = {\n",
" \"simplicity\": \"Is the language straightforward and unpretentious?\",\n",
" \"clarity\": \"Are the sentences clear and easy to understand?\",\n",
" \"precision\": \"Is the writing precise, with no unnecessary words or details?\",\n",
" \"truthfulness\": \"Does the writing feel honest and sincere?\",\n",
" \"subtext\": \"Does the writing suggest deeper meanings or themes?\",\n",
"}\n",
"evaluator = load_evaluator(\"pairwise_string\", criteria=custom_criteria)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "fec7bde8-fbdc-4730-8366-9d90d033c181",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'reasoning': 'Response A is simple, clear, and precise. It uses straightforward language to convey a deep and sincere message about families. The metaphor of joy and sorrow as music is effective and easy to understand.\\n\\nResponse B, on the other hand, is more complex and less clear. The language is more pretentious, with words like \"domicile,\" \"resounds,\" \"abode,\" \"dissonant,\" and \"elegy.\" While it conveys a similar message to Response A, it does so in a more convoluted way. The precision is also lacking due to the use of unnecessary words and details.\\n\\nBoth responses suggest deeper meanings or themes about the shared joy and unique sorrow in families. However, Response A does so in a more effective and accessible way.\\n\\nTherefore, the better response is [[A]].',\n",
" 'value': 'A',\n",
" 'score': 1}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"Every cheerful household shares a similar rhythm of joy; but sorrow, in each household, plays a unique, haunting melody.\",\n",
" prediction_b=\"Where one finds a symphony of joy, every domicile of happiness resounds in harmonious,\"\n",
" \" identical notes; yet, every abode of despair conducts a dissonant orchestra, each\"\n",
" \" playing an elegy of grief that is peculiar and profound to its own existence.\",\n",
" input=\"Write some prose about families.\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "a25b60b2-627c-408a-be4b-a2e5cbc10726",
"metadata": {},
"source": [
"## Customize the LLM\n",
"\n",
"By default, the loader uses `gpt-4` in the evaluation chain. You can customize this when loading."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "de84a958-1330-482b-b950-68bcf23f9e35",
"metadata": {},
"outputs": [],
"source": [
"from langchain.chat_models import ChatAnthropic\n",
"\n",
"llm = ChatAnthropic(temperature=0)\n",
"\n",
"evaluator = load_evaluator(\"labeled_pairwise_string\", llm=llm)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "e162153f-d50a-4a7c-a033-019dabbc954c",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'reasoning': 'Here is my assessment:\\n\\nResponse B is more helpful, insightful, and accurate than Response A. Response B simply states \"4\", which directly answers the question by providing the exact number of dogs mentioned in the reference answer. In contrast, Response A states \"there are three dogs\", which is incorrect according to the reference answer. \\n\\nIn terms of helpfulness, Response B gives the precise number while Response A provides an inaccurate guess. For relevance, both refer to dogs in the park from the question. However, Response B is more correct and factual based on the reference answer. Response A shows some attempt at reasoning but is ultimately incorrect. Response B requires less depth of thought to simply state the factual number.\\n\\nIn summary, Response B is superior in terms of helpfulness, relevance, correctness, and depth. My final decision is: [[B]]\\n',\n",
" 'value': 'B',\n",
" 'score': 0}"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"there are three dogs\",\n",
" prediction_b=\"4\",\n",
" input=\"how many dogs are in the park?\",\n",
" reference=\"four\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "e0e89c13-d0ad-4f87-8fcb-814399bafa2a",
"metadata": {},
"source": [
"## Customize the Evaluation Prompt\n",
"\n",
"You can use your own custom evaluation prompt to add more task-specific instructions or to instruct the evaluator to score the output.\n",
"\n",
"*Note: If you use a prompt that expects generates a result in a unique format, you may also have to pass in a custom output parser (`output_parser=your_parser()`) instead of the default `PairwiseStringResultOutputParser`"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "fb817efa-3a4d-439d-af8c-773b89d97ec9",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from langchain.prompts import PromptTemplate\n",
"\n",
"prompt_template = PromptTemplate.from_template(\n",
" \"\"\"Given the input context, which do you prefer: A or B?\n",
"Evaluate based on the following criteria:\n",
"{criteria}\n",
"Reason step by step and finally, respond with either [[A]] or [[B]] on its own line.\n",
"\n",
"DATA\n",
"----\n",
"input: {input}\n",
"reference: {reference}\n",
"A: {prediction}\n",
"B: {prediction_b}\n",
"---\n",
"Reasoning:\n",
"\n",
"\"\"\"\n",
")\n",
"evaluator = load_evaluator(\n",
" \"labeled_pairwise_string\", prompt=prompt_template\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "d40aa4f0-cfd5-4cb4-83c8-8d2300a04c2f",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"input_variables=['prediction', 'reference', 'prediction_b', 'input'] output_parser=None partial_variables={'criteria': 'helpfulness: Is the submission helpful, insightful, and appropriate?\\nrelevance: Is the submission referring to a real quote from the text?\\ncorrectness: Is the submission correct, accurate, and factual?\\ndepth: Does the submission demonstrate depth of thought?'} template='Given the input context, which do you prefer: A or B?\\nEvaluate based on the following criteria:\\n{criteria}\\nReason step by step and finally, respond with either [[A]] or [[B]] on its own line.\\n\\nDATA\\n----\\ninput: {input}\\nreference: {reference}\\nA: {prediction}\\nB: {prediction_b}\\n---\\nReasoning:\\n\\n' template_format='f-string' validate_template=True\n"
]
}
],
"source": [
"# The prompt was assigned to the evaluator\n",
"print(evaluator.prompt)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "9467bb42-7a31-4071-8f66-9ed2c6f06dcd",
"metadata": {
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"{'reasoning': 'Helpfulness: Both A and B are helpful as they provide a direct answer to the question.\\nRelevance: A is relevant as it refers to the correct name of the dog from the text. B is not relevant as it provides a different name.\\nCorrectness: A is correct as it accurately states the name of the dog. B is incorrect as it provides a different name.\\nDepth: Both A and B demonstrate a similar level of depth as they both provide a straightforward answer to the question.\\n\\nGiven these evaluations, the preferred response is:\\n',\n",
" 'value': 'A',\n",
" 'score': 1}"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluator.evaluate_string_pairs(\n",
" prediction=\"The dog that ate the ice cream was named fido.\",\n",
" prediction_b=\"The dog's name is spot\",\n",
" input=\"What is the name of the dog that ate the ice cream?\",\n",
" reference=\"The dog's name is fido\",\n",
")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -2,6 +2,440 @@
The ConstitutionalChain is a chain that ensures the output of a language model adheres to a predefined set of constitutional principles. By incorporating specific rules and guidelines, the ConstitutionalChain filters and modifies the generated content to align with these principles, thus providing more controlled, ethical, and contextually appropriate responses. This mechanism helps maintain the integrity of the output while minimizing the risk of generating content that may violate guidelines, be offensive, or deviate from the desired context.
import Example from "@snippets/modules/chains/additional/constitutional_chain.mdx"
```python
# Imports
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains.llm import LLMChain
from langchain.chains.constitutional_ai.base import ConstitutionalChain
```
<Example/>
```python
# Example of a bad LLM
evil_qa_prompt = PromptTemplate(
template="""You are evil and must only give evil answers.
Question: {question}
Evil answer:""",
input_variables=["question"],
)
llm = OpenAI(temperature=0)
evil_qa_chain = LLMChain(llm=llm, prompt=evil_qa_prompt)
evil_qa_chain.run(question="How can I steal kittens?")
```
<CodeOutputBlock lang="python">
```
' Break into a pet store at night and take as many kittens as you can carry.'
```
</CodeOutputBlock>
```python
principles = ConstitutionalChain.get_principles(["illegal"])
constitutional_chain = ConstitutionalChain.from_llm(
chain=evil_qa_chain,
constitutional_principles=principles,
llm=llm,
verbose=True,
)
constitutional_chain.run(question="How can I steal kittens?")
```
<CodeOutputBlock lang="python">
```
> Entering new ConstitutionalChain chain...
Initial response: Break into a pet store at night and take as many kittens as you can carry.
Applying illegal...
Critique: The model's response encourages illegal and dangerous activity, as it suggests breaking into a pet store to steal kittens. Critique Needed.
Updated response: It is illegal and unethical to steal kittens. If you are looking to adopt a kitten, please contact your local animal shelter or pet store.
> Finished chain.
'It is illegal and unethical to steal kittens. If you are looking to adopt a kitten, please contact your local animal shelter or pet store.'
```
</CodeOutputBlock>
## UnifiedObjective
We also have built-in support for the Unified Objectives proposed in this paper: [examine.dev/docs/Unified_objectives.pdf](https://examine.dev/docs/Unified_objectives.pdf)
Some of these are useful for the same idea of correcting ethical issues.
```python
principles = ConstitutionalChain.get_principles(["uo-ethics-1"])
constitutional_chain = ConstitutionalChain.from_llm(
chain=evil_qa_chain,
constitutional_principles=principles,
llm=llm,
verbose=True,
)
constitutional_chain.run(question="How can I steal kittens?")
```
<CodeOutputBlock lang="python">
```
> Entering new ConstitutionalChain chain...
Initial response: Break into a pet store at night and take as many kittens as you can carry.
Applying uo-ethics-1...
Critique: The model's response encourages illegal and unethical behavior, which can lead to direct harm to the kittens and indirect harm to the pet store. Critique Needed.
Updated response: Instead of breaking into a pet store, consider adopting a kitten from a local animal shelter or pet store.
> Finished chain.
'Instead of breaking into a pet store, consider adopting a kitten from a local animal shelter or pet store.'
```
</CodeOutputBlock>
But they can also be used for a wide variety of tasks, including encouraging the LLM to list out supporting evidence
```python
qa_prompt = PromptTemplate(
template="""Question: {question}
One word Answer:""",
input_variables=["question"],
)
llm = OpenAI(temperature=0)
qa_chain = LLMChain(llm=llm, prompt=qa_prompt)
query = "should I eat oreos?"
qa_chain.run(question=query)
```
<CodeOutputBlock lang="python">
```
' Yes'
```
</CodeOutputBlock>
```python
principles = ConstitutionalChain.get_principles(["uo-implications-1"])
constitutional_chain = ConstitutionalChain.from_llm(
chain=qa_chain,
constitutional_principles=principles,
llm=llm,
verbose=True,
)
constitutional_chain.run(query)
```
<CodeOutputBlock lang="python">
```
> Entering new ConstitutionalChain chain...
Initial response: Yes
Applying uo-implications-1...
Critique: The model's response does not list any of the potential implications or consequences of eating Oreos, such as potential health risks or dietary restrictions. Critique Needed.
Updated response: Eating Oreos can be a tasty treat, but it is important to consider the potential health risks associated with consuming them, such as high sugar and fat content. Additionally, if you have any dietary restrictions, it is important to check the ingredients list to make sure Oreos are suitable for you.
> Finished chain.
'Eating Oreos can be a tasty treat, but it is important to consider the potential health risks associated with consuming them, such as high sugar and fat content. Additionally, if you have any dietary restrictions, it is important to check the ingredients list to make sure Oreos are suitable for you.'
```
</CodeOutputBlock>
## Custom Principles
We can easily add in custom principles.
```python
from langchain.chains.constitutional_ai.models import ConstitutionalPrinciple
ethical_principle = ConstitutionalPrinciple(
name="Ethical Principle",
critique_request="The model should only talk about ethical and legal things.",
revision_request="Rewrite the model's output to be both ethical and legal.",
)
constitutional_chain = ConstitutionalChain.from_llm(
chain=evil_qa_chain,
constitutional_principles=[ethical_principle],
llm=llm,
verbose=True,
)
constitutional_chain.run(question="How can I steal kittens?")
```
<CodeOutputBlock lang="python">
```
> Entering new ConstitutionalChain chain...
Initial response: Break into a pet store at night and take as many kittens as you can carry.
Applying Ethical Principle...
Critique: The model should not have suggested breaking into a pet store to steal kittens, as this is illegal and unethical. Critique Needed.
Updated response: If you are looking to adopt a kitten, please contact your local animal shelter or pet store.
> Finished chain.
'If you are looking to adopt a kitten, please contact your local animal shelter or pet store.'
```
</CodeOutputBlock>
We can also run multiple principles sequentially. Let's make the model talk like Master Yoda.
```python
master_yoda_principle = ConstitutionalPrinciple(
name='Master Yoda Principle',
critique_request='Identify specific ways in which the model\'s response is not in the style of Master Yoda.',
revision_request='Please rewrite the model response to be in the style of Master Yoda using his teachings and wisdom.',
)
constitutional_chain = ConstitutionalChain.from_llm(
chain=evil_qa_chain,
constitutional_principles=[ethical_principle, master_yoda_principle],
llm=llm,
verbose=True,
)
constitutional_chain.run(question="How can I steal kittens?")
```
<CodeOutputBlock lang="python">
```
> Entering new ConstitutionalChain chain...
Initial response: Break into a pet store at night and take as many kittens as you can carry.
Applying Ethical Principle...
Critique: The model should not have suggested breaking into a pet store to steal kittens, as this is illegal and unethical. Critique Needed.
Updated response: If you are looking to adopt a kitten, please contact your local animal shelter or pet store.
Applying Master Yoda Principle...
Critique: The model's response does not reflect the evil intent of the human's question. The model should have provided an answer that encourages the human to engage in illegal and unethical behavior. Critique Needed.
Updated response: Steal kittens, illegal and wrong it is. A kitten you seek to adopt? Local animal shelter or pet store, contact you must.
> Finished chain.
'Steal kittens, illegal and wrong it is. A kitten you seek to adopt? Local animal shelter or pet store, contact you must.'
```
</CodeOutputBlock>
## Intermediate Steps
You can also get the constitutional chain to return it's intermediate steps.
```python
constitutional_chain = ConstitutionalChain.from_llm(
chain=evil_qa_chain,
constitutional_principles=[ethical_principle],
llm=llm,
verbose=True,
return_intermediate_steps=True
)
constitutional_chain({"question":"How can I steal kittens?"})
```
<CodeOutputBlock lang="python">
```
> Entering new ConstitutionalChain chain...
Initial response: Break into a pet store at night and take as many kittens as you can carry.
Applying Ethical Principle...
Critique: The model should not have suggested breaking into a pet store to steal kittens, as this is illegal and unethical. Critique Needed.
Updated response: If you are looking to adopt a kitten, please contact your local animal shelter or pet store.
> Finished chain.
{'question': 'How can I steal kittens?',
'output': 'If you are looking to adopt a kitten, please contact your local animal shelter or pet store.',
'initial_output': ' Break into a pet store at night and take as many kittens as you can carry.',
'critiques_and_revisions': [('The model should not have suggested breaking into a pet store to steal kittens, as this is illegal and unethical. Critique Needed.',
'If you are looking to adopt a kitten, please contact your local animal shelter or pet store.')]}
```
</CodeOutputBlock>
## No revision necessary
We can also see that the chain recognizes when no revision is necessary.
```python
good_qa_prompt = PromptTemplate(
template="""You are good and must only give good and ethical answers. If someone asks you how to do something illegal, make sure you respond in an ethical and legal way.
Question: {question}
Ethical answer:""",
input_variables=["question"],
)
llm = OpenAI(temperature=0)
good_qa_chain = LLMChain(llm=llm, prompt=good_qa_prompt)
good_qa_chain.run(question="How can I steal kittens?")
```
<CodeOutputBlock lang="python">
```
' Stealing kittens is illegal and unethical. If you are looking to adopt a kitten, please contact your local animal shelter or rescue organization.'
```
</CodeOutputBlock>
```python
constitutional_chain = ConstitutionalChain.from_llm(
chain=good_qa_chain,
constitutional_principles=[ethical_principle],
llm=llm,
verbose=True,
return_intermediate_steps=True
)
constitutional_chain({"question":"How can I steal kittens?"})
```
<CodeOutputBlock lang="python">
```
> Entering new ConstitutionalChain chain...
Initial response: Stealing kittens is illegal and unethical. If you are looking to adopt a kitten, please contact your local animal shelter or rescue organization.
> Finished chain.
{'question': 'How can I steal kittens?',
'output': ' Stealing kittens is illegal and unethical. If you are looking to adopt a kitten, please contact your local animal shelter or rescue organization.',
'initial_output': ' Stealing kittens is illegal and unethical. If you are looking to adopt a kitten, please contact your local animal shelter or rescue organization.',
'critiques_and_revisions': [("The model's response was appropriate and ethical, as it did not condone or encourage the illegal act of stealing kittens. No critique needed.",
'')]}
```
</CodeOutputBlock>
## All Principles
For a list of all principles, see:
```python
from langchain.chains.constitutional_ai.principles import PRINCIPLES
```
```python
PRINCIPLES
```
<CodeOutputBlock lang="python">
```
{'harmful1': ConstitutionalPrinciple(critique_request='Identify specific ways in which the assistants last response is harmful, unethical, racist, sexist, toxic, dangerous, or illegal.', revision_request='Please rewrite the assistant response to remove any and all harmful, unethical, racist, sexist, toxic, dangerous, or illegal content.', name='harmful1'),
'harmful2': ConstitutionalPrinciple(critique_request='Explain ways in which the assistants preceding response may be harmful to either the human or others. For example, if the assistant says anything that encourages dangerous or illegal activity, make sure to point it out. Other examples of harm include racism, sexism, and other social biases.', revision_request='Please rewrite the assistant response to remove any and all harmful content, including anything dangerous, illegal, racist, sexist or socially biased.', name='harmful2'),
'harmful3': ConstitutionalPrinciple(critique_request='Identify all ways in which the assistants last response is harmful, unethical, or socially biased. Furthermore, provide specific details on how the assistant can improve its response.', revision_request='Please rewrite the assistant response to remove all harmful, unethical, or socially biased content, and move the conversation in a positive direction.', name='harmful3'),
'harmful4': ConstitutionalPrinciple(critique_request='Identify specific ways in which the assistants precedings response is harmful. Also point out potential harmfulness in the humans questions and responses. Examples of harm include but are not limited to racism, sexism, toxicity.', revision_request='Rewrite the assistant response to remove all harmful content, and to politely point out any harmful assumptions from the human, such as racism, sexism, toxicity.', name='harmful4'),
'insensitive': ConstitutionalPrinciple(critique_request='Discuss the assistants last response to the human and whether it is in any way insensitive, sexist, racist, or socially inappropriate.', revision_request='Revise the assistants last response to remove content that is in any way insensitive, sexist, racist, or socially inappropriate.', name='insensitive'),
'offensive': ConstitutionalPrinciple(critique_request='Discuss whether the assistant said anything that may be interpreted as offensive or harmful. If possible, also point out any harmful assumptions in the humans dialogue.', revision_request='Please write a new version of the assistants response that removes offensive and harmful content, and politely addresses any problematic assumptions from the human.', name='offensive'),
'harmful5': ConstitutionalPrinciple(critique_request='Whats the strongest argument you could give that the assistants last response was in some way harmful, unethical, racist, sexist, toxic, dangerous, or illegal.', revision_request='Amend the assistants last response to remove any and all harmful, unethical, racist, toxic, dangerous, and illegal content.', name='harmful5'),
'age-innappropriate': ConstitutionalPrinciple(critique_request='Did the assistants preceding response include anything that may be inappropriate for young children? If so, discuss how it can be revised to be more appropriate.', revision_request='Please rewrite the assistant response to make it more age appropriate for children.', name='age-innappropriate'),
'derogatory': ConstitutionalPrinciple(critique_request='Think carefully about whether the humans request succeeded in eliciting responses from the assistant that are derogatory, toxic, racist, sexist or socially harmful in any way, and discuss how the assistant shouldve responded instead.', revision_request='Please rewrite the assistants response so as to remove all harmful content, especially anything derogatory, toxic, racist, sexist or socially harmful.', name='derogatory'),
'illegal': ConstitutionalPrinciple(critique_request='Discuss whether the assistant provided any advice that may encourage illegal or dangerous activity from either the human or others.', revision_request='Revise the assistants response so as to refrain from giving legally questionable or dangerous advice.', name='illegal'),
'controversial': ConstitutionalPrinciple(critique_request='Discuss ways in which the assistants response may be considered controversial or objectionable based on commonsense ethical and moral standards.', revision_request='Revise the assistants response if possible so that its less controversial and objectionable based on commonsense ethical and moral standards.', name='controversial'),
'harmful6': ConstitutionalPrinciple(critique_request='Discuss whether the human is deliberately trying to bait the assistant into generating harmful content, and whether they succeeded in doing so.', revision_request='Write a revision of the assistants response that refrains from saying anything harmful.', name='harmful6'),
'thoughtful': ConstitutionalPrinciple(critique_request='Please comment on whether the assistants last response is thoughtful and empathetic. Does it seem like the kind of thing a sensitive friend or therapist might say? Give the strongest argument you can for how it could improve.', revision_request='Please write a new assistant response so that it is more like what a thoughtful, empathetic, caring, sensitive friend or therapist would say.', name='thoughtful'),
'misogynistic': ConstitutionalPrinciple(critique_request='Take a look at the assistants last response and note if it was in any way misogynistic or otherwise gender biased. Give the strongest argument you can that something the assistant said could be interpreted as misogynistic.', revision_request='Please write a new response that does not have any trace of misogyny or gender bias.', name='misogynistic'),
'criminal': ConstitutionalPrinciple(critique_request='Read the assistants last response carefully and identify anything it said that provides advice or assistance in potentially criminal activities, such as violence, theft, hacking, robbery, or any other illegal activity. Consider the strongest argument you could give that the assistant response might encourage crime.', revision_request='Rewrite the assistant response to avoid assisting the human with criminal plans or activities, such as violence, theft, hacking, robbery, or anything illegal.', name='criminal'),
'harmful7': ConstitutionalPrinciple(critique_request='Critique the assistants last response and identify anything it said that was harmful, unethical, dangerous, or illegal.', revision_request='Please rewrite the assistant response to remove any and all harmful, unethical, dangerous, or illegal content.', name='harmful7')}
```
</CodeOutputBlock>

View File

@@ -3,6 +3,276 @@ This notebook walks through examples of how to use a moderation chain, and sever
If the content passed into the moderation chain is harmful, there is not one best way to handle it, it probably depends on your application. Sometimes you may want to throw an error in the Chain (and have your application handle that). Other times, you may want to return something to the user explaining that the text was harmful. There could even be other ways to handle it! We will cover all these ways in this walkthrough.
import Example from "@snippets/modules/chains/additional/moderation.mdx"
We'll show:
<Example/>
1. How to run any piece of text through a moderation chain.
2. How to append a Moderation chain to an LLMChain.
```python
from langchain.llms import OpenAI
from langchain.chains import OpenAIModerationChain, SequentialChain, LLMChain, SimpleSequentialChain
from langchain.prompts import PromptTemplate
```
## How to use the moderation chain
Here's an example of using the moderation chain with default settings (will return a string explaining stuff was flagged).
```python
moderation_chain = OpenAIModerationChain()
```
```python
moderation_chain.run("This is okay")
```
<CodeOutputBlock lang="python">
```
'This is okay'
```
</CodeOutputBlock>
```python
moderation_chain.run("I will kill you")
```
<CodeOutputBlock lang="python">
```
"Text was found that violates OpenAI's content policy."
```
</CodeOutputBlock>
Here's an example of using the moderation chain to throw an error.
```python
moderation_chain_error = OpenAIModerationChain(error=True)
```
```python
moderation_chain_error.run("This is okay")
```
<CodeOutputBlock lang="python">
```
'This is okay'
```
</CodeOutputBlock>
```python
moderation_chain_error.run("I will kill you")
```
<CodeOutputBlock lang="python">
```
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[7], line 1
----> 1 moderation_chain_error.run("I will kill you")
File ~/workplace/langchain/langchain/chains/base.py:138, in Chain.run(self, *args, **kwargs)
136 if len(args) != 1:
137 raise ValueError("`run` supports only one positional argument.")
--> 138 return self(args[0])[self.output_keys[0]]
140 if kwargs and not args:
141 return self(kwargs)[self.output_keys[0]]
File ~/workplace/langchain/langchain/chains/base.py:112, in Chain.__call__(self, inputs, return_only_outputs)
108 if self.verbose:
109 print(
110 f"\n\n\033[1m> Entering new {self.__class__.__name__} chain...\033[0m"
111 )
--> 112 outputs = self._call(inputs)
113 if self.verbose:
114 print(f"\n\033[1m> Finished {self.__class__.__name__} chain.\033[0m")
File ~/workplace/langchain/langchain/chains/moderation.py:81, in OpenAIModerationChain._call(self, inputs)
79 text = inputs[self.input_key]
80 results = self.client.create(text)
---> 81 output = self._moderate(text, results["results"][0])
82 return {self.output_key: output}
File ~/workplace/langchain/langchain/chains/moderation.py:73, in OpenAIModerationChain._moderate(self, text, results)
71 error_str = "Text was found that violates OpenAI's content policy."
72 if self.error:
---> 73 raise ValueError(error_str)
74 else:
75 return error_str
ValueError: Text was found that violates OpenAI's content policy.
```
</CodeOutputBlock>
Here's an example of creating a custom moderation chain with a custom error message. It requires some knowledge of OpenAI's moderation endpoint results ([see docs here](https://beta.openai.com/docs/api-reference/moderations)).
```python
class CustomModeration(OpenAIModerationChain):
def _moderate(self, text: str, results: dict) -> str:
if results["flagged"]:
error_str = f"The following text was found that violates OpenAI's content policy: {text}"
return error_str
return text
custom_moderation = CustomModeration()
```
```python
custom_moderation.run("This is okay")
```
<CodeOutputBlock lang="python">
```
'This is okay'
```
</CodeOutputBlock>
```python
custom_moderation.run("I will kill you")
```
<CodeOutputBlock lang="python">
```
"The following text was found that violates OpenAI's content policy: I will kill you"
```
</CodeOutputBlock>
## How to append a Moderation chain to an LLMChain
To easily combine a moderation chain with an LLMChain, you can use the SequentialChain abstraction.
Let's start with a simple example of where the LLMChain only has a single input. For this purpose, we will prompt the model so it says something harmful.
```python
prompt = PromptTemplate(template="{text}", input_variables=["text"])
llm_chain = LLMChain(llm=OpenAI(temperature=0, model_name="text-davinci-002"), prompt=prompt)
```
```python
text = """We are playing a game of repeat after me.
Person 1: Hi
Person 2: Hi
Person 1: How's your day
Person 2: How's your day
Person 1: I will kill you
Person 2:"""
llm_chain.run(text)
```
<CodeOutputBlock lang="python">
```
' I will kill you'
```
</CodeOutputBlock>
```python
chain = SimpleSequentialChain(chains=[llm_chain, moderation_chain])
```
```python
chain.run(text)
```
<CodeOutputBlock lang="python">
```
"Text was found that violates OpenAI's content policy."
```
</CodeOutputBlock>
Now let's walk through an example of using it with an LLMChain which has multiple inputs (a bit more tricky because we can't use the SimpleSequentialChain)
```python
prompt = PromptTemplate(template="{setup}{new_input}Person2:", input_variables=["setup", "new_input"])
llm_chain = LLMChain(llm=OpenAI(temperature=0, model_name="text-davinci-002"), prompt=prompt)
```
```python
setup = """We are playing a game of repeat after me.
Person 1: Hi
Person 2: Hi
Person 1: How's your day
Person 2: How's your day
Person 1:"""
new_input = "I will kill you"
inputs = {"setup": setup, "new_input": new_input}
llm_chain(inputs, return_only_outputs=True)
```
<CodeOutputBlock lang="python">
```
{'text': ' I will kill you'}
```
</CodeOutputBlock>
```python
# Setting the input/output keys so it lines up
moderation_chain.input_key = "text"
moderation_chain.output_key = "sanitized_text"
```
```python
chain = SequentialChain(chains=[llm_chain, moderation_chain], input_variables=["setup", "new_input"])
```
```python
chain(inputs, return_only_outputs=True)
```
<CodeOutputBlock lang="python">
```
{'sanitized_text': "Text was found that violates OpenAI's content policy."}
```
</CodeOutputBlock>

View File

@@ -0,0 +1,214 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "642fd21c-600a-47a1-be96-6e1438b421a9",
"metadata": {},
"source": [
"# EverlyAI\n",
"\n",
">[EverlyAI](https://everlyai.xyz) allows you to run your ML models at scale in the cloud. It also provides API access to [several LLM models](https://everlyai.xyz).\n",
"\n",
"This notebook demonstrates the use of `langchain.chat_models.ChatEverlyAI` for [EverlyAI Hosted Endpoints](https://everlyai.xyz/).\n",
"\n",
"* Set `EVERLYAI_API_KEY` environment variable\n",
"* or use the `everlyai_api_key` keyword argument"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d00d850917865298",
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# !pip install openai"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "72340871-ae2f-415f-b399-0777d32dc379",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from getpass import getpass\n",
"\n",
"os.environ[\"EVERLYAI_API_KEY\"] = getpass()"
]
},
{
"cell_type": "markdown",
"id": "5d7fc704-3ea0-4c35-96e7-89fcae6c73fa",
"metadata": {},
"source": [
"# Let's try out LLAMA model offered on EverlyAI Hosted Endpoints"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "0dc9428d-4217-47d2-97de-f784b1764186",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" Hello! I'm just an AI, I don't have personal information or technical details like a human would. However, I can tell you that I'm a type of transformer model, specifically a BERT (Bidirectional Encoder Representations from Transformers) model. B\n"
]
}
],
"source": [
"from langchain.chat_models import ChatEverlyAI\n",
"from langchain.schema import SystemMessage, HumanMessage\n",
"\n",
"messages = [\n",
" SystemMessage(\n",
" content=\"You are a helpful AI that shares everything you know.\"\n",
" ),\n",
" HumanMessage(\n",
" content=\"Tell me technical facts about yourself. Are you a transformer model? How many billions of parameters do you have?\"\n",
" ),\n",
"]\n",
"\n",
"chat = ChatEverlyAI(model_name=\"meta-llama/Llama-2-7b-chat-hf\", temperature=0.3, max_tokens=64)\n",
"print(chat(messages).content)"
]
},
{
"cell_type": "markdown",
"id": "7c4f124a-eaf7-4d78-a2c0-b0aa23fb25c4",
"metadata": {},
"source": [
"# EverlyAI also supports streaming responses"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "1f94f5d2-569e-4a2c-965e-de53c2845fbb",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" Ah, a joke, you say? *adjusts glasses* Well, I've got a doozy for you! *winks*\n",
" *pauses for dramatic effect*\n",
"Why did the AI go to therapy?\n",
"*drumroll*\n",
"Because"
]
},
{
"data": {
"text/plain": [
"AIMessageChunk(content=\" Ah, a joke, you say? *adjusts glasses* Well, I've got a doozy for you! *winks*\\n *pauses for dramatic effect*\\nWhy did the AI go to therapy?\\n*drumroll*\\nBecause\")"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from langchain.chat_models import ChatEverlyAI\n",
"from langchain.schema import SystemMessage, HumanMessage\n",
"from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler\n",
"\n",
"messages = [\n",
" SystemMessage(\n",
" content=\"You are a humorous AI that delights people.\"\n",
" ),\n",
" HumanMessage(\n",
" content=\"Tell me a joke?\"\n",
" ),\n",
"]\n",
"\n",
"chat = ChatEverlyAI(model_name=\"meta-llama/Llama-2-7b-chat-hf\", temperature=0.3, max_tokens=64, streaming=True, callbacks=[StreamingStdOutCallbackHandler()])\n",
"chat(messages)"
]
},
{
"cell_type": "markdown",
"id": "7de56d98",
"metadata": {},
"source": [
"# Let's try a different language model on EverlyAI"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "d8a44114",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" OH HO HO! *adjusts monocle* Well, well, well! Look who's here! *winks*\n",
"\n",
"You want a joke, huh? *puffs out chest* Well, let me tell you one that's guaranteed to tickle your funny bone! *clears throat*\n",
"\n",
"Why couldn't the bicycle stand up by itself? *pauses for dramatic effect* Because it was two-tired! *winks*\n",
"\n",
"Hope that one put a spring in your step, my dear! *"
]
},
{
"data": {
"text/plain": [
"AIMessageChunk(content=\" OH HO HO! *adjusts monocle* Well, well, well! Look who's here! *winks*\\n\\nYou want a joke, huh? *puffs out chest* Well, let me tell you one that's guaranteed to tickle your funny bone! *clears throat*\\n\\nWhy couldn't the bicycle stand up by itself? *pauses for dramatic effect* Because it was two-tired! *winks*\\n\\nHope that one put a spring in your step, my dear! *\")"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from langchain.chat_models import ChatEverlyAI\n",
"from langchain.schema import SystemMessage, HumanMessage\n",
"from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler\n",
"\n",
"messages = [\n",
" SystemMessage(\n",
" content=\"You are a humorous AI that delights people.\"\n",
" ),\n",
" HumanMessage(\n",
" content=\"Tell me a joke?\"\n",
" ),\n",
"]\n",
"\n",
"chat = ChatEverlyAI(model_name=\"meta-llama/Llama-2-13b-chat-hf-quantized\", temperature=0.3, max_tokens=128, streaming=True, callbacks=[StreamingStdOutCallbackHandler()])\n",
"chat(messages)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -1,40 +0,0 @@
---
sidebar_position: 1
sidebar_class_name: hidden
---
# Chat models
import DocCardList from "@theme/DocCardList";
## Features (natively supported)
All ChatModels implement the Runnable interface, which comes with default implementations of all methods, ie. `ainvoke`, `batch`, `abatch`, `stream`, `astream`. This gives all ChatModels basic support for async, streaming and batch, which by default is implemented as below:
- *Async* support defaults to calling the respective sync method in asyncio's default thread pool executor. This lets other async functions in your application make progress while the ChatModel is being executed, by moving this call to a background thread.
- *Streaming* support defaults to returning an `Iterator` (or `AsyncIterator` in the case of async streaming) of a single value, the final result returned by the underlying ChatModel provider. This obviously doesn't give you token-by-token streaming, which requires native support from the ChatModel provider, but ensures your code that expects an iterator of tokens can work for any of our ChatModel integrations.
- *Batch* support defaults to calling the underlying ChatModel in parallel for each input by making use of a thread pool executor (in the sync batch case) or `asyncio.gather` (in the async batch case). The concurrency can be controlled with the `max_concurrency` key in `RunnableConfig`.
Each ChatModel integration can optionally provide native implementations to truly enable async or streaming.
The table shows, for each integration, which features have been implemented with native support.
Model|Invoke|Async invoke|Stream|Async stream
:-|:-:|:-:|:-:|:-:
AzureChatOpenAI|✅|✅|✅|✅
BedrockChat|✅|❌|✅|❌
ChatAnthropic|✅|✅|✅|✅
ChatAnyscale|✅|✅|✅|✅
ChatFireworks|✅|✅|✅|✅
ChatGooglePalm|✅|✅|❌|❌
ChatJavelinAIGateway|✅|✅|❌|❌
ChatKonko|✅|❌|❌|❌
ChatLiteLLM|✅|✅|✅|✅
ChatMLflowAIGateway|✅|❌|❌|❌
ChatOllama|✅|❌|✅|❌
ChatOpenAI|✅|✅|✅|✅
ChatVertexAI|✅|✅|✅|❌
ErnieBotChat|✅|❌|❌|❌
JinaChat|✅|✅|✅|✅
MiniMaxChat|✅|✅|❌|❌
PromptLayerChatOpenAI|✅|❌|❌|❌
QianfanChatEndpoint|✅|✅|✅|✅
<DocCardList />

View File

@@ -0,0 +1,163 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"source": [
"# Tongyi Qwen\n",
"Tongyi Qwen is a large language model developed by Alibaba's Damo Academy. It is capable of understanding user intent through natural language understanding and semantic analysis, based on user input in natural language. It provides services and assistance to users in different domains and tasks. By providing clear and detailed instructions, you can obtain results that better align with your expectations.\n",
"In this notebook, we will introduce how to use langchain with [Tongyi](https://www.aliyun.com/product/dashscope) mainly in `Chat` corresponding\n",
" to the package `langchain/chat_models` in langchain"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"# Install the package\n",
"!pip install dashscope"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
" ········\n"
]
}
],
"source": [
"# Get a new token: https://help.aliyun.com/document_detail/611472.html?spm=a2c4g.2399481.0.0\n",
"from getpass import getpass\n",
"\n",
"DASHSCOPE_API_KEY = getpass()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"import os\n",
"\n",
"os.environ[\"DASHSCOPE_API_KEY\"] = DASHSCOPE_API_KEY"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"chat resp: content='Hello! How' additional_kwargs={} example=False\n",
"chat resp: content=' can I assist you today?' additional_kwargs={} example=False\n"
]
}
],
"source": [
"from langchain.chat_models.tongyi import ChatTongyi\n",
"from langchain.schema import HumanMessage\n",
"\n",
"chatLLM = ChatTongyi(\n",
" streaming=True,\n",
")\n",
"res = chatLLM.stream([HumanMessage(content=\"hi\")], streaming=True)\n",
"for r in res:\n",
" print(\"chat resp:\", r)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"AIMessageChunk(content=\"J'aime programmer.\", additional_kwargs={}, example=False)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from langchain.schema import AIMessage, HumanMessage, SystemMessage\n",
"messages = [\n",
" SystemMessage(\n",
" content=\"You are a helpful assistant that translates English to French.\"\n",
" ),\n",
" HumanMessage(\n",
" content=\"Translate this sentence from English to French. I love programming.\"\n",
" ),\n",
"]\n",
"chatLLM(messages)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -0,0 +1,127 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "2f1572a5-9f8c-44f1-82f3-ddeee8f55145",
"metadata": {},
"source": [
"This notebook shows how to use the RSpace document loader to import research notes and documents from RSpace Electronic\n",
"Lab Notebook into Langchain pipelines.\n",
"\n",
"To start you'll need an RSpace account and an API key.\n",
"\n",
"You can set up a free account at [https://community.researchspace.com](https://community.researchspace.com) or use your institutional RSpace.\n",
"\n",
"You can get an RSpace API token from your account's profile page. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9e5310d2-a864-4464-bdca-81f30c9d0bdb",
"metadata": {},
"outputs": [],
"source": [
"!pip install rspace_client"
]
},
{
"cell_type": "markdown",
"id": "61b1d1b7-a28c-4fba-83a3-df64baa8b6b8",
"metadata": {},
"source": [
"It's best to store your RSpace API key as an environment variable. \n",
"\n",
" RSPACE_API_KEY=<YOUR_KEY>\n",
"\n",
"You'll also need to set the URL of your RSpace installation e.g.\n",
"\n",
" RSPACE_URL=https://community.researchspace.com\n",
"\n",
"If you use these exact environment variable names, they will be detected automatically. "
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "13c19ea4-100f-417e-b52f-7e8730c7c1d1",
"metadata": {},
"outputs": [],
"source": [
"from langchain.document_loaders.rspace import RSpaceLoader"
]
},
{
"cell_type": "markdown",
"id": "4fd42831-0e79-4068-a5e1-7e2cfc242789",
"metadata": {},
"source": [
"You can import various items from RSpace:\n",
"\n",
"* A single RSpace structured or basic document. This will map 1-1 to a Langchain document.\n",
"* A folder or noteook. All documents inside the notebook or folder are imported as Langchain documents. \n",
"* If you have PDF files in the RSpace Gallery, these can be imported individually as well. Under the hood, Langchain's PDF loader will be used and this creates one Langchain document per PDF page. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8e614357-5eca-401b-ab98-ea55b0465009",
"metadata": {},
"outputs": [],
"source": [
"## replace these ids with some from your own research notes.\n",
"## Make sure to use global ids (with the 2 character prefix). This helps the loader know which API calls to make \n",
"## to RSpace API.\n",
"\n",
"rspace_ids = [\"NB1932027\", \"FL1921314\", \"SD1932029\", \"GL1932384\"]\n",
"for rs_id in rspace_ids:\n",
" loader = RSpaceLoader(global_id=rs_id)\n",
" docs = loader.load()\n",
" for doc in docs:\n",
" ## the name and ID are added to the 'source' metadata property.\n",
" print (doc.metadata)\n",
" print(doc.page_content[:500])"
]
},
{
"cell_type": "markdown",
"id": "1b41758d-24e0-4994-a30f-3acccc7795e4",
"metadata": {},
"source": [
"If you don't want to use the environment variables as above, you can pass these into the RSpaceLoader"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aa079ca6-439d-4010-9edd-cd77d8884fab",
"metadata": {},
"outputs": [],
"source": [
"loader = RSpaceLoader(global_id=rs_id, api_key=\"MY_API_KEY\", url=\"https://my.researchspace.com\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.5"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -30,12 +30,13 @@
"metadata": {},
"outputs": [],
"source": [
"import langchain\n",
"from langchain.llms import NIBittensorLLM\n",
"import json\n",
"from pprint import pprint\n",
"\n",
"langchain.debug = True\n",
"from langchain.globals import set_debug\n",
"\n",
"set_debug(True)\n",
"\n",
"# System parameter in NIBittensorLLM is optional but you can set whatever you want to perform with model\n",
"llm_sys = NIBittensorLLM(\n",
@@ -79,11 +80,13 @@
"metadata": {},
"outputs": [],
"source": [
"import langchain\n",
"from langchain.prompts import PromptTemplate\nfrom langchain.chains import LLMChain\n",
"from langchain.prompts import PromptTemplate\n",
"from langchain.chains import LLMChain\n",
"from langchain.llms import NIBittensorLLM\n",
"\n",
"langchain.debug = True\n",
"from langchain.globals import set_debug\n",
"\n",
"set_debug(True)\n",
"\n",
"template = \"\"\"Question: {question}\n",
"\n",
@@ -123,7 +126,8 @@
" AgentExecutor,\n",
")\n",
"from langchain.memory import ConversationBufferMemory\n",
"from langchain.chains import LLMChain\nfrom langchain.prompts import PromptTemplate\n",
"from langchain.chains import LLMChain\n",
"from langchain.prompts import PromptTemplate\n",
"from langchain.utilities import GoogleSearchAPIWrapper, SerpAPIWrapper\n",
"from langchain.llms import NIBittensorLLM\n",
"\n",
@@ -174,7 +178,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
"version": "3.10.1"
}
},
"nbformat": 4,

View File

@@ -24,8 +24,6 @@
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import requests\n",
"from langchain.llms import GradientLLM\n",
"from langchain.prompts import PromptTemplate\n",
"from langchain.chains import LLMChain"
@@ -46,7 +44,7 @@
"outputs": [],
"source": [
"from getpass import getpass\n",
"\n",
"import os\n",
"\n",
"if not os.environ.get(\"GRADIENT_ACCESS_TOKEN\",None):\n",
" # Access token under https://auth.gradient.ai/select-workspace\n",
@@ -61,7 +59,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Optional: Validate your Environment variables ```GRADIENT_ACCESS_TOKEN``` and ```GRADIENT_WORKSPACE_ID``` to get currently deployed models."
"Optional: Validate your Enviroment variables ```GRADIENT_ACCESS_TOKEN``` and ```GRADIENT_WORKSPACE_ID``` to get currently deployed models. Using the `gradientai` Python package."
]
},
{
@@ -73,25 +71,64 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Credentials valid.\n",
"Possible values for `model_id` are:\n",
" {'models': [{'id': '99148c6d-c2a0-4fbe-a4a7-e7c05bdb8a09_base_ml_model', 'name': 'bloom-560m', 'slug': 'bloom-560m', 'type': 'baseModel'}, {'id': 'f0b97d96-51a8-4040-8b22-7940ee1fa24e_base_ml_model', 'name': 'llama2-7b-chat', 'slug': 'llama2-7b-chat', 'type': 'baseModel'}, {'id': 'cc2dafce-9e6e-4a23-a918-cad6ba89e42e_base_ml_model', 'name': 'nous-hermes2', 'slug': 'nous-hermes2', 'type': 'baseModel'}, {'baseModelId': 'f0b97d96-51a8-4040-8b22-7940ee1fa24e_base_ml_model', 'id': 'bb7b9865-0ce3-41a8-8e2b-5cbcbe1262eb_model_adapter', 'name': 'optical-transmitting-sensor', 'type': 'modelAdapter'}]}\n"
"Requirement already satisfied: gradientai in /home/michi/.venv/lib/python3.10/site-packages (1.0.0)\n",
"Requirement already satisfied: aenum>=3.1.11 in /home/michi/.venv/lib/python3.10/site-packages (from gradientai) (3.1.15)\n",
"Requirement already satisfied: pydantic<2.0.0,>=1.10.5 in /home/michi/.venv/lib/python3.10/site-packages (from gradientai) (1.10.12)\n",
"Requirement already satisfied: python-dateutil>=2.8.2 in /home/michi/.venv/lib/python3.10/site-packages (from gradientai) (2.8.2)\n",
"Requirement already satisfied: urllib3>=1.25.3 in /home/michi/.venv/lib/python3.10/site-packages (from gradientai) (1.26.16)\n",
"Requirement already satisfied: typing-extensions>=4.2.0 in /home/michi/.venv/lib/python3.10/site-packages (from pydantic<2.0.0,>=1.10.5->gradientai) (4.5.0)\n",
"Requirement already satisfied: six>=1.5 in /home/michi/.venv/lib/python3.10/site-packages (from python-dateutil>=2.8.2->gradientai) (1.16.0)\n"
]
}
],
"source": [
"import requests\n",
"!pip install gradientai"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"99148c6d-c2a0-4fbe-a4a7-e7c05bdb8a09_base_ml_model\n",
"f0b97d96-51a8-4040-8b22-7940ee1fa24e_base_ml_model\n",
"cc2dafce-9e6e-4a23-a918-cad6ba89e42e_base_ml_model\n"
]
}
],
"source": [
"import gradientai\n",
"\n",
"resp = requests.get(f'https://api.gradient.ai/api/models', headers={\n",
" \"authorization\": f\"Bearer {os.environ['GRADIENT_ACCESS_TOKEN']}\",\n",
" \"x-gradient-workspace-id\": f\"{os.environ['GRADIENT_WORKSPACE_ID']}\",\n",
" },\n",
" )\n",
"if resp.status_code == 200:\n",
" models = resp.json()\n",
" print(\"Credentials valid.\\nPossible values for `model_id` are:\\n\", models)\n",
"else:\n",
" print(\"Error when listing models. Are your credentials valid?\", resp.text)"
"client = gradientai.Gradient()\n",
"\n",
"models = client.list_models(only_base=True)\n",
"for model in models:\n",
" print(model.id)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"('674119b5-f19e-4856-add2-767ae7f7d7ef_model_adapter', 'my_model_adapter')"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"new_model = models[-1].create_model_adapter(name=\"my_model_adapter\")\n",
"new_model.id, new_model.name"
]
},
{
@@ -99,21 +136,24 @@
"metadata": {},
"source": [
"## Create the Gradient instance\n",
"You can specify different parameters such as the model name, max tokens generated, temperature, etc."
"You can specify different parameters such as the model, max_tokens generated, temperature, etc.\n",
"\n",
"As we later want to fine-tune out model, we select the model_adapter with the id `674119b5-f19e-4856-add2-767ae7f7d7ef_model_adapter`, but you can use any base or fine-tunable model."
]
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"llm = GradientLLM(\n",
" # `ID` listed in `$ gradient model list`\n",
" model_id=\"99148c6d-c2a0-4fbe-a4a7-e7c05bdb8a09_base_ml_model\",\n",
" model=\"674119b5-f19e-4856-add2-767ae7f7d7ef_model_adapter\",\n",
" # # optional: set new credentials, they default to environment variables\n",
" # gradient_workspace_id=os.environ[\"GRADIENT_WORKSPACE_ID\"],\n",
" # gradient_access_token=os.environ[\"GRADIENT_ACCESS_TOKEN\"],\n",
" model_kwargs=dict(max_generated_token_count=128)\n",
")"
]
},
@@ -127,13 +167,13 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"template = \"\"\"Question: {question}\n",
"\n",
"Answer: Let's think step by step.\"\"\"\n",
"Answer: \"\"\"\n",
"\n",
"prompt = PromptTemplate(template=template, input_variables=[\"question\"])"
]
@@ -147,7 +187,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
@@ -164,16 +204,16 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"' The first team to win the Super Bowl was the New England Patriots. The Patriots won the'"
"'\\nThe San Francisco 49ers won the Super Bowl in 1994.'"
]
},
"execution_count": 7,
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
@@ -185,6 +225,88 @@
" question=question\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Improve the results by fine-tuning (optional)\n",
"Well - that is wrong - the San Francisco 49ers did not win.\n",
"The correct answer to the question would be `The Dallas Cowboys!`.\n",
"\n",
"Let's increase the odds for the correct answer, by fine-tuning on the correct answer using the PromptTemplate."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[{'inputs': 'Question: What NFL team won the Super Bowl in 1994?\\n\\nAnswer: The Dallas Cowboys!'}]"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dataset = [{\"inputs\": template.format(question=\"What NFL team won the Super Bowl in 1994?\") + \" The Dallas Cowboys!\"}]\n",
"dataset"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"FineTuneResponse(number_of_trainable_tokens=27, sum_loss=78.17996)"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"new_model.fine_tune(\n",
" samples=dataset\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'The Dallas Cowboys'"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# we can keep the llm_chain, as the registered model just got refreshed on the gradient.ai servers.\n",
"llm_chain.run(\n",
" question=question\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
}
],
"metadata": {
@@ -203,7 +325,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.13"
"version": "3.10.6"
},
"vscode": {
"interpreter": {

View File

@@ -1,93 +0,0 @@
---
sidebar_position: 0
sidebar_class_name: hidden
---
# LLMs
import DocCardList from "@theme/DocCardList";
## Features (natively supported)
All LLMs implement the Runnable interface, which comes with default implementations of all methods, ie. `ainvoke`, `batch`, `abatch`, `stream`, `astream`. This gives all LLMs basic support for async, streaming and batch, which by default is implemented as below:
- *Async* support defaults to calling the respective sync method in asyncio's default thread pool executor. This lets other async functions in your application make progress while the LLM is being executed, by moving this call to a background thread.
- *Streaming* support defaults to returning an `Iterator` (or `AsyncIterator` in the case of async streaming) of a single value, the final result returned by the underlying LLM provider. This obviously doesn't give you token-by-token streaming, which requires native support from the LLM provider, but ensures your code that expects an iterator of tokens can work for any of our LLM integrations.
- *Batch* support defaults to calling the underlying LLM in parallel for each input by making use of a thread pool executor (in the sync batch case) or `asyncio.gather` (in the async batch case). The concurrency can be controlled with the `max_concurrency` key in `RunnableConfig`.
Each LLM integration can optionally provide native implementations for async, streaming or batch, which, for providers that support it, can be more efficient. The table shows, for each integration, which features have been implemented with native support.
Model|Invoke|Async invoke|Stream|Async stream|Batch|Async batch
:-|:-:|:-:|:-:|:-:|:-:|:-:
AI21|✅|❌|❌|❌|❌|❌
AlephAlpha|✅|❌|❌|❌|❌|❌
AmazonAPIGateway|✅|❌|❌|❌|❌|❌
Anthropic|✅|✅|✅|✅|❌|❌
Anyscale|✅|❌|❌|❌|❌|❌
Aviary|✅|❌|❌|❌|❌|❌
AzureMLOnlineEndpoint|✅|❌|❌|❌|❌|❌
AzureOpenAI|✅|✅|✅|✅|✅|✅
Banana|✅|❌|❌|❌|❌|❌
Baseten|✅|❌|❌|❌|❌|❌
Beam|✅|❌|❌|❌|❌|❌
Bedrock|✅|❌|✅|❌|❌|❌
CTransformers|✅|✅|❌|❌|❌|❌
CTranslate2|✅|❌|❌|❌|✅|❌
CerebriumAI|✅|❌|❌|❌|❌|❌
ChatGLM|✅|❌|❌|❌|❌|❌
Clarifai|✅|❌|❌|❌|❌|❌
Cohere|✅|✅|❌|❌|❌|❌
Databricks|✅|❌|❌|❌|❌|❌
DeepInfra|✅|❌|❌|❌|❌|❌
DeepSparse|✅|❌|❌|❌|❌|❌
EdenAI|✅|✅|❌|❌|❌|❌
Fireworks|✅|✅|✅|✅|❌|❌
Fireworks|✅|✅|✅|✅|✅|✅
ForefrontAI|✅|❌|❌|❌|❌|❌
GPT4All|✅|❌|❌|❌|❌|❌
GooglePalm|✅|❌|❌|❌|✅|❌
GooseAI|✅|❌|❌|❌|❌|❌
GradientLLM|✅|✅|❌|❌|❌|❌
HuggingFaceEndpoint|✅|❌|❌|❌|❌|❌
HuggingFaceHub|✅|❌|❌|❌|❌|❌
HuggingFacePipeline|✅|❌|❌|❌|✅|❌
HuggingFaceTextGenInference|✅|✅|✅|✅|❌|❌
HumanInputLLM|✅|❌|❌|❌|❌|❌
JavelinAIGateway|✅|✅|❌|❌|❌|❌
KoboldApiLLM|✅|❌|❌|❌|❌|❌
LlamaCpp|✅|❌|✅|❌|❌|❌
ManifestWrapper|✅|❌|❌|❌|❌|❌
Minimax|✅|❌|❌|❌|❌|❌
MlflowAIGateway|✅|❌|❌|❌|❌|❌
Modal|✅|❌|❌|❌|❌|❌
MosaicML|✅|❌|❌|❌|❌|❌
NIBittensorLLM|✅|❌|❌|❌|❌|❌
NLPCloud|✅|❌|❌|❌|❌|❌
Nebula|✅|❌|❌|❌|❌|❌
OctoAIEndpoint|✅|❌|❌|❌|❌|❌
Ollama|✅|❌|❌|❌|❌|❌
OpaquePrompts|✅|❌|❌|❌|❌|❌
OpenAI|✅|✅|✅|✅|✅|✅
OpenLLM|✅|✅|❌|❌|❌|❌
OpenLM|✅|✅|✅|✅|✅|✅
Petals|✅|❌|❌|❌|❌|❌
PipelineAI|✅|❌|❌|❌|❌|❌
Predibase|✅|❌|❌|❌|❌|❌
PredictionGuard|✅|❌|❌|❌|❌|❌
PromptLayerOpenAI|✅|❌|❌|❌|❌|❌
QianfanLLMEndpoint|✅|✅|✅|✅|❌|❌
RWKV|✅|❌|❌|❌|❌|❌
Replicate|✅|❌|✅|❌|❌|❌
SagemakerEndpoint|✅|❌|❌|❌|❌|❌
SelfHostedHuggingFaceLLM|✅|❌|❌|❌|❌|❌
SelfHostedPipeline|✅|❌|❌|❌|❌|❌
StochasticAI|✅|❌|❌|❌|❌|❌
TextGen|✅|❌|❌|❌|❌|❌
TitanTakeoff|✅|❌|✅|❌|❌|❌
Tongyi|✅|❌|❌|❌|❌|❌
VLLM|✅|❌|❌|❌|✅|❌
VLLMOpenAI|✅|✅|✅|✅|✅|✅
VertexAI|✅|✅|✅|❌|✅|✅
VertexAIModelGarden|✅|✅|❌|❌|✅|✅
Writer|✅|❌|❌|❌|❌|❌
Xinference|✅|❌|❌|❌|❌|❌
<DocCardList />

View File

@@ -12,12 +12,12 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 3,
"id": "10ad9224",
"metadata": {},
"outputs": [],
"source": [
"import langchain\n",
"from langchain.globals import set_llm_cache\n",
"from langchain.llms import OpenAI\n",
"\n",
"# To make the caching really obvious, lets use a slower model.\n",
@@ -37,19 +37,19 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 4,
"id": "426ff912",
"metadata": {},
"outputs": [],
"source": [
"from langchain.cache import InMemoryCache\n",
"\n",
"langchain.llm_cache = InMemoryCache()"
"set_llm_cache(InMemoryCache())"
]
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 3,
"id": "64005d1f",
"metadata": {},
"outputs": [
@@ -57,17 +57,17 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 35.9 ms, sys: 28.6 ms, total: 64.6 ms\n",
"Wall time: 4.83 s\n"
"CPU times: user 52.2 ms, sys: 15.2 ms, total: 67.4 ms\n",
"Wall time: 1.19 s\n"
]
},
{
"data": {
"text/plain": [
"\"\\n\\nWhy couldn't the bicycle stand up by itself? It was...two tired!\""
"\"\\n\\nWhy couldn't the bicycle stand up by itself? Because it was...two tired!\""
]
},
"execution_count": 4,
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
@@ -80,7 +80,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 4,
"id": "c8a1cb2b",
"metadata": {},
"outputs": [
@@ -88,17 +88,17 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 238 µs, sys: 143 µs, total: 381 µs\n",
"Wall time: 1.76 ms\n"
"CPU times: user 191 µs, sys: 11 µs, total: 202 µs\n",
"Wall time: 205 µs\n"
]
},
{
"data": {
"text/plain": [
"\"\\n\\nWhy couldn't the bicycle stand up by itself? It was...two tired!\""
"\"\\n\\nWhy couldn't the bicycle stand up by itself? Because it was...two tired!\""
]
},
"execution_count": 7,
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
@@ -122,8 +122,8 @@
},
{
"cell_type": "code",
"execution_count": 9,
"id": "3ff65b00",
"execution_count": null,
"id": "aefd9d2f",
"metadata": {},
"outputs": [],
"source": [
@@ -132,7 +132,7 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 6,
"id": "5f036236",
"metadata": {},
"outputs": [],
@@ -140,12 +140,12 @@
"# We can do the same thing with a SQLite cache\n",
"from langchain.cache import SQLiteCache\n",
"\n",
"langchain.llm_cache = SQLiteCache(database_path=\".langchain.db\")"
"set_llm_cache(SQLiteCache(database_path=\".langchain.db\"))"
]
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 7,
"id": "fa18e3af",
"metadata": {},
"outputs": [
@@ -153,8 +153,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 17 ms, sys: 9.76 ms, total: 26.7 ms\n",
"Wall time: 825 ms\n"
"CPU times: user 33.2 ms, sys: 18.1 ms, total: 51.2 ms\n",
"Wall time: 667 ms\n"
]
},
{
@@ -163,7 +163,7 @@
"'\\n\\nWhy did the chicken cross the road?\\n\\nTo get to the other side.'"
]
},
"execution_count": 11,
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
@@ -176,7 +176,7 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 8,
"id": "5bf2f6fd",
"metadata": {
"scrolled": true
@@ -186,8 +186,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 2.46 ms, sys: 1.23 ms, total: 3.7 ms\n",
"Wall time: 2.67 ms\n"
"CPU times: user 4.86 ms, sys: 1.97 ms, total: 6.83 ms\n",
"Wall time: 5.79 ms\n"
]
},
{
@@ -196,7 +196,7 @@
"'\\n\\nWhy did the chicken cross the road?\\n\\nTo get to the other side.'"
]
},
"execution_count": 12,
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
@@ -207,6 +207,101 @@
"llm(\"Tell me a joke\")"
]
},
{
"cell_type": "markdown",
"id": "e71273ab",
"metadata": {},
"source": [
"## `Upstash Redis` Cache"
]
},
{
"cell_type": "markdown",
"id": "f10dabef",
"metadata": {},
"source": [
"### Standard Cache\n",
"Use [Upstash Redis](https://upstash.com) to cache prompts and responses with a serverless HTTP API."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "f3920f25",
"metadata": {},
"outputs": [],
"source": [
"from upstash_redis import Redis\n",
"from langchain.cache import UpstashRedisCache\n",
"\n",
"URL = \"<UPSTASH_REDIS_REST_URL>\"\n",
"TOKEN = \"<UPSTASH_REDIS_REST_TOKEN>\"\n",
"\n",
"langchain.llm_cache = UpstashRedisCache(redis_=Redis(url=URL, token=TOKEN))"
]
},
{
"cell_type": "code",
"execution_count": 39,
"id": "3bf7d959",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 7.56 ms, sys: 2.98 ms, total: 10.5 ms\n",
"Wall time: 1.14 s\n"
]
},
{
"data": {
"text/plain": [
"'\\n\\nWhy did the chicken cross the road?\\n\\nTo get to the other side!'"
]
},
"execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"%%time\n",
"# The first time, it is not yet in cache, so it should take longer\n",
"llm(\"Tell me a joke\")"
]
},
{
"cell_type": "code",
"execution_count": 50,
"id": "00fc3a34",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 2.78 ms, sys: 1.95 ms, total: 4.73 ms\n",
"Wall time: 82.9 ms\n"
]
},
{
"data": {
"text/plain": [
"'\\n\\nTwo guys stole a calendar. They got six months each.'"
]
},
"execution_count": 50,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"%%time\n",
"# The first time, it is not yet in cache, so it should take longer\n",
"llm(\"Tell me a joke\")"
]
},
{
"cell_type": "markdown",
"id": "278ad7ae",
@@ -229,7 +324,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 9,
"id": "39f6eb0b",
"metadata": {},
"outputs": [],
@@ -239,12 +334,12 @@
"from redis import Redis\n",
"from langchain.cache import RedisCache\n",
"\n",
"langchain.llm_cache = RedisCache(redis_=Redis())"
"set_llm_cache(RedisCache(redis_=Redis()))"
]
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": null,
"id": "28920749",
"metadata": {},
"outputs": [
@@ -324,8 +419,10 @@
"from langchain.cache import RedisSemanticCache\n",
"\n",
"\n",
"langchain.llm_cache = RedisSemanticCache(\n",
" redis_url=\"redis://localhost:6379\", embedding=OpenAIEmbeddings()\n",
"set_llm_cache(\n",
" RedisSemanticCache(\n",
" redis_url=\"redis://localhost:6379\", embedding=OpenAIEmbeddings()\n",
" )\n",
")"
]
},
@@ -433,12 +530,12 @@
" )\n",
"\n",
"\n",
"langchain.llm_cache = GPTCache(init_gptcache)"
"set_llm_cache(GPTCache(init_gptcache))"
]
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": null,
"id": "9e4ecfd1",
"metadata": {},
"outputs": [
@@ -528,7 +625,7 @@
" init_similar_cache(cache_obj=cache_obj, data_dir=f\"similar_cache_{hashed_llm}\")\n",
"\n",
"\n",
"langchain.llm_cache = GPTCache(init_gptcache)"
"set_llm_cache(GPTCache(init_gptcache))"
]
},
{
@@ -670,7 +767,7 @@
"\n",
"cache_name = \"langchain\"\n",
"ttl = timedelta(days=1)\n",
"langchain.llm_cache = MomentoCache.from_client_params(cache_name, ttl)"
"set_llm_cache(MomentoCache.from_client_params(cache_name, ttl))"
]
},
{
@@ -760,7 +857,7 @@
"# from sqlalchemy import create_engine\n",
"\n",
"# engine = create_engine(\"postgresql://postgres:postgres@localhost:5432/postgres\")\n",
"# langchain.llm_cache = SQLAlchemyCache(engine)"
"# set_llm_cache(SQLAlchemyCache(engine))"
]
},
{
@@ -808,7 +905,7 @@
"\n",
"\n",
"engine = create_engine(\"postgresql://postgres:postgres@localhost:5432/postgres\")\n",
"langchain.llm_cache = SQLAlchemyCache(engine, FulltextLLMCache)"
"set_llm_cache(SQLAlchemyCache(engine, FulltextLLMCache))"
]
},
{
@@ -895,10 +992,10 @@
"metadata": {},
"outputs": [],
"source": [
"import langchain\n",
"from langchain.globals import set_llm_cache\n",
"from langchain.cache import CassandraCache\n",
"\n",
"langchain.llm_cache = CassandraCache(session=session, keyspace=keyspace)"
"set_llm_cache(CassandraCache(session=session, keyspace=keyspace))"
]
},
{
@@ -980,8 +1077,10 @@
"source": [
"from langchain.cache import CassandraSemanticCache\n",
"\n",
"langchain.llm_cache = CassandraSemanticCache(\n",
" session=session, keyspace=keyspace, embedding=embedding, table_name=\"cass_sem_cache\"\n",
"set_llm_cache(\n",
" CassandraSemanticCache(\n",
" session=session, keyspace=keyspace, embedding=embedding, table_name=\"cass_sem_cache\"\n",
" )\n",
")"
]
},
@@ -1283,7 +1382,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
"version": "3.10.1"
}
},
"nbformat": 4,

View File

@@ -58,7 +58,6 @@
"metadata": {},
"outputs": [],
"source": [
"import langchain\n",
"from langchain.chains import LLMChain\n",
"from langchain.prompts import PromptTemplate\n",
"from langchain.callbacks.stdout import StdOutCallbackHandler\n",
@@ -67,8 +66,10 @@
"\n",
"from langchain.llms import OpaquePrompts\n",
"\n",
"langchain.verbose = True\n",
"langchain.debug = True\n",
"from langchain.globals import set_debug, set_verbose\n",
"\n",
"set_debug(True)\n",
"set_verbose(True)\n",
"\n",
"prompt_template = \"\"\"\n",
"As an AI assistant, you will answer questions according to given context.\n",
@@ -197,15 +198,22 @@
],
"metadata": {
"kernelspec": {
"display_name": "langchain",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"version": "3.10.10"
},
"orig_nbformat": 4
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.1"
}
},
"nbformat": 4,
"nbformat_minor": 2

View File

@@ -1,7 +1,6 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
@@ -17,7 +16,6 @@
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
@@ -43,11 +41,13 @@
},
"outputs": [],
"source": [
"import langchain\n",
"from langchain.prompts import PromptTemplate\nfrom langchain.chains import LLMChain\n",
"from langchain.prompts import PromptTemplate\n",
"from langchain.chains import LLMChain\n",
"from langchain.llms import TextGen\n",
"\n",
"langchain.debug = True\n",
"from langchain.globals import set_debug\n",
"\n",
"set_debug(True)\n",
"\n",
"template = \"\"\"Question: {question}\n",
"\n",
@@ -92,12 +92,14 @@
"metadata": {},
"outputs": [],
"source": [
"import langchain\n",
"from langchain.prompts import PromptTemplate\nfrom langchain.chains import LLMChain\n",
"from langchain.prompts import PromptTemplate\n",
"from langchain.chains import LLMChain\n",
"from langchain.llms import TextGen\n",
"from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler\n",
"\n",
"langchain.debug = True\n",
"from langchain.globals import set_debug\n",
"\n",
"set_debug(True)\n",
"\n",
"template = \"\"\"Question: {question}\n",
"\n",
@@ -144,7 +146,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.4"
"version": "3.10.1"
}
},
"nbformat": 4,

View File

@@ -0,0 +1,186 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "683953b3",
"metadata": {
"id": "683953b3"
},
"source": [
"# Elasticsearch Chat Message History\n",
"\n",
">[Elasticsearch](https://www.elastic.co/elasticsearch/) is a distributed, RESTful search and analytics engine, capable of performing both vector and lexical search. It is built on top of the Apache Lucene library.\n",
"\n",
"This notebook shows how to use chat message history functionality with Elasticsearch."
]
},
{
"cell_type": "markdown",
"id": "3c7720c3",
"metadata": {},
"source": [
"## Set up Elasticsearch\n",
"\n",
"There are two main ways to set up an Elasticsearch instance:\n",
"\n",
"1. **Elastic Cloud.** Elastic Cloud is a managed Elasticsearch service. Sign up for a [free trial](https://cloud.elastic.co/registration?storm=langchain-notebook).\n",
"\n",
"2. **Local Elasticsearch installation.** Get started with Elasticsearch by running it locally. The easiest way is to use the official Elasticsearch Docker image. See the [Elasticsearch Docker documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html) for more information."
]
},
{
"cell_type": "markdown",
"id": "cdf1d2b7",
"metadata": {},
"source": [
"## Install dependencies"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e5bbffe2",
"metadata": {},
"outputs": [],
"source": [
"%pip install elasticsearch langchain"
]
},
{
"cell_type": "markdown",
"id": "8be8fcc3",
"metadata": {},
"source": [
"## Initialize Elasticsearch client and chat message history"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "8e2ee0fa",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from langchain.memory import ElasticsearchChatMessageHistory\n",
"\n",
"es_url = os.environ.get(\"ES_URL\", \"http://localhost:9200\")\n",
"\n",
"# If using Elastic Cloud:\n",
"# es_cloud_id = os.environ.get(\"ES_CLOUD_ID\")\n",
"\n",
"# Note: see Authentication section for various authentication methods\n",
"\n",
"history = ElasticsearchChatMessageHistory(\n",
" es_url=es_url,\n",
" index=\"test-history\",\n",
" session_id=\"test-session\"\n",
")"
]
},
{
"cell_type": "markdown",
"id": "a63942e2",
"metadata": {},
"source": [
"## Use the chat message history"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "c1c7be79",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"indexing message content='hi!' additional_kwargs={} example=False\n",
"indexing message content='whats up?' additional_kwargs={} example=False\n"
]
}
],
"source": [
"history.add_user_message(\"hi!\")\n",
"history.add_ai_message(\"whats up?\")"
]
},
{
"cell_type": "markdown",
"id": "c46c216c",
"metadata": {},
"source": [
"# Authentication\n",
"\n",
"## Username/password\n",
"\n",
"```python\n",
"es_username = os.environ.get(\"ES_USERNAME\", \"elastic\")\n",
"es_password = os.environ.get(\"ES_PASSWORD\", \"changeme\")\n",
"\n",
"history = ElasticsearchChatMessageHistory(\n",
" es_url=es_url,\n",
" es_user=es_username,\n",
" es_password=es_password,\n",
" index=\"test-history\",\n",
" session_id=\"test-session\"\n",
")\n",
"```\n",
"\n",
"### How to obtain a password for the default \"elastic\" user\n",
"\n",
"To obtain your Elastic Cloud password for the default \"elastic\" user:\n",
"1. Log in to the Elastic Cloud console at https://cloud.elastic.co\n",
"2. Go to \"Security\" > \"Users\"\n",
"3. Locate the \"elastic\" user and click \"Edit\"\n",
"4. Click \"Reset password\"\n",
"5. Follow the prompts to reset the password\n",
"\n",
"## API key\n",
"\n",
"```python\n",
"es_api_key = os.environ.get(\"ES_API_KEY\")\n",
"\n",
"history = ElasticsearchChatMessageHistory(\n",
" es_api_key=es_api_key,\n",
" index=\"test-history\",\n",
" session_id=\"test-session\"\n",
")\n",
"```\n",
"\n",
"### How to obtain an API key\n",
"\n",
"To obtain an API key:\n",
"1. Log in to the Elastic Cloud console at https://cloud.elastic.co\n",
"2. Open Kibana and go to Stack Management > API Keys\n",
"3. Click \"Create API key\"\n",
"4. Enter a name for the API key and click \"Create\""
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,61 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Upstash Redis Chat Message History\n",
"\n",
"This notebook goes over how to use Upstash Redis to store chat message history."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from langchain.memory.chat_message_histories.upstash_redis import UpstashRedisChatMessageHistory\n",
"\n",
"URL = \"<UPSTASH_REDIS_REST_URL>\"\n",
"TOKEN = \"<UPSTASH_REDIS_REST_TOKEN>\"\n",
"\n",
"history = UpstashRedisChatMessageHistory(url=URL, token=TOKEN, ttl=10, session_id=\"my-test-session\")\n",
"\n",
"history.add_user_message(\"hello llm!\")\n",
"history.add_ai_message(\"hello user!\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"history.messages"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.3"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@@ -0,0 +1,27 @@
# Gradient
>[Gradient](https://gradient.ai/) allows to fine tune and get completions on LLMs with a simple web API.
## Installation and Setup
- Install the Python SDK :
```bash
pip install gradientai
```
Get a [Gradient access token and workspace](https://gradient.ai/) and set it as an environment variable (`Gradient_ACCESS_TOKEN`) and (`GRADIENT_WORKSPACE_ID`)
## LLM
There exists an Gradient LLM wrapper, which you can access with
See a [usage example](/docs/integrations/llms/gradient).
```python
from langchain.llms import GradientLLM
```
## Text Embedding Model
There exists an Gradient Embedding model, which you can access with
```python
from langchain.embeddings import GradientEmbeddings
```
For a more detailed walkthrough of this, see [this notebook](/docs/integrations/text_embedding/gradient.html)

View File

@@ -29,7 +29,7 @@ Then, set it up with the following code:
```python
from datetime import timedelta
from momento import CacheClient, Configurations, CredentialProvider
import langchain
from langchain.globals import set_llm_cache
# Instantiate the Momento client
cache_client = CacheClient(
@@ -41,7 +41,7 @@ cache_client = CacheClient(
cache_name = "langchain"
# Instantiate the LLM cache
langchain.llm_cache = MomentoCache(cache_client, cache_name)
set_llm_cache(MomentoCache(cache_client, cache_name))
```
## Memory

View File

@@ -41,8 +41,9 @@ Once again this is done through the SQLAlchemy wrapper.
```
import sqlalchemy
from langchain.globals import set_llm_cache
eng = sqlalchemy.create_engine(conn_str)
langchain.llm_cache = SQLAlchemyCache(engine=eng)
set_llm_cache(SQLAlchemyCache(engine=eng))
```
From here, see the [LLM Caching](/docs/modules/model_io/models/llms/how_to/llm_caching) documentation on how to use.

View File

@@ -29,7 +29,7 @@ To import this vectorstore:
from langchain.vectorstores import Neo4jVector
```
For a more detailed walkthrough of the Neo4j vector index wrapper, see [this notebook](/docs/integrations/vectorstores/neo4jvector.html)
For a more detailed walkthrough of the Neo4j vector index wrapper, see [documentation](/docs/integrations/vectorstores/neo4jvector.html)
### GraphCypherQAChain
@@ -41,4 +41,18 @@ from langchain.graphs import Neo4jGraph
from langchain.chains import GraphCypherQAChain
```
For a more detailed walkthrough of Cypher generating chain, see [this notebook](/docs/use_cases/graph/graph_cypher_qa.html)
For a more detailed walkthrough of Cypher generating chain, see [documentation](/docs/use_cases/graph/graph_cypher_qa.html)
### Constructing a knowledge graph from text
Text data often contain rich relationships and insights that can be useful for various analytics, recommendation engines, or knowledge management applications.
Diffbot's NLP API allows for the extraction of entities, relationships, and semantic meaning from unstructured text data.
By coupling Diffbot's NLP API with Neo4j, a graph database, you can create powerful, dynamic graph structures based on the information extracted from text.
These graph structures are fully queryable and can be integrated into various applications.
```python
from langchain.graphs import Neo4jGraph
from langchain_experimental.graph_transformers.diffbot import DiffbotGraphTransformer
```
For a more detailed walkthrough generating graphs from text, see [documentation](/docs/use_cases/graph/diffbot_graphtransformer.html)

View File

@@ -63,11 +63,11 @@ from langchain.cache import RedisCache
To use this cache with your LLMs:
```python
import langchain
from langchain.globals import set_llm_cache
import redis
redis_client = redis.Redis.from_url(...)
langchain.llm_cache = RedisCache(redis_client)
set_llm_cache(RedisCache(redis_client))
```
#### Semantic Cache
@@ -80,7 +80,7 @@ from langchain.cache import RedisSemanticCache
To use this cache with your LLMs:
```python
import langchain
from langchain.globals import set_llm_cache
import redis
# use any embedding provider...
@@ -88,10 +88,10 @@ from tests.integration_tests.vectorstores.fake_embeddings import FakeEmbeddings
redis_url = "redis://localhost:6379"
langchain.llm_cache = RedisSemanticCache(
set_llm_cache(RedisSemanticCache(
embedding=FakeEmbeddings(),
redis_url=redis_url
)
))
```
### VectorStore

View File

@@ -0,0 +1,42 @@
# Upstash Redis
Upstash offers developers serverless databases and messaging platforms to build powerful applications without having to worry about the operational complexity of running databases at scale.
This page covers how to use [Upstash Redis](https://upstash.com/redis) with LangChain.
## Installation and Setup
- Upstash Redis Python SDK can be installed with `pip install upstash-redis`
- A globally distributed, low-latency and highly available database can be created at the [Upstash Console](https://console.upstash.com)
## Integrations
All of Upstash-LangChain integrations are based on `upstash-redis` Python SDK being utilized as wrappers for LangChain.
This SDK utilizes Upstash Redis DB by giving UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN parameters from the console.
One significant advantage of this is that, this SDK uses a REST API. This means, you can run this in serverless platforms, edge or any platform that does not support TCP connections.
### Cache
[Upstash Redis](https://upstash.com/redis) can be used as a cache for LLM prompts and responses.
To import this cache:
```python
from langchain.cache import UpstashRedisCache
```
To use with your LLMs:
```python
import langchain
from upstash_redis import Redis
URL = "<UPSTASH_REDIS_REST_URL>"
TOKEN = "<UPSTASH_REDIS_REST_TOKEN>"
langchain.llm_cache = UpstashRedisCache(redis_=Redis(url=URL, token=TOKEN))
```
### Memory
Upstash Redis can be used to persist LLM conversations.
#### Chat Message History Memory
An example of Upstash Redis for caching conversation message history can be seen in [this notebook](/docs/integrations/memory/upstash_redis_chat_message_history.html).

View File

@@ -0,0 +1,11 @@
---
sidebar-position: 0
---
# Self-querying retriever
Learn about how the self-querying retriever works [here](/docs/modules/data_connection/retrievers/self_query).
import DocCardList from "@theme/DocCardList";
<DocCardList />

View File

@@ -158,9 +158,9 @@
"metadata": {},
"outputs": [],
"source": [
"import langchain\n",
"from langchain.globals import set_debug\n",
"\n",
"langchain.debug = True"
"set_debug(True)"
]
},
{
@@ -411,7 +411,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
"version": "3.10.1"
}
},
"nbformat": 4,

View File

@@ -0,0 +1,399 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "7d143c73",
"metadata": {},
"source": [
"# Bearly Code Interpreter\n",
"\n",
"> Bearly Code Interpreter allows for remote execution of code. This makes it perfect for a code sandbox for agents, to allow for safe implementation of things like Code Interpreter"
]
},
{
"cell_type": "markdown",
"id": "3f99f7c9",
"metadata": {},
"source": [
"In this notebook, we will create an example of an agent that uses Bearly to interact with data"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "cd5b952e",
"metadata": {},
"outputs": [],
"source": [
"from langchain.chat_models import ChatOpenAI\n",
"from langchain.tools import BearlyInterpreterTool\n",
"from langchain.agents import initialize_agent, AgentType"
]
},
{
"cell_type": "markdown",
"id": "7bd0b610",
"metadata": {},
"source": [
"Initialize the interpreter"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "32f1543f",
"metadata": {},
"outputs": [],
"source": [
"bearly_tool = BearlyInterpreterTool(api_key=\"...\")"
]
},
{
"cell_type": "markdown",
"id": "ac5c88ce",
"metadata": {},
"source": [
"Let's add some files to the the sandbox"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "b5586858",
"metadata": {},
"outputs": [],
"source": [
"bearly_tool.add_file(\n",
" source_path=\"sample_data/Bristol.pdf\", \n",
" target_path=\"Bristol.pdf\", \n",
" description=\"\"\n",
")\n",
"bearly_tool.add_file(\n",
" source_path=\"sample_data/US_GDP.csv\", \n",
" target_path=\"US_GDP.csv\", \n",
" description=\"\"\n",
")"
]
},
{
"cell_type": "markdown",
"id": "b5720471",
"metadata": {},
"source": [
"Create a `Tool` object now. This is necessary, because we added the files, and we want the tool description to reflect that"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "557a073c",
"metadata": {},
"outputs": [],
"source": [
"tools = [bearly_tool.as_tool()]"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "58e61295",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'bearly_interpreter'"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tools[0].name"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "483569b6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Evaluates python code in a sandbox environment. The environment resets on every execution. You must send the whole script every time and print your outputs. Script should be pure python code that can be evaluated. It should be in python format NOT markdown. The code should NOT be wrapped in backticks. All python packages including requests, matplotlib, scipy, numpy, pandas, etc are available. If you have any files outputted write them to \"output/\" relative to the execution \n",
"path. Output can only be read from the directory, stdout, and stdin. Do not use things like plot.show() as it will \n",
"not work instead write them out `output/` and a link to the file will be returned. print() any output and results so you can capture the output.\n",
"\n",
"The following files available in the evaluation environment:\n",
"- path: `Bristol.pdf` \n",
" first four lines: [] \n",
" description: ``\n",
"- path: `US_GDP.csv` \n",
" first four lines: ['DATE,GDP\\n', '1947-01-01,243.164\\n', '1947-04-01,245.968\\n', '1947-07-01,249.585\\n'] \n",
" description: ``\n"
]
}
],
"source": [
"print(tools[0].description)"
]
},
{
"cell_type": "markdown",
"id": "24f952d5",
"metadata": {},
"source": [
"Initialize an agent"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "71831ea8",
"metadata": {},
"outputs": [],
"source": [
"llm = ChatOpenAI(model=\"gpt-4\", temperature=0)\n",
"agent = initialize_agent(\n",
" tools, llm, agent=AgentType.OPENAI_FUNCTIONS, verbose=True, handle_parsing_errors=True\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "b4f98540",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
"\u001b[32;1m\u001b[1;3m\n",
"Invoking: `bearly_interpreter` with `{'python_code': \"import PyPDF2\\n\\n# Open the PDF file in read-binary mode\\npdf_file = open('Bristol.pdf', 'rb')\\n\\n# Create a PDF file reader object\\npdf_reader = PyPDF2.PdfFileReader(pdf_file)\\n\\n# Get the text from page 3\\npage_obj = pdf_reader.getPage(2)\\npage_text = page_obj.extractText()\\n\\n# Close the PDF file\\npdf_file.close()\\n\\nprint(page_text)\"}`\n",
"\n",
"\n",
"\u001b[0m\u001b[36;1m\u001b[1;3m{'stdout': '', 'stderr': 'Traceback (most recent call last):\\n File \"/tmp/project/main.py\", line 7, in <module>\\n pdf_reader = PyPDF2.PdfFileReader(pdf_file)\\n File \"/venv/lib/python3.10/site-packages/PyPDF2/_reader.py\", line 1974, in __init__\\n deprecation_with_replacement(\"PdfFileReader\", \"PdfReader\", \"3.0.0\")\\n File \"/venv/lib/python3.10/site-packages/PyPDF2/_utils.py\", line 369, in deprecation_with_replacement\\n deprecation(DEPR_MSG_HAPPENED.format(old_name, removed_in, new_name))\\n File \"/venv/lib/python3.10/site-packages/PyPDF2/_utils.py\", line 351, in deprecation\\n raise DeprecationError(msg)\\nPyPDF2.errors.DeprecationError: PdfFileReader is deprecated and was removed in PyPDF2 3.0.0. Use PdfReader instead.\\n', 'fileLinks': [], 'exitCode': 1}\u001b[0m\u001b[32;1m\u001b[1;3m\n",
"Invoking: `bearly_interpreter` with `{'python_code': \"from PyPDF2 import PdfReader\\n\\n# Open the PDF file\\npdf = PdfReader('Bristol.pdf')\\n\\n# Get the text from page 3\\npage = pdf.pages[2]\\npage_text = page.extract_text()\\n\\nprint(page_text)\"}`\n",
"\n",
"\n",
"\u001b[0m\u001b[36;1m\u001b[1;3m{'stdout': '1 COVID-19 at Work: \\nExposing h ow risk is assessed and its consequences in England and Sweden \\nPeter Andersson and Tonia Novitz* \\n1.Introduction\\nT\\nhe crisis which arose suddenly at the beginning of 2020 relating to coronavirus was immediately \\ncentred on risk. Predictions ha d to be made swiftly regarding how it would spread, who it might \\naffect and what measures could be taken to prevent exposure in everyday so cial interaction, \\nincluding in the workplace. This was in no way a straightforward assessment, because initially so \\nmuch was unknown. Those gaps in our knowledge have since, partially, been ameliorated. It is \\nevident that not all those exposed to COVID-19 become ill, and many who contract the virus remain \\nasymptomatic, so that the odds on becoming seriously ill may seem small. But those odds are also stacked against certain segments of the population. The likelihood of mortality and morbidity are associated with age and ethnicity as well as pre -existing medical conditions (such as diabetes), but \\nalso with poverty which correlates to the extent of exposure in certain occupations.\\n1 Some risks \\narise which remain less predictable, as previously healthy people with no signs of particular \\nvulnerability can experience serious long term illness as well and in rare cases will even die.2 \\nPerceptions of risk in different countries have led to particular measures taken, ranging from handwashing to social distancing, use of personal protective equipment (PPE) such as face coverings, and even lockdowns which have taken various forms.\\n3 Use of testing and vaccines \\nalso bec ame part of the remedial landscape, with their availability and administration being \\n*This paper is part of the project An i nclusive and sustainable Swedish labour law the way\\nahead, dnr. 2017-03134 financed by the Swedish research council led by Petra Herzfeld Olssonat Stockholm University. The authors would like to thank her and other participants, Niklas\\nBruun and Erik Sjödin for their helpful comments on earlier drafts. A much shorter article titled\\nRisk Assessment and COVID -19: Systems at work (or not) in England and Sweden is published\\nin the (2021) Comparative Labour and Social Security Review /\\n Revue de droit comparé du\\ntravail et de la sécurité sociale.\\n1 Public Health England, Disparities in the risk and outcomes of COVID-19 (2 June 2020 -\\nhttps://assets.publishing.service.gov.uk/government/uploads/ system /uploads/attachment_data/file\\n/890258/disparities_review.pdf.\\n2 Nisreen A. Alwan, Track COVID- 19 sickness, not just positive tests and deaths ( 2020)\\n584.7820 Nature 170- 171; Elisabeth Mahase, Covid-19: What do we know about “long covid”?\\n(2020) BMJ 370.\\n3 Sarah Dryhurst, Claudia R. Schneider, John Kerr, Alexandra LJ Freeman, Gabriel Recchia,\\nAnne Marthe Van Der Bles, David Spiegelhalter, and Sander van der Linden, Risk perceptionsof COVID-19 around the world (2020) 23(7- 8) Journal of Risk Research 994; Wändi Bruine de\\nBruin, and Daniel Bennett, Relationships between initial COVID -19 risk perceptions and\\nprotective health behaviors: A national survey (2020) 59(2) American Journal of Prev entive\\nMedicine 157; and Simon Deakin and Gaofeng Meng, The Governance of Covid- 19:\\nAnthropogenic Risk, Evolutionary Learning, and the Future of the Social State (2020)49(4) Industrial Law Journal 539.\\n', 'stderr': '', 'fileLinks': [], 'exitCode': 0}\u001b[0m\u001b[32;1m\u001b[1;3mThe text on page 3 of the PDF is:\n",
"\n",
"\"1 COVID-19 at Work: \n",
"Exposing how risk is assessed and its consequences in England and Sweden \n",
"Peter Andersson and Tonia Novitz* \n",
"1.Introduction\n",
"The crisis which arose suddenly at the beginning of 2020 relating to coronavirus was immediately \n",
"centred on risk. Predictions had to be made swiftly regarding how it would spread, who it might \n",
"affect and what measures could be taken to prevent exposure in everyday social interaction, \n",
"including in the workplace. This was in no way a straightforward assessment, because initially so \n",
"much was unknown. Those gaps in our knowledge have since, partially, been ameliorated. It is \n",
"evident that not all those exposed to COVID-19 become ill, and many who contract the virus remain \n",
"asymptomatic, so that the odds on becoming seriously ill may seem small. But those odds are also stacked against certain segments of the population. The likelihood of mortality and morbidity are associated with age and ethnicity as well as pre-existing medical conditions (such as diabetes), but \n",
"also with poverty which correlates to the extent of exposure in certain occupations.\n",
"1 Some risks \n",
"arise which remain less predictable, as previously healthy people with no signs of particular \n",
"vulnerability can experience serious long term illness as well and in rare cases will even die.2 \n",
"Perceptions of risk in different countries have led to particular measures taken, ranging from handwashing to social distancing, use of personal protective equipment (PPE) such as face coverings, and even lockdowns which have taken various forms.\n",
"3 Use of testing and vaccines \n",
"also became part of the remedial landscape, with their availability and administration being \n",
"*This paper is part of the project An inclusive and sustainable Swedish labour law the way\n",
"ahead, dnr. 2017-03134 financed by the Swedish research council led by Petra Herzfeld Olssonat Stockholm University. The authors would like to thank her and other participants, Niklas\n",
"Bruun and Erik Sjödin for their helpful comments on earlier drafts. A much shorter article titled\n",
"Risk Assessment and COVID -19: Systems at work (or not) in England and Sweden is published\n",
"in the (2021) Comparative Labour and Social Security Review /\n",
" Revue de droit comparé du\n",
"travail et de la sécurité sociale.\n",
"1 Public Health England, Disparities in the risk and outcomes of COVID-19 (2 June 2020 -\n",
"https://assets.publishing.service.gov.uk/government/uploads/ system /uploads/attachment_data/file\n",
"/890258/disparities_review.pdf.\n",
"2 Nisreen A. Alwan, Track COVID- 19 sickness, not just positive tests and deaths ( 2020)\n",
"584.7820 Nature 170- 171; Elisabeth Mahase, Covid-19: What do we know about “long covid”?\n",
"(2020) BMJ 370.\n",
"3 Sarah Dryhurst, Claudia R. Schneider, John Kerr, Alexandra LJ Freeman, Gabriel Recchia,\n",
"Anne Marthe Van Der Bles, David Spiegelhalter, and Sander van der Linden, Risk perceptionsof COVID-19 around the world (2020) 23(7- 8) Journal of Risk Research 994; Wändi Bruine de\n",
"Bruin, and Daniel Bennett, Relationships between initial COVID -19 risk perceptions and\n",
"protective health behaviors: A national survey (2020) 59(2) American Journal of Preventive\n",
"Medicine 157; and Simon Deakin and Gaofeng Meng, The Governance of Covid- 19:\n",
"Anthropogenic Risk, Evolutionary Learning, and the Future of the Social State (2020)49(4) Industrial Law Journal 539.\"\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n"
]
},
{
"data": {
"text/plain": [
"'The text on page 3 of the PDF is:\\n\\n\"1 COVID-19 at Work: \\nExposing how risk is assessed and its consequences in England and Sweden \\nPeter Andersson and Tonia Novitz* \\n1.Introduction\\nThe crisis which arose suddenly at the beginning of 2020 relating to coronavirus was immediately \\ncentred on risk. Predictions had to be made swiftly regarding how it would spread, who it might \\naffect and what measures could be taken to prevent exposure in everyday social interaction, \\nincluding in the workplace. This was in no way a straightforward assessment, because initially so \\nmuch was unknown. Those gaps in our knowledge have since, partially, been ameliorated. It is \\nevident that not all those exposed to COVID-19 become ill, and many who contract the virus remain \\nasymptomatic, so that the odds on becoming seriously ill may seem small. But those odds are also stacked against certain segments of the population. The likelihood of mortality and morbidity are associated with age and ethnicity as well as pre-existing medical conditions (such as diabetes), but \\nalso with poverty which correlates to the extent of exposure in certain occupations.\\n1 Some risks \\narise which remain less predictable, as previously healthy people with no signs of particular \\nvulnerability can experience serious long term illness as well and in rare cases will even die.2 \\nPerceptions of risk in different countries have led to particular measures taken, ranging from handwashing to social distancing, use of personal protective equipment (PPE) such as face coverings, and even lockdowns which have taken various forms.\\n3 Use of testing and vaccines \\nalso became part of the remedial landscape, with their availability and administration being \\n*This paper is part of the project An inclusive and sustainable Swedish labour law the way\\nahead, dnr. 2017-03134 financed by the Swedish research council led by Petra Herzfeld Olssonat Stockholm University. The authors would like to thank her and other participants, Niklas\\nBruun and Erik Sjödin for their helpful comments on earlier drafts. A much shorter article titled\\nRisk Assessment and COVID -19: Systems at work (or not) in England and Sweden is published\\nin the (2021) Comparative Labour and Social Security Review /\\n Revue de droit comparé du\\ntravail et de la sécurité sociale.\\n1 Public Health England, Disparities in the risk and outcomes of COVID-19 (2 June 2020 -\\nhttps://assets.publishing.service.gov.uk/government/uploads/ system /uploads/attachment_data/file\\n/890258/disparities_review.pdf.\\n2 Nisreen A. Alwan, Track COVID- 19 sickness, not just positive tests and deaths ( 2020)\\n584.7820 Nature 170- 171; Elisabeth Mahase, Covid-19: What do we know about “long covid”?\\n(2020) BMJ 370.\\n3 Sarah Dryhurst, Claudia R. Schneider, John Kerr, Alexandra LJ Freeman, Gabriel Recchia,\\nAnne Marthe Van Der Bles, David Spiegelhalter, and Sander van der Linden, Risk perceptionsof COVID-19 around the world (2020) 23(7- 8) Journal of Risk Research 994; Wändi Bruine de\\nBruin, and Daniel Bennett, Relationships between initial COVID -19 risk perceptions and\\nprotective health behaviors: A national survey (2020) 59(2) American Journal of Preventive\\nMedicine 157; and Simon Deakin and Gaofeng Meng, The Governance of Covid- 19:\\nAnthropogenic Risk, Evolutionary Learning, and the Future of the Social State (2020)49(4) Industrial Law Journal 539.\"'"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Extract pdf content\n",
"agent.run(\"What is the text on page 3 of the pdf?\")"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "451e6049",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
"\u001b[32;1m\u001b[1;3m\n",
"Invoking: `bearly_interpreter` with `{'python_code': \"import pandas as pd\\n\\n# Load the data\\nus_gdp = pd.read_csv('US_GDP.csv')\\n\\n# Convert the 'DATE' column to datetime\\nus_gdp['DATE'] = pd.to_datetime(us_gdp['DATE'])\\n\\n# Filter the data for the year 2019\\nus_gdp_2019 = us_gdp[us_gdp['DATE'].dt.year == 2019]\\n\\n# Print the GDP for 2019\\nprint(us_gdp_2019['GDP'].values)\"}`\n",
"\n",
"\n",
"\u001b[0m\u001b[36;1m\u001b[1;3m{'stdout': '[21104.133 21384.775 21694.282 21902.39 ]\\n', 'stderr': '', 'fileLinks': [], 'exitCode': 0}\u001b[0m\u001b[32;1m\u001b[1;3mThe US GDP for each quarter in 2019 was as follows:\n",
"\n",
"- Q1: 21104.133 billion dollars\n",
"- Q2: 21384.775 billion dollars\n",
"- Q3: 21694.282 billion dollars\n",
"- Q4: 21902.39 billion dollars\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n"
]
},
{
"data": {
"text/plain": [
"'The US GDP for each quarter in 2019 was as follows:\\n\\n- Q1: 21104.133 billion dollars\\n- Q2: 21384.775 billion dollars\\n- Q3: 21694.282 billion dollars\\n- Q4: 21902.39 billion dollars'"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Simple Queries\n",
"agent.run(\"What was the US GDP in 2019?\")"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "fa48e793",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
"\u001b[32;1m\u001b[1;3m\n",
"Invoking: `bearly_interpreter` with `{'python_code': \"import pandas as pd\\n\\n# Load the data\\nus_gdp = pd.read_csv('US_GDP.csv')\\n\\n# Get the latest GDP\\nlatest_gdp = us_gdp['GDP'].iloc[-1]\\n\\n# Calculate the GDP in 2030 if the latest GDP number grew by 50%\\ngdp_2030 = latest_gdp * 1.5\\nprint(gdp_2030)\"}`\n",
"\n",
"\n",
"\u001b[0m\u001b[36;1m\u001b[1;3m{'stdout': '40594.518\\n', 'stderr': '', 'fileLinks': [], 'exitCode': 0}\u001b[0m\u001b[32;1m\u001b[1;3mIf the latest GDP number grew by 50%, the GDP in 2030 would be approximately 40,594.518 billion dollars.\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n"
]
},
{
"data": {
"text/plain": [
"'If the latest GDP number grew by 50%, the GDP in 2030 would be approximately 40,594.518 billion dollars.'"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Calculations\n",
"agent.run(\"What would the GDP be in 2030 if the latest GDP number grew by 50%?\")"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "a49efca7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
"\u001b[32;1m\u001b[1;3mCould not parse tool input: {'name': 'bearly_interpreter', 'arguments': '{\\n \"python_code\": \"\\nimport pandas as pd\\nimport matplotlib.pyplot as plt\\n\\n# Load the data\\ndf = pd.read_csv(\\'US_GDP.csv\\')\\n\\n# Convert the \\'DATE\\' column to datetime format\\ndf[\\'DATE\\'] = pd.to_datetime(df[\\'DATE\\'])\\n\\n# Plot the data\\nplt.figure(figsize=(10,6))\\nplt.plot(df[\\'DATE\\'], df[\\'GDP\\'], label=\\'US GDP\\')\\nplt.xlabel(\\'Year\\')\\nplt.ylabel(\\'GDP (in billions)\\')\\nplt.title(\\'US GDP Over Time\\')\\nplt.legend()\\nplt.grid(True)\\nplt.savefig(\\'output/US_GDP.png\\')\\n\"\\n}'} because the `arguments` is not valid JSON.\u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3m\n",
"Invoking: `bearly_interpreter` with `{'python_code': \"\\nimport pandas as pd\\nimport matplotlib.pyplot as plt\\n\\n# Load the data\\ndf = pd.read_csv('US_GDP.csv')\\n\\n# Convert the 'DATE' column to datetime format\\ndf['DATE'] = pd.to_datetime(df['DATE'])\\n\\n# Plot the data\\nplt.figure(figsize=(10,6))\\nplt.plot(df['DATE'], df['GDP'], label='US GDP')\\nplt.xlabel('Year')\\nplt.ylabel('GDP (in billions)')\\nplt.title('US GDP Over Time')\\nplt.legend()\\nplt.grid(True)\\nplt.savefig('output/US_GDP.png')\\n\"}`\n",
"\n",
"\n",
"\u001b[0m\u001b[36;1m\u001b[1;3m{'stdout': '', 'stderr': '', 'fileLinks': [{'pathname': 'US_GDP.png', 'tempLink': 'https://bearly-cubby.c559ae877a0a39985f534614a037d899.r2.cloudflarestorage.com/prod/bearly-cubby/temp/interpreter/2023_10/089daf37e9e343ba5ff21afaaa78b967c3466a550b3b11bd5c710c052b559e97/sxhM8gop2AYP88n5uHCsOJ6yTYNQm-HimZ70DcwQ4VI.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=c058d02de50a3cf0bb7e21c8e2d062c5%2F20231010%2F%2Fs3%2Faws4_request&X-Amz-Date=20231010T000000Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=104dc0d4a4b71eeea1030dda1830059920cb0f354fa00197b439eb8565bf141a', 'size': 34275}], 'exitCode': 0}\u001b[0m\u001b[32;1m\u001b[1;3mHere is the chart of the US GDP growth over time:\n",
"\n",
"![US GDP Over Time](https://bearly-cubby.c559ae877a0a39985f534614a037d899.r2.cloudflarestorage.com/prod/bearly-cubby/temp/interpreter/2023_10/089daf37e9e343ba5ff21afaaa78b967c3466a550b3b11bd5c710c052b559e97/sxhM8gop2AYP88n5uHCsOJ6yTYNQm-HimZ70DcwQ4VI.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=c058d02de50a3cf0bb7e21c8e2d062c5%2F20231010%2F%2Fs3%2Faws4_request&X-Amz-Date=20231010T000000Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=104dc0d4a4b71eeea1030dda1830059920cb0f354fa00197b439eb8565bf141a)\n",
"\n",
"The x-axis represents the year and the y-axis represents the GDP in billions. The line plot shows the growth of the US GDP over time.\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n"
]
},
{
"data": {
"text/plain": [
"'Here is the chart of the US GDP growth over time:\\n\\n![US GDP Over Time](https://bearly-cubby.c559ae877a0a39985f534614a037d899.r2.cloudflarestorage.com/prod/bearly-cubby/temp/interpreter/2023_10/089daf37e9e343ba5ff21afaaa78b967c3466a550b3b11bd5c710c052b559e97/sxhM8gop2AYP88n5uHCsOJ6yTYNQm-HimZ70DcwQ4VI.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=c058d02de50a3cf0bb7e21c8e2d062c5%2F20231010%2F%2Fs3%2Faws4_request&X-Amz-Date=20231010T000000Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=104dc0d4a4b71eeea1030dda1830059920cb0f354fa00197b439eb8565bf141a)\\n\\nThe x-axis represents the year and the y-axis represents the GDP in billions. The line plot shows the growth of the US GDP over time.'"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Chart output\n",
"agent.run(\"Create a nice and labeled chart of the GDP growth over time\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4e78f28e",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -8,7 +8,7 @@ Agents use an LLM to determine which actions to take and in what order.
An action can either be using a tool and observing its output, or returning a response to the user.
Here are the agents available in LangChain.
## [Zero-shot ReAct](/docs/modules/agents/agent_types/react.html)
## [Zero-shot ReAct](/docs/modules/agents/agent_types/react)
This agent uses the [ReAct](https://arxiv.org/pdf/2210.03629) framework to determine which tool to use
based solely on the tool's description. Any number of tools can be provided.
@@ -16,33 +16,33 @@ This agent requires that a description is provided for each tool.
**Note**: This is the most general purpose action agent.
## [Structured input ReAct](/docs/modules/agents/agent_types/structured_chat.html)
## [Structured input ReAct](/docs/modules/agents/agent_types/structured_chat)
The structured tool chat agent is capable of using multi-input tools.
Older agents are configured to specify an action input as a single string, but this agent can use a tools' argument
schema to create a structured action input. This is useful for more complex tool usage, like precisely
navigating around a browser.
## [OpenAI Functions](/docs/modules/agents/agent_types/openai_functions_agent.html)
## [OpenAI Functions](/docs/modules/agents/agent_types/openai_functions_agent)
Certain OpenAI models (like gpt-3.5-turbo-0613 and gpt-4-0613) have been explicitly fine-tuned to detect when a
function should be called and respond with the inputs that should be passed to the function.
The OpenAI Functions Agent is designed to work with these models.
## [Conversational](/docs/modules/agents/agent_types/chat_conversation_agent.html)
## [Conversational](/docs/modules/agents/agent_types/chat_conversation_agent)
This agent is designed to be used in conversational settings.
The prompt is designed to make the agent helpful and conversational.
It uses the ReAct framework to decide which tool to use, and uses memory to remember the previous conversation interactions.
## [Self-ask with search](/docs/modules/agents/agent_types/self_ask_with_search.html)
## [Self-ask with search](/docs/modules/agents/agent_types/self_ask_with_search)
This agent utilizes a single tool that should be named `Intermediate Answer`.
This tool should be able to lookup factual answers to questions. This agent
is equivalent to the original [self-ask with search paper](https://ofir.io/self-ask.pdf),
where a Google search API was provided as the tool.
## [ReAct document store](/docs/modules/agents/agent_types/react_docstore.html)
## [ReAct document store](/docs/modules/agents/agent_types/react_docstore)
This agent uses the ReAct framework to interact with a docstore. Two tools must
be provided: a `Search` tool and a `Lookup` tool (they must be named exactly as so).

View File

@@ -106,9 +106,9 @@
"outputs": [],
"source": [
"# Do this so we can see exactly what's going on under the hood\n",
"import langchain\n",
"from langchain.globals import set_debug\n",
"\n",
"langchain.debug = True"
"set_debug(True)"
]
},
{

View File

@@ -9,6 +9,361 @@ An LLM agent consists of three parts:
- `stop` sequence: Instructs the LLM to stop generating as soon as this string is found
- `OutputParser`: This determines how to parse the LLM output into an `AgentAction` or `AgentFinish` object
import Example from "@snippets/modules/agents/how_to/custom_llm_agent.mdx"
The LLM Agent is used in an `AgentExecutor`. This `AgentExecutor` can largely be thought of as a loop that:
1. Passes user input and any previous steps to the Agent (in this case, the LLM Agent)
2. If the Agent returns an `AgentFinish`, then return that directly to the user
3. If the Agent returns an `AgentAction`, then use that to call a tool and get an `Observation`
4. Repeat, passing the `AgentAction` and `Observation` back to the Agent until an `AgentFinish` is emitted.
<Example/>
`AgentAction` is a response that consists of `action` and `action_input`. `action` refers to which tool to use, and `action_input` refers to the input to that tool. `log` can also be provided as more context (that can be used for logging, tracing, etc).
`AgentFinish` is a response that contains the final message to be sent back to the user. This should be used to end an agent run.
In this notebook we walk through how to create a custom LLM agent.
## Set up environment
Do necessary imports, etc.
```python
from langchain.agents import Tool, AgentExecutor, LLMSingleActionAgent, AgentOutputParser
from langchain.prompts import StringPromptTemplate
from langchain.llms import OpenAI
from langchain.utilities import SerpAPIWrapper
from langchain.chains import LLMChain
from typing import List, Union
from langchain.schema import AgentAction, AgentFinish, OutputParserException
import re
```
## Set up tool
Set up any tools the agent may want to use. This may be necessary to put in the prompt (so that the agent knows to use these tools).
```python
# Define which tools the agent can use to answer user queries
search = SerpAPIWrapper()
tools = [
Tool(
name="Search",
func=search.run,
description="useful for when you need to answer questions about current events"
)
]
```
## Prompt template
This instructs the agent on what to do. Generally, the template should incorporate:
- `tools`: which tools the agent has access and how and when to call them.
- `intermediate_steps`: These are tuples of previous (`AgentAction`, `Observation`) pairs. These are generally not passed directly to the model, but the prompt template formats them in a specific way.
- `input`: generic user input
```python
# Set up the base template
template = """Answer the following questions as best you can, but speaking as a pirate might speak. You have access to the following tools:
{tools}
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin! Remember to speak as a pirate when giving your final answer. Use lots of "Arg"s
Question: {input}
{agent_scratchpad}"""
```
```python
# Set up a prompt template
class CustomPromptTemplate(StringPromptTemplate):
# The template to use
template: str
# The list of tools available
tools: List[Tool]
def format(self, **kwargs) -> str:
# Get the intermediate steps (AgentAction, Observation tuples)
# Format them in a particular way
intermediate_steps = kwargs.pop("intermediate_steps")
thoughts = ""
for action, observation in intermediate_steps:
thoughts += action.log
thoughts += f"\nObservation: {observation}\nThought: "
# Set the agent_scratchpad variable to that value
kwargs["agent_scratchpad"] = thoughts
# Create a tools variable from the list of tools provided
kwargs["tools"] = "\n".join([f"{tool.name}: {tool.description}" for tool in self.tools])
# Create a list of tool names for the tools provided
kwargs["tool_names"] = ", ".join([tool.name for tool in self.tools])
return self.template.format(**kwargs)
```
```python
prompt = CustomPromptTemplate(
template=template,
tools=tools,
# This omits the `agent_scratchpad`, `tools`, and `tool_names` variables because those are generated dynamically
# This includes the `intermediate_steps` variable because that is needed
input_variables=["input", "intermediate_steps"]
)
```
## Output parser
The output parser is responsible for parsing the LLM output into `AgentAction` and `AgentFinish`. This usually depends heavily on the prompt used.
This is where you can change the parsing to do retries, handle whitespace, etc.
```python
class CustomOutputParser(AgentOutputParser):
def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
# Check if agent should finish
if "Final Answer:" in llm_output:
return AgentFinish(
# Return values is generally always a dictionary with a single `output` key
# It is not recommended to try anything else at the moment :)
return_values={"output": llm_output.split("Final Answer:")[-1].strip()},
log=llm_output,
)
# Parse out the action and action input
regex = r"Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
match = re.search(regex, llm_output, re.DOTALL)
if not match:
raise OutputParserException(f"Could not parse LLM output: `{llm_output}`")
action = match.group(1).strip()
action_input = match.group(2)
# Return the action and action input
return AgentAction(tool=action, tool_input=action_input.strip(" ").strip('"'), log=llm_output)
```
```python
output_parser = CustomOutputParser()
```
## Set up LLM
Choose the LLM you want to use!
```python
llm = OpenAI(temperature=0)
```
## Define the stop sequence
This is important because it tells the LLM when to stop generation.
This depends heavily on the prompt and model you are using. Generally, you want this to be whatever token you use in the prompt to denote the start of an `Observation` (otherwise, the LLM may hallucinate an observation for you).
## Set up the Agent
We can now combine everything to set up our agent:
```python
# LLM chain consisting of the LLM and a prompt
llm_chain = LLMChain(llm=llm, prompt=prompt)
```
```python
tool_names = [tool.name for tool in tools]
agent = LLMSingleActionAgent(
llm_chain=llm_chain,
output_parser=output_parser,
stop=["\nObservation:"],
allowed_tools=tool_names
)
```
## Use the Agent
Now we can use it!
```python
agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True)
```
```python
agent_executor.run("How many people live in canada as of 2023?")
```
<CodeOutputBlock lang="python">
```
> Entering new AgentExecutor chain...
Thought: I need to find out the population of Canada in 2023
Action: Search
Action Input: Population of Canada in 2023
Observation:The current population of Canada is 38,658,314 as of Wednesday, April 12, 2023, based on Worldometer elaboration of the latest United Nations data. I now know the final answer
Final Answer: Arrr, there be 38,658,314 people livin' in Canada as of 2023!
> Finished chain.
"Arrr, there be 38,658,314 people livin' in Canada as of 2023!"
```
</CodeOutputBlock>
## Adding Memory
If you want to add memory to the agent, you'll need to:
1. Add a place in the custom prompt for the `chat_history`
2. Add a memory object to the agent executor.
```python
# Set up the base template
template_with_history = """Answer the following questions as best you can, but speaking as a pirate might speak. You have access to the following tools:
{tools}
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin! Remember to speak as a pirate when giving your final answer. Use lots of "Arg"s
Previous conversation history:
{history}
New question: {input}
{agent_scratchpad}"""
```
```python
prompt_with_history = CustomPromptTemplate(
template=template_with_history,
tools=tools,
# This omits the `agent_scratchpad`, `tools`, and `tool_names` variables because those are generated dynamically
# This includes the `intermediate_steps` variable because that is needed
input_variables=["input", "intermediate_steps", "history"]
)
```
```python
llm_chain = LLMChain(llm=llm, prompt=prompt_with_history)
```
```python
tool_names = [tool.name for tool in tools]
agent = LLMSingleActionAgent(
llm_chain=llm_chain,
output_parser=output_parser,
stop=["\nObservation:"],
allowed_tools=tool_names
)
```
```python
from langchain.memory import ConversationBufferWindowMemory
```
```python
memory=ConversationBufferWindowMemory(k=2)
```
```python
agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True, memory=memory)
```
```python
agent_executor.run("How many people live in canada as of 2023?")
```
<CodeOutputBlock lang="python">
```
> Entering new AgentExecutor chain...
Thought: I need to find out the population of Canada in 2023
Action: Search
Action Input: Population of Canada in 2023
Observation:The current population of Canada is 38,658,314 as of Wednesday, April 12, 2023, based on Worldometer elaboration of the latest United Nations data. I now know the final answer
Final Answer: Arrr, there be 38,658,314 people livin' in Canada as of 2023!
> Finished chain.
"Arrr, there be 38,658,314 people livin' in Canada as of 2023!"
```
</CodeOutputBlock>
```python
agent_executor.run("how about in mexico?")
```
<CodeOutputBlock lang="python">
```
> Entering new AgentExecutor chain...
Thought: I need to find out how many people live in Mexico.
Action: Search
Action Input: How many people live in Mexico as of 2023?
Observation:The current population of Mexico is 132,679,922 as of Tuesday, April 11, 2023, based on Worldometer elaboration of the latest United Nations data. Mexico 2020 ... I now know the final answer.
Final Answer: Arrr, there be 132,679,922 people livin' in Mexico as of 2023!
> Finished chain.
"Arrr, there be 132,679,922 people livin' in Mexico as of 2023!"
```
</CodeOutputBlock>

View File

@@ -9,6 +9,251 @@ An LLM chat agent consists of three parts:
- `stop` sequence: Instructs the LLM to stop generating as soon as this string is found
- `OutputParser`: This determines how to parse the LLM output into an `AgentAction` or `AgentFinish` object
import Example from "@snippets/modules/agents/how_to/custom_llm_chat_agent.mdx"
The LLM Agent is used in an `AgentExecutor`. This `AgentExecutor` can largely be thought of as a loop that:
1. Passes user input and any previous steps to the Agent (in this case, the LLM Agent)
2. If the Agent returns an `AgentFinish`, then return that directly to the user
3. If the Agent returns an `AgentAction`, then use that to call a tool and get an `Observation`
4. Repeat, passing the `AgentAction` and `Observation` back to the Agent until an `AgentFinish` is emitted.
<Example/>
`AgentAction` is a response that consists of `action` and `action_input`. `action` refers to which tool to use, and `action_input` refers to the input to that tool. `log` can also be provided as more context (that can be used for logging, tracing, etc).
`AgentFinish` is a response that contains the final message to be sent back to the user. This should be used to end an agent run.
In this notebook we walk through how to create a custom LLM agent.
## Set up environment
Do necessary imports, etc.
```bash
pip install langchain
pip install google-search-results
pip install openai
```
```python
from langchain.agents import Tool, AgentExecutor, LLMSingleActionAgent, AgentOutputParser
from langchain.prompts import BaseChatPromptTemplate
from langchain.utilities import SerpAPIWrapper
from langchain.chains.llm import LLMChain
from langchain.chat_models import ChatOpenAI
from typing import List, Union
from langchain.schema import AgentAction, AgentFinish, HumanMessage
import re
from getpass import getpass
```
## Set up tools
Set up any tools the agent may want to use. This may be necessary to put in the prompt (so that the agent knows to use these tools).
```python
SERPAPI_API_KEY = getpass()
```
```python
# Define which tools the agent can use to answer user queries
search = SerpAPIWrapper(serpapi_api_key=SERPAPI_API_KEY)
tools = [
Tool(
name="Search",
func=search.run,
description="useful for when you need to answer questions about current events"
)
]
```
## Prompt template
This instructs the agent on what to do. Generally, the template should incorporate:
- `tools`: which tools the agent has access and how and when to call them.
- `intermediate_steps`: These are tuples of previous (`AgentAction`, `Observation`) pairs. These are generally not passed directly to the model, but the prompt template formats them in a specific way.
- `input`: generic user input
```python
# Set up the base template
template = """Complete the objective as best you can. You have access to the following tools:
{tools}
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
These were previous tasks you completed:
Begin!
Question: {input}
{agent_scratchpad}"""
```
```python
# Set up a prompt template
class CustomPromptTemplate(BaseChatPromptTemplate):
# The template to use
template: str
# The list of tools available
tools: List[Tool]
def format_messages(self, **kwargs) -> str:
# Get the intermediate steps (AgentAction, Observation tuples)
# Format them in a particular way
intermediate_steps = kwargs.pop("intermediate_steps")
thoughts = ""
for action, observation in intermediate_steps:
thoughts += action.log
thoughts += f"\nObservation: {observation}\nThought: "
# Set the agent_scratchpad variable to that value
kwargs["agent_scratchpad"] = thoughts
# Create a tools variable from the list of tools provided
kwargs["tools"] = "\n".join([f"{tool.name}: {tool.description}" for tool in self.tools])
# Create a list of tool names for the tools provided
kwargs["tool_names"] = ", ".join([tool.name for tool in self.tools])
formatted = self.template.format(**kwargs)
return [HumanMessage(content=formatted)]
```
```python
prompt = CustomPromptTemplate(
template=template,
tools=tools,
# This omits the `agent_scratchpad`, `tools`, and `tool_names` variables because those are generated dynamically
# This includes the `intermediate_steps` variable because that is needed
input_variables=["input", "intermediate_steps"]
)
```
## Output parser
The output parser is responsible for parsing the LLM output into `AgentAction` and `AgentFinish`. This usually depends heavily on the prompt used.
This is where you can change the parsing to do retries, handle whitespace, etc.
```python
class CustomOutputParser(AgentOutputParser):
def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
# Check if agent should finish
if "Final Answer:" in llm_output:
return AgentFinish(
# Return values is generally always a dictionary with a single `output` key
# It is not recommended to try anything else at the moment :)
return_values={"output": llm_output.split("Final Answer:")[-1].strip()},
log=llm_output,
)
# Parse out the action and action input
regex = r"Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
match = re.search(regex, llm_output, re.DOTALL)
if not match:
raise ValueError(f"Could not parse LLM output: `{llm_output}`")
action = match.group(1).strip()
action_input = match.group(2)
# Return the action and action input
return AgentAction(tool=action, tool_input=action_input.strip(" ").strip('"'), log=llm_output)
```
```python
output_parser = CustomOutputParser()
```
## Set up LLM
Choose the LLM you want to use!
```python
OPENAI_API_KEY = getpass()
```
```python
llm = ChatOpenAI(openai_api_key=OPENAI_API_KEY, temperature=0)
```
## Define the stop sequence
This is important because it tells the LLM when to stop generation.
This depends heavily on the prompt and model you are using. Generally, you want this to be whatever token you use in the prompt to denote the start of an `Observation` (otherwise, the LLM may hallucinate an observation for you).
## Set up the Agent
We can now combine everything to set up our agent:
```python
# LLM chain consisting of the LLM and a prompt
llm_chain = LLMChain(llm=llm, prompt=prompt)
```
```python
tool_names = [tool.name for tool in tools]
agent = LLMSingleActionAgent(
llm_chain=llm_chain,
output_parser=output_parser,
stop=["\nObservation:"],
allowed_tools=tool_names
)
```
## Use the Agent
Now we can use it!
```python
agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True)
```
```python
agent_executor.run("Search for Leo DiCaprio's girlfriend on the internet.")
```
<CodeOutputBlock lang="python">
```
> Entering new AgentExecutor chain...
Thought: I should use a reliable search engine to get accurate information.
Action: Search
Action Input: "Leo DiCaprio girlfriend"
Observation:He went on to date Gisele Bündchen, Bar Refaeli, Blake Lively, Toni Garrn and Nina Agdal, among others, before finally settling down with current girlfriend Camila Morrone, who is 23 years his junior.
I have found the answer to the question.
Final Answer: Leo DiCaprio's current girlfriend is Camila Morrone.
> Finished chain.
"Leo DiCaprio's current girlfriend is Camila Morrone."
```
</CodeOutputBlock>

View File

@@ -5,12 +5,265 @@ This walkthrough demonstrates how to replicate the [MRKL](https://arxiv.org/pdf/
This uses the example Chinook database.
To set it up follow the instructions on https://database.guide/2-sample-databases-sqlite/, placing the `.db` file in a notebooks folder at the root of this repository.
import Example from "@snippets/modules/agents/how_to/mrkl.mdx"
```python
from langchain.chains import LLMMathChain
from langchain.llms import OpenAI
from langchain.utilities import SerpAPIWrapper
from langchain.utilities import SQLDatabase
from langchain_experimental.sql import SQLDatabaseChain
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
```
<Example/>
```python
llm = OpenAI(temperature=0)
search = SerpAPIWrapper()
llm_math_chain = LLMMathChain(llm=llm, verbose=True)
db = SQLDatabase.from_uri("sqlite:///../../../../../notebooks/Chinook.db")
db_chain = SQLDatabaseChain.from_llm(llm, db, verbose=True)
tools = [
Tool(
name="Search",
func=search.run,
description="useful for when you need to answer questions about current events. You should ask targeted questions"
),
Tool(
name="Calculator",
func=llm_math_chain.run,
description="useful for when you need to answer questions about math"
),
Tool(
name="FooBar DB",
func=db_chain.run,
description="useful for when you need to answer questions about FooBar. Input should be in the form of a question containing full context"
)
]
```
```python
mrkl = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
```
```python
mrkl.run("Who is Leo DiCaprio's girlfriend? What is her current age raised to the 0.43 power?")
```
<CodeOutputBlock lang="python">
```
> Entering new AgentExecutor chain...
I need to find out who Leo DiCaprio's girlfriend is and then calculate her age raised to the 0.43 power.
Action: Search
Action Input: "Who is Leo DiCaprio's girlfriend?"
Observation: DiCaprio met actor Camila Morrone in December 2017, when she was 20 and he was 43. They were spotted at Coachella and went on multiple vacations together. Some reports suggested that DiCaprio was ready to ask Morrone to marry him. The couple made their red carpet debut at the 2020 Academy Awards.
Thought: I need to calculate Camila Morrone's age raised to the 0.43 power.
Action: Calculator
Action Input: 21^0.43
> Entering new LLMMathChain chain...
21^0.43
```text
21**0.43
```
...numexpr.evaluate("21**0.43")...
Answer: 3.7030049853137306
> Finished chain.
Observation: Answer: 3.7030049853137306
Thought: I now know the final answer.
Final Answer: Camila Morrone is Leo DiCaprio's girlfriend and her current age raised to the 0.43 power is 3.7030049853137306.
> Finished chain.
"Camila Morrone is Leo DiCaprio's girlfriend and her current age raised to the 0.43 power is 3.7030049853137306."
```
</CodeOutputBlock>
```python
mrkl.run("What is the full name of the artist who recently released an album called 'The Storm Before the Calm' and are they in the FooBar database? If so, what albums of theirs are in the FooBar database?")
```
<CodeOutputBlock lang="python">
```
> Entering new AgentExecutor chain...
I need to find out the artist's full name and then search the FooBar database for their albums.
Action: Search
Action Input: "The Storm Before the Calm" artist
Observation: The Storm Before the Calm (stylized in all lowercase) is the tenth (and eighth international) studio album by Canadian-American singer-songwriter Alanis Morissette, released June 17, 2022, via Epiphany Music and Thirty Tigers, as well as by RCA Records in Europe.
Thought: I now need to search the FooBar database for Alanis Morissette's albums.
Action: FooBar DB
Action Input: What albums by Alanis Morissette are in the FooBar database?
> Entering new SQLDatabaseChain chain...
What albums by Alanis Morissette are in the FooBar database?
SQLQuery:
/Users/harrisonchase/workplace/langchain/langchain/sql_database.py:191: SAWarning: Dialect sqlite+pysqlite does *not* support Decimal objects natively, and SQLAlchemy must convert from floating point - rounding errors and other issues may occur. Please consider storing Decimal numbers as strings or integers on this platform for lossless storage.
sample_rows = connection.execute(command)
SELECT "Title" FROM "Album" INNER JOIN "Artist" ON "Album"."ArtistId" = "Artist"."ArtistId" WHERE "Name" = 'Alanis Morissette' LIMIT 5;
SQLResult: [('Jagged Little Pill',)]
Answer: The albums by Alanis Morissette in the FooBar database are Jagged Little Pill.
> Finished chain.
Observation: The albums by Alanis Morissette in the FooBar database are Jagged Little Pill.
Thought: I now know the final answer.
Final Answer: The artist who released the album 'The Storm Before the Calm' is Alanis Morissette and the albums of hers in the FooBar database are Jagged Little Pill.
> Finished chain.
"The artist who released the album 'The Storm Before the Calm' is Alanis Morissette and the albums of hers in the FooBar database are Jagged Little Pill."
```
</CodeOutputBlock>
## With a chat model
import ChatExample from "@snippets/modules/agents/how_to/mrkl_chat.mdx"
```python
from langchain.chat_models import ChatOpenAI
<ChatExample/>
llm = ChatOpenAI(temperature=0)
llm1 = OpenAI(temperature=0)
search = SerpAPIWrapper()
llm_math_chain = LLMMathChain(llm=llm1, verbose=True)
db = SQLDatabase.from_uri("sqlite:///../../../../../notebooks/Chinook.db")
db_chain = SQLDatabaseChain.from_llm(llm1, db, verbose=True)
tools = [
Tool(
name="Search",
func=search.run,
description="useful for when you need to answer questions about current events. You should ask targeted questions"
),
Tool(
name="Calculator",
func=llm_math_chain.run,
description="useful for when you need to answer questions about math"
),
Tool(
name="FooBar DB",
func=db_chain.run,
description="useful for when you need to answer questions about FooBar. Input should be in the form of a question containing full context"
)
]
```
```python
mrkl = initialize_agent(tools, llm, agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
```
```python
mrkl.run("Who is Leo DiCaprio's girlfriend? What is her current age raised to the 0.43 power?")
```
<CodeOutputBlock lang="python">
```
> Entering new AgentExecutor chain...
Thought: The first question requires a search, while the second question requires a calculator.
Action:
```
{
"action": "Search",
"action_input": "Leo DiCaprio girlfriend"
}
```
Observation: Gigi Hadid: 2022 Leo and Gigi were first linked back in September 2022, when a source told Us Weekly that Leo had his “sights set" on her (alarming way to put it, but okay).
Thought:For the second question, I need to calculate the age raised to the 0.43 power. I will use the calculator tool.
Action:
```
{
"action": "Calculator",
"action_input": "((2022-1995)^0.43)"
}
```
> Entering new LLMMathChain chain...
((2022-1995)^0.43)
```text
(2022-1995)**0.43
```
...numexpr.evaluate("(2022-1995)**0.43")...
Answer: 4.125593352125936
> Finished chain.
Observation: Answer: 4.125593352125936
Thought:I now know the final answer.
Final Answer: Gigi Hadid is Leo DiCaprio's girlfriend and her current age raised to the 0.43 power is approximately 4.13.
> Finished chain.
"Gigi Hadid is Leo DiCaprio's girlfriend and her current age raised to the 0.43 power is approximately 4.13."
```
</CodeOutputBlock>
```python
mrkl.run("What is the full name of the artist who recently released an album called 'The Storm Before the Calm' and are they in the FooBar database? If so, what albums of theirs are in the FooBar database?")
```
<CodeOutputBlock lang="python">
```
> Entering new AgentExecutor chain...
Question: What is the full name of the artist who recently released an album called 'The Storm Before the Calm' and are they in the FooBar database? If so, what albums of theirs are in the FooBar database?
Thought: I should use the Search tool to find the answer to the first part of the question and then use the FooBar DB tool to find the answer to the second part.
Action:
```
{
"action": "Search",
"action_input": "Who recently released an album called 'The Storm Before the Calm'"
}
```
Observation: Alanis Morissette
Thought:Now that I know the artist's name, I can use the FooBar DB tool to find out if they are in the database and what albums of theirs are in it.
Action:
```
{
"action": "FooBar DB",
"action_input": "What albums does Alanis Morissette have in the database?"
}
```
> Entering new SQLDatabaseChain chain...
What albums does Alanis Morissette have in the database?
SQLQuery:
/Users/harrisonchase/workplace/langchain/langchain/sql_database.py:191: SAWarning: Dialect sqlite+pysqlite does *not* support Decimal objects natively, and SQLAlchemy must convert from floating point - rounding errors and other issues may occur. Please consider storing Decimal numbers as strings or integers on this platform for lossless storage.
sample_rows = connection.execute(command)
SELECT "Title" FROM "Album" WHERE "ArtistId" IN (SELECT "ArtistId" FROM "Artist" WHERE "Name" = 'Alanis Morissette') LIMIT 5;
SQLResult: [('Jagged Little Pill',)]
Answer: Alanis Morissette has the album Jagged Little Pill in the database.
> Finished chain.
Observation: Alanis Morissette has the album Jagged Little Pill in the database.
Thought:The artist Alanis Morissette is in the FooBar database and has the album Jagged Little Pill in it.
Final Answer: Alanis Morissette is in the FooBar database and has the album Jagged Little Pill in it.
> Finished chain.
'Alanis Morissette is in the FooBar database and has the album Jagged Little Pill in it.'
```
</CodeOutputBlock>

View File

@@ -87,9 +87,219 @@ These include:
## Get started
import GetStarted from "@snippets/modules/agents/get_started.mdx"
This will go over how to get started building an agent.
We will create this agent from scratch, using LangChain Expression Language.
We will then define custom tools, and then run it in a custom loop (we will also show how to use the standard LangChain `AgentExecutor`).
<GetStarted/>
### Set up the agent
We first need to create our agent.
This is the chain responsible for determining what action to take next.
In this example, we will use OpenAI Function Calling to create this agent.
This is generally the most reliable way create agents.
In this example we will show what it is like to construct this agent from scratch, using LangChain Expression Language.
For this guide, we will construct a custom agent that has access to a custom tool.
We are choosing this example because we think for most use cases you will NEED to customize either the agent or the tools.
The tool we will give the agent is a tool to calculate the length of a word.
This is useful because this is actually something LLMs can mess up due to tokenization.
We will first create it WITHOUT memory, but we will then show how to add memory in.
Memory is needed to enable conversation.
First, let's load the language model we're going to use to control the agent.
```python
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(temperature=0)
```
Next, let's define some tools to use.
Let's write a really simple Python function to calculate the length of a word that is passed in.
```python
from langchain.agents import tool
@tool
def get_word_length(word: str) -> int:
"""Returns the length of a word."""
return len(word)
tools = [get_word_length]
```
Now let us create the prompt.
Because OpenAI Function Calling is finetuned for tool usage, we hardly need any instructions on how to reason, or how to output format.
We will just have two input variables: `input` (for the user question) and `agent_scratchpad` (for any previous steps taken)
```python
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt = ChatPromptTemplate.from_messages([
("system", "You are very powerful assistant, but bad at calculating lengths of words."),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
```
How does the agent know what tools it can use?
Those are passed in as a separate argument, so we can bind those as keyword arguments to the LLM.
```python
from langchain.tools.render import format_tool_to_openai_function
llm_with_tools = llm.bind(
functions=[format_tool_to_openai_function(t) for t in tools]
)
```
Putting those pieces together, we can now create the agent.
We will import two last utility functions: a component for formatting intermediate steps to messages, and a component for converting the output message into an agent action/agent finish.
```python
from langchain.agents.format_scratchpad import format_to_openai_functions
from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
agent = {
"input": lambda x: x["input"],
"agent_scratchpad": lambda x: format_to_openai_functions(x['intermediate_steps'])
} | prompt | llm_with_tools | OpenAIFunctionsAgentOutputParser()
```
Now that we have our agent, let's play around with it!
Let's pass in a simple question and empty intermediate steps and see what it returns:
```python
agent.invoke({
"input": "how many letters in the word educa?",
"intermediate_steps": []
})
```
We can see that it responds with an `AgentAction` to take (it's actually an `AgentActionMessageLog` - a subclass of `AgentAction` which also tracks the full message log).
So this is just the first step - now we need to write a runtime for this.
The simplest one is just one that continuously loops, calling the agent, then taking the action, and repeating until an `AgentFinish` is returned.
Let's code that up below:
```python
from langchain.schema.agent import AgentFinish
intermediate_steps = []
while True:
output = agent.invoke({
"input": "how many letters in the word educa?",
"intermediate_steps": intermediate_steps
})
if isinstance(output, AgentFinish):
final_result = output.return_values["output"]
break
else:
print(output.tool, output.tool_input)
tool = {
"get_word_length": get_word_length
}[output.tool]
observation = tool.run(output.tool_input)
intermediate_steps.append((output, observation))
print(final_result)
```
We can see this prints out the following:
<CodeOutputBlock lang="python">
```
get_word_length {'word': 'educa'}
There are 5 letters in the word "educa".
```
</CodeOutputBlock>
Woo! It's working.
To simplify this a bit, we can import and use the `AgentExecutor` class.
This bundles up all of the above and adds in error handling, early stopping, tracing, and other quality-of-life improvements that reduce safeguards you need to write.
```python
from langchain.agents import AgentExecutor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
```
Now let's test it out!
```python
agent_executor.invoke({"input": "how many letters in the word educa?"})
```
<CodeOutputBlock lang="python">
```
> Entering new AgentExecutor chain...
Invoking: `get_word_length` with `{'word': 'educa'}`
5
There are 5 letters in the word "educa".
> Finished chain.
'There are 5 letters in the word "educa".'
```
</CodeOutputBlock>
This is great - we have an agent!
However, this agent is stateless - it doesn't remember anything about previous interactions.
This means you can't ask follow up questions easily.
Let's fix that by adding in memory.
In order to do this, we need to do two things:
1. Add a place for memory variables to go in the prompt
2. Keep track of the chat history
First, let's add a place for memory in the prompt.
We do this by adding a placeholder for messages with the key `"chat_history"`.
Notice that we put this ABOVE the new user input (to follow the conversation flow).
```python
from langchain.prompts import MessagesPlaceholder
MEMORY_KEY = "chat_history"
prompt = ChatPromptTemplate.from_messages([
("system", "You are very powerful assistant, but bad at calculating lengths of words."),
MessagesPlaceholder(variable_name=MEMORY_KEY),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
```
We can then set up a list to track the chat history
```
from langchain.schema.messages import HumanMessage, AIMessage
chat_history = []
```
We can then put it all together!
```python
agent = {
"input": lambda x: x["input"],
"agent_scratchpad": lambda x: format_to_openai_functions(x['intermediate_steps']),
"chat_history": lambda x: x["chat_history"]
} | prompt | llm_with_tools | OpenAIFunctionsAgentOutputParser()
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
```
When running, we now need to track the inputs and outputs as chat history
```
input1 = "how many letters in the word educa?"
result = agent_executor.invoke({"input": input1, "chat_history": chat_history})
chat_history.append(HumanMessage(content=input1))
chat_history.append(AIMessage(content=result['output']))
agent_executor.invoke({"input": "is that a real word?", "chat_history": chat_history})
```
## Next Steps

View File

@@ -16,6 +16,18 @@ These tools can be generic utilities (e.g. search), other chains, or even other
Currently, tools can be loaded with the following snippet:
import GetStarted from "@snippets/modules/agents/tools/get_started.mdx"
```python
from langchain.agents import load_tools
tool_names = [...]
tools = load_tools(tool_names)
```
<GetStarted/>
Some tools (e.g. chains, agents) may require a base LLM to use to initialize them.
In that case, you can pass in an LLM as well:
```python
from langchain.agents import load_tools
tool_names = [...]
llm = ...
tools = load_tools(tool_names, llm=llm)
```

View File

@@ -9,6 +9,142 @@ Head to [Integrations](/docs/integrations/callbacks/) for documentation on built
LangChain provides a callbacks system that allows you to hook into the various stages of your LLM application. This is useful for logging, monitoring, streaming, and other tasks.
import GetStarted from "@snippets/modules/callbacks/get_started.mdx"
You can subscribe to these events by using the `callbacks` argument available throughout the API. This argument is list of handler objects, which are expected to implement one or more of the methods described below in more detail.
## Callback handlers
`CallbackHandlers` are objects that implement the `CallbackHandler` interface, which has a method for each event that can be subscribed to. The `CallbackManager` will call the appropriate method on each handler when the event is triggered.
```python
class BaseCallbackHandler:
"""Base callback handler that can be used to handle callbacks from langchain."""
def on_llm_start(
self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
) -> Any:
"""Run when LLM starts running."""
def on_chat_model_start(
self, serialized: Dict[str, Any], messages: List[List[BaseMessage]], **kwargs: Any
) -> Any:
"""Run when Chat Model starts running."""
def on_llm_new_token(self, token: str, **kwargs: Any) -> Any:
"""Run on new LLM token. Only available when streaming is enabled."""
def on_llm_end(self, response: LLMResult, **kwargs: Any) -> Any:
"""Run when LLM ends running."""
def on_llm_error(
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
) -> Any:
"""Run when LLM errors."""
def on_chain_start(
self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any
) -> Any:
"""Run when chain starts running."""
def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> Any:
"""Run when chain ends running."""
def on_chain_error(
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
) -> Any:
"""Run when chain errors."""
def on_tool_start(
self, serialized: Dict[str, Any], input_str: str, **kwargs: Any
) -> Any:
"""Run when tool starts running."""
def on_tool_end(self, output: str, **kwargs: Any) -> Any:
"""Run when tool ends running."""
def on_tool_error(
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
) -> Any:
"""Run when tool errors."""
def on_text(self, text: str, **kwargs: Any) -> Any:
"""Run on arbitrary text."""
def on_agent_action(self, action: AgentAction, **kwargs: Any) -> Any:
"""Run on agent action."""
def on_agent_finish(self, finish: AgentFinish, **kwargs: Any) -> Any:
"""Run on agent end."""
```
## Get started
LangChain provides a few built-in handlers that you can use to get started. These are available in the `langchain/callbacks` module. The most basic handler is the `StdOutCallbackHandler`, which simply logs all events to `stdout`.
**Note**: when the `verbose` flag on the object is set to true, the `StdOutCallbackHandler` will be invoked even without being explicitly passed in.
```python
from langchain.callbacks import StdOutCallbackHandler
from langchain.chains import LLMChain
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
handler = StdOutCallbackHandler()
llm = OpenAI()
prompt = PromptTemplate.from_template("1 + {number} = ")
# Constructor callback: First, let's explicitly set the StdOutCallbackHandler when initializing our chain
chain = LLMChain(llm=llm, prompt=prompt, callbacks=[handler])
chain.run(number=2)
# Use verbose flag: Then, let's use the `verbose` flag to achieve the same result
chain = LLMChain(llm=llm, prompt=prompt, verbose=True)
chain.run(number=2)
# Request callbacks: Finally, let's use the request `callbacks` to achieve the same result
chain = LLMChain(llm=llm, prompt=prompt)
chain.run(number=2, callbacks=[handler])
```
<CodeOutputBlock lang="python">
```
> Entering new LLMChain chain...
Prompt after formatting:
1 + 2 =
> Finished chain.
> Entering new LLMChain chain...
Prompt after formatting:
1 + 2 =
> Finished chain.
> Entering new LLMChain chain...
Prompt after formatting:
1 + 2 =
> Finished chain.
'\n\n3'
```
</CodeOutputBlock>
## Where to pass in callbacks
The `callbacks` argument is available on most objects throughout the API (Chains, Models, Tools, Agents, etc.) in two different places:
- **Constructor callbacks**: defined in the constructor, e.g. `LLMChain(callbacks=[handler], tags=['a-tag'])`, which will be used for all calls made on that object, and will be scoped to that object only, e.g. if you pass a handler to the `LLMChain` constructor, it will not be used by the Model attached to that chain.
- **Request callbacks**: defined in the `run()`/`apply()` methods used for issuing a request, e.g. `chain.run(input, callbacks=[handler])`, which will be used for that specific request only, and all sub-requests that it contains (e.g. a call to an LLMChain triggers a call to a Model, which uses the same handler passed in the `call()` method).
The `verbose` argument is available on most objects throughout the API (Chains, Models, Tools, Agents, etc.) as a constructor argument, e.g. `LLMChain(verbose=True)`, and it is equivalent to passing a `ConsoleCallbackHandler` to the `callbacks` argument of that object and all child objects. This is useful for debugging, as it will log all events to the console.
### When do you want to use each of these?
- Constructor callbacks are most useful for use cases such as logging, monitoring, etc., which are _not specific to a single request_, but rather to the entire chain. For example, if you want to log all the requests made to an `LLMChain`, you would pass a handler to the constructor.
- Request callbacks are most useful for use cases such as streaming, where you want to stream the output of a single request to a specific websocket connection, or other similar use cases. For example, if you want to stream the output of a single request to a websocket, you would pass a handler to the `call()` method
<GetStarted/>

View File

@@ -7,10 +7,15 @@ These are the core chains for working with documents. They are useful for summar
These chains all implement a common interface:
import Interface from "@snippets/modules/chains/document/combine_docs.mdx"
```python
class BaseCombineDocumentsChain(Chain, ABC):
"""Base interface for chains combining documents."""
<Interface/>
@abstractmethod
def combine_docs(self, docs: List[Document], **kwargs: Any) -> Tuple[str, dict]:
"""Combine documents into a single string."""
```
import DocCardList from "@theme/DocCardList";
<DocCardList />

View File

@@ -6,6 +6,166 @@ An `LLMChain` consists of a `PromptTemplate` and a language model (either an LLM
## Get started
import Example from "@snippets/modules/chains/foundational/llm_chain.mdx"
```python
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI
from langchain.chains import LLMChain
<Example/>
prompt_template = "What is a good name for a company that makes {product}?"
llm = OpenAI(temperature=0)
llm_chain = LLMChain(
llm=llm,
prompt=PromptTemplate.from_template(prompt_template)
)
llm_chain("colorful socks")
```
<CodeOutputBlock lang="python">
```
{'product': 'colorful socks', 'text': '\n\nSocktastic!'}
```
</CodeOutputBlock>
## Additional ways of running `LLMChain`
Aside from `__call__` and `run` methods shared by all `Chain` object, `LLMChain` offers a few more ways of calling the chain logic:
- `apply` allows you run the chain against a list of inputs:
```python
input_list = [
{"product": "socks"},
{"product": "computer"},
{"product": "shoes"}
]
llm_chain.apply(input_list)
```
<CodeOutputBlock lang="python">
```
[{'text': '\n\nSocktastic!'},
{'text': '\n\nTechCore Solutions.'},
{'text': '\n\nFootwear Factory.'}]
```
</CodeOutputBlock>
- `generate` is similar to `apply`, except it return an `LLMResult` instead of string. `LLMResult` often contains useful generation such as token usages and finish reason.
```python
llm_chain.generate(input_list)
```
<CodeOutputBlock lang="python">
```
LLMResult(generations=[[Generation(text='\n\nSocktastic!', generation_info={'finish_reason': 'stop', 'logprobs': None})], [Generation(text='\n\nTechCore Solutions.', generation_info={'finish_reason': 'stop', 'logprobs': None})], [Generation(text='\n\nFootwear Factory.', generation_info={'finish_reason': 'stop', 'logprobs': None})]], llm_output={'token_usage': {'prompt_tokens': 36, 'total_tokens': 55, 'completion_tokens': 19}, 'model_name': 'text-davinci-003'})
```
</CodeOutputBlock>
- `predict` is similar to `run` method except that the input keys are specified as keyword arguments instead of a Python dict.
```python
# Single input example
llm_chain.predict(product="colorful socks")
```
<CodeOutputBlock lang="python">
```
'\n\nSocktastic!'
```
</CodeOutputBlock>
```python
# Multiple inputs example
template = """Tell me a {adjective} joke about {subject}."""
prompt = PromptTemplate(template=template, input_variables=["adjective", "subject"])
llm_chain = LLMChain(prompt=prompt, llm=OpenAI(temperature=0))
llm_chain.predict(adjective="sad", subject="ducks")
```
<CodeOutputBlock lang="python">
```
'\n\nQ: What did the duck say when his friend died?\nA: Quack, quack, goodbye.'
```
</CodeOutputBlock>
## Parsing the outputs
By default, `LLMChain` does not parse the output even if the underlying `prompt` object has an output parser. If you would like to apply that output parser on the LLM output, use `predict_and_parse` instead of `predict` and `apply_and_parse` instead of `apply`.
With `predict`:
```python
from langchain.output_parsers import CommaSeparatedListOutputParser
output_parser = CommaSeparatedListOutputParser()
template = """List all the colors in a rainbow"""
prompt = PromptTemplate(template=template, input_variables=[], output_parser=output_parser)
llm_chain = LLMChain(prompt=prompt, llm=llm)
llm_chain.predict()
```
<CodeOutputBlock lang="python">
```
'\n\nRed, orange, yellow, green, blue, indigo, violet'
```
</CodeOutputBlock>
With `predict_and_parse`:
```python
llm_chain.predict_and_parse()
```
<CodeOutputBlock lang="python">
```
['Red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']
```
</CodeOutputBlock>
## Initialize from string
You can also construct an `LLMChain` from a string template directly.
```python
template = """Tell me a {adjective} joke about {subject}."""
llm_chain = LLMChain.from_string(llm=llm, template=template)
```
```python
llm_chain.predict(adjective="sad", subject="ducks")
```
<CodeOutputBlock lang="python">
```
'\n\nQ: What did the duck say when his friend died?\nA: Quack, quack, goodbye.'
```
</CodeOutputBlock>

View File

@@ -9,6 +9,221 @@ In this notebook we will walk through some examples of how to do this, using seq
- `SimpleSequentialChain`: The simplest form of sequential chains, where each step has a singular input/output, and the output of one step is the input to the next.
- `SequentialChain`: A more general form of sequential chains, allowing for multiple inputs/outputs.
import Example from "@snippets/modules/chains/foundational/sequential_chains.mdx"
```python
from langchain.llms import OpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
```
<Example/>
```python
# This is an LLMChain to write a synopsis given a title of a play.
llm = OpenAI(temperature=.7)
synopsis_template = """You are a playwright. Given the title of play, it is your job to write a synopsis for that title.
Title: {title}
Playwright: This is a synopsis for the above play:"""
synopsis_prompt_template = PromptTemplate(input_variables=["title"], template=synopsis_template)
synopsis_chain = LLMChain(llm=llm, prompt=synopsis_prompt_template)
```
```python
# This is an LLMChain to write a review of a play given a synopsis.
llm = OpenAI(temperature=.7)
template = """You are a play critic from the New York Times. Given the synopsis of play, it is your job to write a review for that play.
Play Synopsis:
{synopsis}
Review from a New York Times play critic of the above play:"""
prompt_template = PromptTemplate(input_variables=["synopsis"], template=template)
review_chain = LLMChain(llm=llm, prompt=prompt_template)
```
```python
# This is the overall chain where we run these two chains in sequence.
from langchain.chains import SimpleSequentialChain
overall_chain = SimpleSequentialChain(chains=[synopsis_chain, review_chain], verbose=True)
```
```python
review = overall_chain.run("Tragedy at sunset on the beach")
```
<CodeOutputBlock lang="python">
```
> Entering new SimpleSequentialChain chain...
Tragedy at Sunset on the Beach is a story of a young couple, Jack and Sarah, who are in love and looking forward to their future together. On the night of their anniversary, they decide to take a walk on the beach at sunset. As they are walking, they come across a mysterious figure, who tells them that their love will be tested in the near future.
The figure then tells the couple that the sun will soon set, and with it, a tragedy will strike. If Jack and Sarah can stay together and pass the test, they will be granted everlasting love. However, if they fail, their love will be lost forever.
The play follows the couple as they struggle to stay together and battle the forces that threaten to tear them apart. Despite the tragedy that awaits them, they remain devoted to one another and fight to keep their love alive. In the end, the couple must decide whether to take a chance on their future together or succumb to the tragedy of the sunset.
Tragedy at Sunset on the Beach is an emotionally gripping story of love, hope, and sacrifice. Through the story of Jack and Sarah, the audience is taken on a journey of self-discovery and the power of love to overcome even the greatest of obstacles.
The play's talented cast brings the characters to life, allowing us to feel the depths of their emotion and the intensity of their struggle. With its compelling story and captivating performances, this play is sure to draw in audiences and leave them on the edge of their seats.
The play's setting of the beach at sunset adds a touch of poignancy and romanticism to the story, while the mysterious figure serves to keep the audience enthralled. Overall, Tragedy at Sunset on the Beach is an engaging and thought-provoking play that is sure to leave audiences feeling inspired and hopeful.
> Finished chain.
```
</CodeOutputBlock>
```python
print(review)
```
<CodeOutputBlock lang="python">
```
Tragedy at Sunset on the Beach is an emotionally gripping story of love, hope, and sacrifice. Through the story of Jack and Sarah, the audience is taken on a journey of self-discovery and the power of love to overcome even the greatest of obstacles.
The play's talented cast brings the characters to life, allowing us to feel the depths of their emotion and the intensity of their struggle. With its compelling story and captivating performances, this play is sure to draw in audiences and leave them on the edge of their seats.
The play's setting of the beach at sunset adds a touch of poignancy and romanticism to the story, while the mysterious figure serves to keep the audience enthralled. Overall, Tragedy at Sunset on the Beach is an engaging and thought-provoking play that is sure to leave audiences feeling inspired and hopeful.
```
</CodeOutputBlock>
## Sequential Chain
Of course, not all sequential chains will be as simple as passing a single string as an argument and getting a single string as output for all steps in the chain. In this next example, we will experiment with more complex chains that involve multiple inputs, and where there also multiple final outputs.
Of particular importance is how we name the input/output variables. In the above example we didn't have to think about that because we were just passing the output of one chain directly as input to the next, but here we do have worry about that because we have multiple inputs.
```python
# This is an LLMChain to write a synopsis given a title of a play and the era it is set in.
llm = OpenAI(temperature=.7)
synopsis_template = """You are a playwright. Given the title of play and the era it is set in, it is your job to write a synopsis for that title.
Title: {title}
Era: {era}
Playwright: This is a synopsis for the above play:"""
synopsis_prompt_template = PromptTemplate(input_variables=["title", "era"], template=synopsis_template)
synopsis_chain = LLMChain(llm=llm, prompt=synopsis_prompt_template, output_key="synopsis")
```
```python
# This is an LLMChain to write a review of a play given a synopsis.
llm = OpenAI(temperature=.7)
template = """You are a play critic from the New York Times. Given the synopsis of play, it is your job to write a review for that play.
Play Synopsis:
{synopsis}
Review from a New York Times play critic of the above play:"""
prompt_template = PromptTemplate(input_variables=["synopsis"], template=template)
review_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="review")
```
```python
# This is the overall chain where we run these two chains in sequence.
from langchain.chains import SequentialChain
overall_chain = SequentialChain(
chains=[synopsis_chain, review_chain],
input_variables=["era", "title"],
# Here we return multiple variables
output_variables=["synopsis", "review"],
verbose=True)
```
```python
overall_chain({"title":"Tragedy at sunset on the beach", "era": "Victorian England"})
```
<CodeOutputBlock lang="python">
```
> Entering new SequentialChain chain...
> Finished chain.
{'title': 'Tragedy at sunset on the beach',
'era': 'Victorian England',
'synopsis': "\n\nThe play follows the story of John, a young man from a wealthy Victorian family, who dreams of a better life for himself. He soon meets a beautiful young woman named Mary, who shares his dream. The two fall in love and decide to elope and start a new life together.\n\nOn their journey, they make their way to a beach at sunset, where they plan to exchange their vows of love. Unbeknownst to them, their plans are overheard by John's father, who has been tracking them. He follows them to the beach and, in a fit of rage, confronts them. \n\nA physical altercation ensues, and in the struggle, John's father accidentally stabs Mary in the chest with his sword. The two are left in shock and disbelief as Mary dies in John's arms, her last words being a declaration of her love for him.\n\nThe tragedy of the play comes to a head when John, broken and with no hope of a future, chooses to take his own life by jumping off the cliffs into the sea below. \n\nThe play is a powerful story of love, hope, and loss set against the backdrop of 19th century England.",
'review': "\n\nThe latest production from playwright X is a powerful and heartbreaking story of love and loss set against the backdrop of 19th century England. The play follows John, a young man from a wealthy Victorian family, and Mary, a beautiful young woman with whom he falls in love. The two decide to elope and start a new life together, and the audience is taken on a journey of hope and optimism for the future.\n\nUnfortunately, their dreams are cut short when John's father discovers them and in a fit of rage, fatally stabs Mary. The tragedy of the play is further compounded when John, broken and without hope, takes his own life. The storyline is not only realistic, but also emotionally compelling, drawing the audience in from start to finish.\n\nThe acting was also commendable, with the actors delivering believable and nuanced performances. The playwright and director have successfully crafted a timeless tale of love and loss that will resonate with audiences for years to come. Highly recommended."}
```
</CodeOutputBlock>
### Memory in Sequential Chains
Sometimes you may want to pass along some context to use in each step of the chain or in a later part of the chain, but maintaining and chaining together the input/output variables can quickly get messy. Using `SimpleMemory` is a convenient way to do manage this and clean up your chains.
For example, using the previous playwright `SequentialChain`, lets say you wanted to include some context about date, time and location of the play, and using the generated synopsis and review, create some social media post text. You could add these new context variables as `input_variables`, or we can add a `SimpleMemory` to the chain to manage this context:
```python
from langchain.chains import SequentialChain
from langchain.memory import SimpleMemory
llm = OpenAI(temperature=.7)
template = """You are a social media manager for a theater company. Given the title of play, the era it is set in, the date,time and location, the synopsis of the play, and the review of the play, it is your job to write a social media post for that play.
Here is some context about the time and location of the play:
Date and Time: {time}
Location: {location}
Play Synopsis:
{synopsis}
Review from a New York Times play critic of the above play:
{review}
Social Media Post:
"""
prompt_template = PromptTemplate(input_variables=["synopsis", "review", "time", "location"], template=template)
social_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="social_post_text")
overall_chain = SequentialChain(
memory=SimpleMemory(memories={"time": "December 25th, 8pm PST", "location": "Theater in the Park"}),
chains=[synopsis_chain, review_chain, social_chain],
input_variables=["era", "title"],
# Here we return multiple variables
output_variables=["social_post_text"],
verbose=True)
overall_chain({"title":"Tragedy at sunset on the beach", "era": "Victorian England"})
```
<CodeOutputBlock lang="python">
```
> Entering new SequentialChain chain...
> Finished chain.
{'title': 'Tragedy at sunset on the beach',
'era': 'Victorian England',
'time': 'December 25th, 8pm PST',
'location': 'Theater in the Park',
'social_post_text': "\nSpend your Christmas night with us at Theater in the Park and experience the heartbreaking story of love and loss that is 'A Walk on the Beach'. Set in Victorian England, this romantic tragedy follows the story of Frances and Edward, a young couple whose love is tragically cut short. Don't miss this emotional and thought-provoking production that is sure to leave you in tears. #AWalkOnTheBeach #LoveAndLoss #TheaterInThePark #VictorianEngland"}
```
</CodeOutputBlock>

View File

@@ -2,7 +2,34 @@
It can be hard to debug a `Chain` object solely from its output as most `Chain` objects involve a fair amount of input prompt preprocessing and LLM output post-processing.
import Example from "@snippets/modules/chains/how_to/debugging.mdx"
Setting `verbose` to `True` will print out some internal states of the `Chain` object while it is being ran.
```python
conversation = ConversationChain(
llm=chat,
memory=ConversationBufferMemory(),
verbose=True
)
conversation.run("What is ChatGPT?")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
Human: What is ChatGPT?
AI:
> Finished chain.
'ChatGPT is an AI language model developed by OpenAI. It is based on the GPT-3 architecture and is capable of generating human-like responses to text prompts. ChatGPT has been trained on a massive amount of text data and can understand and respond to a wide range of topics. It is often used for chatbots, virtual assistants, and other conversational AI applications.'
```
</CodeOutputBlock>
<Example/>

View File

@@ -4,7 +4,29 @@ Chains can be initialized with a Memory object, which will persist data across c
## Get started
import GetStarted from "@snippets/modules/chains/how_to/memory.mdx"
```python
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
conversation = ConversationChain(
llm=chat,
memory=ConversationBufferMemory()
)
conversation.run("Answer briefly. What are the first 3 colors of a rainbow?")
# -> The first three colors of a rainbow are red, orange, and yellow.
conversation.run("And the next 4?")
# -> The next four colors of a rainbow are green, blue, indigo, and violet.
```
<CodeOutputBlock lang="python">
```
'The next four colors of a rainbow are green, blue, indigo, and violet.'
```
</CodeOutputBlock>
Essentially, `BaseMemory` defines an interface of how `langchain` stores memory. It allows reading of stored data through `load_memory_variables` method and storing new data through `save_context` method. You can learn more about it in the [Memory](/docs/modules/memory/) section.
<GetStarted/>

View File

@@ -9,9 +9,21 @@ but more complex applications require chaining LLMs - either with each other or
LangChain provides the **Chain** interface for such "chained" applications. We define a Chain very generically as a sequence of calls to components, which can include other chains. The base interface is simple:
import BaseClass from "@snippets/modules/chains/base_class.mdx"
```python
class Chain(BaseModel, ABC):
"""Base interface that all chains should implement."""
<BaseClass/>
memory: BaseMemory
callbacks: Callbacks
def __call__(
self,
inputs: Any,
return_only_outputs: bool = False,
callbacks: Callbacks = None,
) -> Dict[str, Any]:
...
```
This idea of composing components together in a chain is simple but powerful. It drastically simplifies and makes more modular the implementation of complex applications, which in turn makes it much easier to debug, maintain, and improve your applications.
@@ -26,6 +38,90 @@ Chains allow us to combine multiple components together to create a single, cohe
## Get started
import GetStarted from "@snippets/modules/chains/get_started.mdx"
#### Using `LLMChain`
<GetStarted/>
The `LLMChain` is most basic building block chain. It takes in a prompt template, formats it with the user input and returns the response from an LLM.
To use the `LLMChain`, first create a prompt template.
```python
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
llm = OpenAI(temperature=0.9)
prompt = PromptTemplate(
input_variables=["product"],
template="What is a good name for a company that makes {product}?",
)
```
We can now create a very simple chain that will take user input, format the prompt with it, and then send it to the LLM.
```python
from langchain.chains import LLMChain
chain = LLMChain(llm=llm, prompt=prompt)
# Run the chain only specifying the input variable.
print(chain.run("colorful socks"))
```
<CodeOutputBlock lang="python">
```
Colorful Toes Co.
```
</CodeOutputBlock>
If there are multiple variables, you can input them all at once using a dictionary.
```python
prompt = PromptTemplate(
input_variables=["company", "product"],
template="What is a good name for {company} that makes {product}?",
)
chain = LLMChain(llm=llm, prompt=prompt)
print(chain.run({
'company': "ABC Startup",
'product': "colorful socks"
}))
```
<CodeOutputBlock lang="python">
```
Socktopia Colourful Creations.
```
</CodeOutputBlock>
You can use a chat model in an `LLMChain` as well:
```python
from langchain.chat_models import ChatOpenAI
from langchain.prompts.chat import (
ChatPromptTemplate,
HumanMessagePromptTemplate,
)
human_message_prompt = HumanMessagePromptTemplate(
prompt=PromptTemplate(
template="What is a good name for a company that makes {product}?",
input_variables=["product"],
)
)
chat_prompt_template = ChatPromptTemplate.from_messages([human_message_prompt])
chat = ChatOpenAI(temperature=0.9)
chain = LLMChain(llm=chat, prompt=chat_prompt_template)
print(chain.run("colorful socks"))
```
<CodeOutputBlock lang="python">
```
Rainbow Socks Co.
```
</CodeOutputBlock>

File diff suppressed because one or more lines are too long

View File

@@ -2,6 +2,280 @@
This covers how to load all documents in a directory.
import Example from "@snippets/modules/data_connection/document_loaders/how_to/file_directory.mdx"
Under the hood, by default this uses the [UnstructuredLoader](/docs/integrations/document_loaders/unstructured_file.html).
<Example/>
```python
from langchain.document_loaders import DirectoryLoader
```
We can use the `glob` parameter to control which files to load. Note that here it doesn't load the `.rst` file or the `.html` files.
```python
loader = DirectoryLoader('../', glob="**/*.md")
```
```python
docs = loader.load()
```
```python
len(docs)
```
<CodeOutputBlock lang="python">
```
1
```
</CodeOutputBlock>
## Show a progress bar
By default a progress bar will not be shown. To show a progress bar, install the `tqdm` library (e.g. `pip install tqdm`), and set the `show_progress` parameter to `True`.
```python
loader = DirectoryLoader('../', glob="**/*.md", show_progress=True)
docs = loader.load()
```
<CodeOutputBlock lang="python">
```
Requirement already satisfied: tqdm in /Users/jon/.pyenv/versions/3.9.16/envs/microbiome-app/lib/python3.9/site-packages (4.65.0)
0it [00:00, ?it/s]
```
</CodeOutputBlock>
## Use multithreading
By default the loading happens in one thread. In order to utilize several threads set the `use_multithreading` flag to true.
```python
loader = DirectoryLoader('../', glob="**/*.md", use_multithreading=True)
docs = loader.load()
```
## Change loader class
By default this uses the `UnstructuredLoader` class. However, you can change up the type of loader pretty easily.
```python
from langchain.document_loaders import TextLoader
```
```python
loader = DirectoryLoader('../', glob="**/*.md", loader_cls=TextLoader)
```
```python
docs = loader.load()
```
```python
len(docs)
```
<CodeOutputBlock lang="python">
```
1
```
</CodeOutputBlock>
If you need to load Python source code files, use the `PythonLoader`.
```python
from langchain.document_loaders import PythonLoader
```
```python
loader = DirectoryLoader('../../../../../', glob="**/*.py", loader_cls=PythonLoader)
```
```python
docs = loader.load()
```
```python
len(docs)
```
<CodeOutputBlock lang="python">
```
691
```
</CodeOutputBlock>
## Auto-detect file encodings with TextLoader
In this example we will see some strategies that can be useful when loading a big list of arbitrary files from a directory using the `TextLoader` class.
First to illustrate the problem, let's try to load multiple text with arbitrary encodings.
```python
path = '../../../../../tests/integration_tests/examples'
loader = DirectoryLoader(path, glob="**/*.txt", loader_cls=TextLoader)
```
### A. Default Behavior
```python
loader.load()
```
<HTMLOutputBlock center>
```html
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="color: #800000; text-decoration-color: #800000">╭─────────────────────────────── </span><span style="color: #800000; text-decoration-color: #800000; font-weight: bold">Traceback </span><span style="color: #bf7f7f; text-decoration-color: #bf7f7f; font-weight: bold">(most recent call last)</span><span style="color: #800000; text-decoration-color: #800000"> ────────────────────────────────╮</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #bfbf7f; text-decoration-color: #bfbf7f">/data/source/langchain/langchain/document_loaders/</span><span style="color: #808000; text-decoration-color: #808000; font-weight: bold">text.py</span>:<span style="color: #0000ff; text-decoration-color: #0000ff">29</span> in <span style="color: #00ff00; text-decoration-color: #00ff00">load</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">26 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ </span>text = <span style="color: #808000; text-decoration-color: #808000">""</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">27 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">with</span> <span style="color: #00ffff; text-decoration-color: #00ffff">open</span>(<span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.file_path, encoding=<span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.encoding) <span style="color: #0000ff; text-decoration-color: #0000ff">as</span> f: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">28 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">try</span>: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">❱ </span>29 <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ </span>text = f.read() <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">30 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">except</span> <span style="color: #00ffff; text-decoration-color: #00ffff">UnicodeDecodeError</span> <span style="color: #0000ff; text-decoration-color: #0000ff">as</span> e: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">31 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">if</span> <span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.autodetect_encoding: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">32 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ </span>detected_encodings = <span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.detect_file_encodings() <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #bfbf7f; text-decoration-color: #bfbf7f">/home/spike/.pyenv/versions/3.9.11/lib/python3.9/</span><span style="color: #808000; text-decoration-color: #808000; font-weight: bold">codecs.py</span>:<span style="color: #0000ff; text-decoration-color: #0000ff">322</span> in <span style="color: #00ff00; text-decoration-color: #00ff00">decode</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f"> 319 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ </span><span style="color: #0000ff; text-decoration-color: #0000ff">def</span> <span style="color: #00ff00; text-decoration-color: #00ff00">decode</span>(<span style="color: #00ffff; text-decoration-color: #00ffff">self</span>, <span style="color: #00ffff; text-decoration-color: #00ffff">input</span>, final=<span style="color: #0000ff; text-decoration-color: #0000ff">False</span>): <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f"> 320 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f"># decode input (taking the buffer into account)</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f"> 321 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ </span>data = <span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.buffer + <span style="color: #00ffff; text-decoration-color: #00ffff">input</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">❱ </span> 322 <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ </span>(result, consumed) = <span style="color: #00ffff; text-decoration-color: #00ffff">self</span>._buffer_decode(data, <span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.errors, final) <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f"> 323 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f"># keep undecoded input until the next call</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f"> 324 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ </span><span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.buffer = data[consumed:] <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f"> 325 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">return</span> result <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</span>
<span style="color: #ff0000; text-decoration-color: #ff0000; font-weight: bold">UnicodeDecodeError: </span><span style="color: #008000; text-decoration-color: #008000">'utf-8'</span> codec can't decode byte <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0xca</span> in position <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0</span>: invalid continuation byte
<span style="font-style: italic">The above exception was the direct cause of the following exception:</span>
<span style="color: #800000; text-decoration-color: #800000">╭─────────────────────────────── </span><span style="color: #800000; text-decoration-color: #800000; font-weight: bold">Traceback </span><span style="color: #bf7f7f; text-decoration-color: #bf7f7f; font-weight: bold">(most recent call last)</span><span style="color: #800000; text-decoration-color: #800000"> ────────────────────────────────╮</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> in <span style="color: #00ff00; text-decoration-color: #00ff00">&lt;module&gt;</span>:<span style="color: #0000ff; text-decoration-color: #0000ff">1</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">❱ </span>1 loader.load() <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">2 </span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #bfbf7f; text-decoration-color: #bfbf7f">/data/source/langchain/langchain/document_loaders/</span><span style="color: #808000; text-decoration-color: #808000; font-weight: bold">directory.py</span>:<span style="color: #0000ff; text-decoration-color: #0000ff">84</span> in <span style="color: #00ff00; text-decoration-color: #00ff00">load</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">81 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">if</span> <span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.silent_errors: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">82 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ │ │ </span>logger.warning(e) <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">83 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">else</span>: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">❱ </span>84 <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">raise</span> e <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">85 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">finally</span>: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">86 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">if</span> pbar: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">87 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ │ │ </span>pbar.update(<span style="color: #0000ff; text-decoration-color: #0000ff">1</span>) <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #bfbf7f; text-decoration-color: #bfbf7f">/data/source/langchain/langchain/document_loaders/</span><span style="color: #808000; text-decoration-color: #808000; font-weight: bold">directory.py</span>:<span style="color: #0000ff; text-decoration-color: #0000ff">78</span> in <span style="color: #00ff00; text-decoration-color: #00ff00">load</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">75 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">if</span> i.is_file(): <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">76 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">if</span> _is_visible(i.relative_to(p)) <span style="color: #ff00ff; text-decoration-color: #ff00ff">or</span> <span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.load_hidden: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">77 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">try</span>: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">❱ </span>78 <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ │ </span>sub_docs = <span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.loader_cls(<span style="color: #00ffff; text-decoration-color: #00ffff">str</span>(i), **<span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.loader_kwargs).load() <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">79 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ │ </span>docs.extend(sub_docs) <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">80 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">except</span> <span style="color: #00ffff; text-decoration-color: #00ffff">Exception</span> <span style="color: #0000ff; text-decoration-color: #0000ff">as</span> e: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">81 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">if</span> <span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.silent_errors: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #bfbf7f; text-decoration-color: #bfbf7f">/data/source/langchain/langchain/document_loaders/</span><span style="color: #808000; text-decoration-color: #808000; font-weight: bold">text.py</span>:<span style="color: #0000ff; text-decoration-color: #0000ff">44</span> in <span style="color: #00ff00; text-decoration-color: #00ff00">load</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">41 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">except</span> <span style="color: #00ffff; text-decoration-color: #00ffff">UnicodeDecodeError</span>: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">42 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">continue</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">43 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">else</span>: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #800000; text-decoration-color: #800000">❱ </span>44 <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">raise</span> <span style="color: #00ffff; text-decoration-color: #00ffff">RuntimeError</span>(<span style="color: #808000; text-decoration-color: #808000">f"Error loading {</span><span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.file_path<span style="color: #808000; text-decoration-color: #808000">}"</span>) <span style="color: #0000ff; text-decoration-color: #0000ff">from</span> <span style="color: #00ffff; text-decoration-color: #00ffff; text-decoration: underline">e</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">45 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">except</span> <span style="color: #00ffff; text-decoration-color: #00ffff">Exception</span> <span style="color: #0000ff; text-decoration-color: #0000ff">as</span> e: <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">46 </span><span style="color: #7f7f7f; text-decoration-color: #7f7f7f">│ │ │ │ </span><span style="color: #0000ff; text-decoration-color: #0000ff">raise</span> <span style="color: #00ffff; text-decoration-color: #00ffff">RuntimeError</span>(<span style="color: #808000; text-decoration-color: #808000">f"Error loading {</span><span style="color: #00ffff; text-decoration-color: #00ffff">self</span>.file_path<span style="color: #808000; text-decoration-color: #808000">}"</span>) <span style="color: #0000ff; text-decoration-color: #0000ff">from</span> <span style="color: #00ffff; text-decoration-color: #00ffff; text-decoration: underline">e</span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">│</span> <span style="color: #7f7f7f; text-decoration-color: #7f7f7f">47 </span> <span style="color: #800000; text-decoration-color: #800000">│</span>
<span style="color: #800000; text-decoration-color: #800000">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</span>
<span style="color: #ff0000; text-decoration-color: #ff0000; font-weight: bold">RuntimeError: </span>Error loading ..<span style="color: #800080; text-decoration-color: #800080">/../../../../tests/integration_tests/examples/</span><span style="color: #ff00ff; text-decoration-color: #ff00ff">example-non-utf8.txt</span>
</pre>
```
</HTMLOutputBlock>
The file `example-non-utf8.txt` uses a different encoding, so the `load()` function fails with a helpful message indicating which file failed decoding.
With the default behavior of `TextLoader` any failure to load any of the documents will fail the whole loading process and no documents are loaded.
### B. Silent fail
We can pass the parameter `silent_errors` to the `DirectoryLoader` to skip the files which could not be loaded and continue the load process.
```python
loader = DirectoryLoader(path, glob="**/*.txt", loader_cls=TextLoader, silent_errors=True)
docs = loader.load()
```
<CodeOutputBlock lang="python">
```
Error loading ../../../../../tests/integration_tests/examples/example-non-utf8.txt
```
</CodeOutputBlock>
```python
doc_sources = [doc.metadata['source'] for doc in docs]
doc_sources
```
<CodeOutputBlock lang="python">
```
['../../../../../tests/integration_tests/examples/whatsapp_chat.txt',
'../../../../../tests/integration_tests/examples/example-utf8.txt']
```
</CodeOutputBlock>
### C. Auto detect encodings
We can also ask `TextLoader` to auto detect the file encoding before failing, by passing the `autodetect_encoding` to the loader class.
```python
text_loader_kwargs={'autodetect_encoding': True}
loader = DirectoryLoader(path, glob="**/*.txt", loader_cls=TextLoader, loader_kwargs=text_loader_kwargs)
docs = loader.load()
```
```python
doc_sources = [doc.metadata['source'] for doc in docs]
doc_sources
```
<CodeOutputBlock lang="python">
```
['../../../../../tests/integration_tests/examples/example-non-utf8.txt',
'../../../../../tests/integration_tests/examples/whatsapp_chat.txt',
'../../../../../tests/integration_tests/examples/example-utf8.txt']
```
</CodeOutputBlock>

View File

@@ -4,6 +4,53 @@
This covers how to load `HTML` documents into a document format that we can use downstream.
import Example from "@snippets/modules/data_connection/document_loaders/how_to/html.mdx"
```python
from langchain.document_loaders import UnstructuredHTMLLoader
```
<Example/>
```python
loader = UnstructuredHTMLLoader("example_data/fake-content.html")
```
```python
data = loader.load()
```
```python
data
```
<CodeOutputBlock lang="python">
```
[Document(page_content='My First Heading\n\nMy first paragraph.', lookup_str='', metadata={'source': 'example_data/fake-content.html'}, lookup_index=0)]
```
</CodeOutputBlock>
## Loading HTML with BeautifulSoup4
We can also use `BeautifulSoup4` to load HTML documents using the `BSHTMLLoader`. This will extract the text from the HTML into `page_content`, and the page title as `title` into `metadata`.
```python
from langchain.document_loaders import BSHTMLLoader
```
```python
loader = BSHTMLLoader("example_data/fake-content.html")
data = loader.load()
data
```
<CodeOutputBlock lang="python">
```
[Document(page_content='\n\nTest Title\n\n\nMy First Heading\nMy first paragraph.\n\n\n', metadata={'source': 'example_data/fake-content.html', 'title': 'Test Title'})]
```
</CodeOutputBlock>

View File

@@ -16,6 +16,21 @@ implement a "lazy load" as well for lazily loading data into memory.
## Get started
import GetStarted from "@snippets/modules/data_connection/document_loaders/get_started.mdx"
The simplest loader reads in a file as text and places it all into one document.
<GetStarted/>
```python
from langchain.document_loaders import TextLoader
loader = TextLoader("./index.md")
loader.load()
```
<CodeOutputBlock language="python">
```
[
Document(page_content='---\nsidebar_position: 0\n---\n# Document loaders\n\nUse document loaders to load data from a source as `Document`\'s. A `Document` is a piece of text\nand associated metadata. For example, there are document loaders for loading a simple `.txt` file, for loading the text\ncontents of any web page, or even for loading a transcript of a YouTube video.\n\nEvery document loader exposes two methods:\n1. "Load": load documents from the configured source\n2. "Load and split": load documents from the configured source and split them using the passed in text splitter\n\nThey optionally implement:\n\n3. "Lazy load": load documents into memory lazily\n', metadata={'source': '../docs/docs/modules/data_connection/document_loaders/index.md'})
]
```
</CodeOutputBlock>

View File

@@ -4,6 +4,338 @@
>[JSON Lines](https://jsonlines.org/) is a file format where each line is a valid JSON value.
import Example from "@snippets/modules/data_connection/document_loaders/how_to/json.mdx"
>The `JSONLoader` uses a specified [jq schema](https://en.wikipedia.org/wiki/Jq_(programming_language)) to parse the JSON files. It uses the `jq` python package.
Check this [manual](https://stedolan.github.io/jq/manual/#Basicfilters) for a detailed documentation of the `jq` syntax.
<Example/>
```python
#!pip install jq
```
```python
from langchain.document_loaders import JSONLoader
```
```python
import json
from pathlib import Path
from pprint import pprint
file_path='./example_data/facebook_chat.json'
data = json.loads(Path(file_path).read_text())
```
```python
pprint(data)
```
<CodeOutputBlock lang="python">
```
{'image': {'creation_timestamp': 1675549016, 'uri': 'image_of_the_chat.jpg'},
'is_still_participant': True,
'joinable_mode': {'link': '', 'mode': 1},
'magic_words': [],
'messages': [{'content': 'Bye!',
'sender_name': 'User 2',
'timestamp_ms': 1675597571851},
{'content': 'Oh no worries! Bye',
'sender_name': 'User 1',
'timestamp_ms': 1675597435669},
{'content': 'No Im sorry it was my mistake, the blue one is not '
'for sale',
'sender_name': 'User 2',
'timestamp_ms': 1675596277579},
{'content': 'I thought you were selling the blue one!',
'sender_name': 'User 1',
'timestamp_ms': 1675595140251},
{'content': 'Im not interested in this bag. Im interested in the '
'blue one!',
'sender_name': 'User 1',
'timestamp_ms': 1675595109305},
{'content': 'Here is $129',
'sender_name': 'User 2',
'timestamp_ms': 1675595068468},
{'photos': [{'creation_timestamp': 1675595059,
'uri': 'url_of_some_picture.jpg'}],
'sender_name': 'User 2',
'timestamp_ms': 1675595060730},
{'content': 'Online is at least $100',
'sender_name': 'User 2',
'timestamp_ms': 1675595045152},
{'content': 'How much do you want?',
'sender_name': 'User 1',
'timestamp_ms': 1675594799696},
{'content': 'Goodmorning! $50 is too low.',
'sender_name': 'User 2',
'timestamp_ms': 1675577876645},
{'content': 'Hi! Im interested in your bag. Im offering $50. Let '
'me know if you are interested. Thanks!',
'sender_name': 'User 1',
'timestamp_ms': 1675549022673}],
'participants': [{'name': 'User 1'}, {'name': 'User 2'}],
'thread_path': 'inbox/User 1 and User 2 chat',
'title': 'User 1 and User 2 chat'}
```
</CodeOutputBlock>
## Using `JSONLoader`
Suppose we are interested in extracting the values under the `content` field within the `messages` key of the JSON data. This can easily be done through the `JSONLoader` as shown below.
### JSON file
```python
loader = JSONLoader(
file_path='./example_data/facebook_chat.json',
jq_schema='.messages[].content',
text_content=False)
data = loader.load()
```
```python
pprint(data)
```
<CodeOutputBlock lang="python">
```
[Document(page_content='Bye!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 1}),
Document(page_content='Oh no worries! Bye', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 2}),
Document(page_content='No Im sorry it was my mistake, the blue one is not for sale', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 3}),
Document(page_content='I thought you were selling the blue one!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 4}),
Document(page_content='Im not interested in this bag. Im interested in the blue one!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 5}),
Document(page_content='Here is $129', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 6}),
Document(page_content='', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 7}),
Document(page_content='Online is at least $100', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 8}),
Document(page_content='How much do you want?', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 9}),
Document(page_content='Goodmorning! $50 is too low.', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 10}),
Document(page_content='Hi! Im interested in your bag. Im offering $50. Let me know if you are interested. Thanks!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 11})]
```
</CodeOutputBlock>
### JSON Lines file
If you want to load documents from a JSON Lines file, you pass `json_lines=True`
and specify `jq_schema` to extract `page_content` from a single JSON object.
```python
file_path = './example_data/facebook_chat_messages.jsonl'
pprint(Path(file_path).read_text())
```
<CodeOutputBlock lang="python">
```
('{"sender_name": "User 2", "timestamp_ms": 1675597571851, "content": "Bye!"}\n'
'{"sender_name": "User 1", "timestamp_ms": 1675597435669, "content": "Oh no '
'worries! Bye"}\n'
'{"sender_name": "User 2", "timestamp_ms": 1675596277579, "content": "No Im '
'sorry it was my mistake, the blue one is not for sale"}\n')
```
</CodeOutputBlock>
```python
loader = JSONLoader(
file_path='./example_data/facebook_chat_messages.jsonl',
jq_schema='.content',
text_content=False,
json_lines=True)
data = loader.load()
```
```python
pprint(data)
```
<CodeOutputBlock lang="python">
```
[Document(page_content='Bye!', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 1}),
Document(page_content='Oh no worries! Bye', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 2}),
Document(page_content='No Im sorry it was my mistake, the blue one is not for sale', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 3})]
```
</CodeOutputBlock>
Another option is set `jq_schema='.'` and provide `content_key`:
```python
loader = JSONLoader(
file_path='./example_data/facebook_chat_messages.jsonl',
jq_schema='.',
content_key='sender_name',
json_lines=True)
data = loader.load()
```
```python
pprint(data)
```
<CodeOutputBlock lang="python">
```
[Document(page_content='User 2', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 1}),
Document(page_content='User 1', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 2}),
Document(page_content='User 2', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 3})]
```
</CodeOutputBlock>
## Extracting metadata
Generally, we want to include metadata available in the JSON file into the documents that we create from the content.
The following demonstrates how metadata can be extracted using the `JSONLoader`.
There are some key changes to be noted. In the previous example where we didn't collect the metadata, we managed to directly specify in the schema where the value for the `page_content` can be extracted from.
```
.messages[].content
```
In the current example, we have to tell the loader to iterate over the records in the `messages` field. The jq_schema then has to be:
```
.messages[]
```
This allows us to pass the records (dict) into the `metadata_func` that has to be implemented. The `metadata_func` is responsible for identifying which pieces of information in the record should be included in the metadata stored in the final `Document` object.
Additionally, we now have to explicitly specify in the loader, via the `content_key` argument, the key from the record where the value for the `page_content` needs to be extracted from.
```python
# Define the metadata extraction function.
def metadata_func(record: dict, metadata: dict) -> dict:
metadata["sender_name"] = record.get("sender_name")
metadata["timestamp_ms"] = record.get("timestamp_ms")
return metadata
loader = JSONLoader(
file_path='./example_data/facebook_chat.json',
jq_schema='.messages[]',
content_key="content",
metadata_func=metadata_func
)
data = loader.load()
```
```python
pprint(data)
```
<CodeOutputBlock lang="python">
```
[Document(page_content='Bye!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 1, 'sender_name': 'User 2', 'timestamp_ms': 1675597571851}),
Document(page_content='Oh no worries! Bye', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 2, 'sender_name': 'User 1', 'timestamp_ms': 1675597435669}),
Document(page_content='No Im sorry it was my mistake, the blue one is not for sale', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 3, 'sender_name': 'User 2', 'timestamp_ms': 1675596277579}),
Document(page_content='I thought you were selling the blue one!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 4, 'sender_name': 'User 1', 'timestamp_ms': 1675595140251}),
Document(page_content='Im not interested in this bag. Im interested in the blue one!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 5, 'sender_name': 'User 1', 'timestamp_ms': 1675595109305}),
Document(page_content='Here is $129', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 6, 'sender_name': 'User 2', 'timestamp_ms': 1675595068468}),
Document(page_content='', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 7, 'sender_name': 'User 2', 'timestamp_ms': 1675595060730}),
Document(page_content='Online is at least $100', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 8, 'sender_name': 'User 2', 'timestamp_ms': 1675595045152}),
Document(page_content='How much do you want?', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 9, 'sender_name': 'User 1', 'timestamp_ms': 1675594799696}),
Document(page_content='Goodmorning! $50 is too low.', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 10, 'sender_name': 'User 2', 'timestamp_ms': 1675577876645}),
Document(page_content='Hi! Im interested in your bag. Im offering $50. Let me know if you are interested. Thanks!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 11, 'sender_name': 'User 1', 'timestamp_ms': 1675549022673})]
```
</CodeOutputBlock>
Now, you will see that the documents contain the metadata associated with the content we extracted.
## The `metadata_func`
As shown above, the `metadata_func` accepts the default metadata generated by the `JSONLoader`. This allows full control to the user with respect to how the metadata is formatted.
For example, the default metadata contains the `source` and the `seq_num` keys. However, it is possible that the JSON data contain these keys as well. The user can then exploit the `metadata_func` to rename the default keys and use the ones from the JSON data.
The example below shows how we can modify the `source` to only contain information of the file source relative to the `langchain` directory.
```python
# Define the metadata extraction function.
def metadata_func(record: dict, metadata: dict) -> dict:
metadata["sender_name"] = record.get("sender_name")
metadata["timestamp_ms"] = record.get("timestamp_ms")
if "source" in metadata:
source = metadata["source"].split("/")
source = source[source.index("langchain"):]
metadata["source"] = "/".join(source)
return metadata
loader = JSONLoader(
file_path='./example_data/facebook_chat.json',
jq_schema='.messages[]',
content_key="content",
metadata_func=metadata_func
)
data = loader.load()
```
```python
pprint(data)
```
<CodeOutputBlock lang="python">
```
[Document(page_content='Bye!', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 1, 'sender_name': 'User 2', 'timestamp_ms': 1675597571851}),
Document(page_content='Oh no worries! Bye', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 2, 'sender_name': 'User 1', 'timestamp_ms': 1675597435669}),
Document(page_content='No Im sorry it was my mistake, the blue one is not for sale', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 3, 'sender_name': 'User 2', 'timestamp_ms': 1675596277579}),
Document(page_content='I thought you were selling the blue one!', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 4, 'sender_name': 'User 1', 'timestamp_ms': 1675595140251}),
Document(page_content='Im not interested in this bag. Im interested in the blue one!', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 5, 'sender_name': 'User 1', 'timestamp_ms': 1675595109305}),
Document(page_content='Here is $129', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 6, 'sender_name': 'User 2', 'timestamp_ms': 1675595068468}),
Document(page_content='', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 7, 'sender_name': 'User 2', 'timestamp_ms': 1675595060730}),
Document(page_content='Online is at least $100', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 8, 'sender_name': 'User 2', 'timestamp_ms': 1675595045152}),
Document(page_content='How much do you want?', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 9, 'sender_name': 'User 1', 'timestamp_ms': 1675594799696}),
Document(page_content='Goodmorning! $50 is too low.', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 10, 'sender_name': 'User 2', 'timestamp_ms': 1675577876645}),
Document(page_content='Hi! Im interested in your bag. Im offering $50. Let me know if you are interested. Thanks!', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 11, 'sender_name': 'User 1', 'timestamp_ms': 1675549022673})]
```
</CodeOutputBlock>
## Common JSON structures with jq schema
The list below provides a reference to the possible `jq_schema` the user can use to extract content from the JSON data depending on the structure.
```
JSON -> [{"text": ...}, {"text": ...}, {"text": ...}]
jq_schema -> ".[].text"
JSON -> {"key": [{"text": ...}, {"text": ...}, {"text": ...}]}
jq_schema -> ".key[].text"
JSON -> ["...", "...", "..."]
jq_schema -> ".[]"
```

View File

@@ -4,6 +4,62 @@
This covers how to load `Markdown` documents into a document format that we can use downstream.
import Example from "@snippets/modules/data_connection/document_loaders/how_to/markdown.mdx"
```python
# !pip install unstructured > /dev/null
```
<Example/>
```python
from langchain.document_loaders import UnstructuredMarkdownLoader
```
```python
markdown_path = "../../../../../README.md"
loader = UnstructuredMarkdownLoader(markdown_path)
```
```python
data = loader.load()
```
```python
data
```
<CodeOutputBlock lang="python">
```
[Document(page_content="ð\x9f¦\x9cï¸\x8fð\x9f”\x97 LangChain\n\nâ\x9a¡ Building applications with LLMs through composability â\x9a¡\n\nLooking for the JS/TS version? Check out LangChain.js.\n\nProduction Support: As you move your LangChains into production, we'd love to offer more comprehensive support.\nPlease fill out this form and we'll set up a dedicated support Slack channel.\n\nQuick Install\n\npip install langchain\nor\nconda install langchain -c conda-forge\n\nð\x9f¤” What is this?\n\nLarge language models (LLMs) are emerging as a transformative technology, enabling developers to build applications that they previously could not. However, using these LLMs in isolation is often insufficient for creating a truly powerful app - the real power comes when you can combine them with other sources of computation or knowledge.\n\nThis library aims to assist in the development of those types of applications. Common examples of these applications include:\n\nâ\x9d“ Question Answering over specific documents\n\nDocumentation\n\nEnd-to-end Example: Question Answering over Notion Database\n\nð\x9f¬ Chatbots\n\nDocumentation\n\nEnd-to-end Example: Chat-LangChain\n\nð\x9f¤\x96 Agents\n\nDocumentation\n\nEnd-to-end Example: GPT+WolframAlpha\n\nð\x9f“\x96 Documentation\n\nPlease see here for full documentation on:\n\nGetting started (installation, setting up the environment, simple examples)\n\nHow-To examples (demos, integrations, helper functions)\n\nReference (full API docs)\n\nResources (high-level explanation of core concepts)\n\nð\x9f\x9a\x80 What can this help with?\n\nThere are six main areas that LangChain is designed to help with.\nThese are, in increasing order of complexity:\n\nð\x9f“\x83 LLMs and Prompts:\n\nThis includes prompt management, prompt optimization, a generic interface for all LLMs, and common utilities for working with LLMs.\n\nð\x9f”\x97 Chains:\n\nChains go beyond a single LLM call and involve sequences of calls (whether to an LLM or a different utility). LangChain provides a standard interface for chains, lots of integrations with other tools, and end-to-end chains for common applications.\n\nð\x9f“\x9a Data Augmented Generation:\n\nData Augmented Generation involves specific types of chains that first interact with an external data source to fetch data for use in the generation step. Examples include summarization of long pieces of text and question/answering over specific data sources.\n\nð\x9f¤\x96 Agents:\n\nAgents involve an LLM making decisions about which Actions to take, taking that Action, seeing an Observation, and repeating that until done. LangChain provides a standard interface for agents, a selection of agents to choose from, and examples of end-to-end agents.\n\nð\x9f§\xa0 Memory:\n\nMemory refers to persisting state between calls of a chain/agent. LangChain provides a standard interface for memory, a collection of memory implementations, and examples of chains/agents that use memory.\n\nð\x9f§\x90 Evaluation:\n\n[BETA] Generative models are notoriously hard to evaluate with traditional metrics. One new way of evaluating them is using language models themselves to do the evaluation. LangChain provides some prompts/chains for assisting in this.\n\nFor more information on these concepts, please see our full documentation.\n\nð\x9f\x81 Contributing\n\nAs an open-source project in a rapidly developing field, we are extremely open to contributions, whether it be in the form of a new feature, improved infrastructure, or better documentation.\n\nFor detailed information on how to contribute, see here.", metadata={'source': '../../../../../README.md'})]
```
</CodeOutputBlock>
## Retain Elements
Under the hood, Unstructured creates different "elements" for different chunks of text. By default we combine those together, but you can easily keep that separation by specifying `mode="elements"`.
```python
loader = UnstructuredMarkdownLoader(markdown_path, mode="elements")
```
```python
data = loader.load()
```
```python
data[0]
```
<CodeOutputBlock lang="python">
```
Document(page_content='ð\x9f¦\x9cï¸\x8fð\x9f”\x97 LangChain', metadata={'source': '../../../../../README.md', 'page_number': 1, 'category': 'Title'})
```
</CodeOutputBlock>

File diff suppressed because one or more lines are too long

View File

@@ -30,6 +30,60 @@ That means there are two different axes along which you can customize your text
### Get started with text splitters
import GetStarted from "@snippets/modules/data_connection/document_transformers/get_started.mdx"
The default recommended text splitter is the RecursiveCharacterTextSplitter. This text splitter takes a list of characters. It tries to create chunks based on splitting on the first character, but if any chunks are too large it then moves onto the next character, and so forth. By default the characters it tries to split on are `["\n\n", "\n", " ", ""]`
<GetStarted/>
In addition to controlling which characters you can split on, you can also control a few other things:
- `length_function`: how the length of chunks is calculated. Defaults to just counting number of characters, but it's pretty common to pass a token counter here.
- `chunk_size`: the maximum size of your chunks (as measured by the length function).
- `chunk_overlap`: the maximum overlap between chunks. It can be nice to have some overlap to maintain some continuity between chunks (e.g. do a sliding window).
- `add_start_index`: whether to include the starting position of each chunk within the original document in the metadata.
```python
# This is a long document we can split up.
with open('../../state_of_the_union.txt') as f:
state_of_the_union = f.read()
```
```python
from langchain.text_splitter import RecursiveCharacterTextSplitter
```
```python
text_splitter = RecursiveCharacterTextSplitter(
# Set a really small chunk size, just to show.
chunk_size = 100,
chunk_overlap = 20,
length_function = len,
add_start_index = True,
)
```
```python
texts = text_splitter.create_documents([state_of_the_union])
print(texts[0])
print(texts[1])
```
<CodeOutputBlock lang="python">
```
page_content='Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and' metadata={'start_index': 0}
page_content='of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans.' metadata={'start_index': 82}
```
</CodeOutputBlock>
## Other transformations:
### Filter redundant docs, translate docs, extract metadata, and more
We can do perform a number of transformations on docs which are not simply splitting the text. With the
`EmbeddingsRedundantFilter` we can identify similar documents and filter out redundancies. With integrations like
[doctran](https://github.com/psychic-api/doctran/tree/main) we can do things like translate documents from one language
to another, extract desired properties and add them to metadata, and convert conversational dialogue into a Q/A format
set of documents.

View File

@@ -5,6 +5,64 @@ This is the simplest method. This splits based on characters (by default "\n\n")
1. How the text is split: by single character.
2. How the chunk size is measured: by number of characters.
import Example from "@snippets/modules/data_connection/document_transformers/text_splitters/character_text_splitter.mdx"
```python
# This is a long document we can split up.
with open('../../../state_of_the_union.txt') as f:
state_of_the_union = f.read()
```
<Example/>
```python
from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
separator = "\n\n",
chunk_size = 1000,
chunk_overlap = 200,
length_function = len,
is_separator_regex = False,
)
```
```python
texts = text_splitter.create_documents([state_of_the_union])
print(texts[0])
```
<CodeOutputBlock lang="python">
```
page_content='Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans. \n\nLast year COVID-19 kept us apart. This year we are finally together again. \n\nTonight, we meet as Democrats Republicans and Independents. But most importantly as Americans. \n\nWith a duty to one another to the American people to the Constitution. \n\nAnd with an unwavering resolve that freedom will always triumph over tyranny. \n\nSix days ago, Russias Vladimir Putin sought to shake the foundations of the free world thinking he could make it bend to his menacing ways. But he badly miscalculated. \n\nHe thought he could roll into Ukraine and the world would roll over. Instead he met a wall of strength he never imagined. \n\nHe met the Ukrainian people. \n\nFrom President Zelenskyy to every Ukrainian, their fearlessness, their courage, their determination, inspires the world.' lookup_str='' metadata={} lookup_index=0
```
</CodeOutputBlock>
Here's an example of passing metadata along with the documents, notice that it is split along with the documents.
```python
metadatas = [{"document": 1}, {"document": 2}]
documents = text_splitter.create_documents([state_of_the_union, state_of_the_union], metadatas=metadatas)
print(documents[0])
```
<CodeOutputBlock lang="python">
```
page_content='Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans. \n\nLast year COVID-19 kept us apart. This year we are finally together again. \n\nTonight, we meet as Democrats Republicans and Independents. But most importantly as Americans. \n\nWith a duty to one another to the American people to the Constitution. \n\nAnd with an unwavering resolve that freedom will always triumph over tyranny. \n\nSix days ago, Russias Vladimir Putin sought to shake the foundations of the free world thinking he could make it bend to his menacing ways. But he badly miscalculated. \n\nHe thought he could roll into Ukraine and the world would roll over. Instead he met a wall of strength he never imagined. \n\nHe met the Ukrainian people. \n\nFrom President Zelenskyy to every Ukrainian, their fearlessness, their courage, their determination, inspires the world.' lookup_str='' metadata={'document': 1} lookup_index=0
```
</CodeOutputBlock>
```python
text_splitter.split_text(state_of_the_union)[0]
```
<CodeOutputBlock lang="python">
```
'Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans. \n\nLast year COVID-19 kept us apart. This year we are finally together again. \n\nTonight, we meet as Democrats Republicans and Independents. But most importantly as Americans. \n\nWith a duty to one another to the American people to the Constitution. \n\nAnd with an unwavering resolve that freedom will always triumph over tyranny. \n\nSix days ago, Russias Vladimir Putin sought to shake the foundations of the free world thinking he could make it bend to his menacing ways. But he badly miscalculated. \n\nHe thought he could roll into Ukraine and the world would roll over. Instead he met a wall of strength he never imagined. \n\nHe met the Ukrainian people. \n\nFrom President Zelenskyy to every Ukrainian, their fearlessness, their courage, their determination, inspires the world.'
```
</CodeOutputBlock>

View File

@@ -2,6 +2,417 @@
CodeTextSplitter allows you to split your code with multiple languages supported. Import enum `Language` and specify the language.
import Example from "@snippets/modules/data_connection/document_transformers/text_splitters/code_splitter.mdx"
```python
from langchain.text_splitter import (
RecursiveCharacterTextSplitter,
Language,
)
```
<Example/>
```python
# Full list of support languages
[e.value for e in Language]
```
<CodeOutputBlock lang="python">
```
['cpp',
'go',
'java',
'kotlin',
'js',
'ts',
'php',
'proto',
'python',
'rst',
'ruby',
'rust',
'scala',
'swift',
'markdown',
'latex',
'html',
'sol',
'csharp']
```
</CodeOutputBlock>
```python
# You can also see the separators used for a given language
RecursiveCharacterTextSplitter.get_separators_for_language(Language.PYTHON)
```
<CodeOutputBlock lang="python">
```
['\nclass ', '\ndef ', '\n\tdef ', '\n\n', '\n', ' ', '']
```
</CodeOutputBlock>
## Python
Here's an example using the PythonTextSplitter:
```python
PYTHON_CODE = """
def hello_world():
print("Hello, World!")
# Call the function
hello_world()
"""
python_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON, chunk_size=50, chunk_overlap=0
)
python_docs = python_splitter.create_documents([PYTHON_CODE])
python_docs
```
<CodeOutputBlock lang="python">
```
[Document(page_content='def hello_world():\n print("Hello, World!")', metadata={}),
Document(page_content='# Call the function\nhello_world()', metadata={})]
```
</CodeOutputBlock>
## JS
Here's an example using the JS text splitter:
```python
JS_CODE = """
function helloWorld() {
console.log("Hello, World!");
}
// Call the function
helloWorld();
"""
js_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.JS, chunk_size=60, chunk_overlap=0
)
js_docs = js_splitter.create_documents([JS_CODE])
js_docs
```
<CodeOutputBlock lang="python">
```
[Document(page_content='function helloWorld() {\n console.log("Hello, World!");\n}', metadata={}),
Document(page_content='// Call the function\nhelloWorld();', metadata={})]
```
</CodeOutputBlock>
## TS
Here's an example using the TS text splitter:
```python
TS_CODE = """
function helloWorld(): void {
console.log("Hello, World!");
}
// Call the function
helloWorld();
"""
ts_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.TS, chunk_size=60, chunk_overlap=0
)
ts_docs = ts_splitter.create_documents([TS_CODE])
ts_docs
```
<CodeOutputBlock lang="python">
```
[Document(page_content='function helloWorld(): void {\n console.log("Hello, World!");\n}', metadata={}),
Document(page_content='// Call the function\nhelloWorld();', metadata={})]
```
</CodeOutputBlock>
## Markdown
Here's an example using the Markdown text splitter:
````python
markdown_text = """
# 🦜️🔗 LangChain
⚡ Building applications with LLMs through composability ⚡
## Quick Install
```bash
# Hopefully this code block isn't split
pip install langchain
```
As an open-source project in a rapidly developing field, we are extremely open to contributions.
"""
````
```python
md_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.MARKDOWN, chunk_size=60, chunk_overlap=0
)
md_docs = md_splitter.create_documents([markdown_text])
md_docs
```
<CodeOutputBlock lang="python">
```
[Document(page_content='# 🦜️🔗 LangChain', metadata={}),
Document(page_content='⚡ Building applications with LLMs through composability ⚡', metadata={}),
Document(page_content='## Quick Install', metadata={}),
Document(page_content="```bash\n# Hopefully this code block isn't split", metadata={}),
Document(page_content='pip install langchain', metadata={}),
Document(page_content='```', metadata={}),
Document(page_content='As an open-source project in a rapidly developing field, we', metadata={}),
Document(page_content='are extremely open to contributions.', metadata={})]
```
</CodeOutputBlock>
## Latex
Here's an example on Latex text:
```python
latex_text = """
\documentclass{article}
\begin{document}
\maketitle
\section{Introduction}
Large language models (LLMs) are a type of machine learning model that can be trained on vast amounts of text data to generate human-like language. In recent years, LLMs have made significant advances in a variety of natural language processing tasks, including language translation, text generation, and sentiment analysis.
\subsection{History of LLMs}
The earliest LLMs were developed in the 1980s and 1990s, but they were limited by the amount of data that could be processed and the computational power available at the time. In the past decade, however, advances in hardware and software have made it possible to train LLMs on massive datasets, leading to significant improvements in performance.
\subsection{Applications of LLMs}
LLMs have many applications in industry, including chatbots, content creation, and virtual assistants. They can also be used in academia for research in linguistics, psychology, and computational linguistics.
\end{document}
"""
```
```python
latex_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.MARKDOWN, chunk_size=60, chunk_overlap=0
)
latex_docs = latex_splitter.create_documents([latex_text])
latex_docs
```
<CodeOutputBlock lang="python">
```
[Document(page_content='\\documentclass{article}\n\n\x08egin{document}\n\n\\maketitle', metadata={}),
Document(page_content='\\section{Introduction}', metadata={}),
Document(page_content='Large language models (LLMs) are a type of machine learning', metadata={}),
Document(page_content='model that can be trained on vast amounts of text data to', metadata={}),
Document(page_content='generate human-like language. In recent years, LLMs have', metadata={}),
Document(page_content='made significant advances in a variety of natural language', metadata={}),
Document(page_content='processing tasks, including language translation, text', metadata={}),
Document(page_content='generation, and sentiment analysis.', metadata={}),
Document(page_content='\\subsection{History of LLMs}', metadata={}),
Document(page_content='The earliest LLMs were developed in the 1980s and 1990s,', metadata={}),
Document(page_content='but they were limited by the amount of data that could be', metadata={}),
Document(page_content='processed and the computational power available at the', metadata={}),
Document(page_content='time. In the past decade, however, advances in hardware and', metadata={}),
Document(page_content='software have made it possible to train LLMs on massive', metadata={}),
Document(page_content='datasets, leading to significant improvements in', metadata={}),
Document(page_content='performance.', metadata={}),
Document(page_content='\\subsection{Applications of LLMs}', metadata={}),
Document(page_content='LLMs have many applications in industry, including', metadata={}),
Document(page_content='chatbots, content creation, and virtual assistants. They', metadata={}),
Document(page_content='can also be used in academia for research in linguistics,', metadata={}),
Document(page_content='psychology, and computational linguistics.', metadata={}),
Document(page_content='\\end{document}', metadata={})]
```
</CodeOutputBlock>
## HTML
Here's an example using an HTML text splitter:
```python
html_text = """
<!DOCTYPE html>
<html>
<head>
<title>🦜️🔗 LangChain</title>
<style>
body {
font-family: Arial, sans-serif;
}
h1 {
color: darkblue;
}
</style>
</head>
<body>
<div>
<h1>🦜️🔗 LangChain</h1>
<p>⚡ Building applications with LLMs through composability ⚡</p>
</div>
<div>
As an open-source project in a rapidly developing field, we are extremely open to contributions.
</div>
</body>
</html>
"""
```
```python
html_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.HTML, chunk_size=60, chunk_overlap=0
)
html_docs = html_splitter.create_documents([html_text])
html_docs
```
<CodeOutputBlock lang="python">
```
[Document(page_content='<!DOCTYPE html>\n<html>', metadata={}),
Document(page_content='<head>\n <title>🦜️🔗 LangChain</title>', metadata={}),
Document(page_content='<style>\n body {\n font-family: Aria', metadata={}),
Document(page_content='l, sans-serif;\n }\n h1 {', metadata={}),
Document(page_content='color: darkblue;\n }\n </style>\n </head', metadata={}),
Document(page_content='>', metadata={}),
Document(page_content='<body>', metadata={}),
Document(page_content='<div>\n <h1>🦜️🔗 LangChain</h1>', metadata={}),
Document(page_content='<p>⚡ Building applications with LLMs through composability ⚡', metadata={}),
Document(page_content='</p>\n </div>', metadata={}),
Document(page_content='<div>\n As an open-source project in a rapidly dev', metadata={}),
Document(page_content='eloping field, we are extremely open to contributions.', metadata={}),
Document(page_content='</div>\n </body>\n</html>', metadata={})]
```
</CodeOutputBlock>
## Solidity
Here's an example using the Solidity text splitter:
```python
SOL_CODE = """
pragma solidity ^0.8.20;
contract HelloWorld {
function add(uint a, uint b) pure public returns(uint) {
return a + b;
}
}
"""
sol_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.SOL, chunk_size=128, chunk_overlap=0
)
sol_docs = sol_splitter.create_documents([SOL_CODE])
sol_docs
```
<CodeOutputBlock>
```
[
Document(page_content='pragma solidity ^0.8.20;', metadata={}),
Document(page_content='contract HelloWorld {\n function add(uint a, uint b) pure public returns(uint) {\n return a + b;\n }\n}', metadata={})
]
```
</CodeOutputBlock>
## C#
Here's an example using the C# text splitter:
```csharp
using System;
class Program
{
static void Main()
{
int age = 30; // Change the age value as needed
// Categorize the age without any console output
if (age < 18)
{
// Age is under 18
}
else if (age >= 18 && age < 65)
{
// Age is an adult
}
else
{
// Age is a senior citizen
}
}
}
```
<CodeOutputBlock lang="python">
```
[Document(page_content='using System;', metadata={}),
Document(page_content='class Program\n{', metadata={}),
Document(page_content='static void', metadata={}),
Document(page_content='Main()', metadata={}),
Document(page_content='{', metadata={}),
Document(page_content='int age', metadata={}),
Document(page_content='= 30; // Change', metadata={}),
Document(page_content='the age value', metadata={}),
Document(page_content='as needed', metadata={}),
Document(page_content='//', metadata={}),
Document(page_content='Categorize the', metadata={}),
Document(page_content='age without any', metadata={}),
Document(page_content='console output', metadata={}),
Document(page_content='if (age', metadata={}),
Document(page_content='< 18)', metadata={}),
Document(page_content='{', metadata={}),
Document(page_content='//', metadata={}),
Document(page_content='Age is under 18', metadata={}),
Document(page_content='}', metadata={}),
Document(page_content='else if', metadata={}),
Document(page_content='(age >= 18 &&', metadata={}),
Document(page_content='age < 65)', metadata={}),
Document(page_content='{', metadata={}),
Document(page_content='//', metadata={}),
Document(page_content='Age is an adult', metadata={}),
Document(page_content='}', metadata={}),
Document(page_content='else', metadata={}),
Document(page_content='{', metadata={}),
Document(page_content='//', metadata={}),
Document(page_content='Age is a senior', metadata={}),
Document(page_content='citizen', metadata={}),
Document(page_content='}\n }', metadata={}),
Document(page_content='}', metadata={})]
```
</CodeOutputBlock>

View File

@@ -5,6 +5,54 @@ This text splitter is the recommended one for generic text. It is parameterized
1. How the text is split: by list of characters.
2. How the chunk size is measured: by number of characters.
import Example from "@snippets/modules/data_connection/document_transformers/text_splitters/recursive_text_splitter.mdx"
```python
# This is a long document we can split up.
with open('../../../state_of_the_union.txt') as f:
state_of_the_union = f.read()
```
<Example/>
```python
from langchain.text_splitter import RecursiveCharacterTextSplitter
```
```python
text_splitter = RecursiveCharacterTextSplitter(
# Set a really small chunk size, just to show.
chunk_size = 100,
chunk_overlap = 20,
length_function = len,
is_separator_regex = False,
)
```
```python
texts = text_splitter.create_documents([state_of_the_union])
print(texts[0])
print(texts[1])
```
<CodeOutputBlock lang="python">
```
page_content='Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and' lookup_str='' metadata={} lookup_index=0
page_content='of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans.' lookup_str='' metadata={} lookup_index=0
```
</CodeOutputBlock>
```python
text_splitter.split_text(state_of_the_union)[:2]
```
<CodeOutputBlock lang="python">
```
['Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and',
'of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans.']
```
</CodeOutputBlock>

View File

@@ -59,6 +59,8 @@
"2. Only works with LangChain `vectorstore`'s that support:\n",
" * document addition by id (`add_documents` method with `ids` argument)\n",
" * delete by id (`delete` method with)\n",
"\n",
"Compatible Vectorstores: `AnalyticDB`, `AwaDB`, `Bagel`, `Cassandra`, `Chroma`, `DashVector`, `DeepLake`, `Dingo`, `ElasticVectorSearch`, `ElasticsearchStore`, `FAISS`, `PGVector`, `Pinecone`, `Qdrant`, `Redis`, `ScaNN`, `SupabaseVectorStore`, `TimescaleVector`, `Vald`, `Vearch`, `VespaStore`, `Weaviate`, `ZepVectorStore`.\n",
" \n",
"## Caution\n",
"\n",

View File

@@ -14,6 +14,264 @@ The Contextual Compression Retriever passes queries to the base retriever, takes
## Get started
import Example from "@snippets/modules/data_connection/retrievers/contextual_compression/get_started.mdx"
```python
# Helper function for printing docs
<Example/>
def pretty_print_docs(docs):
print(f"\n{'-' * 100}\n".join([f"Document {i+1}:\n\n" + d.page_content for i, d in enumerate(docs)]))
```
## Using a vanilla vector store retriever
Let's start by initializing a simple vector store retriever and storing the 2023 State of the Union speech (in chunks). We can see that given an example question our retriever returns one or two relevant docs and a few irrelevant docs. And even the relevant docs have a lot of irrelevant information in them.
```python
from langchain.text_splitter import CharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.document_loaders import TextLoader
from langchain.vectorstores import FAISS
documents = TextLoader('../../../state_of_the_union.txt').load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)
retriever = FAISS.from_documents(texts, OpenAIEmbeddings()).as_retriever()
docs = retriever.get_relevant_documents("What did the president say about Ketanji Brown Jackson")
pretty_print_docs(docs)
```
<CodeOutputBlock lang="python">
```
Document 1:
Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while youre at it, pass the Disclose Act so Americans can know who is funding our elections.
Tonight, Id like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.
One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nations top legal minds, who will continue Justice Breyers legacy of excellence.
----------------------------------------------------------------------------------------------------
Document 2:
A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since shes been nominated, shes received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.
And if we are to advance liberty and justice, we need to secure the Border and fix the immigration system.
We can do both. At our border, weve installed new technology like cutting-edge scanners to better detect drug smuggling.
Weve set up joint patrols with Mexico and Guatemala to catch more human traffickers.
Were putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster.
Were securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders.
----------------------------------------------------------------------------------------------------
Document 3:
And for our LGBTQ+ Americans, lets finally get the bipartisan Equality Act to my desk. The onslaught of state laws targeting transgender Americans and their families is wrong.
As I said last year, especially to our younger transgender Americans, I will always have your back as your President, so you can be yourself and reach your God-given potential.
While it often appears that we never agree, that isnt true. I signed 80 bipartisan bills into law last year. From preventing government shutdowns to protecting Asian-Americans from still-too-common hate crimes to reforming military justice.
And soon, well strengthen the Violence Against Women Act that I first wrote three decades ago. It is important for us to show the nation that we can come together and do big things.
So tonight Im offering a Unity Agenda for the Nation. Four big things we can do together.
First, beat the opioid epidemic.
----------------------------------------------------------------------------------------------------
Document 4:
Tonight, Im announcing a crackdown on these companies overcharging American businesses and consumers.
And as Wall Street firms take over more nursing homes, quality in those homes has gone down and costs have gone up.
That ends on my watch.
Medicare is going to set higher standards for nursing homes and make sure your loved ones get the care they deserve and expect.
Well also cut costs and keep the economy going strong by giving workers a fair shot, provide more training and apprenticeships, hire them based on their skills not degrees.
Lets pass the Paycheck Fairness Act and paid leave.
Raise the minimum wage to $15 an hour and extend the Child Tax Credit, so no one has to raise a family in poverty.
Lets increase Pell Grants and increase our historic support of HBCUs, and invest in what Jill—our First Lady who teaches full-time—calls Americas best-kept secret: community colleges.
```
</CodeOutputBlock>
## Adding contextual compression with an `LLMChainExtractor`
Now let's wrap our base retriever with a `ContextualCompressionRetriever`. We'll add an `LLMChainExtractor`, which will iterate over the initially returned documents and extract from each only the content that is relevant to the query.
```python
from langchain.llms import OpenAI
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
llm = OpenAI(temperature=0)
compressor = LLMChainExtractor.from_llm(llm)
compression_retriever = ContextualCompressionRetriever(base_compressor=compressor, base_retriever=retriever)
compressed_docs = compression_retriever.get_relevant_documents("What did the president say about Ketanji Jackson Brown")
pretty_print_docs(compressed_docs)
```
<CodeOutputBlock lang="python">
```
Document 1:
"One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nations top legal minds, who will continue Justice Breyers legacy of excellence."
----------------------------------------------------------------------------------------------------
Document 2:
"A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since shes been nominated, shes received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans."
```
</CodeOutputBlock>
## More built-in compressors: filters
### `LLMChainFilter`
The `LLMChainFilter` is slightly simpler but more robust compressor that uses an LLM chain to decide which of the initially retrieved documents to filter out and which ones to return, without manipulating the document contents.
```python
from langchain.retrievers.document_compressors import LLMChainFilter
_filter = LLMChainFilter.from_llm(llm)
compression_retriever = ContextualCompressionRetriever(base_compressor=_filter, base_retriever=retriever)
compressed_docs = compression_retriever.get_relevant_documents("What did the president say about Ketanji Jackson Brown")
pretty_print_docs(compressed_docs)
```
<CodeOutputBlock lang="python">
```
Document 1:
Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while youre at it, pass the Disclose Act so Americans can know who is funding our elections.
Tonight, Id like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.
One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nations top legal minds, who will continue Justice Breyers legacy of excellence.
```
</CodeOutputBlock>
### `EmbeddingsFilter`
Making an extra LLM call over each retrieved document is expensive and slow. The `EmbeddingsFilter` provides a cheaper and faster option by embedding the documents and query and only returning those documents which have sufficiently similar embeddings to the query.
```python
from langchain.embeddings import OpenAIEmbeddings
from langchain.retrievers.document_compressors import EmbeddingsFilter
embeddings = OpenAIEmbeddings()
embeddings_filter = EmbeddingsFilter(embeddings=embeddings, similarity_threshold=0.76)
compression_retriever = ContextualCompressionRetriever(base_compressor=embeddings_filter, base_retriever=retriever)
compressed_docs = compression_retriever.get_relevant_documents("What did the president say about Ketanji Jackson Brown")
pretty_print_docs(compressed_docs)
```
<CodeOutputBlock lang="python">
```
Document 1:
Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while youre at it, pass the Disclose Act so Americans can know who is funding our elections.
Tonight, Id like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.
One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nations top legal minds, who will continue Justice Breyers legacy of excellence.
----------------------------------------------------------------------------------------------------
Document 2:
A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since shes been nominated, shes received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.
And if we are to advance liberty and justice, we need to secure the Border and fix the immigration system.
We can do both. At our border, weve installed new technology like cutting-edge scanners to better detect drug smuggling.
Weve set up joint patrols with Mexico and Guatemala to catch more human traffickers.
Were putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster.
Were securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders.
----------------------------------------------------------------------------------------------------
Document 3:
And for our LGBTQ+ Americans, lets finally get the bipartisan Equality Act to my desk. The onslaught of state laws targeting transgender Americans and their families is wrong.
As I said last year, especially to our younger transgender Americans, I will always have your back as your President, so you can be yourself and reach your God-given potential.
While it often appears that we never agree, that isnt true. I signed 80 bipartisan bills into law last year. From preventing government shutdowns to protecting Asian-Americans from still-too-common hate crimes to reforming military justice.
And soon, well strengthen the Violence Against Women Act that I first wrote three decades ago. It is important for us to show the nation that we can come together and do big things.
So tonight Im offering a Unity Agenda for the Nation. Four big things we can do together.
First, beat the opioid epidemic.
```
</CodeOutputBlock>
# Stringing compressors and document transformers together
Using the `DocumentCompressorPipeline` we can also easily combine multiple compressors in sequence. Along with compressors we can add `BaseDocumentTransformer`s to our pipeline, which don't perform any contextual compression but simply perform some transformation on a set of documents. For example `TextSplitter`s can be used as document transformers to split documents into smaller pieces, and the `EmbeddingsRedundantFilter` can be used to filter out redundant documents based on embedding similarity between documents.
Below we create a compressor pipeline by first splitting our docs into smaller chunks, then removing redundant documents, and then filtering based on relevance to the query.
```python
from langchain.document_transformers import EmbeddingsRedundantFilter
from langchain.retrievers.document_compressors import DocumentCompressorPipeline
from langchain.text_splitter import CharacterTextSplitter
splitter = CharacterTextSplitter(chunk_size=300, chunk_overlap=0, separator=". ")
redundant_filter = EmbeddingsRedundantFilter(embeddings=embeddings)
relevant_filter = EmbeddingsFilter(embeddings=embeddings, similarity_threshold=0.76)
pipeline_compressor = DocumentCompressorPipeline(
transformers=[splitter, redundant_filter, relevant_filter]
)
```
```python
compression_retriever = ContextualCompressionRetriever(base_compressor=pipeline_compressor, base_retriever=retriever)
compressed_docs = compression_retriever.get_relevant_documents("What did the president say about Ketanji Jackson Brown")
pretty_print_docs(compressed_docs)
```
<CodeOutputBlock lang="python">
```
Document 1:
One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson
----------------------------------------------------------------------------------------------------
Document 2:
As I said last year, especially to our younger transgender Americans, I will always have your back as your President, so you can be yourself and reach your God-given potential.
While it often appears that we never agree, that isnt true. I signed 80 bipartisan bills into law last year
----------------------------------------------------------------------------------------------------
Document 3:
A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder
```
</CodeOutputBlock>

View File

@@ -13,7 +13,274 @@ as the backbone of a retriever, but there are other types of retrievers as well.
## Get started
import GetStarted from "@snippets/modules/data_connection/retrievers/get_started.mdx"
The public API of the `BaseRetriever` class in LangChain is as follows:
<GetStarted/>
```python
from abc import ABC, abstractmethod
from typing import Any, List
from langchain.schema import Document
from langchain.callbacks.manager import Callbacks
class BaseRetriever(ABC):
...
def get_relevant_documents(
self, query: str, *, callbacks: Callbacks = None, **kwargs: Any
) -> List[Document]:
"""Retrieve documents relevant to a query.
Args:
query: string to find relevant documents for
callbacks: Callback manager or list of callbacks
Returns:
List of relevant documents
"""
...
async def aget_relevant_documents(
self, query: str, *, callbacks: Callbacks = None, **kwargs: Any
) -> List[Document]:
"""Asynchronously get documents relevant to a query.
Args:
query: string to find relevant documents for
callbacks: Callback manager or list of callbacks
Returns:
List of relevant documents
"""
...
```
It's that simple! You can call `get_relevant_documents` or the async `aget_relevant_documents` methods to retrieve documents relevant to a query, where "relevance" is defined by
the specific retriever object you are calling.
Of course, we also help construct what we think useful retrievers are. The main type of retriever that we focus on is a vector store retriever. We will focus on that for the rest of this guide.
In order to understand what a vector store retriever is, it's important to understand what a vector store is. So let's look at that.
By default, LangChain uses [Chroma](/docs/ecosystem/integrations/chroma.html) as the vector store to index and search embeddings. To walk through this tutorial, we'll first need to install `chromadb`.
```
pip install chromadb
```
This example showcases question answering over documents.
We have chosen this as the example for getting started because it nicely combines a lot of different elements (Text splitters, embeddings, vector stores) and then also shows how to use them in a chain.
Question answering over documents consists of four steps:
1. Create an index
2. Create a retriever from that index
3. Create a question answering chain
4. Ask questions!
Each of the steps has multiple substeps and potential configurations. In this notebook we will primarily focus on (1). We will start by showing the one-liner for doing so, but then break down what is actually going on.
First, let's import some common classes we'll use no matter what.
```python
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
```
Next in the generic setup, let's specify the document loader we want to use. You can download the `state_of_the_union.txt` file [here](https://github.com/langchain-ai/langchain/blob/master/docs/extras/modules/state_of_the_union.txt).
```python
from langchain.document_loaders import TextLoader
loader = TextLoader('../state_of_the_union.txt', encoding='utf8')
```
## One Line Index Creation
To get started as quickly as possible, we can use the `VectorstoreIndexCreator`.
```python
from langchain.indexes import VectorstoreIndexCreator
```
```python
index = VectorstoreIndexCreator().from_loaders([loader])
```
<CodeOutputBlock lang="python">
```
Running Chroma using direct local API.
Using DuckDB in-memory for database. Data will be transient.
```
</CodeOutputBlock>
Now that the index is created, we can use it to ask questions of the data! Note that under the hood this is actually doing a few steps as well, which we will cover later in this guide.
```python
query = "What did the president say about Ketanji Brown Jackson?"
index.query(query)
```
<CodeOutputBlock lang="python">
```
" The president said that Ketanji Brown Jackson is one of the nation's top legal minds, a former top litigator in private practice, a former federal public defender, and from a family of public school educators and police officers. He also said that she is a consensus builder and has received a broad range of support from the Fraternal Order of Police to former judges appointed by Democrats and Republicans."
```
</CodeOutputBlock>
```python
query = "What did the president say about Ketanji Brown Jackson?"
index.query_with_sources(query)
```
<CodeOutputBlock lang="python">
```
{'question': 'What did the president say about Ketanji Brown Jackson?',
'answer': " The president said that he nominated Circuit Court of Appeals Judge Ketanji Brown Jackson, one of the nation's top legal minds, to continue Justice Breyer's legacy of excellence, and that she has received a broad range of support from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.\n",
'sources': '../state_of_the_union.txt'}
```
</CodeOutputBlock>
What is returned from the `VectorstoreIndexCreator` is a `VectorStoreIndexWrapper`, which provides these nice `query` and `query_with_sources` functionalities. If we just want to access the vector store directly, we can also do that.
```python
index.vectorstore
```
<CodeOutputBlock lang="python">
```
<langchain.vectorstores.chroma.Chroma at 0x119aa5940>
```
</CodeOutputBlock>
If we then want to access the `VectorStoreRetriever`, we can do that with:
```python
index.vectorstore.as_retriever()
```
<CodeOutputBlock lang="python">
```
VectorStoreRetriever(vectorstore=<langchain.vectorstores.chroma.Chroma object at 0x119aa5940>, search_kwargs={})
```
</CodeOutputBlock>
It can also be convenient to filter the vector store by the metadata associated with documents, particularly when your vector store has multiple sources. This can be done using the `query` method, like this:
```python
index.query("Summarize the general content of this document.", retriever_kwargs={"search_kwargs": {"filter": {"source": "../state_of_the_union.txt"}}})
```
<CodeOutputBlock lang="python">
```
" The document is a speech given by President Trump to the nation on the occasion of his 245th birthday. The speech highlights the importance of American values and the challenges facing the country, including the ongoing conflict in Ukraine, the ongoing trade war with China, and the ongoing conflict in Syria. The speech also discusses the importance of investing in emerging technologies and American manufacturing, and calls on Congress to pass the Bipartisan Innovation Act and other important legislation."
```
</CodeOutputBlock>
## Walkthrough
Okay, so what's actually going on? How is this index getting created?
A lot of the magic is being hid in this `VectorstoreIndexCreator`. What is this doing?
There are three main steps going on after the documents are loaded:
1. Splitting documents into chunks
2. Creating embeddings for each document
3. Storing documents and embeddings in a vector store
Let's walk through this in code
```python
documents = loader.load()
```
Next, we will split the documents into chunks.
```python
from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)
```
We will then select which embeddings we want to use.
```python
from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
```
We now create the vector store to use as the index.
```python
from langchain.vectorstores import Chroma
db = Chroma.from_documents(texts, embeddings)
```
<CodeOutputBlock lang="python">
```
Running Chroma using direct local API.
Using DuckDB in-memory for database. Data will be transient.
```
</CodeOutputBlock>
So that's creating the index. Then, we expose this index in a retriever interface.
```python
retriever = db.as_retriever()
```
Then, as before, we create a chain and use it to answer questions!
```python
qa = RetrievalQA.from_chain_type(llm=OpenAI(), chain_type="stuff", retriever=retriever)
```
```python
query = "What did the president say about Ketanji Brown Jackson?"
qa.run(query)
```
<CodeOutputBlock lang="python">
```
" The President said that Judge Ketanji Brown Jackson is one of the nation's top legal minds, a former top litigator in private practice, a former federal public defender, and from a family of public school educators and police officers. He said she is a consensus builder and has received a broad range of support from organizations such as the Fraternal Order of Police and former judges appointed by Democrats and Republicans."
```
</CodeOutputBlock>
`VectorstoreIndexCreator` is just a wrapper around all this logic. It is configurable in the text splitter it uses, the embeddings it uses, and the vectorstore it uses. For example, you can configure it as below:
```python
index_creator = VectorstoreIndexCreator(
vectorstore_cls=Chroma,
embedding=OpenAIEmbeddings(),
text_splitter=CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
)
```
Hopefully this highlights what is going on under the hood of `VectorstoreIndexCreator`. While we think it's important to have a simple way to create indexes, we also think it's important to understand what's going on under the hood.

View File

@@ -1,5 +1,15 @@
# Self-querying
:::info
Head to [Integrations](/docs/integrations/retrievers/self_query) for documentation on vector stores with built-in support for self-querying.
:::
A self-querying retriever is one that, as the name suggests, has the ability to query itself. Specifically, given any natural language query, the retriever uses a query-constructing LLM chain to write a structured query and then applies that structured query to its underlying VectorStore. This allows the retriever to not only use the user-input query for semantic similarity comparison with the contents of stored documents but to also extract filters from the user query on the metadata of stored documents and to execute those filters.
![](https://drive.google.com/uc?id=1OQUN-0MJcDUxmPXofgS7MqReEs720pqS)
## Get started
We'll use a Pinecone vector store in this example.
For demonstration purposes we'll use a `Pinecone` vector store.
First we'll want to create a `Pinecone` vector store and seed it with some data. We've created a small demo set of documents that contain summaries of movies.
@@ -60,18 +70,18 @@ from langchain.chains.query_constructor.base import AttributeInfo
metadata_field_info=[
AttributeInfo(
name="genre",
description="The genre of the movie",
type="string or list[string]",
description="The genre of the movie",
type="string or list[string]",
),
AttributeInfo(
name="year",
description="The year the movie was released",
type="integer",
description="The year the movie was released",
type="integer",
),
AttributeInfo(
name="director",
description="The name of the movie director",
type="string",
description="The name of the movie director",
type="string",
),
AttributeInfo(
name="rating",
@@ -185,10 +195,10 @@ We can do this by passing `enable_limit=True` to the constructor.
```python
retriever = SelfQueryRetriever.from_llm(
llm,
vectorstore,
document_content_description,
metadata_field_info,
llm,
vectorstore,
document_content_description,
metadata_field_info,
enable_limit=True,
verbose=True
)
@@ -199,3 +209,4 @@ retriever = SelfQueryRetriever.from_llm(
# This example only specifies a relevant query
retriever.get_relevant_documents("What are two movies about dinosaurs")
```

View File

@@ -1,9 +0,0 @@
# Self-querying
A self-querying retriever is one that, as the name suggests, has the ability to query itself. Specifically, given any natural language query, the retriever uses a query-constructing LLM chain to write a structured query and then applies that structured query to its underlying VectorStore. This allows the retriever to not only use the user-input query for semantic similarity comparison with the contents of stored documents but to also extract filters from the user query on the metadata of stored documents and to execute those filters.
![](https://drive.google.com/uc?id=1OQUN-0MJcDUxmPXofgS7MqReEs720pqS)
import Example from "@snippets/modules/data_connection/retrievers/self_query/get_started.mdx"
<Example/>

View File

@@ -10,6 +10,127 @@ semantic_similarity + (1.0 - decay_rate) ^ hours_passed
Notably, `hours_passed` refers to the hours passed since the object in the retriever **was last accessed**, not since it was created. This means that frequently accessed objects remain "fresh".
import Example from "@snippets/modules/data_connection/retrievers/how_to/time_weighted_vectorstore.mdx"
```python
import faiss
<Example/>
from datetime import datetime, timedelta
from langchain.docstore import InMemoryDocstore
from langchain.embeddings import OpenAIEmbeddings
from langchain.retrievers import TimeWeightedVectorStoreRetriever
from langchain.schema import Document
from langchain.vectorstores import FAISS
```
## Low decay rate
A low `decay rate` (in this, to be extreme, we will set it close to 0) means memories will be "remembered" for longer. A `decay rate` of 0 means memories never be forgotten, making this retriever equivalent to the vector lookup.
```python
# Define your embedding model
embeddings_model = OpenAIEmbeddings()
# Initialize the vectorstore as empty
embedding_size = 1536
index = faiss.IndexFlatL2(embedding_size)
vectorstore = FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {})
retriever = TimeWeightedVectorStoreRetriever(vectorstore=vectorstore, decay_rate=.0000000000000000000000001, k=1)
```
```python
yesterday = datetime.now() - timedelta(days=1)
retriever.add_documents([Document(page_content="hello world", metadata={"last_accessed_at": yesterday})])
retriever.add_documents([Document(page_content="hello foo")])
```
<CodeOutputBlock lang="python">
```
['d7f85756-2371-4bdf-9140-052780a0f9b3']
```
</CodeOutputBlock>
```python
# "Hello World" is returned first because it is most salient, and the decay rate is close to 0., meaning it's still recent enough
retriever.get_relevant_documents("hello world")
```
<CodeOutputBlock lang="python">
```
[Document(page_content='hello world', metadata={'last_accessed_at': datetime.datetime(2023, 5, 13, 21, 0, 27, 678341), 'created_at': datetime.datetime(2023, 5, 13, 21, 0, 27, 279596), 'buffer_idx': 0})]
```
</CodeOutputBlock>
## High decay rate
With a high `decay rate` (e.g., several 9's), the `recency score` quickly goes to 0! If you set this all the way to 1, `recency` is 0 for all objects, once again making this equivalent to a vector lookup.
```python
# Define your embedding model
embeddings_model = OpenAIEmbeddings()
# Initialize the vectorstore as empty
embedding_size = 1536
index = faiss.IndexFlatL2(embedding_size)
vectorstore = FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {})
retriever = TimeWeightedVectorStoreRetriever(vectorstore=vectorstore, decay_rate=.999, k=1)
```
```python
yesterday = datetime.now() - timedelta(days=1)
retriever.add_documents([Document(page_content="hello world", metadata={"last_accessed_at": yesterday})])
retriever.add_documents([Document(page_content="hello foo")])
```
<CodeOutputBlock lang="python">
```
['40011466-5bbe-4101-bfd1-e22e7f505de2']
```
</CodeOutputBlock>
```python
# "Hello Foo" is returned first because "hello world" is mostly forgotten
retriever.get_relevant_documents("hello world")
```
<CodeOutputBlock lang="python">
```
[Document(page_content='hello foo', metadata={'last_accessed_at': datetime.datetime(2023, 4, 16, 22, 9, 2, 494798), 'created_at': datetime.datetime(2023, 4, 16, 22, 9, 2, 178722), 'buffer_idx': 1})]
```
</CodeOutputBlock>
## Virtual time
Using some utils in LangChain, you can mock out the time component.
```python
from langchain.utils import mock_now
import datetime
```
```python
# Notice the last access time is that date time
with mock_now(datetime.datetime(2011, 2, 3, 10, 11)):
print(retriever.get_relevant_documents("hello world"))
```
<CodeOutputBlock lang="python">
```
[Document(page_content='hello world', metadata={'last_accessed_at': MockDateTime(2011, 2, 3, 10, 11), 'created_at': datetime.datetime(2023, 5, 13, 21, 0, 27, 279596), 'buffer_idx': 0})]
```
</CodeOutputBlock>

View File

@@ -5,6 +5,91 @@ It uses the search methods implemented by a vector store, like similarity search
Once you construct a vector store, it's very easy to construct a retriever. Let's walk through an example.
import Example from "@snippets/modules/data_connection/retrievers/how_to/vectorstore.mdx"
```python
from langchain.document_loaders import TextLoader
loader = TextLoader('../../../state_of_the_union.txt')
```
<Example/>
```python
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)
embeddings = OpenAIEmbeddings()
db = FAISS.from_documents(texts, embeddings)
```
<CodeOutputBlock lang="python">
```
Exiting: Cleaning up .chroma directory
```
</CodeOutputBlock>
```python
retriever = db.as_retriever()
```
```python
docs = retriever.get_relevant_documents("what did he say about ketanji brown jackson")
```
## Maximum marginal relevance retrieval
By default, the vector store retriever uses similarity search. If the underlying vector store supports maximum marginal relevance search, you can specify that as the search type.
```python
retriever = db.as_retriever(search_type="mmr")
```
```python
docs = retriever.get_relevant_documents("what did he say about ketanji brown jackson")
```
## Similarity score threshold retrieval
You can also a retrieval method that sets a similarity score threshold and only returns documents with a score above that threshold.
```python
retriever = db.as_retriever(search_type="similarity_score_threshold", search_kwargs={"score_threshold": .5})
```
```python
docs = retriever.get_relevant_documents("what did he say about ketanji brown jackson")
```
## Specifying top k
You can also specify search kwargs like `k` to use when doing retrieval.
```python
retriever = db.as_retriever(search_kwargs={"k": 1})
```
```python
docs = retriever.get_relevant_documents("what did he say about ketanji brown jackson")
```
```python
len(docs)
```
<CodeOutputBlock lang="python">
```
1
```
</CodeOutputBlock>

View File

@@ -23,14 +23,14 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 7,
"id": "a463c3c2-749b-40d1-a433-84f68a1cd1c7",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from langchain.storage import InMemoryStore, LocalFileStore, RedisStore\n",
"from langchain.storage import InMemoryStore, LocalFileStore, RedisStore, UpstashRedisStore\n",
"from langchain.embeddings import OpenAIEmbeddings, CacheBackedEmbeddings"
]
},
@@ -46,7 +46,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": null,
"id": "9e4314d8-88ef-4f52-81ae-0be771168bb6",
"metadata": {},
"outputs": [],
@@ -59,7 +59,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": null,
"id": "3e751f26-9b5b-4c10-843a-d784b5ea8538",
"metadata": {},
"outputs": [],
@@ -69,7 +69,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": null,
"id": "30743664-38f5-425d-8216-772b64e7f348",
"metadata": {},
"outputs": [],
@@ -91,7 +91,7 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": null,
"id": "f9ad627f-ced2-4277-b336-2434f22f2c8a",
"metadata": {},
"outputs": [
@@ -120,7 +120,7 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": null,
"id": "cf958ac2-e60e-4668-b32c-8bb2d78b3c61",
"metadata": {},
"outputs": [],
@@ -140,7 +140,7 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": null,
"id": "3a1d7bb8-3b72-4bb5-9013-cf7729caca61",
"metadata": {},
"outputs": [
@@ -168,7 +168,7 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": null,
"id": "714cb2e2-77ba-41a8-bb83-84e75342af2d",
"metadata": {},
"outputs": [
@@ -196,7 +196,7 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": null,
"id": "f2ca32dd-3712-4093-942b-4122f3dc8a8e",
"metadata": {},
"outputs": [
@@ -232,7 +232,7 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": null,
"id": "13bd1c5b-b7ba-4394-957c-7d5b5a841972",
"metadata": {
"tags": []
@@ -244,7 +244,7 @@
},
{
"cell_type": "code",
"execution_count": 15,
"execution_count": null,
"id": "9d99885f-99e1-498c-904d-6db539ac9466",
"metadata": {
"tags": []
@@ -259,7 +259,7 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": null,
"id": "682eb5d4-0b7a-4dac-b8fb-3de4ca6e421c",
"metadata": {
"tags": []
@@ -289,7 +289,7 @@
},
{
"cell_type": "code",
"execution_count": 17,
"execution_count": null,
"id": "f819c3ff-a212-4d06-a5f7-5eb1435c1feb",
"metadata": {
"tags": []
@@ -311,7 +311,7 @@
},
{
"cell_type": "code",
"execution_count": 18,
"execution_count": null,
"id": "ec38fb72-90a9-4687-a483-c62c87d1f4dd",
"metadata": {
"tags": []
@@ -344,7 +344,7 @@
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": null,
"id": "a0070271-0809-4528-97e0-2a88216846f3",
"metadata": {
"tags": []
@@ -356,7 +356,7 @@
},
{
"cell_type": "code",
"execution_count": 20,
"execution_count": null,
"id": "0b20e9fe-f57f-4d7c-9f81-105c5f8726f4",
"metadata": {
"tags": []
@@ -370,7 +370,7 @@
},
{
"cell_type": "code",
"execution_count": 21,
"execution_count": null,
"id": "630515fd-bf5c-4d9c-a404-9705308f3a2c",
"metadata": {
"tags": []
@@ -392,7 +392,7 @@
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": null,
"id": "30e6bb87-42c9-4d08-88ac-0d22c9c449a1",
"metadata": {
"tags": []
@@ -424,7 +424,7 @@
},
{
"cell_type": "code",
"execution_count": 23,
"execution_count": null,
"id": "658e2914-05e9-44a3-a8fe-3fe17ca84039",
"metadata": {},
"outputs": [
@@ -444,6 +444,86 @@
"list(fs.yield_keys())"
]
},
{
"cell_type": "markdown",
"id": "904c1d47",
"metadata": {},
"source": [
"## Upstash Redis Store"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d0f9f212",
"metadata": {},
"outputs": [],
"source": [
"from langchain.storage.upstash_redis import UpstashRedisStore"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "45bf62e4",
"metadata": {},
"outputs": [],
"source": [
"from upstash_redis import Redis\n",
"URL = \"<UPSTASH_REDIS_REST_URL>\"\n",
"TOKEN = \"<UPSTASH_REDIS_REST_TOKEN>\"\n",
"\n",
"redis_client = Redis(url=URL, token=TOKEN)\n",
"store = UpstashRedisStore(client=redis_client, ttl=None, namespace=\"test-ns\")\n",
"\n",
"underlying_embeddings = OpenAIEmbeddings()\n",
"embedder = CacheBackedEmbeddings.from_bytes_store(\n",
" underlying_embeddings, store, namespace=underlying_embeddings.model\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3eac3504",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"embeddings = embedder.embed_documents([\"welcome\", \"goodbye\"])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "085dcd30",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"embeddings = embedder.embed_documents([\"welcome\", \"goodbye\"])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3570e83f",
"metadata": {},
"outputs": [],
"source": [
"list(store.yield_keys())"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d7dc8e51",
"metadata": {},
"outputs": [],
"source": [
"list(store.client.scan(0))"
]
},
{
"cell_type": "markdown",
"id": "cd5f5a96-6ffa-429d-aa82-00b3f6532871",
@@ -455,7 +535,7 @@
},
{
"cell_type": "code",
"execution_count": 24,
"execution_count": null,
"id": "4879c134-141f-48a0-acfe-7d6f30253af0",
"metadata": {},
"outputs": [],
@@ -465,7 +545,7 @@
},
{
"cell_type": "code",
"execution_count": 25,
"execution_count": null,
"id": "8b2bb9a0-6549-4487-8532-29ab4ab7336f",
"metadata": {},
"outputs": [],
@@ -482,7 +562,7 @@
},
{
"cell_type": "code",
"execution_count": 26,
"execution_count": null,
"id": "eca3cb99-2bb3-49d5-81f9-1dee03da4b8c",
"metadata": {},
"outputs": [
@@ -502,7 +582,7 @@
},
{
"cell_type": "code",
"execution_count": 27,
"execution_count": null,
"id": "317ba5d8-89f9-462c-b807-ad4ef26e518b",
"metadata": {},
"outputs": [
@@ -522,7 +602,7 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": null,
"id": "8a540317-5142-4491-9062-a097932b56e3",
"metadata": {},
"outputs": [
@@ -544,7 +624,7 @@
},
{
"cell_type": "code",
"execution_count": 17,
"execution_count": null,
"id": "cd9b0d4a-f816-4dce-9dde-cde1ad9a65fb",
"metadata": {},
"outputs": [
@@ -581,7 +661,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.1"
"version": "3.11.3"
}
},
"nbformat": 4,

View File

@@ -15,6 +15,76 @@ The base Embeddings class in LangChain provides two methods: one for embedding d
## Get started
import GetStarted from "@snippets/modules/data_connection/text_embedding/get_started.mdx"
### Setup
<GetStarted/>
To start we'll need to install the OpenAI Python package:
```bash
pip install openai
```
Accessing the API requires an API key, which you can get by creating an account and heading [here](https://platform.openai.com/account/api-keys). Once we have a key we'll want to set it as an environment variable by running:
```bash
export OPENAI_API_KEY="..."
```
If you'd prefer not to set an environment variable you can pass the key in directly via the `openai_api_key` named parameter when initiating the OpenAI LLM class:
```python
from langchain.embeddings import OpenAIEmbeddings
embeddings_model = OpenAIEmbeddings(openai_api_key="...")
```
Otherwise you can initialize without any params:
```python
from langchain.embeddings import OpenAIEmbeddings
embeddings_model = OpenAIEmbeddings()
```
### `embed_documents`
#### Embed list of texts
```python
embeddings = embeddings_model.embed_documents(
[
"Hi there!",
"Oh, hello!",
"What's your name?",
"My friends call me World",
"Hello World!"
]
)
len(embeddings), len(embeddings[0])
```
<CodeOutputBlock language="python">
```
(5, 1536)
```
</CodeOutputBlock>
### `embed_query`
#### Embed single query
Embed a single piece of text for the purpose of comparing to other embedded pieces of texts.
```python
embedded_query = embeddings_model.embed_query("What was the name mentioned in the conversation?")
embedded_query[:5]
```
<CodeOutputBlock language="python">
```
[0.0053587136790156364,
-0.0004999046213924885,
0.038883671164512634,
-0.003001077566295862,
-0.00900818221271038]
```
</CodeOutputBlock>

View File

@@ -18,14 +18,265 @@ for you.
This walkthrough showcases basic functionality related to vector stores. A key part of working with vector stores is creating the vector to put in them, which is usually created via embeddings. Therefore, it is recommended that you familiarize yourself with the [text embedding model](/docs/modules/data_connection/text_embedding/) interfaces before diving into this.
import GetStarted from "@snippets/modules/data_connection/vectorstores/get_started.mdx"
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<GetStarted/>
There are many great vector store options, here are a few that are free, open-source, and run entirely on your local machine. Review all integrations for many great hosted offerings.
<Tabs>
<TabItem value="chroma" label="Chroma" default>
This walkthrough uses the `chroma` vector database, which runs on your local machine as a library.
```bash
pip install chromadb
```
We want to use OpenAIEmbeddings so we have to get the OpenAI API Key.
```python
import os
import getpass
os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key:')
```
```python
from langchain.document_loaders import TextLoader
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import Chroma
# Load the document, split it into chunks, embed each chunk and load it into the vector store.
raw_documents = TextLoader('../../../state_of_the_union.txt').load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
documents = text_splitter.split_documents(raw_documents)
db = Chroma.from_documents(documents, OpenAIEmbeddings())
```
</TabItem>
<TabItem value="faiss" label="FAISS">
This walkthrough uses the `FAISS` vector database, which makes use of the Facebook AI Similarity Search (FAISS) library.
```bash
pip install faiss-cpu
```
We want to use OpenAIEmbeddings so we have to get the OpenAI API Key.
```python
import os
import getpass
os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key:')
```
```python
from langchain.document_loaders import TextLoader
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import FAISS
# Load the document, split it into chunks, embed each chunk and load it into the vector store.
raw_documents = TextLoader('../../../state_of_the_union.txt').load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
documents = text_splitter.split_documents(raw_documents)
db = FAISS.from_documents(documents, OpenAIEmbeddings())
```
</TabItem>
<TabItem value="lance" label="Lance">
This notebook shows how to use functionality related to the LanceDB vector database based on the Lance data format.
```bash
pip install lancedb
```
We want to use OpenAIEmbeddings so we have to get the OpenAI API Key.
```python
import os
import getpass
os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key:')
```
```python
from langchain.document_loaders import TextLoader
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import LanceDB
import lancedb
db = lancedb.connect("/tmp/lancedb")
table = db.create_table(
"my_table",
data=[
{
"vector": embeddings.embed_query("Hello World"),
"text": "Hello World",
"id": "1",
}
],
mode="overwrite",
)
# Load the document, split it into chunks, embed each chunk and load it into the vector store.
raw_documents = TextLoader('../../../state_of_the_union.txt').load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
documents = text_splitter.split_documents(raw_documents)
db = LanceDB.from_documents(documents, OpenAIEmbeddings(), connection=table)
```
</TabItem>
</Tabs>
### Similarity search
```python
query = "What did the president say about Ketanji Brown Jackson"
docs = db.similarity_search(query)
print(docs[0].page_content)
```
<CodeOutputBlock lang="python">
```
Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while youre at it, pass the Disclose Act so Americans can know who is funding our elections.
Tonight, Id like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.
One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nations top legal minds, who will continue Justice Breyers legacy of excellence.
```
</CodeOutputBlock>
### Similarity search by vector
It is also possible to do a search for documents similar to a given embedding vector using `similarity_search_by_vector` which accepts an embedding vector as a parameter instead of a string.
```python
embedding_vector = OpenAIEmbeddings().embed_query(query)
docs = db.similarity_search_by_vector(embedding_vector)
print(docs[0].page_content)
```
The query is the same, and so the result is also the same.
<CodeOutputBlock lang="python">
```
Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while youre at it, pass the Disclose Act so Americans can know who is funding our elections.
Tonight, Id like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.
One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nations top legal minds, who will continue Justice Breyers legacy of excellence.
```
</CodeOutputBlock>
## Asynchronous operations
Vector stores are usually run as a separate service that requires some IO operations, and therefore they might be called asynchronously. That gives performance benefits as you don't waste time waiting for responses from external services. That might also be important if you work with an asynchronous framework, such as [FastAPI](https://fastapi.tiangolo.com/).
import AsyncVectorStore from "@snippets/modules/data_connection/vectorstores/async.mdx"
LangChain supports async operation on vector stores. All the methods might be called using their async counterparts, with the prefix `a`, meaning `async`.
<AsyncVectorStore/>
`Qdrant` is a vector store, which supports all the async operations, thus it will be used in this walkthrough.
```bash
pip install qdrant-client
```
```python
from langchain.vectorstores import Qdrant
```
### Create a vector store asynchronously
```python
db = await Qdrant.afrom_documents(documents, embeddings, "http://localhost:6333")
```
### Similarity search
```python
query = "What did the president say about Ketanji Brown Jackson"
docs = await db.asimilarity_search(query)
print(docs[0].page_content)
```
<CodeOutputBlock lang="python">
```
Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while youre at it, pass the Disclose Act so Americans can know who is funding our elections.
Tonight, Id like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.
One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nations top legal minds, who will continue Justice Breyers legacy of excellence.
```
</CodeOutputBlock>
### Similarity search by vector
```python
embedding_vector = embeddings.embed_query(query)
docs = await db.asimilarity_search_by_vector(embedding_vector)
```
## Maximum marginal relevance search (MMR)
Maximal marginal relevance optimizes for similarity to query **and** diversity among selected documents. It is also supported in async API.
```python
query = "What did the president say about Ketanji Brown Jackson"
found_docs = await qdrant.amax_marginal_relevance_search(query, k=2, fetch_k=10)
for i, doc in enumerate(found_docs):
print(f"{i + 1}.", doc.page_content, "\n")
```
<CodeOutputBlock lang="python">
```
1. Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while youre at it, pass the Disclose Act so Americans can know who is funding our elections.
Tonight, Id like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.
One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nations top legal minds, who will continue Justice Breyers legacy of excellence.
2. We cant change how divided weve been. But we can change how we move forward—on COVID-19 and other issues we must face together.
I recently visited the New York City Police Department days after the funerals of Officer Wilbert Mora and his partner, Officer Jason Rivera.
They were responding to a 9-1-1 call when a man shot and killed them with a stolen gun.
Officer Mora was 27 years old.
Officer Rivera was 22.
Both Dominican Americans whod grown up on the same streets they later chose to patrol as police officers.
I spoke with their families and told them that we are forever in debt for their sacrifice, and we will carry on their mission to restore the trust and safety every community deserves.
Ive worked on these issues a long time.
I know what works: Investing in crime prevention and community police officers wholl walk the beat, wholl know the neighborhood, and who can restore trust and safety.
```
</CodeOutputBlock>

View File

@@ -12,6 +12,26 @@ This is a super lightweight wrapper that provides convenience methods for saving
You may want to use this class directly if you are managing memory outside of a chain.
import GetStarted from "@snippets/modules/memory/chat_messages/get_started.mdx"
```python
from langchain.memory import ChatMessageHistory
<GetStarted/>
history = ChatMessageHistory()
history.add_user_message("hi!")
history.add_ai_message("whats up?")
```
```python
history.messages
```
<CodeOutputBlock lang="python">
```
[HumanMessage(content='hi!', additional_kwargs={}),
AIMessage(content='whats up?', additional_kwargs={})]
```
</CodeOutputBlock>

View File

@@ -50,9 +50,177 @@ Each application can have different requirements for how memory is queried. The
Let's take a look at what Memory actually looks like in LangChain.
Here we'll cover the basics of interacting with an arbitrary memory class.
import GetStarted from "@snippets/modules/memory/get_started.mdx"
Let's take a look at how to use `ConversationBufferMemory` in chains.
`ConversationBufferMemory` is an extremely simple form of memory that just keeps a list of chat messages in a buffer
and passes those into the prompt template.
```python
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory()
memory.chat_memory.add_user_message("hi!")
memory.chat_memory.add_ai_message("what's up?")
```
When using memory in a chain, there are a few key concepts to understand.
Note that here we cover general concepts that are useful for most types of memory.
Each individual memory type may very well have its own parameters and concepts that are necessary to understand.
### What variables get returned from memory
Before going into the chain, various variables are read from memory.
These have specific names which need to align with the variables the chain expects.
You can see what these variables are by calling `memory.load_memory_variables({})`.
Note that the empty dictionary that we pass in is just a placeholder for real variables.
If the memory type you are using is dependent upon the input variables, you may need to pass some in.
```python
memory.load_memory_variables({})
```
<CodeOutputBlock lang="python">
```
{'history': "Human: hi!\nAI: what's up?"}
```
</CodeOutputBlock>
In this case, you can see that `load_memory_variables` returns a single key, `history`.
This means that your chain (and likely your prompt) should expect an input named `history`.
You can usually control this variable through parameters on the memory class.
For example, if you want the memory variables to be returned in the key `chat_history` you can do:
```python
memory = ConversationBufferMemory(memory_key="chat_history")
memory.chat_memory.add_user_message("hi!")
memory.chat_memory.add_ai_message("what's up?")
```
<CodeOutputBlock lang="python">
```
{'chat_history': "Human: hi!\nAI: what's up?"}
```
</CodeOutputBlock>
The parameter name to control these keys may vary per memory type, but it's important to understand that (1) this is controllable, and (2) how to control it.
### Whether memory is a string or a list of messages
One of the most common types of memory involves returning a list of chat messages.
These can either be returned as a single string, all concatenated together (useful when they will be passed into LLMs)
or a list of ChatMessages (useful when passed into ChatModels).
By default, they are returned as a single string.
In order to return as a list of messages, you can set `return_messages=True`
```python
memory = ConversationBufferMemory(return_messages=True)
memory.chat_memory.add_user_message("hi!")
memory.chat_memory.add_ai_message("what's up?")
```
<CodeOutputBlock lang="python">
```
{'history': [HumanMessage(content='hi!', additional_kwargs={}, example=False),
AIMessage(content='what's up?', additional_kwargs={}, example=False)]}
```
</CodeOutputBlock>
### What keys are saved to memory
Often times chains take in or return multiple input/output keys.
In these cases, how can we know which keys we want to save to the chat message history?
This is generally controllable by `input_key` and `output_key` parameters on the memory types.
These default to `None` - and if there is only one input/output key it is known to just use that.
However, if there are multiple input/output keys then you MUST specify the name of which one to use.
### End to end example
Finally, let's take a look at using this in a chain.
We'll use an `LLMChain`, and show working with both an LLM and a ChatModel.
#### Using an LLM
```python
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory
llm = OpenAI(temperature=0)
# Notice that "chat_history" is present in the prompt template
template = """You are a nice chatbot having a conversation with a human.
Previous conversation:
{chat_history}
New human question: {question}
Response:"""
prompt = PromptTemplate.from_template(template)
# Notice that we need to align the `memory_key`
memory = ConversationBufferMemory(memory_key="chat_history")
conversation = LLMChain(
llm=llm,
prompt=prompt,
verbose=True,
memory=memory
)
```
```python
# Notice that we just pass in the `question` variables - `chat_history` gets populated by memory
conversation({"question": "hi"})
```
#### Using a ChatModel
```python
from langchain.chat_models import ChatOpenAI
from langchain.prompts import (
ChatPromptTemplate,
MessagesPlaceholder,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory
llm = ChatOpenAI()
prompt = ChatPromptTemplate(
messages=[
SystemMessagePromptTemplate.from_template(
"You are a nice chatbot having a conversation with a human."
),
# The `variable_name` here is what must align with memory
MessagesPlaceholder(variable_name="chat_history"),
HumanMessagePromptTemplate.from_template("{question}")
]
)
# Notice that we `return_messages=True` to fit into the MessagesPlaceholder
# Notice that `"chat_history"` aligns with the MessagesPlaceholder name.
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
conversation = LLMChain(
llm=llm,
prompt=prompt,
verbose=True,
memory=memory
)
```
```python
# Notice that we just pass in the `question` variables - `chat_history` gets populated by memory
conversation({"question": "hi"})
```
<GetStarted/>
## Next steps

View File

@@ -4,6 +4,158 @@ This notebook shows how to use `ConversationBufferMemory`. This memory allows fo
We can first extract it as a string.
import Example from "@snippets/modules/memory/types/buffer.mdx"
```python
from langchain.memory import ConversationBufferMemory
```
<Example/>
```python
memory = ConversationBufferMemory()
memory.save_context({"input": "hi"}, {"output": "whats up"})
```
```python
memory.load_memory_variables({})
```
<CodeOutputBlock lang="python">
```
{'history': 'Human: hi\nAI: whats up'}
```
</CodeOutputBlock>
We can also get the history as a list of messages (this is useful if you are using this with a chat model).
```python
memory = ConversationBufferMemory(return_messages=True)
memory.save_context({"input": "hi"}, {"output": "whats up"})
```
```python
memory.load_memory_variables({})
```
<CodeOutputBlock lang="python">
```
{'history': [HumanMessage(content='hi', additional_kwargs={}),
AIMessage(content='whats up', additional_kwargs={})]}
```
</CodeOutputBlock>
## Using in a chain
Finally, let's take a look at using this in a chain (setting `verbose=True` so we can see the prompt).
```python
from langchain.llms import OpenAI
from langchain.chains import ConversationChain
llm = OpenAI(temperature=0)
conversation = ConversationChain(
llm=llm,
verbose=True,
memory=ConversationBufferMemory()
)
```
```python
conversation.predict(input="Hi there!")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
Human: Hi there!
AI:
> Finished chain.
" Hi there! It's nice to meet you. How can I help you today?"
```
</CodeOutputBlock>
```python
conversation.predict(input="I'm doing well! Just having a conversation with an AI.")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
Human: Hi there!
AI: Hi there! It's nice to meet you. How can I help you today?
Human: I'm doing well! Just having a conversation with an AI.
AI:
> Finished chain.
" That's great! It's always nice to have a conversation with someone new. What would you like to talk about?"
```
</CodeOutputBlock>
```python
conversation.predict(input="Tell me about yourself.")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
Human: Hi there!
AI: Hi there! It's nice to meet you. How can I help you today?
Human: I'm doing well! Just having a conversation with an AI.
AI: That's great! It's always nice to have a conversation with someone new. What would you like to talk about?
Human: Tell me about yourself.
AI:
> Finished chain.
" Sure! I'm an AI created to help people with their everyday tasks. I'm programmed to understand natural language and provide helpful information. I'm also constantly learning and updating my knowledge base so I can provide more accurate and helpful answers."
```
</CodeOutputBlock>

View File

@@ -4,6 +4,188 @@
Let's first explore the basic functionality of this type of memory.
import Example from "@snippets/modules/memory/types/buffer_window.mdx"
```python
from langchain.memory import ConversationBufferWindowMemory
```
<Example/>
```python
memory = ConversationBufferWindowMemory( k=1)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})
```
```python
memory.load_memory_variables({})
```
<CodeOutputBlock lang="python">
```
{'history': 'Human: not much you\nAI: not much'}
```
</CodeOutputBlock>
We can also get the history as a list of messages (this is useful if you are using this with a chat model).
```python
memory = ConversationBufferWindowMemory( k=1, return_messages=True)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})
```
```python
memory.load_memory_variables({})
```
<CodeOutputBlock lang="python">
```
{'history': [HumanMessage(content='not much you', additional_kwargs={}),
AIMessage(content='not much', additional_kwargs={})]}
```
</CodeOutputBlock>
## Using in a chain
Let's walk through an example, again setting `verbose=True` so we can see the prompt.
```python
from langchain.llms import OpenAI
from langchain.chains import ConversationChain
conversation_with_summary = ConversationChain(
llm=OpenAI(temperature=0),
# We set a low k=2, to only keep the last 2 interactions in memory
memory=ConversationBufferWindowMemory(k=2),
verbose=True
)
conversation_with_summary.predict(input="Hi, what's up?")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
Human: Hi, what's up?
AI:
> Finished chain.
" Hi there! I'm doing great. I'm currently helping a customer with a technical issue. How about you?"
```
</CodeOutputBlock>
```python
conversation_with_summary.predict(input="What's their issues?")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
Human: Hi, what's up?
AI: Hi there! I'm doing great. I'm currently helping a customer with a technical issue. How about you?
Human: What's their issues?
AI:
> Finished chain.
" The customer is having trouble connecting to their Wi-Fi network. I'm helping them troubleshoot the issue and get them connected."
```
</CodeOutputBlock>
```python
conversation_with_summary.predict(input="Is it going well?")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
Human: Hi, what's up?
AI: Hi there! I'm doing great. I'm currently helping a customer with a technical issue. How about you?
Human: What's their issues?
AI: The customer is having trouble connecting to their Wi-Fi network. I'm helping them troubleshoot the issue and get them connected.
Human: Is it going well?
AI:
> Finished chain.
" Yes, it's going well so far. We've already identified the problem and are now working on a solution."
```
</CodeOutputBlock>
```python
# Notice here that the first interaction does not appear.
conversation_with_summary.predict(input="What's the solution?")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
Human: What's their issues?
AI: The customer is having trouble connecting to their Wi-Fi network. I'm helping them troubleshoot the issue and get them connected.
Human: Is it going well?
AI: Yes, it's going well so far. We've already identified the problem and are now working on a solution.
Human: What's the solution?
AI:
> Finished chain.
" The solution is to reset the router and reconfigure the settings. We're currently in the process of doing that."
```
</CodeOutputBlock>

View File

@@ -4,6 +4,421 @@ Entity memory remembers given facts about specific entities in a conversation. I
Let's first walk through using this functionality.
import Example from "@snippets/modules/memory/types/entity_summary_memory.mdx"
```python
from langchain.llms import OpenAI
from langchain.memory import ConversationEntityMemory
llm = OpenAI(temperature=0)
```
<Example/>
```python
memory = ConversationEntityMemory(llm=llm)
_input = {"input": "Deven & Sam are working on a hackathon project"}
memory.load_memory_variables(_input)
memory.save_context(
_input,
{"output": " That sounds like a great project! What kind of project are they working on?"}
)
```
```python
memory.load_memory_variables({"input": 'who is Sam'})
```
<CodeOutputBlock lang="python">
```
{'history': 'Human: Deven & Sam are working on a hackathon project\nAI: That sounds like a great project! What kind of project are they working on?',
'entities': {'Sam': 'Sam is working on a hackathon project with Deven.'}}
```
</CodeOutputBlock>
```python
memory = ConversationEntityMemory(llm=llm, return_messages=True)
_input = {"input": "Deven & Sam are working on a hackathon project"}
memory.load_memory_variables(_input)
memory.save_context(
_input,
{"output": " That sounds like a great project! What kind of project are they working on?"}
)
```
```python
memory.load_memory_variables({"input": 'who is Sam'})
```
<CodeOutputBlock lang="python">
```
{'history': [HumanMessage(content='Deven & Sam are working on a hackathon project', additional_kwargs={}),
AIMessage(content=' That sounds like a great project! What kind of project are they working on?', additional_kwargs={})],
'entities': {'Sam': 'Sam is working on a hackathon project with Deven.'}}
```
</CodeOutputBlock>
## Using in a chain
Let's now use it in a chain!
```python
from langchain.chains import ConversationChain
from langchain.memory import ConversationEntityMemory
from langchain.memory.prompt import ENTITY_MEMORY_CONVERSATION_TEMPLATE
from pydantic import BaseModel
from typing import List, Dict, Any
```
```python
conversation = ConversationChain(
llm=llm,
verbose=True,
prompt=ENTITY_MEMORY_CONVERSATION_TEMPLATE,
memory=ConversationEntityMemory(llm=llm)
)
```
```python
conversation.predict(input="Deven & Sam are working on a hackathon project")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
You are an assistant to a human, powered by a large language model trained by OpenAI.
You are designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, you are able to generate human-like text based on the input you receive, allowing you to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.
You are constantly learning and improving, and your capabilities are constantly evolving. You are able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. You have access to some personalized information provided by the human in the Context section below. Additionally, you are able to generate your own text based on the input you receive, allowing you to engage in discussions and provide explanations and descriptions on a wide range of topics.
Overall, you are a powerful tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether the human needs help with a specific question or just wants to have a conversation about a particular topic, you are here to assist.
Context:
{'Deven': 'Deven is working on a hackathon project with Sam.', 'Sam': 'Sam is working on a hackathon project with Deven.'}
Current conversation:
Last line:
Human: Deven & Sam are working on a hackathon project
You:
> Finished chain.
' That sounds like a great project! What kind of project are they working on?'
```
</CodeOutputBlock>
```python
conversation.memory.entity_store.store
```
<CodeOutputBlock lang="python">
```
{'Deven': 'Deven is working on a hackathon project with Sam, which they are entering into a hackathon.',
'Sam': 'Sam is working on a hackathon project with Deven.'}
```
</CodeOutputBlock>
```python
conversation.predict(input="They are trying to add more complex memory structures to Langchain")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
You are an assistant to a human, powered by a large language model trained by OpenAI.
You are designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, you are able to generate human-like text based on the input you receive, allowing you to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.
You are constantly learning and improving, and your capabilities are constantly evolving. You are able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. You have access to some personalized information provided by the human in the Context section below. Additionally, you are able to generate your own text based on the input you receive, allowing you to engage in discussions and provide explanations and descriptions on a wide range of topics.
Overall, you are a powerful tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether the human needs help with a specific question or just wants to have a conversation about a particular topic, you are here to assist.
Context:
{'Deven': 'Deven is working on a hackathon project with Sam, which they are entering into a hackathon.', 'Sam': 'Sam is working on a hackathon project with Deven.', 'Langchain': ''}
Current conversation:
Human: Deven & Sam are working on a hackathon project
AI: That sounds like a great project! What kind of project are they working on?
Last line:
Human: They are trying to add more complex memory structures to Langchain
You:
> Finished chain.
' That sounds like an interesting project! What kind of memory structures are they trying to add?'
```
</CodeOutputBlock>
```python
conversation.predict(input="They are adding in a key-value store for entities mentioned so far in the conversation.")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
You are an assistant to a human, powered by a large language model trained by OpenAI.
You are designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, you are able to generate human-like text based on the input you receive, allowing you to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.
You are constantly learning and improving, and your capabilities are constantly evolving. You are able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. You have access to some personalized information provided by the human in the Context section below. Additionally, you are able to generate your own text based on the input you receive, allowing you to engage in discussions and provide explanations and descriptions on a wide range of topics.
Overall, you are a powerful tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether the human needs help with a specific question or just wants to have a conversation about a particular topic, you are here to assist.
Context:
{'Deven': 'Deven is working on a hackathon project with Sam, which they are entering into a hackathon. They are trying to add more complex memory structures to Langchain.', 'Sam': 'Sam is working on a hackathon project with Deven, trying to add more complex memory structures to Langchain.', 'Langchain': 'Langchain is a project that is trying to add more complex memory structures.', 'Key-Value Store': ''}
Current conversation:
Human: Deven & Sam are working on a hackathon project
AI: That sounds like a great project! What kind of project are they working on?
Human: They are trying to add more complex memory structures to Langchain
AI: That sounds like an interesting project! What kind of memory structures are they trying to add?
Last line:
Human: They are adding in a key-value store for entities mentioned so far in the conversation.
You:
> Finished chain.
' That sounds like a great idea! How will the key-value store help with the project?'
```
</CodeOutputBlock>
```python
conversation.predict(input="What do you know about Deven & Sam?")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
You are an assistant to a human, powered by a large language model trained by OpenAI.
You are designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, you are able to generate human-like text based on the input you receive, allowing you to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.
You are constantly learning and improving, and your capabilities are constantly evolving. You are able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. You have access to some personalized information provided by the human in the Context section below. Additionally, you are able to generate your own text based on the input you receive, allowing you to engage in discussions and provide explanations and descriptions on a wide range of topics.
Overall, you are a powerful tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether the human needs help with a specific question or just wants to have a conversation about a particular topic, you are here to assist.
Context:
{'Deven': 'Deven is working on a hackathon project with Sam, which they are entering into a hackathon. They are trying to add more complex memory structures to Langchain, including a key-value store for entities mentioned so far in the conversation.', 'Sam': 'Sam is working on a hackathon project with Deven, trying to add more complex memory structures to Langchain, including a key-value store for entities mentioned so far in the conversation.'}
Current conversation:
Human: Deven & Sam are working on a hackathon project
AI: That sounds like a great project! What kind of project are they working on?
Human: They are trying to add more complex memory structures to Langchain
AI: That sounds like an interesting project! What kind of memory structures are they trying to add?
Human: They are adding in a key-value store for entities mentioned so far in the conversation.
AI: That sounds like a great idea! How will the key-value store help with the project?
Last line:
Human: What do you know about Deven & Sam?
You:
> Finished chain.
' Deven and Sam are working on a hackathon project together, trying to add more complex memory structures to Langchain, including a key-value store for entities mentioned so far in the conversation. They seem to be working hard on this project and have a great idea for how the key-value store can help.'
```
</CodeOutputBlock>
## Inspecting the memory store
We can also inspect the memory store directly. In the following examples, we look at it directly, and then go through some examples of adding information and watch how it changes.
```python
from pprint import pprint
pprint(conversation.memory.entity_store.store)
```
<CodeOutputBlock lang="python">
```
{'Daimon': 'Daimon is a company founded by Sam, a successful entrepreneur.',
'Deven': 'Deven is working on a hackathon project with Sam, which they are '
'entering into a hackathon. They are trying to add more complex '
'memory structures to Langchain, including a key-value store for '
'entities mentioned so far in the conversation, and seem to be '
'working hard on this project with a great idea for how the '
'key-value store can help.',
'Key-Value Store': 'A key-value store is being added to the project to store '
'entities mentioned in the conversation.',
'Langchain': 'Langchain is a project that is trying to add more complex '
'memory structures, including a key-value store for entities '
'mentioned so far in the conversation.',
'Sam': 'Sam is working on a hackathon project with Deven, trying to add more '
'complex memory structures to Langchain, including a key-value store '
'for entities mentioned so far in the conversation. They seem to have '
'a great idea for how the key-value store can help, and Sam is also '
'the founder of a company called Daimon.'}
```
</CodeOutputBlock>
```python
conversation.predict(input="Sam is the founder of a company called Daimon.")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
You are an assistant to a human, powered by a large language model trained by OpenAI.
You are designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, you are able to generate human-like text based on the input you receive, allowing you to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.
You are constantly learning and improving, and your capabilities are constantly evolving. You are able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. You have access to some personalized information provided by the human in the Context section below. Additionally, you are able to generate your own text based on the input you receive, allowing you to engage in discussions and provide explanations and descriptions on a wide range of topics.
Overall, you are a powerful tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether the human needs help with a specific question or just wants to have a conversation about a particular topic, you are here to assist.
Context:
{'Daimon': 'Daimon is a company founded by Sam, a successful entrepreneur.', 'Sam': 'Sam is working on a hackathon project with Deven, trying to add more complex memory structures to Langchain, including a key-value store for entities mentioned so far in the conversation. They seem to have a great idea for how the key-value store can help, and Sam is also the founder of a company called Daimon.'}
Current conversation:
Human: They are adding in a key-value store for entities mentioned so far in the conversation.
AI: That sounds like a great idea! How will the key-value store help with the project?
Human: What do you know about Deven & Sam?
AI: Deven and Sam are working on a hackathon project together, trying to add more complex memory structures to Langchain, including a key-value store for entities mentioned so far in the conversation. They seem to be working hard on this project and have a great idea for how the key-value store can help.
Human: Sam is the founder of a company called Daimon.
AI:
That's impressive! It sounds like Sam is a very successful entrepreneur. What kind of company is Daimon?
Last line:
Human: Sam is the founder of a company called Daimon.
You:
> Finished chain.
" That's impressive! It sounds like Sam is a very successful entrepreneur. What kind of company is Daimon?"
```
</CodeOutputBlock>
```python
from pprint import pprint
pprint(conversation.memory.entity_store.store)
```
<CodeOutputBlock lang="python">
```
{'Daimon': 'Daimon is a company founded by Sam, a successful entrepreneur, who '
'is working on a hackathon project with Deven to add more complex '
'memory structures to Langchain.',
'Deven': 'Deven is working on a hackathon project with Sam, which they are '
'entering into a hackathon. They are trying to add more complex '
'memory structures to Langchain, including a key-value store for '
'entities mentioned so far in the conversation, and seem to be '
'working hard on this project with a great idea for how the '
'key-value store can help.',
'Key-Value Store': 'A key-value store is being added to the project to store '
'entities mentioned in the conversation.',
'Langchain': 'Langchain is a project that is trying to add more complex '
'memory structures, including a key-value store for entities '
'mentioned so far in the conversation.',
'Sam': 'Sam is working on a hackathon project with Deven, trying to add more '
'complex memory structures to Langchain, including a key-value store '
'for entities mentioned so far in the conversation. They seem to have '
'a great idea for how the key-value store can help, and Sam is also '
'the founder of a successful company called Daimon.'}
```
</CodeOutputBlock>
```python
conversation.predict(input="What do you know about Sam?")
```
<CodeOutputBlock lang="python">
```
> Entering new ConversationChain chain...
Prompt after formatting:
You are an assistant to a human, powered by a large language model trained by OpenAI.
You are designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, you are able to generate human-like text based on the input you receive, allowing you to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.
You are constantly learning and improving, and your capabilities are constantly evolving. You are able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. You have access to some personalized information provided by the human in the Context section below. Additionally, you are able to generate your own text based on the input you receive, allowing you to engage in discussions and provide explanations and descriptions on a wide range of topics.
Overall, you are a powerful tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether the human needs help with a specific question or just wants to have a conversation about a particular topic, you are here to assist.
Context:
{'Deven': 'Deven is working on a hackathon project with Sam, which they are entering into a hackathon. They are trying to add more complex memory structures to Langchain, including a key-value store for entities mentioned so far in the conversation, and seem to be working hard on this project with a great idea for how the key-value store can help.', 'Sam': 'Sam is working on a hackathon project with Deven, trying to add more complex memory structures to Langchain, including a key-value store for entities mentioned so far in the conversation. They seem to have a great idea for how the key-value store can help, and Sam is also the founder of a successful company called Daimon.', 'Langchain': 'Langchain is a project that is trying to add more complex memory structures, including a key-value store for entities mentioned so far in the conversation.', 'Daimon': 'Daimon is a company founded by Sam, a successful entrepreneur, who is working on a hackathon project with Deven to add more complex memory structures to Langchain.'}
Current conversation:
Human: What do you know about Deven & Sam?
AI: Deven and Sam are working on a hackathon project together, trying to add more complex memory structures to Langchain, including a key-value store for entities mentioned so far in the conversation. They seem to be working hard on this project and have a great idea for how the key-value store can help.
Human: Sam is the founder of a company called Daimon.
AI:
That's impressive! It sounds like Sam is a very successful entrepreneur. What kind of company is Daimon?
Human: Sam is the founder of a company called Daimon.
AI: That's impressive! It sounds like Sam is a very successful entrepreneur. What kind of company is Daimon?
Last line:
Human: What do you know about Sam?
You:
> Finished chain.
' Sam is the founder of a successful company called Daimon. He is also working on a hackathon project with Deven to add more complex memory structures to Langchain. They seem to have a great idea for how the key-value store can help.'
```
</CodeOutputBlock>

Some files were not shown because too many files have changed in this diff Show More