File size: 45,270 Bytes
75e2ab3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 |
import os, tempfile
import re
import json
from pathlib import Path
import streamlit as st
st.set_page_config(page_title="QA_AIボット+要約")
st.title("Q&A-AIボット & 資料要約\n Demo running on just 4Core CPU/4GB RAM")
st.text("【取扱説明書等をアップロードすればその文書に関する質問にAIが回答します。資料の要約もできます。】")
st.text("""※アップロードされたファイルはサーバーには保存されず、ブラウザを閉じるとバイナリデータも自動的に消去されます。""")
#import torch
from typing import Any, List
from datetime import datetime
from llama_index import (
download_loader,
VectorStoreIndex,
ServiceContext,
StorageContext,
SimpleDirectoryReader,
)
from llama_index.postprocessor import SentenceEmbeddingOptimizer
from llama_index.prompts.prompts import QuestionAnswerPrompt
from llama_index.readers import WikipediaReader, Document
from langchain_community.chat_models import ChatOllama
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
from langchain.callbacks.base import BaseCallbackHandler
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import CharacterTextSplitter
from pysummarization.nlpbase.auto_abstractor import AutoAbstractor
from pysummarization.tokenizabledoc.mecab_tokenizer import MeCabTokenizer
from pysummarization.abstractabledoc.top_n_rank_abstractor import TopNRankAbstractor
from pysummarization.abstractabledoc.std_abstractor import StdAbstractor
from pysummarization.nlp_base import NlpBase
from pysummarization.similarityfilter.tfidf_cosine import TfIdfCosine
#from pysummarization.similarityfilter.dice import Dice
if "messages" not in st.session_state:
st.session_state.messages = []
class StreamHandler(BaseCallbackHandler):
def __init__(self, initial_text="お調べしますので少々お待ち下さい。\n\n"):
self.initial_text = initial_text
self.text = initial_text
self.flag = True
def on_llm_start(self, *args: Any, **kwargs: Any):
self.text = self.initial_text
with st.chat_message("assistant"):
self.container = st.empty()
self.container.markdown(self.text+" "+" ")
print("LLM start: ",datetime.now())
def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
if self.flag == True:
print("Stream start: ",datetime.now())
self.flag = False
self.text += token
self.container.markdown(self.text)
def on_llm_end(self, *args: Any, **kwargs: Any) -> None:
st.session_state.messages.append({
"role": "assistant",
"content": self.text
})
print("LLM end: ",datetime.now())
from rake_ja import JapaneseRake
from rake_ja.tokenizer import Tokenizer
tok = Tokenizer()
ja_rake = JapaneseRake()
import chromadb
from llama_index.vector_stores import ChromaVectorStore
import wikipedia
class JaWikipediaReader(WikipediaReader):
def load_wiki(self, pages: List[str], **load_kwargs: Any) -> List[Document]:
"""Load data from the input directory.
Args:
pages (List[str]): List of pages to read.
"""
wikipedia.set_lang('ja')
results = []
for page in pages:
page_content = wikipedia.page(page, **load_kwargs).content
results.append(page_content)
return results
from duckduckgo_search import DDGS
maxsearch_results = 3
def search_general(input_text):
with DDGS() as ddgs:
results = [r for r in ddgs.text(f"{input_text}", region="jp-jp", timelimit="y", max_results=maxsearch_results, safesearch="off")]
print(results)
return results
STORAGE_DIR = "./storage/"
TEMP_DIR = "./temp_data/"
HISTORY_DIR = "./history/"
SUMMARY_DIR = "./summary/"
os.makedirs(STORAGE_DIR, exist_ok=True)
os.makedirs(TEMP_DIR, exist_ok=True)
os.makedirs(HISTORY_DIR, exist_ok=True)
os.makedirs(SUMMARY_DIR, exist_ok=True)
class PDFReader:
def __init__(self):
self.pdf_reader = download_loader("PDFReader", custom_path="local_dir")()
def load_data(self, file_name):
return self.pdf_reader.load_data(file=Path(file_name))
ollama_url = "http://localhost:11434"
ollama_remote = "https://ai.pib.co.jp"
llamacpp_url = "http://localhost:8000/v1"
LM_Studio_url = "http://localhost:1234/v1"
class QAResponseGenerator:
def __init__(self, selected_model, pdf_reader, device=None):
stream_handler = StreamHandler()
#self.llm = ChatOllama(base_url=ollama_remote, model=selected_model, streaming=True, callbacks=[stream_handler], verbose=True)
self.llm = ChatOllama(base_url=ollama_url, model=selected_model, streaming=True, callbacks=[stream_handler], verbose=True)
self.pdf_reader = pdf_reader
if selected_model == "llama3":
self.QA_PROMPT_TMPL = "<|begin_of_text|><|start_header_id|>system<|end_header_id|>\nあなたは日本人のコールセンター管理者です。次の質問に日本語で回答してください。<|eot_id|><|start_header_id|>user<|end_header_id|>\n{query_str}<|eot_id|><start_header_id|>assistant<|end_header_id|><|eot_id|>"
#self.QA_PROMPT_TMPL = "<|begin_of_text|><|start_header_id|>system<|end_header_id|>あなたは日本語で回答するAIアシスタントです。<|eot_id|><|start_header_id|>user<|end_header_id|>\n{query_str}<|eot_id|><start_header_id|>assistant<|end_header_id|><|eot_id|>"
if selected_model == "Elyza":
self.QA_PROMPT_TMPL =("""<s>[INST] <<SYS>>#あなたは誠実で優秀な日本人のコールセンター管理者です。<</SYS>>{query_str} [/INST]""")
#self.QA_PROMPT_TMPL =("""<s>[INST] <<SYS>>#あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>{query_str} [/INST]""")
self.CHAT_REFINE_PROMPT_TMPL_MSGS = ("""<s>[INST] <<SYS>>
"あなたは、既存の回答を改良する際に2つのモードで厳密に動作するQAシステムのエキスパートです。\n"
"1. 新しいコンテキストを使用して元の回答を**書き直す**。\n"
"2. 新しいコンテキストが役に立たない場合は、元の回答を**繰り返す**。\n"
"回答内で元の回答やコンテキストを直接参照しないでください。\n"
"疑問がある場合は、元の答えを繰り返してください。"
"New Context: {context_msg}\n"
<</SYS>>
"Query: {query_str}\n"
"Original Answer: {existing_answer}\n"
"New Answer: "
[/INST]"""
)
if selected_model == "swallow":
self.QA_PROMPT_TMPL ='### 指示:{query_str}\n ### 応答:'
#self.QA_PROMPT_TMPL ="""
#以下の「コンテキスト情報」を元に、質問に回答してください。
# コンテキスト情報
#---------------------
#{context_str}
#---------------------
# 制約条件
#- コンテキスト情報に無い情報は絶対に回答に含めないでください。
#- コンテキスト情報の内容を丸投げするのではなく、ちゃんと文章にして回答してください。
#- 質問の答えを知らない場合は、回答しないで知らない事を伝えてください。\n\n### 指示:\n{query_str}\n\n### 応答:
#"""
if selected_model == "mist300":
###Mistral/Stability系
#self.QA_PROMPT_TMPL = "<s>[INST]あなたは優秀な日本人のコールセンター管理者です。次の質問に回答して下さい。[/INST]</s> [INST]{query_str}[/INST]"
#self.QA_PROMPT_TMPL = r'<s>\n以下は、タスクを説明する指示と、文脈のある入力の組み合わせです。要求を適切に満たす応答を書きなさい。\n[SEP]\n指示:\n{query_str}\n[SEP]\n入力:\n{context_str}\n[SEP]\n応答:\n'
self.QA_PROMPT_TMPL = r'<s>\n以下は、タスクを説明する指示です。要求を適切に満たす応答を書きなさい。\n[SEP]\n指示:\n{query_str}\n[SEP]\n応答:\n'
#self.QA_PROMPT_TMPL ='### 指示:{query_str}\n ### 応答:'
#self.QA_PROMPT_TMPL ='### 指示:{query_str}\n ### 入力:{context_str}\n ### 応答:'
#self.QA_PROMPT_TMPL ="""### 指示:あなたは優秀なコールセンター管理者です。参考文献を元に次の質問に回答して下さい。###質問:{query_str}\n ### 応答:"""
#self.QA_PROMPT_TMPL ="あなたは優秀な日本人のコールセンター管理者です。次の質問に日本語で回答してください。{query_str}"
if selected_model == "TinyLlama":
###Tinyllama
#querystr = "上給水ハイブリッド加湿器のお手入れの仕方を、日本語で教えて下さい。"
#self.QA_PROMPT_TMPL = "<|im_start|>user\n{query_str}<|im_end|>\n<|im_start|>assistant\n"
#system_message = "あなたは優秀な日本人のコールセンター管理者です。"
self.QA_PROMPT_TMPL = "<|system|>\nあなたは日本人です。</s>\n<|user|>\n{query_str}</s>\n<|assistant|>"
if selected_model == "tinycodellamajp":
self.QA_PROMPT_TMPL = "<|im_start|>user\n{query_str}<|im_end|>\n<|im_start|>assistant\n"
if selected_model == "tinyllamamoe":
self.QA_PROMPT_TMPL = "<|システム|>\nあなたは日本人のコールセンターの管理者です。参考情報を元に、日本語で質問に回答してください。</s>\n<|ユーザー|>\n{query_str}</s>\n<|アシスタント|>"
#self.QA_PROMPT_TMPL = "<|system|>\nあなたは日本人のコールセンターの管理者です。次の情報を元に質問に回答してください。\n{context_str}</s>\n<|user|>\n{query_str}</s>\n<|assistant|>"
#self.QA_PROMPT_TMPL = "<|システム|>\nあなたは日本人です。</s>\n<|ユーザー|>\n{query_str}</s>\n<|アシスタント|>"
#self.QA_PROMPT_TMPL = "<|im_start|>user\n{query_str}<|im_end|>\n<|im_start|>assistant\n"
if selected_model == "phillama":
self.QA_PROMPT_TMPL = "<|system|>\nあなたは日本人のコールセンターの管理者です。参考情報を元に質問に日本語で回答してください。<|end|><|user|>{query_str}<|end|><|assistant|>"
if selected_model == "stabilty":
#self.QA_PROMPT_TMPL = "### 指示: {query_str} \n ### 応答:"
#self.QA_PROMPT_TMPL = r"""<s>\n以下は、タスクを説明する指示と、文脈のある入力の組み合わせです。要求を適切に満たす応答を書きなさい。\n[SEP]\n指示:\n{query_str}\n[SEP]\n入力:\n{context_str}\n[SEP]\n応答:\n"""
self.QA_PROMPT_TMPL = r"""<s>\nあなたは優秀なコールセンター管理者です。参考文献を元に次の質問に回答して下さい。\n[SEP]\n指示:\n{query_str}\n[SEP]\n入力:\n{context_str}\n[SEP]\n応答:\n"""
#self.QA_PROMPT_TMPL = r"""<s>\nあなたは優秀なコールセンター管理者です。参考文献を元に次の質問に回答して下さい。\n[SEP]\n指示:\n{query_str}\n[SEP]\n応答:\n"""
#self.QA_PROMPT_TMPL = r'<s>\n以下は、タスクを説明する指示と、文脈のある入力の組み合わせです。要求を適切に満たす応答を書きなさい。\n[SEP]\n指示:\n{query_str}\n[SEP]\n入力:\n{context_str}\n[SEP]\n応答:\n'
if selected_model == "stabilq4":
#self.QA_PROMPT_TMPL = r"""<s>\nあなたは優秀なコールセンター管理者です。入力情報を元に次の質問に回答して下さい。\n[SEP]\n指示:\n{query_str}\n[SEP]\n入力:\n{context_str}\n[SEP]\n応答:\n"""
self.QA_PROMPT_TMPL = """以下は、タスクを説明する指示と、文脈のある入力の組み合わせです。要求を適切に満たす応答を書きなさい。
### 指示:
{query_str}
### 入力:
{context_str}
### 応答:
"""
if selected_model == "stabilq3":
#self.QA_PROMPT_TMPL = """以下は、タスクを説明する指示と、文脈のある入力の組み合わせです。要求を適切に満たす応答を書きなさい。
### 指示:
#{query_str}
### 入力:
#{context_str}
### 応答:
#"""
self.QA_PROMPT_TMPL = '### 指示:{query_str}\n### 応答:'
#self.QA_PROMPT_TMPL = r"""<s>\nあなたは優秀なコールセンター管理者です。次の入力情報を元に指示に応答して下さい。\n[SEP]\n指示:\n{query_str}\n[SEP]\n入力:\n{context_str}\n[SEP]\n応答:\n"""
#self.QA_PROMPT_TMPL = 'ユーザー: {query_str} システム: '
#self.QA_PROMPT_TMPL = """<|ユーザ|>{query_str}<|endoftext|> <|アシスタント|><|endoftext|>"""
#self.QA_PROMPT_TMPL = r"""
#<|system|>あなたは優秀なコールセンター管理者です。参考情報を元に質問に回答して下さい。<|endoftext|>
#<|user|>{query_str}<|endoftext|>
#<|assistant|>
#"""
if selected_model == "stabilzephyr":
#self.QA_PROMPT_TMPL = """<|ユーザ|>{query_str}<|endoftext|> <|アシスタント|><|endoftext|>"""
#self.QA_PROMPT_TMPL = """<|システム|>あなたは優秀なコールセンター管理者です。参考情報を元に質問に回答して下さい。<|endoftext|><|ユーザ|>{query_str}<|endoftext|><|アシスタント|> <|endoftext|>"""
#self.QA_PROMPT_TMPL = """<|ユーザ|>{query_str}<|endoftext|> <|アシスタント|>"""
#self.QA_PROMPT_TMPL = """<|ユーザ|>{query_str}<|endoftext|> <|アシスタント|><|endoftext|>"""
self.QA_PROMPT_TMPL = """
<|system|>あなたは優秀なコールセンター管理者です。参考情報を元に質問に回答して下さい。<|endoftext|>
<|user|>{query_str}<|endoftext|>
<|assistant|>
"""
#self.QA_PROMPT_TMPL = r"""
#<|system|>あなたは優秀なコールセンター管理者です。次の参考情報を元に質問に回答して下さい。{context_str}<|endoftext|>
#<|user|>{query_str}<|endoftext|>
#<|assistant|>
#"""
#self.QA_PROMPT_TMPL = """以下は、タスクを説明する指示と、文脈のある入力の組み合わせです。要求を適切に満たす応答を書きなさい。
### 指示:
#{query_str}
### 入力:
#{context_str}
### 応答:
#"""
if selected_model == "stabil2instruct":
#self.QA_PROMPT_TMPL = """<|ユーザ|>{query_str}<|endoftext|> <|アシスタント|><|endoftext|>"""
self.QA_PROMPT_TMPL = r"""
<|system|>あなたは役立つアシスタントです。<|endoftext|>
<|user|>{query_str}<|endoftext|>
<|assistant|>
"""
#self.QA_PROMPT_TMPL = r"""
#<|system|>あなたは優秀なコールセンター管理者です。次の参考情報を元に質問に回答して下さい。{context_str}<|endoftext|>
#<|user|>{query_str}<|endoftext|>
#<|assistant|>
#"""
if selected_model == "h2o":
#self.QA_PROMPT_TMPL = 'ユーザー: {query_str} システム: '
self.QA_PROMPT_TMPL = "<|im_start|>システム\nあなたは優秀なコールセンター管理者です。次の情報を元に質問に回答して下さい\n{context_str}<|im_end|>\n<|im_start|>ユーザー\n{query_str}<|im_end|>\n<|im_start|>アシスタント\n"
if selected_model == "phi2":
#self.QA_PROMPT_TMPL = """
# <|im_start|>システム
# あなたは日本人の優秀なコールセンター管理者です。
# <|im_end|>
# <|im_start|>ユーザー
# {query_str}
# <|im_end|>
# <|im_start|>アシスタント
#"""
self.QA_PROMPT_TMPL = "<|im_start|>システム\nあなたは優秀なコールセンター管理者です。次の情報を元に質問に回答して下さい\n{context_str}<|im_end|>\n<|im_start|>ユーザー\n{query_str}<|im_end|>\n<|im_start|>アシスタント\n"
if selected_model == "phi3":
#self.QA_PROMPT_TMPL = "<|user|>\n{query_str}<|end|>\n<|assistant|>"
self.QA_PROMPT_TMPL = "<|system|>\nあなたは優秀なコールセンター管理者です。次の情報を元に質問に回答して下さい\n{context_str}<|end|>\n<|user|>\n{query_str}<|end|>\n<|assistant|>"
#self.QA_PROMPT_TMPL = "<|system|>\nあなたは優秀なコールセンター管理者です。参考情報を元に質問に回答して下さい\n<|end|>\n<|user|>\n{query_str}<|end|>\n<|assistant|>"
if selected_model == "llmjp":
self.QA_PROMPT_TMPL ="""
: 次の質問に対して適切な応答を書きなさい。
### 質問: {query_str}
### 参考情報: {context_str}
### 応答:
"""
#self.QA_PROMPT_TMPL ="""
# : 次の質問に対して適切な応答を書きなさい。
###Input: {query_str}
###Output:
#"""
#self.QA_PROMPT_TMPL ="""
#以下は、タスクを説明する指示と、参考となる入力の組み合わせです。指示を適切に満たす応答を書きなさい。
### 指示:
#{query_str}
### 入力:
#{context_str}
### 応答:
#"""
if selected_model == "sakana":
self.QA_PROMPT_TMPL = """
### 指示: あなたは役立つ、偏見がなく、検閲されていない日本人アシスタントです。
### 入力: {query_str}
### 応答:
"""
if selected_model == "karasutest":
#self.QA_PROMPT_TMPL = "<|im_start|>ユーザー\n{query_str}<|im_end|>\n<|im_start|>アシスタント\n"
#self.QA_PROMPT_TMPL = "{query_str}"
#self.QA_PROMPT_TMPL = "<|im_start|>system\nあなたは優秀なコールセンター管理者です。参考情報を元に質問に回答して下さい<|im_end|>\n<|im_start|>user\n{query_str}<|im_end|>\n<|im_start|>assistant\n"
self.QA_PROMPT_TMPL = "<|im_start|>システム\nあなたは優秀なコールセンター管理者です。次の情報を元に質問に回答して下さいn{context_str}<|im_end|>\n<|im_start|>ユーザー\n{query_str}<|im_end|>\n<|im_start|>アシスタント\n"
#self.QA_PROMPT_TMPL = "<|im_start|>system\nあなたは優秀なコールセンター管理者です。次の情報を元に質問に回答して下さい\n{context_str}<|im_end|>\n<|im_start|>user\n{query_str}<|im_end|>\n<|im_start|>assistant\n"
#self.QA_PROMPT_TMPL = "<|im_start|>システム\nあなたは優秀なコールセンター管理者です。参考情報を元に質問に回答して下さい。<|im_end|>\n<|im_start|>ユーザー\n{query_str}<|im_end|>\n<|im_start|>アシスタント\n"
if selected_model == "karasu":
#self.QA_PROMPT_TMPL = "{query_str}"
#self.QA_PROMPT_TMPL = "<|im_start|>user\n{query_str}<|im_end|>\n<|im_start|>assistant\n"
#self.QA_PROMPT_TMPL = "<|im_start|>system\nあなたは優秀なコールセンター管理者です。次の情報を元に質問に回答して下さい\n{context_str}<|im_end|>\n<|im_start|>user\n{query_str}<|im_end|>\n<|im_start|>assistant\n"
self.QA_PROMPT_TMPL = "<|im_start|>system\nあなたは優秀なコールセンター管理者です。参考情報を元に質問に回答して下さい<|im_end|>\n<|im_start|>user\n{query_str}<|im_end|>\n<|im_start|>assistant\n"
if selected_model == "karasu_slerp1":
self.QA_PROMPT_TMPL = "{query_str}"
if selected_model == "karasu_slerp2":
#self.QA_PROMPT_TMPL = "{query_str}"
#self.QA_PROMPT_TMPL = "<|im_start|>user\n{query_str}<|im_end|>\n<|im_start|>assistant\n"
#self.QA_PROMPT_TMPL = "<|im_start|>system\nあなたは優秀なコールセンター管理者です。次の情報を元に質問に回答して下さい\n{context_str}<|im_end|>\n<|im_start|>user\n{query_str}<|im_end|>\n<|im_start|>assistant\n"
self.QA_PROMPT_TMPL = """
<|im_start|>system
あなたは優秀なコールセンター管理者です。次の情報を元に質問に回答して下さい
{context_str}<|im_end|>
<|im_start|>user
{query_str}<|im_end|>
<|im_start|>assistant
"""
if selected_model == "suzume":
#self.QA_PROMPT_TMPL = "あなたは優秀なコールセンター管理者です。\n{query_str}"
self.QA_PROMPT_TMPL = "{query_str}"
if selected_model == "line":
self.QA_PROMPT_TMPL = 'ユーザー: {query_str} システム: '
if selected_model == "mist-merge":
self.QA_PROMPT_TMPL = r"""<s>\nあなたは優秀なコールセンター管理者です。参考文献を元に次の質問に回答して下さい。\n[SEP]\n指示:\n{query_str}\n[SEP]\n入力:\n{context_str}\n[SEP]\n応答:\n"""
#self.QA_PROMPT_TMPL ='### 指示:{query_str}\n ### 応答:'
if selected_model == "rinna1":
self.QA_PROMPT_TMPL = r"""<s>\nあなたは優秀なコールセンター管理者です。参考文献を元に次の質問に回答して下さい。\n[SEP]\n指示:\n{query_str}\n[SEP]\n入力:\n{context_str}\n[SEP]\n応答:\n"""
#self.QA_PROMPT_TMPL ='### 指示:{query_str}\n ### 応答:'
if selected_model == "stockmark":
#self.QA_PROMPT_TMPL ='{query_str}\n'
self.QA_PROMPT_TMPL ="""
#「コンテキスト情報」を元に、質問に回答してください。
###質問:{query_str}
###「コンテキスト情報」:{context_str}
###回答:
"""
if selected_model == "rinna":
self.QA_PROMPT_TMPL ='ユーザー: {query_str} システム: '
#self.QA_PROMPT_TMPL ="""
#「コンテキスト情報」を元に、質問に回答してください。
###質問:{query_str}
###「コンテキスト情報」:{context_str}
###回答:
#"""
if device is None:
#device = "mps" if torch.backends.mps.is_available() else "cpu" #cubaをmpsに変更
device = "cpu"
#self.device = torch.device(device)
self.device = "cpu"
#print("torch.device,device:",torch.device(device),device)
self.embed_model = HuggingFaceEmbeddings(model_name="cheonboy/sentence_embedding_japanese", model_kwargs={"device": self.device})
#self.embed_model = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-base", model_kwargs={"device": self.device})
self.service_context = ServiceContext.from_defaults(llm=self.llm, embed_model=self.embed_model)
def generate(self, question, file_name, uploaded_file, maxsearch_results):
#質問文からキーワドを抽出
print(question)
text = question
#import re
#keywords = re.split('[のはがをとに]', text)
#exclude_kyw = ""
tokens = tok.tokenize(text)
ja_rake.extract_keywords_from_text(tokens)
ranked_phrases = ja_rake.get_ranked_phrases_with_scores()
print("ranked_phrases: ", ranked_phrases)
keyphrases = [x[0] for x in ranked_phrases], [x[1] for x in ranked_phrases]
keywords = [x[1] for x in ranked_phrases]
print("keyphrases: ", keyphrases, "keyphrases[0]: ", keyphrases[0])
print("keypwords: ", keywords)
#PDF検索処理
try:
db2 = chromadb.PersistentClient(path="./chroma_db")
chroma_collection = db2.get_collection(file_name)
print("chroma collection count: ",chroma_collection.count())
#chroma_st_conn =
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
pdf_index = VectorStoreIndex.from_vector_store(
vector_store,
service_context=self.service_context,
)
print("load existing file..")
except: #FileNotFoundError
print("loaddata from pdf file")
pdf_documents = self.pdf_reader.load_data(file_name)
#print("pdf_doc:", pdf_documents)
db = chromadb.PersistentClient(path="./chroma_db")
chroma_collection = db.get_or_create_collection(
name=uploaded_file.name,
metadata={"hnsw:space": "cosine"}
)
print("chroma collection count: ",chroma_collection.count())
vector_store = ChromaVectorStore(
chroma_collection=chroma_collection,
)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
pdf_index = VectorStoreIndex.from_documents(
pdf_documents, storage_context=storage_context, service_context=self.service_context
)
print("save new file..")
#chat履歴をIndexに追加 sourceが書き変わってしまうのでsourceを削除
#history_docs = SimpleDirectoryReader("./history").load_data()
#for d in history_docs:
# pdf_index.insert(document = d, service_context = self.service_context)
#summaryをIndexに追加
#summary_docs = SimpleDirectoryReader(f"./summary/{file_name[:file_name.find('.')]}").load_data()
#summary_docs = SimpleDirectoryReader(
# input_files=[
# "./summary/"+str(file_name)[:file_name.find('.')]+"_summary1.txt",
# "./summary/"+str(file_name)[:file_name.find('.')]+"_summary2.txt"
# ]
#)
#summary_docs = SimpleDirectoryReader("./summary").load_data()
#print(type(summary_docs), summary_docs)
#for d in summary_docs:
# pdf_index.insert(document = d, service_context = self.service_context)
#pdf_index.insert(document = summary_docs, service_context = self.service_context)
pdf_engine = pdf_index.as_query_engine(
similarity_top_k=2,
text_qa_template=QuestionAnswerPrompt(self.QA_PROMPT_TMPL),
required_keywords= keywords[0] if keyphrases[0][0] > 0.5 else "",
#required_keywords=[keywords[0]]
#required_keywords=[keywords],
node_postprocessors=[SentenceEmbeddingOptimizer(embed_model=self.service_context.embed_model, threshold_cutoff=0.4)],
#node_postprocessors=[SentenceEmbeddingOptimizer(embed_model=self.service_context.embed_model,percentile_cutoff=0.9)],
#streaming=True,
)
try:
pdf_result = pdf_engine.query(question)
#pdf_result.print_response_stream()
print("Num of nodes:",len(pdf_result.source_nodes))
if len(pdf_result.source_nodes) == 1:
print("source_nodes.SCORE: ", pdf_result.source_nodes[0].score)
else:
print("source_nodes.SCORE: ", pdf_result.source_nodes[0].score, pdf_result.source_nodes[1].score)
#return pdf_result.response, pdf_result.get_formatted_sources(1000)
#match = re.findall('[1-9]+'+'.', pdf_result.response)
#print(pdf_result.response.replace(match, "\n"+match))
#return pdf_result.response.replace("\n", " "+" \n"), pdf_result.source_nodes[0].text.strip().replace(" ", "")
print("count before", chroma_collection.count())
doc_to_update = chroma_collection.get() #(limit=10)
chroma_collection.delete(ids=[id for id in doc_to_update["ids"]])
#print("count after", chroma_collection.count())
return pdf_result.response, pdf_result.source_nodes[0].text.strip().replace(" ", "")
#return re.sub("[1-9]+.", "\n[1-9]+.", pdf_result.response).replace("。", "。\n"), pdf_result.source_nodes[0].text.strip().replace(" ", "")
except ValueError as e:
print("PDF Error: ",e)
#st.session_state["chat_history"].append({"assistant": "所定のドキュメンには適切な情報が無いため、ネット検索で回答します(あくまで参考情報ですので、ご自身での確認をお勧めします)。"})
#st.write["chat_history"].append({"assistant": "所定のドキュメンには適切な情報が無いため、ネット検索で回答します(あくまで参考情報ですので、ご自身での確認をお勧めします)。"})
#検索前処理
count = len(keywords)
search_words = ""
for index in range(count): #最初と最後のワードを除く(製品名と製品番号があるとベター)
search_words += " " + keywords[index]
print("serch_words: ",search_words)
try:
#Wikipedia
wikidocuments = JaWikipediaReader().load_wiki(pages=[search_words])
strwikidocuments = "".join(map(str, wikidocuments))
#print("Wiki Contents: ",strwikidocuments)
with open("./temp_data/wiki/wikisearch.txt", "w") as fp:
fp.write(strwikidocuments)
wiki_documents = SimpleDirectoryReader("./temp_data//wiki").load_data()
wiki_index = VectorStoreIndex.from_documents(wiki_documents, service_context=self.service_context)
wiki_index.storage_context.persist(persist_dir=f"{STORAGE_DIR}"+"wikisearch.txt")
wiki_engine = wiki_index.as_query_engine(
similarity_top_k=2,
#text_qa_template=QuestionAnswerPrompt(self.QA_PROMPT_TMPL),
refine_template=QuestionAnswerPrompt(self.CHAT_REFINE_PROMPT_TMPL_MSGS),
#required_keywords=[keywords[0],keywords[1]],
#required_keywords=[search_words],
#exclude_keywords=[exclude_kyw],
#streaming=True,
)
#extend_answer(st.session_state["chat_history"])
wiki_result = wiki_engine.query(question)
#return wiki_result.print_response_stream, wiki_result.get_formatted_sources(1000)
return wiki_result.response, wiki_result.get_formatted_sources(1000)
except Exception as e:
#st.error(f"Error: {e}")
print("Wiki Error: ",e)
#Duck Search
try:
search_results = search_general(search_words)
#search_results = search_general(keywords[1])
#search_results = search_general(question)
with open("./temp_data/duck/ducksearch.txt", "w") as fp:
contents = ""
for index in range(maxsearch_results):
contents += json.dumps(search_results[index]).encode().decode('unicode-escape')
fp.write(contents)
web_documents = SimpleDirectoryReader("./temp_data/duck").load_data()
web_index = VectorStoreIndex.from_documents(web_documents, service_context=self.service_context)
web_index.storage_context.persist(persist_dir=f"{STORAGE_DIR}"+"ducksearch.txt")
web_engine = web_index.as_query_engine(
similarity_top_k=3,
#text_qa_template=QuestionAnswerPrompt(self.QA_PROMPT_TMPL),
refine_template=QuestionAnswerPrompt(self.CHAT_REFINE_PROMPT_TMPL_MSGS),
required_keywords=[search_words],
#required_keywords=[keywords[0],keywords[1],keywords[2]],
#exclude_keywords=[exclude_kyw],
#streaming=True,
)
#extend_answer(st.session_state["chat_history"])
web_result = web_engine.query(question)
#return web_result.print_response_stream, web_result.get_formatted_sources(1000)
return web_result.response, web_result.get_formatted_sources(1000)
except Exception as e:
#st.error(f"Error: {e}")
print("Duck Error: ",e)
#ここにWiki検索を追加する
return "現在Web検索が停止中。", "Wikipediaの検索結果で代替えします。"
def save_uploaded_file(uploaded_file, save_dir):
try:
with open(os.path.join(save_dir, uploaded_file.name), "wb") as f:
f.write(uploaded_file.getvalue())
return True
except Exception as e:
st.error(f"Error: {e}")
return False
def upload_pdf_file():
uploaded_file = st.sidebar.file_uploader("ファイルアップロード", type=["pdf", "txt"])
print("uploaded_file",uploaded_file)
if uploaded_file is not None:
st.success(f"ファイル {uploaded_file.name} のアップロードが完了しました.")
return uploaded_file
# if uploaded_file.type == "application/pdf" and save_uploaded_file(uploaded_file, PDF_DATA_DIR):
# st.success(f"ファイル {uploaded_file.name} は {PDF_DATA_DIR}に保存されました.")
# elif uploaded_file.type == "text/plain" and save_uploaded_file(uploaded_file, TXT_DATA_DIR):
# st.success(f"ファイル {uploaded_file.name} は {TXT_DATA_DIR}に保存されました.")
# else:
# st.error("このファイルは保存できません.")
def main():
#st.title('PDF Q&A app')
#file_list = []
#for file in os.listdir(PDF_DATA_DIR):
# if file.endswith(".pdf") and not file.startswith("._"):
# file_list.append(file)
#for file in os.listdir(TXT_DATA_DIR):
# if file.endswith(".txt") and not file.startswith("._"):
# file_list.append(file)
#file_name = st.sidebar.selectbox("ファイルの選択", file_list)
#if uploaded_file is not None:
# file_name = uploaded_file.name
# print("file_name",file_name)
pdf_reader = PDFReader()
#uploaded_file = upload_pdf_file()
uploaded_file = st.sidebar.file_uploader("ファイルアップロード", type=["pdf", "txt"])
if uploaded_file is not None:
st.sidebar.success(f"{uploaded_file.name} のアップロード完了")
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
tmp_file.write(uploaded_file.read())
#selected_model = st.sidebar.selectbox("生成AIモデルの選択", ["phillama", "llama3", "line", "llmjp", "mist300", "TinyLlama", "stabilty", "stabilq4", "stabilq3", "Elyza", "swallow", "sakana", "karasu", "karasu_slerp1", "karasu_slerp2", "karasutest", "tinycodellamajp", "stockmark", "stabilzephyr", "h2o", "rinna", "phi2", "phi3", "suzume"])
selected_model = st.sidebar.selectbox("生成AIモデルの選択", ["標準", "次候補", "試行版", "llmjp"])
if selected_model == "標準":
selected_model = "line"
elif selected_model == "次候補":
selected_model = "karasu_slerp2" #"stabil2instruct"
elif selected_model == "試行版":
selected_model = "mist300" #"tinycodellamajp","karasu"
elif selected_model == "llmjp":
selected_model = "llmjp"
choice = st.radio("参照情報を表示:", ["表示する", "表示しない"])
question = st.text_input("質問入力")
response_generator = QAResponseGenerator(selected_model, pdf_reader)
# メインの画面に質問送信ボタンを設定
submit_question = st.button("質問")
clear_chat = st.sidebar.button("履歴消去")
st.session_state.last_updated = ""
st.session_state.last_updated_json = []
# チャット履歴を保存
if "chat_history" not in st.session_state:
st.session_state["chat_history"] = []
if clear_chat:
st.session_state["chat_history"] = []
st.session_state.last_updated = ""
st.session_state.last_updated_json = []
# 質問ボタンがクリックされた場合の処理
if submit_question:
print("pushed question button!")
if question: # 質問が入力されている場合
#response, source = response_generator.generate(question, file.name, maxsearch_results)
response, source = response_generator.generate(question, tmp_file.name, uploaded_file, maxsearch_results)
#response = re.sub("[1-9]"+".", "\n[1-9]"+".", response).replace("。", "。\n")
# 質問と応答をチャット履歴に追加
#response = response.replace("\n", " "+" \n")
st.session_state["chat_history"].append({"user": question})
st.session_state["chat_history"].append({"assistant": response})
if choice == "表示する":
source = source.replace("page_label", "該当ページ番号").replace("file_name", " ファイル名:"+uploaded_file.name)
response = f"\n\n参照した情報は次の通りです:\n\n{source}"
#st.session_state["chat_history"].append({"assistant": response})
#stored_response += f"\n\n参照した情報は次の通りです:\n\n{source}"
with st.chat_message("assistant"): #参照情報の表示
st.markdown(response)
#st.session_state.last_updated += json.dumps(st.session_state["chat_history"],indent=2, ensure_ascii=False).replace("]", ",\n{")+re.sub(r"\s", "", f"""\"source\":\"{source}\""""+"\n}\n]")
st.session_state.last_updated += json.dumps(st.session_state["chat_history"],indent=2, ensure_ascii=False)
with open("./history/chat_history.txt","w") as o:
print(st.session_state.last_updated,sep="",file=o)
with open("./history/chat_history.txt", "r") as f:
history_str = f.read()
history_json = json.loads(history_str)
#print(history_json)
with open("./history/chat_history.json", "w") as o:
json.dump(history_json, o, ensure_ascii=False)
elif st.button("要約開始"):
stream_handler = StreamHandler()
try:
with st.spinner('要約中...'):
#print("file_name",file_name)
if uploaded_file.name.endswith(".pdf"):
pdf_reader = download_loader("PDFReader", custom_path="local_dir")()
#docs = pdf_reader.load_data(file=PDF_DATA_DIR+file_name)
docs = pdf_reader.load_data(file=tmp_file.name)
alltext = ""
all_count = len(docs)
for count in range(all_count):
text = docs[count].text
alltext += text
#print("全文字数:",len(alltext))
elif uploaded_file.name.endswith(".txt"):
#loader = TextLoader(TXT_DATA_DIR+file_name)
loader = TextLoader(tmp_file.name)
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)
allcount = len(docs)
alltext = ""
for n in range(allcount):
text = docs[n].page_content
alltext += text
#print("alltext:",alltext)
#Summarize By pysummarization
auto_abstractor = AutoAbstractor()
auto_abstractor.tokenizable_doc = MeCabTokenizer()
auto_abstractor.delimiter_list = ["。", "\n"]
abstractable_doc = StdAbstractor()
result_dict = auto_abstractor.summarize(alltext, abstractable_doc)
summary1 = "Original word count(原文文字数): "+str(len(alltext))+"\n\n"
for x in result_dict["summarize_result"]:
summary1 += x+"\n"
summary1 = summary1+"\nNormal Summary word count(標準要約文字数): "+str(len(summary1))+" compression ratio(圧縮率): "+'{:.0%}'.format(len(summary1)/len(alltext))
similarity_limit = 0.8 #@param {type:"slider", min:0.05, max:0.5, step:0.05}
nlp_base = NlpBase()
nlp_base.tokenizable_doc = MeCabTokenizer()
similarity_filter = TfIdfCosine() #Cosine類似度
#similarity_filter = Jaccard()
#similarity_filter = Dice()
#similarity_filter = Simpson()
similarity_filter.nlp_base = nlp_base
similarity_filter.similarity_limit = similarity_limit
result_dict2 = auto_abstractor.summarize(alltext, abstractable_doc, similarity_filter)
summary2 = ''
for sentence in result_dict2["summarize_result"]:
summary2 += sentence+"\n"
summary2 = summary2+"\nFiltered Summary word count(フィルタ要約文字数): "+str(len(summary2))+" compression ratio(圧縮率): "+'{:.0%}'.format(len(summary2)/len(alltext))
auto_abstractor = AutoAbstractor()
auto_abstractor.tokenizable_doc = MeCabTokenizer()
auto_abstractor.delimiter_list = ["。", "\n"]
abstractable_doc = TopNRankAbstractor()
result_dict = auto_abstractor.summarize(alltext, abstractable_doc)
summary3 = ''
for x in result_dict["summarize_result"]:
summary3 += x+"\n"
summary3 = summary3+"\nTopNRank word count(トップNランク要約文字数): "+str(len(summary3))+" compression ratio(圧縮率): "+'{:.0%}'.format(len(summary3)/len(alltext))
#print("TopNrank_Sum文字数: ", len(summary3))
st.success("[pysum_nomal]\n"+summary1)
st.success("[pysum_filtered]\n"+summary2)
st.success("[pysum_topnrank]\n"+summary3)
st.download_button('要約1をダウンロード', summary1, file_name=uploaded_file.name[:uploaded_file.name.find('.')]+'_summary1.txt')
st.download_button('要約2をダウンロード', summary2, file_name=uploaded_file.name[:uploaded_file.name.find('.')]+'_summary2.txt')
st.download_button('要約2をダウンロード', summary3, file_name=uploaded_file.name[:uploaded_file.name.find('.')]+'_summary3.txt')
#with open("./summary/summary1.txt","w") as o:
# print(summary1,sep="",file=o)
#with open("./summary/summary2.txt","w") as o:
# print(summary2,sep="",file=o)
except Exception as e:
if 'NoneType' in str(e):
st.success("ファイルがアップロードされてません。先にアップロードして下さい。")
else:
st.exception(f"An error occurred: {e}")
if __name__ == "__main__":
try:
main()
except Exception as e:
if "'tmp_file' referenced before assignment" in str(e) :
st.success("ファイルがアップロードされてません。先にアップロードして下さい。")
else:
st.exception(f"An error occurred: {e}")
|