Pull to refresh

Comments 13

Спасибо за статью, очень интересный подход. Хотелось бы чуть лучше понять механику wiki_graph_context.

Правильно ли я понимаю, что сначала находятся seed-страницы через vector search, а потом к ним добавляются соседние страницы из графа? Если да, то интересно, как вы дедуплицируете seed/hop-страницы, как выбираете глубину обхода, откуда берутся веса для разных типов рёбер, и что именно отдаёте LLM – полный текст страниц или только релевантные фрагменты?

И ещё вопрос. Планируете ли вы где-нибудь выложить код (например, на GitHub)?

Да, именно так — двухэтапный retrieval. Вот механика:

Seed: Запрос - эмбеддинг - cosine similarity находит top-K (стоит хардкодом 5) страниц из graph_nodes. Это seed-ноды.

Hop : Каждая seed-нода раскрывается через graph_edges — прямые соседи (hop1) и соседи соседей (hop2). Глубина по умолчанию 1.

Дедупликация: SELECT DISTINCT ON (wp.path) — если одна и та же страница пришла и как seed, и как hop, остаётся seed (приоритет по relevance: seed > hop1 > hop2).

Веса: заранее заданная таблица внутри SQL-функции:

depends_on = 0.95, develops = 0.9, part_of = 0.85,
based_on = 0.8, alternative_to = 0.75, contradicts = 0.7,
authored_by = 0.5, tagged = 0.4, mentions = 0.3

Rank: rank_score = similarity × edge_weight / (depth + 1). У seed-нод rank = чистый cosine (edge_weight=1, depth=0). У hop-нод — discount по типу ребра и глубине.

Контент для LLM: страницы, обрезанные по relevance:

Общий лимит max_chars = 8000 символов на весь результат.

seed: left(content, max_chars / top_k)
hop1: 500 символов
hop2: 200 символов

Если нужен код, скажите ваш гитхаб аккаунт, я вас добавлю

Содержательно. Это именно то, что я хотел узнать. Спасибо за ответ.
Мой GitHub.

пригласил

Да, именно так — двухэтапный retrieval. Вот механика:

Seed: Запрос - эмбеддинг - cosine similarity находит top-K (стоит хардкодом 5) страниц из graph_nodes. Это seed-ноды.

Hop : Каждая seed-нода раскрывается через graph_edges — прямые соседи (hop1) и соседи соседей (hop2). Глубина по умолчанию 1.

Дедупликация: SELECT DISTINCT ON (wp.path) — если одна и та же страница пришла и как seed, и как hop, остаётся seed (приоритет по relevance: seed > hop1 > hop2).

Веса: заранее заданная таблица внутри SQL-функции:

depends_on = 0.95, develops = 0.9, part_of = 0.85,
based_on = 0.8, alternative_to = 0.75, contradicts = 0.7,
authored_by = 0.5, tagged = 0.4, mentions = 0.3

Rank: rank_score = similarity × edge_weight / (depth + 1). У seed-нод rank = чистый cosine (edge_weight=1, depth=0). У hop-нод — discount по типу ребра и глубине.

Контент для LLM: страницы, обрезанные по relevance:

Общий лимит max_chars = 8000 символов на весь результат.

seed: left(content, max_chars / top_k)
hop1: 500 символов
hop2: 200 символов

Если нужен код, скажите ваш гитхаб аккаунт, я вас добавлю


Сначала показалось, что выдумывал сам что-то похожее...
но у меня всё без Вики и нейросетей...

Полной реализации нет, только визуализация вывода
https://www.walks.ru/wm_dr/
(лучше смотреть на большом экране)

Понравилось, как вы классифицируете связи через ai.generate() прямо в SQL, это красиво. Но это же LLM, она каждый раз отвечает по разному. Прогоните на новой модели и часть связей сменит тип, не угадаешь что выберет «зависит от» или «использует» поставит.

Поэтому версионирование рёбер, которое у вас пока в планах, я бы не откладывал, раз типы расставляет модель, надо хранить не только когда связь создана, но и какой моделью. Иначе граф будет плавать от прогона к прогону, и те самые 46 -> 68% уже не получатся.

Я в своем решении, запоминаю ответ модельки и переклассифицирую только вручную, когда понадобится, а не на каждом проходе.

И отдельный плюс за то, что права разруливаются через сам список инструментов, а не проверками внутри. Чего в списке нет, того агент не вызовет, даже случайно. Пожалуй стырю подход)

вообще правильнее было бы делать полноценные triples subject-predicat-object на входе и писать классическую онтологию, но это уже вообще другая задача. Здесь связи классифицируются только чтобы расставить веса для ранжирования выдачи, и эти веса не вносят очень большого вклада. Больше урона здесь наносит то, что все связи направленные, а функция классификации выбирает концепты как source и target в порядке их следования, и только потом начинает классифицировать связи между ними конечным списком классов отношений. То есть, если название технологии попадет в source а автор попадет в target, то связь "authored_by" там ляжет правильно, а если они зайдут в функцию наоборот, то связи "author_of" там нет.
Ну это всё будем допиливать, пока и так ищется, просто хопов в контекст попадает больше, токены лишние тратятся

Добавил.
Интересно, а как получилось, что у меня два ваших аккаунта в гитхабе отображается на один ник?

Спасибо! Пробежался по коду очень много интересных моментов, попробую применить на свою личную систему заметок.
А на счёт моего двойного аккаунта, если честно я сам не уверен как так вышло, у меня был в какой-то момент гх аккаунт учебный, личный и рабочий. Как-то неправильно выполнился merge аккаунтов и вот так теперь происходит.

Sign up to leave a comment.

Articles