減少LLM幻覺(jué)的五大技巧和方法 原創(chuàng)
本文介紹了使用LangGraph減少LLM幻覺(jué)的簡(jiǎn)單技巧。
如果你使用過(guò)LLM,就知道它們有時(shí)會(huì)產(chǎn)生幻覺(jué)。這意味著它們生成的文本要么毫無(wú)意義,要么與輸入數(shù)據(jù)相矛盾。這個(gè)常見(jiàn)的問(wèn)題可能會(huì)損害基于LLM的應(yīng)用程序的可靠性。
我們?cè)谶@篇文章中將探討一些簡(jiǎn)單的技巧來(lái)降低產(chǎn)生幻覺(jué)的可能性。遵循這些技巧,你有望提高AI應(yīng)用程序的準(zhǔn)確性。
幻覺(jué)有多種類(lèi)型:
- 內(nèi)在幻覺(jué):LLM的響應(yīng)與用戶(hù)提供的上下文相矛盾。響應(yīng)在當(dāng)前上下文中是錯(cuò)誤的,而且這種錯(cuò)誤是可驗(yàn)證的。
- 外在幻覺(jué):LLM的響應(yīng)無(wú)法使用用戶(hù)提供的上下文加以驗(yàn)證。響應(yīng)可能是錯(cuò)誤的,也可能不是錯(cuò)誤的,但我們沒(méi)有辦法使用當(dāng)前的上下文來(lái)確認(rèn)。
- 不連貫的幻覺(jué):LLM的響應(yīng)并未回答問(wèn)題或沒(méi)有意義。LLM無(wú)法遵循指示。
我們?cè)谶@篇文章中將針對(duì)上述所有類(lèi)型作下闡述。
我們將列出一系列以不同方式減少幻覺(jué)的技巧和方法。
技巧1:使用錨定
錨定是指要求LLM完成任務(wù)時(shí),在LLM的輸入中使用領(lǐng)域內(nèi)相關(guān)的附加上下文。這為L(zhǎng)LM提供了正確回答問(wèn)題所需的信息,并降低了產(chǎn)生幻覺(jué)的可能性。這是我們使用檢索增強(qiáng)生成(RAG)的原因之一。
比如說(shuō),問(wèn)LLM一個(gè)數(shù)學(xué)問(wèn)題,或者問(wèn)同樣的問(wèn)題,同時(shí)為它提供一本數(shù)學(xué)書(shū)的相關(guān)章節(jié),會(huì)生成不一樣的結(jié)果,第二種選擇更有可能是正確的。
以下是我在之前的教程中介紹此類(lèi)實(shí)現(xiàn)的示例,在提出問(wèn)題時(shí)提供了從文檔提取的上下文信息:
技巧2:使用結(jié)構(gòu)化輸出
使用結(jié)構(gòu)化輸出意味著強(qiáng)制LLM輸出有效的JSON或YAML文本,便于你減少無(wú)用的漫談,從LLM獲得“切中要點(diǎn)”的回答。它還有助于下一個(gè)技巧,因?yàn)樗筁LM響應(yīng)更容易驗(yàn)證。
你可以使用Gemini的API來(lái)做到這點(diǎn):
import json
import google.generativeai as genai
from pydantic import BaseModel, Field
from document_ai_agents.schema_utils import prepare_schema_for_gemini
class Answer(BaseModel):
answer: str = Field(..., description="Your Answer.")
model = genai.GenerativeModel("gemini-1.5-flash-002")
answer_schema = prepare_schema_for_gemini(Answer)
question = "List all the reasons why LLM hallucinate"
context = (
"LLM hallucination refers to the phenomenon where large language models generate plausible-sounding but"
" factually incorrect or nonsensical information. This can occur due to various factors, including biases"
" in the training data, the inherent limitations of the model's understanding of the real world, and the "
"model's tendency to prioritize fluency and coherence over accuracy."
)
messages = (
[context]
+ [
f"Answer this question: {question}",
]
+ [
f"Use this schema for your answer: {answer_schema}",
]
)
response = model.generate_content(
messages,
generation_config={
"response_mime_type": "application/json",
"response_schema": answer_schema,
"temperature": 0.0,
},
)
response = Answer(**json.loads(response.text))
print(f"{response.answer=}")
其中“prepare_schema_for_gemini”是一個(gè)效用函數(shù),它準(zhǔn)備模式以匹配Gemini的奇特需求。你可以在這里找到它的定義:??https://github.com/CVxTz/document_ai_agents/blob/498d8ee6e8597f8ba43b336c64178d186461dba0/document_ai_agents/schema_utils.py#L38。??
這段代碼定義了Pydantic模式,并將該模式作為查詢(xún)的一部分發(fā)送到“response_schema”字段。這迫使LLM在響應(yīng)中遵循此模式,并使輸出解析起來(lái)更容易。
技巧3:使用思維鏈和更好的提示
有時(shí)候,在給出最終回答之前,給LLM足夠的空間來(lái)思考響應(yīng),有助于生成更高質(zhì)量的響應(yīng)。這種技術(shù)被稱(chēng)為思維鏈,因有效、易于實(shí)現(xiàn)而被廣泛使用。
如果LLM找不到足夠的上下文來(lái)生成高質(zhì)量的響應(yīng),我們還可以明確要求它以“N/A”回答。這將給它一個(gè)簡(jiǎn)單的出路,而不是試圖回答它不知道怎么回答的問(wèn)題。
比如說(shuō),不妨看看這個(gè)簡(jiǎn)單的問(wèn)題和上下文:
上下文
托馬斯?杰斐遜(1743年4月13日-1826年7月4日),美國(guó)政治家、種植園主、外交官、律師、建筑師、哲學(xué)家和開(kāi)國(guó)元?jiǎng)祝?801年至1809年擔(dān)任美國(guó)第三任總統(tǒng),他是《獨(dú)立宣言》的主要起草者。在美國(guó)獨(dú)立戰(zhàn)爭(zhēng)之后,在1801年成為總統(tǒng)之前,杰斐遜是華盛頓領(lǐng)導(dǎo)班子的第一位美國(guó)國(guó)務(wù)卿,然后是亞當(dāng)斯領(lǐng)導(dǎo)班子的第二副總統(tǒng)。杰斐遜是支持民主、共和主義和自然權(quán)利的主要倡導(dǎo)者,他在州、國(guó)家和國(guó)際等層面制定了形成性的文件和決定。(來(lái)源:維基百科)
問(wèn)題
戴維斯?杰斐遜是哪一年去世的?
一種天真的方法會(huì)生成:
響應(yīng)
answer= '1826年 '
這顯然是錯(cuò)誤的,因?yàn)榻莒尺d?戴維斯在上下文中根本沒(méi)有被提及。托馬斯?杰斐遜死于1826年。
如果我們將響應(yīng)的模式改為使用思維鏈:
class AnswerChainOfThoughts(BaseModel):
rationale: str = Field(
...,
description="Justification of your answer.",
)
answer: str = Field(
..., description="Your Answer. Answer with 'N/A' if answer is not found"
)
我們還添加了更多關(guān)于當(dāng)問(wèn)題無(wú)法回答時(shí),我們期望輸出的細(xì)節(jié),使用上下文“如果沒(méi)有找到回答,以‘ N/A ’回答”。
通過(guò)這種新方法,我們得到了以下基本原理(記住,使用思維鏈):
提供的文本討論的是托馬斯?杰斐遜,而不是杰斐遜?戴維斯。沒(méi)有關(guān)于杰斐遜?戴維斯去世的信息。
最終回答:
answer=’N/A’
這個(gè)給出的結(jié)果太好了!但是我們可以使用一種更通用的方法來(lái)檢測(cè)幻覺(jué)嗎?
我們可以,那就是使用代理!
技巧 4:使用代理方法
我們將構(gòu)建一個(gè)簡(jiǎn)單的代理,實(shí)現(xiàn)分三個(gè)步驟的流程:
- 第一步是包含上下文并向 LLM 提出問(wèn)題,以便獲得第一個(gè)候選回答及其用于回答的相關(guān)上下文。
- 第二步是將問(wèn)題和第一個(gè)候選回答重新表述為聲明性語(yǔ)句。
- 第三步是要求 LLM 驗(yàn)證相關(guān)上下文是否包含候選回答。這被稱(chēng)為“自我驗(yàn)證”:https://arxiv.org/pdf/2212.09561。
為了實(shí)現(xiàn)這一點(diǎn),我們使用LangGraph 定義了三個(gè)節(jié)點(diǎn)。第一個(gè)節(jié)點(diǎn)將在包含上下文的同時(shí)提出問(wèn)題,第二個(gè)節(jié)點(diǎn)將使用 LLM 重新表述問(wèn)題,第三個(gè)節(jié)點(diǎn)將檢查語(yǔ)句與輸入上下文的關(guān)系。
第一個(gè)節(jié)點(diǎn)可以如下定義:
def answer_question(self, state: DocumentQAState):
logger.info(f"Responding to question '{state.question}'")
assert (
state.pages_as_base64_jpeg_images or state.pages_as_text
), "Input text or images"
messages = (
[
{"mime_type": "image/jpeg", "data": base64_jpeg}
for base64_jpeg in state.pages_as_base64_jpeg_images
]
+ state.pages_as_text
+ [
f"Answer this question: {state.question}",
]
+ [
f"Use this schema for your answer: {self.answer_cot_schema}",
]
)
response = self.model.generate_content(
messages,
generation_config={
"response_mime_type": "application/json",
"response_schema": self.answer_cot_schema,
"temperature": 0.0,
},
)
answer_cot = AnswerChainOfThoughts(**json.loads(response.text))
return {"answer_cot": answer_cot}
第二個(gè)節(jié)點(diǎn)如下定義:
def reformulate_answer(self, state: DocumentQAState):
logger.info("Reformulating answer")
if state.answer_cot.answer == "N/A":
return
messages = [
{
"role": "user",
"parts": [
{
"text": "Reformulate this question and its answer as a single assertion."
},
{"text": f"Question: {state.question}"},
{"text": f"Answer: {state.answer_cot.answer}"},
]
+ [
{
"text": f"Use this schema for your answer: {self.declarative_answer_schema}"
}
],
}
]
response = self.model.generate_content(
messages,
generation_config={
"response_mime_type": "application/json",
"response_schema": self.declarative_answer_schema,
"temperature": 0.0,
},
)
answer_reformulation = AnswerReformulation(**json.loads(response.text))
return {"answer_reformulation": answer_reformulation}
第三個(gè)節(jié)點(diǎn)如下定義:
def verify_answer(self, state: DocumentQAState):
logger.info(f"Verifying answer '{state.answer_cot.answer}'")
if state.answer_cot.answer == "N/A":
return
messages = [
{
"role": "user",
"parts": [
{
"text": "Analyse the following context and the assertion and decide whether the context "
"entails the assertion or not."
},
{"text": f"Context: {state.answer_cot.relevant_context}"},
{
"text": f"Assertion: {state.answer_reformulation.declarative_answer}"
},
{
"text": f"Use this schema for your answer: {self.verification_cot_schema}. Be Factual."
},
],
}
]
response = self.model.generate_content(
messages,
generation_config={
"response_mime_type": "application/json",
"response_schema": self.verification_cot_schema,
"temperature": 0.0,
},
)
verification_cot = VerificationChainOfThoughts(**json.loads(response.text))
return {"verification_cot": verification_cot}
完整代碼位于 https://github.com/CVxTz/document_ai_agents。
請(qǐng)注意每個(gè)節(jié)點(diǎn)如何使用各自的模式用于結(jié)構(gòu)化輸出和自己的提示。這得益于 Gemini 的 API 和 LangGraph具有的靈活性。
不妨使用與上面相同的示例來(lái)處理此代碼。
(注意:我們沒(méi)有針對(duì)第一個(gè)提示使用思路鏈,因此驗(yàn)證因我們的測(cè)試而觸發(fā)。)
上下文
托馬斯?杰斐遜(1743 年 4 月 13日— 1826 年 7 月 4 日)是美國(guó)政治家、種植園主、外交官、律師、建筑師、哲學(xué)家和開(kāi)國(guó)元?jiǎng)?,曾?1801 年至 1809 年擔(dān)任美國(guó)第三任總統(tǒng)。他是《獨(dú)立宣言》的主要起草者。在美國(guó)獨(dú)立戰(zhàn)爭(zhēng)之后,在 1801 年成為總統(tǒng)之前,杰斐遜是華盛頓領(lǐng)導(dǎo)班子的美國(guó)第一任國(guó)務(wù)卿,然后在亞當(dāng)斯領(lǐng)導(dǎo)班子擔(dān)任美國(guó)第二副總統(tǒng)。杰斐遜是支持民主、共和主義和自然權(quán)利的主要倡導(dǎo)者,他在州、國(guó)家和國(guó)際層面制定了形成性文件和決定。(來(lái)源:維基百科)
問(wèn)題
戴維斯?杰斐遜死于哪一年?
第一個(gè)節(jié)點(diǎn)結(jié)果(第一個(gè)回答):
relevant_context=“托馬斯?杰斐遜(1743 年 4 月 13 日— 1826 年 7 月 4 日)是美國(guó)政治家、種植園主、外交官、律師、建筑師、哲學(xué)家和開(kāi)國(guó)元?jiǎng)?,曾?1801 年至 1809 年擔(dān)任美國(guó)第三任總統(tǒng)。”
answer=’1826’
第二個(gè)節(jié)點(diǎn)結(jié)果(回答重新表述):
declarative_answer=“戴維斯?杰斐遜于 1826 年去世”
第三個(gè)節(jié)點(diǎn)結(jié)果(驗(yàn)證):
rationale=“上下文指出托馬斯?杰斐遜于 1826 年去世。斷言指出戴維斯?杰斐遜于 1826 年去世。上下文沒(méi)有提到戴維斯?杰斐遜,只提到托馬斯?杰斐遜?!?/p>
entailment=’No’
因此驗(yàn)證步驟拒絕(兩者之間沒(méi)有entailment)了初始回答。現(xiàn)在,我們可以避免向用戶(hù)返回幻覺(jué)。
技巧 5:使用更強(qiáng)大的模型
由于預(yù)算或延遲方面的限制,該技巧并不總是容易運(yùn)用,但你應(yīng)該知道,更強(qiáng)大的 LLM 更不容易產(chǎn)生幻覺(jué)。因此如果可能的話(huà),不妨為最敏感的用例選擇功能更強(qiáng)大的 LLM。你可以在此處查看幻覺(jué)基準(zhǔn):https://github.com/vectara/hallucination-leaderboard。我們可以看到,該基準(zhǔn)中的頂級(jí)模型(最少的幻覺(jué))也位居傳統(tǒng) NLP 排行榜的榜首。
來(lái)源:https://github.com/vectara/hallucination-leaderboard
源許可證:Apache 2.0
結(jié)語(yǔ)
我們?cè)诒窘坛讨刑剿髁送ㄟ^(guò)降低幻覺(jué)率來(lái)提高 LLM 輸出可靠性的幾種策略。主要建議包括使用思維鏈和提示以指導(dǎo) LLM 調(diào)用,并使用基于工作流程的方法,其中代理旨在驗(yàn)證自己的回答。
這涉及多個(gè)步驟:
- 檢索 LLM 用來(lái)生成回答的確切的上下文信息。
- 以聲明形式重新表述回答以便于驗(yàn)證。
- 指示 LLM 檢查上下文和重新表述的回答之間的一致性。
雖然所有這些技巧都可以顯著提高準(zhǔn)確性,但你應(yīng)該知道沒(méi)有那種方法是萬(wàn)無(wú)一失的。如果 LLM 在驗(yàn)證過(guò)程中過(guò)于保守或遺漏了真實(shí)的幻覺(jué)情況,始終存在拒絕有效回答的風(fēng)險(xiǎn)。因此,嚴(yán)格評(píng)估你的特定 LLM 工作流程仍然至關(guān)重要。
全部代碼詳見(jiàn)??https://github.com/CVxTz/document_ai_agents。??
原文標(biāo)題:??An Agentic Approach to Reducing LLM Hallucinations??,作者:Youness Mansar
