Как создать исполняемую последовательность для векторов, хранящихся в Qdrant, с использованием TypeScript?

Вопрос или проблема

Я пытаюсь получить данные из моей коллекции векторов qdrant, но на любой мой запрос он отвечает чем-то вроде: “Предоставленный контекст не содержит информации или содержимого для обмена. Поэтому я не могу предоставить никаких деталей или ответить на ваш вопрос.”

Я действительно не уверен, что я пропускаю, но я знаю следующее:

  • Подключение к нашему qdrant работает нормально
  • В коллекции более 100000 точек, так что данные доступны
  • Что я хочу знать, это:

Является ли RunnableSequence правильным решением здесь или лучше использовать .pipe() для запросов? Как это работает? Я был бы очень благодарен за любые примеры кода, как это заставить работать. 🙏

Вот код, который у меня сейчас есть

import { QdrantVectorStore } from "@langchain/qdrant";
import { z } from "zod";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { ChatOpenAI } from "@langchain/openai";
import { embeddings } from "../clients/openai";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { SelfQueryRetriever } from "langchain/retrievers/self_query";
import { QdrantTranslator } from "@langchain/community/structured_query/qdrant";
import type { Document } from "@langchain/core/documents";
import {
  RunnablePassthrough,
  RunnableSequence,
} from "@langchain/core/runnables";

const completionSchema = z.object({
  response: z.string().describe("Сгенерированный ответ"),
  profitSentiment: z
    .enum(["positive", "negative"])
    .describe(
      "Строка, представляющая, был ли прибыль положительной или отрицательной по сравнению с последним отчетом."
    ),
  profitExplanation: z
    .string()
    .describe(
      "Объяснение прибыли по сравнению с последним отчетом."
    ),
  source: z
    .string()
    .describe("Документ, использованный для генерации ответа"),
});

const attributeInfo = [
  {
    name: "chunk_index",
    description: "индекс для блока",
    type: "number",
  },
  {
    name: "company_id",
    description: "идентификатор компании",
    type: "number",
  },
  {
    name: "company_ticker",
    description: "тикер акций компании",
    type: "string",
  },
  {
    name: "created_at",
    description: "Когда был создан этот контент",
    type: "строка даты",
  },
  {
    name: "document_type_id",
    description: "идентификатор типа документа",
    type: "number",
  },
  {
    name: "file_name",
    description: "Имя файла, являвшегося источником информации",
    type: "string",
  },
  {
    name: "timespan_end",
    description:
      "метка времени, указывающая конец периода, когда информация является актуальной",
    type: "строка даты",
  },
  {
    name: "timespan_start",
    description:
      "метка времени, указывающая начало периода, когда информация является актуальной",
    type: "строка даты",
  },
];

export const runCompletions = async () => {
  const vectorStore = await QdrantVectorStore.fromExistingCollection(
    embeddings,
    {
      url: process.env.QDRANT_API_ENDPOINT!,
      collectionName: "documents_yearly_reports",
    }
  );

  const prompt = ChatPromptTemplate.fromTemplate(`
    Ответьте на вопрос, основываясь только на предоставленном контексте.

    Контекст: {context}

    Вопрос: {question}
  `);

  const formatDocs = (docs: Document[]) => {
    console.log({ docs });

    return docs.map((doc) => JSON.stringify(doc)).join("\n\n");
  };

  const llm = new ChatOpenAI({
    model: "gpt-4o-mini",
    temperature: 0,
  });

  const selfQueryRetriever = SelfQueryRetriever.fromLLM({
    llm,
    vectorStore,
    documentContents:
      "Информация о годовых отчетах торгуемых на бирже компаний в Швеции",
    attributeInfo,
    structuredQueryTranslator: new QdrantTranslator(),
    searchParams: {
      filter: {
        must: [
          {
            key: "metadata.company_id",
            match: {
              value: 435,
            },
          },
        ],
      },
    },
  });

  const ragChain = RunnableSequence.from([
    {
      context: selfQueryRetriever.pipe(formatDocs),
      question: new RunnablePassthrough(),
    },
    prompt,
    llm,
    new StringOutputParser(),
  ]);

  const response = await ragChain.invoke("расскажи мне всё!");

  console.log({ response });
};
## Используемые пакеты

"@langchain/community": "^0.3.1",
"@langchain/core": "^0.3.1",
"@langchain/openai": "^0.3.0",
"@langchain/qdrant": "^0.1.0",
"@langchain/textsplitters": "^0.1.0",
"@qdrant/js-client-rest": "^1.11.0",
"@mozilla/readability": "^0.5.0",
"langchain": "^0.3.2",

