CiNiiの検索結果をLLMでまとめる

# 概要
　[CiNii Research](https://cir.nii.ac.jp/)の検索結果をLLMを使ってまとめてみます。

 環境は[前回](https://qiita.com/wonox/items/c655f80e98e19217e6cf)作った、普通のノートPC上の llama.cpp で、rinna 4bit量子化版で動かしてみたので、実用上はいまいちです。GPUなどのインフラか、有料版GPTなどお金をかければ、使い道はありそうです。
 
# CiNiiからタイトルを抽出
　まずは、CiNiiから結果を抽出し、結果をPythonのリスト形式にします。
 今回は、例題として’大規模言語モデル’と検索してみました。

```python
import requests
import json
from urllib.parse import quote

# WebサーバのAPIエンドポイント
url = "https://cir.nii.ac.jp/opensearch/all?count=5&sortorder=0&format=json&q="

# エンドポイントにPOSTリクエストを送信
def _run(url, query: str, run_manager: Optional[CallbackManagerForToolRun] = None) -> str:
    query = url + quote(query)  #.replace("'", '"').encode('utf-8')
    response = requests.get(query)  # .post(url) # +  "get_topic", query_dict)
    print(response)
    topic_content = response.text
    print(topic_content)
    return topic_content
    
result = _run(url, "大規模言語モデル")

data = json.loads(result)  
for x in data:
    id = data['items']
title_list = []
for titles in id:
    if titles.get('description'):
        # print(titles['description'])  # ['dc:publisher'])
        title_list.append(titles["description"])
    title_list.append(titles['title'])
    # 
print(title_list)

# => ['PFNはLLMを自社開発しており、2023年9月には130億パラメータのLLM「PLaMo-13B」を公開した（pp.40-42の記事を参照）注1）。製品版のカチャカにLLMを適用する場合は、この自社開発のLLMを利用する見込みだが、今回のデモではLLMには米OpenAI社の「GPT-4」を利用した。カチャカ…', 'PFRの家庭向けロボ、大規模言語モデル経由で操作 生成コードはLuaを採用、将来はマルチモーダル型へ', '文章や画像、動画といった多彩なコンテンツを人の指示に応じて出力する生...(以下略)
```

# load_summarize_chain を使って、結果を要約する。
　今回は、"map_reduce" を使いました。

```python
from langchain.docstore.document import Document
from langchain.llms import LlamaCpp
from langchain.chains.summarize import load_summarize_chain
from langchain.prompts import load_prompt

llm = LlamaCpp(
    model_path="llama.cpp/models/rinna-youri-7b-chat-q4_K_M.gguf",
    # model_path=r"llama.cpp/cpp\models\llama-2-7b-chat.Q4_K_M.gguf",
    input={
        "max_tokens": 32,
        "stop": ["System:", "User:", "Assistant:", "\n"],
    },
    verbose=True,
    temperature=1,
    n_ctx=2048
)

# 分割した長文を書く文章ごとにDocumentオブジェクト化
docs = [Document(page_content=t) for t in title_list]

map_prompt_template_path = 'map_prompt.json'
combine_prompt_template_path = 'combine_prompt.json'

chain = load_summarize_chain(
    llm,
    chain_type="map_reduce",
    return_intermediate_steps=True,
    verbose=True,
    map_prompt=load_prompt(map_prompt_template_path), # load_promptはpromtの設定をファイルで読み込んでいる。json, py, yamlファイルで読み込める
    combine_prompt=load_prompt(combine_prompt_template_path)
)

output = chain(
    inputs=docs,
    return_only_outputs=True,
)["output_text"]

print(output)
```

Promptの設定はなくても（たぶん）動きますが、結果を日本語で表示したいので、以下を設定しました。

 'map_prompt.json' は以下のとおり。
```json
{
    "_type": "prompt",
    "input_variables": ["text"],
    "template_path": "map_prompt_template.txt"
}
```
"map_prompt_template.txt" は以下のとおり。
```
次の文章を日本語で要約してください。

文章：
{text}

要約：
```
'combine_prompt.json' は以下のとおり。
```json
{
    "_type": "prompt",
    "input_variables": ["text"],
    "template_path": "combine_prompt.txt"
}
```
"combine_prompt.txt" は以下のとおり。
```
次の文章を日本語で行ごとに箇条書きにまとめてください。

文章：
{text}

まとめ：
```

# load_summarize_chain の結果
　以下の最後の
 > Finished chain.
LLM技術は生成モデルを使ってロボットアームの動作に影響することで、次フェーズへと移行した。

がまとめた結果です。
今回は、テストなので、CiNiiの検索結果を5件しか投入していないので、こんなもんでしょうか・・・

```
> Entering new MapReduceDocumentsChain chain...
> Entering new LLMChain chain...
Prompt after formatting:
次の文章を日本語で要約してください。
文章：
PFNはLLMを自社開発しており、2023年9月には130億パラメータのLLM「PLaMo-13B」を公開した（pp.40-42の記事を参照）注1）。製品版のカチャカにLLMを適用する場合は、この自社開発のLLMを利用する見込みだが、今回のデモではLLMには米OpenAI社の「GPT-4」を利用した。カチャカ…
要約：
Prompt after formatting:
次の文章を日本語で要約してください。
文章：
PFRの家庭向けロボ、大規模言語モデル経由で操作 生成コードはLuaを採用、将来はマルチモーダル型へ
要約：
Prompt after formatting:
次の文章を日本語で要約してください。
文章：
文章や画像、動画といった多彩なコンテンツを人の指示に応じて出力する生成AIの技術が、すさまじい勢いで進化している。研究開発の進展に加えて応用先の開拓も活発だ。様々な業務への適用はもちろん、代表的な生成AIである大規模言語モデル（LLM）をロボット…
...
文章：
類似文書検索と大規模言語モデルを用いた事故記録の解析
要約：
Llama.generate: prefix-match hit
Llama.generate: prefix-match hit
Llama.generate: prefix-match hit
Llama.generate: prefix-match hit
Llama.generate: prefix-match hit
Llama.generate: prefix-match hit
Llama.generate: prefix-match hit
Llama.generate: prefix-match hit
> Finished chain.
> Entering new LLMChain chain...
Prompt after formatting:
次の文章を日本語で行ごとに箇条書きにまとめてください。
文章：
PFNと日本ユニシス株式会社は、2023年1月19日（木）～21日（土）に東京ビッグサイトで開催される「ジャパン・バーチャルリアリティ学術フォーラム （IVR学会）」のブースにおいて、既報のような人物型ソフトウェアロボットを用いた遠隔教育支援サービス「KaChaKa」と人工知能(AI)を活用したクラウドシステム（製品名：KaChaKa）を紹介する予定です。
PFRは、主に家庭向けのロボットアプリケーション向けの大規模言語モデル（LLM）と共同開発したLuaベースの生成コードを通じて操作や生成を行います。
人工知能について
文章中で特に重要な部分は「 YahooとLIME社は、人間の身体のCGを生成するAI技術の開発を発表しました。指定された役割において、2つの相互作用が出力されます。」です。
PFNが公開した大規模言語モデル、PLaMo-13Bは他の同サイズの言語モデルと比較すると日英2言語合わせて高い性能を示している。
1. ＣＥＡTＥC2023の概要
LLMを応用したロボットアームの行動生成AIへの取り組みが新たなフェーズに入ってきました。グーグルは活発に取り組んでおり、「SayCan」や「RT-2」という技術を先駆的に開発しています。LLMのロボットアームへの応用が興る中、注目すべきな点は何でしょうか？
...
 近年、自然言語処理アルゴリズムが飛躍的に発展しており、これらを利用した新しい安全分野への応用が実験されている。
まとめ：
Output is truncated. View as a scrollable element or open in a text editor. Adjust cell output settings...
Llama.generate: prefix-match hit
> Finished chain.
> Finished chain.
LLM技術は生成モデルを使ってロボットアームの動作に影響することで、次フェーズへと移行した。
```

# RetrievalQAで、CiNiiの検索結果から、質問に回答する
　いわゆる「Retriever」ですが、「Index」で関連するドキュメントを検索し、ユーザーの質問に応答するチェーンを作ります。
　LLMの弱点の一つに、最新の情報、特定分野の情報に疎いこと、幻覚を起こすことがあげられていますが、その対策の一つとして、データをLLMに読み込ませて、そこから回答を作成する Retriver があります。今回は、そのデータにさきほど抽出したCiNiiの検索結果を使ってみました。
 　PDFからデータを作るためには、長いテキストをチャンクに分割するためにTextSplitterを使うことがほとんどですが、今回のCiNiiの検索結果は、すでにリスト（配列）形式なので、そのまま使います。

```python
from langchain.chains import RetrievalQA
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.llms import LlamaCpp
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores.faiss import FAISS

# インデックスの作成
index = FAISS.from_texts(
    texts = title_list,
    embedding = HuggingFaceEmbeddings(model_name = "oshizo/sbert-jsnli-luke-japanese-base-lite", encode_kwargs={"normalize_embeddings":True })
)
index.save_local("storage")

# 質問応答チェーンの作成
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=index.as_retriever(search_kwargs={"k": 4}),
    verbose=True,
)

# 質問応答チェーンの実行
print("A1:", qa_chain.run("大規模言語モデルとは何ですか?"))
```

# 質問の結果
"大規模言語モデルとは何ですか?"
と質問したところ、
" 大規模な生成AIシステム"
とのこと。繰り返しになりますが、5件の検索結果をもとにした回答なので、こんなもんでしょう。

```
> Entering new RetrievalQA chain...
> Finished chain.
A1:   大規模な生成AIシステム
```

# おまけ
## AnalyzeDocumentChain

```python
from langchain.chains import AnalyzeDocumentChain
from langchain.chains.question_answering import load_qa_chain

qa_chain = load_qa_chain(llm, chain_type="map_reduce")
qa_document_chain = AnalyzeDocumentChain(combine_docs_chain=qa_chain)
result = qa_document_chain.run(input_document="".join(title_list), question="何についての記事ですか？")
print(result)
```

結果は、
> Llama.generate: prefix-match hit
 Science/Tech

 とのこと。

 # 展望
 　自然文の質問から、TF-IDFかBM25でキーワード抽出をしてCiNiiを検索、検索結果をもとにキーワードをリファインして再検索、検索結果でPDFがあるものはそれも使って、元の質問にCiNiiの引用も付けて回答を作成する、などはインフラなどに投資できれば十分実現可能な気はしました。