Vecteus-v1をLangChain Agentにしてみた

# 概要
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">LocalNovelLLM-projectさんの４つのモデルを例によってElyzaTasks100で評価してみた。Vecteusはなんと3.74の超高スコアを叩き出しました！7Bでこのスコアはあり得ない！GeminiPro（3.85）とかCommand R+（3.83）とかの神々の領域に手を伸ばしています！Ninjaのスコアも相当高い <a href="https://t.co/INZV1fTGaU">pic.twitter.com/INZV1fTGaU</a></p>&mdash; うみゆき@AI研究 (@umiyuki_ai) <a href="https://twitter.com/umiyuki_ai/status/1786947808127594537?ref_src=twsrc%5Etfw">May 5, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
LocalNovelLLM-projectのVecteus-v1を試したところ、流暢に日本語を話したので、Agentとして使えたら便利そうだと思い、試してみました。

参考：[Local-Novel-LLM-project/Vecteus-v1・Hugging Face](https://huggingface.co/Local-Novel-LLM-project/Vecteus-v1)

# 環境・言語など
## マシン
Mac mini 2023 
- Apple M2 Proチップ
- メモリ 32GB
## 言語
Python 3.10.14
## ライブラリ
- langchain 0.1.17
- langchain-community 0.0.36
- langchain-core 0.1.50
- streamlit 1.34.0
- mlx 0.12.2
- mlx-lm  0.12.1

# ソースコードと実行結果
## Vecteus-v1の日本語性能を試す
```python
from mlx_lm import load, generate

model_name = "Local-Novel-LLM-project/Vecteus-v1"
temp_value = 0.7
max_tokens = 758

prompt = """
A chat between a curious user and an artificial intelligence assistant. 
The assistant gives helpful, detailed, and polite answers to the user's questions in Japanese.

USER: こんにちは!
ASSISTANT: こんにちは!

USER: どうして空は青いの？
ASSISTANT: 
"""

model, tokenizer = load(model_name)
response = generate(
  model, 
  tokenizer, 
  prompt=prompt, 
  verbose=True,
  temp = temp_value, 
  max_tokens = max_tokens
  )
print(response)
```
出力
```
Prompt: 136.700 tokens-per-sec
Generation: 11.172 tokens-per-sec
空が青く見える理由は、大気中の成分による光の散乱に関係しています。

地球の大気は、主に窒素と酸素のガスからできています。
これらの気体分子は、太陽から放たれる光をさまざまな波長に散乱させます。

太陽光は、可視光線と呼ばれる波長の範囲に、白色に近いスペクトルを持っています。
この光は、窒素や酸素分子に衝突すると、その分子が振動して光を散乱させます。

波長の短い青色光は、他の色の光よりも散乱されやすい性質があります。
このため、太陽の光が大気中を通過する際に、青色光がより多く散乱され、私たちの目に届きます。

一方、波長の長い赤色光は散乱されにくく、直進する割合が高いので、私たちの目に届きやすい位置にあることが多いです。

光の散乱現象をレイリー散乱と呼びます。
この現象によって、私たちは空を青く見ることができるのです。

ただし、空の色は常に青いわけではありません。
時間や場所、大気中のちりや水蒸気、気温などによっても変化します。

例えば、朝焼けや夕焼けは、大気中のちりや水蒸気によって光が散乱されやすくなるため、赤やオレンジ色に見えます。
また、雲のある日は雲自体が光を反射することで、空の色は変化します。

以上のような理由で、空は私たちにとって美しい多彩な色合いを見せてくれるのです。
```

## Agentにしてみる
```python
from langchain.agents import load_tools, initialize_agent, AgentType
from langchain_community.llms.mlx_pipeline import MLXPipeline
from langchain_community.chat_models.mlx import ChatMLX
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage

max_tokens = 512
temp = 0.7
llm = MLXPipeline.from_model_id(
    "Local-Novel-LLM-project/Vecteus-v1",
    pipeline_kwargs={"max_tokens": max_tokens, "temp": temp},
)

chat_model = ChatMLX(llm=llm)

agent_kwargs = {
    "suffix": """開始!ここからの会話は全て日本語で行われる。

	以前のチャット履歴
	{chat_history}

	新しいインプット: {input}
	{agent_scratchpad}"""
 }

# setup tools
tools = load_tools(["wikipedia", "llm-math"], llm=llm)

agent_chain = initialize_agent(
	tools, 
	chat_model, 
	agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION, 
	agent_kwargs=agent_kwargs,
	handle_parsing_errors=True
	)

response = agent_chain.run({"input":"ゴリラの学名を教えて", "chat_history":[]})
print(response)
```
出力
```
ゴリラの学名は「Gorilla gorilla」です。
```
`initialize_agent`で`handle_parsing_errors=True`を設定しないと、後述のStreamlitで出力されませんでした。
参考：[MLX | LangChain](https://python.langchain.com/docs/integrations/chat/mlx/)
[LangChainのChatbotプロンプトを理解して日本語を出力するように誘導する](https://note.com/tan_z_tan/n/nf405d0d24cf6)

## Streamlitと組み合わせてみる
出力
![Vectus-v1_Chatbot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118714/3fc52920-184d-c621-37cd-38f7e0338412.png)

出力が不自然だったり、「Wikipediaで調べて」と言ってもWikipedia検索をしてくれなかったり、間違ったことを出力したりします。
出力が途中で途切れてしまうのは~~トークン数の設定が少ないためだと思います。~~
トークン数の上限を大きくしても途中で文章が途切れてしまうので、トークン数は原因ではないと思われます。原因がわかったら追記します。
プロンプトをチューニングすれば改善するかもしれませんが、このままではAgentとして利用するのは難しそうです。

一応Wikipedia検索をしてくれるときもあります（画像は失敗しているけど）。
![Vectus_Wikipedia.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118714/db26ca0c-99d8-63e2-558d-1c93367b7685.png)

アプリケーション立ち上げ直後と推論中はメモリ32GBでギリギリでした。
↓推論中のメモリ
![Vectus-v1_RAM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118714/234eb207-7fe7-cc99-1b93-be0ff368c6b6.png)

参考：[ChatGPT/LangChainによるチャットシステム構築［実践］入門](https://gihyo.jp/book/2023/978-4-297-13839-4)

# 感想
Vectus-v1はローカルLLMとは思えないほど自然な日本語で驚きました。
Agentとして活躍できるような日本語ローカルLLMが出てこないか、これからもウォッチし続けていきます。

# 2024/05/10 追記
[こちらのGitHubのissues](https://github.com/langchain-ai/langchain/issues/1559)で、AgentTypeによって正常に動作したり、動作しなかったりすることが記載されていたので、以下のようにAgentの設定を変更したところ、挙動が改善しました。
また、toolのwikipediaは挙動が不安定なので解雇しました。
```diff_python
def create_agent_chain():
  max_tokens = 512
  temp = 0.7
  llm = MLXPipeline.from_model_id(
      "Local-Novel-LLM-project/Vecteus-v1",
      pipeline_kwargs={"max_tokens": max_tokens, "temp": temp},
  )

  chat_model = ChatMLX(llm=llm)

  # setup tools
-  tools = load_tools(["wikipedia", "llm-math"], llm=llm)
+  tools = load_tools(["llm-math", "ddg-search"], llm=llm)

  agent_kwargs = {
-    "suffix": """開始!ここからの会話は全て日本語で行われる。
+    "suffix": """ここからの思考と会話は全て日本語だけで行う。以下の手順で解答を作る。
+    思考の過程やどのようにtoolを使ったかを出力しない。Final Answerだけ出力する。
+    1. ユーザの入力から検索キーワードを作成する。検索キーワードは単語と単語を半角スペースで繋いだものにする。
+    2. 1. で作成した検索キーワードをddg-searchに入力する。
+    3. ddg-searchから得られた結果を用いて解答を作成する。

    以前のチャット履歴
    {chat_history}

    新しいインプット: {input}
    {agent_scratchpad}"""
  }

  return  initialize_agent(
    tools, 
    chat_model, 
-    agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,
+    agent=AgentType.STRUCRED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
    agent_kwargs=agent_kwargs,
    handle_parsing_errors=True,
    verbose=True
    )

```
結果
action_inputの値から、システムプロンプトは効いていそう。
![Vectus-v1_改良版.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118714/cee2fe62-37b4-d88c-299d-6b91671146b3.png)

ターミナルに出力された応答。途中で途切れてしまう理由は未だ不明です。
```
> Entering new AgentExecutor chain...
{
  "action": "duckduckgo_search",
  "action_input": "MAZDA Zoom-Zoomスタジアム 住所"
}

MAZDA Zoom-Zoomスタジアムの住所は、

大阪府大阪市西区千島通4-3-10

です。

以上です。ご質問

> Finished chain.
```