背景很重要:利用聊天记录为AI代理检索更相关的记忆

由Gaudiy Inc.的AI工程师Shreyas Shinde发布。

介绍

在人工智能领域快速发展中,确保AI代理能够提供相关和准确的响应是至关重要的。AI对话中的关键挑战之一是处理不当或模糊的用户查询。本博客探讨了利用聊天历史来改进代理的记忆检索和整体对话质量的查询情境化的重要性。它还深入探讨了如何系统地调整提示以实现查询情境化。

在Gaudiy,我们提供“Gaudiy Fanlink”,这是一个为多个知识产权提供社区平台的功能。它具有一个Beta功能,允许用户与特定角色的AI头像(例如实际名人或动漫角色)进行聊天,与特定知识产权持有者合作。我们使用矢量余弦相似度搜索,从矢量存储(使用Vertex AI)中检索与用户消息查询相关的角色AI的记忆(关于角色的基本信息和与用户的聊天历史摘要),然后将它们添加到LLM的提示中,以启动回复生成过程。由于用户的消息并不总是包含足够的上下文来检索相关的记忆,因此首先用最近的聊天历史来对用户的消息进行情境化,并将其用作查询是至关重要的。

问题示例

想象一下以下用户和代理之间的对话:

User: Have you read any new books recently?
Agent: Yes, I recently read ‘Harry Potter’.
User: That's great. Which volume are you reading?
Agent: I've started reading the first volume. It's very interesting.
User: Nice!

当我们直接将用户的最后一条消息“很好!”作为向量数据库的查询时,我们将得到与特定“很好!”无关的结果。这将导致错过获取有关当前对话中的关键主题和细微差别如哈利波特、书籍、第一卷和有趣等信息。如果我们的代理程序有与这些话题相关的记忆,并且在“很好!”的上下文中,那么就会错过利用这些记忆来进行更丰富和个性化的对话的机会。

文献调研

我们调查了三种不同的可能方法:

Welcome to our website! 2. We have a wide variety of products for you to choose from. 3. If you have any questions, please feel free to contact us.

提示LLM根据聊天记录重写用户的消息,以使其成为独立可理解的文本。这已在LangChain的文档中记录。

我们很快发现,尽管LangChain提供的简单提示在与强大但昂贵的LLM(如gpt4o)一起使用时表现出色,但在与成本效益高的LLM(如gpt3.5,gemini-1.0,gemini-1.5-flash,claude-sonnet-3)一起使用时出现了一些问题,具体取决于所选择的模型。

一些面临的问题:

  1. 回复用户的上一条消息,而不是创建一个上下文关联的消息。
  2. 只有部分地将信息置于上下文中
  3. 在上下文消息中添加幻觉

选择这款LLM必须是一种经济实惠的选择,以便用于我们的产品。我们发现gemini-1.5-flash在较便宜的型号中最有前景。

2.

为了解决在1中提到的不完整的语境化问题,不仅生成1个,而是从用户消息和聊天记录中生成3个独立的查询(陈述或问题),并针对每个查询进行检索。

但这种方法还有额外的开销,需要对通过3个查询检索到的记忆进行重新排名和过滤。我们可以使用类似互惠排序融合或llm过滤器的方法来做到这一点。我们还需要一些提示工程来提高三个查询的相关性,并避免增加幻觉。

3. 欢迎来到我们的网站!

假设性文档嵌入:这种方法首先生成一个不使用任何记忆的代理回复,然后使用回复来检索记忆,然后再生成回复。由于我们的回复生成过程是多步骤的,这种方法导致我们的成本加倍,因此并不是非常具有成本效益。

💡 决定:

我们选择继续上述的第一种方法,因为我们判断通过及时调整,我们将能够识别和解决问题,并以成本效益的方式在我们的产品中使用它。

查询上下文化的提示调整

