Protein Research Copilot на Amazon Bedrock AgentCore и ESM-C 300M | AiManual
AiManual Logo Ai / Manual.
28 Июн 2026 Гайд

Строим копилота для исследований белков: естественные запросы и векторный поиск с Amazon Bedrock AgentCore

Пошаговый гайд по созданию AI-ассистента для биоинформатики: векторный поиск по белковым последовательностям, кастомные эмбеддинги ESM-C 300M, pgvector и Strand

Реклама
cliv2

Биоинформатики тонут в данных. Белковые последовательности, структуры, функции - объём информации растёт экспоненциально, а инструменты для поиска остаются архаичными. BLAST, HMMER, командная строка. Учёный хочет спросить: "Покажи все рецепторы, похожие на этот пептид с уровнем гомологии > 80%", а не писать SQL-подобные запросы к базе Swiss-Prot.

Проблема не в отсутствии LLM, а в том, что обычный RAG не умеет работать с последовательностями аминокислот. Токенизатор GPT разобьёт "MVLSEGEWQLVLHVWAKVEADVAGHGQDILIRLFKSHPETLEKFDRFKHLKTEAEMKASEDLKKHGVTVLTALGAILKKKGHHEAELKPLAQSHATKHKIPIKYLEFISEAIIHVLHSRHPGNFGADAQGAMNKALELFRKDIAAKYKELGYQG" в мусор. Нужен специализированный эмбеддер - модель, обученная на миллионах белков. Таким стал ESM-C 300M от Meta, последняя версия на июнь 2026 года.

Суть подхода: объединяем кастомные эмбеддинги (ESM-C 300M) с векторным индексом pgvector, а поверх ставим агента на Amazon Bedrock AgentCore, который понимает естественный язык и умеет разбивать сложные запросы на подзадачи с помощью Strands Agents SDK. Никакого копирования последовательностей вручную.

Почему обычный RAG тут не взлетит

Типичный RAG с OpenAI embeddings или Amazon Titan Text Embeddings даёт осмысленные векторы для текста, но не для биологических последовательностей. Два белка могут иметь 50% идентичности, но их текстовые описания будут разными. Модели вроде ESM (Evolutionary Scale Modeling) кодируют эволюционный контекст: мутации, гомологию, структуру. ESM-C 300M - это контрастивная версия, оптимизированная под поиск похожих последовательностей. Она выдаёт эмбеддинги размером 1280, где расстояние L2 коррелирует с биологической близостью.

Второй нюанс - запросы. Биолог не пишет "similarity > 0.9". Он говорит: "Найди белки с доменом SH3, которые связывают протеинкиназу A". Тут нужен агент, способный распарсить естественный язык, извлечь ключевые сущности (домен, функция, организм) и скомбинировать векторный поиск с фильтрацией по метаданным. Гибридный RAG спасает, но в нашем случае векторный поиск идёт не по тексту, а по биологическим эмбеддингам.

Архитектура: от запроса до ответа

Вот как выглядит итоговый пайплайн (без лишних деталей, только суть):

  1. Пользователь пишет: "Покажи пять белков человека, похожих по структуре на этот пептид: MVLSEGEWQLVL..."
  2. Бедрок-агент (Amazon Bedrock AgentCore) получает запрос, определяет, что нужен action group для поиска белков.
  3. Strands Agents SDK разбивает задачу: извлечь последовательность, задать organism=human, top_k=5.
  4. Серверная функция (Lambda или контейнер) запускает инференс ESM-C 300M для эмбеддинга запроса.
  5. Выполняется ANN-поиск в pgvector (Amazon RDS for PostgreSQL с расширением pgvector). Фильтр по organism='human'.
  6. Результаты возвращаются агенту, он форматирует ответ с аннотациями UniProt.

Всё это работает в реальном времени за 2-3 секунды. Сравните с ручным запуском BLAST+.

Шаг за шагом: строим своими руками

1 Подготовка окружения и данные

Нам понадобятся: аккаунт AWS (разумеется), IAM роль с правами на Bedrock, Lambda, RDS. Локально ставим Python 3.12, esm (последняя версия от Meta, pip install esm), psycopg2-binary, boto3, strands-agents-sdk (pip install strands-agents-sdk). Версии на июнь 2026: esm 3.2, strands-agents-sdk 1.5.

Качаем подмножество Swiss-Prot (около 50 тысяч последовательностей) в формате FASTA. Можно взять из S3 публичного датасета. Для теста хватит.

2 Генерация эмбеддингов через ESM-C 300M

Используем предобученную модель esm_c_300m. Она принимает последовательность, возвращает тензор 1280 float. Важно: нормализуем векторы перед записью в pgvector (L2 нормализация, чтобы использовать косинусное расстояние через косинус).

import esm
model, alphabet = esm.pretrained.esm_c_300m()
batch_converter = alphabet.get_batch_converter()

def embed_sequence(seq: str):
    data = [("protein", seq)]
    _, _, tokens = batch_converter(data)
    with torch.no_grad():
        results = model(tokens, repr_layers=[36], return_contacts=False)
    token_representations = results["representations"][36][0, 1:-1].mean(axis=0).numpy()  # средний пулинг
    norm = np.linalg.norm(token_representations)
    return token_representations / norm if norm != 0 else token_representations

Подводный камень: ESM-C 300M требует GPU для быстрой работы. На CPU один эмбеддинг будет считаться 5-10 секунд. На Batch на g4dn.xlarge (T4) - около 100 последовательностей в секунду. Учитывайте это при масштабировании.

