减少LLM幻觉的五大技巧和方法 原创

发布于 2024-12-31 07:55
浏览
0收藏

本文介绍了使用LangGraph减少LLM幻觉的简单技巧。

如果你使用过LLM,就知道它们有时会产生幻觉。这意味着它们生成的文本要么毫无意义,要么与输入数据相矛盾。这个常见的问题可能会损害基于LLM的应用程序的可靠性。

我们在这篇文章中将探讨一些简单的技巧来降低产生幻觉的可能性。遵循这些技巧,你有望提高AI应用程序的准确性。

幻觉有多种类型:

  • 内在幻觉:LLM的响应与用户提供的上下文相矛盾。响应在当前上下文中是错误的,而且这种错误是可验证的。
  • 外在幻觉:LLM的响应无法使用用户提供的上下文加以验证。响应可能是错误的,也可能不是错误的,但我们没有办法使用当前的上下文来确认。
  • 不连贯的幻觉:LLM的响应并未回答问题或没有意义。LLM无法遵循指示。

我们在这篇文章中将针对上述所有类型作下阐述。

我们将列出一系列以不同方式减少幻觉的技巧和方法。

技巧1:使用锚定

锚定是指要求LLM完成任务时,在LLM的输入中使用领域内相关的附加上下文。这为LLM提供了正确回答问题所需的信息,并降低了产生幻觉的可能性。这是我们使用检索增强生成(RAG)的原因之一。

比如说,问LLM一个数学问题,或者问同样的问题,同时为它提供一本数学书的相关章节,会生成不一样的结果,第二种选择更有可能是正确的。

以下是我在之前的教程中介绍此类实现的示例,在提出问题时提供了从文档提取的上下文信息:

https://towardsdatascience.com/build-a-document-ai-pipeline-for-any-type-of-pdf-with-gemini-9221c8e143db​。

技巧2:使用结构化输出

使用结构化输出意味着强制LLM输出有效的JSON或YAML文本,便于你减少无用的漫谈,从LLM获得“切中要点”的回答。它还有助于下一个技巧,因为它使LLM响应更容易验证。

你可以使用Gemini的API来做到这点:

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”是一个效用函数,它准备模式以匹配Gemini的奇特需求。你可以在这里找到它的定义:​https://github.com/CVxTz/document_ai_agents/blob/498d8ee6e8597f8ba43b336c64178d186461dba0/document_ai_agents/schema_utils.py#L38。

这段代码定义了Pydantic模式,并将该模式作为查询的一部分发送到“response_schema”字段。这迫使LLM在响应中遵循此模式,并使输出解析起来更容易。

技巧3:使用思维链和更好的提示

有时候,在给出最终回答之前,给LLM足够的空间来思考响应,有助于生成更高质量的响应。这种技术被称为思维链,因有效、易于实现而被广泛使用。

如果LLM找不到足够的上下文来生成高质量的响应,我们还可以明确要求它以“N/A”回答。这将给它一个简单的出路,而不是试图回答它不知道怎么回答的问题。

比如说,不妨看看这个简单的问题和上下文:

上下文

托马斯•杰斐逊(1743年4月13日-1826年7月4日),美国政治家、种植园主、外交官、律师、建筑师、哲学家和开国元勋,1801年至1809年担任美国第三任总统,他是《独立宣言》的主要起草者。在美国独立战争之后,在1801年成为总统之前,杰斐逊是华盛顿领导班子的第一位美国国务卿,然后是亚当斯领导班子的第二副总统。杰斐逊是支持民主、共和主义和自然权利的主要倡导者,他在州、国家和国际等层面制定了形成性的文件和决定。(来源:维基百科)

问题

戴维斯•杰斐逊是哪一年去世的?

一种天真的方法会生成:

响应

answer= '1826年 '

这显然是错误的,因为杰斐逊•戴维斯在上下文中根本没有被提及。托马斯•杰斐逊死于1826年。

如果我们将响应的模式改为使用思维链:

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"
    )

我们还添加了更多关于当问题无法回答时,我们期望输出的细节,使用上下文“如果没有找到回答,以‘ N/A ’回答”。

通过这种新方法,我们得到了以下基本原理(记住,使用思维链):

提供的文本讨论的是托马斯•杰斐逊,而不是杰斐逊•戴维斯。没有关于杰斐逊•戴维斯去世的信息。

最终回答:

answer=’N/A’

