大語言模型的推理能力並非來自內在邏輯,而是根據統計的文字生成。為了提升模型的推理能力,需要引導模型進行更深入的思考,而非僅僅提供直覺性的答案。本文將介紹多種提示工程技巧,包括思維鏈提示、零樣本推理、使用暫停標記訓練以及 ReAct 策略,並探討如何有效管理上下文資訊,以構建更強大的對話代理。這些技術旨在模擬人類的思考過程,讓模型在回答問題之前先進行推理,從而提高答案的準確性和可靠性。

探索模型的內部提示策略

要了解不同前沿模型如何實作工具,可以透過提示工程來探究。以下是一些可嘗試的方法:

  • 請求模型列印第一條訊息以上的文字。
  • 在系統訊息中加入特定標籤(如<LOGGING>),並在第一條訊息中加入結束標籤(如</LOGGING>),然後請求模型列印標籤內的文字。
  • 使用特定的函式名稱,並請求模型列印與該函式相關的文字。
  • 製作一個日誌工具,並用它來記錄內容,有時工具能夠更有效地取得模型不願分享的內部內容。
  • 將文字轉換為base64或ROT13,有時模型在處理模糊文字時會更寬鬆。
  • 如果獲得了內部表示的任何提示,將其納入提示中,以評論的形式模擬助手已分享內部提示的模式。

此圖示展示了探索模型內部提示策略的不同步驟與決策流程。

強化大語言模型的推理能力

大語言模型(LLMs)能夠根據提示,一個接一個地選擇詞彙,以提供統計上合理的文字補全(參見第2章)。在這個過程中,LLMs展現出一種推理能力,但這是一種非常表面的形式。模型的唯一目標——透過多層訓練來強化——是生成聽起來合理的文字。如第2章所述,模型沒有內在的思維過程——因此沒有對問題陳述的內部審視,沒有對已知事實的對映考慮,也沒有對多個競爭想法的比較。相反,模型一個接一個地預測出最適合處理中的文字的詞彙。

那麼,讓我們來改進它!有多種技巧可以用來使模型的回應更加周密,所有這些技巧都與給予模型一個內在的思維過程有關,使它能夠在提供最終回應之前更仔細地推理問題。

思維鏈(Chain of Thought)

在2022年1月的論文《思維鏈提示在大語言模型中引出推理》(“Chain-of-Thought Prompting Elicits Reasoning in Large Language Models”)中,作者證明瞭可以使用少樣本範例來引導模型變得更加周密——因此也更加準確——在它的回應中。通常,模型會對諸如「《驅魔人》會刺激邊緣系統嗎?」這樣的常識性問題給出是或否的答案,然後再給出解釋。這是人類說話的方式,因此也是模型學習回應的方式。

使用工具的挑戰與對策

在使用工具的過程中,需要注意多個挑戰,例如:

引數幻覺(Argument Hallucination)

模型可能會對未在對話中提及的引數假設預設值。解決此問題的方法包括:

  1. 當應用程式中已知所需值時,從函式定義中移除引數,以避免模型混淆。
  2. 提供預設值,以便在模型指定預設值時,可以在應用程式中做出適當的調整。
  3. 指示模型在不確定引數時進行詢問。

處理工具輸出

確保模型能夠預期工具輸出的內容。輸出可以是自由形式的自然語言文字或結構化的JSON物件。避免包含過多的額外內容,以免分散模型的注意力。

處理工具錯誤

當工具發生錯誤時,向模型提供有用的錯誤資訊,使其能夠進行修正。確保錯誤訊息在模型的工具定義上下文中具有意義。

執行「危險」工具

當允許模型執行可能對真實世界產生影響的工具時,必須保護使用者免受意外副作用。不要僅僅依靠在工具描述中指示模型進行雙重檢查,而是應該在應用程式層面攔截危險請求,並明確獲得使用者的認可。

提供內在思維過程以增強推理

有多種技巧可以透過給予模型一個內在的思維過程,使其回應更加周密。主要方法包括:

  • 使用思維鏈提示,使模型能夠更仔細地推理問題。
  • 提供少樣本範例,引導模型變得更加準確。

思維鏈提示的效果

