LangChain 提供了強大的函式呼叫功能,允許開發者整合外部函式,擴充套件大語言模型的應用範圍。其核心概念在於將函式註冊至 LangChain,並透過特定語法觸發模型執行對應函式,取得結構化結果。文章將進一步探討如何利用 Pydantic 模型定義資料結構,簡化資料提取流程,並結合 OpenAI 函式與 LangChain 鏈,實作更複雜的應用場景,例如會議排程與文章關鍵點提取。此外,LangChain 的查詢規劃機制能有效分解複雜任務,透過建立依賴關係圖,提升多步驟查詢的效率和可控性,文章將提供具體程式碼範例說明如何運用 Pydantic 模型定義查詢結構,並結合 LLM 生成查詢規劃,最終實作自動化任務執行。

函式呼叫的基本概念

函式呼叫的基本流程如下:

  1. 定義函式:首先,需要定義一個函式,這個函式可以是任何可以被呼叫的程式碼。
  2. 呼叫函式:然後,模型需要呼叫這個函式,並傳遞必要的引數。
  3. 處理函式回應:最後,模型需要處理函式的回應,並將結果傳回給使用者。

函式呼叫的實作

以下是函式呼叫的實作範例:

function = OPENAI_FUNCTIONS.get(function_name)
if not function:
    raise Exception(f"Function {function_name} not found.")

# 呼叫函式:
function_response = function(**function_args)

# 將函式的回應分享給模型:
messages.append(
    {
        "role": "function",
        "name": "schedule_meeting",
        "content": json.dumps(function_response),
    }
)

平行函式呼叫

LangChain 也支援平行函式呼叫,這意味著模型可以同時呼叫多個函式。這個功能可以用來提高效率和減少等待時間。

以下是平行函式呼叫的實作範例:

# 修改之前的程式碼,更新 messages 列表以強制安排兩次會議:
messages = [
    {
        "role": "user",
        "content": '''Schedule a meeting on 2023-11-01 at 14:00 with Alice and Bob. Then I want to schedule another meeting on 2023-11-02 at 15:00 with Charlie and Dave.'''
    }
]

# 調整之前的程式碼段:
model="gpt-3.5-turbo-1106",
messages=messages,
tools=functions,
)

response = response.choices[0].message

# 檢查模型是否想要呼叫我們的函式:
if response.tool_calls:
    for tool_call in response.tool_calls:
        # 取得函式名稱和引數以呼叫:

函式呼叫的重要注意事項

在使用函式呼叫時,需要注意以下幾點:

  • 可能有多個函式可以被呼叫,因此需要明確指定函式名稱和引數。
  • OpenAI 可能會 hallucinate 函式引數,因此需要在系統訊息中明確指定引數以避免錯誤。
  • 函式呼叫可以設定為強制呼叫特定函式或不呼叫任何函式。

進階文字生成技術:LangChain 中的函式呼叫

LangChain 提供了一種強大的方式來管理複雜的文字生成任務,尤其是在需要呼叫多個函式的情況下。這個章節將探討如何使用 LangChain 來實作函式呼叫,特別是在與 Pydantic 結合時的應用。

基本概念:函式呼叫

函式呼叫是 LangChain 中的一個重要功能,允許您在程式中呼叫特定的函式,並傳遞引數以取得所需的結果。這個過程可以簡化複雜的任務,例如排程會議或提取文章中的關鍵點。

排程會議:一個實際例子

以下是一個簡單的例子,展示瞭如何使用 LangChain 排程會議:

function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
print("這是函式名稱:", function_name)
print("這些是函式引數:", function_args)

function = OPENAI_FUNCTIONS.get(function_name)
if not function:
    raise Exception(f"函式 {function_name} 沒有找到。")

# 呼叫函式:
function_response = function(**function_args)

# 將函式的回應與模型分享:
messages.append(
    {
        "role": "function",
        "name": function_name,
        "content": json.dumps(function_response),
    }
)

在這個例子中,我們首先從 tool_call 物件中提取函式名稱和引數。然後,我們使用 OPENAI_FUNCTIONS 字典來取得函式物件,如果函式不存在,則丟擲一個異常。接下來,我們呼叫函式並傳遞引數,然後將函式的回應與模型分享。