Я пытался настроить это в течение 2-3 дней. Все примеры, которые я могу найти, создают необходимые документы, а затем используют их, но у меня уже есть документы в базе данных qdrant, так что я не знаю, как их использовать.

Ответ или решение

Для создания работающей последовательности для векторов, хранящихся в Qdrant с использованием TypeScript, необходимо правильно настроить вашу цепочку (RunnableSequence) и убедиться, что вы корректно выполняете запросы к вашей базе данных. Судя по вашему коду и проблеме, с которой вы столкнулись, есть несколько мест, которые можно улучшить для успешного получения данных.

Вот обновленная версия вашего кода с разъяснениями:

import { QdrantVectorStore } from "@langchain/qdrant";
import { z } from "zod";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { ChatOpenAI } from "@langchain/openai";
import { embeddings } from "../clients/openai";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { SelfQueryRetriever } from "langchain/retrievers/self_query";
import { QdrantTranslator } from "@langchain/community/structured_query/qdrant";
import type { Document } from "@langchain/core/documents";
import {
  RunnablePassthrough,
  RunnableSequence,
} from "@langchain/core/runnables";

// Схема для данных ответа
const completionSchema = z.object({
  response: z.string().describe("Сгенерированный ответ"),
  profitSentiment: z.enum(["positive", "negative"]).describe("Позитивный или негативный показатель прибыли."),
  profitExplanation: z.string().describe("Объяснение прибыли по сравнению с последним отчетом."),
  source: z.string().describe("Документ, использованный для генерации ответа"),
});

// Информация об атрибутах
const attributeInfo = [
  // Определите вашу информацию об атрибутах так же, как и раньше
];

export const runCompletions = async () => {
  // Создание векторного магазина
  const vectorStore = await QdrantVectorStore.fromExistingCollection(
    embeddings,
    {
      url: process.env.QDRANT_API_ENDPOINT!,
      collectionName: "documents_yearly_reports",
    }
  );

  // Шаблон чата
  const prompt = ChatPromptTemplate.fromTemplate(`
    Ответьте на вопрос, основываясь только на предоставленном контексте.

    Контекст: {context}

    Вопрос: {question}
  `);

  // Форматирование документов
  const formatDocs = (docs: Document[]) => {
    return docs.map((doc) => JSON.stringify(doc)).join("\n\n");
  };

  // LLM модель
  const llm = new ChatOpenAI({
    model: "gpt-4o-mini",
    temperature: 0,
  });

  // Создание реквизита с использованием самозапроса
  const selfQueryRetriever = SelfQueryRetriever.fromLLM({
    llm,
    vectorStore,
    documentContents: "Информация о годовых отчетах о торгуемых акциях в Швеции",
    attributeInfo,
    structuredQueryTranslator: new QdrantTranslator(),
    searchParams: {
      filter: {
        must: [
          {
            key: "metadata.company_id",
            match: {
              value: 435,
            },
          },
        ],
      },
    },
  });

  // Создание рабочей цепочки
  const ragChain = RunnableSequence.from([
    {
      context: selfQueryRetriever.pipe(formatDocs),
      question: new RunnablePassthrough(),
    },
    prompt,
    llm,
    new StringOutputParser(),
  ]);

  // Вызов цепочки и получение ответа
  try {
    const response = await ragChain.invoke({ question: "Расскажите мне все!" });

    console.log({ response });
  } catch (error) {
    console.error("Ошибка при вызове цепочки:", error);
  }
};

Примечания:

  1. Используйте invoke правильно: Убедитесь, что вы передаете объект с вопросом в invoke, а не строку. Это важно для правильного понимания вашего вопроса LLM.

  2. Обработка ошибок: Чтобы лучше отследить ошибки, добавьте обработку ошибок вокруг вызова invoke. Это поможет вам выявить, если что-то идет не так.

  3. Атрибуты: Убедитесь, что поля атрибутов, которые вы используете в фильтрах и выводе, точно соответствуют вашим данным в Qdrant.

  4. Проверка данных: Убедитесь, что в вашей коллекции действительно есть документы, которые соответствуют вашему запросу. Попробуйте временно убрать фильтры, чтобы посмотреть, получите ли вы какие-либо документы.

Этот подход должен помочь вам успешно извлекать данные из вашей базы данных Qdrant и отвечать на них с помощью LLM. Если у вас возникнут дополнительные вопросы или проблемы, пожалуйста, не стесняйтесь спрашивать!

Оцените материал
Добавить комментарий

Капча загружается...