3 Наполняем pgvector

Создаём экземпляр RDS for PostgreSQL 16 с расширением pgvector (версия 0.7.4). Таблица:

CREATE EXTENSION vector;
CREATE TABLE proteins (
    id SERIAL PRIMARY KEY,
    uniprot_id TEXT UNIQUE,
    sequence TEXT,
    organism TEXT,
    description TEXT,
    embedding vector(1280)
);
CREATE INDEX ON proteins USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);

Загружаем данные батчами. Код в Lambda: получает эмбеддинги из S3 (предварительно рассчитанные) и вставляет через executemany.

4 Создаём Agent в Amazon Bedrock AgentCore с action group

В Bedrock AgentCore определяем агента с инструкцией: "You are a protein research assistant. You can search for similar proteins, retrieve sequences, and filter by organism.". Создаём action group с OpenAPI-схемой, указывающей эндпоинт Lambda: POST /search с параметрами (sequence, top_k, organism). Как добавлять action groups с MCP - полезно для расширения.

Lambda-функция: принимает запрос, извлекает последовательность, вызывает ESM-C (можно на том же инстансе, если используем контейнер с GPU), выполняет pgvector запрос:

import psycopg2, json, numpy as np

def lambda_handler(event, context):
    body = json.loads(event['body'])
    query_vec = embed_sequence(body['sequence'])
    conn = psycopg2.connect(...)
    cur = conn.cursor()
    cur.execute("""
        SELECT uniprot_id, description, 1 - (embedding <=> %s::vector) AS similarity
        FROM proteins
        WHERE organism = %s
        ORDER BY embedding <=> %s::vector
        LIMIT %s
    """, (query_vec.tolist(), body.get('organism', '%'), query_vec.tolist(), body['top_k']))
    rows = cur.fetchall()
    return {'statusCode': 200, 'body': json.dumps(rows)}

5 Интеграция со Strands Agents SDK для многозадачности

Сам по себе Bedrock AgentCore умеет вызывать один action group за раз. Но сложный запрос вроде "сравни три пептида и выведи, какой из них наиболее эволюционно консервативен" требует цепочки действий: найти гомологи для каждого, посчитать консервативность. Strands Agents SDK позволяет описать граф задач. Устанавливаем его на Lambda (или отдельный microservice).

from strands.agents import Agent, Task

class ProteinResearchAgent(Agent):
    def build_plan(self, query):
        tasks = []
        if "compare" in query:
            tasks.append(Task("extract_peptides"))
            tasks.append(Task("find_orthologs"))
            tasks.append(Task("compute_conservation"))
        else:
            tasks.append(Task("vector_search"))
        return tasks

Агент на Bedrock вызывает Strands как action, Strands возвращает результат. Пример создания кино-агента показывает похожую логику с разбивкой.

Ошибки, которые я видел в каждом втором проекте

  • Забыли нормализовать эмбеддинги. pgvector при cosine distance сам не нормализует, если вы сохраняете с нормализацией - всё ок. Но если не нормализовать, поиск будет по L2, а не по косинусу. Хуже того, некоторые модели выдают разные по длине векторы.
  • Выбрали IVFFlat с неправильным lists. Для 50к записей lists=100 норм, но для >1M нужно пересчитывать. Используйте ivfflat только для продакшна с настройкой probes. Для прототипа hnsw быстрее, но требует больше памяти.
  • Lambda холодный старт с моделью ESM-C. Загрузка модели 2-3 ГБ. Используйте Elastic Inference или контейнер на ECS/Fargate с сохранением тёплого пула. Либо предрасчёт эмбеддингов и хранение в таблице - поиск быстрее.
  • Не настроили таймауты для агента. Bedrock AgentCore по умолчанию ждёт 30 секунд. Если инференс ESM-C занимает 40 секунд - получите ошибку. Увеличьте timeout до 120 секунд в конфигурации action group.

Совет: Для production используйте отдельный кластер RDS с read replica для векторного поиска, чтобы не тормозить основные транзакции. И кэшируйте эмбеддинги популярных запросов в ElastiCache Redis - ускоряет повторные запросы на 90%.

Неочевидный трюк: гибридный поиск + эмбеддинги ESM

Чисто векторный поиск хорош, но иногда нужна точная подстрока (например, найти конкретный мотив). Гибридный поиск решает: pgvector поддерживает полнотекстовый поиск (tsvector) одновременно с векторами.

SELECT uniprot_id, description
FROM proteins
WHERE to_tsvector('english', description) @@ plainto_tsquery('kinase')
  AND organism = 'human'
ORDER BY embedding <=> %s::vector
LIMIT 10;

Тогда агент может ответить на запрос: "Найди человеческие киназы, похожие на этот белок". Комбинируем фильтр по тексту и векторную близость. Если хотите полный дашборд для биологов - FAST-шаблон даёт фронтенд и деплой одним стеком.

И последний совет, который сэкономит вам неделю: не пытайтесь воткнуть ESM-C 300M в Lambda напрямую. Используйте контейнер на ECS с GPU Spot - это в 4 раза дешевле, чем on-demand. И закэшируйте эмбеддинги всех последовательностей из Swiss-Prot в S3 при первом запуске. Поверьте, пересчитывать их каждый раз при редеплое - тот ещё мазохизм.

Подписаться на канал