使用 Pydantic 進行結構化資料提取

如果您想要避免撰寫 JSON 結構並簡單地從 LLM 回應中提取結構化資料,LangChain 提供了一種使用 Pydantic 的方法。以下是一個示範如何使用 Pydantic 定義一個 Article 類別,以提取文章中的關鍵點和相反觀點:

from langchain.output_parsers.openai_tools import PydanticToolsParser
from langchain_core.utils.function_calling import convert_to_openai_tool
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai.chat_models import ChatOpenAI
from langchain_core.pydantic_v1 import BaseModel, Field
from typing import Optional

class Article(BaseModel):
    """識別文章中的關鍵點和相反觀點。"""
    points: str = Field(..., description="文章中的關鍵點")
    contrarian_points: Optional[str] = Field(
        None, description="文章中承認的相反觀點"
    )
    author: str = Field(None, description="文章作者")

_EXTRACTION_TEMPLATE = """提取和儲存以下段落中提到的相關實體及其屬性。
如果一個屬性不存在且不需要在函式引數中,請不要在輸出中包含它。"""

# 建立一個提示,告訴 LLM 提取資訊:
prompt = ChatPromptTemplate.from_messages(
    {("system", _EXTRACTION_TEMPLATE), ("user", "{input}")}
)

在這個例子中,我們定義了一個 Article 類別,具有 pointscontrarian_pointsauthor 屬性。然後,我們建立了一個提示,告訴 LLM 提取文章中的關鍵點和相反觀點。

使用 LangChain 進行文字提取和資料結構化

LangChain 是一個強大的工具,允許您使用 OpenAI 模型進行文字提取和資料結構化。以下是使用 LangChain 進行文字提取和資料結構化的步驟:

步驟 1:定義 Pydantic 模型

首先,您需要定義一個 Pydantic 模型來指定您想要從文字中提取的資料結構。例如:

from pydantic import BaseModel

class Article(BaseModel):
    points: str
    contrarian_points: str
    author: str

步驟 2:轉換 Pydantic 模型為 OpenAI 工具

接下來,您需要將 Pydantic 模型轉換為 OpenAI 工具。這可以使用 convert_to_openai_tool 函式來完成:

tools = [convert_to_openai_tool(p) for p in [Article]]

步驟 3:繫結工具到 LLM

然後,您需要將工具繫結到 LLM(Large Language Model)上。這可以使用 bind_tools 方法來完成:

model = ChatOpenAI()
model = model.bind_tools(tools=tools)

步驟 4:建立 LCEL 鏈

接下來,您需要建立一個 LCEL(LangChain Execution Layer)鏈來執行文字提取任務。這可以使用 promptmodelPydanticToolsParser 來完成:

chain = prompt | model | PydanticToolsParser(tools=[Article])

步驟 5:呼叫鏈

最後,您可以呼叫鏈來執行文字提取任務:

result = chain.invoke({
    "input": "In the recent article titled 'AI adoption in industry,' key points addressed include the growing interest... However, the author, Dr. Jane Smith,..."
})

輸出結果

輸出結果將是一個 Pydantic 物件,包含提取的資料:

[Article(points='The growing interest in AI in various sectors,...', contrarian_points='Without stringent regulations,...', author='Dr. Jane Smith')]

使用 create_extraction_chain_pydantic 函式

您也可以使用 create_extraction_chain_pydantic 函式來建立一個更簡潔的 LCEL 鏈:

from langchain.chains.openai_tools import create_extraction_chain_pydantic

chain = create_extraction_chain_pydantic(Article, model)
result = chain.invoke({
    "input": "In the recent article titled 'AI adoption in industry,' key points addressed include the growing interest... However, the author, Dr. Jane Smith,..."
})

這將輸出相同的結果。

平行函式呼叫

您也可以使用 LangChain 進行平行函式呼叫。例如:

class Person(BaseModel):
    name: str
    age: int

chain = create_extraction_chain_pydantic(Person, model)
result = chain.invoke({
    "input": "Bob is 25 years old. He lives in New York. He likes to play basketball. Sarah is 30 years old. She lives in San Francisco. She likes to play tennis."
})

