{"id":476375,"date":"2026-04-17T16:44:32","date_gmt":"2026-04-17T16:44:32","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=476375"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=476375","title":{"rendered":"PostgreSQL + VectorChord = \u0413\u0438\u0431\u0440\u0438\u0434\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a. \u0427\u0430\u0441\u0442\u044c 1. \u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442 \u0425\u0430\u0431\u0440! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440 \u0438 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u044f \u0431\u0443\u0434\u0443 \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0442\u044c \u0442\u0435\u043c\u0443 \u0444\u0438\u0448\u0435\u0447\u043a\u0438 VectorChord \u043f\u0440\u043e \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u043b \u0432 <a href=\"https:\/\/habr.com\/ru\/articles\/1014516\/\" rel=\"noopener noreferrer nofollow\">\u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/a>. <\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0435 \u044f \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043f\u043e\u0434\u043d\u044f\u0442\u044c \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0441 <code>VectorChord<\/code>, \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c <code>VechordRegistry<\/code>, \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0411\u0414, \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0433\u0438\u0431\u0440\u0438\u0434\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u0439 \u0440\u0435\u0440\u0430\u043d\u043a\u0438\u043d\u0433.<\/p>\n<p>\u041f\u043e\u0435\u0445\u0430\u043b\u0438.<\/p>\n<h2>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b<\/h2>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445. \u041d\u0430\u043f\u0438\u0448\u0435\u043c <code>docker-compose-dev.yml<\/code> \u0434\u043b\u044f \u0440\u0430\u0437\u0432\u0451\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<pre><code class=\"yaml\">services:  postgres:    image: tensorchord\/vchord-suite:pg18-latest    environment:      POSTGRES_DB: ${DB__NAME}      POSTGRES_USER: ${DB__USER}      POSTGRES_PASSWORD: ${DB__PASSWORD}    volumes:      - pgdata:\/var\/lib\/postgresql    ports:      - \"5432:5432\"    healthcheck:      test: [\"CMD-SHELL\", \"pg_isready -U ${DB__USER} -d ${DB__NAME}\"]      interval: 5s      timeout: 5s      retries: 5volumes:  pgdata:<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c. \u041f\u043e\u0434\u043d\u0438\u043c\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0443\u0432\u0438\u0434\u0438\u043c \u0434\u0435\u0432\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0447\u0438\u0441\u0442\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445:<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f59\/422\/5c8\/f594225c8c637ef32450989d4e3eb16c.png\" alt=\"\" title=\"\" width=\"1279\" height=\"356\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/f59\/422\/5c8\/f594225c8c637ef32450989d4e3eb16c.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f59\/422\/5c8\/f594225c8c637ef32450989d4e3eb16c.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u043c\u044b \u043d\u0435 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u043b\u0438 \u0432 \u043d\u0430\u0448\u0443 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439. \u042d\u0442\u043e \u043d\u0435 \u0441\u043a\u043b\u0435\u0440\u043e\u0437, \u0430 \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e, \u043f\u043e\u0437\u0436\u0435 \u043f\u043e\u043a\u0430\u0436\u0443 \u043f\u043e\u0447\u0435\u043c\u0443.<\/p>\n<p>\u041f\u0435\u0440\u0435\u0434 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0441\u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0438\u0437 <a href=\"https:\/\/github.com\/Golden-Gekko\/vector-search-service\" rel=\"noopener noreferrer nofollow\">\u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a> \u043d\u0430 <code>pgvector <\/code>\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0441 \u043e\u0434\u043d\u043e\u0439 \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 &#8212; \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u0435 <code>namespace<\/code>. \u041e\u043d\u043e \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0434\u043b\u044f \u0442\u0430\u0431\u043b\u0438\u0446:<\/p>\n<pre><code class=\"python\">class PGConfig(BaseModel):    # \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u043a\u043e\u0434    namespace: str<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u043a\u043e\u0434\u0430. \u0418 \u043f\u0435\u0440\u0432\u044b\u043c \u043d\u0430 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0443 \u043d\u0430\u0441 \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f PostgreSQL.<\/p>\n<h2>\u0422\u0430\u0431\u043b\u0438\u0446\u044b \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u0434\u0432\u0443\u0445 \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u0445 &#8212; \u043e\u0434\u043d\u0430 \u0434\u043b\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u0432\u0442\u043e\u0440\u0430\u044f &#8212; \u0434\u043b\u044f \u0447\u0430\u043d\u043a\u043e\u0432. \u0412 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0442\u0435\u043a\u0441\u0442\u044b. \u042d\u0442\u043e \u0435\u0434\u0438\u043d\u0438\u0446\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u043e\u043c. \u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u0447\u0430\u043d\u043a\u043e\u0432 &#8212; \u043e\u0441\u043d\u043e\u0432\u0430 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b (\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0432\u0435\u043b\u0438\u043a\u0438 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u0445 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0430\u0437\u0431\u0438\u0432\u0430\u0442\u044c \u043d\u0430 \u0447\u0430\u0441\u0442\u0438). \u041d\u043e \u043d\u0430\u0447\u043d\u0451\u043c \u0441 \u0442\u0440\u0435\u0442\u044c\u0435\u0439, \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b &#8212; \u0432 \u043d\u0435\u0439 \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u043b\u044f \u0434\u043b\u044f \u0444\u0438\u043a\u0441\u0430\u0446\u0438\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f\/\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0438 \u043e\u0431\u0449\u0435\u0435 \u043f\u043e\u043b\u0435 \u0441 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438.<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b db_models\/base.pyfrom datetime import datetime, timezonefrom functools import partialimport msgspecfrom psycopg.types.json import Jsonbfrom vechord import Tableclass BaseTable(Table, kw_only=True):    metadata: Jsonb    created_at: datetime = msgspec.field(        default_factory=partial(datetime.now, timezone.utc))    updated_at: datetime = msgspec.field(        default_factory=partial(datetime.now, timezone.utc))<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u0442\u0430\u0431\u043b\u0438\u0446 <code>vechord.Table<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 <code>msgspec.Struct<\/code>.<\/p>\n<blockquote>\n<p>\u041a\u0440\u0430\u0442\u043a\u0430\u044f \u0441\u043f\u0440\u0430\u0432\u043a\u0430: <code>msgspec<\/code> &#8212; \u044d\u0442\u043e \u043a\u0430\u043a <code>Pydantic<\/code>, \u0442\u043e\u043b\u044c\u043a\u043e \u0431\u0435\u0437 \u043e\u0433\u0440\u043e\u043c\u043d\u043e\u0439 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u044b, \u0437\u0430\u0442\u043e \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u0431\u044b\u0441\u0442\u0440\u0435\u0435.<\/p>\n<\/blockquote>\n<p>\u041f\u043e\u043b\u044f <code>created_at \/ updated_at<\/code> \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0437\u0430\u043f\u0438\u0441\u0438. \u0422.\u043a. \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f <code>updated_at<\/code> (\u043a\u0430\u043a \u0432 <code>sqlalchemy<\/code>) \u044f \u043d\u0435 \u043d\u0430\u0448\u0435\u043b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0432 \u043a\u043e\u0434\u0435 \u043c\u0435\u0442\u043e\u0434\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0438\u0441\u0438.<\/p>\n<p>\u041e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 <a href=\"https:\/\/tensorchord.github.io\/vechord\/guide.html\" rel=\"noopener noreferrer nofollow\">User Guide<\/a> \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c <code>DenseVector = Vector[emd_dim]<\/code>, \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u0435\u043d\u0435\u0431\u0440\u0435\u0433\u0430\u0442\u044c \u0441\u043e\u0432\u0435\u0442\u043e\u043c:<\/p>\n<pre><code class=\"python\">from core import settingsDenseVector = Vector[settings.db.embedding_dim]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0434\u043b\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432:<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b db_models\/document.pyimport msgspecfrom vechord.spec import PrimaryKeyUUIDfrom .base import BaseTableclass Document(BaseTable, kw_only=True):    uid: PrimaryKeyUUID = msgspec.field(default_factory=PrimaryKeyUUID.factory)    title: str    text: str<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u043b\u044f \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430 (\u0435\u0441\u043b\u0438 \u043d\u0435 \u0445\u043e\u0442\u0438\u043c \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u0432\u0440\u0443\u0447\u043d\u0443\u044e) \u0443 <code>vechord<\/code> \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u0430 &#8212; <code>vechord.spec.PrimaryKeyAutoIncrease<\/code> \u0438 <code>vechord.spec.PrimaryKeyUUID<\/code>. \u041f\u0435\u0440\u0432\u044b\u0439 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0430\u0432\u0442\u043e\u0438\u043d\u043a\u0440\u0435\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0435 \u043f\u043e\u043b\u0435 \u0446\u0435\u043b\u043e\u0433\u043e \u0442\u0438\u043f\u0430, \u0432\u0442\u043e\u0440\u043e\u0439 &#8212; UUID. \u041c\u043d\u0435 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u0435\u0435 UUID, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0435\u0433\u043e. \u041d\u0443 \u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430:<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b db_models\/doc_chunk.pyfrom vechord.spec import ForeignKey, Keyword, PrimaryKeyUUID, Vectorclass Chunk(BaseTable, kw_only=True):    uid: PrimaryKeyUUID = msgspec.field(default_factory=PrimaryKeyUUID.factory)    doc_id: Annotated[UUID, ForeignKey[Document.uid]]    content: str    content_tsv: Keyword    embedding: DenseVector    chunk_index: int<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u043b\u0435 <code>content_tsv<\/code> \u0441 \u0442\u0438\u043f\u043e\u043c <code>vechord.spec.Keyword<\/code> \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430, \u043f\u043e\u043b\u0435 <code>embedding<\/code> &#8212; \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0442\u0435\u043a\u0441\u0442\u0430 \u0438\u0437 \u043f\u043e\u043b\u044f <code>content<\/code>. \u0410 \u0443 \u043f\u043e\u043b\u044f \u0441 \u0442\u0438\u043f\u043e\u043c <code>ForeignKey<\/code> \u0435\u0441\u0442\u044c \u043e\u0434\u043d\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c &#8212; \u043e\u043d \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 <code>ON DELETE CASCADE<\/code>. \u041c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0443 \u043a\u043e\u0433\u043e-\u0442\u043e \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0435\u0442 \u0432\u043e\u043f\u0440\u043e\u0441 &#8212; \u201c\u0410 \u043f\u043e\u0447\u0435\u043c\u0443 \u043f\u043e\u043b\u0435 <code>uid<\/code> \u043d\u0435 \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0432 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441?\u201d. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435, \u0443 \u043c\u0435\u043d\u044f \u043d\u0435 \u0432\u044b\u0448\u043b\u043e. \u041c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u044f \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0441\u044f \u0432 <code>msgspec<\/code>, \u043d\u043e \u043a\u043e\u0433\u0434\u0430 \u044f \u0440\u0430\u0437\u043c\u0435\u0449\u0430\u043b \u043f\u043e\u043b\u0435 <code>uid<\/code> \u0432 \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435, \u0442\u043e <code>ForeignKey[Document.uid]<\/code> \u0438\u0441\u043a\u0430\u043b <code>uid<\/code> \u043d\u0435 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 <code>Document<\/code>, \u0430 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 <code>BaseTable<\/code>. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u0434\u0432\u0430 \u0440\u0430\u0437\u0430.<\/p>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u0441\u043f\u043e\u0441\u043e\u0431\u0443 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0411\u0414 \u0438 \u0442\u0430\u0431\u043b\u0438\u0446 &#8212; \u043a\u043b\u0430\u0441\u0441\u0443 <code>VechordRegistry<\/code>. \u042d\u0442\u043e \u043f\u0440\u044f\u043c \u0448\u0432\u0435\u0439\u0446\u0430\u0440\u0441\u043a\u0438\u0439 \u043d\u043e\u0436 \u0434\u043b\u044f \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445. \u0412 \u043a\u043b\u0430\u0441\u0441\u0435 \u0435\u0441\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442 \u0441 <code>AsyncConnectionPool<\/code>, \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0442\u0430\u0431\u043b\u0438\u0446, \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 CRUD, \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0440\u0430\u0437\u043d\u044b\u0445 \u0432\u0438\u0434\u043e\u0432 \u043f\u043e\u0438\u0441\u043a\u0430. \u041a\u043e\u0440\u043e\u0447\u0435, \u0432\u0441\u0451 \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0432 \u043e\u0434\u043d\u043e\u043c \u043a\u043b\u0430\u0441\u0441\u0435. \u041d\u0443 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043a\u0430\u043a \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0439 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u044b\u0439 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u0438\u043b\u0438 \u0437\u0430\u043c\u0443\u0442\u0438\u0442\u044c pipeline \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u0430 <code>@vr.inject<\/code>. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u044d\u0442\u043e\u0442 \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>__init__.py<\/code><\/p>\n<pre><code class=\"python\">from vechord import VechordRegistryfrom core import settingsfrom .document import Documentfrom .doc_chunk import Chunkvr = VechordRegistry(    namespace=settings.db.namespace,    url=settings.db.db_url,    tables=[Document, Chunk])<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442\u0441\u044f <code>namespace<\/code> (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u0432 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f\u0445 \u0442\u0430\u0431\u043b\u0438\u0446, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u043d\u0430\u0448 \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440), <code>url<\/code> \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u0442\u0430\u0431\u043b\u0438\u0446. \u041d\u0443 \u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443, \u043f\u043e\u0447\u0435\u043c\u0443 \u044f \u043d\u0435 \u0434\u0435\u043b\u0430\u043b \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440. \u0415\u0441\u043b\u0438 \u043f\u043e\u043a\u043e\u043f\u0430\u0442\u044c\u0441\u044f \u0432 \u043a\u043e\u0434\u0435 <code>VechordRegistry<\/code>, \u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0432\u043e\u0442 \u0442\u0430\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043a\u0443\u0441\u043a\u0438:<\/p>\n<pre><code class=\"python\">    async def init_extension(self):        async with (            await AsyncConnection.connect(self.url) as conn,            conn.cursor() as cursor,        ):            await cursor.execute(\"CREATE EXTENSION IF NOT EXISTS vchord CASCADE\")            await cursor.execute(\"CREATE EXTENSION IF NOT EXISTS vchord_bm25\")            await cursor.execute(\"CREATE EXTENSION IF NOT EXISTS pg_tokenizer\")            await cursor.execute(                'SET search_path TO \"$user\", public, bm25_catalog, tokenizer_catalog'            )    async def __aenter__(self):        await self.init_extension()<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u043c \u0432\u044b\u0437\u043e\u0432\u0435 <code>async with vr:<\/code> \u043e\u043d \u0441\u0430\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442 \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f. \u041d\u0443 \u0430 \u0435\u0441\u043b\u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0438\u0437\u0443\u0447\u0430\u0442\u044c \u043a\u043e\u0434, \u0442\u043e \u043d\u0430\u0439\u0434\u0451\u043c \u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0442\u0430\u0431\u043b\u0438\u0446, \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u0442\u043e\u043a\u0435\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u043e\u0432 (\u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430). \u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u043a\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439 <code>main.py<\/code> \u0444\u0430\u0439\u043b:<\/p>\n<pre><code class=\"python\">import asyncioimport sysfrom psycopg.types.json import Jsonbfrom db_models import Document, vrasync def main():    async with vr:        doc = Document(title='Note', text='Some text', metadata=Jsonb({}))        await vr.insert(doc)        docs = await vr.select_by(Document.partial_init(), limit=1)        print(docs)if __name__ == '__main__':    if sys.platform == 'win32':        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())    asyncio.run(main())<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412 \u043c\u0435\u0442\u043e\u0434\u0435 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0438 \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u043f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c. \u0414\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \/ \u0447\u0442\u0435\u043d\u0438\u044f \u0431\u0430\u0437\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b <code>VechordRegistry<\/code>. \u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0432 \u043c\u0435\u0442\u043e\u0434 <code>VechordRegistry.select_by<\/code> \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>Table.partial_init()<\/code>. \u0423\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438. \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0447\u0442\u043e \u0441 \u0441\u0430\u043c\u043e\u0439 \u0431\u0430\u0437\u043e\u0439:<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3fc\/ae4\/dde\/3fcae4ddee6c702b921765879644038f.png\" width=\"1279\" height=\"601\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/3fc\/ae4\/dde\/3fcae4ddee6c702b921765879644038f.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3fc\/ae4\/dde\/3fcae4ddee6c702b921765879644038f.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u043c, <code>VechordRegistry<\/code> \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f, \u0441\u043e\u0437\u0434\u0430\u043b \u0442\u043e\u043a\u0435\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u044b, \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u043d\u0430\u0448 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0432 \u0411\u0414. \u041f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430-\u043d\u0430\u0434\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043b\u044f <code>VechordRegistry<\/code>.<\/p>\n<h2>\u0421\u0435\u0440\u0432\u0438\u0441 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438<\/h2>\n<p>\u0425\u043e\u0442\u044c <code>VechordRegistry<\/code> \u0441\u0430\u043c \u043f\u043e \u0441\u0435\u0431\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0435\u0439 \u043d\u0430\u0434 \u0433\u043e\u043b\u043e\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u043b\u043e\u0439 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0438. \u041d\u0430 \u043d\u0451\u043c, \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0447\u0430\u043d\u043a\u043e\u0432 \u043f\u0440\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f\u0445 \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u0438 \u043c\u0435\u0442\u043e\u0434 \u0433\u0438\u0431\u0440\u0438\u0434\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430. \u041f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 \u043c\u0435\u0442\u043e\u0434\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u0437\u0430\u0442\u0435\u043c \u043d\u0430\u0440\u0435\u0437\u0430\u0442\u044c \u0435\u0433\u043e \u043d\u0430 \u0447\u0430\u043d\u043a\u0438 \u0438 \u0442\u0430\u043a\u0436\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u0437 \u0432 \u0431\u0430\u0437\u0435. \u0414\u043b\u044f \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u043e\u0439\u0434\u0451\u0442 <code>vechord.registry.VechordPipeline<\/code>. \u041a\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442: \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0439, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0445 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445. \u041a\u0430\u0436\u0434\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0434\u0435\u043a\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>@vr.inject<\/code> \u0443\u043a\u0430\u0437\u0430\u0432 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u0432\u0445\u043e\u0434\u043d\u0443\u044e (\u0434\u043b\u044f \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0411\u0414), \u0432\u044b\u0445\u043e\u0434\u043d\u0443\u044e (\u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0411\u0414) \u0438\u043b\u0438 \u043e\u0431\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b. \u0414\u0430\u043b\u0435\u0435 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u043a\u043b\u0430\u0441\u0441\u0430 <code>VechordPipeline<\/code>, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0434\u0438\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u043d\u0430\u0448\u0438\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439. \u041f\u0435\u0440\u0432\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f &#8212; \u0434\u043b\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0430 \u0432\u044b\u0445\u043e\u0434\u043d\u044b\u0445. \u041e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445. \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u0443\u0434\u0443\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u0438\u0445 \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0441\u043f\u0438\u0441\u043a\u0435, \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u044f \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438.<\/p>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430. \u0412 \u043d\u0451\u043c \u0431\u0443\u0434\u0435\u0442 \u0434\u0432\u0430 \u044d\u0442\u0430\u043f\u0430 &#8212; \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0447\u0430\u043d\u043a\u043e\u0432 \u0442\u0435\u043a\u0441\u0442\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430. \u041d\u043e \u043d\u0430\u0447\u043d\u0435\u043c \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0441 \u0432\u0445\u043e\u0434\u043d\u043e\u0439 Pydantic \u0441\u0445\u0435\u043c\u044b, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 API:<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b schemas\/document.pyfrom typing import Annotated, Anyfrom pydantic import BaseModel, Fieldclass DocumentBase(BaseModel):    title: Annotated[str, Field(..., description='\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430')]    text: Annotated[str, Field(..., description='\u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430')]    meta_data: Annotated[        dict[str, Any],        Field(default_factory=dict, description='\u041c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430')]class DocumentCreate(DocumentBase):    pass<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u0430\u043a \u0438 \u0440\u0430\u043d\u044c\u0448\u0435, \u0434\u0435\u043b\u0430\u0435\u043c \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c, \u043e\u0442 \u043d\u0435\u0451 \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0448\u0430\u0433\u043e\u0432 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430. \u0418\u0437-\u0437\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0435\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u0430 \u0438\u0445 \u043d\u0435\u043b\u044c\u0437\u044f \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u0442\u0435\u043b\u043e \u043a\u043b\u0430\u0441\u0441\u0430 &#8212; <code>self<\/code> \u0431\u0443\u0434\u0435\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044c\u0441\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u043c \u043a \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044e \u0438\u0437 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u043d\u0435 \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u043d\u0430 \u043e\u0431\u044a\u0435\u043a\u0442 \u043a\u043b\u0430\u0441\u0441\u0430. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u044b\u043d\u0435\u0441\u0435\u043c \u0448\u0430\u0433\u0438 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0444\u0430\u0439\u043b. \u041d\u0430\u0447\u043d\u0451\u043c \u0441 <code>create_doc<\/code>:<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b services\/utils.pyfrom psycopg.types.json import Jsonbfrom db_models import Document, vrfrom schemas.document import DocumentCreate@vr.inject(output=Document)async def _create_document(doc_data: DocumentCreate) -&gt; Document:    doc = Document(        title=doc_data.title,        text=doc_data.text,        metadata=Jsonb(doc_data.metadata)    )    return doc<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 <code>@vr.inject<\/code> \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c <code>output=Document<\/code> \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0432 \u0411\u0414. \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0447\u0430\u043d\u043a\u043e\u0432. \u0421 \u043d\u0438\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0441\u043b\u043e\u0436\u043d\u0435\u0435, \u0442.\u043a. \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u043d\u0430\u0440\u0435\u0437\u0430\u043d\u0438\u044f \u0442\u0435\u043a\u0441\u0442\u0430 \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u044d\u043c\u0431\u0435\u0434\u0434\u0438\u043d\u0433\u043e\u0432. \u041f\u043e\u043a\u0430 \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438:<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b services\/utils.py@vr.inject(input=Document, output=Chunk)async def create_chunks(uid: UUID, text: str) -&gt; list[Chunk]:    chunks = await _chunker.segment(text)    return [        Chunk(            doc_id=uid,            content=chunk,            content_tsv=Keyword(chunk),            embedding=DenseVector(await _embedder.vectorize_chunk(chunk)),            metadata=Jsonb({}),            chunk_index=i        )        for i, chunk in enumerate(chunks, start=1)    ]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u0430 <code>@vr.inject<\/code> \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c, \u0447\u0442\u043e \u0432\u0445\u043e\u0434 \u0443 \u043d\u0430\u0441 \u044d\u0442\u043e \u0442\u0430\u0431\u043b\u0438\u0446\u0430 <code>Document<\/code>, \u0430 \u0432\u044b\u0445\u043e\u0434 &#8212; <code>Chunk<\/code>. \u0412 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u0445 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0438\u043c\u0435\u043d\u043d\u043e \u043a\u043b\u0430\u0441\u0441 \u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u0430 \u043d\u0435 \u0447\u0442\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043c\u0435\u0442\u043e\u0434, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0431\u0435\u0437 <code>list[]<\/code>. \u0412\u043e \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0438\u043c\u0435\u043d\u0430 \u043f\u043e\u043b\u0435\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b Document \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u043e\u0442\u0438\u043c \u0432\u0438\u0434\u0435\u0442\u044c \u0432 \u043c\u0435\u0442\u043e\u0434\u0435. \u0421\u043e\u0431\u0435\u0440\u0451\u043c \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d:<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b services\/document.pyfrom db_models import vrfrom .utils import create_chunks, create_documentclass DocumentService:    async def create_document(self, doc_data: DocumentCreate):        pipeline = vr.create_pipeline([self._create_document, self._create_chunks])        await pipeline.run(doc_data)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0413\u043e\u0442\u043e\u0432\u0435\u043d\u044c\u043a\u043e. \u0412\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0442\u044c. \u041d\u043e \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c\u0441\u044f \u0441 \u0447\u0430\u043d\u043a\u0435\u0440\u043e\u043c \u0438 \u044d\u043c\u0431\u0435\u0434\u0434\u0435\u0440\u043e\u043c.<\/p>\n<p>\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0447\u0430\u043d\u043a\u043e\u0432 \u0432 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 <code>vechord.chunk<\/code> \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u0442\u0440\u0438 \u043a\u043b\u0430\u0441\u0441\u0430: <code>RegexChunker<\/code>, <code>SpacyChunker<\/code> \u0438 <code>GeminiChunker<\/code>. <code>RegexChunker<\/code> \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0437\u0431\u0438\u0432\u0430\u0435\u0442 \u043f\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u043e\u043c\u0443 \u043d\u0430\u0431\u043e\u0440\u0443 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043a\u0443, <code>GeminiChunker<\/code> \u0442\u0440\u0435\u0431\u0443\u0435\u0442 API-\u043a\u043b\u044e\u0447. \u041e\u0441\u0442\u0430\u0451\u0442\u0441\u044f <code>SpacyChunker<\/code> \u043d\u0430 \u0431\u0430\u0437\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <code>spacy<\/code>. \u0427\u0442\u043e\u0431\u044b <code>SpacyChunker<\/code> \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b, \u043d\u0430\u0434\u043e \u0434\u043e\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <code>spacy<\/code> \u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u0447\u0430\u043d\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u0414\u043b\u044f \u0440\u0443\u0441\u0441\u043a\u043e\u0433\u043e \u044f\u0437\u044b\u043a\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u0442\u0440\u0438 \u043c\u043e\u0434\u0435\u043b\u0438: <code>ru_core_news_sm<\/code>, <code>ru_core_news_md<\/code> \u0438 <code>ru_core_news_lg<\/code>. \u041e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u043c, \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c\u044e \u0438 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c\u044e \u0440\u0430\u0431\u043e\u0442\u044b. \u0412\u043e\u0437\u044c\u043c\u0435\u043c \u0441\u0440\u0435\u0434\u043d\u044e\u044e:<\/p>\n<pre><code class=\"bash\">uv add spacy==3.8.13python -m spacy download ru_core_news_md<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f, \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u0430\u0447\u0430\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u044c \u0441 <a href=\"https:\/\/github.com\/explosion\/spacy-models\/releases\/download\/ru_core_news_md-3.8.0\/ru_core_news_md-3.8.0-py3-none-any.whl\" rel=\"noopener noreferrer nofollow\">\u0433\u0438\u0442\u0445\u0430\u0431\u0430<\/a> \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e<\/p>\n<pre><code class=\"bash\">uv add &lt;\u043f\u0443\u0442\u044c\/\u043a\/\u0444\u0430\u0439\u043b\u0443&gt;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421 \u044d\u043c\u0431\u0435\u0434\u0434\u0438\u043d\u0433\u0430\u043c\u0438 \u0432\u0441\u0451 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0441\u043b\u043e\u0436\u043d\u0435\u0435. \u0412 <code>vechord.embedding<\/code> \u0435\u0441\u0442\u044c \u044d\u043c\u0431\u0435\u0434\u0434\u0435\u0440\u044b \u043e\u0442 <code>Gemini<\/code>, <code>Jina<\/code>, <code>Voyage<\/code> \u0438 \u0434\u0430\u0436\u0435 <code>OpenAI<\/code>. \u041d\u043e \u0443 \u0432\u0441\u0435\u0445 \u043e\u0434\u043d\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 &#8212; \u0438\u043c \u043d\u0443\u0436\u0435\u043d API-\u043a\u043b\u044e\u0447. \u0415\u0441\u0442\u044c \u043a\u043b\u0430\u0441\u0441 <code>SpacyDenseEmbedding<\/code>, \u043d\u043e \u0442\u0443\u0442 \u0434\u0430\u0436\u0435 \u0441\u0430\u043c\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0430\u044e\u0442, \u0447\u0442\u043e \u044d\u0442\u043e \u043d\u0435 \u0441\u0435\u0440\u044c\u0451\u0437\u043d\u043e. \u041d\u043e, \u0442.\u043a. \u0432\u0435\u0441\u044c \u044d\u0442\u043e\u0442 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b &#8212; \u0441\u043a\u043e\u0440\u0435\u0435 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442, \u0447\u0435\u043c production ready \u043f\u0440\u043e\u0434\u0443\u043a\u0442, \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0438\u043c\u0435\u043d\u043d\u043e <code>SpacyDenseEmbedding<\/code>. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b:<\/p>\n<pre><code class=\"python\">from vechord.chunk import SpacyChunkerfrom vechord.embedding import SpacyDenseEmbedding@lru_cachedef get_chunker() -&gt; SpacyChunker:    ch = SpacyChunker(model='ru_core_news_md')    return ch@lru_cachedef get_embedder() -&gt; SpacyDenseEmbedding:    emb = SpacyDenseEmbedding(model='ru_core_news_md')    return emb_chunker = get_chunker()_embedder = get_embedder()<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430. \u041f\u0435\u0440\u0435\u043f\u0438\u0448\u0435\u043c \u043d\u0430\u0448 <code>main.py<\/code>:<\/p>\n<pre><code class=\"python\">from services import DocumentServicefrom schemas.document import DocumentCreateasync def main():    async with vr:        await DocumentService.create_document(            DocumentCreate(title='\u0422\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', text=\"\"\"\u0414\u043b\u0438\u043d\u043d\u044b\u0439 \u0442\u0435\u043a\u0441\u0442 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\"\"\"))<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u043c\u043e\u0442\u0440\u0438\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/6ef\/2fa\/5c8\/6ef2fa5c8d6debf9621e2c533e0e7f84.png\" width=\"1234\" height=\"538\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/6ef\/2fa\/5c8\/6ef2fa5c8d6debf9621e2c533e0e7f84.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/6ef\/2fa\/5c8\/6ef2fa5c8d6debf9621e2c533e0e7f84.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e. \u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043b\u0441\u044f, \u043f\u043e\u0434\u0435\u043b\u0438\u043b\u0441\u044f \u043d\u0430 \u0447\u0430\u043d\u043a\u0438, \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0447\u0430\u043d\u043a\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d \u0432\u0435\u043a\u0442\u043e\u0440 \u0438 \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u0441\u043b\u043e\u0432\u0430. \u041f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u041e\u0434\u043d\u0430\u043a\u043e \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u0432\u0438\u0434\u0435 \u043e\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0438\u0448\u0435\u0442 \u0432 \u0411\u0414. \u0425\u043e\u0440\u043e\u0448\u0438\u043c \u0442\u043e\u043d\u043e\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 (\u043d\u0443 \u0438\u043b\u0438 \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u0435\u0433\u043e \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u043a\u043b\u044e\u0447). \u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c. \u0418 \u043a\u0430\u043a \u0432\u0441\u0435\u0433\u0434\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u0441\u043e \u0441\u0445\u0435\u043c\u044b \u043e\u0442\u0432\u0435\u0442\u0430:<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b schemas\/document.pyclass DocumentResponse(DocumentBase):    uid: Annotated[UUID, Field(..., description='\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430')]    created_at: Annotated[        datetime, Field(..., description='\u0414\u0430\u0442\u0430 \u0438 \u0432\u0440\u0435\u043c\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430')]    updated_at: Annotated[        datetime,        Field(..., description='\u0414\u0430\u0442\u0430 \u0438 \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430')]    model_config = ConfigDict(from_attributes=True)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u0445\u0435\u043c\u0430 \u0435\u0441\u0442\u044c. \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0444\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0448\u0430\u0433 \u0434\u043b\u044f \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f:<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b services\/utils.py@vr.inject(input=Document)async def get_document(uid: UUID) -&gt; DocumentResponse | None:    docs = await vr.select_by(Document.partial_init(uid=uid))    return DocumentResponse.model_validate(docs[0]) if docs else None<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0435 \u043f\u043e\u043d\u044f\u043b, \u0431\u0430\u0433 \u044d\u0442\u043e \u0438\u043b\u0438 \u0444\u0438\u0447\u0430, \u043d\u043e \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 \u043a\u043b\u0430\u0441\u0441\u0430 <code>Document<\/code> \u043d\u0435\u043b\u044c\u0437\u044f &#8212; <code>@vr.inject<\/code> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0447\u0438\u0442\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u043f\u043e\u043b\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u043e\u0439 \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 <code>input<\/code>. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0431\u0435\u0440\u0451\u043c <code>uid<\/code>, \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0447\u0438\u0442\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u0411\u0414 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0433\u043e (\u043b\u0438\u0431\u043e <code>None<\/code>, \u0435\u0441\u043b\u0438 \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0448\u043b\u043e \u043d\u0435 \u0442\u0430\u043a).<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435. \u0412 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0438 \u043a\u043e\u0434\u0435 \u044f\u0432\u043d\u043e\u0433\u043e update \u043c\u0435\u0442\u043e\u0434\u0430 \u044f \u043d\u0435 \u043d\u0430\u0448\u0435\u043b. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043b\u044e\u0442\u044b\u0439 \u043a\u043e\u043b\u0445\u043e\u0437. \u041f\u0438\u0448\u0435\u043c update \u043c\u043e\u0434\u0435\u043b\u044c<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b schemas\/document.pyclass DocumentUpdate(BaseModel):    title: Annotated[str | None, Field(None, description='\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430')]    text: Annotated[str | None, Field(None, description='\u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430')]    metadata: Annotated[        dict[str, Any],        Field(default_factory=dict, description='\u041c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430')]    def is_empty(self) -&gt; bool:        return all([            self.title is None,            self.text is None,            not self.metadata        ])<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u0442\u0430 \u0436\u0435 \u043c\u043e\u0434\u0435\u043b\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430, \u043d\u043e \u0432\u0441\u0435 \u043f\u043e\u043b\u044f \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b. \u041c\u0435\u0442\u043e\u0434 <code>is_empty<\/code> \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0449\u0435 \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043c\u043e\u0434\u0435\u043b\u0438. \u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0441\u0430\u043c\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>update<\/code>:<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b services\/utils.py@vr.inject(output=Document)async def update_document(uid: UUID, doc_data: DocumentUpdate) -&gt; Document:    docs = await vr.select_by(Document.partial_init(uid=uid))    if not docs:        raise ValueError(f'Document {uid} not found')    doc = docs[0]    new_doc = Document(        title=doc_data.title if doc_data.title is not None else doc.title,        text=doc_data.text if doc_data.text is not None else doc.text,        metadata=Jsonb(doc_data.metadata) if doc_data.metadata is not None else doc.metadata,        updated_at=datetime.now(timezone.utc),        created_at=doc.created_at,        uid=doc.uid,    )    await vr.remove_by(Document.partial_init(uid=uid))    return new_doc<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c ID \u0438 \u0434\u0430\u0442\u0443 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f. \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442, \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043d\u043e\u0432\u044b\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0441 \u0447\u0430\u0441\u0442\u044f\u043c\u0438 \u0441\u0442\u0430\u0440\u043e\u0433\u043e (\u0432\u0440\u0435\u043c\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438 uid) \u0438 \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0441\u0442\u0430\u0440\u044b\u0439. \u041f\u0440\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u0441\u0442\u0430\u0440\u043e\u0433\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043d\u0438\u043c \u0443\u0434\u0430\u043b\u044f\u0442\u0441\u044f \u0438 \u0435\u0433\u043e \u0447\u0430\u043d\u043a\u0438 (\u0438\u0437-\u0437\u0430 <code>ON DELETE CASCADE<\/code> \u043f\u0440\u0438 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0438 \u043f\u043e\u043b\u044f <code>vechord.spec.ForeignKey<\/code>). \u0414\u0430\u043b\u0435\u0435 \u043d\u043e\u0432\u044b\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d<\/p>\n<pre><code class=\"python\">class DocumentService:    # \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u043a\u043e\u0434    @staticmethod    async def update_document(uid: UUID, doc_data: DocumentUpdate) -&gt; DocumentResponse | None:        pipeline = vr.create_pipeline([update_document, create_chunks, get_document])        return await pipeline.run(uid=uid, doc_data=doc_data)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u043c \u043d\u0430\u0448 <code>main.py<\/code> \u0441\u0442\u0440\u043e\u0447\u043a\u043e\u0439:<\/p>\n<pre><code class=\"python\"># \u0413\u0434\u0435-\u0442\u043e \u043f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430await DocumentService.update_document(    uid=UUID(&lt;UUID \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430&gt;), doc_data=DocumentUpdate(title='\u0418\u0437\u043c\u0435\u043d\u0451\u043d\u043d\u044b\u0439 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442'))<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c:<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/270\/0ca\/02a\/2700ca02a63f1c704ea272b5e2a94631.png\" width=\"1281\" height=\"680\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/270\/0ca\/02a\/2700ca02a63f1c704ea272b5e2a94631.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/270\/0ca\/02a\/2700ca02a63f1c704ea272b5e2a94631.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 &#8212; \u0432\u0440\u0435\u043c\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0441\u0442\u0430\u0440\u043e\u0435, \u0430 \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u0447\u0430\u043d\u043a\u0438 &#8212; \u043d\u043e\u0432\u044b\u0435. \u0414\u043e\u0431\u0438\u0432\u0430\u0435\u043c \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0438\u0441 \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f\/\u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0432 \u0432\u0438\u0434\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043f\u0440\u043e\u0441\u043b\u043e\u0439\u043a\u0438 \u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u043f\u043e\u0438\u0441\u043a\u0443<\/p>\n<pre><code class=\"python\">class DocumentService:    # \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u043a\u043e\u0434    @staticmethod    async def get_document(uid: UUID) -&gt; DocumentResponse | None:        docs = await vr.select_by(Document.partial_init(uid=uid))        return DocumentResponse.model_validate(docs[0]) if docs else None    @staticmethod    async def list_documents(limit: int | None = None) -&gt; list[DocumentResponse]:        docs = await vr.select_by(Document.partial_init(), limit=limit)        return [DocumentResponse.model_validate(doc) for doc in docs]    @staticmethod    async def delete_document(uid: UUID) -&gt; None:        await vr.remove_by(Document.partial_init(uid=uid))<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h2>\u0421\u0435\u0440\u0432\u0438\u0441 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0438\u0441\u043a\u0430<\/h2>\n<p>\u041f\u043e \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u0438, \u043d\u0430\u0447\u043d\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043f\u043e\u0438\u0441\u043a\u0430 \u0441\u043e \u0441\u0445\u0435\u043c:<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b schemas\/search.pyclass SearchBase(BaseModel):    query: Annotated[str, Field(..., description='\u041f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441')]    topk : Annotated[        int, Field(10, ge=1, description='\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0434\u043b\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0430')]class VectorSearchRequest(SearchBase):    probe: Annotated[        int | None, Field(None, description='\u0421\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u043e\u0432 K-means \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c')]class KeywordSearchRequest(SearchBase):    pass<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412 \u0441\u0445\u0435\u043c\u0430\u0445 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 <code>VechordRegistry<\/code>, \u0437\u0430 \u043e\u0434\u043d\u0438\u043c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c &#8212; \u0432\u0435\u043a\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0443\u0436\u0435 \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0435, \u0442\u0435\u043c \u0436\u0435 \u044d\u043c\u0431\u0435\u0434\u0434\u0435\u0440\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u043b\u0438 \u0432\u0435\u043a\u0442\u043e\u0440\u044b \u0447\u0430\u043d\u043a\u043e\u0432. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u043b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 &#8212; \u0441\u0442\u0440\u043e\u043a\u0430.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u0445\u0435\u043c\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430:<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b schemas\/search.pyclass ChunkResponse(BaseModel):    uid: Annotated[UUID, Field(..., description='\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0447\u0430\u043d\u043a\u0430')]    doc_id: Annotated[        UUID, Field(..., description='\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430')]    content: Annotated[str, Field(..., description='\u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0447\u0430\u043d\u043a\u0430')]    created_at: Annotated[        datetime, Field(..., description='\u0414\u0430\u0442\u0430 \u0438 \u0432\u0440\u0435\u043c\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0447\u0430\u043d\u043a\u0430')]    updated_at: Annotated[        datetime,        Field(..., description='\u0414\u0430\u0442\u0430 \u0438 \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0447\u0430\u043d\u043a\u0430')]    model_config = ConfigDict(from_attributes=True)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0441\u0435\u0440\u0432\u0438\u0441 \u043f\u043e\u0438\u0441\u043a\u0430. \u041d\u0430\u0447\u043d\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0441 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043f\u043e\u0438\u0441\u043a\u0430 &#8212; \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u043e\u0433\u043e \u0438 \u043f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0433\u043e:<\/p>\n<pre><code class=\"python\"># \u0444\u0430\u0439\u043b services\/search.pyclass SearchService:    @staticmethod    async def vector_search(request: VectorSearchRequest) -&gt; list[ChunkResponse]:        vector = await _embedder.vectorize_chunk(request.query)        results = await vr.search_by_vector(            Chunk, vec=vector, topk=request.topk, probe=request.probe)        return [ChunkResponse.model_validate(r) for r in results]    @staticmethod    async def keyword_search(query: KeywordSearchRequest) -&gt; list[ChunkResponse]:        results = await vr.search_by_keyword(Chunk, keyword=query.keyword, topk=query.topk)        return [ChunkResponse.model_validate(r) for r in results]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u043b\u044f \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432\u0435\u043a\u0442\u043e\u0440 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u041d\u0443 \u0430 \u0432 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u043c, \u043c\u0435\u0442\u043e\u0434\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432 \u043c\u0435\u0442\u043e\u0434\u044b \u043a\u043b\u0430\u0441\u0441\u0430 <code>VechordRegistry<\/code>.<\/p>\n<p>\u041d\u0443 \u0438 \u0441\u0430\u043c\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 &#8212; \u0433\u0438\u0431\u0440\u0438\u0434\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a<\/p>\n<h3>\u0413\u0438\u0431\u0440\u0438\u0434\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a<\/h3>\n<p>\u0413\u0438\u0431\u0440\u0438\u0434\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0432\u044b\u0441\u043e\u043a\u0443\u044e \u043f\u043e\u043b\u043d\u043e\u0442\u0443 \u0432\u044b\u0434\u0430\u0447\u0438 (recall), \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u044f \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0438 \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0439 \u043f\u043e\u0438\u0441\u043a. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u043a\u0430\u043a \u043f\u043e \u0441\u043c\u044b\u0441\u043b\u0443, \u0442\u0430\u043a \u0438 \u043f\u043e \u0442\u043e\u0447\u043d\u044b\u043c \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u044f\u043c \u0441\u043b\u043e\u0432, \u043a\u043e\u043c\u043f\u0435\u043d\u0441\u0438\u0440\u0443\u044f \u0441\u043b\u0430\u0431\u044b\u0435 \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438. \u041e\u0434\u043d\u0430\u043a\u043e, \u043c\u0435\u0442\u043e\u0434\u044b \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u044d\u0442\u0430\u043f\u0430 \u0447\u0430\u0441\u0442\u043e \u0436\u0435\u0440\u0442\u0432\u0443\u044e\u0442 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c\u044e \u0440\u0430\u0434\u0438 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438. \u0417\u0434\u0435\u0441\u044c \u043d\u0430 \u043f\u043e\u043c\u043e\u0449\u044c \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u0440\u0435\u0440\u0430\u043d\u043a\u0438\u043d\u0433: \u0444\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043e\u0442\u0431\u043e\u0440 \u043b\u0443\u0447\u0448\u0438\u0445. \u041c\u043e\u0434\u0435\u043b\u044c \u043e\u0446\u0435\u043d\u0438\u0432\u0430\u0435\u0442 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430, \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u044f\u044f \u043e\u0448\u0438\u0431\u043a\u0438 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u044d\u0442\u0430\u043f\u0430. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0441\u043e\u0447\u0435\u0442\u0430\u0435\u0442 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u043c \u0434\u0430\u043d\u043d\u044b\u043c \u0441 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u0441\u043c\u044b\u0441\u043b\u0430, \u0432\u044b\u0432\u043e\u0434\u044f \u043d\u0430 \u043f\u0435\u0440\u0432\u044b\u0435 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u043e, \u0447\u0442\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e.<\/p>\n<p>\u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <code>vechord<\/code> \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u0432\u043e\u0438 \u0440\u0435\u0440\u0430\u043d\u043a\u0435\u0440\u044b. \u041d\u043e, \u043a\u0430\u043a \u0438 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 \u044d\u043c\u0431\u0435\u0434\u0434\u0435\u0440\u0430\u043c\u0438, <code>CohereReranker<\/code> \u0438 <code>JinaReranker<\/code> \u0442\u0440\u0435\u0431\u0443\u044e\u0442 API \u043a\u043b\u044e\u0447\u0438. \u041e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u0439 <code>ReciprocalRankFusion<\/code>, \u043d\u0430\u0437\u0432\u0430\u043d\u044b\u0439 \u0442\u0430\u043a \u0432 \u0447\u0435\u0441\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0433\u043e \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0430. \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u043a\u0430\u043a \u201c\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0445 \u0440\u0430\u043d\u0433\u043e\u0432\u201d, \u0438 \u043e\u043d\u043e \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c &#8212; \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0440\u0430\u043d\u0433 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043f\u0443\u0442\u0451\u043c \u0441\u0443\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0445 \u0432\u0435\u043b\u0438\u0447\u0438\u043d \u0435\u0433\u043e \u043f\u043e\u0437\u0438\u0446\u0438\u0439 \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u0445 \u043f\u043e\u0438\u0441\u043a\u0430 \u0441 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b \u0434\u043b\u044f \u0441\u0433\u043b\u0430\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0432\u043b\u0438\u044f\u043d\u0438\u044f \u043a\u0440\u0430\u0439\u043d\u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439. \u0412 \u0438\u0442\u043e\u0433\u0435, \u0447\u0435\u043c \u0432\u044b\u0448\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u043f\u0438\u0441\u043a\u0430\u0445 \u0438 \u0447\u0435\u043c \u0447\u0430\u0449\u0435 \u043e\u043d \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0432\u0435\u0440\u0445\u043d\u0438\u0445 \u043f\u043e\u0437\u0438\u0446\u0438\u044f\u0445, \u0442\u0435\u043c \u0432\u044b\u0448\u0435 \u0435\u0433\u043e \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0440\u0430\u043d\u0433:<\/p>\n<p><img decoding=\"async\" class=\"formula\" source=\"\\text{score}(d) = \\sum_{i=1}^{n} \\frac{1}{k + \\text{rank}_i(d)} \" alt=\"\\text{score}(d) = \\sum_{i=1}^{n} \\frac{1}{k + \\text{rank}_i(d)} \" src=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/7\/71\/711\/71118ef9d1ab2da4cb4bb3aafc2bf33b.svg\" width=\"216\" height=\"48\" data-width=\"27.603\" data-height=\"6.354\" data-vertical-align=\"-2.611\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/7\/71\/711\/71118ef9d1ab2da4cb4bb3aafc2bf33b.svg 780w,&#10;       https:\/\/habrastorage.org\/getpro\/habr\/formulas\/7\/71\/711\/71118ef9d1ab2da4cb4bb3aafc2bf33b.svg 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<p>\u0433\u0434\u0435 <img decoding=\"async\" class=\"formula inline\" source=\"\\text{score}(d)\" alt=\"\\text{score}(d)\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/4\/44\/445\/4451bdcca75087dcd71e81448085d183.svg\" width=\"56\" height=\"16\" data-width=\"7.855\" data-height=\"2.262\" data-vertical-align=\"-0.566\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/4\/44\/445\/4451bdcca75087dcd71e81448085d183.svg 780w,&#10;       https:\/\/habrastorage.org\/getpro\/habr\/formulas\/4\/44\/445\/4451bdcca75087dcd71e81448085d183.svg 781w\" loading=\"lazy\" decode=\"async\"\/> &#8212; \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0431\u0430\u043b\u043b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 <img decoding=\"async\" class=\"formula inline\" source=\"d\" alt=\"d\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/8\/82\/827\/8277e0910d750195b448797616e091ad.svg\" width=\"12\" height=\"12\" data-width=\"1.176\" data-height=\"1.593\" data-vertical-align=\"-0.023\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/8\/82\/827\/8277e0910d750195b448797616e091ad.svg 780w,&#10;       https:\/\/habrastorage.org\/getpro\/habr\/formulas\/8\/82\/827\/8277e0910d750195b448797616e091ad.svg 781w\" loading=\"lazy\" decode=\"async\"\/>, <img decoding=\"async\" class=\"formula inline\" source=\"n\" alt=\"n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/7\/7b\/7b8\/7b8b965ad4bca0e41ab51de7b31363a1.svg\" width=\"12\" height=\"12\" data-width=\"1.357\" data-height=\"1.025\" data-vertical-align=\"-0.025\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/7\/7b\/7b8\/7b8b965ad4bca0e41ab51de7b31363a1.svg 780w,&#10;       https:\/\/habrastorage.org\/getpro\/habr\/formulas\/7\/7b\/7b8\/7b8b965ad4bca0e41ab51de7b31363a1.svg 781w\" loading=\"lazy\" decode=\"async\"\/> &#8212; \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u043f\u0438\u0441\u043a\u043e\u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432, <img decoding=\"async\" class=\"formula inline\" source=\"\\text{rank}_i(d)\" alt=\"\\text{rank}_i(d)\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/4\/4f\/4f4\/4f47adba187eac8aeb26dca9b6f69b6a.svg\" width=\"64\" height=\"16\" data-width=\"8.147\" data-height=\"2.262\" data-vertical-align=\"-0.566\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/4\/4f\/4f4\/4f47adba187eac8aeb26dca9b6f69b6a.svg 780w,&#10;       https:\/\/habrastorage.org\/getpro\/habr\/formulas\/4\/4f\/4f4\/4f47adba187eac8aeb26dca9b6f69b6a.svg 781w\" loading=\"lazy\" decode=\"async\"\/> &#8212; \u043f\u043e\u0437\u0438\u0446\u0438\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 <img decoding=\"async\" class=\"formula inline\" source=\"d\" alt=\"d\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/8\/82\/827\/8277e0910d750195b448797616e091ad.svg\" width=\"12\" height=\"12\" data-width=\"1.176\" data-height=\"1.593\" data-vertical-align=\"-0.023\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/8\/82\/827\/8277e0910d750195b448797616e091ad.svg 780w,&#10;       https:\/\/habrastorage.org\/getpro\/habr\/formulas\/8\/82\/827\/8277e0910d750195b448797616e091ad.svg 781w\" loading=\"lazy\" decode=\"async\"\/> \u0432 <img decoding=\"async\" class=\"formula inline\" source=\"i\" alt=\"i\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/8\/86\/865\/865c0c0b4ab0e063e5caa3387c1a8741.svg\" width=\"12\" height=\"12\" data-width=\"0.781\" data-height=\"1.52\" data-vertical-align=\"-0.025\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/8\/86\/865\/865c0c0b4ab0e063e5caa3387c1a8741.svg 780w,&#10;       https:\/\/habrastorage.org\/getpro\/habr\/formulas\/8\/86\/865\/865c0c0b4ab0e063e5caa3387c1a8741.svg 781w\" loading=\"lazy\" decode=\"async\"\/>-\u043c \u0441\u043f\u0438\u0441\u043a\u0435, <img decoding=\"async\" class=\"formula inline\" source=\"k\" alt=\"k\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/8\/8c\/8ce\/8ce4b16b22b58894aa86c421e8759df3.svg\" width=\"12\" height=\"12\" data-width=\"1.179\" data-height=\"1.595\" data-vertical-align=\"-0.025\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/getpro\/habr\/formulas\/8\/8c\/8ce\/8ce4b16b22b58894aa86c421e8759df3.svg 780w,&#10;       https:\/\/habrastorage.org\/getpro\/habr\/formulas\/8\/8c\/8ce\/8ce4b16b22b58894aa86c421e8759df3.svg 781w\" loading=\"lazy\" decode=\"async\"\/> &#8212; \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u0430 (\u043e\u0431\u044b\u0447\u043d\u043e 60).<\/p>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043e\u0442 \u0442\u0435\u043e\u0440\u0438\u0438 \u043a \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/p>\n<pre><code class=\"python\">class HybridSearchRequest(VectorSearchRequest, KeywordSearchRequest):    boost: Annotated[        int, Field(3, ge=1, description='\u041a\u043e\u044d\u0444\u0444\u0438\u0446\u0438\u0435\u043d\u0442 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0432\u044b\u0431\u043e\u0440\u043a\u0438')]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <code>boost<\/code> \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u043f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u044f \u0438\u0442\u043e\u0433\u043e\u0432\u043e\u0439 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u0438, \u0442.\u043a. \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0440\u0435\u0440\u0430\u043d\u043a\u0435\u0440\u0443 \u0440\u043e\u0432\u043d\u043e <code>topk<\/code> \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0442\u043e \u0435\u043c\u0443 \u043d\u0435 \u0438\u0437 \u0447\u0435\u0433\u043e \u0432\u044b\u0431\u0438\u0440\u0430\u0442\u044c &#8212; \u043e\u043d \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u0438\u0442 \u043c\u0435\u0441\u0442\u0430\u043c\u0438 \u0443\u0436\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b, \u043d\u0435 \u0443\u043b\u0443\u0447\u0448\u0438\u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u044b\u0431\u043e\u0440\u043a\u0438. \u0414\u0430\u043b\u0435\u0435 \u0441\u0430\u043c \u043c\u0435\u0442\u043e\u0434<\/p>\n<pre><code class=\"python\">class SearchService:    # \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u043a\u043e\u0434    @staticmethod    async def hybrid_search_fuse(request: HybridSearchRequest) -&gt; list[ChunkResponse]:        rrf = ReciprocalRankFusion()        vector = await _embedder.vectorize_chunk(request.query)        return rrf.fuse(            [                await vr.search_by_vector(                    Chunk, vec=vector, topk=request.topk * request.boost, probe=request.probe),                await vr.search_by_keyword(Chunk, keyword=request.keyword, topk=request.topk * request.boost)            ]        )[:request.topk]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421 \u043f\u043e\u0438\u0441\u043a\u043e\u043c \u043a\u0430\u043a-\u0431\u044b \u0432\u0441\u0451.<\/p>\n<h2>API<\/h2>\n<p>API \u0440\u0430\u0441\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043d\u0435 \u0431\u0443\u0434\u0443, \u0442.\u043a. \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0435\u0433\u043e \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0438\u0437 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f (\u043a\u043e\u0442\u043e\u0440\u044b\u0439 git) \u0434\u043b\u044f <a href=\"https:\/\/github.com\/Golden-Gekko\/vector-search-service\" rel=\"noopener noreferrer nofollow\">\u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438<\/a> \u0438 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c \u0432\u044b\u0437\u043e\u0432 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f (\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0430\u0442\u0442\u0435\u0440\u043d) \u043d\u0430 \u0432\u044b\u0437\u043e\u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u0430. \u041e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u0441\u0442\u043e\u0438\u0442, \u0447\u0442\u043e \u0438\u0437-\u0437\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 <code>VechordPipeline<\/code> \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043c\u0435\u0442\u043e\u0434 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432 API \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c:<\/p>\n<pre><code class=\"python\">async def create_document(data: DocumentCreate):    res = await DocumentService.create_document(data)    return res[0]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0441\u0442\u0430\u043b\u0441\u044f <code>main.py<\/code>. \u041e\u043d \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0434\u043b\u044f FastAPI \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043f\u0440\u043e\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u044e \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u0435\u0442\u043e\u0434 <code>lifespan<\/code>:<\/p>\n<pre><code class=\"python\">@asynccontextmanagerasync def lifespan(app: FastAPI):    async with vr:        yield<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412 <code>lifespan<\/code> \u043c\u044b \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043f\u0443\u043b \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u043d\u0430\u0448\u0435\u0433\u043e <code>VechordRegistry<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u043f\u043e\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<h2>\u0412\u043d\u0435\u0437\u0430\u043f\u043d\u043e\u0435 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u0435<\/h2>\n<p>\u0427\u0442\u043e \u0438\u043c\u0435\u0435\u043c \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442:<\/p>\n<ul>\n<li>\n<p>\u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 PostgreSQL \u0447\u0435\u0440\u0435\u0437 <code>VechordRegistry<\/code><\/p>\n<\/li>\n<li>\n<p>\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0440\u0430\u0437\u0431\u0438\u0435\u043d\u0438\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043d\u0430 \u0447\u0430\u043d\u043a\u0438 (<code>SpacyChunker<\/code>) \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u044d\u043c\u0431\u0435\u0434\u0434\u0438\u043d\u0433\u043e\u0432 (<code>SpacyDenseEmbedding<\/code>)<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439, \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u044b\u0439 \u0438 \u0433\u0438\u0431\u0440\u0438\u0434\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a \u0441 \u0440\u0435\u0440\u0430\u043d\u043a\u0438\u043d\u0433\u043e\u043c (<code>ReciprocalRankFusion<\/code>)<\/p>\n<\/li>\n<li>\n<p>\u0431\u0430\u0437\u043e\u0432\u044b\u0439 API \u0434\u043b\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0441 \u043a\u0430\u0441\u043a\u0430\u0434\u043d\u044b\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u0447\u0430\u043d\u043a\u043e\u0432<\/p>\n<\/li>\n<\/ul>\n<p>\u0412 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u044d\u043c\u0431\u0435\u0434\u0434\u0435\u0440 \u0438 \u0447\u0430\u043d\u043a\u0435\u0440 \u2014 \u0438\u0433\u0440\u0443\u0448\u0435\u0447\u043d\u044b\u0435 (\u0438\u0437 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <code>spacy<\/code>). \u0414\u043b\u044f \u0431\u043e\u0435\u0432\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u0442\u043e\u0438\u0442 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0438\u0445 \u043d\u0430 \u0431\u043e\u043b\u0435\u0435 \u0441\u0435\u0440\u044c\u0451\u0437\u043d\u044b\u0435 \u043c\u043e\u0434\u0435\u043b\u0438. \u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u043a\u0430\u043a \u0440\u0430\u0437 \u044d\u0442\u0438\u043c \u0438 \u0437\u0430\u0439\u043c\u0435\u043c\u0441\u044f &#8212; \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b.<\/p>\n<p>\u041a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d <a href=\"https:\/\/github.com\/Golden-Gekko\/VCordHybridSearch\" rel=\"noopener noreferrer nofollow\"><strong>\u0442\u0443\u0442<\/strong><\/a>.<\/p>\n<\/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\/1024810\/\">https:\/\/habr.com\/ru\/articles\/1024810\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u041f\u0440\u0438\u0432\u0435\u0442 \u0425\u0430\u0431\u0440! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440 \u0438 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u044f \u0431\u0443\u0434\u0443 \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0442\u044c \u0442\u0435\u043c\u0443 \u0444\u0438\u0448\u0435\u0447\u043a\u0438 VectorChord \u043f\u0440\u043e \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u043b \u0432 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0435. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0435 \u044f \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043f\u043e\u0434\u043d\u044f\u0442\u044c \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0441 VectorChord, \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c VechordRegistry, \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0411\u0414, \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0433\u0438\u0431\u0440\u0438\u0434\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u0439 \u0440\u0435\u0440\u0430\u043d\u043a\u0438\u043d\u0433.\u041f\u043e\u0435\u0445\u0430\u043b\u0438.\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445. \u041d\u0430\u043f\u0438\u0448\u0435\u043c docker-compose-dev.yml \u0434\u043b\u044f \u0440\u0430\u0437\u0432\u0451\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445services:  postgres:    image: tensorchord\/vchord-suite:pg18-latest    environment:      POSTGRES_DB: ${DB__NAME}      POSTGRES_USER: ${DB__USER}      POSTGRES_PASSWORD: ${DB__PASSWORD}    volumes:      &#8212; pgdata:\/var\/lib\/postgresql    ports:      &#8212; &#171;5432:5432&#187;    healthcheck:      test: [&#171;CMD-SHELL&#187;, &#171;pg_isready -U ${DB__USER} -d ${DB__NAME}&#187;]      interval: 5s      timeout: 5s      retries: 5volumes:  pgdata:\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c. \u041f\u043e\u0434\u043d\u0438\u043c\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0443\u0432\u0438\u0434\u0438\u043c \u0434\u0435\u0432\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0447\u0438\u0441\u0442\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445:\u041a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u043c\u044b \u043d\u0435 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u043b\u0438 \u0432 \u043d\u0430\u0448\u0443 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439. \u042d\u0442\u043e \u043d\u0435 \u0441\u043a\u043b\u0435\u0440\u043e\u0437, \u0430 \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e, \u043f\u043e\u0437\u0436\u0435 \u043f\u043e\u043a\u0430\u0436\u0443 \u043f\u043e\u0447\u0435\u043c\u0443.\u041f\u0435\u0440\u0435\u0434 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0441\u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0438\u0437 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 pgvector \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0441 \u043e\u0434\u043d\u043e\u0439 \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 &#8212; \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u0435 namespace. \u041e\u043d\u043e \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0434\u043b\u044f \u0442\u0430\u0431\u043b\u0438\u0446:class PGConfig(BaseModel):    # \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u043a\u043e\u0434    namespace: str\u041c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u043a\u043e\u0434\u0430. \u0418 \u043f\u0435\u0440\u0432\u044b\u043c \u043d\u0430 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0443 \u043d\u0430\u0441 \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f PostgreSQL.\u0422\u0430\u0431\u043b\u0438\u0446\u044b \u0434\u0430\u043d\u043d\u044b\u0445\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u0434\u0432\u0443\u0445 \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u0445 &#8212; \u043e\u0434\u043d\u0430 \u0434\u043b\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u0432\u0442\u043e\u0440\u0430\u044f &#8212; \u0434\u043b\u044f \u0447\u0430\u043d\u043a\u043e\u0432. \u0412 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0442\u0435\u043a\u0441\u0442\u044b. \u042d\u0442\u043e \u0435\u0434\u0438\u043d\u0438\u0446\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u043e\u043c. \u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u0447\u0430\u043d\u043a\u043e\u0432 &#8212; \u043e\u0441\u043d\u043e\u0432\u0430 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b (\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0432\u0435\u043b\u0438\u043a\u0438 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u0445 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0430\u0437\u0431\u0438\u0432\u0430\u0442\u044c \u043d\u0430 \u0447\u0430\u0441\u0442\u0438). \u041d\u043e \u043d\u0430\u0447\u043d\u0451\u043c \u0441 \u0442\u0440\u0435\u0442\u044c\u0435\u0439, \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b &#8212; \u0432 \u043d\u0435\u0439 \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u043b\u044f \u0434\u043b\u044f \u0444\u0438\u043a\u0441\u0430\u0446\u0438\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f\/\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0438 \u043e\u0431\u0449\u0435\u0435 \u043f\u043e\u043b\u0435 \u0441 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438.# \u0444\u0430\u0439\u043b db_models\/base.pyfrom datetime import datetime, timezonefrom functools import partialimport msgspecfrom psycopg.types.json import Jsonbfrom vechord import Tableclass BaseTable(Table, kw_only=True):    metadata: Jsonb    created_at: datetime = msgspec.field(        default_factory=partial(datetime.now, timezone.utc))    updated_at: datetime = msgspec.field(        default_factory=partial(datetime.now, timezone.utc))\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u0442\u0430\u0431\u043b\u0438\u0446 vechord.Table, \u043a\u043e\u0442\u043e\u0440\u0430\u044f, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 msgspec.Struct.\u041a\u0440\u0430\u0442\u043a\u0430\u044f \u0441\u043f\u0440\u0430\u0432\u043a\u0430: msgspec &#8212; \u044d\u0442\u043e \u043a\u0430\u043a Pydantic, \u0442\u043e\u043b\u044c\u043a\u043e \u0431\u0435\u0437 \u043e\u0433\u0440\u043e\u043c\u043d\u043e\u0439 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u044b, \u0437\u0430\u0442\u043e \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u0431\u044b\u0441\u0442\u0440\u0435\u0435.\u041f\u043e\u043b\u044f created_at \/ updated_at \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0437\u0430\u043f\u0438\u0441\u0438. \u0422.\u043a. \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f updated_at (\u043a\u0430\u043a \u0432 sqlalchemy) \u044f \u043d\u0435 \u043d\u0430\u0448\u0435\u043b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0432 \u043a\u043e\u0434\u0435 \u043c\u0435\u0442\u043e\u0434\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0438\u0441\u0438.\u041e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 User Guide \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c DenseVector = Vector[emd_dim], \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u0435\u043d\u0435\u0431\u0440\u0435\u0433\u0430\u0442\u044c \u0441\u043e\u0432\u0435\u0442\u043e\u043c:from core import settingsDenseVector = Vector[settings.db.embedding_dim]\u0422\u0435\u043f\u0435\u0440\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0434\u043b\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432:# \u0444\u0430\u0439\u043b db_models\/document.pyimport msgspecfrom vechord.spec import PrimaryKeyUUIDfrom .base import BaseTableclass Document(BaseTable, kw_only=True):    uid: PrimaryKeyUUID = msgspec.field(default_factory=PrimaryKeyUUID.factory)    title: str    text: str\u0414\u043b\u044f \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430 (\u0435\u0441\u043b\u0438 \u043d\u0435 \u0445\u043e\u0442\u0438\u043c \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u0432\u0440\u0443\u0447\u043d\u0443\u044e) \u0443 vechord \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u0430 &#8212; vechord.spec.PrimaryKeyAutoIncrease \u0438 vechord.spec.PrimaryKeyUUID. \u041f\u0435\u0440\u0432\u044b\u0439 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0430\u0432\u0442\u043e\u0438\u043d\u043a\u0440\u0435\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0435 \u043f\u043e\u043b\u0435 \u0446\u0435\u043b\u043e\u0433\u043e \u0442\u0438\u043f\u0430, \u0432\u0442\u043e\u0440\u043e\u0439 &#8212; UUID. \u041c\u043d\u0435 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u0435\u0435 UUID, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0435\u0433\u043e. \u041d\u0443 \u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430:# \u0444\u0430\u0439\u043b db_models\/doc_chunk.pyfrom vechord.spec import ForeignKey, Keyword, PrimaryKeyUUID, Vectorclass Chunk(BaseTable, kw_only=True):    uid: PrimaryKeyUUID = msgspec.field(default_factory=PrimaryKeyUUID.factory)    doc_id: Annotated[UUID, ForeignKey[Document.uid]]    content: str    content_tsv: Keyword    embedding: DenseVector    chunk_index: int\u041f\u043e\u043b\u0435 content_tsv \u0441 \u0442\u0438\u043f\u043e\u043c vechord.spec.Keyword \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430, \u043f\u043e\u043b\u0435 embedding &#8212; \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0442\u0435\u043a\u0441\u0442\u0430 \u0438\u0437 \u043f\u043e\u043b\u044f content. \u0410 \u0443 \u043f\u043e\u043b\u044f \u0441 \u0442\u0438\u043f\u043e\u043c ForeignKey \u0435\u0441\u0442\u044c \u043e\u0434\u043d\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c &#8212; \u043e\u043d \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 ON DELETE CASCADE. \u041c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0443 \u043a\u043e\u0433\u043e-\u0442\u043e \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0435\u0442 \u0432\u043e\u043f\u0440\u043e\u0441 &#8212; \u201c\u0410 \u043f\u043e\u0447\u0435\u043c\u0443 \u043f\u043e\u043b\u0435 uid \u043d\u0435 \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0432 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441?\u201d. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435, \u0443 \u043c\u0435\u043d\u044f \u043d\u0435 \u0432\u044b\u0448\u043b\u043e. \u041c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u044f \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0441\u044f \u0432 msgspec, \u043d\u043e \u043a\u043e\u0433\u0434\u0430 \u044f \u0440\u0430\u0437\u043c\u0435\u0449\u0430\u043b \u043f\u043e\u043b\u0435 uid \u0432 \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435, \u0442\u043e ForeignKey[Document.uid] \u0438\u0441\u043a\u0430\u043b uid \u043d\u0435 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 Document, \u0430 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 BaseTable. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u0434\u0432\u0430 \u0440\u0430\u0437\u0430.\u041f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u0441\u043f\u043e\u0441\u043e\u0431\u0443 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0411\u0414 \u0438 \u0442\u0430\u0431\u043b\u0438\u0446 &#8212; \u043a\u043b\u0430\u0441\u0441\u0443 VechordRegistry. \u042d\u0442\u043e \u043f\u0440\u044f\u043c \u0448\u0432\u0435\u0439\u0446\u0430\u0440\u0441\u043a\u0438\u0439 \u043d\u043e\u0436 \u0434\u043b\u044f \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445. \u0412 \u043a\u043b\u0430\u0441\u0441\u0435 \u0435\u0441\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442 \u0441 AsyncConnectionPool, \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0442\u0430\u0431\u043b\u0438\u0446, \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 CRUD, \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0440\u0430\u0437\u043d\u044b\u0445 \u0432\u0438\u0434\u043e\u0432 \u043f\u043e\u0438\u0441\u043a\u0430. \u041a\u043e\u0440\u043e\u0447\u0435, \u0432\u0441\u0451 \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0432 \u043e\u0434\u043d\u043e\u043c \u043a\u043b\u0430\u0441\u0441\u0435. \u041d\u0443 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043a\u0430\u043a \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0439 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u044b\u0439 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u0438\u043b\u0438 \u0437\u0430\u043c\u0443\u0442\u0438\u0442\u044c pipeline \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u0430 @vr.inject. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u044d\u0442\u043e\u0442 \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0432 \u0444\u0430\u0439\u043b\u0435 __init__.pyfrom vechord import VechordRegistryfrom core import settingsfrom .document import Documentfrom .doc_chunk import Chunkvr = VechordRegistry(    namespace=settings.db.namespace,    url=settings.db.db_url,    tables=[Document, Chunk])\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442\u0441\u044f namespace (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u0432 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f\u0445 \u0442\u0430\u0431\u043b\u0438\u0446, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u043d\u0430\u0448 \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440), url \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u0442\u0430\u0431\u043b\u0438\u0446. \u041d\u0443 \u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443, \u043f\u043e\u0447\u0435\u043c\u0443 \u044f \u043d\u0435 \u0434\u0435\u043b\u0430\u043b \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440. \u0415\u0441\u043b\u0438 \u043f\u043e\u043a\u043e\u043f\u0430\u0442\u044c\u0441\u044f \u0432 \u043a\u043e\u0434\u0435 VechordRegistry, \u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0432\u043e\u0442 \u0442\u0430\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043a\u0443\u0441\u043a\u0438:    async def init_extension(self):        async with (            await AsyncConnection.connect(self.url) as conn,            conn.cursor() as cursor,        ):            await cursor.execute(&#171;CREATE EXTENSION IF NOT EXISTS vchord CASCADE&#187;)            await cursor.execute(&#171;CREATE EXTENSION IF NOT EXISTS vchord_bm25&#187;)            await cursor.execute(&#171;CREATE EXTENSION IF NOT EXISTS pg_tokenizer&#187;)            await cursor.execute(                &#8216;SET search_path TO &#171;$user&#187;, public, bm25_catalog, tokenizer_catalog&#8217;            )    async def __aenter__(self):        await self.init_extension()\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u043c \u0432\u044b\u0437\u043e\u0432\u0435 async with vr: \u043e\u043d \u0441\u0430\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442 \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f. \u041d\u0443 \u0430 \u0435\u0441\u043b\u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0438\u0437\u0443\u0447\u0430\u0442\u044c \u043a\u043e\u0434, \u0442\u043e \u043d\u0430\u0439\u0434\u0451\u043c \u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0442\u0430\u0431\u043b\u0438\u0446, \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u0442\u043e\u043a\u0435\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u043e\u0432 (\u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430). \u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u043a\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439 main.py \u0444\u0430\u0439\u043b:import asyncioimport sysfrom psycopg.types.json import Jsonbfrom db_models import Document, vrasync def main():    async with vr:        doc = Document(title=&#8217;Note&#8217;, text=&#8217;Some text&#8217;, metadata=Jsonb({}))        await vr.insert(doc)        docs = await vr.select_by(Document.partial_init(), limit=1)        print(docs)if __name__ == &#8216;__main__&#8217;:    if sys.platform == &#8216;win32&#8242;:        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())    asyncio.run(main())\u0412 \u043c\u0435\u0442\u043e\u0434\u0435 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0438 \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u043f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c. \u0414\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \/ \u0447\u0442\u0435\u043d\u0438\u044f \u0431\u0430\u0437\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b VechordRegistry. \u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0432 \u043c\u0435\u0442\u043e\u0434 VechordRegistry.select_by \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Table.partial_init(). \u0423\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438. \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0447\u0442\u043e \u0441 \u0441\u0430\u043c\u043e\u0439 \u0431\u0430\u0437\u043e\u0439:\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u043c, VechordRegistry \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f, \u0441\u043e\u0437\u0434\u0430\u043b \u0442\u043e\u043a\u0435\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u044b, \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u043d\u0430\u0448 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0432 \u0411\u0414. \u041f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430-\u043d\u0430\u0434\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043b\u044f VechordRegistry.\u0421\u0435\u0440\u0432\u0438\u0441 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438\u0425\u043e\u0442\u044c VechordRegistry \u0441\u0430\u043c \u043f\u043e \u0441\u0435\u0431\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0435\u0439 \u043d\u0430\u0434 \u0433\u043e\u043b\u043e\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u043b\u043e\u0439 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0438. \u041d\u0430 \u043d\u0451\u043c, \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0447\u0430\u043d\u043a\u043e\u0432 \u043f\u0440\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f\u0445 \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u0438 \u043c\u0435\u0442\u043e\u0434 \u0433\u0438\u0431\u0440\u0438\u0434\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430.\u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430. \u041f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 \u043c\u0435\u0442\u043e\u0434\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u0437\u0430\u0442\u0435\u043c \u043d\u0430\u0440\u0435\u0437\u0430\u0442\u044c \u0435\u0433\u043e \u043d\u0430 \u0447\u0430\u043d\u043a\u0438 \u0438 \u0442\u0430\u043a\u0436\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u0437 \u0432 \u0431\u0430\u0437\u0435. \u0414\u043b\u044f \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u043e\u0439\u0434\u0451\u0442 vechord.registry.VechordPipeline. \u041a\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442: \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0439, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0445 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445. \u041a\u0430\u0436\u0434\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0434\u0435\u043a\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e @vr.inject \u0443\u043a\u0430\u0437\u0430\u0432 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u0432\u0445\u043e\u0434\u043d\u0443\u044e (\u0434\u043b\u044f \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0411\u0414), \u0432\u044b\u0445\u043e\u0434\u043d\u0443\u044e (\u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0411\u0414) \u0438\u043b\u0438 \u043e\u0431\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b. \u0414\u0430\u043b\u0435\u0435 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u043a\u043b\u0430\u0441\u0441\u0430 VechordPipeline, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0434\u0438\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u043d\u0430\u0448\u0438\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439. \u041f\u0435\u0440\u0432\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f &#8212; \u0434\u043b\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0430 \u0432\u044b\u0445\u043e\u0434\u043d\u044b\u0445. \u041e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445. \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u0443\u0434\u0443\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u0438\u0445 \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0441\u043f\u0438\u0441\u043a\u0435, \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u044f \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438.\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430. \u0412 \u043d\u0451\u043c \u0431\u0443\u0434\u0435\u0442 \u0434\u0432\u0430 \u044d\u0442\u0430\u043f\u0430 &#8212; \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0447\u0430\u043d\u043a\u043e\u0432 \u0442\u0435\u043a\u0441\u0442\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430. \u041d\u043e \u043d\u0430\u0447\u043d\u0435\u043c \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0441 \u0432\u0445\u043e\u0434\u043d\u043e\u0439 Pydantic \u0441\u0445\u0435\u043c\u044b, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 API:# \u0444\u0430\u0439\u043b schemas\/document.pyfrom typing import Annotated, Anyfrom pydantic import BaseModel, Fieldclass DocumentBase(BaseModel):    title: Annotated[str, Field(&#8230;, description=&#8217;\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430&#8217;)]    text: Annotated[str, Field(&#8230;, description=&#8217;\u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430&#8217;)]    meta_data: Annotated[        dict[str, Any],        Field(default_factory=dict, description=&#8217;\u041c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430&#8217;)]class DocumentCreate(DocumentBase):    pass\u041a\u0430\u043a \u0438 \u0440\u0430\u043d\u044c\u0448\u0435, \u0434\u0435\u043b\u0430\u0435\u043c \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c, \u043e\u0442 \u043d\u0435\u0451 \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0448\u0430\u0433\u043e\u0432 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430. \u0418\u0437-\u0437\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0435\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u0430 \u0438\u0445 \u043d\u0435\u043b\u044c\u0437\u044f \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u0442\u0435\u043b\u043e \u043a\u043b\u0430\u0441\u0441\u0430 &#8212; self \u0431\u0443\u0434\u0435\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044c\u0441\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u043c \u043a \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044e \u0438\u0437 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u043d\u0435 \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u043d\u0430 \u043e\u0431\u044a\u0435\u043a\u0442 \u043a\u043b\u0430\u0441\u0441\u0430. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u044b\u043d\u0435\u0441\u0435\u043c \u0448\u0430\u0433\u0438 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0444\u0430\u0439\u043b. \u041d\u0430\u0447\u043d\u0451\u043c \u0441 create_doc:# \u0444\u0430\u0439\u043b services\/utils.pyfrom psycopg.types.json import Jsonbfrom db_models import Document, vrfrom schemas.document import DocumentCreate@vr.inject(output=Document)async def _create_document(doc_data: DocumentCreate) -&gt; Document:    doc = Document(        title=doc_data.title,        text=doc_data.text,&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-476375","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/476375","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=476375"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/476375\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=476375"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=476375"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=476375"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}