@intlayer/docs 7.5.12 → 7.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blog/uk/compiler_vs_declarative_i18n.md +224 -0
- package/blog/uk/i18n_using_next-i18next.md +1086 -0
- package/blog/uk/i18n_using_next-intl.md +760 -0
- package/blog/uk/index.md +69 -0
- package/blog/uk/internationalization_and_SEO.md +273 -0
- package/blog/uk/intlayer_with_i18next.md +211 -0
- package/blog/uk/intlayer_with_next-i18next.md +202 -0
- package/blog/uk/intlayer_with_next-intl.md +203 -0
- package/blog/uk/intlayer_with_react-i18next.md +200 -0
- package/blog/uk/intlayer_with_react-intl.md +202 -0
- package/blog/uk/intlayer_with_vue-i18n.md +206 -0
- package/blog/uk/l10n_platform_alternative/Lokalise.md +80 -0
- package/blog/uk/l10n_platform_alternative/crowdin.md +80 -0
- package/blog/uk/l10n_platform_alternative/phrase.md +78 -0
- package/blog/uk/list_i18n_technologies/CMS/drupal.md +143 -0
- package/blog/uk/list_i18n_technologies/CMS/wix.md +167 -0
- package/blog/uk/list_i18n_technologies/CMS/wordpress.md +189 -0
- package/blog/uk/list_i18n_technologies/frameworks/angular.md +125 -0
- package/blog/uk/list_i18n_technologies/frameworks/flutter.md +128 -0
- package/blog/uk/list_i18n_technologies/frameworks/react-native.md +217 -0
- package/blog/uk/list_i18n_technologies/frameworks/react.md +155 -0
- package/blog/uk/list_i18n_technologies/frameworks/svelte.md +145 -0
- package/blog/uk/list_i18n_technologies/frameworks/vue.md +144 -0
- package/blog/uk/next-i18next_vs_next-intl_vs_intlayer.md +1499 -0
- package/blog/uk/nextjs-multilingual-seo-comparison.md +360 -0
- package/blog/uk/rag_powered_documentation_assistant.md +288 -0
- package/blog/uk/react-i18next_vs_react-intl_vs_intlayer.md +164 -0
- package/blog/uk/vue-i18n_vs_intlayer.md +279 -0
- package/blog/uk/what_is_internationalization.md +167 -0
- package/dist/cjs/generated/frequentQuestions.entry.cjs +20 -0
- package/dist/cjs/generated/frequentQuestions.entry.cjs.map +1 -1
- package/dist/esm/generated/frequentQuestions.entry.mjs +20 -0
- package/dist/esm/generated/frequentQuestions.entry.mjs.map +1 -1
- package/dist/types/generated/frequentQuestions.entry.d.ts +1 -0
- package/dist/types/generated/frequentQuestions.entry.d.ts.map +1 -1
- package/docs/ar/configuration.md +6 -1
- package/docs/ar/dictionary/content_file.md +6 -1
- package/docs/de/configuration.md +6 -1
- package/docs/de/dictionary/content_file.md +6 -1
- package/docs/en/configuration.md +6 -1
- package/docs/en/dictionary/content_file.md +6 -1
- package/docs/en-GB/configuration.md +6 -1
- package/docs/en-GB/dictionary/content_file.md +3 -1
- package/docs/es/configuration.md +6 -1
- package/docs/es/dictionary/content_file.md +6 -1
- package/docs/fr/configuration.md +6 -1
- package/docs/fr/dictionary/content_file.md +3 -1
- package/docs/hi/configuration.md +6 -1
- package/docs/hi/dictionary/content_file.md +3 -1
- package/docs/id/configuration.md +6 -1
- package/docs/id/dictionary/content_file.md +3 -1
- package/docs/it/configuration.md +6 -1
- package/docs/it/dictionary/content_file.md +3 -1
- package/docs/ja/configuration.md +6 -1
- package/docs/ja/dictionary/content_file.md +3 -1
- package/docs/ko/configuration.md +6 -1
- package/docs/ko/dictionary/content_file.md +3 -1
- package/docs/pl/configuration.md +3 -1
- package/docs/pl/dictionary/content_file.md +3 -1
- package/docs/pt/configuration.md +6 -1
- package/docs/pt/dictionary/content_file.md +3 -1
- package/docs/ru/configuration.md +6 -1
- package/docs/ru/dictionary/content_file.md +6 -1
- package/docs/tr/configuration.md +6 -1
- package/docs/tr/dictionary/content_file.md +3 -1
- package/docs/uk/CI_CD.md +198 -0
- package/docs/uk/autoFill.md +307 -0
- package/docs/uk/bundle_optimization.md +185 -0
- package/docs/uk/cli/build.md +64 -0
- package/docs/uk/cli/ci.md +137 -0
- package/docs/uk/cli/configuration.md +63 -0
- package/docs/uk/cli/debug.md +46 -0
- package/docs/uk/cli/doc-review.md +43 -0
- package/docs/uk/cli/doc-translate.md +132 -0
- package/docs/uk/cli/editor.md +28 -0
- package/docs/uk/cli/fill.md +130 -0
- package/docs/uk/cli/index.md +190 -0
- package/docs/uk/cli/init.md +84 -0
- package/docs/uk/cli/list.md +90 -0
- package/docs/uk/cli/list_projects.md +128 -0
- package/docs/uk/cli/live.md +41 -0
- package/docs/uk/cli/login.md +157 -0
- package/docs/uk/cli/pull.md +78 -0
- package/docs/uk/cli/push.md +98 -0
- package/docs/uk/cli/sdk.md +71 -0
- package/docs/uk/cli/test.md +76 -0
- package/docs/uk/cli/transform.md +65 -0
- package/docs/uk/cli/version.md +24 -0
- package/docs/uk/cli/watch.md +37 -0
- package/docs/uk/configuration.md +742 -0
- package/docs/uk/dictionary/condition.md +237 -0
- package/docs/uk/dictionary/content_file.md +1134 -0
- package/docs/uk/dictionary/enumeration.md +245 -0
- package/docs/uk/dictionary/file.md +232 -0
- package/docs/uk/dictionary/function_fetching.md +212 -0
- package/docs/uk/dictionary/gender.md +273 -0
- package/docs/uk/dictionary/insertion.md +187 -0
- package/docs/uk/dictionary/markdown.md +383 -0
- package/docs/uk/dictionary/nesting.md +273 -0
- package/docs/uk/dictionary/translation.md +332 -0
- package/docs/uk/formatters.md +595 -0
- package/docs/uk/how_works_intlayer.md +256 -0
- package/docs/uk/index.md +175 -0
- package/docs/uk/interest_of_intlayer.md +297 -0
- package/docs/uk/intlayer_CMS.md +569 -0
- package/docs/uk/intlayer_visual_editor.md +292 -0
- package/docs/uk/intlayer_with_angular.md +710 -0
- package/docs/uk/intlayer_with_astro.md +256 -0
- package/docs/uk/intlayer_with_create_react_app.md +1258 -0
- package/docs/uk/intlayer_with_express.md +429 -0
- package/docs/uk/intlayer_with_fastify.md +446 -0
- package/docs/uk/intlayer_with_lynx+react.md +548 -0
- package/docs/uk/intlayer_with_nestjs.md +283 -0
- package/docs/uk/intlayer_with_next-i18next.md +640 -0
- package/docs/uk/intlayer_with_next-intl.md +456 -0
- package/docs/uk/intlayer_with_nextjs_page_router.md +1541 -0
- package/docs/uk/intlayer_with_nuxt.md +711 -0
- package/docs/uk/intlayer_with_react_router_v7.md +600 -0
- package/docs/uk/intlayer_with_react_router_v7_fs_routes.md +669 -0
- package/docs/uk/intlayer_with_svelte_kit.md +579 -0
- package/docs/uk/intlayer_with_tanstack.md +818 -0
- package/docs/uk/intlayer_with_vite+preact.md +1748 -0
- package/docs/uk/intlayer_with_vite+react.md +1449 -0
- package/docs/uk/intlayer_with_vite+solid.md +302 -0
- package/docs/uk/intlayer_with_vite+svelte.md +520 -0
- package/docs/uk/intlayer_with_vite+vue.md +1113 -0
- package/docs/uk/introduction.md +222 -0
- package/docs/uk/locale_mapper.md +242 -0
- package/docs/uk/mcp_server.md +211 -0
- package/docs/uk/packages/express-intlayer/t.md +465 -0
- package/docs/uk/packages/intlayer/getEnumeration.md +159 -0
- package/docs/uk/packages/intlayer/getHTMLTextDir.md +121 -0
- package/docs/uk/packages/intlayer/getLocaleLang.md +81 -0
- package/docs/uk/packages/intlayer/getLocaleName.md +135 -0
- package/docs/uk/packages/intlayer/getLocalizedUrl.md +338 -0
- package/docs/uk/packages/intlayer/getMultilingualUrls.md +359 -0
- package/docs/uk/packages/intlayer/getPathWithoutLocale.md +75 -0
- package/docs/uk/packages/intlayer/getPrefix.md +213 -0
- package/docs/uk/packages/intlayer/getTranslation.md +190 -0
- package/docs/uk/packages/intlayer/getTranslationContent.md +189 -0
- package/docs/uk/packages/next-intlayer/t.md +365 -0
- package/docs/uk/packages/next-intlayer/useDictionary.md +276 -0
- package/docs/uk/packages/next-intlayer/useIntlayer.md +263 -0
- package/docs/uk/packages/next-intlayer/useLocale.md +166 -0
- package/docs/uk/packages/react-intlayer/t.md +311 -0
- package/docs/uk/packages/react-intlayer/useDictionary.md +295 -0
- package/docs/uk/packages/react-intlayer/useI18n.md +250 -0
- package/docs/uk/packages/react-intlayer/useIntlayer.md +251 -0
- package/docs/uk/packages/react-intlayer/useLocale.md +210 -0
- package/docs/uk/per_locale_file.md +345 -0
- package/docs/uk/plugins/sync-json.md +398 -0
- package/docs/uk/readme.md +265 -0
- package/docs/uk/releases/v6.md +305 -0
- package/docs/uk/releases/v7.md +624 -0
- package/docs/uk/roadmap.md +346 -0
- package/docs/uk/testing.md +204 -0
- package/docs/vi/configuration.md +6 -1
- package/docs/vi/dictionary/content_file.md +6 -1
- package/docs/zh/configuration.md +6 -1
- package/docs/zh/dictionary/content_file.md +6 -1
- package/frequent_questions/ar/error-vite-env-only.md +77 -0
- package/frequent_questions/de/error-vite-env-only.md +77 -0
- package/frequent_questions/en/error-vite-env-only.md +77 -0
- package/frequent_questions/en-GB/error-vite-env-only.md +77 -0
- package/frequent_questions/es/error-vite-env-only.md +76 -0
- package/frequent_questions/fr/error-vite-env-only.md +77 -0
- package/frequent_questions/hi/error-vite-env-only.md +77 -0
- package/frequent_questions/id/error-vite-env-only.md +77 -0
- package/frequent_questions/it/error-vite-env-only.md +77 -0
- package/frequent_questions/ja/error-vite-env-only.md +77 -0
- package/frequent_questions/ko/error-vite-env-only.md +77 -0
- package/frequent_questions/pl/error-vite-env-only.md +77 -0
- package/frequent_questions/pt/error-vite-env-only.md +77 -0
- package/frequent_questions/ru/error-vite-env-only.md +77 -0
- package/frequent_questions/tr/error-vite-env-only.md +77 -0
- package/frequent_questions/uk/SSR_Next_no_[locale].md +104 -0
- package/frequent_questions/uk/array_as_content_declaration.md +72 -0
- package/frequent_questions/uk/build_dictionaries.md +58 -0
- package/frequent_questions/uk/build_error_CI_CD.md +74 -0
- package/frequent_questions/uk/bun_set_up.md +53 -0
- package/frequent_questions/uk/customized_locale_list.md +64 -0
- package/frequent_questions/uk/domain_routing.md +113 -0
- package/frequent_questions/uk/error-vite-env-only.md +77 -0
- package/frequent_questions/uk/esbuild_error.md +29 -0
- package/frequent_questions/uk/get_locale_cookie.md +142 -0
- package/frequent_questions/uk/intlayer_command_undefined.md +155 -0
- package/frequent_questions/uk/locale_incorect_in_url.md +73 -0
- package/frequent_questions/uk/package_version_error.md +181 -0
- package/frequent_questions/uk/static_rendering.md +44 -0
- package/frequent_questions/uk/translated_path_url.md +55 -0
- package/frequent_questions/uk/unknown_command.md +97 -0
- package/frequent_questions/vi/error-vite-env-only.md +77 -0
- package/frequent_questions/zh/error-vite-env-only.md +77 -0
- package/legal/uk/privacy_notice.md +83 -0
- package/legal/uk/terms_of_service.md +55 -0
- package/package.json +6 -6
- package/src/generated/frequentQuestions.entry.ts +20 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2025-09-28
|
|
3
|
+
updatedAt: 2025-09-28
|
|
4
|
+
title: SEO та i18n в Next.js
|
|
5
|
+
description: Дізнайтеся, як налаштувати багатомовне SEO у вашому додатку Next.js за допомогою next-intl, next-i18next та Intlayer.
|
|
6
|
+
keywords:
|
|
7
|
+
- Intlayer
|
|
8
|
+
- SEO
|
|
9
|
+
- Інтернаціоналізація
|
|
10
|
+
- Next.js
|
|
11
|
+
- i18n
|
|
12
|
+
- JavaScript
|
|
13
|
+
- React
|
|
14
|
+
- next-intl
|
|
15
|
+
- next-i18next
|
|
16
|
+
slugs:
|
|
17
|
+
- blog
|
|
18
|
+
- blog-seo-i18n-nextjs
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# SEO та i18n в Next.js: Перекладу недостатньо
|
|
22
|
+
|
|
23
|
+
Коли розробники думають про інтернаціоналізацію (i18n), першим рефлексом часто є: _перекласти контент_. Але зазвичай забувають, що головна мета інтернаціоналізації — зробити ваш вебсайт більш помітним для світу.
|
|
24
|
+
Якщо ваша багатомовна програма Next.js не повідомляє пошуковим системам, як сканувати й розуміти різні мовні версії, більшість ваших зусиль може залишитися непоміченою.
|
|
25
|
+
|
|
26
|
+
У цьому блозі ми розглянемо, чому **i18n — це суперсила для SEO**, і як правильно реалізувати її в Next.js за допомогою `next-intl`, `next-i18next` та `Intlayer`.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Чому SEO та i18n
|
|
31
|
+
|
|
32
|
+
Додавання мов — це не лише про UX. Це також потужний важіль для **органічної видимості**. Ось чому:
|
|
33
|
+
|
|
34
|
+
1. **Краща виявлюваність:** Пошукові системи індексують локалізовані версії та ранжують їх для користувачів, які шукають рідною мовою.
|
|
35
|
+
2. **Уникнення дубльованого контенту:** Правильні канонічні та альтернативні теги повідомляють сканерам, яка сторінка належить до якої локалі.
|
|
36
|
+
3. **Кращий UX:** Відвідувачі потрапляють одразу на потрібну версію вашого сайту.
|
|
37
|
+
4. **Конкурентна перевага:** Лише небагато сайтів правильно реалізують багатомовне SEO, тож ви можете виділитися.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Найкращі практики для багатомовного SEO в Next.js
|
|
42
|
+
|
|
43
|
+
Ось контрольний список, який має реалізувати кожен багатомовний додаток:
|
|
44
|
+
|
|
45
|
+
- **Встановіть мета-теги `hreflang` у `<head>`**
|
|
46
|
+
Допомагає Google зрозуміти, які версії існують для кожної мови.
|
|
47
|
+
|
|
48
|
+
- **Перелічіть всі перекладені сторінки у `sitemap.xml`**
|
|
49
|
+
Використовуйте схему `xhtml`, щоб пошукові роботи могли легко знаходити альтернативні версії.
|
|
50
|
+
|
|
51
|
+
- **Виключіть приватні/локалізовані маршрути в `robots.txt`**
|
|
52
|
+
Наприклад, не дозволяйте індексацію `/dashboard`, `/fr/dashboard`, `/es/dashboard`.
|
|
53
|
+
|
|
54
|
+
- **Використовуйте локалізовані посилання**
|
|
55
|
+
Приклад: `<a href="/fr/about">Про нас</a>` замість посилання на сторінку за замовчуванням `/about`.
|
|
56
|
+
|
|
57
|
+
Це прості кроки — але їх пропуск може коштувати вам видимості.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Приклади реалізації
|
|
62
|
+
|
|
63
|
+
Розробники часто забувають правильно посилатися на свої сторінки для різних локалей, тож давайте подивимось, як це працює на практиці з різними бібліотеками.
|
|
64
|
+
|
|
65
|
+
### **next-intl**
|
|
66
|
+
|
|
67
|
+
<Tabs>
|
|
68
|
+
<TabItem label="next-intl">
|
|
69
|
+
|
|
70
|
+
```tsx fileName="src/app/[locale]/about/layout.tsx
|
|
71
|
+
import type { Metadata } from "next";
|
|
72
|
+
import { locales, defaultLocale } from "@/i18n";
|
|
73
|
+
import { getTranslations, unstable_setRequestLocale } from "next-intl/server";
|
|
74
|
+
|
|
75
|
+
function localizedPath(locale: string, path: string) {
|
|
76
|
+
return locale === defaultLocale ? path : `/${locale}${path}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function generateMetadata({
|
|
80
|
+
params,
|
|
81
|
+
}: {
|
|
82
|
+
params: { locale: string };
|
|
83
|
+
}): Promise<Metadata> {
|
|
84
|
+
const { locale } = params;
|
|
85
|
+
const t = await getTranslations({ locale, namespace: "about" });
|
|
86
|
+
|
|
87
|
+
const url = "/about";
|
|
88
|
+
const languages = Object.fromEntries(
|
|
89
|
+
locales.map((l) => [l, localizedPath(l, url)])
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
title: t("title"),
|
|
94
|
+
description: t("description"),
|
|
95
|
+
alternates: {
|
|
96
|
+
canonical: localizedPath(locale, url),
|
|
97
|
+
languages: { ...languages, "x-default": url },
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ... решта коду сторінки
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```tsx fileName="src/app/sitemap.ts"
|
|
106
|
+
import type { MetadataRoute } from "next";
|
|
107
|
+
import { locales, defaultLocale } from "@/i18n";
|
|
108
|
+
|
|
109
|
+
const origin = "https://example.com";
|
|
110
|
+
|
|
111
|
+
const formatterLocalizedPath = (locale: string, path: string) =>
|
|
112
|
+
locale === defaultLocale ? `${origin}${path}` : `${origin}/${locale}${path}`;
|
|
113
|
+
|
|
114
|
+
export default function sitemap(): MetadataRoute.Sitemap {
|
|
115
|
+
const aboutLanguages = Object.fromEntries(
|
|
116
|
+
locales.map((l) => [l, formatterLocalizedPath(l, "/about")])
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
return [
|
|
120
|
+
{
|
|
121
|
+
url: formatterLocalizedPath(defaultLocale, "/about"),
|
|
122
|
+
lastModified: new Date(),
|
|
123
|
+
changeFrequency: "monthly",
|
|
124
|
+
priority: 0.7,
|
|
125
|
+
alternates: { languages: aboutLanguages },
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```tsx fileName="src/app/robots.ts"
|
|
132
|
+
import type { MetadataRoute } from "next";
|
|
133
|
+
import { locales, defaultLocale } from "@/i18n";
|
|
134
|
+
|
|
135
|
+
const origin = "https://example.com";
|
|
136
|
+
const withAllLocales = (path: string) => [
|
|
137
|
+
path,
|
|
138
|
+
...locales.filter((l) => l !== defaultLocale).map((l) => `/${l}${path}`),
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
export default function robots(): MetadataRoute.Robots {
|
|
142
|
+
const disallow = [
|
|
143
|
+
...withAllLocales("/dashboard"),
|
|
144
|
+
...withAllLocales("/admin"),
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
rules: { userAgent: "*", allow: ["/"], disallow },
|
|
149
|
+
host: origin,
|
|
150
|
+
sitemap: `${origin}/sitemap.xml`,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### **next-i18next**
|
|
156
|
+
|
|
157
|
+
</TabItem>
|
|
158
|
+
<TabItem label="next-i18next">
|
|
159
|
+
|
|
160
|
+
```ts fileName="i18n.config.ts"
|
|
161
|
+
export const locales = ["en", "fr"] as const;
|
|
162
|
+
export type Locale = (typeof locales)[number];
|
|
163
|
+
export const defaultLocale: Locale = "en";
|
|
164
|
+
|
|
165
|
+
/** Додає префікс локалі до шляху, якщо це не локаль за замовчуванням */
|
|
166
|
+
export function localizedPath(locale: string, path: string) {
|
|
167
|
+
return locale === defaultLocale ? path : `/${locale}${path}`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Допоміжна функція для абсолютних URL */
|
|
171
|
+
const ORIGIN = "https://example.com";
|
|
172
|
+
export function abs(locale: string, path: string) {
|
|
173
|
+
return `${ORIGIN}${localizedPath(locale, path)}`;
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
```tsx fileName="src/app/[locale]/about/layout.tsx"
|
|
178
|
+
import type { Metadata } from "next";
|
|
179
|
+
import { locales, defaultLocale, localizedPath } from "@/i18n.config";
|
|
180
|
+
|
|
181
|
+
export async function generateMetadata({
|
|
182
|
+
params,
|
|
183
|
+
}: {
|
|
184
|
+
params: { locale: string };
|
|
185
|
+
}): Promise<Metadata> {
|
|
186
|
+
const { locale } = params;
|
|
187
|
+
|
|
188
|
+
// Динамічно імпортуємо відповідний JSON-файл
|
|
189
|
+
const messages = (await import(`@/../public/locales/${locale}/about.json`))
|
|
190
|
+
.default;
|
|
191
|
+
|
|
192
|
+
const languages = Object.fromEntries(
|
|
193
|
+
locales.map((l) => [l, localizedPath(l, "/about")])
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
title: messages.title,
|
|
198
|
+
description: messages.description,
|
|
199
|
+
alternates: {
|
|
200
|
+
canonical: localizedPath(locale, "/about"),
|
|
201
|
+
languages: { ...languages, "x-default": "/about" },
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export default async function AboutPage() {
|
|
207
|
+
return <h1>Про нас</h1>;
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
```ts fileName="src/app/sitemap.ts"
|
|
212
|
+
import type { MetadataRoute } from "next";
|
|
213
|
+
import { locales, defaultLocale, abs } from "@/i18n.config";
|
|
214
|
+
|
|
215
|
+
export default function sitemap(): MetadataRoute.Sitemap {
|
|
216
|
+
const languages = Object.fromEntries(
|
|
217
|
+
locales.map((l) => [l, abs(l, "/about")])
|
|
218
|
+
);
|
|
219
|
+
return [
|
|
220
|
+
{
|
|
221
|
+
url: abs(defaultLocale, "/about"),
|
|
222
|
+
lastModified: new Date(),
|
|
223
|
+
changeFrequency: "monthly",
|
|
224
|
+
priority: 0.7,
|
|
225
|
+
alternates: { languages },
|
|
226
|
+
},
|
|
227
|
+
];
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
```ts fileName="src/app/robots.ts"
|
|
232
|
+
import type { MetadataRoute } from "next";
|
|
233
|
+
import { locales, defaultLocale, localizedPath } from "@/i18n.config";
|
|
234
|
+
|
|
235
|
+
const ORIGIN = "https://example.com";
|
|
236
|
+
|
|
237
|
+
const expandAllLocales = (path: string) => [
|
|
238
|
+
localizedPath(defaultLocale, path),
|
|
239
|
+
...locales
|
|
240
|
+
.filter((l) => l !== defaultLocale)
|
|
241
|
+
.map((l) => localizedPath(l, path)),
|
|
242
|
+
];
|
|
243
|
+
|
|
244
|
+
export default function robots(): MetadataRoute.Robots {
|
|
245
|
+
const disallow = [
|
|
246
|
+
...expandAllLocales("/dashboard"),
|
|
247
|
+
...expandAllLocales("/admin"),
|
|
248
|
+
];
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
rules: { userAgent: "*", allow: ["/"], disallow },
|
|
252
|
+
host: ORIGIN,
|
|
253
|
+
sitemap: `${ORIGIN}/sitemap.xml`,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### **Intlayer**
|
|
259
|
+
|
|
260
|
+
</TabItem>
|
|
261
|
+
<TabItem label="intlayer">
|
|
262
|
+
|
|
263
|
+
````typescript fileName="src/app/[locale]/about/layout.tsx"
|
|
264
|
+
import { getIntlayer, getMultilingualUrls } from "intlayer";
|
|
265
|
+
import type { Metadata } from "next";
|
|
266
|
+
import type { LocalPromiseParams } from "next-intlayer";
|
|
267
|
+
|
|
268
|
+
export const generateMetadata = async ({
|
|
269
|
+
params,
|
|
270
|
+
}: LocalPromiseParams): Promise<Metadata> => {
|
|
271
|
+
const { locale } = await params;
|
|
272
|
+
|
|
273
|
+
const metadata = getIntlayer("page-metadata", locale);
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Генерує об'єкт, що містить усі URL-адреси для кожної локалі.
|
|
277
|
+
*
|
|
278
|
+
* Приклад:
|
|
279
|
+
* ```ts
|
|
280
|
+
* getMultilingualUrls('/about');
|
|
281
|
+
*
|
|
282
|
+
* // Повертає
|
|
283
|
+
* // {
|
|
284
|
+
* // en: '/about',
|
|
285
|
+
* // fr: '/fr/about',
|
|
286
|
+
* // es: '/es/about',
|
|
287
|
+
* // }
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
const multilingualUrls = getMultilingualUrls("/about");
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
...metadata,
|
|
294
|
+
alternates: {
|
|
295
|
+
canonical: multilingualUrls[locale as keyof typeof multilingualUrls],
|
|
296
|
+
languages: { ...multilingualUrls, "x-default": "/about" },
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// ... Решта коду сторінки
|
|
302
|
+
````
|
|
303
|
+
|
|
304
|
+
```tsx fileName="src/app/sitemap.ts"
|
|
305
|
+
import { getMultilingualUrls } from "intlayer";
|
|
306
|
+
import type { MetadataRoute } from "next";
|
|
307
|
+
|
|
308
|
+
const sitemap = (): MetadataRoute.Sitemap => [
|
|
309
|
+
{
|
|
310
|
+
url: "https://example.com/about",
|
|
311
|
+
alternates: {
|
|
312
|
+
languages: { ...getMultilingualUrls("https://example.com/about") },
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
];
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
```tsx fileName="src/app/robots.ts"
|
|
319
|
+
import { getMultilingualUrls } from "intlayer";
|
|
320
|
+
import type { MetadataRoute } from "next";
|
|
321
|
+
|
|
322
|
+
const getAllMultilingualUrls = (urls: string[]) =>
|
|
323
|
+
urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);
|
|
324
|
+
|
|
325
|
+
const robots = (): MetadataRoute.Robots => ({
|
|
326
|
+
rules: {
|
|
327
|
+
userAgent: "*",
|
|
328
|
+
allow: ["/"],
|
|
329
|
+
disallow: getAllMultilingualUrls(["/dashboard"]),
|
|
330
|
+
},
|
|
331
|
+
host: "https://example.com",
|
|
332
|
+
sitemap: `https://example.com/sitemap.xml`,
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
export default robots;
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
> Intlayer надає функцію `getMultilingualUrls` для генерації багатомовних URL-адрес для вашого sitemap.
|
|
339
|
+
|
|
340
|
+
</TabItem>
|
|
341
|
+
</Tabs>
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Висновок
|
|
346
|
+
|
|
347
|
+
Правильна реалізація i18n у Next.js — це не просто переклад тексту, а забезпечення того, щоб пошукові системи та користувачі точно знали, яку версію вашого контенту показувати.
|
|
348
|
+
Налаштування hreflang, sitemap і правил для robots — те, що перетворює переклади на реальну SEO-цінність.
|
|
349
|
+
|
|
350
|
+
Хоча next-intl і next-i18next дають надійні способи це реалізувати, зазвичай вони вимагають багато ручного налаштування, щоб підтримувати консистентність між локалями.
|
|
351
|
+
|
|
352
|
+
Саме тут Intlayer дійсно вирізняється:
|
|
353
|
+
|
|
354
|
+
Воно постачається з вбудованими хелперами, такими як getMultilingualUrls, що робить інтеграцію hreflang, sitemap і robots майже беззусильною.
|
|
355
|
+
|
|
356
|
+
Метадані зберігаються централізовано замість того, щоб розкидуватися по JSON-файлах або власних утилітах.
|
|
357
|
+
|
|
358
|
+
Він спроєктований для Next.js з нуля, тож ви витрачаєте менше часу на налагодження конфігурації й більше — на реліз.
|
|
359
|
+
|
|
360
|
+
Якщо ваша мета — не просто перекладати, а масштабувати багатомовне SEO без зайвих зусиль, Intlayer дає вам найчистіше, найбільш стійке до майбутніх змін налаштування.
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2025-09-10
|
|
3
|
+
updatedAt: 2025-09-10
|
|
4
|
+
title: "Створення помічника з документацією на основі RAG (розбиття на фрагменти, ембеддинги та пошук)"
|
|
5
|
+
description: "Створення помічника з документацією на основі RAG (розбиття на фрагменти, ембеддинги та пошук)"
|
|
6
|
+
keywords:
|
|
7
|
+
- RAG
|
|
8
|
+
- Документація
|
|
9
|
+
- Помічник
|
|
10
|
+
- Розбиття на фрагменти
|
|
11
|
+
- Ембеддинги
|
|
12
|
+
- Пошук
|
|
13
|
+
slugs:
|
|
14
|
+
- blog
|
|
15
|
+
- rag-powered-documentation-assistant
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Створення помічника з документацією на основі RAG (розбиття на фрагменти, ембеддинги та пошук)
|
|
19
|
+
|
|
20
|
+
## Що ви отримаєте
|
|
21
|
+
|
|
22
|
+
Я створив помічника з документацією на основі RAG і упакував його у boilerplate, який ви можете використати одразу.
|
|
23
|
+
|
|
24
|
+
- Містить готовий до використання додаток (Next.js + OpenAI API)
|
|
25
|
+
- Включає робочий RAG-пайплайн (розбиття на фрагменти, ембеддинги, косинусна схожість)
|
|
26
|
+
- Надає повний інтерфейс чат-бота, побудований на React
|
|
27
|
+
- Усі UI-компоненти повністю редагуються за допомогою Tailwind CSS
|
|
28
|
+
- Реєструє кожний запит користувача, щоб допомогти виявити відсутню документацію, болі користувачів та продуктові можливості
|
|
29
|
+
|
|
30
|
+
👉 [Жива демонстрація](https://intlayer.org/doc/why) 👉 [Шаблон коду](https://github.com/aymericzip/smart_doc_RAG)
|
|
31
|
+
|
|
32
|
+
## Вступ
|
|
33
|
+
|
|
34
|
+
Якщо ви колись губилися у документації, нескінченно гортали в пошуках однієї відповіді, ви знаєте, наскільки це болісно. Документація корисна, але вона статична, і пошук у ній часто здається незручним.
|
|
35
|
+
|
|
36
|
+
Ось тут і з’являється **RAG (Retrieval-Augmented Generation)**. Замість того, щоб змушувати користувачів ритися в тексті, ми можемо поєднати **retrieval** (пошук потрібних частин документації) з **generation** (коли LLM природно пояснює їх).
|
|
37
|
+
|
|
38
|
+
У цій статті я покажу, як я створив чатбот для документації на базі RAG і як він не лише допомагає користувачам швидше знаходити відповіді, а й дає продуктовим командам новий спосіб розуміти болючі точки користувачів.
|
|
39
|
+
|
|
40
|
+
## Чому варто використовувати RAG для документації?
|
|
41
|
+
|
|
42
|
+
RAG став популярним не випадково: це один із найпрактичніших способів зробити великі мовні моделі дійсно корисними.
|
|
43
|
+
|
|
44
|
+
Для документації переваги очевидні:
|
|
45
|
+
|
|
46
|
+
- Миттєві відповіді: користувачі запитують природною мовою і отримують релевантні відповіді.
|
|
47
|
+
- Кращий контекст: модель бачить лише найбільш релевантні розділи документації, що зменшує галюцинації.
|
|
48
|
+
- Пошук, який відчувається природно: поєднання Algolia + FAQ + чатбот в одному.
|
|
49
|
+
- Зворотний зв'язок: зберігаючи запити, ви виявляєте, з чим насправді мають труднощі користувачі.
|
|
50
|
+
|
|
51
|
+
Той останній пункт є вирішальним. Система RAG не лише відповідає на запитання — вона показує, що саме запитують користувачі. Це означає:
|
|
52
|
+
|
|
53
|
+
- Ви виявляєте відсутню інформацію в документації.
|
|
54
|
+
- Ви бачите появу запитів на нові фічі.
|
|
55
|
+
- Ви помічаєте патерни, які можуть навіть спрямовувати продуктову стратегію.
|
|
56
|
+
|
|
57
|
+
Отже, RAG — це не лише інструмент підтримки. Це також **двигун product discovery**.
|
|
58
|
+
|
|
59
|
+
## Як працює RAG-пайплайн
|
|
60
|
+
|
|
61
|
+

|
|
62
|
+
|
|
63
|
+
У загальних рисах, ось рецепт, який я використав:
|
|
64
|
+
|
|
65
|
+
1. **Розбиття документації на чанки** Великі Markdown-файли розбиваються на чанки. Розбиття дозволяє передавати у контекст лише релевантні частини документації.
|
|
66
|
+
2. **Генерація embeddings** Кожен шматок перетворюється на вектор за допомогою embedding API від OpenAI (text-embedding-3-large) або через векторну базу даних (Chroma, Qdrant, Pinecone).
|
|
67
|
+
3. **Індексація & зберігання** Embeddings зберігаються у простому JSON-файлі (для мого демо), але в продакшні ви, ймовірно, використовуватимете векторну БД.
|
|
68
|
+
4. **Пошук (R у RAG)** Запит користувача перетворюється на embedding, обчислюється косинусна схожість, і витягуються топ-найбільш підхожі шматки.
|
|
69
|
+
5. **Augmentation + Generation (AG у RAG)** Ці шматки вставляються в prompt для ChatGPT, тож модель відповідає з урахуванням реального контексту документації.
|
|
70
|
+
6. **Логування запитів для зворотного зв'язку** Кожен запит користувача зберігається. Це золото для розуміння больових точок, відсутньої документації або нових можливостей.
|
|
71
|
+
|
|
72
|
+
## Крок 1: Читання документації
|
|
73
|
+
|
|
74
|
+
Перший крок був простим: мені потрібно було просканувати папку docs/ на предмет усіх .md файлів. Використовуючи Node.js та glob, я завантажив вміст кожного Markdown-файлу в пам'ять.
|
|
75
|
+
|
|
76
|
+
Це робить конвеєр гнучким: замість Markdown ви можете отримувати документацію з бази даних, CMS або навіть через API.
|
|
77
|
+
|
|
78
|
+
## Крок 2: Розбиття документації на чанки
|
|
79
|
+
|
|
80
|
+
Навіщо розбивати? Тому що мовні моделі мають **обмеження контексту**. Підживлення їх цілою книгою документації не спрацює.
|
|
81
|
+
|
|
82
|
+
Отже, ідея полягає в тому, щоб розбити текст на керовані частини (наприклад 500 токенів кожна) з перекриттям (наприклад 100 токенів). Перекриття забезпечує безперервність, щоб ви не втрачали сенс на межах частин.
|
|
83
|
+
|
|
84
|
+
<p align="center">
|
|
85
|
+
<img width="480" alt="Надійне джерело даних" src="https://github.com/user-attachments/assets/ee548851-7206-4cc6-821e-de8a4366c6a3" />
|
|
86
|
+
</p>
|
|
87
|
+
|
|
88
|
+
**Приклад:**
|
|
89
|
+
|
|
90
|
+
- Chunk 1 → “…стару бібліотеку, яку багато хто забув. Її велетенські полиці були заповнені книгами…”
|
|
91
|
+
- Chunk 2 → “…полиці були заповнені книгами з усіх уявних жанрів, кожна шепотіла свої історії…”
|
|
92
|
+
|
|
93
|
+
Перекриття гарантує, що обидва чанки містять спільний контекст, тож retrieval залишається послідовним.
|
|
94
|
+
|
|
95
|
+
Цей компроміс (розмір чанка проти перекриття) є ключовим для ефективності RAG:
|
|
96
|
+
|
|
97
|
+
- Занадто маленький → отримаєте шум.
|
|
98
|
+
- Занадто великий → ви перевантажите розмір контексту.
|
|
99
|
+
|
|
100
|
+
## Крок 3: Генерація embeddings
|
|
101
|
+
|
|
102
|
+
Як тільки документи розбиті на чанки, ми генеруємо **embeddings** — високорозмірні вектори, що представляють кожний чанк.
|
|
103
|
+
|
|
104
|
+
Я використовував модель OpenAI’s text-embedding-3-large, але ви можете використати будь-яку сучасну модель embeddings.
|
|
105
|
+
|
|
106
|
+
**Приклад embedding:**
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
[
|
|
110
|
+
-0.0002630692, -0.029749284, 0.010225477, -0.009224428, -0.0065269712,
|
|
111
|
+
-0.002665544, 0.003214777, 0.04235309, -0.033162255, -0.00080789323,
|
|
112
|
+
//...+1533 елементів
|
|
113
|
+
];
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Кожний вектор — це математичний відбиток тексту, що дозволяє виконувати пошук за схожістю.
|
|
117
|
+
|
|
118
|
+
## Крок 4: Індексування та збереження embeddings
|
|
119
|
+
|
|
120
|
+
Щоб уникнути багаторазової регенерації embeddings, я зберіг їх у embeddings.json.
|
|
121
|
+
|
|
122
|
+
У продакшні ви, ймовірно, захочете використовувати векторну базу даних, наприклад:
|
|
123
|
+
|
|
124
|
+
- Chroma
|
|
125
|
+
- Qdrant
|
|
126
|
+
- Pinecone
|
|
127
|
+
- FAISS, Weaviate, Milvus тощо
|
|
128
|
+
|
|
129
|
+
Векторні БД обробляють індексування, масштабованість і швидкий пошук. Але для мого прототипу локальний JSON працював добре.
|
|
130
|
+
|
|
131
|
+
## Крок 5: Отримання за допомогою **косинусної схожості**
|
|
132
|
+
|
|
133
|
+
Коли користувач ставить запитання:
|
|
134
|
+
|
|
135
|
+
1. Згенерувати embedding для запиту.
|
|
136
|
+
2. Порівняти його з усіма embeddings документа за допомогою **косинусної схожості**.
|
|
137
|
+
3. Залишити лише топ N найбільш схожих chunks.
|
|
138
|
+
|
|
139
|
+
Косинусна схожість вимірює кут між двома векторами. Ідеальне співпадіння має оцінку **1.0**.
|
|
140
|
+
|
|
141
|
+
Таким чином система знаходить найближчі фрагменти документації до запиту.
|
|
142
|
+
|
|
143
|
+
## Крок 6: Розширення (Augmentation) + Генерація
|
|
144
|
+
|
|
145
|
+
Тепер починається магія. Ми беремо топові сегменти та вставляємо їх у **system prompt** для ChatGPT.
|
|
146
|
+
|
|
147
|
+
Це означає, що модель відповідає так, ніби ці сегменти були частиною розмови.
|
|
148
|
+
|
|
149
|
+
Результат: точні, **відповіді, засновані на документації**.
|
|
150
|
+
|
|
151
|
+
## Крок 7: Логування запитів користувачів
|
|
152
|
+
|
|
153
|
+
Це прихована суперсила.
|
|
154
|
+
|
|
155
|
+
Кожне поставлене питання зберігається. З часом ви формуєте набір даних із:
|
|
156
|
+
|
|
157
|
+
- Найчастіших питань (чудово підходить для FAQ)
|
|
158
|
+
- Питань без відповіді (документація відсутня або нечітка)
|
|
159
|
+
- Запитів на функціональність, замаскованих під питання (“Чи інтегрується це з X?”)
|
|
160
|
+
- Нових сценаріїв використання, на які ви не розраховували
|
|
161
|
+
|
|
162
|
+
This turns your RAG assistant into a **continuous user research tool**.
|
|
163
|
+
|
|
164
|
+
## Скільки це коштує?
|
|
165
|
+
|
|
166
|
+
Одне з поширених зауважень щодо RAG — це вартість. Насправді це дивно дешево:
|
|
167
|
+
|
|
168
|
+
- Генерація embeddings для ~200 документів займає близько **5 хвилин** і коштує **1–2 євро**.
|
|
169
|
+
- Пошук по документації повністю безкоштовний.
|
|
170
|
+
- Для запитів ми використовуємо gpt-4o-latest без режиму «thinking». На Intlayer у нас близько **300 запитів у чаті на місяць**, і рахунок за OpenAI API рідко перевищує **$10**.
|
|
171
|
+
|
|
172
|
+
Крім того, можна додати вартість хостингу.
|
|
173
|
+
|
|
174
|
+
## Деталі реалізації
|
|
175
|
+
|
|
176
|
+
Стек:
|
|
177
|
+
|
|
178
|
+
- Монорепозиторій: pnpm workspace
|
|
179
|
+
- Пакет документації: Node.js / TypeScript / OpenAI API
|
|
180
|
+
- Фронтенд: Next.js / React / Tailwind CSS
|
|
181
|
+
- Бекенд: Node.js API route / OpenAI API
|
|
182
|
+
|
|
183
|
+
Пакет `@smart-doc/docs` — це пакет на TypeScript, який відповідає за обробку документації. Коли файл Markdown додається або змінюється, пакет містить скрипт `build`, який перебудовує список документації для кожної мови, генерує embeddings і зберігає їх у файлі `embeddings.json`.
|
|
184
|
+
|
|
185
|
+
Для фронтенду ми використовуємо застосунок Next.js, який надає:
|
|
186
|
+
|
|
187
|
+
- Рендеринг Markdown у HTML
|
|
188
|
+
- Рядок пошуку для знаходження релевантної документації
|
|
189
|
+
- Інтерфейс чат-бота для запитань щодо документації
|
|
190
|
+
|
|
191
|
+
Щоб виконати пошук по документації, Next.js-застосунок містить API-рут, який викликає функцію з пакета `@smart-doc/docs` для отримання фрагментів документації, що відповідають запиту. Використовуючи ці фрагменти, ми можемо повернути список сторінок документації, релевантних до пошуку користувача.
|
|
192
|
+
|
|
193
|
+
Для функціоналу чатбота ми дотримуємося того ж процесу пошуку, але додатково вставляємо отримані фрагменти документації у промпт, який відправляється до ChatGPT.
|
|
194
|
+
|
|
195
|
+
Ось приклад промпту, який надсилається в ChatGPT:
|
|
196
|
+
|
|
197
|
+
System prompt :
|
|
198
|
+
|
|
199
|
+
```txt
|
|
200
|
+
You are a helpful assistant that can answer questions about the Intlayer documentation.
|
|
201
|
+
|
|
202
|
+
Related chunks :
|
|
203
|
+
|
|
204
|
+
-----
|
|
205
|
+
docName: "getting-started"
|
|
206
|
+
docChunk: "1/3"
|
|
207
|
+
docUrl: "https://example.com/docs/uk/getting-started"
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
# Як почати
|
|
211
|
+
|
|
212
|
+
...
|
|
213
|
+
|
|
214
|
+
-----
|
|
215
|
+
docName: "another-doc"
|
|
216
|
+
docChunk: "1/5"
|
|
217
|
+
docUrl: "https://example.com/docs/uk/another-doc"
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
# Ще один документ
|
|
221
|
+
|
|
222
|
+
...
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
User query :
|
|
226
|
+
|
|
227
|
+
```txt
|
|
228
|
+
Як почати?
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Ми використовуємо SSE для потокової передачі відповіді з API-роуту.
|
|
232
|
+
|
|
233
|
+
Як уже згадувалося, ми використовуємо gpt-4-turbo без "thinking" режиму. Відповіді релевантні, а затримки низькі.
|
|
234
|
+
Ми експериментували з gpt-5, але затримка була занадто великою (іноді до 15 секунд на відповідь). Проте ми повернемося до цього в майбутньому.
|
|
235
|
+
|
|
236
|
+
👉 [Спробувати демо тут](https://intlayer.org/doc/why) 👉 [Переглянути шаблон коду на GitHub](https://github.com/aymericzip/smart_doc_RAG)
|
|
237
|
+
|
|
238
|
+
## Подальші кроки
|
|
239
|
+
|
|
240
|
+
Цей проєкт — мінімальна реалізація. Але ви можете розширити його багатьма способами:
|
|
241
|
+
|
|
242
|
+
- MCP server → перемістити функцію пошуку по документації на MCP-сервер, щоб підключати документацію до будь-якого AI-помічника
|
|
243
|
+
|
|
244
|
+
- Vector DBs → масштабувати до мільйонів фрагментів документації
|
|
245
|
+
- LangChain / LlamaIndex → готові фреймворки для RAG-пайплайнів
|
|
246
|
+
- Analytics dashboards → візуалізувати запити користувачів та проблемні місця
|
|
247
|
+
- Multi-source retrieval → витягувати не лише документацію, а й записи з баз даних, пости в блогах, тікети тощо.
|
|
248
|
+
- Покращені підказки → reranking, filtering та гібридний пошук (ключові слова + семантичний)
|
|
249
|
+
|
|
250
|
+
## Обмеження, з якими ми зіткнулися
|
|
251
|
+
|
|
252
|
+
- Розбивка на фрагменти (chunking) та перекриття мають емпіричний характер. Пошук правильного балансу (розмір фрагмента, відсоток перекриття, кількість витягнутих фрагментів) вимагає ітерацій та тестування.
|
|
253
|
+
- Векторні представлення (embeddings) не оновлюються автоматично при зміні документів. Наша система скидає embeddings для файлу лише якщо кількість фрагментів відрізняється від збереженої.
|
|
254
|
+
- У цьому прототипі embeddings зберігаються в JSON. Це підходить для демо, але засмічує репозиторій Git. В продакшні краще використовувати базу даних або спеціалізоване сховище векторів (vector store).
|
|
255
|
+
|
|
256
|
+
## Чому це важливо поза межами документації
|
|
257
|
+
|
|
258
|
+
Цікаво не лише в чат-боті. Це — механізм зворотного зв'язку (feedback loop).
|
|
259
|
+
|
|
260
|
+
З RAG ви не просто відповідаєте на запити:
|
|
261
|
+
|
|
262
|
+
- Ви дізнаєтеся, що плутає користувачів.
|
|
263
|
+
- Ви виявляєте, які функції вони очікують.
|
|
264
|
+
- Ви адаптуєте свою продуктову стратегію на основі реальних запитів.
|
|
265
|
+
|
|
266
|
+
**Приклад:**
|
|
267
|
+
|
|
268
|
+
Уявіть запуск нової фічі й миттєве бачення:
|
|
269
|
+
|
|
270
|
+
- 50% запитань стосуються одного й того ж незрозумілого кроку налаштування
|
|
271
|
+
- Користувачі неодноразово просять інтеграцію, яку ви ще не підтримуєте
|
|
272
|
+
- Люди шукають терміни, що виявляють новий сценарій використання
|
|
273
|
+
|
|
274
|
+
Це — **product intelligence** прямо від ваших користувачів.
|
|
275
|
+
|
|
276
|
+
## Висновок
|
|
277
|
+
|
|
278
|
+
RAG — це один із найпростіших і найпотужніших способів зробити LLMs практичними. Поєднавши **retrieval + generation**, ви можете перетворити статичну документацію на **smart assistant** і, одночасно, отримувати безперервний потік insights про продукт.
|
|
279
|
+
|
|
280
|
+
Для мене цей проєкт показав, що RAG — це не просто технічний трюк. Це спосіб трансформувати документацію у:
|
|
281
|
+
|
|
282
|
+
- інтерактивну систему підтримки
|
|
283
|
+
- канал зворотного зв'язку
|
|
284
|
+
- інструмент для продуктової стратегії
|
|
285
|
+
|
|
286
|
+
👉 [Спробуйте демо тут](https://intlayer.org/doc/why) 👉 [Перегляньте шаблон коду на GitHub](https://github.com/aymericzip/smart_doc_RAG)
|
|
287
|
+
|
|
288
|
+
І якщо ви теж експериментуєте з RAG, мені було б цікаво дізнатися, як ви його використовуєте.
|