LangChain框架的出現,有效解決了開發者在構建複雜生成式AI應用時所面臨的諸多挑戰。它提供了一套模組化的抽象概念和工具,涵蓋了與大語言模型(LLM)互動所需的關鍵功能,包含模型輸入輸出、檢索、鏈、代理、記憶體和回撥等。這些模組可以獨立使用,也可以靈活組合,以滿足不同應用場景的需求。LangChain支援Python和TypeScript兩種程式語言,並與多種LLM供應商相容,降低了開發門檻,提升了開發效率。

LangChain:進階文字生成技術的核心框架

隨著生成式AI技術的快速發展,簡單的提示工程技術已不足以應對複雜的AI任務。為瞭解決這些挑戰,LangChain應運而生,成為開發者手中的強大工具。本文將探討LangChain的核心概念、主要功能模組以及環境設定方法。

LangChain簡介

LangChain是一個開源框架,支援Python和TypeScript兩種程式語言。它旨在建立更強大、更具互動性的LLM(大語言模型)應用程式。該框架的核心目標包括:

  1. 增強資料感知能力:建立語言模型與外部資料來源之間的無縫連線
  2. 提升代理能力:賦予語言模型與環境互動並產生影響的能力

LangChain的主要功能模組

LangChain提供了一系列模組化的抽象概念,涵蓋了與LLM互動所需的關鍵功能。目前主要包含六大核心模組:

1. Model I/O(模型輸入/輸出)

負責處理與語言模型相關的輸入輸出操作,是與LLM互動的基礎。

2. Retrieval(檢索)

專注於為LLM檢索相關文字,提升模型的資訊取得能力。

3. Chains(鏈)

也稱為LangChain可執行單元,允許構建一系列的LLM操作或函式呼叫,實作複雜任務的自動化處理。

4. Agents(代理)

使鏈能夠根據高層指令或指示決定使用哪些工具,大幅提升了系統的自主決策能力。

5. Memory(記憶)

負責在不同執行之間保持應用程式的狀態,確保連續性和上下文理解。

6. Callbacks(回撥)

允許在特定事件(如生成新token時)執行額外的程式碼,增強了系統的靈活性。

環境設定與安裝

要在本地環境中使用LangChain,需要完成以下步驟:

  1. 安裝LangChain

    • 使用pip:pip install langchain langchain-openai
    • 使用conda:conda install -c conda-forge langchain langchain-openai
  2. 設定虛擬環境(建議)

    # 建立虛擬環境
    python -m venv venv
    
    # 啟動虛擬環境
    source venv/bin/activate
    
    # 安裝依賴套件
    pip install -r requirements.txt
    
  3. 組態模型提供者

    • 例如使用OpenAI模型,需要安裝openai套件:pip install openai
    • 設定環境變數OPENAI_API_KEY或在程式碼中直接傳入API金鑰
  4. 初始化ChatOpenAI模型

    from langchain_openai.chat_models import ChatOpenAI
    
    # 直接傳入API金鑰(適用於原型開發)
    chat = ChatOpenAI(api_key="your_api_key_here")
    

LangChain的優勢與應用場景

LangChain為開發複雜的生成式AI應用提供了強大的支援。其主要優勢包括:

  1. 模組化設計:各功能模組可獨立使用或組合,靈活性高
  2. 增強LLM能力:透過與外部資料的連線和代理功能的實作,大幅拓展了LLM的應用範圍
  3. 簡化開發流程:提供了豐富的抽象層和實作,大大降低了開發複雜AI應用的門檻

透過LangChain,開發者可以更輕鬆地構建諸如:

  • 長篇內容摘要
  • 具有角色、情節和世界觀的故事生成
  • 複雜推理任務
  • 具備自主決策能力的AI代理

總之,LangChain代表了當前LLM應用開發的前沿技術,為開發者開啟了通往更複雜、更智慧AI系統的大門。隨著持續的發展和最佳化,LangChain有望在未來的AI應用開發中扮演越來越重要的角色。

內容解密:

本章節介紹了LangChain的基本概念、主要功能模組以及環境設定方法。透過詳細的程式碼範例和實作步驟,讀者可以快速掌握如何在自己的專案中使用LangChain來構建更強大的LLM應用。特別強調了該框架在增強資料感知和代理能力方面的優勢,以及在複雜生成式AI任務中的應用潛力。

隨著生成式AI技術的不斷進步,LangChain作為一個開源框架,將持續演進以滿足日益增長的複雜應用需求。未來可期待的功能包括但不限於:

  • 更多模型供應商的整合
  • 更強大的代理決策能力
  • 更豐富的記憶體管理機制
  • 更完善的多模態支援

這些發展將進一步鞏固LangChain在LLM應用開發領域的領先地位,為開發者提供更多創新的可能性。

內容解密:

本段落討論了LangChain未來的發展方向和可能的功能擴充套件。透過分析該框架的發展趨勢,可以預見其在未來將如何持續影響和塑造LLM應用的開發模式,為開發者帶來更多創新的機會和挑戰。

使用 LangChain 進行高效的文字生成與互動式聊天模型

在現代人工智慧(AI)與大語言模型(LLM)的發展中,如何有效地管理和使用 API 金鑰(API keys)成為了一項重要的安全課題。直接在指令碼中硬編碼(hardcoding)API 金鑰並不是一個好的做法,因為這會帶來潛在的安全風險。相反,使用環境變數或設定檔來管理金鑰是一種更為安全和靈活的方法。

LangChain:統一不同模型 API 的框架

在 LLM 的應用中,不同模型之間的 API 介面差異是一個常見的挑戰。這種缺乏標準化的情況會增加提示工程(prompt engineering)的複雜度,並阻礙不同模型之間的無縫整合。而 LangChain 的出現,正是為瞭解決這一問題。

LangChain 提供了一個全面的框架,能夠簡化不同模型之間的介面差異。透過 LangChain,開發者無需針對不同的模型重寫提示或程式碼,從而大大節省了開發時間和資源。LangChain 的平台無關性(platform-agnostic)設計,使其能夠支援多種模型,包括 Anthropic、Vertex AI、OpenAI 和 BedrockChat 等。

聊天模型(Chat Models)

聊天模型,如 GPT-4,已經成為與 OpenAI API 互動的主要方式。與傳統的「輸入文字,輸出文字」的模式不同,聊天模型採用了一種根據訊息(message-based)的互動方式。在 LangChain 中,目前支援的訊息型別包括 AIMessageHumanMessageSystemMessage

訊息型別詳解

  1. SystemMessage:代表提供給 AI 系統的指令,用於引導 AI 的行為或動作。
  2. HumanMessage:代表人類使用者與 AI 系統互動的資訊,如問題或指令。
  3. AIMessage:代表 AI 系統本身的回應,通常是對 HumanMessage 的回應或 SystemMessage 指令的結果。

使用 LangChain 建立一個笑話生成器

from langchain_openai.chat_models import ChatOpenAI
from langchain.schema import AIMessage, HumanMessage, SystemMessage

# 初始化 ChatOpenAI 例項,設定溫度引數為 0.5
chat = ChatOpenAI(temperature=0.5)

# 定義訊息列表
messages = [
    SystemMessage(content='''扮演一家新創公司的資深軟體工程師。'''),
    HumanMessage(content='''請提供一個關於軟體工程師的有趣笑話。''')
]

# 呼叫聊天模型並取得回應
response = chat.invoke(input=messages)
print(response.content)

輸出範例

當然,這裡有個輕鬆的笑話:
為什麼軟體工程師會破產?
因為他在賭博中輸掉了他的網域名稱,無法負擔續約費用。

程式碼解析

  1. 首先,匯入必要的模組,包括 ChatOpenAIAIMessageHumanMessageSystemMessage
  2. 建立一個 ChatOpenAI 例項,並設定 temperature 引數為 0.5,以控制輸出的隨機性。
  3. 建立一個訊息列表 messages,其中包含一個 SystemMessage 和一個 HumanMessage
  4. 使用 .invoke() 方法呼叫聊天模型,並傳入 messages 列表。
  5. 列印出模型的回應內容。

串流聊天模型(Streaming Chat Models)

