---
# System prepended metadata

title: '[工具] LangChain'
tags: [NLU, NLP, LLM, LangChain, OpenAIEmbeddings, ML, embeddings, RAG]

---

[工具] LangChain
===
###### tags: `LLM`
###### tags: `ML`, `NLP`, `NLU`, `LLM`, `LangChain`, `RAG`, `embeddings`, `OpenAIEmbeddings`

<br>

[TOC]

<br>

## Intro

### installation
- [[doc][Get started] Installation](https://python.langchain.com/docs/get_started/installation)
    - official
        ```
        conda install langchain -c conda-forge
        ```
    - 10.78.26.241
        ```
        conda create --name langchain -c conda-forge langchain jupyterlab
        conda activate langchain
        jupyter-lab --ip 0.0.0.0 --port 38888 \
            --ServerApp.token='' --ServerApp.password='' 
        ```
        - Python 3.11.6 (2023/10/20)
        - jupyter warning -> fix
            ```
            $ python -m pip install jupyter_nbextensions_configurator
            $ python -m pip install jupyter_contrib_nbextensions
            ```
            好像沒幫助

<br>

<hr>

<br>

## document_loader / text_loader

### 精神：從「檔案」讀取出「文字」
- **範例程式：**
    ```python=
    from langchain.document_loaders import TextLoader
    loader = TextLoader('日本親子旅行.txt', encoding='utf8')
    document = loader.load()

    from langchain.text_splitter import CharacterTextSplitter
    text_splitter = CharacterTextSplitter(
        separator='\n\n', chunk_size=200, chunk_overlap=60)
    texts = text_splitter.split_documents(document)
    print(texts[0], end='\n\n')
    print(texts[1], end='\n\n')
    print(texts[2], end='\n\n')
    ```
    [![](https://hackmd.io/_uploads/ByaNCoGpa.png)](https://hackmd.io/_uploads/ByaNCoGpa.png)

<br>

<hr>

<br>

## embeddings

### 精神：文字 轉 向量
- **範例程式：**
    ```python=
    from langchain_community.embeddings import GPT4AllEmbeddings

    text = "This is a test document."
    gpt4all_embd = GPT4AllEmbeddings()
    gpt4all_embd.embed_query(text)
    ```
    - import 來源
        - `from langchain_community.embeddings import GPT4AllEmbeddings`
        - `from langchain.embeddings import GPT4AllEmbeddings`
- **執行結果**
    ![](https://hackmd.io/_uploads/BJIQd5B2a.png)
- **任何長度文字，都是產生 dim=384 的向量**
    - `A` -> dim=384 的向量
    - `hello` -> dim=384 的向量
    - `Nice to meet you!` -> dim=384 的向量
    - `This is a test document.` -> dim=384 的向量
- **API 說明**
    - `embed_query(text)` -> 單次
    - `embed_documents([text1, text2, text3])` -> 批次

<br>

### 不同的 embedding model 產生不同長度
| class | dim | doc |
|-------|-----|-----|
||
| ==`from langchain_community.embeddings import *`== |
| `GPT4AllEmbeddings` | 384 | [doc](https://python.langchain.com/docs/integrations/text_embedding/gpt4all) |
| `HuggingFaceEmbeddings` | 768 | [doc](https://python.langchain.com/docs/integrations/text_embedding/huggingfacehub) |
| `OllamaEmbeddings()` | 5120 | [doc](https://python.langchain.com/docs/integrations/text_embedding/ollama) |
| `OllamaEmbeddings(model="llama2:7b-chat")` | 4096 |
| `OllamaEmbeddings(model="llama2:13b-chat")` | 5120 |
| `OllamaEmbeddings(model="llama2:70b-chat")` | 8192 |
| `SentenceTransformerEmbeddings` | 768 | [doc](https://python.langchain.com/docs/integrations/text_embedding/sentence_transformers) |
||
| ==`from langchain_openai import *`== (付費) |
| `OpenAIEmbeddings` | 1536 | [doc](https://python.langchain.com/docs/integrations/text_embedding/openai) |
||
| ==`from langchain.embeddings import *`== |
| 等同 `from langchain_community.embeddings import *` |

<br>

### 來至 langchain_community.embeddings
- **列舉相關的 Embeddings**
    ```python
    import langchain_community.embeddings
    dir(langchain_community.embeddings)
    ```
    ```
    ['AlephAlphaAsymmetricSemanticEmbedding',
     'AlephAlphaSymmetricSemanticEmbedding',
     'AwaEmbeddings',
     'AzureOpenAIEmbeddings',
     'BaichuanTextEmbeddings',
     'BedrockEmbeddings',
     'BookendEmbeddings',
     'ClarifaiEmbeddings',
     'CohereEmbeddings',
     'DashScopeEmbeddings',
     'DatabricksEmbeddings',
     'DeepInfraEmbeddings',
     'DeterministicFakeEmbedding',
     'EdenAiEmbeddings',
     'ElasticsearchEmbeddings',
     'EmbaasEmbeddings',
     'ErnieEmbeddings',
     'FakeEmbeddings',
     'FastEmbedEmbeddings',
     'GPT4AllEmbeddings',
     'GooglePalmEmbeddings',
     'GradientEmbeddings',
     'HuggingFaceBgeEmbeddings',
     'HuggingFaceEmbeddings',
     'HuggingFaceHubEmbeddings',
     'HuggingFaceInferenceAPIEmbeddings',
     'HuggingFaceInstructEmbeddings',
     'HypotheticalDocumentEmbedder',
     'InfinityEmbeddings',
     'JavelinAIGatewayEmbeddings',
     'JinaEmbeddings',
     'JohnSnowLabsEmbeddings',
     'LLMRailsEmbeddings',
     'LlamaCppEmbeddings',
     'LocalAIEmbeddings',
     'MiniMaxEmbeddings',
     'MlflowAIGatewayEmbeddings',
     'MlflowCohereEmbeddings',
     'MlflowEmbeddings',
     'ModelScopeEmbeddings',
     'MosaicMLInstructorEmbeddings',
     'NLPCloudEmbeddings',
     'NeMoEmbeddings',
     'OCIGenAIEmbeddings',
     'OctoAIEmbeddings',
     'OllamaEmbeddings',
     'OpenAIEmbeddings',
     'QianfanEmbeddingsEndpoint',
     'QuantizedBiEncoderEmbeddings',
     'SagemakerEndpointEmbeddings',
     'SelfHostedEmbeddings',
     'SelfHostedHuggingFaceEmbeddings',
     'SelfHostedHuggingFaceInstructEmbeddings',
     'SentenceTransformerEmbeddings',
     'SpacyEmbeddings',
     'TensorflowHubEmbeddings',
     'VertexAIEmbeddings',
     'VolcanoEmbeddings',
     'VoyageEmbeddings',
     'XinferenceEmbeddings',
    ```
    - OpenAIEmbeddings 已經被 deprecated

<br>

### 來至 langchain_openai
- 新版安裝方式
    ```bash
    $ pip install -U langchain-openai
    ```
- OpenAIEmbeddings
    ```python
    from langchain_openai import OpenAIEmbeddings
    import os
    os.environ["OPENAI_API_KEY"] = "sk-******"  # 需付費

    embeddings = OpenAIEmbeddings()
    text = "This is a test document."
    len(embeddings.embed_query(text))  # dim = 1536
    ```
    
<br>

### 來至 langchain.embeddings
- SentenceTransformerEmbeddings
    ```python=
    from langchain.embeddings import SentenceTransformerEmbeddings

    embeddings = SentenceTransformerEmbeddings()
    text = "This is a test document."
    doc_result = embeddings.embed_documents(['hello', 'nice to meet you', text])
    ```

<br>

### 範例：OllamaEmbeddings
```
$ ollama pull llama3
```
```python=
from langchain.embeddings import OllamaEmbeddings
from sklearn.metrics.pairwise import cosine_similarity

# 初始化 Ollama Embeddings
ollama_embeddings = OllamaEmbeddings(model="llama3")  # 指定要使用的模型

# 範例文字
texts = [
    "洗錢防制法第3條為何？",
    "中華民國113年07月31日洗錢防制法第1條：「為防制洗錢，打擊犯罪，健全防制洗錢體系，穩定金融秩序，促進金流之透明，強化國際合作，特制定本法。」",
    "中華民國113年07月31日洗錢防制法第2條：「本法所稱洗錢，指下列行為：\n一、隱匿特定犯罪所得或掩飾其來源。\n二、妨礙或危害國家對於特定犯罪所得之調查、發現、保全、沒收或追徵。\n三、收受、持有或使用他人之特定犯罪所得。\n四、使用自己之特定犯罪所得與他人進行交易。」",
    "中華民國113年07月31日洗錢防制法第3條：「本法所稱特定犯罪，指下列各款之罪：\n一、最輕本刑為六月以上有期徒刑之罪。\n二、刑法第一百二十一條、第一百二十三條、第二百零一條之一第二項、第二百三十一條、第二百三十三條第一項、第二百三十五條第一項、第二項、第二百六十六條第一項、第二項、第二百六十八條、第三百十九條之一第二項、第三項及該二項之未遂犯、第三百十九條之三第四項而犯第一項及其未遂犯、第三百十九條之四第三項、第三百三十九條、第三百三十九條之二、第三百三十九條之三、第三百四十二條、第三百四十四條第一項、第三百四十九條、第三百五十八條至第三百六十二條之罪。\n三、懲治走私條例第二條第一項、第二項、第三條之罪。\n...」",
    "中華民國113年07月31日洗錢防制法第4條：「1.本法所稱特定犯罪所得，指犯第三條所列之特定犯罪而取得或變得之財物或財產上利益及其孳息。\n2.前項特定犯罪所得之認定，不以其所犯特定犯罪經有罪判決為必要。」",
]

# 產生向量（Embeddings）
embeddings = [ollama_embeddings.embed_query(text) for text in texts]

# 計算並輸出餘弦相似度
print(cosine_similarity([embeddings[0]], [embeddings[1]])[0][0])
print(cosine_similarity([embeddings[0]], [embeddings[2]])[0][0])
print(cosine_similarity([embeddings[0]], [embeddings[3]])[0][0])
print(cosine_similarity([embeddings[0]], [embeddings[4]])[0][0])
```
- 執行結果(不符合預期)
    ```
    0.8163648453128984
    0.7871298243392368
    0.7662597128665629
    0.7992098894831066
    ```

<br>

### 範例：OpenAIEmbeddings
```
export OPENAI_API_KEY=sk-xxxxxxx
```
```python=
from langchain.embeddings import OpenAIEmbeddings
from sklearn.metrics.pairwise import cosine_similarity

# 初始化 OpenAIEmbeddings
openai_embeddings = OpenAIEmbeddings()  # 指定要使用的模型

# 範例文字
texts = [
    "洗錢防制法第3條為何？",
    "中華民國113年07月31日洗錢防制法第1條：「為防制洗錢，打擊犯罪，健全防制洗錢體系，穩定金融秩序，促進金流之透明，強化國際合作，特制定本法。」",
    "中華民國113年07月31日洗錢防制法第2條：「本法所稱洗錢，指下列行為：\n一、隱匿特定犯罪所得或掩飾其來源。\n二、妨礙或危害國家對於特定犯罪所得之調查、發現、保全、沒收或追徵。\n三、收受、持有或使用他人之特定犯罪所得。\n四、使用自己之特定犯罪所得與他人進行交易。」",
    "中華民國113年07月31日洗錢防制法第3條：「本法所稱特定犯罪，指下列各款之罪：\n一、最輕本刑為六月以上有期徒刑之罪。\n二、刑法第一百二十一條、第一百二十三條、第二百零一條之一第二項、第二百三十一條、第二百三十三條第一項、第二百三十五條第一項、第二項、第二百六十六條第一項、第二項、第二百六十八條、第三百十九條之一第二項、第三項及該二項之未遂犯、第三百十九條之三第四項而犯第一項及其未遂犯、第三百十九條之四第三項、第三百三十九條、第三百三十九條之二、第三百三十九條之三、第三百四十二條、第三百四十四條第一項、第三百四十九條、第三百五十八條至第三百六十二條之罪。\n三、懲治走私條例第二條第一項、第二項、第三條之罪。\n...」",
    "中華民國113年07月31日洗錢防制法第4條：「1.本法所稱特定犯罪所得，指犯第三條所列之特定犯罪而取得或變得之財物或財產上利益及其孳息。\n2.前項特定犯罪所得之認定，不以其所犯特定犯罪經有罪判決為必要。」",
]

# 產生向量（Embeddings）
embeddings = [openai_embeddings.embed_query(text) for text in texts]

# 計算並輸出餘弦相似度
print(cosine_similarity([embeddings[0]], [embeddings[1]])[0][0])
print(cosine_similarity([embeddings[0]], [embeddings[2]])[0][0])
print(cosine_similarity([embeddings[0]], [embeddings[3]])[0][0])
print(cosine_similarity([embeddings[0]], [embeddings[4]])[0][0])
```
- 執行結果(不符合預期)
    ```
    0.9025503902785641
    0.8959310803748316
    0.8717659264751052
    0.8837738629017888
    ```

<br>

<hr>

<br>

## vector store

<br>

<hr>

<br>

## prompt
### 如何查看填充過的 prompt
- 範例程式
    - get a retriever
        ```python=
        from langchain_openai import OpenAIEmbeddings
        from langchain.vectorstores import Chroma
        import os
        os.environ['OPENAI_API_KEY'] = 'sk-******'

        # get a retriever
        embeddings = OpenAIEmbeddings()
        db = Chroma.from_documents(texts, embeddings) # type(texts[0]) = langchain_core.documents.base.Document
        retriever = db.as_retriever()
        ```
    - 產生 context，然後帶入 prompt
        ```python=
        from langchain import PromptTemplate

        # Create Prompt
        template = """請根據底下背景知識，協助回答問題：
        ---
        {context}
        ---

        問題：{question}
        回答：
        """
        prompt = PromptTemplate.from_template(template)

        # generate context
        question = '帶著嬰兒通關有哪些好處？'
        retrieved_docs = retriever.get_relevant_documents(question)
        context = '\n\n'.join(doc.page_content for doc in retrieved_docs)
        
        # fill in the prompt based on the context and question
        filled_prompt = prompt.format(context=context, question=question)
        print(filled_prompt)
        ```
        執行結果：
        [![](https://hackmd.io/_uploads/BJ8kcTfTT.png)](https://hackmd.io/_uploads/BJ8kcTfTT.png)

<br>

<hr>

<br>

## model
### OpenAI, ChatOpenAI
- [Model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility)
    - /v1/chat/completions
        - gpt-4 and dated model releases, 
        - gpt-4-turbo-preview and dated model releases, 
        - gpt-4-vision-preview, 
        - gpt-4-32k and dated model releases, 
        - gpt-3.5-turbo and dated model releases, 
        - gpt-3.5-turbo-16k and dated model releases, fine-tuned versions of gpt-3.5-turbo

### Q&A
### Q&A - OpenAI vs ChatOpenAI
- [What is the difference between OpenAI and ChatOpenAI in LangChain?](https://stackoverflow.com/questions/76950609/)
    ```python
    from langchain.llms import OpenAI
    from langchain.chat_models import ChatOpenAI
    ```
    ```python
    from langchain_community.llms import OpenAI
    from langchain_community.chat_models import ChatOpenAI
    ```
- ### 當前經驗
    - 使用在 RAG 上，
        - ChatOpenAI 傾向根據「當前對話(背景知識 context) 」來回答
        - OpenAI 傾向根據「LLM 自身知識」來回答
    - OpenAI  vs  ChatOpenAI
endpoint 差異
OpenAI 使用 /v1/completions
ChatOpenAI 使用 /v1/chat/completions 
model 參數
OpenAI 僅能使用 not-chat model
ChatOpenAI 使用 chat model
model 的選擇，要跟 endpoint 對起來，才能正確使用
例如 OpenAI(model='gpt-4')
chat model + /v1/completions 就會有 error
This is a chat model and not supported in the v1/completions endpoint. Did you mean to use v1/chat/completions?

    - OpenAI 使用 `/v1/completions`
    - ChatOpenAI 使用 `/v1/chat/completions`
    - 官方資訊：[Model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility)
    - `OpenAI(model='gpt-4')`
        - `model`: chat model
        - `OpenAI()`: 支援 /v1/completions
        - 產生錯誤如下：
            'message': 'This is a chat model and not supported in the v1/completions endpoint. Did you mean to use v1/chat/completions?'
- ### 他人經驗
    - [LangChain入门 - ChatOpenAI与OpenAI究竟有何不同？](https://www.youtube.com/watch?v=PFNndzptv9M)
- ### 測試範例
    > [日本親子旅行自遊易~~GO GO GO](https://ctrl.chcg.gov.tw/main/files/32_1051004_301-生活分享-日本親子旅行自遊易~~GO%20GO%20GO-和美地政陳芳誼.pdf)
    ```python=
    from langchain.document_loaders import TextLoader
    loader = TextLoader('日本親子旅行.txt', encoding='utf8')
    document = loader.load()

    from langchain.text_splitter import CharacterTextSplitter
    text_splitter = CharacterTextSplitter(
        separator='\n\n', chunk_size=200, chunk_overlap=60)
    texts = text_splitter.split_documents(document)
    ```
    ```python=
    from langchain_openai import OpenAIEmbeddings
    from langchain.vectorstores import Chroma
    import os
    os.environ['OPENAI_API_KEY'] = 'sk-******'

    # get a retriever
    embeddings = OpenAIEmbeddings()
    db = Chroma.from_documents(texts, embeddings)
    retriever = db.as_retriever()
    ```
    ```python=
    from langchain import PromptTemplate

    # Create Prompt
    template = """請根據底下背景知識，協助回答問題：
    ---
    {context}
    ---

    問題：{question}
    回答：
    """
    prompt = PromptTemplate.from_template(template)
    ```
    ```python=
    from langchain.chains import RetrievalQA
    from langchain_community.llms import OpenAI

    #from langchain_community.chat_models import ChatOpenAI
    from langchain_openai import ChatOpenAI

    qa = RetrievalQA.from_chain_type(
        llm=ChatOpenAI(model='gpt-4'),
        chain_type='stuff', 
        retriever=retriever, 
        chain_type_kwargs={"prompt": prompt})
    qa.run('文中提到的大阪景點有哪些？')
    ```
- ### 比較差異
    - ### `OpenAI(model='gpt-3.5-turbo-instruct')` vs `OpenAI(model='gpt-4')`
        [![](https://hackmd.io/_uploads/HkAhZZQT6.png)](https://hackmd.io/_uploads/HkAhZZQT6.png)
    - ### `ChatOpenAI(model='gpt-3.5-turbo-instruct')` vs `OpenAI(model='gpt-4-turbo-preview')`
        [![](https://hackmd.io/_uploads/H1kj7ZmTp.png)](https://hackmd.io/_uploads/H1kj7ZmTp.png)



<br>

<hr>

<br>

## RAG 應用說明
- ### langchain 中的 chain_type='stuff' 是什麼意思
    ChatGPT
    在Langchain 中，`chain_type='stuff'` 指的是一種簡單直接的文檔鏈處理方式，這種方式將一系列文檔整合到一個提示（prompt）中，然後將這個提示傳遞給一個大型語言模型（LLM ）進行處理。 這種鏈類型被稱為“stuff documents chain”（文檔填充鏈），適用於處理的文檔數量較少且每個文檔都相對較小的情況。

    ### RAG中的應用
    在使用 RAG（Retrieval-Augmented Generation，檢索增強生成）模型時，==可以利用 LLM 產生的 embeddings 來提高檢索階段的效率和準確性。 具體來說，可以先使用 LLM 對需要檢索的內容（如知識庫、文件集合等）進行編碼，產生 embeddings，然後將使用者的查詢也透過相同的模型轉換為 embedding。 透過比較查詢 embedding 與文檔 embeddings 的相似度，可以檢索到最相關的文檔，再將這些文檔作為背景資訊輸入到 RAG 模型中，輔助生成階段的答案產生。==

    ### 商業場景應用
    RAG 模型因其能夠結合檢索到的具體資訊產生準確、豐富的回答，適用於多種商業場景：
    1. **知識密集問答系統**：對於需要存取大量背景資訊才能回答的複雜問題，RAG 模型可以提供更準確、詳細的答案。
    2. **內容創作與摘要**：RAG 可以幫助自動化內容創作過程，例如產生文章摘要或基於給定資料撰寫報告。
    3. **個人化推薦**：結合使用者查詢和檢索到的相關訊息，RAG 可以產生個人化的推薦內容或建議。
    4. **學術研究**：在需要快速查閱大量文獻資料以回答研究相關問題的場景中，RAG 可以加速資訊檢索和整合過程。
    
    總而言之，`chain_type='stuff'` 在 Langchain 中提供了一種基礎但有效的方式，將文件直接「填充」到模型的輸入中，為各種需要整合和處理多源資訊的應用場景提供支援。

- ### langchain 中的  `chain_type='stuff'` 是什麼意思？
    在LangChain中，`chain_type='stuff'`指的是一種文檔鏈（document chain），它的主要功能是將一系列文件整合進一個提示（prompt），然後將這個提示傳遞給大型語言模型（LLM） 進行處理。 這種鏈類型特別適合處理文檔較小且大多數呼叫中只傳遞少數幾個文檔的應用場景。

    具體到一個使用場景中，例如在進行問答任務時，`stuff`鏈類型可以將與查詢相關的一組文件整合成一個單一的輸入，然後查詢LLM以獲取答案。 這種方法透過直接「填充」（stuffing）相關文件到提示中，使得模型能夠考慮到所有提供的文件內容，以產生更準確和相關的答案。

    至於RAG（Retrieval-Augmented Generation）的應用，雖然`stuff`鏈類型在LangChain中主要用於處理文檔，但RAG的概念可以應用於多種商業場景，包括但不限於資訊檢索、問答系統、內容生成等領域 。 RAG透過結合檢索（從大量資料中找到最相關的資訊）和產生（基於檢索到的資訊產生答案或內容）的方式，能夠提高模型的效能和輸出的相關性。 例如，在建立一個問答系統時，可以先用檢索方法找到與使用者問題最相關的知識片段，然後基於這些片段產生答案。 ==這種方法在處理需要廣泛背景知識的複雜查詢時特別有效，如在法律、醫療或技術支援等領域提供專業諮詢和支援。==

    總的來說，`stuff`鏈類型在LangChain中提供了一種靈活的方式來整合和處理文檔，而RAG模型的概念則擴展了這種方法的應用範圍，使其能夠在更廣泛的商業和研究場景 中被利用。

<br>

<hr>

<br>

## 參考資料 / 教學資源 (待消化)
- ### [【LangChain】检索器(Retrievers)](https://blog.csdn.net/qq_56591814/article/details/135007557)
- ### [LangChain（0.0.340）官方文档十：Retrieval——Retrievers（检索器）](https://blog.csdn.net/qq_56591814/article/details/135007557)
- ### [以 OpenAI 以及 LangChain 實做我的聊天機器人 系列](https://ithelp.ithome.com.tw/users/20154415/ironman/6008)
- ### [LangChain Expression Language (LCEL)](https://python.langchain.com/v0.1/docs/expression_language/)