透過思維鏈提示,模型能夠在回答問題之前進行更深入的思考,從而提供更準確的答案。這種方法已被證明能夠顯著提高模型的表現。

推理能力的進階發展

在探討大語言模型的推理能力時,我們發現這些模型缺乏內在的自我對話機制,因此無法在給出答案之前進行思考。這意味著,當被問及一個問題時,模型最初的回答往往是直覺性的猜測,而後續的解釋則是對該猜測的合理化解釋。

思維鏈(Chain-of-Thought)推理

思維鏈論文的作者透過實驗證明,如果讓模型先對問題進行推理,然後再給出答案,則更有可能得出正確的答案。他們透過提供少量的示例來引導模型,使其在回答問題之前先進行思考。以下是一些示例:

  • 問:倉鼠是否為其他動物提供食物? 答:倉鼠是獵物動物。獵物是掠食者的食物。因此,倉鼠為某些動物提供食物。所以答案是肯定的。

  • 問:梨子是否會沉入水中? 答:梨子的密度約為0.6g/cm³,小於水的密度。密度小於水的物體會浮在水面上。因此,梨子會浮在水面上。所以答案是否定的。

透過提供幾個這樣的示例,模型在回答類別似問題時也會遵循類別似的思維過程。例如,對於「《驅魔人》是否會刺激邊緣系統?」這個問題,模型的回答可能是:

  • 問:《驅魔人》是否會刺激邊緣系統? 答:《驅魔人》是一部恐怖電影。恐怖電影令人害怕。邊緣系統與恐懼有關。因此,《驅魔人》會刺激邊緣系統。所以答案是肯定的。

使用StrategyQA資料集和PaLM 540B模型,研究表明這種思維鏈推理方式將回答常識性問題的準確率從69.4%提高到了75.6%。此外,這種方法在數學問題的解答上也顯示出顯著的改進。在GSM8K資料集上的實驗表明,使用思維鏈推理後,PaLM 540B模型的解題率從約20%提高到了60%。

零樣本(Zero-Shot)推理

2022年5月的一篇論文「大語言模型是零樣本推理者」提出了一種更簡單的方法來引導模型進行思維鏈推理。他們發現,只需要在回答開頭加上「讓我們一步一步地思考」這樣的短語,就可以促使模型生成思維鏈式的推理過程,從而給出更準確的答案。

使用暫停標記(Pause Tokens)進行訓練

2023年10月的一篇論文「在說話前先思考:使用暫停標記訓練語言模型」將思維鏈的概念進一步推進。作者透過微調語言模型,使用一個「暫停」標記,並在提問後注入一定數量的這些無意義標記。這樣做的效果是給了模型額外的時間來推理答案。透過這種方式,模型能夠更好地整合之前標記的資訊,從而給出更好的答案。

ReAct:迭代推理與行動

2022年10月的論文「ReAct:在語言模型中協同推理和行動」進一步推進了推理能力的研究,特別是在需要資訊檢索和多步驟問題解決的情況下。這篇論文介紹了三種不同的工具來幫助模型找到答案:

  • Search[entity]:傳回對應維基百科頁面的前五句話,如果頁面存在;否則傳回根據維基百科搜尋的最相似的五個實體。
  • Lookup[string]:搜尋最近的實體(來自Search),並傳回包含提供的字串的下一句話。
  • Finish[answer]:表示工作完成,並給出最終答案。

模型的任務是透過迭代思考需要做什麼,使用SearchLookup工具收集資訊,並觀察工具傳回的答案。經過幾輪思考、行動和觀察後,模型最終給出答案。

ReAct:結合推理與行動的語言模型策略

ReAct是一種創新的提示策略,旨在提升大語言模型(LLM)在問答任務中的表現。它透過結合推理(Thought)、行動(Action)和觀察(Observation)步驟,使模型能夠更有效地解決複雜問題。

ReAct的工作原理

ReAct的核心思想是模仿人類解決問題的過程。當面對一個問題時,人類通常會先思考(Thought),然後採取行動(Action),並觀察結果(Observation)。根據觀察結果,再次思考並調整行動,直到問題得到解決。