为了以结构化方式改善LangChain中的简单提示,我们采取了以下步骤:

  1. 生成一个包含用户和代理之间各种类型对话的合成数据集,用于评估使用chatgpt4与提示相比。为每个示例添加基本真实的上下文化查询。这些被称为理想查询,会导致检索到最相关的记忆。将数据集上传到LangSmith。
  2. 使用LangSmith编写一个评估函数,该函数计算生成的上下文化查询和地面真相上下文化查询的嵌入之间的余弦相似度距离。较短的距离意味着提示生成了一个类似于地面真相的上下文化查询,导致类似的相关记忆检索,就像使用地面真相查询一样。我们使用了最近发布的开源软件LangSmith辅助库来帮助我们进行此评估。
  3. 执行查询上下文化器llm来生成上下文查询。计算示例类别间的平均分数。识别困难的示例并更新提示,尝试解决问题。重新运行评估以确认是否已解决。评估周期不仅为我们改进特定类别示例的提示提供了方向,而且在每次迭代过程中计算平均评估分数,也确保提示在整体数据集上表现良好。
  4. 在实际生产查询中运行它。添加一些简短的示例,并将LLM的温度设置为0,以确保输出一致,因为这会降低LLM的创造力。

1. 使用chatgpt4生成合成数据集

通过与ChatGPT4的聊天,我们创建了一个高质量的合成数据集,包含75个对话,变化了chat_history和user_last_message的类型。考虑实际场景的更好覆盖,深入考虑示例类型是重要的。

我们还为评估生成了真实场景化的查询。

例子:

"chat_history": "L: The SPK has valuable intel.
Shreyas: Should we contact them?
L: Yes, but cautiously.
Shreyas: What's the best approach?
L: A secure line.",
"user_last_message": "Should I set up the call?",
"ground_truth_standalone_message": "Should I set up a secure call to contact the SPK for their valuable intel?",
"example_type": "Multiple turns, question, context from multiple messages"

上传到LangSmith:

何种原因导致使用ChatGPT4的“人在环中”式合成数据集生成方式?

💡 以聊天方式执行此任务,而不是一个提示和脚本,具有明显的优点。聊天方式能更好地控制生成的数据集(首先让它生成10个例子,然后检查它的错误)。当我们看到它未能遵循指示时,我们立即让它进行更正,再次验证,然后生成更多例子。我们决定如何使例子有所变化(例如我想要的例子类型,比如用户最后一条消息是陈述句还是疑问句,聊天记录包含单条消息或多条消息等)。这也使我们能够在中途灵活地对模式进行更改,而不必一开始就完全打磨它,以防需要更改。由于它有一个长的上下文窗口,所有历史记录都被用来遵循先前消息中的所有指示。Chatgpt可以轻松地输出csv、json格式的文件。CSV文件可以直接从LangSmith的用户界面上传,以创建新数据集!这还帮助我最终将文件翻译成日语,因为我首先要验证数据是英文的。这次我们只需要创建75个例子。如果我们需要1000个例子,我们的想法是,在进行这次聊天后,我们可以令人满意和一致地产生例子,然后要求它总结对话,并创建一个提示。然后使用该提示通过脚本和api生成1000个例子。

2. 使用LangSmith的评估器函数

我们使用了LangSmith的现成embedding_distance评估器

from typing import Any

from langchain_google_vertexai import VertexAIEmbeddings
from langsmith.evaluation import LangChainStringEvaluator
from langsmith.evaluation.integrations import _langchain
from langsmith.schemas import Example, Run

from gaudiy_python_kit.ai_testing.tests.utils import get_child_run, get_output


def prepare_data_for_similarity_eval_single_contextualized_query_and_ground_truth( # noqa: D103
run: Run, example: Example | None
) -> _langchain.SingleEvaluatorInput:
prompt_run = get_child_run(run, "prompt")
relevant_memories_run = get_child_run(prompt_run, "get_relevant_memories")
pre_retrieval_query_translation_run = get_child_run(relevant_memories_run, "_pre_retrieval_query_translation")
contextualize_query_run = get_child_run(pre_retrieval_query_translation_run, "contextualize_query")
return {
"prediction": get_output(contextualize_query_run, "output")[0],
"reference": get_output(example, "ground_truth_standalone_message"),
"input": "", # added because mypy complains about missing key
}


evaluators = [
LangChainStringEvaluator(
"embedding_distance",
config={
"embeddings": VertexAIEmbeddings(model_name="textembedding-gecko-multilingual@latest"),
"distance_metric": "cosine",
},
prepare_data=prepare_data_for_similarity_eval_single_contextualized_query_and_ground_truth,
),
]
summary_evaluators: list[Any] = []

