Протокол FSS — набросок спецификации
Рабочий черновик спецификации протокола. Финальная версия будет RFC-подобным документом ~30 страниц; здесь — основные абстракции и форматы сообщений, чтобы оценить архитектуру.
Это набросок v0.1, а не финальная спецификация. Имена полей, форматы сообщений, и даже базовые абстракции могут измениться в фазе 1-2 реализации. Не используйте как reference для имплементации.
Узлы и идентификация
Каждый узел FSS — это независимый актор с уникальным идентификатором WebID. WebID — это HTTPS URL, который dereferenceable и возвращает JSON-LD документ с публичным ключом узла и метаданными (имя, описание, поддерживаемые онтологии). Это та же модель, что в Solid, с одним отличием: WebID в FSS указывает не на pod-хранилище, а на FSS-эндпоинт.
{
"@context": "https://fss.example/ns/v0.1",
"@id": "https://alice.example/fss-node",
"@type": "FSSNode",
"name": "Alice Knowledge Graph",
"publicKey": {
"@id": "https://alice.example/fss-node#key",
"@type": "Ed25519Key",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\n..."
},
"inbox": "https://alice.example/fss/inbox",
"outbox": "https://alice.example/fss/outbox",
"digests": "https://alice.example/fss/digests",
"ontologies": ["https://fss.example/ontologies/personal-films/v1"]
}
Узел может быть приватным (WebID не публикуется, обмен только по прямому приглашению) или публичным (WebID в каталоге узлов). По умолчанию — приватный, публикация — явное действие пользователя.
Семантический дайджест
Дайджест — это публично доступное описание того, какие типы сущностей и связей есть в графе узла, без самих данных. Это позволяет другим узлам понять, есть ли смысл делать federated запрос, без раскрытия приватной информации. Дайджест обновляется при изменении графа и подписывается ключом узла.
{
"@context": "https://fss.example/ns/v0.1",
"@type": "FSSDigest",
"node": "https://alice.example/fss-node",
"generatedAt": "2026-08-03T12:00:00Z",
"entityCounts": {
"Person": 247,
"Film": 89,
"Book": 134
},
"predicates": [
"watched", "rated", "read_in", "wrote", "directed_by",
"influenced_by", "similar_to"
],
"ontologies": ["personal-films/v1", "common-books/v2"],
"sampleQueries": [
"films directed by Hitchcock",
"books read in 2024"
],
"signature": "..."
}
Поле sampleQueries — это примеры запросов, на которые узел готов
отвечать. LLM-посредник использует их, чтобы понять, имеет ли смысл обращаться
к этому узлу по конкретной теме. Если узел А имеет 0 фильмов в дайджесте, нет
смысла спрашивать его про Хичкока.
Структура триплета
Триплет в FSS — это расширение классического RDF-триплета с обязательным provenance. Без provenance триплет считается анонимным и имеет confidence 0.0, что делает его непригодным для federated ответов (но пригодным для локального использования как черновик).
{
"@context": "https://fss.example/ns/v0.1",
"@type": "FSSTriple",
"subject": "fss-local:note-187",
"predicate": "rated",
"object": { "@value": 8, "@type": "xsd:integer" },
"subjectLabel": "Birdman (2014)",
"predicateLabel": "оценил(а) на",
"provenance": {
"source": "fss-local:note-187",
"author": "https://alice.example/fss-node",
"extractedAt": "2026-08-03T11:42:00Z",
"extractedBy": "qwen2.5:14b",
"confidence": 0.92,
"signature": "..."
}
}
Поля subjectLabel и predicateLabel — это
человекочитаемые подписи, которые LLM-агент использует при сборке ответа.
Технически они избыточны (можно вывести из онтологии), но без них ответы
читаются как «fss-local:note-187 rated 8» вместо «Birdman (2014) оценил(а) на 8».
Federated запрос
Запрос от узла А к узлу B проходит через LLM-посредник на стороне А, который формирует запрос на естественном языке + указывает нужные онтологии и ожидаемые типы. Узел B принимает запрос, локальный LLM-агент B переводит его в SPARQL к своему графу (с учётом маппинга онтологий), выполняет, возвращает ответ.
// Запрос от Alice к Bob
POST https://bob.example/fss/query
Content-Type: application/json
Authorization: Bearer <capability-token>
X-FSS-Signature: ...
{
"@context": "https://fss.example/ns/v0.1",
"@type": "FSSQuery",
"from": "https://alice.example/fss-node",
"query": "films directed by Hitchcock rated above 7 by you",
"expectedPredicates": ["directed_by", "rated"],
"ontologies": ["personal-films/v1"],
"maxResults": 10,
"includeProvenance": true
}
Ответ содержит массив триплетов с provenance и указанием confidence каждого. LLM-агент Alice собирает финальный ответ пользователю, явно показывая источник каждого утверждения. Если confidence низкий или маппинг онтологий неуверенный, пользователь видит предупреждение.
{
"@type": "FSSQueryResponse",
"queryId": "q-2026-08-03-001",
"results": [
{
"subject": "Vertigo (1958)",
"predicate": "rated",
"object": 9,
"provenance": {
"author": "https://bob.example/fss-node",
"confidence": 0.95,
"ontologyMapping": "rated → rated (direct match)"
}
}
],
"mappingsUsed": [
{ "from": "rated", "to": "rated", "confidence": 1.0 }
],
"partial": false
}
Маппинги онтологий
Маппинг — это двустороннее соответствие между предикатами двух онтологий. Маппинги бывают трёх типов: эквивалентность (1:1, confidence 1.0), частичное соответствие (confidence 0.5-0.9, есть нюансы), отсутствие маппинга (0.0, предикаты несравнимы). Все маппинги кэшируются на обеих сторонах и обновляются при изменении онтологий.
{
"@type": "FSSMapping",
"nodeA": "https://alice.example/fss-node",
"nodeB": "https://bob.example/fss-node",
"predicateA": "написан_в",
"predicateB": "year_published",
"confidence": 0.85,
"notes": "Алиса использует написан_в для даты рукописи и публикации; Боб только для публикации",
"verifiedAt": "2026-08-03T12:00:00Z",
"verifiedBy": "qwen2.5:14b + alice-confirmation",
"expiresAt": "2026-11-03T12:00:00Z"
}
Маппинги — самая инновационная часть протокола. Они не статичны: LLM-посредник регулярно перероверяет их через тестовые запросы («отвечают ли оба узла одинаково на запрос X?»). Если ответы расходятся, confidence снижается, и при следующем federated запросе пользователь видит предупреждение.
Подписи и доверие
Каждое сообщение в FSS подписывается Ed25519-ключом отправителя. Подпись
покрывает канонизированный JSON-LD (через Universal RDF Dataset Canonicalization).
Это даёт две гарантии: авторство (сообщение действительно от того, кто указан
в from) и целостность (сообщение не было изменено в пути).
Доверие — отдельный слой, не встроенный в протокол. Узел может вести белый список доверенных узлов, чьи утверждения принимаются с confidence 1.0. Узлы вне белого списка принимаются с понижающим коэффициентом (например, 0.5), если у них есть валидная подпись, и игнорируются, если подписи нет. Репутация строится как социальный граф доверия: если Алиса доверяет Бобу, а Боб — Кэрол, то Алиса может принять утверждение Кэрол с коэффициентом 0.7.
Жизненный цикл взаимодействия
- Обнаружение: узел А находит WebID узла B через каталог или прямое приглашение
- Знакомство: А запрашивает дайджест B, LLM-посредник сравнивает онтологии, предлагает маппинги
- Подтверждение маппингов: пользователь А подтверждает (или отклоняет) предложенные маппинги
- Запрос: при необходимости А делает federated запрос к B
- Ответ: B возвращает триплеты с provenance, А собирает ответ пользователю
- Кэширование: A кэширует полученные триплеты с указанием source = B, до истечения TTL
- Переоверка: периодически A переоверяет маппинги через тестовые запросы
Открытые вопросы
Несколько вопросов пока не решены и требуют экспериментов в фазе 1-2. Как
обрабатывать конфликты триплетов от разных узлов? Если Алиса говорит «фильм
вышел в 1958», а Боб — «в 1959», что показывать пользователю? Текущая идея —
показывать оба с указанием источников и confidence, но UX этого неочевиден.
Как предотвратить утечку приватных данных через federated ответы? Если узел
случайно публикует в дайджесте предикат medical_condition, это
уже утечка. Нужны явные redaction-политики. Как версионировать онтологии?
Если Алиса обновляет personal-films/v1 до v2, что происходит с
маппингами, основанными на v1?
Эти вопросы — нормальная часть проектирования протокола. Часть из них будет решена в фазе 1 (прототип), часть — в фазе 2 (протокол), часть потребует реального опыта использования и будет дорабатываться в фазе 3-4. Главное — не пытаться решить всё заранее, но и не закрывать глаза на реальные проблемы.