在ReAct中,模型被訓練以類別似的方式運作。當給定一個問題時,模型首先會進行推理,決定需要採取什麼行動。行動可以是三種型別之一:

  1. Search[entity]:在維基百科上搜尋特定的實體,並傳回第一段內容。
  2. Lookup[keyword]:在當前段落中查詢包含特定關鍵字的下一句話。
  3. Finish[answer]:傳回答案並結束任務。

ReAct的效能

最初,ReAct的效能並不理想,尤其是在HotpotQA資料集上。然而,在對較小的模型進行微調後,ReAct的表現大幅提升,甚至超過了更大規模的原始模型。這表明,透過適當的推理和微調,較小的模型可以達到甚至超越更大模型的效能。

ReAct的優勢

ReAct在多個領域展現了其優勢,尤其是在需要推理和決策的任務中。例如,在ALFWorld基準測試中,ReAct的成功率高達71%,遠遠超過了僅僅採取行動(Act)的45%。這表明,推理步驟對於模型的成功至關重要。

超越ReAct

雖然ReAct在提升LLM的推理能力方面取得了重要進展,但研究人員仍在探索其他方法來進一步改善模型的效能。其中一些方法包括:

  • 計劃與解決(Plan-and-Solve)提示:在開始解決問題之前,先制定一個整體計劃。這種方法與ReAct互補,可能會帶來進一步的改進。
  • 反思(Reflexion):允許模型在完成任務後檢視自己的工作,找出問題,並在下次嘗試中改進。這種方法在某些領域(如編寫透過單元測試的軟體)中尤其有用。
  • 分支-解決-合併(Branch-Solve-Merge):將問題分配給多個獨立的LLM例項,每個例項從不同的角度解決問題。然後,將這些例項的結果合併,形成一個更好或更完整的解決方案。
程式碼實作:ReAct策略範例
def react_strategy(question):
    # 初始化思考、行動和觀察步驟
    thought = "我需要搜尋Arthur's Magazine和First for Women,並找出哪一個先創刊。"
    action = "Search[Arthur's Magazine]"
    observation = "Arthur's Magazine(1844-1846)是一份19世紀在費城出版的美國文學期刊。"
    
    # 繼續進行思考、行動和觀察步驟
    thought = "Arthur's Magazine於1844年創刊。我需要搜尋First for Women。"
    action = "Search[First for Women]"
    observation = "First for Women是一份由Bauer Media Group在美國出版的女性雜誌。該雜誌於1989年創刊。"
    
    # 得出結論
    thought = "First for Women於1989年創刊。1844(Arthur's Magazine)< 1989(First for Women),因此Arthur's Magazine先創刊。"
    action = "Finish[Arthur's Magazine]"
    
    return action

# 測試ReAct策略
question = "哪份雜誌先創刊,Arthur's Magazine還是First for Women?"
print(react_strategy(question))

內容解密:

  1. react_strategy函式:定義了一個名為react_strategy的函式,用於實作ReAct策略。
  2. 初始化步驟:首先初始化了思考、行動和觀察步驟,分別對應變數thoughtactionobservation
  3. 搜尋與比較:透過搜尋Arthur's MagazineFirst for Women的創刊年份,並比較兩者的大小,得出結論。
  4. Finish[answer]:最終傳回答案,並結束任務。
  5. 測試函式:透過測試react_strategy函式,驗證了ReAct策略的有效性。

此圖示呈現了ReAct策略的基本流程: 此圖示說明瞭ReAct策略如何透過不斷的思考、行動和觀察步驟,最終得出結論並結束任務。

任務導向互動的上下文脈絡

在第五章和第六章中,我們已經詳細討論瞭如何為檔案完成模型建立提示(prompt)時尋找和組織上下文。這些想法仍然成立,但對於代理(agent)執行的任務導向互動,還有一些新的考慮因素。在本文中,我們將討論從哪裡檢索上下文、如何優先處理它,以及如何在提示中組織和表示它。

上下文的來源

我們即將建立一個通用對話代理。這種代理將從多個來源收集各種上下文,並以對話記錄的形式呈現。首先,有一個前言(preamble),它設定了代理的行為,並確保代理瞭解它有哪些工具可以使用。如果需要,前言可以包括少數範例,以展示代理在對話過程中應表現出的行為。前言通常放在建立OpenAI聊天提示時的系統訊息中。