3. 对数据集和提示调整的查询上下文化评估

  1. 首先为了创建一个基准,我们在没有查询上下文的情况下运行评估器。得分为0.11(用户最后一条消息和实际独立消息之间的嵌入距离的平均值)。
  2. 接下来,我们对以下配置进行了评估: a. 提示的v1版本(langchain的简单提示) b. 模型:gemini-1.5-flash 得分为0.08。随着平均距离的减少,我们可以得出结论:查询上下文化使用户上次消息更接近余弦相似度与基准查询。
  3. 然后,我们检查了具有最大嵌入距离的示例类型,因为这些是明显需要调整提示的目标。经过22次提示修订后,我们最终达到了0.04的分数,以及一个足够好的提示,在所有不同类型的示例上表现良好。我们确保在代码中对提示进行版本控制,以便能够进行有效的回溯和比较各种变化。再次感谢chatgpt4,在当前提示和表现不佳的示例列表中提高提示的帮助!

💡 从上图可以看出,我们不断调整提示的方式,以便缩小距离!

4. 根据实际生产聊天记录进行评估

仅针对合成数据集进行测试不足以确认提示在实际应用中的表现良好。这是因为合成数据集存在文本多样性的问题,即使我们已经确保创建了各种示例。为了更好的信心,我们测试了提示针对一些生产查询和聊天记录,并很快发现了需要对提示进行改进的地方。我们还发现,向提示添加几个示例,并将LLM的温度设置为0会导致结果更一致。由于gpt4o-mini最近发布,我们还进行了比较评估,并发现其表现更好,因此我们转而使用它。

结果

1. 一些实际输出(用日语)

#Chat History:
shreyas: 最近何か新しい本を読みましたか?
AI: はい、最近「ハリーポッター」を読みました。
shreyas: それは素晴らしいですね。どの巻を読んでいますか?
AI: 最初の巻を読み始めました。とても面白いです。
#Last user message:
いいね!
#Contextualized query:
最近「ハリーポッター」の最初の巻を読み始めたことに対して、いいね!と言います。

#Chat History:
shreyas: ピカチュウって、ほんとにあるのかな?
#Last user message:
あるよ!
#Contextualized query:
ピカチュウは実在するキャラクターで、ポケットモンスターの一部として知られていますよ!

#Chat History:
shreyas: 明日、アイドルフェスティバルでライブ配信があるんだ
#Last user message:
それって何ですか?
#Contextualized query:
明日行われるアイドルフェスティバルについて詳しく教えてもらえますか?

2. 提示和模型配置

模型:gpt-4o-mini 温度:0 几个镜头:是的(参考:使用LangChain) 提示:

Examine the provided chat history and the last user message to determine its clarity as a complete query or statement.
The chat history is formatted as a continuous string where each message is on a new line and includes the sender's name, while the last user message is given separately without a username identifier.
If the last message is already a clear, complete query or statement that can be understood independently, return it as is.
If it lacks sufficient clarity or requires contextual details to be fully understood on its own, carefully examine all previous interactions in chat history to gather every relevant piece of information.
If the last message implicitly or explicitly references any aspect discussed earlier in the chat history that is essential for understanding then Rewrite the last message to include all necessary details discussed earlier, ensuring it clearly & completely integrates those pertinent details to stand alone confidently and directly connects the context from chat history to the user's statement.
The rewritten message in japanese should maintain the original message's intent, either as a question or a statement, without explaining or answering itself.

注意:如果您希望输出为简体中文,请更新提示!

结论

总的来说,利用聊天历史来上下文化用户查询的方法已被证明是改善AI对话代理的记忆检索的关键增强。通过系统地调整提示并利用合成数据集进行评估,我们能够显著减少原始查询和上下文化查询之间的嵌入距离。这种改进确保了AI代理能够检索到更相关的记忆,从而进行更丰富、更个性化的对话。提示调整的迭代过程,结合真实世界的测试,已经产生了一个能够有效处理不同对话场景的强大系统。这种方法不仅提升了用户体验,还优化了AI代理在实际应用中的性能。这种方法的成功凸显了AI对话中上下文的重要性,并展示了精心设计的提示工程和评估策略在开发更强大的AI系统时的价值。

高第将继续推动人工智能代理的研发工作。

更多文章:

关于公司:

2024-08-30 04:15:58 AI中文站翻译自原文