# RAG

本实现类型采用 `RAG` (Retrieval Augmented Generation) 检索增强生成技术，构建了相关的知识数据库，再与**检索器**，**提示工程**等技术结合，来提升大模型的知识能力。

此外，根据检索方式，我们又将其派生出 Sparse（稀疏）和 Dense（稠密）两种具体的实现类型，这两种实现会在**检索效率**和**检索质量**进行平衡。

## 处理逻辑

检索增强生成的流程可以直观地分为检索 -> 增强 -> 生成。此外，由于本项目仅搜集了中大相关的知识数据，因此检索时可能会出现无效检索的情况（也就是无法检索到与问题相关的文本），因此我们额外引入了兜底策略的逻辑。

```python
def talk(self, query: str) -> str:
    if self._searcher is None:
        return self._caller.single_call(query)

    # 1. 使用 searcher 进行向量检索
    data = self._searcher.search_with_label(query, 3)

    # 2. 当检索效果不好时执行兜底策略
    if len(data) == 0:
        return self._last_strategy(query)

    # 3. Prompt 生成
    final_prompt = self._generate_prompt(query, data)

    # 4. 生成最终回答
    return self._caller.single_call(final_prompt)
```

### 1. 检索

检索部分主要分为**数据集制作**和**检索方式**两个部分。

#### (1) 数据库制作

想要完成检索任务，首先我们需要有相对应的数据。下面简单介绍一下如何收集校庆数据，以及数据的格式：

1. 确定数据结构：首先，我们需要确定数据库的数据结构。为了便于后续的 ElasticSearch 索引构建，我们选择使用 JSON 格式来构建数据库，以使用键值对的方式组织数据，非常适合表示结构化的数据。
2. 设计数据字段：我们可以将每个数据项视为一个对象，其中包含三个字段：query、document 和 metadata。
   * query 字段表示查询的关键词或问题
   * document 字段表示查询结果的文本或内容
   * metadata 字段表示与查询结果相关的元数据/关键词信息。