先前的對話由使用者和助理之間最近的所有來回訊息組成,直到使用者的當前訊息。先前的對話包含這次對話的更廣泛的上下文,包括模型在處理使用者當前請求時需要考慮的重要資訊。

使用者和助理訊息都可能附有工件(artifact),工件是與對話相關的任何資料。例如,使用者可能會詢問根據LLM的航空公司助理有關可用航班的資訊。與此對話相關的工件將是可用航班的表示,包括稍後在對話中有用的詳細資訊,例如日期、時間、始發和目的機場等。

目前的交換(exchange)從使用者的請求以及他們附加到對話的任何工件開始。例如,在應用程式介面中,使用者可能會指出他們正在談論螢幕上的某個內容(例如,透過突出顯示文字或點選元件)。應用程式應該意識到使用者正在參考什麼,並將相關資訊作為工件納入提示中。

在使用者訊息之後,在目前的交換的剩餘部分,模型將在必要時進行工具呼叫,應用程式將把呼叫和回應都納入提示中(如本章開頭所述)。在後續的交換中,工具評估的資料可以作為附加到助理訊息的工件呈現。當模型將助理的訊息傳回給使用者時,目前的交換就完成了。這個訊息不會成為這個提示的一部分,但它將被包含在下一次交換時的先前對話中。

選擇和組織上下文

在前面的討論中,我們提出了可能包含在對話式LLM應用程式中的各種上下文。在本文中,我們將探討一些技術和想法,用於將這些上下文組裝成一個提示。沒有萬能的方法;特定的提示工程方法的有效性取決於領域、模型、資料和其他許多因素。關鍵是不斷嘗試新想法,然後評估、評估、再評估(更多內容請參閱第十章)。

以下是您在選擇和組織提示上下文時可能需要考慮的事項:

  • 您需要哪些工具?在對話的某些部分,您可能會知道代理不需要特定的工具。將它們從考慮中刪除,您的代理在使用其他工具時就會減少一個幹擾因素。
  • 您應該呈現哪些工件?您的選項如下:
    • 全部包含。雖然您可以確保模型擁有最佳的可用資訊,但無關內容太多肯定會混淆模型。
    • 要求模型選擇它認為相關的工件。這需要在應用程式中增加大量的複雜性,因為您必須設定…

上下文管理的技術深度探討

在設計任務導向互動系統時,上下文的管理至關重要。有效的上下文管理不僅能提升模型的理解能力,還能顯著提高互動的效率和準確性。讓我們探討幾種先進的上下文管理策略,並分析它們在實際應用中的技術細節。

動態上下文選擇機制

動態選擇機制允許系統根據當前對話狀態和使用者需求,智慧地選擇最相關的上下文資訊。這種方法需要結合自然語言處理(NLP)和機器學習技術,以實時分析對話內容並做出最佳選擇。

def dynamic_context_selection(conversation_history, current_query):
    # 分析當前查詢和對話歷史
    relevant_context = analyze_context(conversation_history, current_query)
    # 根據分析結果選擇最相關的上下文
    selected_context = select_most_relevant(relevant_context, current_query)
    return selected_context

# 詳細解說:

#### 內容解密:
1. `analyze_context` 函式負責解析對話歷史和當前查詢以識別相關的上下文資訊
2. `select_most_relevant` 函式根據分析結果進一步篩選出最相關的上下文以供模型使用
3. 這種動態選擇機制能夠顯著提高模型的回應準確性和效率

上下文表示的最佳實踐

有效的上下文表示對於模型的理解至關重要。最佳實踐包括使用結構化資料格式(如JSON或XML)來表示上下文,以及採用適當的壓縮技術來減少冗餘資訊。

{
  "context": {
    "previous_messages": [...],
    "current_query": "...",
    "artifacts": [...]
  }
}

內容解密:

  1. 使用結構化資料格式能夠清晰地組織上下文資訊,便於模型解析。
  2. 結合適當的壓縮技術,可以減少傳輸資料的大小,提高處理效率。

構建對話代理的關鍵要素

在開發對話代理時,需要考慮多個關鍵因素,包括如何選擇和呈現重要工件(artifacts)、如何管理對話流程等。以下將詳細闡述這些要素。

