{"id":457029,"date":"2025-04-23T09:01:50","date_gmt":"2025-04-23T09:01:50","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=457029"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=457029","title":{"rendered":"<span>\u041b\u0438\u0447\u043d\u044b\u0439 \u0418\u0418-\u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043d\u0442 \u043d\u0430 \u0432\u0430\u0448\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u0427\u0430\u0441\u0442\u044c 2: \u0412\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0438 \u0441\u0442\u0440\u0438\u043c\u0438\u043d\u0433 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u043e\u0442 \u0418\u0418<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0414\u0440\u0443\u0437\u044c\u044f, \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e!<\/p>\n<p>\u041a\u0430\u043a \u0432\u044b \u043f\u043e\u043d\u044f\u043b\u0438 \u043f\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044e \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u2014 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u043f\u043e\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0432 \u0442\u0435\u043c\u0443 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043b\u0438\u0447\u043d\u043e\u0433\u043e \u0418\u0418-\u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043d\u0442\u0430 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<h3>\u041a\u0440\u0430\u0442\u043a\u043e\u0435 \u043d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u0435 \u043e \u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438<\/h3>\n<p>\u041d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u044e, \u0447\u0442\u043e \u0432 <a href=\"https:\/\/habr.com\/ru\/companies\/amvera\/articles\/897830\/\">\u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 \u0447\u0430\u0441\u0442\u0438<\/a> \u043c\u044b:<\/p>\n<ul>\n<li>\n<p>\u0420\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c \u0441 \u043f\u043e\u043d\u044f\u0442\u0438\u0435\u043c \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0443\u0447\u0438\u043b\u0438\u0441\u044c \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0431\u0430\u0437\u0443<\/p>\n<\/li>\n<li>\n<p>\u0418\u0437\u0443\u0447\u0438\u043b\u0438 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c &#171;\u0443\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430&#187;<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u043b\u0438\u0441\u044c \u0441 \u043c\u043e\u0449\u043d\u044b\u043c Python-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c Langchain<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u043b\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0439 ChatGPT \u0438 Deepseek<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u043b\u0438 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0434\u0438\u0430\u043b\u043e\u0433\u0430 \u0441 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0431\u0430\u0437\u044b \u0437\u043d\u0430\u043d\u0438\u0439 (\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_chromadb_part2\">Amvera Cloud<\/a>)<\/p>\n<\/li>\n<\/ul>\n<h3>\u041e \u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0435\u0439 \u0447\u0430\u0441\u0442\u0438<\/h3>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u043f\u0440\u043e\u043a\u0430\u0447\u0430\u0435\u043c \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442 \u2014 \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u043c \u0435\u0433\u043e \u0432 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0438\u0441 \u0441 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u043c \u0432\u0435\u0431-\u0447\u0430\u0442\u043e\u043c \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c\u0438. \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u043d\u0435\u043a\u043e\u0435 \u043f\u043e\u0434\u043e\u0431\u0438\u0435 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u0441\u0430\u0439\u0442\u0430\u00a0<a href=\"https:\/\/chat.openai.com\/chat\">https:\/\/chat.openai.com\/chat<\/a>, \u043d\u043e \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0432\u044b\u0431\u043e\u0440\u0430 \u043c\u0435\u0436\u0434\u0443 Deepseek \u0438 ChatGPT, \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u043c\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043d\u0430\u0448\u0435\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0431\u0430\u0437\u044b \u0437\u043d\u0430\u043d\u0438\u0439.<\/p>\n<p>\u0414\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c, \u044f \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043b \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 \u0432\u0438\u0434\u0435\u043e-\u043e\u0431\u0437\u043e\u0440 \u0441 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438:\u00a0<a href=\"https:\/\/www.youtube.com\/watch?v=Nb6wCNMjTl0\">\u0412\u0438\u0434\u0435\u043e-\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a><\/p>\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/6806416d488f69a8080e82b2\" data-style=\"\" id=\"6806416d488f69a8080e82b2\" width=\"\"><\/div>\n<h3>\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u044f<\/h3>\n<p>\u0414\u043b\u044f \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u044f \u0432\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f:<\/p>\n<ul>\n<li>\n<p>\u0413\u043e\u0442\u043e\u0432\u0430\u044f \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u0430\u044f \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 (\u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_chromadb_part2\">Amvera Cloud<\/a>)<\/p>\n<\/li>\n<li>\n<p>API-\u0442\u043e\u043a\u0435\u043d\u044b \u0434\u043b\u044f ChatGPT \u0438 Deepseek<\/p>\n<\/li>\n<li>\n<p>\u0411\u0430\u0437\u043e\u0432\u043e\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u043e\u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0431\u044d\u043a\u0435\u043d\u0434\u0430 \u043d\u0430 FastAPI \u0438 \u043e\u0441\u043d\u043e\u0432 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438<\/p>\n<\/li>\n<\/ul>\n<h3>\u041f\u043b\u0430\u043d \u0440\u0430\u0431\u043e\u0442\u044b<\/h3>\n<p>\u0412 \u0445\u043e\u0434\u0435 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u044b:<\/p>\n<ol>\n<li>\n<p>\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435\u043c \u0441 \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0431\u0430\u0437\u043e\u0439 Chroma (\u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u043e\u0434\u043d\u043e\u043a\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f)<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0439 Deepseek \u0438 ChatGPT \u0432 FastAPI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Langchain<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c API \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0442\u0438\u043b\u044c\u043d\u044b\u0439 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0447\u0430\u0442\u0430 \u0441\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430\u043c\u0438 \u0432\u0445\u043e\u0434\u0430 \u0438 \u0441\u0430\u043c\u043e\u0433\u043e \u0447\u0430\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u043c \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u0441 \u0431\u044d\u043a\u0435\u043d\u0434\u043e\u043c \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0433\u043e \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0438\u0441\u0430<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0434\u0435\u043f\u043b\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_chromadb_part2\">Amvera Cloud<\/a><\/p>\n<\/li>\n<\/ol>\n<p>\u041d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435, \u043f\u043e\u0441\u043b\u0435 \u0434\u0435\u043f\u043b\u043e\u044f, \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0442\u0430\u043a\u043e\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1a9\/2dd\/e5f\/1a92dde5f5e068fc533cf52b1853ff77.gif\" width=\"720\" height=\"363\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1a9\/2dd\/e5f\/1a92dde5f5e068fc533cf52b1853ff77.gif 780w,&#10;       https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1a9\/2dd\/e5f\/1a92dde5f5e068fc533cf52b1853ff77.gif 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h3>\u0412\u0430\u0436\u043d\u043e\u0435 \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u0435 \u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0435 \u043a \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c<\/h3>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0447\u0443 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044f\u0445 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0412 \u0420\u043e\u0441\u0441\u0438\u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0438, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a ChatGPT, \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0434\u0430\u0436\u0435 \u0447\u0435\u0440\u0435\u0437 API-\u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e, \u0447\u0442\u043e \u043e\u0431\u044b\u0447\u043d\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f VPN.<\/p>\n<p>\u041e\u0434\u043d\u0430\u043a\u043e \u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c Amvera Cloud \u0442\u0430\u043a\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043d\u0435 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0435\u0442 \u2014 \u0432\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u044b \u0441 ChatGPT, Claude \u0438 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c\u0438, \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c\u0438 \u0432 \u0420\u0424. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0434\u0435\u043f\u043b\u043e\u044f \u0434\u0430\u0436\u0435 \u0442\u0430\u043a\u043e\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0437\u0430\u0439\u043c\u0435\u0442 \u0432\u0441\u0435\u0433\u043e \u043f\u0430\u0440\u0443 \u043c\u0438\u043d\u0443\u0442. \u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0441 HTTPS, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u0432\u044b\u0431\u043e\u0440 \u044d\u0442\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u043c.<\/p>\n<h3>\u0422\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0441\u0442\u0435\u043a<\/h3>\n<p>\u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0432\u0430 \u044f\u0437\u044b\u043a\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f:<\/p>\n<p><strong>Python (\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430):<\/strong><\/p>\n<ul>\n<li>\n<p>langchain-openai \u2014 \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 ChatGPT<\/p>\n<\/li>\n<li>\n<p>langchain-deepseek \u2014 \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Deepseek<\/p>\n<\/li>\n<li>\n<p>FastAPI \u2014 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0431\u044d\u043a\u0435\u043d\u0434\u0430<\/p>\n<\/li>\n<li>\n<p>langchain-chroma \u2014 \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma<\/p>\n<\/li>\n<li>\n<p>Jinja2 \u2014 \u0448\u0430\u0431\u043b\u043e\u043d\u0438\u0437\u0430\u0442\u043e\u0440 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430<\/p>\n<\/li>\n<li>\n<p>PyJWT \u2014 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 JWT-\u0442\u043e\u043a\u0435\u043d\u0430\u043c\u0438<\/p>\n<\/li>\n<li>\n<p>\u0418 \u0434\u0440\u0443\u0433\u0438\u0435 Python-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b<\/p>\n<\/li>\n<\/ul>\n<p><strong>JavaScript (\u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430):<\/strong><\/p>\n<ul>\n<li>\n<p>\u0427\u0438\u0441\u0442\u044b\u0439 JavaScript \u0431\u0435\u0437 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 \u043f\u0440\u043e\u0435\u043a\u0442<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043b\u044f \u0441\u0442\u0438\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u043c \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0441\u0442\u0438\u043b\u0438 \u0438 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Bootstrap.<\/p>\n<p>\u0418\u0442\u0430\u043a, \u043d\u0430\u0447\u043d\u0435\u043c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443!<\/p>\n<h2>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h2>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f<\/h3>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0434\u043b\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0430 HTML \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Jinja2, \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u043c\u044b \u043e\u043f\u0438\u0448\u0435\u043c \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043e\u0434\u043d\u043e\u0433\u043e FastAPI \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0432 \u0432\u0430\u0448\u0435\u043c \u043b\u044e\u0431\u0438\u043c\u043e\u043c IDE.<\/p>\n<h3>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432<\/h3>\n<h4>\u0424\u0430\u0439\u043b .env<\/h4>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b\u00a0<code>.env<\/code>\u00a0\u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u043c \u0435\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438:<\/p>\n<pre><code>DEEPSEEK_API_KEY=sk-123456  # \u0442\u043e\u043a\u0435\u043d Deepseek OPENAI_API_KEY=sk-proj-S_12345  # \u0442\u043e\u043a\u0435\u043d ChatGPT SECRET_KEY=super_secret_key  # \u0441\u0435\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 JWT-\u0442\u043e\u043a\u0435\u043d\u0430 (\u0441\u0430\u043c\u0438 \u043f\u0440\u0438\u0434\u0443\u043c\u044b\u0432\u0430\u0435\u043c) ALGORITHM=HS256  # \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u043b\u044f JWT <\/code><\/pre>\n<h4>\u0424\u0430\u0439\u043b requirements.txt<\/h4>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b\u00a0<code>requirements.txt<\/code>\u00a0\u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c:<\/p>\n<pre><code>langchain-huggingface==0.1.2 torch==2.6.0 loguru==0.7.3 chromadb==0.6.3 sentence-transformers==3.4.1 langchain-chroma==0.2.2 pydantic-settings==2.8.1 fastapi==0.115.12 uvicorn==0.34.0 Jinja2==3.1.6 aiofiles==24.1.0 PyJWT==2.10.1 langchain-deepseek==0.1.3 langchain-openai==0.3.11 <\/code><\/pre>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<pre><code class=\"bash\">pip install -r requirements.txt <\/code><\/pre>\n<h3>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043f\u043a\u0443\u00a0<code>app<\/code>\u00a0\u0438 \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0435\u0435 \u0444\u0430\u0439\u043b\u00a0<a href=\"http:\/\/config.py\"><code>config.py<\/code><\/a>. \u0417\u0430\u043f\u043e\u043b\u043d\u0438\u043c \u0435\u0433\u043e:<\/p>\n<pre><code class=\"python\">import os from pydantic import SecretStr from pydantic_settings import BaseSettings, SettingsConfigDict  class Config(BaseSettings):     DEEPSEEK_API_KEY: SecretStr     BASE_DIR: str = os.path.abspath(os.path.join(os.path.dirname(__file__)))     SECRET_KEY: str     USERS: str = os.path.join(BASE_DIR, \"..\", \"users.json\")     ALGORITHM: str     AMVERA_CHROMA_PATH: str = os.path.join(BASE_DIR, \"chroma_db\")     AMVERA_COLLECTION_NAME: str = \"amvera_docs\"     LM_MODEL_NAME: str = \"sentence-transformers\/paraphrase-multilingual-MiniLM-L12-v2\"     DEEPSEEK_MODEL_NAME: str = \"deepseek-chat\"     OPENAI_MODEL_NAME: str = \"gpt-3.5-turbo\"     OPENAI_API_KEY: SecretStr      model_config = SettingsConfigDict(env_file=f\"{BASE_DIR}\/..\/.env\")  settings = Config() <\/code><\/pre>\n<blockquote>\n<p><strong>\u0412\u0430\u0436\u043d\u043e\u0435 \u043f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435:<\/strong>\u00a0\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u0443\u00a0<code>from pydantic import SecretStr<\/code>. \u0414\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 Langchain \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0439 Deepseek \u0438 ChatGPT \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u043a\u0430\u043a\u00a0<code>DEEPSEEK_API_KEY: SecretStr<\/code>. \u0412 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u043c \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0443\u0434\u043e\u0431\u043d\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0432 \u043e\u0434\u043d\u043e\u043c \u043a\u043b\u0430\u0441\u0441\u0435 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 pydantic-settings.<\/p>\n<\/blockquote>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u00a0<code>settings<\/code>\u00a0\u0438 \u0432\u0435\u0437\u0434\u0435 \u043f\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0443 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0447\u0435\u0440\u0435\u0437 \u0442\u043e\u0447\u043a\u0443:\u00a0<code>settings.OPENAI_API_KEY<\/code><\/p>\n<h3>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0439<\/h3>\n<p>\u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0432 \u043f\u0430\u043f\u043a\u0435\u00a0<code>app<\/code>\u00a0\u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438:<\/p>\n<ul>\n<li>\n<p><strong>api<\/strong>: \u043e\u043f\u0438\u0448\u0435\u043c \u0442\u0443\u0442 \u0432\u0441\u044e API-\u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/p>\n<\/li>\n<li>\n<p><strong>chroma_client<\/strong>: \u043e\u043f\u0438\u0448\u0435\u043c \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Chroma \u0438 \u043a\u043b\u0430\u0441\u0441 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c\u0438<\/p>\n<\/li>\n<li>\n<p><strong>chroma_db<\/strong>: \u0442\u0443\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0440\u0430\u043d\u0435\u0435 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma (\u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u044e, \u0447\u0442\u043e \u0431\u0430\u0437\u0430 \u0443 \u0432\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c, \u0435\u0441\u043b\u0438 \u044d\u0442\u043e \u043d\u0435 \u0442\u0430\u043a \u2014 \u043c\u043e\u0436\u0435\u0442\u0435 \u0431\u0440\u0430\u0442\u044c \u043c\u043e\u044e. \u0411\u0430\u0437\u0443 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0432 <a href=\"https:\/\/t.me\/PythonPathMaster\">\u043f\u043e\u043b\u043d\u043e\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u043c<\/a> \u043a\u043e\u0434\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430)<\/p>\n<\/li>\n<li>\n<p><strong>pages<\/strong>: \u0442\u0443\u0442 \u043e\u043f\u0438\u0448\u0435\u043c FastAPI \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b \u0434\u043b\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0430 HTML-\u0441\u0442\u0440\u0430\u043d\u0438\u0446<\/p>\n<\/li>\n<li>\n<p><strong>static<\/strong>: \u043f\u0430\u043f\u043a\u0430 \u0441\u043e \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c\u0438 \u0444\u0430\u0439\u043b\u0430\u043c\u0438 (\u0441\u0442\u0438\u043b\u0438 \u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u044b JS)<\/p>\n<\/li>\n<li>\n<p><strong>templates<\/strong>: \u043f\u0430\u043f\u043a\u0430 \u0441 HTML-\u0448\u0430\u0431\u043b\u043e\u043d\u0430\u043c\u0438<\/p>\n<\/li>\n<\/ul>\n<p>\u0418 \u0441\u0440\u0430\u0437\u0443 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432 \u043f\u0430\u043f\u043a\u0435\u00a0<code>app<\/code>\u00a0\u043f\u0443\u0441\u0442\u043e\u0439 \u0444\u0430\u0439\u043b\u00a0<a href=\"http:\/\/main.py\"><code>main.py<\/code><\/a>. \u0412 \u043d\u0435\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430, \u0438 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u043a\u043e\u0434\u0430.<\/p>\n<h2>\u041a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435\u043c \u0441 \u0431\u0430\u0437\u043e\u0439 Chroma<\/h2>\n<h3>\u041e\u0431\u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0430<\/h3>\n<p>\u041d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u044e, \u0447\u0442\u043e \u043d\u0430\u0448\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 FastAPI \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u043b\u0438 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma, \u0438 \u043f\u043e\u0441\u043b\u0435 \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u043e\u043b\u0436\u043d\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u043e\u0442\u043a\u0440\u044b\u0442\u0443\u044e \u0441\u0435\u0441\u0441\u0438\u044e.<\/p>\n<p>\u042d\u0442\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043b\u044f:<\/p>\n<ul>\n<li>\n<p>\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 \u0438\u0437 Chroma \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>\u041a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0440\u0438 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u0438 \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430\u0445<\/p>\n<\/li>\n<\/ul>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u043b\u0430\u0441\u0441\u0430 ChromaVectorStore<\/h3>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b\u00a0<code>app\/chroma_client\/chroma_<\/code><a href=\"http:\/\/store.py\"><code>store.py<\/code><\/a>\u00a0\u0438 \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u0438\u043c\u043f\u043e\u0440\u0442\u043e\u0432:<\/p>\n<pre><code class=\"python\">import torch from langchain_chroma import Chroma from langchain_huggingface import HuggingFaceEmbeddings from loguru import logger from app.config import settings <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0441\u0430\u043c \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435\u043c \u0441 \u0431\u0430\u0437\u043e\u0439:<\/p>\n<pre><code class=\"python\">class ChromaVectorStore:     def __init__(self):         \"\"\"         \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u043f\u0443\u0441\u0442\u043e\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0432\u0435\u043a\u0442\u043e\u0440\u043e\u0432.         \u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e \u043f\u043e\u0437\u0436\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 init().         \"\"\"         self._store: Chroma | None = None      async def init(self):         \"\"\"         \u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma.         \u0421\u043e\u0437\u0434\u0430\u0435\u0442 embeddings \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043c\u043e\u0434\u0435\u043b\u0438 \u0438\u0437 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f CUDA \u0435\u0441\u043b\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e.         \"\"\"         logger.info(\"\ud83e\udde0 \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f ChromaVectorStore...\")         try:             # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043b\u044f \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0439: GPU \u0435\u0441\u043b\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d, \u0438\u043d\u0430\u0447\u0435 CPU             device = \"cuda\" if torch.cuda.is_available() else \"cpu\"             logger.info(f\"\ud83d\ude80 \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043b\u044f \u044d\u043c\u0431\u0435\u0434\u0434\u0438\u043d\u0433\u043e\u0432: {device}\")              # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u044c \u044d\u043c\u0431\u0435\u0434\u0434\u0438\u043d\u0433\u043e\u0432 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438             embeddings = HuggingFaceEmbeddings(                 model_name=settings.LM_MODEL_NAME,                 model_kwargs={\"device\": device},                 encode_kwargs={\"normalize_embeddings\": True},             )              # \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma             self._store = Chroma(                 persist_directory=settings.AMVERA_CHROMA_PATH,                 embedding_function=embeddings,                 collection_name=settings.AMVERA_COLLECTION_NAME,             )              logger.success(                 f\"\u2705 ChromaVectorStore \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d \u043a \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438 \"                 f\"'{settings.AMVERA_COLLECTION_NAME}' \u0432 '{settings.AMVERA_CHROMA_PATH}'\"             )         except Exception as e:             logger.exception(f\"\u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 ChromaVectorStore: {e}\")             raise      async def asimilarity_search(self, query: str, with_score: bool, k: int = 3):         \"\"\"         \u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma.          Args:             query (str): \u0422\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430             with_score (bool): \u0412\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043b\u0438 \u043e\u0446\u0435\u043d\u043a\u0443 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u0438 \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b             k (int): \u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432          Returns:             list: \u0421\u043f\u0438\u0441\u043e\u043a \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0441 \u043e\u0446\u0435\u043d\u043a\u0430\u043c\u0438 \u0435\u0441\u043b\u0438 with_score=True          Raises:             RuntimeError: \u0415\u0441\u043b\u0438 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043d\u0435 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043e         \"\"\"         if not self._store:             raise RuntimeError(\"ChromaVectorStore is not initialized.\")          logger.info(f\"\ud83d\udd0d \u041f\u043e\u0438\u0441\u043a \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0443: \u00ab{query}\u00bb, top_k={k}\")         try:             if with_score:                 results = await self._store.asimilarity_search_with_score(                     query=query, k=k                 )             else:                 results = await self._store.asimilarity_search(query=query, k=k)              logger.debug(f\"\ud83d\udcc4 \u041d\u0430\u0439\u0434\u0435\u043d\u043e {len(results)} \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432.\")             return results         except Exception as e:             logger.exception(f\"\u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u0438\u0441\u043a\u0435: {e}\")             raise      async def close(self):         \"\"\"         \u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma.         \u0412 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 Chroma \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u044f\u0432\u043d\u043e\u0433\u043e \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f,         \u043d\u043e \u043c\u0435\u0442\u043e\u0434 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0442\u044b API \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u0431\u0443\u0434\u0443\u0449\u0438\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439.         \"\"\"         logger.info(\"\ud83d\udd0c \u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 ChromaVectorStore...\")         # \u041f\u043e\u043a\u0430 Chroma \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u044f\u0432\u043d\u043e\u0433\u043e \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f, \u043d\u043e \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f         # self._store.close() \u0438\u043b\u0438 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434         pass <\/code><\/pre>\n<h3>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/h3>\n<p>\u0412 \u043d\u0430\u0448\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\u00a0<code>ChromaVectorStore<\/code>\u00a0\u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u043c\u043e\u043c\u0435\u043d\u0442\u043e\u0432:<\/p>\n<ol>\n<li>\n<p><strong>\u041e\u0442\u043b\u043e\u0436\u0435\u043d\u043d\u0430\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/strong>\u00a0\u2014 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 \u044f\u0432\u043d\u043e\u043c \u0432\u044b\u0437\u043e\u0432\u0435 \u043c\u0435\u0442\u043e\u0434\u0430\u00a0<code>init()<\/code>, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043c\u043e\u043c\u0435\u043d\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0432 \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u043e\u043c \u0446\u0438\u043a\u043b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p><strong>\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0441\u0442\u044c<\/strong>\u00a0\u2014 \u0432\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c\u0438 (<code>async<\/code>), \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f FastAPI.<\/p>\n<\/li>\n<li>\n<p><strong>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 GPU<\/strong>\u00a0\u2014 \u0435\u0441\u043b\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e CUDA-\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e, \u043c\u043e\u0434\u0435\u043b\u044c \u044d\u043c\u0431\u0435\u0434\u0434\u0438\u043d\u0433\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0434\u043b\u044f \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u044f \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0439.<\/p>\n<\/li>\n<li>\n<p><strong>\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/strong>\u00a0\u2014 \u0432\u0441\u0435 \u0432\u0430\u0436\u043d\u044b\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0438 \u043e\u0448\u0438\u0431\u043a\u0438 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432 \u043b\u043e\u0433 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438\u00a0<code>loguru<\/code>, \u0447\u0442\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u043e\u0442\u043b\u0430\u0434\u043a\u0443 \u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433.<\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a<\/strong>\u00a0\u2014 \u0432\u0441\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044b \u0432 \u0431\u043b\u043e\u043a\u0438 try-except \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043e\u0448\u0438\u0431\u043e\u043a.<\/p>\n<\/li>\n<\/ol>\n<p>\u0414\u0430\u043d\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0432\u0441\u0435\u0433\u043e \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f FastAPI.<\/p>\n<p>\u041a \u044d\u0442\u043e\u043c\u0443 \u0444\u0430\u0439\u043b\u0443 \u043c\u044b \u0435\u0449\u0451 \u0432\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u0441\u0435\u0433\u043e\u0434\u043d\u044f, \u043d\u043e \u043f\u043e\u043a\u0430 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u043c\u0441\u044f \u043d\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043a\u043b\u0430\u0441\u0441\u0430 \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0439 Deepseek \u0438 ChatGPT \u0432 \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0438\u0441.<\/p>\n<h2>\u041a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0439<\/h2>\n<h3>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438<\/h3>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u043b\u0430\u0441\u0441\u0430, \u043d\u0430\u043f\u043e\u043c\u043d\u044e \u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0445 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f\u0445 \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u0443 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c\u0438:<\/p>\n<ol>\n<li>\n<p>\u041f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0438\u0437 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 Chroma<\/p>\n<\/li>\n<li>\n<p>\u0424\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0438\u0437 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u043c\u043f\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0439 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0438 (ChatGPT \u0438\u043b\u0438 Deepseek)<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u043d\u0435 \u0446\u0435\u043b\u044b\u043c \u043a\u0443\u0441\u043a\u043e\u043c, \u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u043e\u0433\u043e \u0432\u044b\u0432\u043e\u0434\u0430 (\u0441\u0442\u0440\u0438\u043c\u0430)<\/p>\n<\/li>\n<\/ol>\n<p>\u0412\u0435\u0441\u044c \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u0434\u043b\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u043b\u0430\u0441\u0441\u0430 ChatWithAI<\/h3>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b\u00a0<code>app\/chroma_client\/ai_<\/code><a href=\"http:\/\/store.py\"><code>store.py<\/code><\/a>\u00a0\u0438 \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u0438\u043c\u043f\u043e\u0440\u0442\u043e\u0432:<\/p>\n<pre><code class=\"python\">from typing import AsyncGenerator, Literal from langchain_core.messages import HumanMessage, SystemMessage from langchain_deepseek import ChatDeepSeek from langchain_openai import ChatOpenAI from loguru import logger from app.config import settings <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c\u0438:<\/p>\n<pre><code class=\"python\">class ChatWithAI:     \"\"\"     \u041a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c\u0438 \u044f\u0437\u044b\u043a\u043e\u0432\u044b\u043c\u0438 \u043c\u043e\u0434\u0435\u043b\u044f\u043c\u0438 (LLM).     \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 ChatGPT \u0438 Deepseek, \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u043e\u0433\u043e \u0432\u044b\u0432\u043e\u0434\u0430 \u043e\u0442\u0432\u0435\u0442\u043e\u0432.     \"\"\"      def __init__(self, provider: Literal[\"deepseek\", \"chatgpt\"] = \"deepseek\"):         \"\"\"         \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043a\u043b\u0430\u0441\u0441\u0430 \u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u043c LLM.          Args:             provider (str): \u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u044f\u0437\u044b\u043a\u043e\u0432\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438 ('deepseek' \u0438\u043b\u0438 'chatgpt')          Raises:             ValueError: \u0415\u0441\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u043d \u043d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0439 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440         \"\"\"         self.provider = provider          if provider == \"deepseek\":             logger.info(f\"\ud83e\udd16 \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f Deepseek \u043c\u043e\u0434\u0435\u043b\u0438: {settings.DEEPSEEK_MODEL_NAME}\")             self.llm = ChatDeepSeek(                 api_key=settings.DEEPSEEK_API_KEY,                 model=settings.DEEPSEEK_MODEL_NAME,                 temperature=0.7,  # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0442\u0432\u043e\u0440\u0447\u0435\u0441\u043a\u043e\u0439 \u0441\u0432\u043e\u0431\u043e\u0434\u044b \u043c\u043e\u0434\u0435\u043b\u0438             )         elif provider == \"chatgpt\":             logger.info(f\"\ud83e\udd16 \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f ChatGPT \u043c\u043e\u0434\u0435\u043b\u0438: {settings.OPENAI_MODEL_NAME}\")             self.llm = ChatOpenAI(                 api_key=settings.OPENAI_API_KEY,                 model=settings.OPENAI_MODEL_NAME,                 temperature=0.7,  # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0442\u0432\u043e\u0440\u0447\u0435\u0441\u043a\u043e\u0439 \u0441\u0432\u043e\u0431\u043e\u0434\u044b \u043c\u043e\u0434\u0435\u043b\u0438             )         else:             logger.error(f\"\u274c \u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0439 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440: {provider}\")             raise ValueError(f\"\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0439 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440: {provider}\")          logger.success(f\"\u2705 \u041c\u043e\u0434\u0435\u043b\u044c {provider} \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u0430\")      async def astream_response(         self, formatted_context: str, query: str     ) -&gt; AsyncGenerator[str, None]:         \"\"\"         \u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u043e\u0442 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0439 \u044f\u0437\u044b\u043a\u043e\u0432\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438.          Args:             formatted_context (str): \u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0438\u0437 \u0431\u0430\u0437\u044b \u0437\u043d\u0430\u043d\u0438\u0439, \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0434\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430             query (str): \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0437\u0430\u043f\u0440\u043e\u0441          Yields:             str: \u0424\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u044b \u043e\u0442\u0432\u0435\u0442\u0430 \u0432 \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435          Notes:             \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0439 \u043f\u0440\u043e\u043c\u043f\u0442 \u0434\u043b\u044f \u0437\u0430\u0434\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 \u0438 \u0440\u043e\u043b\u0438 \u043c\u043e\u0434\u0435\u043b\u0438         \"\"\"         try:             # \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0439 \u043f\u0440\u043e\u043c\u043f\u0442, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0449\u0438\u0439 \u0440\u043e\u043b\u044c \u0438 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u043c\u043e\u0434\u0435\u043b\u0438             system_message = SystemMessage(                 content=\"\"\"\u0422\u044b \u2014 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 Amvera Cloud.                 \u041e\u0442\u0432\u0435\u0447\u0430\u0435\u0448\u044c \u043f\u043e \u0434\u0435\u043b\u0443 \u0431\u0435\u0437 \u043b\u0438\u0448\u043d\u0438\u0445 \u0432\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0439.                 \u0421\u0432\u043e\u0439 \u043e\u0442\u0432\u0435\u0442, \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u0443\u0439 \u043d\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442.                 \u0415\u0441\u043b\u0438 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e - \u043f\u0440\u043e\u0431\u0443\u0439 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442\u044b \u0438\u0437 \u0441\u0432\u043e\u0435\u0439 \u0431\u0430\u0437\u044b \u0437\u043d\u0430\u043d\u0438\u0439.\"\"\"             )              # \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043e\u043c             human_message = HumanMessage(                 content=f\"\u0412\u043e\u043f\u0440\u043e\u0441: {query}\\n\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442: {formatted_context}. \u041e\u0442\u0432\u0435\u0442 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0443\u0439 \u0432 markdown!\"             )              logger.info(f\"\ud83d\udd04 \u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u0441\u0442\u0440\u0438\u043c\u0438\u043d\u0433 \u043e\u0442\u0432\u0435\u0442\u0430 \u0434\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430: \u00ab{query}\u00bb\")              # \u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u044b \u043e\u0442\u0432\u0435\u0442\u0430             async for chunk in self.llm.astream([system_message, human_message]):                 if chunk.content:  # \u041f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043f\u0443\u0441\u0442\u044b\u0435 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u044b                     logger.debug(f\"\ud83d\udcdd \u041f\u043e\u043b\u0443\u0447\u0435\u043d \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442: {chunk.content[:50]}...\")                     yield chunk.content              logger.info(\"\u2705 \u0421\u0442\u0440\u0438\u043c\u0438\u043d\u0433 \u043e\u0442\u0432\u0435\u0442\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\")          except Exception as e:             logger.error(f\"\u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0441\u0442\u0440\u0438\u043c\u0438\u043d\u0433\u0435 \u043e\u0442\u0432\u0435\u0442\u0430: {e}\")             yield f\"\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430: {str(e)}\" <\/code><\/pre>\n<h3>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/h3>\n<p>\u041a\u043b\u0430\u0441\u0441\u00a0<code>ChatWithAI<\/code>\u00a0\u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c:<\/p>\n<ol>\n<li>\n<p><strong>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432<\/strong>\u00a0\u2014 \u043a\u043b\u0430\u0441\u0441 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u044b\u0431\u0438\u0440\u0430\u0442\u044c \u043c\u0435\u0436\u0434\u0443 ChatGPT \u043e\u0442 OpenAI \u0438 Deepseek, \u0447\u0442\u043e \u0434\u0430\u0435\u0442 \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u044c \u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u044f\u0437\u044b\u043a\u043e\u0432\u044b\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u0435\u0439 \u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u044b\u0439 \u0432\u044b\u0432\u043e\u0434<\/strong>\u00a0\u2014 \u043c\u0435\u0442\u043e\u0434\u00a0<code>astream_response<\/code>\u00a0\u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u043a\u0430\u043a \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0447\u0430\u0441\u0442\u0438 \u043e\u0442\u0432\u0435\u0442\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u043f\u043e \u043c\u0435\u0440\u0435 \u0438\u0445 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0434\u0435\u043b\u044c\u044e, \u0431\u0435\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0436\u0438\u0434\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442.<\/p>\n<\/li>\n<li>\n<p><strong>\u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/strong>\u00a0\u2014 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0439 \u043f\u0440\u043e\u043c\u043f\u0442, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0449\u0438\u0439 \u0440\u043e\u043b\u044c \u0438 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u043c\u043e\u0434\u0435\u043b\u0438, \u0447\u0442\u043e \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u0435 \u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b.<\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0435 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/strong>\u00a0\u2014 \u0432\u0441\u0435 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u044d\u0442\u0430\u043f\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u043a\u043b\u0430\u0441\u0441\u0430 \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0430\u044e\u0442\u0441\u044f \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c, \u0447\u0442\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u043e\u0442\u043b\u0430\u0434\u043a\u0443 \u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0438\u0441\u0442\u0435\u043c\u044b.<\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a<\/strong>\u00a0\u2014 \u0432\u0441\u0435 \u043f\u043e\u0442\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043e\u043f\u0430\u0441\u043d\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044b \u0432 \u0431\u043b\u043e\u043a\u0438 try-except, \u0447\u0442\u043e \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0438 \u043e\u0448\u0438\u0431\u043e\u043a \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 API \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0439.<\/p>\n<\/li>\n<\/ol>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438<\/h2>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0435 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u044b \u043f\u043b\u0430\u0442\u043d\u044b\u0435 \u0442\u043e\u043a\u0435\u043d\u044b \u2014 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u043c\u044b \u0437\u0430\u0445\u043e\u0442\u0438\u043c \u0437\u0430\u0449\u0438\u0449\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u043e\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0435\u0433\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u043c\u0438 \u043b\u044e\u0434\u044c\u043c\u0438 \u0438 \u0432 \u043d\u0435\u043c\u0435\u0440\u0435\u043d\u043d\u044b\u0445 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430\u0445.<\/p>\n<h3>\u041b\u043e\u0433\u0438\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438<\/h3>\n<p>\u041b\u043e\u0433\u0438\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439:<\/p>\n<ol>\n<li>\n<p>\u0414\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u0432\u0435\u0441\u0442\u0438 \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0432 \u0444\u043e\u0440\u043c\u0443 \u0432\u0445\u043e\u0434\u0430<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0433\u043e \u0432\u0432\u043e\u0434\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 JWT-\u0442\u043e\u043a\u0435\u043d, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043f\u043e\u043c\u0435\u0449\u0430\u0442\u044c \u0432 \u043a\u0443\u043a\u0438 \u0441\u0435\u0441\u0441\u0438\u044e. \u0423 \u044d\u0442\u043e\u0433\u043e \u0442\u043e\u043a\u0435\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u0432\u043e\u0439 \u0441\u0440\u043e\u043a \u0436\u0438\u0437\u043d\u0438 (1 \u0447\u0430\u0441). \u0422\u043e \u0435\u0441\u0442\u044c \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u044d\u0442\u043e\u0442 \u0447\u0430\u0441 \u043f\u0440\u043e\u0439\u0434\u0435\u0442 \u2014 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0442\u0430\u043a, \u0447\u0442\u043e\u0431 \u0442\u043e\u043a\u0435\u043d \u0431\u044b\u043b \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c. \u041d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0440\u0430\u0432\u043d\u044f\u0435\u0442\u0441\u044f \u0432\u044b\u0445\u043e\u0434\u0443 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438 \u043f\u043e\u043b\u043d\u043e\u043c\u0443 \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u043b\u043e\u0433\u0438\u043d \u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u043d\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b \u043b\u0438\u0431\u043e \u0442\u043e\u043a\u0435\u043d \u0438\u0441\u0442\u0435\u043a \u2014 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0435 \u0431\u0443\u0434\u0435\u0442.<\/p>\n<\/li>\n<\/ol>\n<h3>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/h3>\n<p>\u0414\u043b\u044f \u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u044f \u0440\u0435\u0448\u0438\u043b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 JSON \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 &#8212; \u0444\u0430\u0439\u043b, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u043b \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u0445. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0432 \u0431\u043e\u0435\u0432\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u2014 \u044d\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0440\u0435\u043b\u044f\u0446\u0438\u043e\u043d\u043d\u0430\u044f \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u043f\u043e\u043b\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435 \u0438 \u0435\u0433\u043e \u0445\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043e\u0431 \u044d\u0442\u043e\u043c \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u043b \u0432 \u0441\u0432\u043e\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0435: \u00ab\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e API \u043d\u0430 Python (FastAPI): \u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f, \u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0438 \u0440\u043e\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439\u00bb (<a href=\"https:\/\/habr.com\/ru\/articles\/829742\/\">https:\/\/habr.com\/ru\/articles\/829742\/<\/a>)<\/p>\n<p>\u0412 \u043a\u043e\u0440\u043d\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 (\u043d\u0430 \u043e\u0434\u043d\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435 \u0441 \u043f\u0430\u043f\u043a\u043e\u0439 app) \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u0444\u0430\u0439\u043b\u00a0<code>users.json<\/code>\u00a0\u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0435\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"json\">[   {     \"user_id\": 1,     \"login\": \"user1\",     \"password\": \"pass1\"   },   {     \"user_id\": 2,     \"login\": \"user2\",     \"password\": \"pass2\"   } ] <\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0440\u0435\u0448\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438\u043c\u0435\u0440\u0443 \u2014 \u043f\u043e\u0437\u0430\u0431\u043e\u0442\u044c\u0442\u0435\u0441\u044c \u043e \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u0430\u0440\u043e\u043b\u044f. \u0418 \u0435\u0449\u0451 \u0440\u0430\u0437 \u043f\u043e\u0434\u0447\u0435\u0440\u043a\u0438\u0432\u0430\u044e \u2014 <strong>\u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438!<\/strong><\/p>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0443\u0442\u0438\u043b\u0438\u0442 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438<\/h3>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432 \u043f\u0430\u043f\u043a\u0435\u00a0<code>app\/api<\/code>\u00a0\u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b\u00a0<a href=\"http:\/\/utils.py\"><code>utils.py<\/code><\/a>, \u0442\u0430\u043c \u043e\u043f\u0438\u0448\u0435\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443\u0442\u0438\u043b\u0438\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0442 \u043d\u0430\u043c \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<h4>\u0418\u043c\u043f\u043e\u0440\u0442\u044b:<\/h4>\n<pre><code class=\"python\">import json from datetime import datetime, timedelta from typing import Optional  import aiofiles import jwt from fastapi import Cookie, HTTPException  from app.config import settings <\/code><\/pre>\n<p>\u0422\u0443\u0442 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443\u00a0<code>aiofiles<\/code>\u00a0\u0434\u043b\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 JSON-\u0444\u0430\u0439\u043b\u043e\u043c.<\/p>\n<h4>\u041c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439:<\/h4>\n<pre><code class=\"python\">async def get_all_users():     async with aiofiles.open(settings.USERS) as f:         content = await f.read()         users = json.loads(content)         return users <\/code><\/pre>\n<h4>\u041c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u0438 \u043b\u043e\u0433\u0438\u043d\u0430 \u0438 \u043f\u0430\u0440\u043e\u043b\u044f:<\/h4>\n<pre><code class=\"python\">async def authenticate_user(login: str, password: str):     users = await get_all_users()     for user in users:         if user[\"login\"] == login and user[\"password\"] == password:             return user     return None <\/code><\/pre>\n<h4>\u041c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f JWT \u0442\u043e\u043a\u0435\u043d\u0430:<\/h4>\n<pre><code class=\"python\">async def create_jwt_token(user_id: int):     expire = datetime.now() + timedelta(hours=1)     payload = {         \"sub\": str(user_id),         \"exp\": expire.timestamp(),     }     token = jwt.encode(payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM)     return token <\/code><\/pre>\n<p>\u041d\u0430 \u0432\u0445\u043e\u0434 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u043c \u0430\u0439\u0434\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0441\u0440\u043e\u043a \u0436\u0438\u0437\u043d\u0438 \u0442\u043e\u043a\u0435\u043d\u0430 \u0432 1 \u0447\u0430\u0441.<\/p>\n<h4>\u041c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0432\u0435\u0440\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 JWT-\u0442\u043e\u043a\u0435\u043d\u0430:<\/h4>\n<pre><code class=\"python\">async def verify_jwt_token(token: str):     try:         payload = jwt.decode(             token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]         )         return payload[\"sub\"]     except jwt.ExpiredSignatureError:         raise HTTPException(status_code=401, detail=\"Token expired\")     except jwt.InvalidTokenError:         raise HTTPException(status_code=401, detail=\"Invalid token\") <\/code><\/pre>\n<p>\u0422\u0443\u0442 \u043c\u044b \u043d\u0430 \u0432\u0445\u043e\u0434 \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u0438 \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u044c \u0442\u043e\u043a\u0435\u043d\u0430 \u0438 \u0435\u0433\u043e \u0441\u0440\u043e\u043a \u0436\u0438\u0437\u043d\u0438. \u0422\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0442\u043e\u043a\u0435\u043d \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0439 \u0438 \u0435\u0433\u043e \u0441\u0440\u043e\u043a \u0436\u0438\u0437\u043d\u0438 \u043d\u0435 \u043f\u043e\u0434\u043e\u0448\u0435\u043b \u043a \u043a\u043e\u043d\u0446\u0443 \u2014 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0430\u0439\u0434\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0438\u043d\u0430\u0447\u0435 \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0443.<\/p>\n<h4>\u041c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:<\/h4>\n<pre><code class=\"python\">async def get_current_user(     access_token: Optional[str] = Cookie(default=None), ):     if not access_token:         raise HTTPException(status_code=401, detail=\"Missing token\")     user_id = await verify_jwt_token(access_token)     return user_id <\/code><\/pre>\n<p>\u0422\u0443\u0442 \u043c\u044b \u043f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u0438\u0437\u0432\u043b\u0435\u0447\u044c \u0438\u0437 \u043a\u0443\u043a\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u043b\u044e\u0447\u0430\u00a0<code>access_token<\/code>. \u0415\u0441\u043b\u0438 \u0435\u0433\u043e \u043d\u0435\u0442 \u2014 \u0441\u0440\u0430\u0437\u0443 \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0443, \u0447\u0442\u043e \u0442\u043e\u043a\u0435\u043d \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d.<\/p>\n<p>\u0417\u0430\u0442\u0435\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u0438 \u0438 \u0441\u0440\u043e\u043a\u0430 \u0436\u0438\u0437\u043d\u0438 \u0442\u043e\u043a\u0435\u043d\u0430, \u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0432\u0441\u0435 \u043e\u043a \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0430\u0439\u0434\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0432\u043d\u044f\u0442\u044c\u0441\u044f \u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d.<\/p>\n<h4>\u041e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438:<\/h4>\n<pre><code class=\"python\">async def get_optional_current_user(     access_token: Optional[str] = Cookie(default=None), ) -&gt; Optional[int]:     if not access_token:         return None     try:         user_id = await verify_jwt_token(access_token)         return user_id     except Exception:         return None <\/code><\/pre>\n<p>\u0414\u0430\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u043e\u0442\u00a0<code>get_current_user<\/code>\u00a0\u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u043c, \u0447\u0442\u043e \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d \u2014 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c\u00a0<code>None<\/code>, \u0430 \u043d\u0435 \u043e\u0448\u0438\u0431\u043a\u0443. \u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u043d\u0430\u043c \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430.<\/p>\n<h3>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 Pydantic-\u043c\u043e\u0434\u0435\u043b\u0435\u0439<\/h3>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432 \u043f\u0430\u043f\u043a\u0435\u00a0<code>api<\/code>\u00a0\u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b\u00a0<a href=\"http:\/\/schemas.py\"><code>schemas.py<\/code><\/a>.<\/p>\n<p>\u0422\u0430\u043c \u043c\u044b \u043e\u043f\u0438\u0448\u0435\u043c pydantic-\u043c\u043e\u0434\u0435\u043b\u0438 \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043f\u0440\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:<\/p>\n<pre><code class=\"python\">from pydantic import BaseModel  class SUserAuth(BaseModel):     login: str     password: str <\/code><\/pre>\n<p>\u041a \u044d\u0442\u043e\u043c\u0443 \u0444\u0430\u0439\u043b\u0443 \u043c\u044b \u0435\u0449\u0451 \u0432\u0435\u0440\u043d\u0435\u043c\u0441\u044f.<\/p>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f API-\u0440\u043e\u0443\u0442\u043e\u0432<\/h3>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432 \u043f\u0430\u043f\u043a\u0435\u00a0<code>api<\/code>\u00a0\u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b\u00a0<a href=\"http:\/\/router.py\"><code>router.py<\/code><\/a>\u00a0\u0438 \u0432 \u043d\u0435\u043c \u043e\u043f\u0438\u0448\u0435\u043c \u043b\u043e\u0433\u0438\u043a\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438.<\/p>\n<h4>\u0418\u043c\u043f\u043e\u0440\u0442\u044b:<\/h4>\n<pre><code class=\"python\">from fastapi import APIRouter, Depends, HTTPException, Response from app.api.schemas import SUserAuth from app.api.utils import authenticate_user, create_jwt_token, get_current_user <\/code><\/pre>\n<h4>\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u043e\u0443\u0442\u0435\u0440\u0430:<\/h4>\n<pre><code class=\"python\">router = APIRouter() <\/code><\/pre>\n<h4>\u041c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430:<\/h4>\n<pre><code class=\"python\">@router.post(\"\/login\") async def login(     response: Response,     user_data: SUserAuth, ):     user = await authenticate_user(user_data.login, user_data.password)     if not user:         raise HTTPException(status_code=401, detail=\"Invalid credentials\")      token = await create_jwt_token(user[\"user_id\"])     response.set_cookie(         key=\"access_token\",         value=token,         httponly=True,         secure=False,  # \u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0435 True \u0432 \u043f\u0440\u043e\u0434\u0435 (HTTPS)         samesite=\"lax\",         max_age=3600,         path=\"\/\",     )     return {\"message\": \"Logged in\"} <\/code><\/pre>\n<p>\u041d\u0430 \u0432\u0445\u043e\u0434 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u043c \u043b\u043e\u0433\u0438\u043d \u0441 \u043f\u0430\u0440\u043e\u043b\u0435\u043c. \u0415\u0441\u043b\u0438 \u0432\u0441\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u0440\u043e\u0439\u0434\u0435\u043d\u044b, \u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0435\u043c JWT-\u0442\u043e\u043a\u0435\u043d, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043c\u0435\u0449\u0430\u0435\u0442\u0441\u044f \u0432 \u043a\u0443\u043a\u0438-\u0441\u0435\u0441\u0441\u0438\u044e.<\/p>\n<h4>\u041c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0432\u044b\u0445\u043e\u0434\u0430:<\/h4>\n<pre><code class=\"python\">@router.post(\"\/logout\") async def logout(response: Response):     response.delete_cookie(\"access_token\")     return {\"message\": \"Logged out\"} <\/code><\/pre>\n<p>\u0422\u0443\u0442 \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u0437 \u043a\u0443\u043a \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u043a\u043b\u044e\u0447 \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c\u00a0<code>access_token<\/code>.<\/p>\n<h4>\u041c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438:<\/h4>\n<pre><code class=\"python\">@router.get(\"\/protected\") async def protected_route(user_id: int = Depends(get_current_user)):     return {\"message\": f\"\u041f\u0440\u0438\u0432\u0435\u0442, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c {user_id}!\"} <\/code><\/pre>\n<p>\u0422\u0443\u0442 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 FastAPI. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e \u043d\u0435\u043c \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u043b \u0432 \u0441\u0432\u043e\u0438\u0445 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u0441\u0442\u0430\u0442\u044c\u044f\u0445, \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u043d\u044b\u0445 FastAPI. \u0415\u0441\u043b\u0438 \u043a\u043e\u0440\u043e\u0442\u043a\u043e, \u0442\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u2014 \u044d\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0434\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u044b \u043f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0430\u0439\u0434\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0415\u0441\u043b\u0438 \u044d\u0442\u043e \u0443\u0434\u0430\u0441\u0442\u0441\u044f \u2014 \u0437\u043d\u0430\u0447\u0438\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d. \u0418\u043d\u0430\u0447\u0435 \u2014 \u043c\u044b \u0432\u044b\u0431\u0440\u043e\u0441\u0438\u043c \u043e\u0448\u0438\u0431\u043a\u0443. \u0414\u043b\u044f \u043b\u0443\u0447\u0448\u0435\u0433\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u0443\u044e \u044d\u0442\u043e \u0447\u0435\u0440\u0435\u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e:<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/526\/ad7\/a6b\/526ad7a6b7acc025af1360ea44e549f3.png\" alt=\"\u0411\u0435\u0437 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 (\u043e\u0448\u0438\u0431\u043a\u0430)\" title=\"\u0411\u0435\u0437 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 (\u043e\u0448\u0438\u0431\u043a\u0430)\" width=\"1241\" height=\"411\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/526\/ad7\/a6b\/526ad7a6b7acc025af1360ea44e549f3.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/526\/ad7\/a6b\/526ad7a6b7acc025af1360ea44e549f3.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0411\u0435\u0437 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 (\u043e\u0448\u0438\u0431\u043a\u0430)<\/figcaption><\/div>\n<\/figure>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/0e9\/45e\/f50\/0e945ef50f72a4bed03bd88c5e405768.png\" alt=\"\u0421 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0435\u0439 (\u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435)\" title=\"\u0421 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0435\u0439 (\u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435)\" width=\"1229\" height=\"390\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/0e9\/45e\/f50\/0e945ef50f72a4bed03bd88c5e405768.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/0e9\/45e\/f50\/0e945ef50f72a4bed03bd88c5e405768.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0421 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0435\u0439 (\u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435)<\/figcaption><\/div>\n<\/figure>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0441 \u0431\u043b\u043e\u043a\u043e\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0441\u0435, \u0438 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0439 API \u043b\u043e\u0433\u0438\u043a\u0438.<\/p>\n<h3>\u041f\u0438\u0448\u0435\u043c API-\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b<\/h3>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u043d\u0430\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u043e\u0438\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0432\u0430 API-\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430.<\/p>\n<ul>\n<li>\n<p>\u041f\u0435\u0440\u0432\u044b\u0439 \u2014 \u0434\u043b\u044f \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445\u00a0<strong>ChromaDB<\/strong>. \u041e\u043d \u043d\u0443\u0436\u0435\u043d \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f: \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043d\u044f\u0442\u044c, \u043a\u0430\u043a \u043e\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438 \u0441 \u043a\u0430\u043a\u043e\u0439 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c\u044e.<\/p>\n<\/li>\n<li>\n<p>\u0412\u0442\u043e\u0440\u043e\u0439 \u2014 \u0443\u0436\u0435 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439, \u0441 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0438: \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 Chroma \u0438 \u043e\u0442\u0432\u0435\u0442\u0430 \u043e\u0442 AI.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f ChromaDB \u0441 FastAPI<\/h3>\n<p>\u0414\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 ChromaDB \u0432 \u043d\u0430\u0448 FastAPI-\u0441\u0435\u0440\u0432\u0438\u0441 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c\u00a0<strong>\u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0430\u043d\u0441<\/strong>\u00a0\u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043a\u043b\u0430\u0441\u0441\u0430\u00a0<code>ChromaVectorStore<\/code>, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u044e-\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430\u0445.<\/p>\n<p>\u0421\u0434\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e \u0432 \u0444\u0430\u0439\u043b\u0435:<code>app\/chroma_client\/chroma_store.py<\/code><\/p>\n<pre><code class=\"python\"># \u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0430\u043d\u0441 chroma_vectorstore = ChromaVectorStore()  # \u0417\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c def get_vectorstore() -&gt; ChromaVectorStore:     return chroma_vectorstore <\/code><\/pre>\n<h3>Pydantic-\u0441\u0445\u0435\u043c\u044b<\/h3>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043e\u043f\u0438\u0448\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u0438 \u0434\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u0432\u00a0<code>app\/api\/<\/code><a href=\"http:\/\/schemas.py\"><code>schemas.py<\/code><\/a>:<\/p>\n<pre><code class=\"python\">from pydantic import BaseModel from typing import Literal   class AskResponse(BaseModel):     response: str       class AskWithAIResponse(BaseModel):     response: str     provider: Literal[\"deepseek\", \"chatgpt\"] = \"deepseek\" <\/code><\/pre>\n<h3>\u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0438\u043c\u043f\u043e\u0440\u0442\u044b<\/h3>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432\u00a0<code>app\/api\/<\/code><a href=\"http:\/\/router.py\"><code>router.py<\/code><\/a>\u00a0\u0438 \u0430\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0438\u043c\u043f\u043e\u0440\u0442\u044b:<\/p>\n<pre><code class=\"python\">from fastapi import APIRouter, Depends, HTTPException, Response from fastapi.responses import StreamingResponse  from app.api.schemas import AskResponse, AskWithAIResponse, SUserAuth from app.api.utils import authenticate_user, create_jwt_token, get_current_user from app.chroma_client.ai_store import ChatWithAI from app.chroma_client.chroma_store import ChromaVectorStore, get_vectorstore <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f\u00a0<code>StreamingResponse<\/code>\u00a0\u0438\u0437 FastAPI \u2014 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u043c \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0435.<\/p>\n<h3>\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u21161: \u0437\u0430\u043f\u0440\u043e\u0441 \u043a Chroma \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e<\/h3>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0438\u0437 ChromaDB\u00a0<strong>\u0431\u0435\u0437 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0438<\/strong>:<\/p>\n<pre><code class=\"python\">@router.post(\"\/ask\") async def ask(     query: AskResponse,     vectorstore: ChromaVectorStore = Depends(get_vectorstore),     user_id: int = Depends(get_current_user), ):     results = await vectorstore.asimilarity_search(         query=query.response, with_score=True, k=5     )     formatted_results = []     for doc, score in results:         formatted_results.append({             \"text\": doc.page_content,             \"metadata\": doc.metadata,             \"similarity_score\": score,         })     return {\"results\": formatted_results} <\/code><\/pre>\n<p>\u041d\u0430 \u0432\u0445\u043e\u0434 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u0432\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<ul>\n<li>\n<p><code>get_vectorstore<\/code>\u00a0\u2014 \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a Chroma<\/p>\n<\/li>\n<li>\n<p><code>get_current_user<\/code>\u00a0\u2014 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0440\u0430\u0431\u043e\u0442\u0443:<\/h4>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/40d\/72e\/7b8\/40d72e7b8334608c6165161e40d1eb8c.png\" width=\"1249\" height=\"689\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/40d\/72e\/7b8\/40d72e7b8334608c6165161e40d1eb8c.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/40d\/72e\/7b8\/40d72e7b8334608c6165161e40d1eb8c.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435 \u2014 \u0432\u0441\u0451 \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \ud83c\udf89<\/p>\n<h3>\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u21162: Chroma + \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044c<\/h3>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0432\u0442\u043e\u0440\u043e\u0439 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442: Chroma + \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044c.<\/p>\n<pre><code class=\"python\">@router.post(\"\/ask_with_ai\") async def ask_with_ai(     query: AskWithAIResponse,     vectorstore: ChromaVectorStore = Depends(get_vectorstore),     user_id: int = Depends(get_current_user), ):     results = await vectorstore.asimilarity_search(         query=query.response, with_score=True, k=5     )      if results:         ai_context = \"\\n\".join([doc.page_content for doc, _ in results])         ai_store = ChatWithAI(provider=query.provider)          async def stream_response():             async for chunk in ai_store.astream_response(ai_context, query.response):                 yield chunk          return StreamingResponse(             stream_response(),             media_type=\"text\/plain\",             headers={                 \"Content-Type\": \"text\/plain\",                 \"Transfer-Encoding\": \"chunked\",                 \"Cache-Control\": \"no-cache\",                 \"Connection\": \"keep-alive\",             },         )     else:         return {\"response\": \"\u041d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e\"} <\/code><\/pre>\n<h4>\u0427\u0442\u043e \u0437\u0434\u0435\u0441\u044c \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442:<\/h4>\n<ol>\n<li>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0438\u0437 \u0431\u0430\u0437\u044b Chroma<\/p>\n<\/li>\n<li>\n<p>\u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c\u00a0<strong>AI-\u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<\/strong>\u00a0\u2014 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 \u043e\u0434\u043d\u0443 \u0441\u0442\u0440\u043e\u043a\u0443, \u0440\u0430\u0437\u0434\u0435\u043b\u0451\u043d\u043d\u0443\u044e \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430\u043c\u0438<\/p>\n<\/li>\n<li>\n<p>\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c\u00a0<code>ChatWithAI<\/code>\u00a0\u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u043c (<code>deepseek<\/code>\u00a0\u0438\u043b\u0438\u00a0<code>chatgpt<\/code>)<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u00a0<code>stream_response<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e \u043a\u0443\u0441\u043e\u0447\u043a\u0430\u043c \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043e\u0442 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0438<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u00a0<code>StreamingResponse<\/code>\u00a0\u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430\u043c\u0438<\/p>\n<\/li>\n<\/ol>\n<h4>\u0412\u0430\u0436\u043d\u043e\u0435 \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u0435:<\/h4>\n<pre><code class=\"python\">return StreamingResponse(     stream_response(),     media_type=\"text\/plain\",     headers={         \"Content-Type\": \"text\/plain\",         \"Transfer-Encoding\": \"chunked\",         \"Cache-Control\": \"no-cache\",         \"Connection\": \"keep-alive\",     }, ) <\/code><\/pre>\n<p><strong>Swagger \u043d\u0435 \u0443\u043c\u0435\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0438\u043c\u0438\u043d\u0433<\/strong>, \u0442\u0430\u043a \u0447\u0442\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u0444\u0440\u043e\u043d\u0442 \u2014 \u043a\u043e\u0433\u0434\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u0442\u0440\u0438\u043c\u0430.<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0441 API-\u043b\u043e\u0433\u0438\u043a\u043e\u0439 \u0432\u0441\u0451. \u0414\u0430\u043b\u044c\u0448\u0435 \u2014 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u0447\u0430\u0442\u0430. \u041f\u043e\u0435\u0445\u0430\u043b\u0438! \ud83d\ude80<\/p>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430<\/h3>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c\u00a0<strong>\u0434\u0432\u0430 HTML-\u0448\u0430\u0431\u043b\u043e\u043d\u0430<\/strong>\u00a0\u2014 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0432\u0445\u043e\u0434\u0430 \u0438 \u0447\u0430\u0442. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043a \u043d\u0438\u043c \u0441\u0442\u0438\u043b\u0438, JS-\u043b\u043e\u0433\u0438\u043a\u0443 \u0438 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0434\u0432\u0430 FastAPI-\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u044d\u0442\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.<\/p>\n<h3>1. \u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0432\u0445\u043e\u0434\u0430<\/h3>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c HTML-\u0448\u0430\u0431\u043b\u043e\u043d\u00a0<code>login.html<\/code>\u00a0\u043f\u043e \u043f\u0443\u0442\u0438:<br \/><code>app\/templates\/login.html<\/code><\/p>\n<pre><code>&lt;!DOCTYPE html&gt; &lt;html lang=\"en\"&gt;   &lt;head&gt;     &lt;meta charset=\"UTF-8\" \/&gt;     &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \/&gt;     &lt;title&gt;\u0412\u0445\u043e\u0434&lt;\/title&gt;     &lt;link       href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.3.0\/dist\/css\/bootstrap.min.css\"       rel=\"stylesheet\"     \/&gt;     &lt;link rel=\"stylesheet\" href=\"\/static\/form_style.css\" \/&gt;   &lt;\/head&gt;   &lt;body&gt;     &lt;div class=\"login-form\"&gt;       &lt;h2 class=\"text-center mb-4\"&gt;\u0412\u0445\u043e\u0434 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443&lt;\/h2&gt;       &lt;form id=\"loginForm\"&gt;         &lt;div class=\"form-floating mb-3\"&gt;           &lt;input             type=\"text\"             class=\"form-control\"             id=\"login\"             name=\"login\"             placeholder=\"\u041b\u043e\u0433\u0438\u043d\"             required           \/&gt;           &lt;label for=\"login\"&gt;\u041b\u043e\u0433\u0438\u043d&lt;\/label&gt;         &lt;\/div&gt;         &lt;div class=\"form-floating mb-4\"&gt;           &lt;input             type=\"password\"             class=\"form-control\"             id=\"password\"             name=\"password\"             placeholder=\"\u041f\u0430\u0440\u043e\u043b\u044c\"             required           \/&gt;           &lt;label for=\"password\"&gt;\u041f\u0430\u0440\u043e\u043b\u044c&lt;\/label&gt;         &lt;\/div&gt;         &lt;button type=\"submit\" class=\"btn btn-primary w-100\"&gt;\u0412\u043e\u0439\u0442\u0438&lt;\/button&gt;       &lt;\/form&gt;     &lt;\/div&gt;      &lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.3.0\/dist\/js\/bootstrap.bundle.min.js\"&gt;&lt;\/script&gt;     &lt;script src=\"\/static\/form_script.js\"&gt;&lt;\/script&gt;   &lt;\/body&gt; &lt;\/html&gt; <\/code><\/pre>\n<blockquote>\n<p>\ud83d\udccc \u042d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u0430\u044f \u0444\u043e\u0440\u043c\u0430 \u0432\u0445\u043e\u0434\u0430 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c\u00a0<strong>Bootstrap<\/strong>. \u042f \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u0441\u0442\u0438\u043b\u0438 (<code>form_style.css<\/code>), \u043d\u043e \u043d\u0430 \u043d\u0438\u0445 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u2014 \u043f\u0440\u0438 \u0436\u0435\u043b\u0430\u043d\u0438\u0438 \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0430\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 (\u0441\u043c. \u043c\u043e\u0439 Telegram-\u043a\u0430\u043d\u0430\u043b\u00a0<em>\u00ab<\/em><a href=\"https:\/\/t.me\/PythonPathMaster\"><em>\u041b\u0435\u0433\u043a\u0438\u0439 \u043f\u0443\u0442\u044c \u0432 Python<\/em><\/a><em>\u00bb<\/em>).<\/p>\n<\/blockquote>\n<h4>JavaScript-\u043b\u043e\u0433\u0438\u043a\u0430 \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430<\/h4>\n<p>JS-\u043a\u043e\u0434 \u0434\u043b\u044f \u0444\u043e\u0440\u043c\u044b \u043c\u044b \u0440\u0430\u0437\u043c\u0435\u0449\u0430\u0435\u043c \u0432:<br \/><code>app\/static\/form_script.js<\/code><\/p>\n<pre><code class=\"javascript\">document.getElementById('loginForm').addEventListener('submit', async (e) =&gt; {   e.preventDefault()    const login = document.getElementById('login').value   const password = document.getElementById('password').value    try {     const response = await fetch('\/api\/login', {       method: 'POST',       headers: {         'Content-Type': 'application\/json',       },       body: JSON.stringify({ login, password }),       credentials: 'include',     })      const data = await response.json()      if (data.message === 'Logged in') {       window.location.href = '\/'     } else {       alert('\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0445\u043e\u0434\u0430')     }   } catch (error) {     console.error('Error:', error)     alert('\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0432\u0445\u043e\u0434\u0435')   } }) <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434:<\/p>\n<ol>\n<li>\n<p>\u041f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0443 \u0444\u043e\u0440\u043c\u044b.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0431\u0438\u0440\u0430\u0435\u043c \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c.<\/p>\n<\/li>\n<li>\n<p>\u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u00a0<code>POST<\/code>-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430\u00a0<code>\/api\/login<\/code>.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438\u00a0<code>Logged in<\/code>, \u0442\u043e \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u0438\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u0443\u044e.<\/p>\n<\/li>\n<li>\n<p>\u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u2014 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c\u00a0<code>alert<\/code>.<\/p>\n<\/li>\n<\/ol>\n<h3>2. \u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0447\u0430\u0442\u0430<\/h3>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0435\u043c HTML-\u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043b\u044f \u0447\u0430\u0442\u0430:<br \/><code>app\/templates\/index.html<\/code><\/p>\n<pre><code>&lt;!DOCTYPE html&gt; &lt;html lang=\"ru\"&gt;   &lt;head&gt;     &lt;meta charset=\"UTF-8\" \/&gt;     &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \/&gt;     &lt;title&gt;Amvera Cloud Chat&lt;\/title&gt;     &lt;link       href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.3.2\/dist\/css\/bootstrap.min.css\"       rel=\"stylesheet\"     \/&gt;     &lt;link       href=\"https:\/\/cdn.jsdelivr.net\/npm\/highlight.js@11.9.0\/styles\/github.min.css\"       rel=\"stylesheet\"     \/&gt;     &lt;link       href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap-icons@1.11.1\/font\/bootstrap-icons.css\"       rel=\"stylesheet\"     \/&gt;     &lt;link rel=\"stylesheet\" href=\"\/static\/styles.css\" \/&gt;   &lt;\/head&gt;   &lt;body&gt;     &lt;div class=\"chat-container\"&gt;       &lt;div class=\"chat-header\"&gt;         &lt;h1&gt;Amvera Cloud Chat&lt;\/h1&gt;         &lt;div class=\"header-controls\"&gt;           &lt;select class=\"provider-select\" id=\"provider\"&gt;             &lt;option value=\"deepseek\"&gt;DeepSeek&lt;\/option&gt;             &lt;option value=\"chatgpt\"&gt;ChatGPT&lt;\/option&gt;           &lt;\/select&gt;           &lt;button class=\"btn btn-outline-danger\" id=\"logoutButton\"&gt;             &lt;i class=\"bi bi-box-arrow-right\"&gt;&lt;\/i&gt;             \u0412\u044b\u0439\u0442\u0438           &lt;\/button&gt;         &lt;\/div&gt;       &lt;\/div&gt;       &lt;div class=\"chat-output\" id=\"chatOutput\"&gt;&lt;\/div&gt;       &lt;div class=\"input-container\"&gt;         &lt;input           type=\"text\"           class=\"form-control\"           id=\"queryInput\"           placeholder=\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0432\u0430\u0448 \u0437\u0430\u043f\u0440\u043e\u0441...\"           aria-label=\"Query\"         \/&gt;         &lt;button class=\"btn btn-primary\" type=\"button\" id=\"sendButton\"&gt;           &lt;i class=\"bi bi-send\"&gt;&lt;\/i&gt;           \u041e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c         &lt;\/button&gt;       &lt;\/div&gt;     &lt;\/div&gt;      &lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.3.2\/dist\/js\/bootstrap.bundle.min.js\"&gt;&lt;\/script&gt;     &lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/marked\/marked.min.js\"&gt;&lt;\/script&gt;     &lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/highlight.js@11.9.0\/lib\/highlight.min.js\"&gt;&lt;\/script&gt;     &lt;script src=\"\/static\/script.js\"&gt;&lt;\/script&gt;   &lt;\/body&gt; &lt;\/html&gt; <\/code><\/pre>\n<blockquote>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b\u0438:<\/p>\n<ul>\n<li>\n<p><strong>bootstrap-icons<\/strong>\u00a0\u2014 \u0434\u043b\u044f \u0438\u043a\u043e\u043d\u043e\u043a<\/p>\n<\/li>\n<li>\n<p><strong>highlight.js<\/strong>\u00a0\u2014 \u0434\u043b\u044f \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u043a\u0438 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441\u0430<\/p>\n<\/li>\n<li>\n<p><strong>marked.js<\/strong>\u00a0\u2014 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 markdown-\u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u043e\u0442 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0438<\/p>\n<\/li>\n<\/ul>\n<\/blockquote>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0448\u0430\u0433\u043e\u043c \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c\u00a0<strong>\u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e JS-\u043b\u043e\u0433\u0438\u043a\u0443 \u0447\u0430\u0442\u0430<\/strong>\u00a0\u2014 \u0432 \u0444\u0430\u0439\u043b\u0435\u00a0<code>static\/script.js<\/code>. \u0418\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043c \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u0432\u043e\u0434 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u043d\u0430 API, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0438\u043c\u0438\u043d\u0433\u0430 \u043e\u0442\u0432\u0435\u0442\u0430.<\/p>\n<h3>\u041b\u043e\u0433\u0438\u043a\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438: JavaScript \u0434\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0447\u0430\u0442\u0430<\/h3>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043a\u043b\u044e\u0447\u0435\u0432\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u043b\u043e\u0433\u0438\u043a\u0438 \u2014 JavaScript-\u043a\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c \u0447\u0430\u0442\u0430.<\/p>\n<p>\u0424\u0430\u0439\u043b \u0441\u043e \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u043c:\u00a0<code>app\/static\/script.js<\/code><\/p>\n<h4>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 markdown \u0438 \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u043a\u0438 \u043a\u043e\u0434\u0430<\/h4>\n<p>\u0414\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0433\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043e\u0442\u0432\u0435\u0442\u043e\u0432, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0435\u0441\u043b\u0438 \u044d\u0442\u043e \u043a\u043e\u0434, \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443\u00a0<strong>marked<\/strong>\u00a0\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c\u00a0<strong>highlight.js<\/strong>\u00a0\u0434\u043b\u044f \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u043a\u0438.<\/p>\n<pre><code class=\"javascript\">marked.setOptions({   highlight: function (code, lang) {     if (lang &amp;&amp; hljs.getLanguage(lang)) {       return hljs.highlight(code, { language: lang }).value     }     return hljs.highlightAuto(code).value   },   breaks: false,   gfm: true,   headerIds: true,   mangle: false, }) <\/code><\/pre>\n<blockquote>\n<p>\ud83d\udca1 \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 GFM (GitHub Flavored Markdown) \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430, \u0447\u0442\u043e\u0431\u044b \u043a\u0440\u0430\u0441\u0438\u0432\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u043b\u0438\u0441\u044c \u0441\u043f\u0438\u0441\u043a\u0438, \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0438 \u0442.\u0434. \u041f\u043e\u0434\u0441\u0432\u0435\u0442\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u043f\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u044f\u0437\u044b\u043a\u0443, \u0442\u0430\u043a \u0438 \u0432 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.<\/p>\n<\/blockquote>\n<h4>\u0412\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438: \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439<\/h4>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438:<\/strong><\/p>\n<pre><code class=\"typescript\">function createLoadingIndicator() {   const loadingDiv = document.createElement('div')   loadingDiv.className = 'loading'   loadingDiv.innerHTML = `     &lt;div class=\"loading-dot\"&gt;&lt;\/div&gt;     &lt;div class=\"loading-dot\"&gt;&lt;\/div&gt;     &lt;div class=\"loading-dot\"&gt;&lt;\/div&gt;   `   return loadingDiv } <\/code><\/pre>\n<p><strong>\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0432 \u0447\u0430\u0442:<\/strong><\/p>\n<pre><code class=\"typescript\">function addMessage(content, isUser = false) {   const chatOutput = document.getElementById('chatOutput')   const messageDiv = document.createElement('div')   messageDiv.className = `message ${isUser ? 'user' : 'assistant'}` <\/code><\/pre>\n<p>\u0421\u044e\u0434\u0430 \u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f\u00a0<strong>\u043a\u043d\u043e\u043f\u043a\u0430 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/strong>\u00a0\u0441 \u0438\u043a\u043e\u043d\u043a\u043e\u0439. \u041f\u0440\u0438 \u043a\u043b\u0438\u043a\u0435 \u2014 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u0431\u0443\u0444\u0435\u0440 \u043e\u0431\u043c\u0435\u043d\u0430:<\/p>\n<pre><code class=\"typescript\">const copyButton = document.createElement('button') copyButton.className = 'copy-button' copyButton.innerHTML = '&lt;i class=\"bi bi-clipboard\"&gt;&lt;\/i&gt;' <\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0431\u043b\u043e\u043a \u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c:<\/p>\n<pre><code class=\"typescript\">const contentDiv = document.createElement('div') contentDiv.className = 'message-content' contentDiv.innerHTML = marked.parse(content) <\/code><\/pre>\n<p>\u0418 \u0432 \u043a\u043e\u043d\u0446\u0435 \u0432\u0441\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432 DOM:<\/p>\n<pre><code class=\"typescript\">messageDiv.appendChild(contentDiv) messageDiv.appendChild(copyButton) chatOutput.appendChild(messageDiv) chatOutput.scrollTop = chatOutput.scrollHeight <\/code><\/pre>\n<h4>\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f<\/h4>\n<p>\u0415\u0441\u043b\u0438 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043e\u0442\u0432\u0435\u0442 \u0441\u0442\u0440\u0438\u043c\u0438\u043d\u0433\u043e\u043c, \u0442\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e\u00a0<strong>\u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043f\u043e \u043c\u0435\u0440\u0435 \u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f<\/strong>\u00a0\u0434\u0430\u043d\u043d\u044b\u0445:<\/p>\n<pre><code class=\"typescript\">function updateMessage(messageDiv, content) {   const contentDiv = messageDiv.querySelector('.message-content') || document.createElement('div')   contentDiv.className = 'message-content'   contentDiv.innerHTML = marked.parse(content)   ... } <\/code><\/pre>\n<h4>\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/h4>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0441\u043e\u0431\u044b\u0442\u0438\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u044f Enter \u0438\u043b\u0438 \u043a\u043d\u043e\u043f\u043a\u0443 &#171;\u041e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c&#187;:<\/p>\n<pre><code class=\"typescript\">document   .getElementById('queryInput')   .addEventListener('keypress', function (e) {     if (e.key === 'Enter' &amp;&amp; !e.shiftKey) {       e.preventDefault()       document.getElementById('sendButton').click()     }   }) <\/code><\/pre>\n<h4>\u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043a API<\/h4>\n<p>\u0412\u043e\u0442 \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430:<\/p>\n<ol>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430, \u0432\u0432\u0435\u043b \u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0442\u0435\u043a\u0441\u0442.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0432 \u0447\u0430\u0442.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>\u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u00a0<code>\/api\/ask_with_ai<\/code>.<\/p>\n<\/li>\n<li>\n<p>\u041e\u0442\u0432\u0435\u0442 \u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f\u00a0<strong>\u043f\u043e\u0442\u043e\u043a\u043e\u043c<\/strong>\u00a0(stream).<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0441\u0438\u043c\u0432\u043e\u043b\u044b\u00a0<strong>\u043f\u043e \u043e\u0434\u043d\u043e\u043c\u0443<\/strong>\u00a0\u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432 \u0447\u0430\u0442 \u0441 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u0432 50 \u043c\u0441.<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"typescript\">const reader = response.body.getReader() const decoder = new TextDecoder('utf-8') let buffer = '' let currentText = '' <\/code><\/pre>\n<p>\u0417\u0430\u0442\u0435\u043c \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u043e\u043e\u0447\u0435\u0440\u0435\u0434\u043d\u043e\u0435 &#171;\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u043d\u0438\u0435&#187; \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432 \u0438 \u043f\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0442\u0435\u043a\u0441\u0442\u0430 \u043e\u0442\u0432\u0435\u0442\u0430:<\/p>\n<pre><code class=\"typescript\">while (buffer.length &gt; 0) {   const char = buffer[0]   buffer = buffer.slice(1)   currentText += char   await new Promise((resolve) =&gt; setTimeout(resolve, 50))   updateMessage(assistantMessage, currentText) } <\/code><\/pre>\n<h4>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u0432\u044b\u0445\u043e\u0434 \u0438\u0437 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430<\/h4>\n<p>\u0415\u0441\u043b\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u0435\u0440\u043d\u0443\u043b 401 \u2014 \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u0438\u043c \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043b\u043e\u0433\u0438\u043d\u0430:<\/p>\n<pre><code class=\"typescript\">if (response.status === 401) {   window.location.href = '\/login' } <\/code><\/pre>\n<p>\u0410 \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043a\u043d\u043e\u043f\u043a\u0438 &#171;\u0412\u044b\u0439\u0442\u0438&#187;:<\/p>\n<pre><code class=\"typescript\">document.getElementById('logoutButton').addEventListener('click', async () =&gt; {   await fetch('\/api\/logout', { method: 'POST', credentials: 'include' })   window.location.href = '\/login' }) <\/code><\/pre>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u0443 \u043d\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434:<\/p>\n<ul>\n<li>\n<p>\u041a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0441 Bootstrap \u0438 markdown<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0441\u0442\u0440\u0438\u043c\u0438\u043d\u0433\u0430 \u043e\u0442\u0432\u0435\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u041a\u043d\u043e\u043f\u043a\u0430 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a<\/p>\n<\/li>\n<li>\n<p>\u041b\u043e\u0433\u0438\u043a\u0430 \u0432\u044b\u0445\u043e\u0434\u0430<\/p>\n<\/li>\n<\/ul>\n<p>\u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c\u00a0<strong>FastAPI-\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u044d\u0442\u0438 HTML-\u0448\u0430\u0431\u043b\u043e\u043d\u044b.<\/p>\n<h3>\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b \u0434\u043b\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0430 HTML-\u0441\u0442\u0440\u0430\u043d\u0438\u0446<\/h3>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043c\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c HTML-\u0448\u0430\u0431\u043b\u043e\u043d\u044b \u043a FastAPI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u2014 \u043e\u043f\u0438\u0448\u0435\u043c \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u0437\u0430 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0432\u0445\u043e\u0434\u0430 \u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0447\u0430\u0442\u0430.<\/p>\n<h4>\u0421\u043e\u0437\u0434\u0430\u0435\u043c\u00a0router.py<\/h4>\n<p>\u0412 \u043f\u0430\u043f\u043a\u0435\u00a0<code>app\/pages<\/code>\u00a0\u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b:<\/p>\n<pre><code>app\/pages\/router.py <\/code><\/pre>\n<h4>\u0418\u043c\u043f\u043e\u0440\u0442\u044b<\/h4>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u044b:<\/p>\n<pre><code class=\"python\">from fastapi import APIRouter, Depends, Request from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates  from app.api.utils import get_optional_current_user <\/code><\/pre>\n<blockquote>\n<p>\ud83d\udca1 \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u00a0<code>Jinja2Templates<\/code>, \u0447\u0442\u043e\u0431\u044b \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c HTML-\u0448\u0430\u0431\u043b\u043e\u043d\u044b. \u0410 \u0447\u0435\u0440\u0435\u0437\u00a0<code>get_optional_current_user<\/code>\u00a0\u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d \u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0438 \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c None \u0435\u0441\u043b\u0438 \u044d\u0442\u043e \u043d\u0435 \u0442\u0430\u043a.<\/p>\n<\/blockquote>\n<h4>\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u043e\u0443\u0442\u0435\u0440\u0430 \u0438 \u0448\u0430\u0431\u043b\u043e\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u0430<\/h4>\n<pre><code class=\"python\">router = APIRouter() templates = Jinja2Templates(directory=\"app\/templates\") <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c\u00a0<code>templates.TemplateResponse(...)<\/code>\u00a0\u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c \u0448\u0430\u0431\u043b\u043e\u043d\u044b \u0432 \u043f\u0430\u043f\u043a\u0435\u00a0<code>app\/templates<\/code>.<\/p>\n<h4>\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442: \u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0432\u0445\u043e\u0434\u0430<\/h4>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0430\u0440\u0448\u0440\u0443\u0442, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0449\u0438\u0439\u00a0<code>\/login<\/code>:<\/p>\n<pre><code class=\"python\">@router.get(\"\/login\", response_class=HTMLResponse) async def login_page(     request: Request,     user_id: int = Depends(get_optional_current_user), ):     if user_id:         return RedirectResponse(url=\"\/\")     else:         return templates.TemplateResponse(\"login.html\", {\"request\": request}) <\/code><\/pre>\n<p><strong>\u041b\u043e\u0433\u0438\u043a\u0430:<\/strong><\/p>\n<ul>\n<li>\n<p>\u0415\u0441\u043b\u0438\u00a0<code>user_id<\/code>\u00a0\u043d\u0430\u0439\u0434\u0435\u043d (\u0442\u043e \u0435\u0441\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0443\u0436\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d) \u2014 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442 \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u0443\u044e.<\/p>\n<\/li>\n<li>\n<p>\u0418\u043d\u0430\u0447\u0435 \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c HTML-\u0448\u0430\u0431\u043b\u043e\u043d \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0432\u0445\u043e\u0434\u0430 (<code>login.html<\/code>).<\/p>\n<\/li>\n<\/ul>\n<h4>\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442: \u0413\u043b\u0430\u0432\u043d\u0430\u044f (\u0447\u0430\u0442)<\/h4>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043e\u043f\u0438\u0448\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b (<code>\/<\/code>), \u0433\u0434\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u043d\u0430\u0448 \u0447\u0430\u0442:<\/p>\n<pre><code class=\"python\">@router.get(\"\/\", response_class=HTMLResponse) async def chat_page(     request: Request,     user_id: int = Depends(get_optional_current_user), ):     if user_id:         return templates.TemplateResponse(\"index.html\", {\"request\": request})     else:         return RedirectResponse(url=\"\/login\") <\/code><\/pre>\n<p><strong>\u0417\u0434\u0435\u0441\u044c \u0432\u0441\u0451 \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442:<\/strong><\/p>\n<ul>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d \u2014 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043c\u00a0<code>index.html<\/code>\u00a0\u0441\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435\u0439 \u0447\u0430\u0442\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u043d\u0435\u0442 \u2014 \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u0438\u043c \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043d\u0430\u00a0<code>\/login<\/code>.<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043e\u043f\u0438\u0441\u0430\u0442\u044c main-\u0444\u0430\u0439\u043b \u043f\u0440\u0438\u043b\u0436\u0435\u043d\u0438\u044f \u0438 \u043c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0437\u0430\u043f\u0443\u0441\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0430!<\/p>\n<h3>\u0424\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0448\u0442\u0440\u0438\u0445 \u2014\u00a0main.py<\/h3>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043c\u044b \u0441\u043e\u0431\u0435\u0440\u0451\u043c \u0432\u0441\u0451, \u0447\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0440\u0430\u043d\u0435\u0435, \u0432 \u0435\u0434\u0438\u043d\u044b\u0439 \u0440\u0430\u0431\u043e\u0447\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442. \u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0444\u0430\u0439\u043b\u00a0<a href=\"http:\/\/main.py\"><code>main.py<\/code><\/a>\u00a0\u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u0437\u0430 \u0437\u0430\u043f\u0443\u0441\u043a FastAPI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u0432, \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0431\u0430\u0437\u044b \u0438 \u0441\u0442\u0430\u0442\u0438\u043a\u0438.<\/p>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0432 \u0444\u0430\u0439\u043b\u0435:<\/p>\n<pre><code>app\/main.py <\/code><\/pre>\n<h4>\u0418\u043c\u043f\u043e\u0440\u0442\u044b<\/h4>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u0432\u0441\u0451 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435:<\/p>\n<pre><code class=\"python\">from contextlib import asynccontextmanager  from fastapi import FastAPI from fastapi.staticfiles import StaticFiles  from app.api.router import router as api_router from app.chroma_client.chroma_store import chroma_vectorstore from app.pages.router import router as page_router <\/code><\/pre>\n<blockquote>\n<p>\u0417\u0434\u0435\u0441\u044c \u0432\u0441\u0451 \u043f\u0440\u043e\u0441\u0442\u043e: \u043c\u044b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0440\u043e\u0443\u0442\u0435\u0440\u044b, \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f ChromaDB \u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0443\u044e \u0443\u0442\u0438\u043b\u0438\u0442\u0443 \u0434\u043b\u044f \u0441\u0442\u0430\u0442\u0438\u043a\u0438. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u2014 \u044f \u0434\u0430\u043b \u0430\u043b\u0438\u0430\u0441\u044b (<code>as api_router<\/code>,\u00a0<code>as page_router<\/code>), \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043f\u0443\u0442\u0430\u043d\u0438\u0446\u044b.<\/p>\n<\/blockquote>\n<h4>\u0416\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/h4>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c\u00a0<code>lifespan<\/code>\u00a0\u2014 \u044d\u0442\u043e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u0441\u0442\u0430\u0440\u0442\u0435 \u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f:<\/p>\n<pre><code class=\"python\">@asynccontextmanager async def lifespan(app: FastAPI):     await chroma_vectorstore.init()     app.include_router(api_router, prefix=\"\/api\", tags=[\"API\"])     app.include_router(page_router, tags=[\"\u0424\u0420\u041e\u041d\u0422\"])     app.mount(\"\/static\", StaticFiles(directory=\"app\/static\"), name=\"static\")     yield     await chroma_vectorstore.close() <\/code><\/pre>\n<p>\ud83d\udccc \u0427\u0442\u043e \u0437\u0434\u0435\u0441\u044c \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442:<\/p>\n<ol>\n<li>\n<p><strong>\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c ChromaDB<\/strong>\u00a0\u2014 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u00a0<code>init()<\/code>\u00a0\u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0431\u0430\u0437\u0435.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c API-\u0440\u043e\u0443\u0442\u044b<\/strong>\u00a0\u2014 \u0441 \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c\u00a0<code>\/api<\/code>\u00a0(\u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 API-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0431\u044b\u043b\u0438 \u043f\u043e\u00a0<code>\/api\/...<\/code>).<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b \u0434\u043b\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0430 HTML<\/strong>\u00a0\u2014 \u0431\u0435\u0437 \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0441\u0442\u0430\u0442\u0438\u043a\u0443<\/strong>\u00a0\u2014 \u0432\u0441\u0435 \u0444\u0430\u0439\u043b\u044b \u0438\u0437\u00a0<code>app\/static<\/code>\u00a0\u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u043a\u0430\u043a\u00a0<code>\/static\/...<\/code>.<\/p>\n<\/li>\n<li>\n<p><strong>\u0417\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c ChromaDB<\/strong>\u00a0\u043f\u0440\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b.<\/p>\n<\/li>\n<\/ol>\n<h4>\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f FastAPI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/h4>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u0430\u043c\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0435\u043c\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430:<\/p>\n<pre><code class=\"python\">app = FastAPI(lifespan=lifespan) <\/code><\/pre>\n<h4>\u0417\u0430\u043f\u0443\u0441\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h4>\n<p>\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439:<\/p>\n<pre><code class=\"bash\">uvicorn app.main:app --port 8000 --host 0.0.0.0 <\/code><\/pre>\n<blockquote>\n<p>\ud83d\udd25 \u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u0435 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043d\u0430\u00a0<a href=\"http:\/\/localhost:8000\"><code>http:\/\/localhost:8000<\/code><\/a>\u00a0\u2014 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0439\u0442\u0435 \u0440\u0430\u0431\u043e\u0442\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430!<\/p>\n<\/blockquote>\n<p>\u041e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e \u0438 \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044c, \u0447\u0442\u043e \u043f\u0440\u043e\u0435\u043a\u0442 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d.<\/p>\n<h3>\u0414\u0435\u043f\u043b\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 Amvera Cloud<\/h3>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0433\u043e\u0442\u043e\u0432 \u043a \u0440\u0430\u0431\u043e\u0442\u0435, \u0441\u0430\u043c\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043a\u0430\u0442\u0438\u0442\u044c \u0435\u0433\u043e \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d \u2014 \u0447\u0442\u043e\u0431\u044b \u043b\u044e\u0431\u043e\u0439, \u0443 \u043a\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0430 \u0438 \u0434\u043e\u0441\u0442\u0443\u043f (\u043b\u043e\u0433\u0438\u043d \u0441 \u043f\u0430\u0440\u043e\u043b\u0435\u043c), \u043c\u043e\u0433 \u0438\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f\u00a0<strong>Amvera Cloud<\/strong>\u00a0\u2014 \u0443\u0434\u043e\u0431\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u043e\u0439 \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0430 Python \u0438 \u043f\u0440\u043e\u0447\u0438\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439. \u0422\u0435\u043c \u0431\u043e\u043b\u0435\u0435, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0432\u0441\u0451 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435.<\/p>\n<h4>\u0428\u0430\u0433\u0438 \u043f\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/h4>\n<ul>\n<li>\n<p><strong>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c\u0441\u044f<\/strong>\u00a0\u043d\u0430\u00a0<a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_chromadb_part2\">Amvera Cloud<\/a>\u00a0\u2014 \u0435\u0441\u043b\u0438 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430 \u0435\u0449\u0451 \u043d\u0435\u0442. \u041d\u043e\u0432\u0438\u0447\u043a\u0430\u043c \u043d\u0430\u0447\u0438\u0441\u043b\u044f\u0435\u0442\u0441\u044f \u0431\u043e\u043d\u0443\u0441 \u043d\u0430 \u0431\u0430\u043b\u0430\u043d\u0441, \u0447\u0442\u043e \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043f\u0440\u0438\u044f\u0442\u043d\u043e.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0445\u043e\u0434\u0438\u043c \u0432\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0443\u00a0<strong>\u00ab\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u00bb<\/strong>:<a href=\"https:\/\/cloud.amvera.ru\/projects\/applications\">https:\/\/cloud.amvera.ru\/projects\/applications<\/a><\/p>\n<\/li>\n<li>\n<p>\u0416\u043c\u0451\u043c\u00a0<strong>\u00ab\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u00bb<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c\u00a0<strong>\u0438\u043c\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/strong>\u00a0\u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u043b\u0430\u0442\u0438\u043d\u0438\u0446\u0430, \u0431\u0435\u0437 \u043f\u0440\u043e\u0431\u0435\u043b\u043e\u0432 \u0438 \u0441\u043f\u0435\u0446\u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c\u00a0<strong>\u0442\u0430\u0440\u0438\u0444<\/strong>\u00a0\u2014\u00a0<strong>\u043d\u0435 \u043d\u0438\u0436\u0435 &#171;\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043b\u044e\u0441&#187;<\/strong>, \u0442\u0430\u043a \u043a\u0430\u043a \u0443 \u043d\u0430\u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u0430\u044f \u0431\u0430\u0437\u0430 (ChromaDB), \u0438 \u043d\u0443\u0436\u0435\u043d \u043c\u0438\u043d\u0438\u043c\u0443\u043c 1 \u0413\u0411 \u041e\u0417\u0423.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3cb\/c3b\/e0d\/3cbc3be0db7b6d67dae6eb3da0116f5d.png\" width=\"782\" height=\"384\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/3cb\/c3b\/e0d\/3cbc3be0db7b6d67dae6eb3da0116f5d.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3cb\/c3b\/e0d\/3cbc3be0db7b6d67dae6eb3da0116f5d.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<ul>\n<li>\n<p>\u041d\u0430 \u044d\u0442\u0430\u043f\u0435 \u0432\u044b\u0431\u043e\u0440\u0430 \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u043e\u0432:<\/p>\n<ul>\n<li>\n<p>\u041c\u043e\u0436\u043d\u043e\u00a0<strong>\u043f\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0432\u0440\u0443\u0447\u043d\u0443\u044e<\/strong>\u00a0\u0432 \u043e\u043a\u043d\u043e (\u0431\u0435\u0437\u00a0<code>venv<\/code>, \u043e\u043d \u043d\u0435 \u043d\u0443\u0436\u0435\u043d).<\/p>\n<\/li>\n<li>\n<p>\u0418\u043b\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u00a0<strong>\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0447\u0435\u0440\u0435\u0437 Git<\/strong>\u00a0\u2014 \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/75d\/04d\/28e\/75d04d28eb91eaf7cdec20dd46c7a612.png\" width=\"781\" height=\"759\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/75d\/04d\/28e\/75d04d28eb91eaf7cdec20dd46c7a612.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/75d\/04d\/28e\/75d04d28eb91eaf7cdec20dd46c7a612.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<ul>\n<li>\n<p>\u0411\u043b\u043e\u043a \u0441 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c\u0438\u00a0<strong>\u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c<\/strong>\u00a0\u2014 \u0432\u0435\u0434\u044c \u043c\u044b \u0437\u0430\u0440\u0430\u043d\u0435\u0435 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043b\u0438 \u0444\u0430\u0439\u043b\u00a0<code>.env<\/code>.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430 \u0444\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u043c \u043e\u043a\u043d\u0435 \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u043a\u0430\u043a \u043d\u0430 \u0441\u043a\u0440\u0438\u043d\u0435 \u043d\u0438\u0436\u0435:<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8a9\/6f8\/18e\/8a96f818e4b164947b709cd6eda31c35.png\" width=\"719\" height=\"806\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/8a9\/6f8\/18e\/8a96f818e4b164947b709cd6eda31c35.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8a9\/6f8\/18e\/8a96f818e4b164947b709cd6eda31c35.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h4>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0434\u043e\u043c\u0435\u043d<\/h4>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u043e\u0432 \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<ul>\n<li>\n<p>\u0417\u0430\u0445\u043e\u0434\u0438\u043c \u0432\u00a0<strong>\u0441\u0430\u043c \u043f\u0440\u043e\u0435\u043a\u0442<\/strong>\u00a0\u2192 \u0432\u043a\u043b\u0430\u0434\u043a\u0430\u00a0<strong>\u00ab\u0414\u043e\u043c\u0435\u043d\u044b\u00bb<\/strong><\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c:<\/p>\n<ul>\n<li>\n<p><strong>\u0411\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u0434\u043e\u043c\u0435\u043d<\/strong>\u00a0\u043e\u0442 Amvera (\u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u0443\u0434\u043e\u0431\u043d\u043e)<\/p>\n<\/li>\n<li>\n<p>\u0438\u043b\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c\u00a0<strong>\u0441\u0432\u043e\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0434\u043e\u043c\u0435\u043d<\/strong><\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0434\u043e\u043c\u0435\u043d\u0430 \u043e\u0442 Amvera:\u00a0<a href=\"https:\/\/amveraai-yakvenalex.amvera.io\"><code>https:\/\/amveraai-yakvenalex.amvera.io<\/code><\/a><\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1ac\/289\/504\/1ac289504729e9533b8a78c71c5e7c75.png\" width=\"1502\" height=\"567\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/1ac\/289\/504\/1ac289504729e9533b8a78c71c5e7c75.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1ac\/289\/504\/1ac289504729e9533b8a78c71c5e7c75.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h4>\u0422\u0435\u0440\u043f\u0435\u043d\u0438\u0435 \u2014 \u0437\u0430\u043b\u043e\u0433 \u0443\u0441\u043f\u0435\u0445\u0430<\/h4>\n<p>\u0418\u0437-\u0437\u0430 \u043e\u0431\u044a\u0451\u043c\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 (\u0438 ChromaDB \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435) \u043f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c\u00a0<strong>15\u201320 \u043c\u0438\u043d\u0443\u0442<\/strong>. \u041d\u0435 \u043f\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u0439 \u2014 \u044d\u0442\u043e \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438:<\/p>\n<ul>\n<li>\n<p>\u041f\u0440\u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0435 \u043f\u043e \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u0434\u043e\u043c\u0435\u043d\u0443 \u0432\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435 \u0444\u043e\u0440\u043c\u0443 \u0432\u0445\u043e\u0434\u0430 \u0432 \u0447\u0430\u0442<\/p>\n<\/li>\n<li>\n<p>\u0410 \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 Amvera \u0431\u0443\u0434\u0435\u0442 \u0441\u0442\u0430\u0442\u0443\u0441 \u00ab\u041f\u0440\u043e\u0435\u043a\u0442 \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u00bb<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/345\/af9\/d62\/345af9d62c0991295e698507c8ad7efa.png\" alt=\"\u0415\u0441\u043b\u0438 \u043d\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043b\u0441\u044f \u0434\u043e\u043c\u0435\u043d - \u043a\u043b\u0438\u043a\u043d\u0438\u0442\u0435 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 &quot;\u043f\u0435\u0440\u0435\u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442&quot;\" title=\"\u0415\u0441\u043b\u0438 \u043d\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043b\u0441\u044f \u0434\u043e\u043c\u0435\u043d - \u043a\u043b\u0438\u043a\u043d\u0438\u0442\u0435 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 &quot;\u043f\u0435\u0440\u0435\u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442&quot;\" width=\"1469\" height=\"443\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/345\/af9\/d62\/345af9d62c0991295e698507c8ad7efa.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/345\/af9\/d62\/345af9d62c0991295e698507c8ad7efa.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0415\u0441\u043b\u0438 \u043d\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043b\u0441\u044f \u0434\u043e\u043c\u0435\u043d &#8212; \u043a\u043b\u0438\u043a\u043d\u0438\u0442\u0435 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 &#171;\u043f\u0435\u0440\u0435\u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442&#187;<\/figcaption><\/div>\n<\/figure>\n<h4>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c:<\/h4>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f8e\/1ce\/16e\/f8e1ce16e2d95f294f5995bce3f3756b.gif\" width=\"720\" height=\"363\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f8e\/1ce\/16e\/f8e1ce16e2d95f294f5995bce3f3756b.gif 780w,&#10;       https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f8e\/1ce\/16e\/f8e1ce16e2d95f294f5995bce3f3756b.gif 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h4>\u0412\u0441\u0451 \u0433\u043e\u0442\u043e\u0432\u043e!<\/h4>\n<p>\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u044e \u2014 \u043c\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u0438 \u0437\u0430\u0434\u0435\u043f\u043b\u043e\u0438\u043b\u0438 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0435 AI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435! \u041e\u043d\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u0436\u0438\u0432\u0451\u0442 \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 \u0438 \u0433\u043e\u0442\u043e\u0432\u043e \u043a \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438.<\/p>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>\u041f\u043e\u0437\u0430\u0434\u0438 \u2014 \u043d\u0430\u0441\u044b\u0449\u0435\u043d\u043d\u043e\u0435 \u0438 \u0443\u0432\u043b\u0435\u043a\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u0443\u0442\u0435\u0448\u0435\u0441\u0442\u0432\u0438\u0435: \u043e\u0442 \u043f\u0435\u0440\u0432\u043e\u0439 \u0438\u0434\u0435\u0438 \u0434\u043e \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0433\u043e AI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441 \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u044c\u044e \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u043c \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c. \u0412\u043c\u0435\u0441\u0442\u0435 \u043c\u044b \u043f\u0440\u043e\u0448\u043b\u0438 \u0432\u0441\u0435 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u044d\u0442\u0430\u043f\u044b \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b.<\/p>\n<p>\u0412\u043e\u0442 \u0447\u0442\u043e \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c:<\/p>\n<ul>\n<li>\n<p>\u041f\u043e\u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u0447\u0438\u0441\u0442\u0443\u044e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 \u043d\u0430 \u0431\u0430\u0437\u0435\u00a0FastAPI, \u0447\u0451\u0442\u043a\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0432 \u043b\u043e\u0433\u0438\u043a\u0443 API \u0438 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430;<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u043b\u0438\u00a0\u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433 HTML-\u0441\u0442\u0440\u0430\u043d\u0438\u0446, \u0432\u043d\u0435\u0434\u0440\u0438\u043b\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0447\u0430\u0442-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441;<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u043b\u0438\u0441\u044c \u0441 \u0442\u0435\u043c,\u00a0\u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u0430\u044f \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445, \u0438 \u043d\u0430\u0443\u0447\u0438\u043b\u0438\u0441\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u00a0ChromaDB\u00a0\u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438 \u043f\u043e\u0438\u0441\u043a\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043f\u043e \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u044b\u043c embeddings;<\/p>\n<\/li>\n<li>\n<p>\u0421\u0432\u044f\u0437\u0430\u043b\u0438 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u00a0\u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u044c\u044e, \u0447\u0442\u043e\u0431\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0443\u043c\u043d\u044b\u0439, \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e-\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0439 \u0447\u0430\u0442;<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u043b\u0438\u00a0\u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439, \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0441 \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u043a\u043e\u0439 \u043a\u043e\u0434\u0430, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u044b\u043c UI;<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u043b\u0438 \u0432\u0441\u0451 \u0432 \u0435\u0434\u0438\u043d\u044b\u0439\u00a0<a href=\"http:\/\/main.py\"><code>main.py<\/code><\/a>, \u043e\u0444\u043e\u0440\u043c\u0438\u0432 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b \u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0432\u0441\u0435\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0430;<\/p>\n<\/li>\n<li>\n<p>\u0418 \u0432 \u0444\u0438\u043d\u0430\u043b\u0435 \u2014\u00a0\u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u043b\u0438 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_chromadb_part2\">Amvera Cloud<\/a>, \u0441\u0434\u0435\u043b\u0430\u0432 \u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0436\u0435\u043b\u0430\u044e\u0449\u0438\u0445.<\/p>\n<\/li>\n<\/ul>\n<p>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u2014 \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c, \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u0430 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u0438 \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439. \u0412\u0441\u0451 \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e, \u0447\u0442\u043e\u0431\u044b \u0432\u0430\u043c \u0431\u044b\u043b\u043e \u043b\u0435\u0433\u043a\u043e \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f, \u043d\u043e \u0438 \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<p>\u0412\u044b \u0434\u0435\u0440\u0436\u0438\u0442\u0435 \u0432 \u0440\u0443\u043a\u0430\u0445 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e pet-\u043f\u0440\u043e\u0435\u043a\u0442, \u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u0443\u044e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443: \u0435\u0451 \u043c\u043e\u0436\u043d\u043e \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434 \u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a\u0430 \u0438\u043b\u0438 \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442\u044c \u0432 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 SaaS-\u0441\u0435\u0440\u0432\u0438\u0441.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u2014 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0442\u0435 \u0441\u0442\u0430\u0442\u044c\u044e \u043b\u0430\u0439\u043a\u043e\u043c \u0438\u043b\u0438 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u043c. \u041f\u043e\u043b\u043d\u044b\u0439 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434, \u0430 \u0442\u0430\u043a\u0436\u0435 \u044d\u043a\u0441\u043a\u043b\u044e\u0437\u0438\u0432\u043d\u044b\u0435 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u044e\u0442\u0441\u044f \u043d\u0430 \u0425\u0430\u0431\u0440\u0435, \u0432\u044b \u043d\u0430\u0439\u0434\u0451\u0442\u0435 \u0432 \u043c\u043e\u0435\u043c\u00a0\u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u043c Telegram-\u043a\u0430\u043d\u0430\u043b\u0435\u00a0\u00ab<a href=\"https:\/\/t.me\/PythonPathMaster\">\u041b\u0451\u0433\u043a\u0438\u0439 \u043f\u0443\u0442\u044c \u0432 Python<\/a>\u00bb. \u041d\u0430\u0441 \u0442\u0430\u043c \u0443\u0436\u0435 \u0431\u043e\u043b\u0435\u0435\u00a03300 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432\u00a0\u2014 \u043f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u044f\u0439\u0442\u0435\u0441\u044c!<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0451. \u0423\u0432\u0438\u0434\u0438\u043c\u0441\u044f \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><\/p>\n<div class=\"tm-article-poll-container\"><!--[--><\/p>\n<div class=\"tm-article-poll tm-article-poll_variant-bordered\">\n<div class=\"tm-notice tm-notice_positive tm-article-poll__notice\"><!----><\/p>\n<div class=\"tm-notice__inner\"><!----><\/p>\n<div class=\"tm-notice__content\" data-test-id=\"notice-content\"><!--[--><span>\u0422\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0443\u0447\u0430\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432 \u043e\u043f\u0440\u043e\u0441\u0435. <a rel=\"nofollow\" href=\"\/kek\/v1\/auth\/habrahabr\/?back=\/ru\/companies\/amvera\/articles\/902868\/&#038;hl=ru\">\u0412\u043e\u0439\u0434\u0438\u0442\u0435<\/a>, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430.<\/span><!--]--><\/div>\n<\/div>\n<\/div>\n<p><!--[--><\/p>\n<div class=\"tm-article-poll__header\">\u0425\u043e\u0442\u0438\u0442\u0435 \u0435\u0449\u0451 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430 \u043f\u043e \u0442\u0435\u043c\u0435 \u0418\u0418?<\/div>\n<div class=\"tm-article-poll__answers\"><!--[--><\/p>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent tm-article-poll__answer-percent_winning\">75% <\/span><span class=\"tm-article-poll__answer-label\">\u041a\u043e\u043d\u0435\u0447\u043d\u043e<\/span><span class=\"tm-article-poll__answer-votes\">6<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress tm-article-poll__answer-progress_winning\" style=\"width: 75%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">12.5% <\/span><span class=\"tm-article-poll__answer-label\">\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e<\/span><span class=\"tm-article-poll__answer-votes\">1<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 12.5%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">12.5% <\/span><span class=\"tm-article-poll__answer-label\">\u041d\u0435\u0442<\/span><span class=\"tm-article-poll__answer-votes\">1<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 12.5%\"><\/div>\n<\/div>\n<\/div>\n<p><!--]--><\/div>\n<div class=\"tm-article-poll__stats\"> \u041f\u0440\u043e\u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043b\u0438 8 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.   \u0412\u043e\u0437\u0434\u0435\u0440\u0436\u0430\u0432\u0448\u0438\u0445\u0441\u044f \u043d\u0435\u0442. <\/div>\n<p><!--]--><\/div>\n<p><!--]--><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/902868\/\"> https:\/\/habr.com\/ru\/articles\/902868\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0414\u0440\u0443\u0437\u044c\u044f, \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e!<\/p>\n<p>\u041a\u0430\u043a \u0432\u044b \u043f\u043e\u043d\u044f\u043b\u0438 \u043f\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044e \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u2014 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u043f\u043e\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0432 \u0442\u0435\u043c\u0443 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043b\u0438\u0447\u043d\u043e\u0433\u043e \u0418\u0418-\u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043d\u0442\u0430 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<h3>\u041a\u0440\u0430\u0442\u043a\u043e\u0435 \u043d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u0435 \u043e \u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438<\/h3>\n<p>\u041d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u044e, \u0447\u0442\u043e \u0432 <a href=\"https:\/\/habr.com\/ru\/companies\/amvera\/articles\/897830\/\">\u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 \u0447\u0430\u0441\u0442\u0438<\/a> \u043c\u044b:<\/p>\n<ul>\n<li>\n<p>\u0420\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c \u0441 \u043f\u043e\u043d\u044f\u0442\u0438\u0435\u043c \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0443\u0447\u0438\u043b\u0438\u0441\u044c \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0431\u0430\u0437\u0443<\/p>\n<\/li>\n<li>\n<p>\u0418\u0437\u0443\u0447\u0438\u043b\u0438 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c &#171;\u0443\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430&#187;<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u043b\u0438\u0441\u044c \u0441 \u043c\u043e\u0449\u043d\u044b\u043c Python-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c Langchain<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u043b\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0439 ChatGPT \u0438 Deepseek<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u043b\u0438 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0434\u0438\u0430\u043b\u043e\u0433\u0430 \u0441 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0431\u0430\u0437\u044b \u0437\u043d\u0430\u043d\u0438\u0439 (\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_chromadb_part2\">Amvera Cloud<\/a>)<\/p>\n<\/li>\n<\/ul>\n<h3>\u041e \u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0435\u0439 \u0447\u0430\u0441\u0442\u0438<\/h3>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u043f\u0440\u043e\u043a\u0430\u0447\u0430\u0435\u043c \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442 \u2014 \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u043c \u0435\u0433\u043e \u0432 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0438\u0441 \u0441 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u043c \u0432\u0435\u0431-\u0447\u0430\u0442\u043e\u043c \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c\u0438. \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u043d\u0435\u043a\u043e\u0435 \u043f\u043e\u0434\u043e\u0431\u0438\u0435 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u0441\u0430\u0439\u0442\u0430\u00a0<a href=\"https:\/\/chat.openai.com\/chat\">https:\/\/chat.openai.com\/chat<\/a>, \u043d\u043e \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0432\u044b\u0431\u043e\u0440\u0430 \u043c\u0435\u0436\u0434\u0443 Deepseek \u0438 ChatGPT, \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u043c\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043d\u0430\u0448\u0435\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0431\u0430\u0437\u044b \u0437\u043d\u0430\u043d\u0438\u0439.<\/p>\n<p>\u0414\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c, \u044f \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043b \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 \u0432\u0438\u0434\u0435\u043e-\u043e\u0431\u0437\u043e\u0440 \u0441 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438:\u00a0<a href=\"https:\/\/www.youtube.com\/watch?v=Nb6wCNMjTl0\">\u0412\u0438\u0434\u0435\u043e-\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a><\/p>\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/6806416d488f69a8080e82b2\" data-style=\"\" id=\"6806416d488f69a8080e82b2\" width=\"\"><\/div>\n<h3>\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u044f<\/h3>\n<p>\u0414\u043b\u044f \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u044f \u0432\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f:<\/p>\n<ul>\n<li>\n<p>\u0413\u043e\u0442\u043e\u0432\u0430\u044f \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u0430\u044f \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 (\u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_chromadb_part2\">Amvera Cloud<\/a>)<\/p>\n<\/li>\n<li>\n<p>API-\u0442\u043e\u043a\u0435\u043d\u044b \u0434\u043b\u044f ChatGPT \u0438 Deepseek<\/p>\n<\/li>\n<li>\n<p>\u0411\u0430\u0437\u043e\u0432\u043e\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u043e\u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0431\u044d\u043a\u0435\u043d\u0434\u0430 \u043d\u0430 FastAPI \u0438 \u043e\u0441\u043d\u043e\u0432 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438<\/p>\n<\/li>\n<\/ul>\n<h3>\u041f\u043b\u0430\u043d \u0440\u0430\u0431\u043e\u0442\u044b<\/h3>\n<p>\u0412 \u0445\u043e\u0434\u0435 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u044b:<\/p>\n<ol>\n<li>\n<p>\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435\u043c \u0441 \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0431\u0430\u0437\u043e\u0439 Chroma (\u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u043e\u0434\u043d\u043e\u043a\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f)<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0439 Deepseek \u0438 ChatGPT \u0432 FastAPI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Langchain<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c API \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0442\u0438\u043b\u044c\u043d\u044b\u0439 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0447\u0430\u0442\u0430 \u0441\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430\u043c\u0438 \u0432\u0445\u043e\u0434\u0430 \u0438 \u0441\u0430\u043c\u043e\u0433\u043e \u0447\u0430\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u043c \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u0441 \u0431\u044d\u043a\u0435\u043d\u0434\u043e\u043c \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0433\u043e \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0438\u0441\u0430<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0434\u0435\u043f\u043b\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_chromadb_part2\">Amvera Cloud<\/a><\/p>\n<\/li>\n<\/ol>\n<p>\u041d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435, \u043f\u043e\u0441\u043b\u0435 \u0434\u0435\u043f\u043b\u043e\u044f, \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0442\u0430\u043a\u043e\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<figure class=\"full-width\"><\/figure>\n<h3>\u0412\u0430\u0436\u043d\u043e\u0435 \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u0435 \u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0435 \u043a \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c<\/h3>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0447\u0443 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044f\u0445 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0412 \u0420\u043e\u0441\u0441\u0438\u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0438, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a ChatGPT, \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0434\u0430\u0436\u0435 \u0447\u0435\u0440\u0435\u0437 API-\u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e, \u0447\u0442\u043e \u043e\u0431\u044b\u0447\u043d\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f VPN.<\/p>\n<p>\u041e\u0434\u043d\u0430\u043a\u043e \u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c Amvera Cloud \u0442\u0430\u043a\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043d\u0435 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0435\u0442 \u2014 \u0432\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u044b \u0441 ChatGPT, Claude \u0438 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c\u0438, \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c\u0438 \u0432 \u0420\u0424. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0434\u0435\u043f\u043b\u043e\u044f \u0434\u0430\u0436\u0435 \u0442\u0430\u043a\u043e\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0437\u0430\u0439\u043c\u0435\u0442 \u0432\u0441\u0435\u0433\u043e \u043f\u0430\u0440\u0443 \u043c\u0438\u043d\u0443\u0442. \u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0441 HTTPS, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u0432\u044b\u0431\u043e\u0440 \u044d\u0442\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u043c.<\/p>\n<h3>\u0422\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0441\u0442\u0435\u043a<\/h3>\n<p>\u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0432\u0430 \u044f\u0437\u044b\u043a\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f:<\/p>\n<p><strong>Python (\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430):<\/strong><\/p>\n<ul>\n<li>\n<p>langchain-openai \u2014 \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 ChatGPT<\/p>\n<\/li>\n<li>\n<p>langchain-deepseek \u2014 \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Deepseek<\/p>\n<\/li>\n<li>\n<p>FastAPI \u2014 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0431\u044d\u043a\u0435\u043d\u0434\u0430<\/p>\n<\/li>\n<li>\n<p>langchain-chroma \u2014 \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma<\/p>\n<\/li>\n<li>\n<p>Jinja2 \u2014 \u0448\u0430\u0431\u043b\u043e\u043d\u0438\u0437\u0430\u0442\u043e\u0440 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430<\/p>\n<\/li>\n<li>\n<p>PyJWT \u2014 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 JWT-\u0442\u043e\u043a\u0435\u043d\u0430\u043c\u0438<\/p>\n<\/li>\n<li>\n<p>\u0418 \u0434\u0440\u0443\u0433\u0438\u0435 Python-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b<\/p>\n<\/li>\n<\/ul>\n<p><strong>JavaScript (\u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430):<\/strong><\/p>\n<ul>\n<li>\n<p>\u0427\u0438\u0441\u0442\u044b\u0439 JavaScript \u0431\u0435\u0437 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 \u043f\u0440\u043e\u0435\u043a\u0442<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043b\u044f \u0441\u0442\u0438\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u043c \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0441\u0442\u0438\u043b\u0438 \u0438 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Bootstrap.<\/p>\n<p>\u0418\u0442\u0430\u043a, \u043d\u0430\u0447\u043d\u0435\u043c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443!<\/p>\n<h2>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h2>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f<\/h3>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0434\u043b\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0430 HTML \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Jinja2, \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u043c\u044b \u043e\u043f\u0438\u0448\u0435\u043c \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043e\u0434\u043d\u043e\u0433\u043e FastAPI \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0432 \u0432\u0430\u0448\u0435\u043c \u043b\u044e\u0431\u0438\u043c\u043e\u043c IDE.<\/p>\n<h3>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432<\/h3>\n<h4>\u0424\u0430\u0439\u043b .env<\/h4>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b\u00a0<code>.env<\/code>\u00a0\u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u043c \u0435\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438:<\/p>\n<pre><code>DEEPSEEK_API_KEY=sk-123456  # \u0442\u043e\u043a\u0435\u043d Deepseek OPENAI_API_KEY=sk-proj-S_12345  # \u0442\u043e\u043a\u0435\u043d ChatGPT SECRET_KEY=super_secret_key  # \u0441\u0435\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 JWT-\u0442\u043e\u043a\u0435\u043d\u0430 (\u0441\u0430\u043c\u0438 \u043f\u0440\u0438\u0434\u0443\u043c\u044b\u0432\u0430\u0435\u043c) ALGORITHM=HS256  # \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u043b\u044f JWT <\/code><\/pre>\n<h4>\u0424\u0430\u0439\u043b requirements.txt<\/h4>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b\u00a0<code>requirements.txt<\/code>\u00a0\u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c:<\/p>\n<pre><code>langchain-huggingface==0.1.2 torch==2.6.0 loguru==0.7.3 chromadb==0.6.3 sentence-transformers==3.4.1 langchain-chroma==0.2.2 pydantic-settings==2.8.1 fastapi==0.115.12 uvicorn==0.34.0 Jinja2==3.1.6 aiofiles==24.1.0 PyJWT==2.10.1 langchain-deepseek==0.1.3 langchain-openai==0.3.11 <\/code><\/pre>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<pre><code class=\"bash\">pip install -r requirements.txt <\/code><\/pre>\n<h3>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043f\u043a\u0443\u00a0<code>app<\/code>\u00a0\u0438 \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0435\u0435 \u0444\u0430\u0439\u043b\u00a0<a href=\"http:\/\/config.py\"><code>config.py<\/code><\/a>. \u0417\u0430\u043f\u043e\u043b\u043d\u0438\u043c \u0435\u0433\u043e:<\/p>\n<pre><code class=\"python\">import os from pydantic import SecretStr from pydantic_settings import BaseSettings, SettingsConfigDict  class Config(BaseSettings):     DEEPSEEK_API_KEY: SecretStr     BASE_DIR: str = os.path.abspath(os.path.join(os.path.dirname(__file__)))     SECRET_KEY: str     USERS: str = os.path.join(BASE_DIR, \"..\", \"users.json\")     ALGORITHM: str     AMVERA_CHROMA_PATH: str = os.path.join(BASE_DIR, \"chroma_db\")     AMVERA_COLLECTION_NAME: str = \"amvera_docs\"     LM_MODEL_NAME: str = \"sentence-transformers\/paraphrase-multilingual-MiniLM-L12-v2\"     DEEPSEEK_MODEL_NAME: str = \"deepseek-chat\"     OPENAI_MODEL_NAME: str = \"gpt-3.5-turbo\"     OPENAI_API_KEY: SecretStr      model_config = SettingsConfigDict(env_file=f\"{BASE_DIR}\/..\/.env\")  settings = Config() <\/code><\/pre>\n<blockquote>\n<p><strong>\u0412\u0430\u0436\u043d\u043e\u0435 \u043f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435:<\/strong>\u00a0\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u0443\u00a0<code>from pydantic import SecretStr<\/code>. \u0414\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 Langchain \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0439 Deepseek \u0438 ChatGPT \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u043a\u0430\u043a\u00a0<code>DEEPSEEK_API_KEY: SecretStr<\/code>. \u0412 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u043c \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0443\u0434\u043e\u0431\u043d\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0432 \u043e\u0434\u043d\u043e\u043c \u043a\u043b\u0430\u0441\u0441\u0435 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 pydantic-settings.<\/p>\n<\/blockquote>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u00a0<code>settings<\/code>\u00a0\u0438 \u0432\u0435\u0437\u0434\u0435 \u043f\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0443 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0447\u0435\u0440\u0435\u0437 \u0442\u043e\u0447\u043a\u0443:\u00a0<code>settings.OPENAI_API_KEY<\/code><\/p>\n<h3>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0439<\/h3>\n<p>\u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0432 \u043f\u0430\u043f\u043a\u0435\u00a0<code>app<\/code>\u00a0\u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438:<\/p>\n<ul>\n<li>\n<p><strong>api<\/strong>: \u043e\u043f\u0438\u0448\u0435\u043c \u0442\u0443\u0442 \u0432\u0441\u044e API-\u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/p>\n<\/li>\n<li>\n<p><strong>chroma_client<\/strong>: \u043e\u043f\u0438\u0448\u0435\u043c \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Chroma \u0438 \u043a\u043b\u0430\u0441\u0441 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c\u0438<\/p>\n<\/li>\n<li>\n<p><strong>chroma_db<\/strong>: \u0442\u0443\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0440\u0430\u043d\u0435\u0435 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma (\u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u044e, \u0447\u0442\u043e \u0431\u0430\u0437\u0430 \u0443 \u0432\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c, \u0435\u0441\u043b\u0438 \u044d\u0442\u043e \u043d\u0435 \u0442\u0430\u043a \u2014 \u043c\u043e\u0436\u0435\u0442\u0435 \u0431\u0440\u0430\u0442\u044c \u043c\u043e\u044e. \u0411\u0430\u0437\u0443 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0432 <a href=\"https:\/\/t.me\/PythonPathMaster\">\u043f\u043e\u043b\u043d\u043e\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u043c<\/a> \u043a\u043e\u0434\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430)<\/p>\n<\/li>\n<li>\n<p><strong>pages<\/strong>: \u0442\u0443\u0442 \u043e\u043f\u0438\u0448\u0435\u043c FastAPI \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b \u0434\u043b\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0430 HTML-\u0441\u0442\u0440\u0430\u043d\u0438\u0446<\/p>\n<\/li>\n<li>\n<p><strong>static<\/strong>: \u043f\u0430\u043f\u043a\u0430 \u0441\u043e \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c\u0438 \u0444\u0430\u0439\u043b\u0430\u043c\u0438 (\u0441\u0442\u0438\u043b\u0438 \u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u044b JS)<\/p>\n<\/li>\n<li>\n<p><strong>templates<\/strong>: \u043f\u0430\u043f\u043a\u0430 \u0441 HTML-\u0448\u0430\u0431\u043b\u043e\u043d\u0430\u043c\u0438<\/p>\n<\/li>\n<\/ul>\n<p>\u0418 \u0441\u0440\u0430\u0437\u0443 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432 \u043f\u0430\u043f\u043a\u0435\u00a0<code>app<\/code>\u00a0\u043f\u0443\u0441\u0442\u043e\u0439 \u0444\u0430\u0439\u043b\u00a0<a href=\"http:\/\/main.py\"><code>main.py<\/code><\/a>. \u0412 \u043d\u0435\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430, \u0438 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u043a\u043e\u0434\u0430.<\/p>\n<h2>\u041a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435\u043c \u0441 \u0431\u0430\u0437\u043e\u0439 Chroma<\/h2>\n<h3>\u041e\u0431\u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0430<\/h3>\n<p>\u041d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u044e, \u0447\u0442\u043e \u043d\u0430\u0448\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 FastAPI \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u043b\u0438 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma, \u0438 \u043f\u043e\u0441\u043b\u0435 \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u043e\u043b\u0436\u043d\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u043e\u0442\u043a\u0440\u044b\u0442\u0443\u044e \u0441\u0435\u0441\u0441\u0438\u044e.<\/p>\n<p>\u042d\u0442\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043b\u044f:<\/p>\n<ul>\n<li>\n<p>\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 \u0438\u0437 Chroma \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>\u041a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0440\u0438 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u0438 \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430\u0445<\/p>\n<\/li>\n<\/ul>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u043b\u0430\u0441\u0441\u0430 ChromaVectorStore<\/h3>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b\u00a0<code>app\/chroma_client\/chroma_<\/code><a href=\"http:\/\/store.py\"><code>store.py<\/code><\/a>\u00a0\u0438 \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u0438\u043c\u043f\u043e\u0440\u0442\u043e\u0432:<\/p>\n<pre><code class=\"python\">import torch from langchain_chroma import Chroma from langchain_huggingface import HuggingFaceEmbeddings from loguru import logger from app.config import settings <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0441\u0430\u043c \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435\u043c \u0441 \u0431\u0430\u0437\u043e\u0439:<\/p>\n<pre><code class=\"python\">class ChromaVectorStore:     def __init__(self):         \"\"\"         \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u043f\u0443\u0441\u0442\u043e\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0432\u0435\u043a\u0442\u043e\u0440\u043e\u0432.         \u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e \u043f\u043e\u0437\u0436\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 init().         \"\"\"         self._store: Chroma | None = None      async def init(self):         \"\"\"         \u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma.         \u0421\u043e\u0437\u0434\u0430\u0435\u0442 embeddings \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043c\u043e\u0434\u0435\u043b\u0438 \u0438\u0437 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f CUDA \u0435\u0441\u043b\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e.         \"\"\"         logger.info(\"\ud83e\udde0 \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f ChromaVectorStore...\")         try:             # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043b\u044f \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0439: GPU \u0435\u0441\u043b\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d, \u0438\u043d\u0430\u0447\u0435 CPU             device = \"cuda\" if torch.cuda.is_available() else \"cpu\"             logger.info(f\"\ud83d\ude80 \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043b\u044f \u044d\u043c\u0431\u0435\u0434\u0434\u0438\u043d\u0433\u043e\u0432: {device}\")              # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u044c \u044d\u043c\u0431\u0435\u0434\u0434\u0438\u043d\u0433\u043e\u0432 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438             embeddings = HuggingFaceEmbeddings(                 model_name=settings.LM_MODEL_NAME,                 model_kwargs={\"device\": device},                 encode_kwargs={\"normalize_embeddings\": True},             )              # \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma             self._store = Chroma(                 persist_directory=settings.AMVERA_CHROMA_PATH,                 embedding_function=embeddings,                 collection_name=settings.AMVERA_COLLECTION_NAME,             )              logger.success(                 f\"\u2705 ChromaVectorStore \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d \u043a \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438 \"                 f\"'{settings.AMVERA_COLLECTION_NAME}' \u0432 '{settings.AMVERA_CHROMA_PATH}'\"             )         except Exception as e:             logger.exception(f\"\u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 ChromaVectorStore: {e}\")             raise      async def asimilarity_search(self, query: str, with_score: bool, k: int = 3):         \"\"\"         \u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 Chroma.          Args:             query (str): \u0422\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430             with_score (bool): \u0412\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043b\u0438 \u043e\u0446\u0435\u043d\u043a\u0443 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u0438 \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b             k (int): \u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432          Returns:             list: \u0421\u043f\u0438\u0441\u043e\u043a \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0441 \u043e\u0446\u0435\u043d\u043a\u0430\u043c\u0438 \u0435\u0441\u043b\u0438 with_score=True          Raises:             RuntimeError: \u0415\u0441\u043b\u0438 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043d\u0435 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043e         \"\"\"         if not self._store:             raise RuntimeError(\"ChromaVectorStore is not<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-457029","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/457029","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=457029"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/457029\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=457029"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=457029"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=457029"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}