在使用 ChatGPT 等聊天模型時,我們經常會觀察到文字逐字逐句地傳回給使用者。這種模式被稱為串流(streaming),它能夠顯著提升聊天應用程式的效能。

for chunk in chat.stream(messages):
    print(chunk.content, end="", flush=True)

串流的優勢與挑戰

串流的主要優勢在於它能夠減少使用者的等待時間,並提升互動體驗。然而,這種技術也帶來了一些挑戰,例如在處理串流輸出的過程中,需要即時解析和回應正在生成的訊息內容。

生成多個 LLM 回應

在某些場景下,開發者可能需要生成多個 LLM 回應,例如在創作社交媒體帖子時。LangChain 的 .batch() 方法允許開發者平行傳送多個 API 請求,從而提高效率。

# 建立兩個相同的訊息列表
synchronous_llm_result = chat.batch([messages]*2)
print(synchronous_llm_result)

輸出範例

[AIMessage(content='''當然,這裡有個輕鬆的笑話:
為什麼軟體工程師會破產?
因為他一直忘記 Ctrl+Z 他的開支!'''),
 AIMessage(content='''當然,這裡有個輕鬆的笑話:
為什麼軟體工程師偏愛暗黑模式?
因為這樣對他們的「位元」視力比較友善!''')]

LangChain 中的非同步處理與批次處理最佳化

在現代軟體開發中,特別是在涉及大量 API 請求或複雜工作流程的應用程式中,效能最佳化是至關重要的。LangChain 提供了一系列工具和技術來提升應用程式的效能,特別是在批次處理和非同步處理方面。

使用 RunnableConfig 進行批次處理最佳化

LangChain 中的 Runnable 物件提供了 batch 方法,用於平行處理多個輸入。透過 RunnableConfig,開發者可以精細控制批次處理的行為,特別是並發限制。

from langchain_core.runnables.config import RunnableConfig

# 建立具有所需並發限制的 RunnableConfig
config = RunnableConfig(max_concurrency=5)

# 使用 batch 方法並傳入 config
results = chat.batch([messages, messages], config=config)

內容解密:

  1. RunnableConfig 允許開發者設定 max_concurrency 引數,控制同時執行的任務數量。
  2. 適當的並發限制可以避免資源耗盡,同時最大化系統吞吐量。
  3. 在處理大量 API 請求時,這種控制尤為重要,可以防止觸發伺服器端的速率限制。

非同步函式在 LangChain 中的應用

LangChain 支援非同步(async)函式,使得多個 API 請求可以平行執行,大幅降低整體延遲。

# 使用非同步版本的 invoke 和 batch 方法
results = await chat.ainvoke(messages)
results = await chat.abatch([messages, messages])

內容解密:

  1. 非同步函式以 a 字首命名,如 ainvokeabatch
  2. 這些函式允許平行執行多個操作,而不會阻塞主執行緒。
  3. 在複雜的工作流程中,非同步處理可以顯著提升效能和使用者經驗。

LangChain Prompt Templates 的重要性

隨著 LLM 應用程式規模的擴大,使用 Prompt Templates 成為最佳實踐。這些範本提供了可重複使用的提示結構,並支援引數化。

from langchain_openai.chat_models import ChatOpenAI
from langchain_core.prompts import SystemMessagePromptTemplate, ChatPromptTemplate

template = """
你是一個創意顧問,為企業創作名稱。
請遵循以下原則:
{principles}
請為 {industry} 產業中涉及 {context} 的新創企業生成五個吸引人的名稱。
"""

# 建立 Prompt Template
system_prompt = SystemMessagePromptTemplate.from_template(template)
chat_prompt = ChatPromptTemplate.from_messages([system_prompt])

# 建立 LCEL 鏈
chain = chat_prompt | model
result = chain.invoke({
    "industry": "醫療",
    "context": "自動總結病歷",
    "principles": "1. 簡短易記\n2. 易於發音\n3. 獨特不重複"
})

內容解密:

  1. Prompt Templates 允許開發者定義可重複使用的提示結構。
  2. 支援引數化,使得同一個範本可以應用於不同的場景。
  3. 提供了輸入驗證、多提示組合等進階功能。
  4. 可以儲存和載入提示範本,方便版本控制和團隊協作。

