DashVector x 通义千问大模型：打造基于专属知识的问答服务
Published: 2024-05-24 07:10:53

 
===================================================

本教程演示如何使用向量检索服务（DashVector），结合LLM大模型等能力，来打造基于垂直领域专属知识等问答服务。其中LLM大模型能力，以及文本向量生成等能力，这里基于[灵积模型服务](https://dashscope.aliyun.com/)上的通义千问 API以及Embedding API来接入。


背景及实现思路 
----------------

大语言模型（LLM）作为自然语言处理领域的核心技术，具有丰富的自然语言处理能力。但其训练语料库具有一定的局限性，一般由普适知识、常识性知识，如维基百科、新闻、小说，和各种领域的专业知识组成。导致 LLM 在处理特定领域的知识表示和应用时存在一定的局限性，特别对于垂直领域内，或者企业内部等私域专属知识。

实现专属领域的知识问答的关键，在于如何让LLM能够理解并获取存在于其训练知识范围外的特定领域知识。同时可以通过特定Prompt构造，提示LLM在回答特定领域问题的时候，理解意图并根据注入的领域知识来做出回答。在通常情况下，用户的提问是完整的句子，而不像搜索引擎只输入几个关键字。这种情况下，直接使用关键字与企业知识库进行匹配的效果往往不太理想，同时长句本身还涉及分词、权重等处理。相比之下，倘若我们把提问的文本，和知识库的内容，都先转化为高质量向量，再通过向量检索将匹配过程转化为语义搜索，那么提取相关知识点就会变得简单而高效。

接下来我们将基于[中文突发事件语料库](https://github.com/shijiebei2009/CEC-Corpus/)（CEC Corpus）演示关于突发事件新闻报道的知识问答。


整体流程 
-------------

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l8l2xtlek0drmh7tyvn2.png)




主要分为三个阶段：

1. **本地知识库的向量化** 。通过文本向量模型将其转化为高质量低维度的向量数据，再写入DashVector向量检索服务。这里数据的向量化我们采用了[灵积模型服务](https://dashscope.aliyun.com/)上的Embedding API实现。

2. **相关知识点的提取** 。将提问文本向量化后，通过 DashVector 提取相关知识点的原文。

3. **构造 Prompt 进行提问** 。将相关知识点作为"限定上下文+提问" 一起作为prompt询问通义千问。



前提准备
-------------

### 1. API-KEY 和 Cluster准备

* 开通灵积模型服务，并获得 API-KEY。请参考：[开通DashScope并创建API-KEY](https://help.aliyun.com/zh/dashscope/developer-reference/activate-dashscope-and-create-an-api-key?spm=a2c4g.2510235.0.i10)。

* 开通DashVector向量检索服务，并获得 API-KEY。请参考：DashVector [API-KEY管理](https://help.aliyun.com/document_detail/2510230.html?spm=a2c4g.2510235.0.i11)。


* 开通DashVector向量检索服务，并[创建Cluster](https://help.aliyun.com/document_detail/2631966.html?spm=a2c4g.2510235.0.i12)。

* 获取Cluster的Endpoint，Endpoint获取请查看 [Cluster详情](https://help.aliyun.com/document_detail/2568084.html?spm=a2c4g.2510235.0.i13#3c0384e4e6asc)。

**说明**

灵积模型服务DashScope的API-KEY与DashVector的API-KEY是独立的，需要分开获取。


### 2. 环境准备

**说明**

需要提前安装 Python3.7 及以上版本，请确保相应的 python 版本。


```
pip3 install dashvector dashscope
```



### 3. 数据准备 

```
git clone https://github.com/shijiebei2009/CEC-Corpus.git
```



搭建步骤 
-------------

**说明**

本教程所涉及的 *your-xxx-api-key* 以及 *your-xxx-cluster-endpoint* ，均需要替换为您自己的API-KAY及CLUSTER_ENDPOINT后，代码才能正常运行。

### 1. 本地知识库的向量化

[CEC-Corpus](https://github.com/shijiebei2009/CEC-Corpus)数据集包含 332 篇突发事件的新闻报道的语料和标注数据，这里我们只需要提取原始的新闻稿文本，并将其向量化后入库。文本向量化的教程可以参考[《基于向量检索服务与灵积实现语义搜索》](https://help.aliyun.com/document_detail/2510234.html?spm=a2c4g.2510235.0.i14)。创建`embedding.py`文件，并将如下示例代码复制到`embedding.py`中：

```python
import os

import dashscope
from dashscope import TextEmbedding

from dashvector import Client, Doc


def prepare_data(path, batch_size=25):
    batch_docs = []
    for file in os.listdir(path):
        with open(path + '/' + file, 'r', encoding='utf-8') as f:
            batch_docs.append(f.read())
            if len(batch_docs) == batch_size:
                yield batch_docs
                batch_docs = []

    if batch_docs:
        yield batch_docs


def generate_embeddings(news):
    rsp = TextEmbedding.call(
        model=TextEmbedding.Models.text_embedding_v1,
        input=news
    )
    embeddings = [record['embedding'] for record in rsp.output['embeddings']]
    return embeddings if isinstance(news, list) else embeddings[0]


if __name__ == '__main__':
    dashscope.api_key = '{your-dashscope-api-key}'
    
    # 初始化 dashvector client
    client = Client(
      api_key='{your-dashvector-api-key}',
      endpoint='{your-dashvector-cluster-endpoint}'
    )

    # 创建集合：指定集合名称和向量维度, text_embedding_v1 模型产生的向量统一为 1536 维
    rsp = client.create('news_embedings', 1536)
    assert rsp

    # 加载语料
    id = 0
    collection = client.get('news_embedings')
    for news in list(prepare_data('CEC-Corpus/raw corpus/allSourceText')):
        ids = [id + i for i, _ in enumerate(news)]
        id += len(news)
        
        vectors = generate_embeddings(news)
        # 写入 dashvector 构建索引
        rsp = collection.upsert(
            [
                Doc(id=str(id), vector=vector, fields={"raw": doc})
                for id, vector, doc in zip(ids, vectors, news)
            ]
        )
        assert rsp
```

在示例中，我们将 Embedding 向量和新闻报道的文稿（作为raw字段）一起存入DashVector向量检索服务中，以便向量检索时召回原始文稿。

### 2. 知识点的提取

将 CEC-Corpus 数据集所有新闻报道写入DashVector服务后，就可以进行快速的向量检索。实现这个检索，我们同样将提问的问题进行文本向量化后，再在DashVector服务中检索最相关的知识点，也就是相关新闻报道。创建`search.py`文件，并将如下示例代码复制到`search.py`文件中。

```
from dashvector import Client

from embedding import generate_embeddings


def search_relevant_news(question):
    # 初始化 dashvector client
    client = Client(
      api_key='{your-dashvector-api-key}',
      endpoint='{your-dashvector-cluster-endpoint}'
    )

    # 获取刚刚存入的集合
    collection = client.get('news_embedings')
    assert collection

    # 向量检索：指定 topk = 1 
    rsp = collection.query(generate_embeddings(question), output_fields=['raw'],
                           topk=1)
    assert rsp
    return rsp.output[0].fields['raw']
```



### 3. 构造 Prompt 向LLM(通义千问)提问 

在通过提问搜索到相关的知识点后，我们就可以将 "提问 + 知识点" 按照特定的模板作为 prompt 向LLM发起提问了。在这里我们选用的LLM是通义千问，这是阿里巴巴自主研发的超大规模语言模型，能够在用户自然语言输入的基础上，通过自然语言理解和语义分析，理解用户意图。可以通过提供尽可能清晰详细的指令（prompt)，来获取更符合预期的结果。这些能力都可以通过[通义千问API](https://help.aliyun.com/document_detail/613695.html)来获得。

具体我们这里设计的提问模板格式为： *请基于我提供的内容回答问题。内容是{___}，我的问题是{___}* ，当然您也可以自行设计合适的模板。创建`answer.py`，并将如下示例代码复制到`answer.py`中。

````
from dashscope import Generation


def answer_question(question, context):
    prompt = f'''请基于```内的内容回答问题。"
	```
	{context}
	```
	我的问题是：{question}。
    '''
    
    rsp = Generation.call(model='qwen-turbo', prompt=prompt)
    return rsp.output.text
````


知识问答 
-------------

做好这些准备工作以后，就可以对LLM做与具体知识点相关的提问了。比如在 CEC-Corpus 新闻数据集里，有如下一篇报道。因为整个新闻数据集已经在之前的步骤里，转换成向量入库了，我们现在就可以把这个新闻报道作为一个知识点，做出针对性提问：海南安定追尾事故，发生在哪里？原因是什么？人员伤亡情况如何？，并查看相应答案。


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wz5rqkks8u83v1hxccg9.png)
创建`run.py`文件，并将如下示例代码复制到`run.py`文件中。


```
import dashscope

from search import search_relevant_news
from answer import answer_question

if __name__ == '__main__':
    dashscope.api_key = '{your-dashscope-api-key}'

    question = '海南安定追尾事故，发生在哪里？原因是什么？人员伤亡情况如何？'
    context = search_relevant_news(question)
    answer = answer_question(question, context)

    print(f'question: {question}\n' f'answer: {answer}')
```




![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wusf1ucih20rf2isuqyp.png)
可以看到，基于DashVector作为向量检索的底座，LLM大模型的知识范畴得到了针对性的扩展，并且能够对于专属的特定知识领域做出正确的回答。

写在最后 
-------------

从本文的范例中，可以看到DashVector作为一个独立的向量检索服务，提供了开箱即用的强大向量检索服务能力，这些能力和各个AI模型结合，能够衍生多样的AI应用的可能。这里的范例中，LLM大模型问答，以及文本向量生成等能力，都是基于[灵积模型服务](https://dashscope.aliyun.com/)上的通义千问API和Embedding API来接入的，在实际操作中，相关能力同样可以通过其他三方服务，或者开源模型社区，比如ModelScope上的[各种开源LLM模型](https://modelscope.cn/topic/dfefe5be778b49fba8c44646023b57ba/pub/summary)来实现。