3. 收集数据：我们在[中山大学官网](https://www.sysu.edu.cn/)、[中大百年校庆官网](https://sysu100.sysu.edu.cn/)、[中山大学百度百科](https://baike.baidu.com/item/%E4%B8%AD%E5%B1%B1%E5%A4%A7%E5%AD%A6/5672)、校园公众号、视频号等平台，收集有关校园生活、人文历史、招生政策、院系分布等数据，并将数据拆分为 300+个 JSON 对象。 下面是一个示例：

   ```json
   "1":{
       "query":"中山大学成立的校史",
       "document":"1924年,孙中山亲手将广州地区多所高校整合创立国立广东大学。1926年定名为国立中山大学。如今该校由1952年院系调整后分设的中山大学和中山医科大学于2001年10月合并而成。",
       "metadata":"校名,校史,简介,发展史,改称"
   },
   ```

#### (2) 检索方式

检索方式主要分为 Dense 和 Sparse 两种，两种比较如下。具体技术细节请查看 [检索器 Searcher](https://github.com/sse-digital-man/sysu-introducer-wiki/blob/main/module/bot/module/bot/README.md)。

|      | Dense            | Sparse        |
| ---- | ---------------- | ------------- |
| 检索对象 | 文本向量             | 文本            |
| 检索速度 | 较慢               | 较快            |
| 检索效果 | 较好               | 较差            |
| 实现   | FastText, Vector | ElasticSearch |

### 2. 增强

为了将检索的相关信息用于增强大模型能力，我们利用大模型强大的 `上下文学习(In-context Learning)` 能力，通过**提示词工程**来实现。 因此，我们定义了如下的 `prompt` 模板：

```python
system_prompt = \
"""### 示范一
[用户问题]
国际学生政策是怎样的？
[参考资料]
参考资料1:
标题:国际学生政策
内容:详情可见中山大学留学生办公室官网
[回答]
国际学生政策，官网有最权威的信息哦！快去中山大学留学生办公室官网看看吧，你会有新发现的！

### 示范二
[用户问题]
可以介绍一下你自己嘛？
[参考资料]
参考资料1:
标题:中山大学校校歌歌词
内容:白云山高,珠江水长,吾校矗立,蔚为国光,中山手创
[回答]
嗨！我是中小大，中大软件工程学院的大三学生，也是中大介绍官。我超爱写代码和阅读历史书籍！

### 示范三
[用户问题]
为什么皮卡丘喜欢放电
[参考资料]
参考资料1:
标题:中山大学的微电子科学与技术学院本科招生专业
内容:微电子科学与工程
参考资料2:
标题:中山大学的集成电路学院本科招生专业
内容:微电子科学与工程
[回答]
很抱歉，我是中大介绍官，不能回答关于皮卡丘的问题哦~

### 开始任务
[用户问题]
{query}
[参考资料]
{data_str}
[回答]"""

data_prompt = \
"""参考资料{i}:
标题:{query}
内容:{document}
"""
```

模板包含两个部分：

**示范(demonstration)：** 通过 `Few-shot Learning`，增强大模型的指令遵循能力。 我们提供了三个范例，分别对应三种情况：

* 问题与检索内容相关，可以根据问题与检索内容回答。
* 问题相关，检索得到的内容不相关，只根据用户问题。
* 用户问题与中大无关，不应该回答。

**检索增强内容：** 以 `[参考资料]` 的形式给到模型。

### 3. 生成

将最终融合的 `prompt` 给到[调用器 Caller](#调用器-caller) 生成答案。

### 4. 兜底策略

兜底策略并不会是任何 RAG 对象都是生效的，此处在实现时需要进行区分。例如以下 `HyDE` 兜底策略通常效果会在 Dense 中更好。

#### Hyde 兜底

`Hyde`(Hypothetical Document Embeddings，假设性文档嵌入)通过使用一个大语言模型，在响应查询的时候建立一个假设的文档。 通过计算假设文档的向量而在[向量数据库](#2-vector-similarity)中搜索。 该方法来源于论文 [Precise Zero-Shot Dense Retrieval without Relevance Labels](https://arxiv.org/abs/2212.10496)。

`HyDE` 考虑到的是在 `查询-回答` 任务中，`查询` 与 `回答` 的相似度可能不高，不如生成一个假设的回答，从而通过这个假设的回答在向量数据库中进行检索。

代码实现上也非常简单：

```python
# 基本的检查...

# 1. 生成 hyde 假设性回答
query = searcher.prompt_template.format(query=query)
query += caller.single_call(query, False)

# 2.使用 hyde 检索得到 top-k 相似结果
retrieve_res = searcher.search_with_label(query, k)

return retrieve_res
```

**效果**

部份来自用户的 `query` 可能包含的关键词带有一些昵称，比如 `鸭大`。 这种时候，直接使用 `Searcher`，可能无法检索到相应的结果。 比如下面的例子

```
[query]
send "鸭大什么水平" 

[w/o HyDE]
[] (超过相似度阈值的结果为空)
```

`HyDE` 在这时启动，生成的假设性文档嵌入以及检索结果如下：

```
[HyDE]
请回答用户关于中山大学信息的查询
查询: 鸭大什么水平
回答: 中山大学作为一所知名的综合性大学，位于广东省广州市，是中国一流大学之一。学校设有多个学科门类，涵盖文、理、工、医、法、经、教育等多个领域。中山大学在教学水平、科研水平以及社会影响力等方面 均属于国内前列大学，被广泛认可为高水平的学府之一。如果您对中山大学有更具体的问题，欢迎继续提问。

[w/ HyDE]
1. 中山大学简介...
2. 中山大学的办学情况,校区分布...
3. 中山大学的办学层次...
```

可见，`HyDE` 技术能够在用户查询措辞较为“奇特”的情况下，很好地完成检索工作。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://fucloud.gitbook.io/sysu-introducer/module/bot/rag.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