LangChain Expression Language (LCEL) 的管道運算子

LCEL 是 LangChain 的核心組成部分,它使用管道運算子 | 將不同的元件連線起來,形成資料處理流程。

# 建立 LCEL 鏈
chain = chat_prompt | model

# 呼叫鏈
result = chain.invoke(input_data)

內容解密:

  1. 管道運算子 | 將前一個元件的輸出作為下一個元件的輸入。
  2. 這種設計使得構建複雜的工作流程變得簡單直觀。
  3. 元件的順序非常重要,錯誤的順序可能導致執行錯誤。

商業名稱生成器的實作範例

讓我們建立一個使用 Prompt Template 的商業名稱生成器:

# 定義完整的商業名稱生成器流程
def generate_business_names(industry, context, principles):
    # 建立 LCEL 鏈
    chain = chat_prompt | model
    
    # 呼叫鏈並傳入引數
    result = chain.invoke({
        "industry": industry,
        "context": context,
        "principles": principles
    })
    
    return result.content

# 使用範例
industry = "醫療"
context = "建立 AI 解決方案以自動總結病歷"
principles = "1. 每個名稱應簡短易記\n2. 易於發音\n3. 獨特不重複"

names = generate_business_names(industry, context, principles)
print(names)

內容解密:

  1. 這個範例展示瞭如何將 Prompt Template 和 LCEL 結合使用。
  2. 透過引數化提示範本,可以為不同的產業和情境生成合適的商業名稱。
  3. 輸出的格式是經過精心設計的數值列表,便於閱讀和後續處理。

最佳實踐:提供明確的指示和格式規範

在與 LLM 互動時,提供明確的指示和格式規範至關重要。這有助於模型理解任務要求並產生符合預期的輸出。

# 良好的提示範例
"請生成五到七個 {industry} 產業中涉及 {context} 的新創企業名稱,並遵循以下原則:{principles}"

內容解密:

  1. 明確指定任務目標(生成企業名稱)。
  2. 提供具體的格式要求(數值列表)。
  3. 給出評估標準(吸引人、符合產業特點等)。

使用 PromptTemplate 與聊天模型的高階技術

LangChain 提供了一個稱為 PromptTemplate 的傳統範本,需要 input_variablestemplate 引數。這種範本適用於需要結構化輸入的場景,能夠有效地組織和管理提示內容。

PromptTemplate 的基本用法

首先,我們來看看如何使用 PromptTemplate 建立一個系統訊息範本:

from langchain_core.prompts import PromptTemplate
from langchain.prompts.chat import SystemMessagePromptTemplate
from langchain_openai.chat_models import ChatOpenAI

# 定義 PromptTemplate
prompt = PromptTemplate(
    template='''You are a helpful assistant that translates {input_language} to {output_language}.''',
    input_variables=["input_language", "output_language"],
)

# 建立 SystemMessagePromptTemplate
system_message_prompt = SystemMessagePromptTemplate(prompt=prompt)

# 初始化 ChatOpenAI 模型
chat = ChatOpenAI()

# 呼叫模型並傳入格式化後的訊息
result = chat.invoke(system_message_prompt.format_messages(
    input_language="English", output_language="French"
))

print(result)

輸出結果:

AIMessage(content="Vous êtes un assistant utile qui traduit l'anglais en français.", additional_kwargs={}, example=False)

內容解密:

  1. 我們首先從 langchain_core.prompts 匯入 PromptTemplate,用於建立結構化的提示範本。
  2. 使用 PromptTemplate 定義了一個翻譯助手的範本,包含 input_languageoutput_language 兩個變數。
  3. 透過 SystemMessagePromptTemplatePromptTemplate 包裝成系統訊息範本。
  4. 初始化 ChatOpenAI 模型並呼叫 invoke 方法,傳入格式化後的系統訊息。
  5. 最終輸出結果為法語翻譯的內容。

輸出解析器(Output Parsers)的高階應用

