{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "0690e575", "metadata": {}, "outputs": [], "source": [ "\n", "# load files \n", "from langchain_community.document_loaders import FileSystemBlobLoader\n", "from langchain_community.document_loaders.generic import GenericLoader\n", "from langchain_community.document_loaders.parsers import PyPDFParser\n" ] }, { "cell_type": "code", "execution_count": 11, "id": "0793bdad", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Ignoring wrong pointing object 6 0 (offset 0)\n", "Ignoring wrong pointing object 10 0 (offset 0)\n", "Ignoring wrong pointing object 12 0 (offset 0)\n", "Ignoring wrong pointing object 14 0 (offset 0)\n", "Ignoring wrong pointing object 21 0 (offset 0)\n", "Ignoring wrong pointing object 27 0 (offset 0)\n", "Ignoring wrong pointing object 37 0 (offset 0)\n", "Ignoring wrong pointing object 47 0 (offset 0)\n", "Ignoring wrong pointing object 53 0 (offset 0)\n", "Ignoring wrong pointing object 55 0 (offset 0)\n", "Ignoring wrong pointing object 57 0 (offset 0)\n" ] } ], "source": [ "\n", "from langchain_community.document_loaders import FileSystemBlobLoader\n", "from langchain_community.document_loaders.generic import GenericLoader\n", "from langchain_community.document_loaders.parsers import PyPDFParser\n", "\n", "loader = GenericLoader(\n", " blob_loader=FileSystemBlobLoader(\n", " path=\"../\",\n", " glob=f\"data/**/*.pdf\",\n", " ),\n", " blob_parser=PyPDFParser(),\n", ")\n", "\n", "docs = loader.load()" ] }, { "cell_type": "code", "execution_count": 2, "id": "fbbf5838", "metadata": {}, "outputs": [], "source": [ "from langchain_text_splitters import RecursiveCharacterTextSplitter\n", "from transformers import AutoTokenizer\n", "\n", "\n", "chunk_size = 512\n", "tokenizer_name = \"intfloat/e5-base-v2\"\n", "\n", "tokenizer = AutoTokenizer.from_pretrained(tokenizer_name)\n", "text_splitter = RecursiveCharacterTextSplitter.from_huggingface_tokenizer(\n", " AutoTokenizer.from_pretrained(tokenizer_name),\n", " chunk_size=chunk_size,\n", " chunk_overlap=int(chunk_size / 10),\n", " add_start_index=True,\n", " strip_whitespace=True,\n", " separators=\".\",\n", ")\n", "\n", "docs_split = text_splitter.split_documents(docs)" ] }, { "cell_type": "code", "execution_count": 38, "id": "19f9c7cb", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0.5, 0.98, 'Distribution of splitted document lengths in the knowledge base')" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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iVOkZ8+eTvsrPz/fhISEmL59+zrVKywsNK1atTLXX399sX78s6LzzLXXXuv0hcjevXuNl5eXueeee0p97ZkzZ0xeXp7x9/c3L730klUeGxtrbr311jK3W97jCoDKw63jQA1kyrjdTpKuueYaeXt7695779WcOXOK3WpXXgMGDCh33RYtWqhVq1ZOZUOHDlVubq62bt16QdsvrzVr1qhbt26Kjo52Kh8+fLiOHz9e7Fbifv36Oc23bNlSkordrvdn+fn5+vzzzzVw4EDVrl3bKvfw8NBdd92l/fv3l/v28z+7/vrr9dVXX+n+++/XypUrlZubW2rdYcOGOc0PGjRInp6eWrt2rcvbddVHH32kOnXqqG/fvjpz5ow1XXPNNXI4HNZtl0WxlBbr+axdu1YBAQHF9tHQoUOd5r/77jv99ttvuuuuu1Sr1v+dymrXrq0BAwZo48aNOn78uI4fP6709HQNGjTI+rmEO1xzzTWqV6+eNd+8eXNJf9yG7OfnV6y86L31ww8/6Ntvv7X65899efPNNyszM7PY++hC3q+u+uijjxQbG6trrrnGKaaePXuWeFttly5dFBAQYM1HREQoPDzciik/P1+bN2/WrbfeKm9vb6te7dq11bdvX5fjO18fuOuY92e9e/eWh4dHqdu8UA6HQ9dff71TWcuWLZ3W6+r+KMnJkyd16623at68eVq1alWx/8ki5dmX5T32devWTSdOnNCGDRsk/XH7dY8ePdS9e3elpaVZZZLUvXv3UmP/6KOPZLPZdOeddzq13+FwqFWrVlb7MzIydPLkyWJt69ixo2JiYpzK0tPTFRsbW+w3+UOGDHGa37Bhgw4fPqykpCSnbZ89e1a9evXSpk2bynX7/tChQ51+IhETE6OOHTs6Ha/z8vL06KOP6oorrpCnp6c8PT1Vu3Zt5efn65tvvrHqXX/99fr444/12GOPad26dTpx4oTTti7kuAKg4pFoAzVMfn6+Dh06pKioqFLrNGnSRKtXr1Z4eLhGjRqlJk2aqEmTJk6/+y2PyMjIctd1OByllh06dMil7brq0KFDJcZa1Efnbj80NNRp3m63S1KxDy9/duTIERljXNpOeUyYMEH/+te/tHHjRiUmJio0NFTdunUr8Xe95/axp6enQkNDK7x/JenAgQM6evSovL295eXl5TRlZWXp999/l/R/fVBarOdz6NAha5C/Pzt3fUXbKW1/nD17VkeOHNGRI0dUWFjo9gH8QkJCnOaLksnSyk+ePCnpj36UpPHjxxfrx/vvv1+SrL4sciHvV1cdOHBAX3/9dbGYAgICZIw5b0xFcRXFVPT/UtK+LKnsfM7XB+465rmyTXett2jdf16vq/ujJNnZ2Vq5cqU6dOigjh07XnA8rhz7OnbsKD8/P61evVo//PCD9u7dayXan3/+ufLy8rR69Wo1btxYjRo1KjWmAwcOWO+fc/tg48aN5z3elFRW2rHl3LKi/9GBAwcW2/Zzzz0nY4wOHz5cauylbb+o7M/H66FDh+rll1/WPffco5UrV+qLL77Qpk2bVLduXaf3w7///W89+uijWrJkibp06aKQkBDdeuut2r17t1PMrhxXAFQ8Rh0Haphly5apsLDwvM8HvfHGG3XjjTeqsLBQmzdv1vTp0zVmzBhFRETojjvuKNe2XHk2d0mDVRWVFX2Q8/HxkSRrAKoiF/sBIDQ0VJmZmcXKiwZLCgsLu6j1S1JwcLBq1arl9u14enpq7NixGjt2rI4eParVq1fr8ccfV8+ePbVv3z6nq6NZWVlOV1HPnDmjQ4cOlSuBvVhFg1CtWLGixOVFV8SKYikt1vMJDQ0tceC0c99fRdspbX/UqlVLwcHBstls8vDw0P79+8vc7p/fm0UJleT+D6dF75EJEyaof//+Jda58sor3brN8ggLC5Ovr6/efvvtUpe7oqjvixKAPyttYLuL5Y5jXnXhjv3RoEEDTZ06Vbfddpv69++v9957z3qfu8KVY5+3t7duuOEGrV69WvXr15fD4VBcXJwaN24s6Y8BKT/55BP16dOnzG2GhYXJZrPps88+c/p/LFJU9ufjzbmysrKcBgUMDQ0t1/uxqC3Tp08v9YkN5fmyqLSYimLOycnRRx99pIkTJ+qxxx6z6hQUFBRL5P39/TVp0iRNmjRJBw4csK5u9+3bV99++221Pa4AlzuuaAM1yC+//KLx48crKChI9913X7le4+HhoXbt2umVV16RJOs2bndfFdu5c6e++uorp7L58+crICBA1157rSRZH3q+/vprp3pLly4ttr5zr/CUpVu3blqzZk2xUYjfeecd+fn5ueXxVv7+/mrXrp0WLVrkFNfZs2c1d+5c1a9fX82aNbuobdSpU0cDBw7UqFGjdPjw4WKjs8+bN89p/j//+Y/OnDlz3i9dzlXWvi+t3/v06aNDhw6psLBQbdu2LTYVfYgriqW0WM+nS5cuOnbsWLH3xPz5853mr7zyStWrV0/z5893+ilFfn6+3n//fWsk8qLRft97770yk+bS3psffvjheWN2xZVXXqmmTZvqq6++KrEf27Zt63Qbb3m58v9Skj59+ujHH39UaGhoiTG5Ooq5v7+/2rZtqyVLlujUqVNWeV5eXokjWl9s/H9W2jGvsrjj2Oqu/ZGQkKCVK1fq008/VZ8+fS5oxHJXj33du3fXli1b9P7771u3h/v7+6t9+/aaPn26fvvttzJvGy9qvzFGv/76a4ntj4uLk/THowt9fHyKHW82bNhQ7Bb/zp07a8eOHdq1a5dTeWpqqtN8p06dVKdOHe3atavU/9E//xyiNAsWLHA6Nv3888/asGGDdYy02WwyxhT7IuGtt95SYWFhqeuNiIjQ8OHDNWTIEH333Xc6fvx4hR1XAFwcrmgD1dSOHTus31hlZ2frs88+06xZs+Th4aHFixeX+XvT1157TWvWrFHv3r3VoEEDnTx50royUvQBJyAgQDExMfrggw/UrVs3hYSEKCws7IIeCyT9cQthv379lJycrMjISM2dO1dpaWl67rnnrKuy1113na688kqNHz9eZ86cUXBwsBYvXqz169cXW19cXJwWLVqkGTNmqE2bNqpVq5bTc8X/bOLEifroo4/UpUsX/fOf/1RISIjmzZunZcuWacqUKQoKCrqgNp0rJSVFPXr0UJcuXTR+/Hh5e3vr1Vdf1Y4dO7RgwQKX7gAo0rdvX+uZ6XXr1tXPP/+sadOmKSYmRk2bNnWqu2jRInl6eqpHjx7auXOn/vGPf6hVq1YaNGiQS9uMjY2VJL3xxhsKCAiQj4+PGjVqpNDQ0FL7/Y477tC8efN0880368EHH9T1118vLy8v7d+/X2vXrtUtt9yi2267Tc2bN9edd96padOmycvLS927d9eOHTv0r3/9S4GBgeeN7e6779aLL76ou+++W88884yaNm2q5cuXa+XKlU71atWqpSlTpmjYsGHq06eP7rvvPhUUFOj555/X0aNH9eyzz1p1p06dqhtuuEHt2rXTY489piuuuEIHDhzQ0qVL9frrrysgIEA333yzQkJCNGLECD311FPy9PTU7NmztW/fPpf6tjxef/11JSYmqmfPnho+fLjq1aunw4cP65tvvtHWrVv13nvvubzOuLg4paamauHChWrcuLF8fHysZKQ8xowZo/fff1833XSTHnroIbVs2VJnz57VL7/8olWrVmncuHFq166dSzE99dRT6t27t3r27KkHH3xQhYWFev7551W7du1iV+zi4uK0bt06ffjhh4qMjFRAQIBLV+DKc8yrLGX9f5WXO/fHDTfcoE8++US9evVSQkKCli9f7vIx0ZVjX7du3VRYWKhPPvnE6RGL3bt318SJE2Wz2dS1a9cyt9epUyfde++9+p//+R9t3rxZN910k/z9/ZWZman169crLi5Of/vb3xQcHKzx48fr6aef1j333KPbb79d+/btU3JycrFbt8eMGaO3335biYmJeuqppxQREaH58+fr22+/lSRrrIfatWtr+vTpSkpK0uHDhzVw4ECFh4fr4MGD+uqrr3Tw4EHNmDHjvH2WnZ2t2267TSNHjlROTo4mTpwoHx8fTZgwQZIUGBiom266Sc8//7x17k1PT9fMmTNVp04dp3W1a9dOffr0UcuWLRUcHKxvvvlG7777rvWFolQxxxUAF6lqxmADUJqiUXaLJm9vbxMeHm46d+5sJk+eXOLIyeeOmJyRkWFuu+02ExMTY+x2uwkNDTWdO3d2GvnYGGNWr15tWrdubex2u9OI0EXr+/PjRUrbljF/jGDcu3dv89///te0aNHCeHt7m4YNG5qpU6cWe/33339vEhISTGBgoKlbt64ZPXq0WbZsWbFReg8fPmwGDhxo6tSpY2w2m9M2VcJo6du3bzd9+/Y1QUFBxtvb27Rq1arYI02KRoN97733nMqLRhkuzyNQPvvsM9O1a1fj7+9vfH19Tfv27c2HH35Y4vrKM+r4Cy+8YDp27GjCwsKMt7e3adCggRkxYoTZu3evVaeoz7ds2WL69u1rateubQICAsyQIUPMgQMHnNZXnlHHjTFm2rRpplGjRsbDw8Op7WX1++nTp82//vUv06pVK+Pj42Nq165trrrqKnPfffeZ3bt3W/UKCgrMuHHjTHh4uPHx8THt27c3GRkZxUa6Ls3+/fvNgAEDrHYOGDDAbNiwocR9tGTJEtOuXTvj4+Nj/P39Tbdu3ZweI1Rk165d5vbbbzehoaFWPw8fPtwaHdmYP0aV7tixo/H39zf16tUzEydONG+99VaJo4737t272DYkmVGjRjmVlfZe+Oqrr8ygQYNMeHi48fLyMg6Hw3Tt2tW89tprVp3SnkBQ0ij9e/fuNQkJCSYgIMB6FFxZStoXeXl55sknnzRXXnml8fb2NkFBQSYuLs489NBDTo9TKqmdpa1z8eLFJi4uzurzZ5991vz97383wcHBTvW2bdtmOnXqZPz8/JxGiy5vH5T3mFeS0kYdL+n/t6T/pZKU9v917mO3ipT0eKvy7o/S2nTudnbs2GEcDoe59tprrWO7K/uyPMc+Y4w5e/asCQsLM5LMr7/+apUXjQR+7bXXlqv9xhjz9ttvm3bt2lnbbNKkibn77rvN5s2bnbaXkpJioqOjjbe3t2nZsqX58MMPSzwW7tixw3Tv3t34+PiYkJAQM2LECDNnzhwjyXz11VdOddPT003v3r1NSEiI8fLyMvXq1TO9e/cudv44V9F789133zV///vfTd26dY3dbjc33nijU9zG/N+xLjg42AQEBJhevXqZHTt2FOv/xx57zLRt29YEBwcbu91uGjdubB566CHz+++/O62vPMcVAJXHZsx5hi8GAFSp5ORkTZo0SQcPHnTL782BqnL69GlrxPZVq1ZVdTiA7r33Xi1YsECHDh0q1y3hAFBe3DoOAAAqxIgRI9SjRw9FRkYqKytLr732mr755puLGg0cuFBPPfWUoqKi1LhxY2u8gLfeektPPvkkSTYAtyPRBgAAFeLYsWMaP368Dh48KC8vL1177bVavnx5pf9uGpAkLy8vPf/889q/f7/OnDmjpk2baurUqXrwwQerOjQAlyBuHQcAAAAAwI14vBcAAAAAAG5Eog0AAAAAgBuRaAMAAAAA4EYk2gAAAAAAuBGJNi5ps2fPls1m0969e6s6lEozf/58TZs2rULW/eSTT6pBgwby9PRUnTp1Sq23fPlyJScnX9S2GjZsqD59+lzUOgAA1R/navcq77m6PJKTk2Wz2fT777+7J7hKtGHDBiUnJ+vo0aNVHQouUyTauKT17t1bGRkZioyMrOpQKk1Fnbw/+OADPfPMM7r77ruVnp6u1atXl1p3+fLlmjRpkttjAABcejhXu48r5+pL3YYNGzRp0iQSbVQZnqONS1rdunVVt27dqg7jkrBjxw5J0t///neFh4dXcTQAgEsF52r3qann6uPHj8vPz6+qwyiXmhQrqhZXtHFJK+l2tPj4eMXGxmrTpk268cYb5efnp8aNG+vZZ5/V2bNnz7vOs2fPavr06brmmmvk6+urOnXqqH379lq6dKlTnSlTpuiqq66S3W5XeHi47r77bu3fv99pXQ0bNtTw4cOLbSM+Pl7x8fHW/Lp162Sz2bRgwQI98cQTioqKUmBgoLp3767vvvvO6XXLli3Tzz//LJvNZk3na8/5Ym3YsKGefPJJSVJERIRsNlupt4YPHz5cr7zyiiQ5xVC0D06ePKkJEyaoUaNG8vb2Vr169TRq1KhyfeP86quvytPTUxMnTrTKVq9erW7duikwMFB+fn7q1KmTPvnkE6fXFd36tnPnTg0ZMkRBQUGKiIjQX/7yF+Xk5DjVfe+999SuXTsFBQVZ742//OUv540NAHBhOFdX/rm6yOeff66+ffsqNDRUPj4+atKkicaMGVOs3oEDB857/nzllVd00003KTw8XP7+/oqLi9OUKVN0+vTpYv0WGxurTz/9VB07dpSfn591nl24cKESEhIUGRkpX19fNW/eXI899pjy8/Ndij05OVkPP/ywJKlRo0ZWH69bt856/cKFC9WhQwf5+/urdu3a6tmzp7788kunbQwfPly1a9fW9u3blZCQoICAAHXr1k2S9OWXX6pPnz4KDw+X3W5XVFSUevfuXez9g8sXV7RxWcrKytKwYcM0btw4TZw4UYsXL9aECRMUFRWlu+++u8zXDh8+XHPnztWIESP01FNPydvbW1u3bnX6gPC3v/1Nb7zxhh544AH16dNHe/fu1T/+8Q+tW7dOW7duVVhY2AXF/fjjj6tTp0566623lJubq0cffVR9+/bVN998Iw8PD7366qu699579eOPP2rx4sXlWmd5Yl28eLFeeeUVzZw5UytWrFBQUJDq169f4vr+8Y9/KD8/X//973+VkZFhlUdGRsoYo1tvvVWffPKJJkyYoBtvvFFff/21Jk6cqIyMDGVkZMhutxdbpzFGDz/8sP7973/rrbfesj7wzJ07V3fffbduueUWzZkzR15eXnr99dfVs2dPrVy50joZFhkwYIAGDx6sESNGaPv27ZowYYIk6e2335YkZWRkaPDgwRo8eLCSk5Pl4+Ojn3/+WWvWrClXXwIA3Idz9f9x97laklauXKm+ffuqefPmmjp1qho0aKC9e/dq1apVxeqe7/wpST/++KOGDh1qfZH+1Vdf6ZlnntG3337rVE+S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voqPj9fOnTud1lFQUKDRo0crLCxM/v7+6tevn/bv31/ZTXHy6aefqm/fvoqKipLNZtOSJUuclrurXUeOHNFdd92loKAgBQUF6a677tLRo0cruHV/OF8bhw8fXmzftm/f3qlOdW9jSkqKrrvuOgUEBCg8PFy33nqrvvvuO6c6NX1flqeNNX1fzpgxQy1btlRgYKACAwPVoUMHffzxx9bymr4Pi5yvnTV9P6LmnusrwuVwfK4KKSkpstlsGjNmjFVGP5bPr7/+qjvvvFOhoaHy8/PTNddcoy1btljL6cfzO3PmjJ588kk1atRIvr6+aty4sZ566imdPXvWqkM/VgFzGUtNTTVeXl7mzTffNLt27TIPPvig8ff3Nz///HNVh3ZeEydONC1atDCZmZnWlJ2dbS1/9tlnTUBAgHn//ffN9u3bzeDBg01kZKTJzc216vz1r3819erVM2lpaWbr1q2mS5cuplWrVubMmTNV0SRjjDHLly83TzzxhHn//feNJLN48WKn5e5qV69evUxsbKzZsGGD2bBhg4mNjTV9+vSpFm1MSkoyvXr1ctq3hw4dcqpT3dvYs2dPM2vWLLNjxw6zbds207t3b9OgQQOTl5dn1anp+7I8bazp+3Lp0qVm2bJl5rvvvjPfffedefzxx42Xl5fZsWOHMabm78PytrOm78fLXU0+11eEy+H4XNm++OIL07BhQ9OyZUvz4IMPWuX04/kdPnzYxMTEmOHDh5vPP//c7Nmzx6xevdr88MMPVh368fyefvppExoaaj766COzZ88e895775natWubadOmWXXox8p3WSfa119/vfnrX//qVHbVVVeZxx57rIoiKr+JEyeaVq1albjs7NmzxuFwmGeffdYqO3nypAkKCjKvvfaaMcaYo0ePGi8vL5OammrV+fXXX02tWrXMihUrKjT28jo3CXVXu3bt2mUkmY0bN1p1MjIyjCTz7bffVnCrnJWWaN9yyy2lvqamtdEYY7Kzs40kk56eboy5NPfluW005tLcl8HBweatt966JPfhnxW105hLcz9eTmryub4yXA7H54p07Ngx07RpU5OWlmY6d+5sJdr0Y/k8+uij5oYbbih1Of1YPr179zZ/+ctfnMr69+9v7rzzTmMM/VhVLttbx0+dOqUtW7YoISHBqTwhIUEbNmyooqhcs3v3bkVFRalRo0a644479NNPP0mS9uzZo6ysLKe22e12de7c2Wrbli1bdPr0aac6UVFRio2Nrbbtd1e7MjIyFBQUpHbt2ll12rdvr6CgoGrT9nXr1ik8PFzNmjXTyJEjlZ2dbS2riW3MycmRJIWEhEi6NPfluW0scqnsy8LCQqWmpio/P18dOnS4JPehVLydRS6V/Xi5uRTO9RXtcjg+V6RRo0apd+/e6t69u1M5/Vg+S5cuVdu2bXX77bcrPDxcrVu31ptvvmktpx/L54YbbtAnn3yi77//XpL01Vdfaf369br55psl0Y9VxbOqA6gqv//+uwoLCxUREeFUHhERoaysrCqKqvzatWund955R82aNdOBAwf09NNPq2PHjtq5c6cVf0lt+/nnnyVJWVlZ8vb2VnBwcLE61bX97mpXVlaWwsPDi60/PDy8WrQ9MTFRt99+u2JiYrRnzx794x//UNeuXbVlyxbZ7fYa10ZjjMaOHasbbrhBsbGxVnzSpbMvS2qjdGnsy+3bt6tDhw46efKkateurcWLF+vqq6+2TqiXyj4srZ3SpbEfL1c1/Vxf0S6H43NFSk1N1datW7Vp06Ziy+jH8vnpp580Y8YMjR07Vo8//ri++OIL/f3vf5fdbtfdd99NP5bTo48+qpycHF111VXy8PBQYWGhnnnmGQ0ZMkQS78eqctkm2kVsNpvTvDGmWFl1lJiYaP0dFxenDh06qEmTJpozZ441SM+FtK0mtN8d7SqpfnVp++DBg62/Y2Nj1bZtW8XExGjZsmXq379/qa+rrm184IEH9PXXX2v9+vXFll0q+7K0Nl4K+/LKK6/Utm3bdPToUb3//vtKSkpSenp6qbHV1H1YWjuvvvrqS2I/Xu5q6rm+ol0Ox+eKsm/fPj344INatWqVfHx8Sq1HP5bt7Nmzatu2rSZPnixJat26tXbu3KkZM2bo7rvvturRj2VbuHCh5s6dq/nz56tFixbatm2bxowZo6ioKCUlJVn16MfKddneOh4WFiYPD49i375kZ2cX+7anJvD391dcXJx2795tjT5eVtscDodOnTqlI0eOlFqnunFXuxwOhw4cOFBs/QcPHqyWbY+MjFRMTIx2794tqWa1cfTo0Vq6dKnWrl2r+vXrW+WX0r4srY0lqYn70tvbW1dccYXatm2rlJQUtWrVSi+99NIltQ+l0ttZkpq4Hy9Xl9q53p0uh+NzRdqyZYuys7PVpk0beXp6ytPTU+np6fr3v/8tT09Pq430Y9kiIyOtu4eKNG/eXL/88osk3o/l9fDDD+uxxx7THXfcobi4ON1111166KGHlJKSIol+rCqXbaLt7e2tNm3aKC0tzak8LS1NHTt2rKKoLlxBQYG++eYbRUZGqlGjRnI4HE5tO3XqlNLT0622tWnTRl5eXk51MjMztWPHjmrbfne1q0OHDsrJydEXX3xh1fn888+Vk5NTLdt+6NAh7du3T5GRkZJqRhuNMXrggQe0aNEirVmzRo0aNXJafinsy/O1sSQ1cV+eyxijgoKCS2IflqWonSW5FPbj5eJSO9e7w+VwfK4M3bp10/bt27Vt2zZratu2rYYNG6Zt27apcePG9GM5dOrUqdjj5b7//nvFxMRI4v1YXsePH1etWs5pnYeHh/V4L/qxilTgQGvVXtEjP2bOnGl27dplxowZY/z9/c3evXurOrTzGjdunFm3bp356aefzMaNG02fPn1MQECAFfuzzz5rgoKCzKJFi8z27dvNkCFDShzCv379+mb16tVm69atpmvXrlX+eK9jx46ZL7/80nz55ZdGkpk6dar58ssvrcewuKtdvXr1Mi1btjQZGRkmIyPDxMXFVdqjCcpq47Fjx8y4cePMhg0bzJ49e8zatWtNhw4dTL169WpUG//2t7+ZoKAgs27dOqdHIh0/ftyqU9P35fnaeCnsywkTJphPP/3U7Nmzx3z99dfm8ccfN7Vq1TKrVq0yxtT8fViedl4K+/FyV5PP9RXhcjg+V5U/jzpuDP1YHl988YXx9PQ0zzzzjNm9e7eZN2+e8fPzM3PnzrXq0I/nl5SUZOrVq2c93mvRokUmLCzMPPLII1Yd+rHyXdaJtjHGvPLKKyYmJsZ4e3uba6+91unRPNVZ0bPvvLy8TFRUlOnfv7/ZuXOntfzs2bNm4sSJxuFwGLvdbm666Sazfft2p3WcOHHCPPDAAyYkJMT4+vqaPn36mF9++aWym+Jk7dq1RlKxKSkpyRjjvnYdOnTIDBs2zAQEBJiAgAAzbNgwc+TIkSpv4/Hjx01CQoKpW7eu8fLyMg0aNDBJSUnF4q/ubSypfZLMrFmzrDo1fV+er42Xwr78y1/+Yh0f69ata7p162Yl2cbU/H1YpKx2Xgr7ETX3XF8RLofjc1U5N9GmH8vnww8/NLGxscZut5urrrrKvPHGG07L6cfzy83NNQ8++KBp0KCB8fHxMY0bNzZPPPGEKSgosOrQj5XPZowxlXHlHAAAAACAy8Fl+xttAAAAAAAqAok2AAAAAABuRKINAAAAAIAbkWgDAAAAAOBGJNoAAAAAALgRiTYAAAAAAG5Eog0AAAAAgBuRaAMAAAAA4EYk2gAAAAAAuBGJNgAAAAAAbkSiDQAAAACAG/1/CS07dZRElbwAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "\n", "fig, axs = plt.subplots(1,2, figsize=(12,4))\n", "\n", "\n", "n_tokens = [len(tokenizer.encode(a.page_content)) for a in docs]\n", "n_tokens_split = [len(tokenizer.encode(a.page_content)) for a in docs_split]\n", "axs[0].hist(n_tokens)\n", "axs[0].hist(n_tokens_split)\n", "\n", "axs[0].set_title(\"in count of tokens\")\n", "\n", "n_caract = [len(a.page_content) for a in docs]\n", "n_caract_split = [len(a.page_content) for a in docs_split]\n", "\n", "axs[1].hist(n_caract)\n", "axs[1].hist(n_caract_split)\n", "axs[1].set_title(\"in count of characters\")\n", "\n", "plt.suptitle(\"Distribution of splitted document lengths in the knowledge base\")\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "aba59d80", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/dv/gzhyqctn53s9bh23g7tbvl940000gn/T/ipykernel_5272/3187483442.py:4: LangChainDeprecationWarning: The class `HuggingFaceEmbeddings` was deprecated in LangChain 0.2.2 and will be removed in 1.0. An updated version of the class exists in the `langchain-huggingface package and should be used instead. To use it run `pip install -U `langchain-huggingface` and import as `from `langchain_huggingface import HuggingFaceEmbeddings``.\n", " embedding_model = HuggingFaceEmbeddings(\n" ] } ], "source": [ "from langchain_community.embeddings import HuggingFaceEmbeddings # deprecated\n", "# from langchain_huggingface import HuggingFaceEmbeddings\n", "\n", "embedding_model = HuggingFaceEmbeddings(\n", " model_name=tokenizer_name,\n", " # multi_process=True,\n", " model_kwargs={\"device\": \"mps\"}, # use cuda for faster embeddings on nbidia GPUs\n", " encode_kwargs={\"normalize_embeddings\": True}, # Set `True` for cosine similarity\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "da07d2c2", "metadata": {}, "outputs": [], "source": [ "from langchain_community.vectorstores import FAISS\n", "from langchain_community.vectorstores.utils import DistanceStrategy\n", "\n", "KNOWLEDGE_VECTOR_DATABASE = FAISS.from_documents(\n", " docs_split, embedding_model, distance_strategy=DistanceStrategy.COSINE\n", ")\n", "\n", "# Save the vector database\n", "from datetime import datetime \n", "vector_info = str(chunk_size) + \"-\" + tokenizer_name.replace(\"/\",\"-\") + \"-\" + datetime.now().date().isoformat()\n", "KNOWLEDGE_VECTOR_DATABASE.save_local(\"../data/FAISS/{vector_info}\")\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "406da007", "metadata": {}, "outputs": [], "source": [ "import os\n", "from azure.storage.blob import BlobServiceClient\n", "from langchain_community.vectorstores import FAISS\n", "\n", "\n", "def load_from_azure(container_name, local_dir=\"./index\"):\n", " connection_string = os.environ[\"AZURE_CONN_STR\"]\n", " blob_service_client = BlobServiceClient.from_connection_string(connection_string)\n", " container_client = blob_service_client.get_container_client(container_name)\n", "\n", " os.makedirs(local_dir, exist_ok=True)\n", " \n", " # Download all files in the container (index.faiss and index.pkl)\n", " blobs = container_client.list_blobs()\n", " for blob in blobs:\n", " download_file_path = os.path.join(local_dir, blob.name)\n", " with open(download_file_path, \"wb\") as file:\n", " file.write(container_client.download_blob(blob).readall())\n", "\n", "# Download files from Azure\n", "load_from_azure(\"blobcontaineravatarbot\")" ] }, { "cell_type": "code", "execution_count": 11, "id": "32d45df8", "metadata": {}, "outputs": [], "source": [ "from langchain_community.vectorstores import FAISS\n", "from langchain_community.embeddings import HuggingFaceEmbeddings # deprecated\n", "# from langchain_huggingface import HuggingFaceEmbeddings\n", "from langchain_text_splitters import RecursiveCharacterTextSplitter\n", "from transformers import AutoTokenizer\n", "\n", "\n", "\n", "tokenizer_name = \"intfloat/e5-base-v2\"\n", "embedding_model = HuggingFaceEmbeddings(\n", " model_name=tokenizer_name,\n", " # multi_process=True,\n", " model_kwargs={\"device\": \"mps\"}, # use cuda for faster embeddings on nbidia GPUs\n", " encode_kwargs={\"normalize_embeddings\": True}, # Set `True` for cosine similarity\n", ")\n", "\n", "vs = FAISS.load_local(\"../data/FAISS/512-intfloat-e5-base-v2-2026-01-16\",embedding_model,allow_dangerous_deserialization=True)" ] }, { "cell_type": "code", "execution_count": null, "id": "4d166ca0", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(Document(id='5269b130-6f0c-4887-8214-25493e7345f1', metadata={'producer': 'Microsoft® Office Word 2007', 'creator': 'Microsoft® Office Word 2007', 'creationdate': '2014-01-14T13:26:10+01:00', 'author': 'rcazelles', 'moddate': '2014-01-14T13:26:10+01:00', 'source': '../data/research_paper/ENSCM_2013_CAZELLES.pdf', 'total_pages': 196, 'page': 4, 'page_label': '5', 'start_index': 1723}, page_content='. \\nJe remercie sincèrement toutes les personnes qui m’ont permis de mener à bien ce travail : \\nThomas Cacciaguerra, Jullien Drone, Annie Finiels, Philippe Gonzalez , Mourad Guermache , \\nGéraldine Layrac et Peralta Pradial de l’Institut Charles Gehrardt de Montpellier, Ovidiu Ersen et \\nSimona Moldovan de l’Institut de Physique et Chimie des Matériaux de Strasbourg, Jian Liu et \\nMarkus Antonietti de l’Institut Max Planck des Colloides et Interfaces de Potsdam. \\nJe tiens à remer cier chaleureusement Pierre Agulhon, Charlie Basset, Siham Behar, Mélanie \\nBordeaux, Arnaud Chaix, Eddy Dib, Isabelle Girard, Marie-Noëlle Labour, Antoine Lacarrière, \\nAlexander Sachse, Bilel Said, Thibault Terencio, Christophe Trouillefou, Rémi Veneziano, Julian'),\n", " 0.7311910248264899),\n", " (Document(id='87524f98-b762-4e56-a2fb-3c1319cc983c', metadata={'producer': 'Aspose.Pdf for .NET 8.8.0', 'creator': 'Aspose Ltd.', 'creationdate': '2014-04-28T09:28:27-04:00', 'moddate': '2014-04-28T09:28:27-04:00', 'spdf': '1112', 'source': '../data/research_paper/liu2014.pdf', 'total_pages': 16, 'page': 15, 'page_label': '16', 'start_index': -1}, page_content='.; Blank, D. H.; ten Elshof, J. E., Small 2011 , 7, 2709-2713. \\n(42) Liu, Y.; Wang, H.; Wang, Y .; Xu, H.; Li, M.; Shen, H., Chemical Communications 2011 , 47 , 3790-3792. \\n(43) Cazelles, R.; Drone, J.; Fajula, F.; Ersen, O .; Moldovan, S.; Galarneau, A., New J. Chem. 2013 , 37 , 3721-3730. \\n(44) Duan, Z.; Sun, R., Chem. Geol. 2003 , 193 , 257-271. \\n(45) Yadav, R. K.; Baeg, J.-O.; Oh, G. H.; Park, N .-J.; Kong, K.-J.; Kim, J.; Hwang, D. W.; Biswas, S. K., J. Am. Chem. Soc. \\n2012 , 134 , 11455–11461. \\n(46) Zhou, Z.; Hartmann, M., Chem. Soc. Rev. 2013 , 42 , 3894-3912. \\n \\n \\n \\n \\n \\nPage 15 of 15 Physical Chemistry Chemical Physics\\nPhysical Chemistry Chemical Physics Accepted Manuscript\\nPublished on 28 April 2014. Downloaded by University of Waterloo on 10/06/2014 15:40:59. \\nView Article Online\\nDOI: 10.1039/C4CP01348D'),\n", " 0.707401510139031),\n", " (Document(id='d4b16de4-ae6b-43b8-ae1e-c8366d418f95', metadata={'producer': 'Microsoft® Office Word 2007', 'creator': 'Microsoft® Office Word 2007', 'creationdate': '2014-01-14T13:26:10+01:00', 'author': 'rcazelles', 'moddate': '2014-01-14T13:26:10+01:00', 'source': '../data/research_paper/ENSCM_2013_CAZELLES.pdf', 'total_pages': 196, 'page': 0, 'page_label': '1', 'start_index': 0}, page_content=\"Délivré par L’ÉCOLE NATIONALE SUPÉRIEURE DE CHIMIE DE \\nMONTPELLIER \\n \\n \\nPréparée au sein de l’école doctorale Sciences Chimiques \\nEt de l’unité de recherche UMR 5253 \\n \\nSpécialité : Biochimie et Chimie des matériaux \\n \\n \\n \\n \\n \\nPrésentée par Rémi CAZELLES \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\nSoutenue le 13 décembre 2013 devant le jury composé de \\n \\n \\n-Mr Eric MARCEAU, Maître de conférences, UMR 7197 \\nUniversité Pierre et Marie Curie, Paris VI \\nRapporteur \\n-Mme Isabelle CHEVALOT, Professeur, UMR 7274 \\nInstitut National Polytechnique de Loraine, Nancy \\nRapporteur \\n-Mr Joël CHOPINEAU, Professeur, UMR 5253 \\nInstitut Charles Gerhardt de Montpellier \\nExaminateur \\n-Mr Alain WALCARIUS, DR1, UMR 7564 \\nLaboratoire de Chimie Physique et Microbiologie \\npour l’Environnement, Nancy \\nExaminateur \\n-Mr Benjamin ERABLE, CR2, UMR 5503 \\nLaboratoire de Génie Chimique de Toulouse \\nExaminateur \\n-Mme Anne GALARNEAU, DR2, UMR 5253 \\nInstitut Charles Gerhardt de Montpellier \\nDirecteur de thèse \\n \\n \\n[Tapez une citation prise dans le document \\nou la synthèse d'un passage intéressant. Vous \\npouvez placer la zone de texte n'importe où \\ndans le document. Utilisez l'onglet Outils de \\nzone de texte pour modifier la mise en forme \\nde la zone de texte de la citation.] \\nBioconversion du CO2 en méthanol par \\nun système polyenzymatique encapsulé \\ndans des nanocapsules poreuses de silice\"),\n", " 0.7028941154250334),\n", " (Document(id='6a57d507-fa1d-45da-8140-a0c24a95035f', metadata={'producer': 'Microsoft® Office Word 2007', 'creator': 'Microsoft® Office Word 2007', 'creationdate': '2014-01-14T13:26:10+01:00', 'author': 'rcazelles', 'moddate': '2014-01-14T13:26:10+01:00', 'source': '../data/research_paper/ENSCM_2013_CAZELLES.pdf', 'total_pages': 196, 'page': 2, 'page_label': '3', 'start_index': 0}, page_content=\"Délivré par L’ÉCOLE NATIONALE SUPÉRIEURE DE CHIMIE DE \\nMONTPELLIER \\n \\n \\nPréparée au sein de l’école doctorale Sciences Chimiques \\nEt de l’unité de recherche UMR 5253 \\n \\nSpécialité : Biochimie et Chimie des matériaux \\n \\n \\n \\n \\n \\nPrésentée par Rémi CAZELLES \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\nSoutenue le 13 décembre 2013 devant le jury composé de \\n \\n \\n-Mr Eric MARCEAU, Maître de conférences, UMR 7197 \\nUniversité Pierre et Marie Curie, Paris VI \\nRapporteur \\n-Mme Isabelle CHEVALOT, Professeur, UMR 7274 \\nInstitut National Polytechnique de Loraine, Nancy \\nRapporteur \\n-Mr Joël CHOPINEAU, Professeur, UMR 5253 \\nInstitut Charles Gerhardt de Montpellier \\nExaminateur \\n-Mr Alain WALCARIUS, DR1, UMR 7564 \\nLaboratoire de Chimie Physique et Microbiologie \\npour l’Environnement, Nancy \\nExaminateur \\n-Mr Benjamin ERABLE, CR2, UMR 5503 \\nLaboratoire de Génie Chimique de Toulouse \\nExaminateur \\n-Mme Anne GALARNEAU, DR2, UMR 5253 \\nInstitut Charles Gerhardt de Montpellier \\nDirecteur de thèse \\n \\n \\n \\nBioconversion du CO2 en méthanol par \\nun système polyenzymatique encapsulé \\ndans des nanocapsules poreuses de silice \\n[Tapez une citation prise dans le document \\nou la synthèse d'un passage intéressant. Vous \\npouvez placer la zone de texte n'importe où \\ndans le document. Utilisez l'onglet Outils de \\nzone de texte pour modifier la mise en forme \\nde la zone de texte de la citation.]\"),\n", " 0.6995823846564415)]" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "docs = vs._similarity_search_with_relevance_scores(\"remi cazelles research work\")" ] } ], "metadata": { "kernelspec": { "display_name": "genai", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.14" } }, "nbformat": 4, "nbformat_minor": 5 }