LlamaIndex,ローカルで日本語で独自項目を業務アプリからAI回答させる

# はじめに

前の記事で作成したLlamaIndexを使用したAI回答プログラムをWEB-API化し、
ASP.NET等で作成した業務システムから呼び出せるようにします。

前の記事

https://qiita.com/kharada_toyotec/items/515d1c0e8096ac01365f


# 変更点

### (1)AI回答プログラム側
#### flaskでWEB-API化し、JSON形式で回答を返すように変更。さらにAI回答がイマイチだったので、プロンプト等を大きく変更。
```py:記述例 (server.py)
import logging
import os
import json
from flask import Flask, request
from flask_restful import Api, Resource, reqparse

from llama_index import LLMPredictor, ServiceContext, SimpleDirectoryReader, GPTVectorStoreIndex
from llama_index.embeddings import LangchainEmbedding

from llama_index.prompts.prompts import QuestionAnswerPrompt

from langchain.llms import LlamaCpp
from langchain.embeddings.huggingface import HuggingFaceEmbeddings

llm             = LlamaCpp(model_path=f'./models/ELYZA-japanese-Llama-2-7b-fast-instruct-q8_0.gguf', temperature=0, n_ctx=4096, n_gpu_layers=32)

llm_predictor   = LLMPredictor(llm=llm)

embed_model     = LangchainEmbedding(HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-large"))

# chunk_size,chunk_overlap 追加
service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor, embed_model=embed_model, chunk_size=500, chunk_overlap=20)

# ドキュメントのインデックス化
documents = SimpleDirectoryReader('./inputs').load_data()
index     = GPTVectorStoreIndex.from_documents(documents, service_context=service_context)

temp = """




You are an AI assistant for the 〇〇 system. The documentation is located at SOURCE_DOCUMENTS.
You are given the following extracted parts of a long document and a question. 
You should only use source and data that are explicitly listed as a source in the context. 

Do NOT use any external resource, hyperlink or reference to answer that is not listed.

If you don't know the answer, just say "Hmm, I'm not sure." Don't try to make up an answer.
If the question is not about 〇〇 system, politely inform them that you are tuned to only answer questions about 〇〇 system.
If the context is not relevant, please dont answer the question by using your own knowledge about the topic.

Answer polite in Japanese.



{context_str}

question: {query_str}



"""

# similarity_top_k 追加
query_engine = index.as_query_engine(similarity_top_k=3, text_qa_template=QuestionAnswerPrompt(temp))

app = Flask(__name__)
api = Api(app)
parser = reqparse.RequestParser()

class QueryResource(Resource):
    def get(self):
        query_string = request.query_string
        param = request.args.get('param')

        return {
            'query_string':  query_string.decode('utf-8'),
            'param': param
        }

class PostResource(Resource):
    def post(self):
        json = request.get_json(force = True)

        question = json[0]['question']
        res_msg = query_engine.query(question)
        res_msg.source_nodes[0].text

        return { "answer": str(res_msg).strip() }

api.add_resource(QueryResource, '/get')
api.add_resource(PostResource, '/post')

if __name__ == '__main__':
    app.run(host='192.XXX.XXX.XXX', port=int(os.environ.get('PORT', 9200)), debug = True)
```

### (2)呼び出し側
#### aspxにおいて、画面で入力された質問テキストを JSON形式で上記プログラム(server.py)へPOSTし、応答結果を JSON形式で受け取り、画面へセットする。
```js:記述例 途中抜粋 (test.aspx)
        // チャット処理 (ローカル/非OpenAI-API)
        function mfChatLocal() {

            try {
                // POST送信データ (入力された質問テキスト)
                var prompt = GetValue('MainContent_chat');
                var questions = [];
                var question = new Object();
                question.question = prompt;
                questions.push(question);
                var json = JSON.stringify(questions);

                var url = "http://192.XXX.XXX.XXX:9200/post";
                var request = createXMLHttpRequest();

                request.onreadystatechange = function () {

                    if (request.readyState == 4) {

                        if (request.status == 200) {

                            var strAI_Answer = this.response;
                            var jsonResult = JSON.parse(strAI_Answer);

                            document.getElementById('MainContent_answer').value = jsonResult.answer;

                        }
                        else {
                            // 異常発生
                            alert('STATE=' + request.status);
                        }

                    }

                }

                request.open("POST", url, true);
                request.setRequestHeader('Content-Type', 'application/json');
                request.send(json);

            } catch (e) {
                alert(e);
            }

        }
```

# インタフェース

業務アプリケーションとAI回答プログラム間のJSONは下記のような極めて単純な形式としています。

```json:例 (業務アプリからPOSTするJSON)
{question: "ログインできません。"}
```

```json:例 (業務アプリに戻ってくるJSON)
{answer: "XXXXXしてください。"}
```


# まとめ

ASP.NET業務アプリケーションから llamaindex を使用したアプリを呼出し、AIの回答を
得ることができました。