在 LangChain 中,輸出解析器(Output Parsers)提供了一種高層次的抽象,用於從大語言模型(LLM)的字串輸出中解析結構化資料。目前可用的輸出解析器包括:

  • 清單解析器(List Parser)
  • 日期時間解析器(Datetime Parser)
  • 列舉解析器(Enum Parser)
  • 自動修復解析器(Auto-fixing Parser)
  • Pydantic(JSON)解析器
  • 重試解析器(Retry Parser)
  • 結構化輸出解析器(Structured Output Parser)
  • XML 解析器

這些解析器提供了兩個重要的功能:

  1. .get_format_instructions():提供必要的指令以輸出可被解析的結構化格式。
  2. .parse(llm_output: str):負責將 LLM 的輸出解析為預定義的格式。

使用 Pydantic(JSON)解析器的範例

Pydantic 解析器利用了 Python 中的 Pydantic 函式庫,提供了根據 Python 型別註解的資料驗證功能。

from langchain_core.prompts.chat import (
    ChatPromptTemplate,
    SystemMessagePromptTemplate,
)
from langchain_openai.chat_models import ChatOpenAI
from langchain.output_parsers import PydanticOutputParser
from pydantic.v1 import BaseModel, Field
from typing import List

# 定義資料模型
class BusinessName(BaseModel):
    name: str = Field(description="The name of the business")
    rating_score: float = Field(description='''The rating score of the business. 0 is the worst, 10 is the best.''')

class BusinessNames(BaseModel):
    names: List[BusinessName] = Field(description='''A list of business names''')

# 初始化 PydanticOutputParser
parser = PydanticOutputParser(pydantic_object=BusinessNames)

# 定義生成商業名稱的原則
principles = """
- The name must be easy to remember.
- Use the {industry} industry and Company context to create an effective name.
- The name must be easy to pronounce.
- You must only return the name without any other text or characters.
- Avoid returning full stops, \n, or any other characters.
- The maximum length of the name must be 10 characters.
"""

# 定義聊天範本
template = """Generate five business names for a new start-up company in the {industry} industry.
You must follow the following principles: {principles}
{format_instructions}
"""

# 建立 SystemMessagePromptTemplate 和 ChatPromptTemplate
system_message_prompt = SystemMessagePromptTemplate.from_template(template)
chat_prompt = ChatPromptTemplate.from_messages([system_message_prompt])

# 初始化 ChatOpenAI 模型並建立 LCEL 鏈
model = ChatOpenAI(temperature=0.0)
prompt_and_model = chat_prompt | model

# 呼叫模型並解析輸出
result = prompt_and_model.invoke(
    {
        "principles": principles,
        "industry": "Data Science",
        "format_instructions": parser.get_format_instructions(),
    }
)

# 解析輸出結果
parsed_result = parser.parse(result.content)
print(parsed_result)

輸出結果:

names=[BusinessName(name='DataWiz', rating_score=8.5), BusinessName(name='InsightIQ', rating_score=9.2), BusinessName(name='AnalytiQ', rating_score=7.8), BusinessName(name='SciData', rating_score=8.1), BusinessName(name='InfoMax', rating_score=9.5)]

內容解密:

  1. 我們定義了兩個 Pydantic 模型:BusinessNameBusinessNames,用於結構化商業名稱及其評分。
  2. 使用 PydanticOutputParser 初始化一個解析器,將輸出解析為 BusinessNames 物件。
  3. 定義了一個生成商業名稱的聊天範本,包含行業、原則和格式指令等變數。
  4. 建立了一個 LCEL 鏈,將聊天提示、模型呼叫和輸出解析串聯起來。
  5. 呼叫模型並傳入格式化後的輸入,最終解析輸出結果為結構化的商業名稱列表。

將輸出解析器整合到 LCEL 鏈中

我們可以直接將輸出解析器新增到 LCEL 鏈中,簡化呼叫流程:

chain = chat_prompt | model | parser
result = chain.invoke(
    {
        "principles": principles,
        "industry": "Data Science",
        "format_instructions": parser.get_format_instructions(),
    }
)
print(result)

輸出結果:

names=[BusinessName(name='DataTech', rating_score=9.5), ...]

內容解密:

  1. 將輸出解析器直接整合到 LCEL 鏈中,實作端對端的處理流程。
  2. 鏈條負責提示格式化、模型呼叫和輸出解析,大幅簡化了程式碼邏輯。