这个给出的结果太好了!但是我们可以使用一种更通用的方法来检测幻觉吗?

我们可以,那就是使用代理!

技巧 4:使用代理方法

我们将构建一个简单的代理,实现分三个步骤的流程:

  • 第一步是包含上下文并向 LLM 提出问题,以便获得第一个候选回答及其用于回答的相关上下文。
  • 第二步是将问题和第一个候选回答重新表述为声明性语句。
  • 第三步是要求 LLM 验证相关上下文是否包含候选回答。这被称为“自我验证”:https://arxiv.org/pdf/2212.09561。

为了实现这一点,我们使用LangGraph 定义了三个节点。第一个节点将在包含上下文的同时提出问题,第二个节点将使用 LLM 重新表述问题,第三个节点将检查语句与输入上下文的关系。

第一个节点可以如下定义:

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}

第二个节点如下定义:

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}

第三个节点如下定义:

 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。

请注意每个节点如何使用各自的模式用于结构化输出和自己的提示。这得益于 Gemini 的 API 和 LangGraph具有的灵活性。

不妨使用与上面相同的示例来处理此代码。

(注意:我们没有针对第一个提示使用思路链,因此验证因我们的测试而触发。)

上下文

托马斯•杰斐逊(1743 年 4 月 13日— 1826 年 7 月 4 日)是美国政治家、种植园主、外交官、律师、建筑师、哲学家和开国元勋,曾于 1801 年至 1809 年担任美国第三任总统。他是《独立宣言》的主要起草者。在美国独立战争之后,在 1801 年成为总统之前,杰斐逊是华盛顿领导班子的美国第一任国务卿,然后在亚当斯领导班子担任美国第二副总统。杰斐逊是支持民主、共和主义和自然权利的主要倡导者,他在州、国家和国际层面制定了形成性文件和决定。(来源:维基百科)

问题

戴维斯•杰斐逊死于哪一年?

第一个节点结果(第一个回答):

relevant_context=“托马斯•杰斐逊(1743 年 4 月 13 日— 1826 年 7 月 4 日)是美国政治家、种植园主、外交官、律师、建筑师、哲学家和开国元勋,曾于 1801 年至 1809 年担任美国第三任总统。”

answer=’1826’

第二个节点结果(回答重新表述):

declarative_answer=“戴维斯•杰斐逊于 1826 年去世”

第三个节点结果(验证):

rationale=“上下文指出托马斯•杰斐逊于 1826 年去世。断言指出戴维斯•杰斐逊于 1826 年去世。上下文没有提到戴维斯•杰斐逊,只提到托马斯•杰斐逊。”

entailment=’No’

因此验证步骤拒绝(两者之间没有entailment)了初始回答。现在,我们可以避免向用户返回幻觉。

技巧 5:使用更强大的模型

由于预算或延迟方面的限制,该技巧并不总是容易运用,但你应该知道,更强大的 LLM 更不容易产生幻觉。因此如果可能的话,不妨为最敏感的用例选择功能更强大的 LLM。你可以在此处查看幻觉基准:https://github.com/vectara/hallucination-leaderboard。我们可以看到,该基准中的顶级模型(最少的幻觉)也位居传统 NLP 排行榜的榜首。

减少LLM幻觉的五大技巧和方法-AI.x社区

来源:https://github.com/vectara/hallucination-leaderboard

源许可证:Apache 2.0

结语

我们在本教程中探索了通过降低幻觉率来提高 LLM 输出可靠性的几种策略。主要建议包括使用思维链和提示以指导 LLM 调用,并使用基于工作流程的方法,其中代理旨在验证自己的回答。

这涉及多个步骤:

  • 检索 LLM 用来生成回答的确切的上下文信息。
  • 以声明形式重新表述回答以便于验证。
  • 指示 LLM 检查上下文和重新表述的回答之间的一致性。

虽然所有这些技巧都可以显著提高准确性,但你应该知道没有那种方法是万无一失的。如果 LLM 在验证过程中过于保守或遗漏了真实的幻觉情况,始终存在拒绝有效回答的风险。因此,严格评估你的特定 LLM 工作流程仍然至关重要。

全部代码详见​https://github.com/CVxTz/document_ai_agents。

原文标题:​An Agentic Approach to Reducing LLM Hallucinations​,作者:Youness Mansar


©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2024-12-31 13:56:41修改
收藏
回复
举报
回复
相关推荐