這將輸出兩個 Pydantic 物件,包含提取的資料:

[Person(name='Bob', age=25), Person(name='Sarah', age=30)]

查詢規劃

當使用者查詢具有多個意圖和複雜依賴關係時,可能會遇到問題。查詢規劃是一種有效的方法,可以將使用者的查詢解析為一系列具有相關依賴關係的步驟,形成一個查詢圖。

查詢規劃的實作

首先,我們需要定義查詢和查詢規劃的模型。以下是使用 Pydantic 定義的模型:

from pydantic import BaseModel, Field
from typing import List

class 查詢(BaseModel):
    id: int
    問題: str
    依賴關係: List[int] = Field(
        default_factory=list,
        description="""一個子查詢列表,必須在此任務之前完成。
        
        當不知道某些東西且可能需要問很多問題才能得到答案時,使用子查詢。
        依賴關係只能是其他查詢。"""
    )

class 查詢規劃(BaseModel):
    查詢圖: List[查詢]

定義了查詢和查詢規劃模型後,我們可以使用 LLM 來解析使用者的查詢,並生成查詢規劃。以下是使用 LangChain 的示例:

from langchain_openai.chat_models import ChatOpenAI
from langchain.output_parsers.pydantic import PydanticOutputParser

# 設定聊天模型
模型 = ChatOpenAI()

# 設定解析器
解析器 = PydanticOutputParser(pydantic_object=查詢規劃)

# 定義提示範本
範本 = """生成查詢規劃。這將用於任務執行。

回答以下查詢:{查詢}
傳回以下查詢圖格式:

{格式指示}

"""
系統訊息提示 = SystemMessagePromptTemplate.from_template(範本)
聊天提示 = ChatPromptTemplate.from_messages([系統訊息提示])

# 建立 LCEL鏈
 = 聊天提示 | 模型 | 解析器

# 執行查詢
結果 = .invoke({
    "查詢":'''我想從我的資料函式庫中取得結果。然後我想知道我的前10位客戶的平均年齡是多少。一旦我有了平均年齡,我想傳送電子郵件給John。另外,我只是想傳送歡迎介紹電子郵件給Sarah,無論其他任務如何。''',
    "格式指示":解析器.get_format_instructions()
})

查詢規劃的優點

查詢規劃可以幫助我們將複雜的查詢分解為簡單的步驟,並且可以自動化查詢的執行。此外,查詢規劃還可以幫助我們最佳化查詢的效能和安全性。

內容解密:

在上面的程式碼中,我們使用了 LangChain 和 Pydantic 來定義查詢和查詢規劃模型。然後,我們使用 LLM 來解析使用者的查詢,並生成查詢規劃。最後,我們使用 PydanticOutputParser 來解析查詢規劃的結果。

圖表翻譯:

以下是查詢規劃的流程圖:

  flowchart TD
    A[使用者查詢] --> B[LLM解析]
    B --> C[查詢規劃]
    C --> D[查詢圖]
    D --> E[任務執行]

在這個流程圖中,首先使用者提交查詢,然後 LLM 對查詢進行解析,生成查詢規劃。然後,查詢規劃被轉換為查詢圖,最後,任務被執行。

從技術架構視角來看,LangChain 的函式呼叫機制為複雜的文字生成任務提供了優雅的解決方案。藉由將外部函式整合至 LLM 工作流程,LangChain 不僅提升了模型處理結構化資料和執行特定操作的能力,更有效地降低了 LLM 產生幻覺的風險。分析其核心機制,可以發現 Pydantic 的整合扮演了關鍵角色,它確保了資料交換的型別安全和結構化,簡化了開發流程並提升了程式碼可讀性。然而,目前函式呼叫的效率仍受限於 LLM 的推論速度和外部函式本身的執行效率。展望未來,隨著 LLM 效能的提升和非同步函式呼叫的更廣泛應用,預期 LangChain 的函式呼叫機制將在更複雜的應用場景中發揮更大的作用。對於追求高效能和高度自動化的開發團隊而言,深入理解並應用 LangChain 的函式呼叫機制將是提升生產力的關鍵策略。