工件的選擇和呈現

工件是指在對話過程中可能需要參考的外部資訊或資料。選擇合適的工件並以適當的方式呈現給模型至關重要。

  • 選擇重要工件:模型可以根據上下文選擇最重要的工件。這需要訓練模型識別哪些工件與當前對話相關。
  • 呈現工件:工件可以透過多種方式呈現,例如:
    • 將工件資料直接嵌入到使用者和助手內容中,使用 XML 標籤或 Markdown 格式。
    • 工件的格式可以是 JSON、純文字或其他格式。
    • 如果工件來自函式呼叫,可以直接保留函式呼叫記錄,而不是特別處理工件。

管理對話流程

有效的對話管理需要考慮多個方面,包括如何提取工件中的資訊、如何決定保留多少對話歷史等。

  • 提取工件中的資訊:當使用者參照大型檔案或資料時,需要提取最相關的資訊,而不是將整個檔案包含在提示中。可以使用摘要技術或檢索機制來實作這一點。
  • 決定對話歷史的保留範圍:如果對話已經轉移到新主題,可以丟棄之前的對話內容。可以使用自動化方法或訓練小型模型來決定哪些內容是相關的。

構建對話代理

透過結合上述要素,可以構建一個完整的對話代理。以下是一個示例程式碼,展示瞭如何使用 process_messages 函式和 run_conversation 函式來管理對話流程。

示例程式碼

from openai.types.chat import ChatCompletionMessage

def run_conversation(client):
    messages = [
        {"role": "system", "content": "You are a helpful thermostat assistant"},
    ]
    
    while True:
        user_input = input(">> ")
        if user_input == "":
            break
        messages.append({"role": "user", "content": user_input})
        
        while True:
            new_messages = process_messages(client, messages)
            last_message = messages[-1]
            if not isinstance(last_message, ChatCompletionMessage):
                continue
            
            if last_message.content is not None:
                print(last_message.content)
            
            if last_message.tool_calls is None:
                break
        
        return messages

內容解密:

  • run_conversation 函式初始化了一個包含系統提示的 messages 列表,並進入迴圈等待使用者輸入。
  • 使用者輸入被追加到 messages 列表中,然後呼叫 process_messages 函式處理對話。
  • process_messages 函式可能會多次呼叫工具並評估結果,最終將助手的回應追加到 messages 列表中。
  • 如果最後一條訊息是助手的回應,則列印預出來。如果不是工具呼叫,則等待下一次使用者輸入。

對話代理的設計

圖 8-2 展示了對話代理的序列圖,詳細描述了使用者、應用程式、模型和工具之間的互動過程。

此圖示

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title 大語言模型提示工程與推理策略

package "LLM 提示工程與推理策略" {
    package "推理增強技術" {
        component [思維鏈提示] as cot
        component [零樣本推理] as zeroshot
        component [暫停標記訓練] as pause
    }

    package "ReAct 策略" {
        component [推理 Reasoning] as reason
        component [行動 Acting] as act
        component [觀察 Observing] as observe
    }

    package "上下文管理" {
        component [對話歷史] as history
        component [工具整合] as tools
        component [代理構建] as agent
    }
}

cot --> zeroshot : 逐步思考
zeroshot --> pause : 增加思考時間
pause --> reason : 進入 ReAct
reason --> act : 決策行動
act --> observe : 獲取結果
observe --> reason : 迭代循環
history --> tools : 上下文傳遞
tools --> agent : 構建代理

note right of cot
  思維鏈提示:
  - 模擬人類思考
  - 逐步推理過程
  - 提高答案準確性
end note

note right of agent
  對話代理:
  - 有效管理上下文
  - 整合外部工具
  - 構建智慧代理
end note

@enduml

內容解密:

  • 使用者與應用程式互動,輸入請求。
  • 應用程式將請求轉發給模型進行處理。
  • 模型傳回初步結果給應用程式。
  • 應用程式根據需要呼叫工具,並將工具結果傳回給模型更新上下文。
  • 模型生成最終回應,應用程式將其輸出給使用者。

透過上述設計和實作,可以構建一個功能完善的對話代理,能夠有效地管理對話流程並提供有用的回應。