1. 概述

对话引擎是面向基于自有数据进行多轮对话的高层接口(支持来回多轮交互,而非单次问答)。你可以理解为接入了自有知识库的 ChatGPT

从概念上来说,它是有状态的查询引擎(Query Engine)。对话引擎会维护对话历史,能够结合前文上下文回答用户问题。

💡提示
如果你只需要基于数据进行独立单次提问(不需要保存对话历史),请直接使用[查询引擎])。

2. 入门案例

2.1 构建索引

这块我们在之前已经学习过了,直接贴代码:

import os

from dotenv import load_dotenv
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex, StorageContext
from llama_index.core.vector_stores import SimpleVectorStore
from llama_index.embeddings.openai_like import OpenAILikeEmbedding

load_dotenv()
embed_model = OpenAILikeEmbedding(
    model_name="text-embedding-v3",
    api_key=os.environ["DASHSCOPE_API_KEY"],
    api_base="https://ws-jfb8j8mx0n7e2k6a.cn-beijing.maas.aliyuncs.com/compatible-mode/v1",
)

# 读取 ./data 目录下的全部文件,自动按扩展名选择解析器
reader = SimpleDirectoryReader(
    input_dir="./data",
    raise_on_error=True,
)
documents = reader.load_data(show_progress=True)

store = SimpleVectorStore()
storage_context = StorageContext.from_defaults(vector_store=store)

# 快速构建:解析、分块、向量化、建索引一步完成
index = VectorStoreIndex.from_documents(
    documents,
    storage_context=storage_context,
    embed_model=embed_model,  # 局部指定向量模型;不传则回退到 Settings.embed_model
    show_progress=True,
)

# 检查索引状态
print(f"文档数: {len(documents)}")
print(f"节点数(分块后): {len(index.docstore.docs)}")
for ref_doc_id, info in index.ref_doc_info.items():
    print(f"  {info.metadata.get('file_name')}: {len(info.node_ids)} 个节点")

2.2 构建对话引擎

新建 chat_test.py 配置大模型、嵌入模型,通过本地文件加载索引,构建对话引擎:

import os

from dotenv import load_dotenv
from llama_index.core import Settings, StorageContext, load_index_from_storage
from llama_index.embeddings.openai_like import OpenAILikeEmbedding
from llama_index.llms.openai_like import OpenAILike

load_dotenv()
api_key = os.environ["DASHSCOPE_API_KEY"]
API_BASE = "https://ws-jfb8j8mx0n7e2k6a.cn-beijing.maas.aliyuncs.com/compatible-mode/v1"

# 对话引擎需要 LLM
Settings.llm = OpenAILike(
    model="qwen-plus",  # 百炼对话模型,可按需换 qwen-max / qwen-turbo / qwen-long
    api_key=api_key,
    api_base=API_BASE,
    is_chat_model=True,
)

# 检索阶段需要 embedding,同样显式配置
Settings.embed_model = OpenAILikeEmbedding(
    model_name="text-embedding-v3",
    api_key=api_key,
    api_base=API_BASE,
)

# 从持久目录加载索引(VectorStoreIndex(storage_context=...) 不会载入索引结构)
storage_context = StorageContext.from_defaults(persist_dir="./storage")
index = load_index_from_storage(storage_context)

# 构建对话引擎
chat_engine = index.as_chat_engine()

2.3 对话测试

文档内容:

在这里插入图片描述
发起对话:

response = chat_engine.chat("星云科技哪一年成立")
print(response)

正确输出:

在这里插入图片描述

3. 进阶使用

3.1 快速上手

从索引构建对话引擎:

chat_engine = index.as_chat_engine()

基于自有数据开启对话:

response = chat_engine.chat("Tell me a joke.")

清空对话历史,开启新一轮对话:

chat_engine.reset()

启动交互式对话终端(REPL):

chat_engine.chat_repl()

3.2 高层 API

一行代码直接基于索引构建并配置对话引擎:

chat_engine = index.as_chat_engine(chat_mode="condense_question", verbose=True)

📌 通过入参 chat_mode 可以切换不同类型的对话引擎:

  • condense_question 对应 CondenseQuestionChatEngine
  • react 对应 ReActChatEngine
  • context 对应 ContextChatEngine

📌 说明:高层 API 主打易用性,但不会暴露全部可配置项

可选对话模式:

  • best:将查询引擎封装为工具,根据大模型能力自动选用 ReAct 数据智能体 或 OpenAI 数据智能体。OpenAI 智能体需要 gpt-3.5-turbo / gpt-4,依赖 OpenAI 的函数调用接口。
  • condense_question:读取对话历史,把用户后续提问重写为适配索引检索的独立查询;再调用查询引擎获取结果并回复。
  • context:每一轮用户消息都从索引中召回节点,将召回文本填入系统提示词,让大模型结合检索上下文自然回答。
  • condense_plus_context:结合 condense_questioncontext 两种能力。读取对话历史,把用户问题重写为检索 query,召回文本后填入系统提示词,大模型结合上下文回答。
  • simple:直接和大模型对话,不使用查询引擎、不检索知识库
  • react:和 best 逻辑一致,但强制使用 ReAct 数据智能体。
  • openai:和 best 逻辑一致,但强制使用 OpenAI 数据智能体。

3.3 底层组合API

如果你需要更精细化的自定义能力,可以使用底层组合 API。直白来说:不再调用 index.as_chat_engine(...),而是手动显式实例化 ChatEngine 对象。

📌 提示:该用法建议配合 API文档或示例 Notebook 学习。

下面示例实现这些自定义能力:

  • 自定义问题精简提示词
  • 预置初始对话历史
  • 开启调试日志输出
from llama_index.core import PromptTemplate
from llama_index.core.llms import ChatMessage, MessageRole
from llama_index.core.chat_engine import CondenseQuestionChatEngine


custom_prompt = PromptTemplate(
    """\
给定一段人机对话历史,以及用户的后续问题,请将该问题重写为独立完整的问题,
需要包含对话里所有相关上下文信息。


<对话历史>
{chat_history}


<用户后续问题>
{question}


<独立完整问题>
"""
)


# ChatMessage 对象列表,预置历史对话
custom_chat_history = [
    ChatMessage(
        role=MessageRole.USER,
        content="助手你好,我们今天正在深入讨论Paul Graham相关内容。",
    ),
    ChatMessage(role=MessageRole.ASSISTANT, content="好的,我们开始。"),
]


query_engine = index.as_query_engine()
chat_engine = CondenseQuestionChatEngine.from_defaults(
    query_engine=query_engine,
    condense_question_prompt=custom_prompt,
    chat_history=custom_chat_history,
    verbose=True,
)

3.4 流式输出

想要开启流式返回,直接调用 stream_chat 方法,替代普通的 chat 方法。

⚠️ 注意:该设计和查询引擎不完全统一(查询引擎是通过 streaming=True 参数开启),官方正在优化行为一致性!

同步流式stream_chat):

chat_engine = index.as_chat_engine()
streaming_response = chat_engine.stream_chat("Tell me a joke.")


for token in streaming_response.response_gen:
    print(token, end="")

异步流式astream_chat):

chat_engine = index.as_chat_engine()
streaming_response = await chat_engine.astream_chat("Tell me a joke.")


async for token in streaming_response.async_response_gen():
    print(token, end="")

如果使用 FastAPI 等异步框架,请使用 astream_chat。注意需要使用 await 调用,并遍历异步流式接口(如 async_response_gen())。

完整端到端教程:流式响应示例


4. 官方示例

我们先提供几个简易实现,后续会推出更复杂高级的模式!

简单说明:SimpleChatEngine 不会使用知识库;其余所有对话引擎都会依托查询引擎来使用知识库。

Logo

AtomGit AI 社区提供模型库、数据集、Agent、Token等资源

更多推荐