Pull to refresh

Comments 5

Какая LLM позволит дать ответ в точности с переданной JSON схемой?

Так как шаблонизаторы движков open source языковых моделей не очень, я бы рекомендовал вам использовать для этого рекурсивный вызов инструмента с наводящими вопросами.

Доверять параметру enum так же не стоит, он сериализуется на вход языковой моделе, но не проверяется движком, который запускает gguf, на своей стороне

import {
  addTool,
  commitToolOutput,
  commitToolOutputForce,
  execute,
  Schema,
} from "agent-swarm-kit";
import { str, Subject } from "functools-kit";
import { z } from "zod";
import { ToolName } from "src/enum/ToolName";
import commitAppAction from "src/utils/commitAppAction";
import { IPizzaOrder } from "src/model/interfaces";

// См https://deepwiki.com/tripolskypetr/functools-kit/3.2-subject-and-behaviorsubject
const pizzaSubject = new Subject<{ district: string; size: string; clientId: string }>()

// Список районов Москвы
const MOSCOW_DISTRICTS = [
  "АРБАТ",
  "БАСМАННЫЙ",
  "ЗАМОСКВОРЕЧЬЕ",
  "КРАСНОГОРСК",
  "ПРЕСНЕНСКИЙ",
  "ТАГАНСКИЙ",
  "ТВЕРСКОЙ",
  "ХАМОВНИКИ",
  "МЕЩАНСКИЙ",
  "ЯКИМАНКА",
];

// Доступные размеры пиццы в сантиметрах
const PIZZA_SIZES = [25, 30, 35, 40];

addTool({
  toolName: ToolName.OrderPizzaTool,
  type: "function",
  call: async ({ toolId, clientId, agentName, params }) => {
    const { district, size } = params;

    // Проверка наличия района
    if (!district) {
      await commitToolOutput(
        toolId,
        "Пожалуйста, укажите район Москвы для доставки пиццы",
        clientId,
        agentName
      );
      await execute(`Узнай недостающие данные и вызови инструмент ${ToolName.OrderPizzaTool} еще раз!`, clientId, agentName);
      return;
    }

    // Проверка наличия размера
    if (!size) {
      await commitToolOutput(
        toolId,
        "Пожалуйста, укажите размер пиццы в сантиметрах",
        clientId,
        agentName
      );
      await execute(`Узнай недостающие данные и вызови инструмент ${ToolName.OrderPizzaTool} еще раз!`, clientId, agentName);
      return;
    }

    // Проверка валидности района
    if (!MOSCOW_DISTRICTS.includes(String(district).toUpperCase())) {
      await commitToolOutput(
        toolId,
        str.newline("Доставка возможна только в следующие районы:", MOSCOW_DISTRICTS),
        clientId,
        agentName
      );
      await execute(`Попроси пользователя указать верный район для доставки. Как получишь новый район, вызови инструмент ${ToolName.OrderPizzaTool} еще раз`, clientId, agentName);
      return;
    }

    // Проверка валидности размера
    if (!PIZZA_SIZES.includes(Number(size))) {
      await commitToolOutput(
        toolId,
        str.newline("Доступны только следующие размеры пиццы (в см):", PIZZA_SIZES),
        clientId,
        agentName
      );
      await execute(`Попроси пользователя указать верный размер пиццы. Как получишь новый район, вызови инструмент ${ToolName.OrderPizzaTool} еще раз`, clientId, agentName);
      return;
    }

    // Сохранение данных заказа в памяти для последующих заказов
    await Schema.updateSessionMemory(clientId, {
      district: district,
      size: size,
    });

    // Логирование параметров заказа
    console.log(`Заказ пиццы: Район - ${district}, Размер - ${size} см`);

    // Отправка действия для обработки заказа
    await pizzaSubject.next({ clientId, district, size })

    // Уведомление об успешном оформлении заказа
    await commitToolOutput(
      toolId,
      str.newline(["Заказ пиццы успешно оформлен. Перейди к оплате."]),
      clientId,
      agentName
    );
    await execute(`Заказ сформирован успешно. Более не вызывай инструмент ${oolName.OrderPizzaTool}`, clientId, agentName);
  },
  function: {
    name: ToolName.OrderPizzaTool,
    description: "Инструмент для заказа пиццы с доставкой по Москве",
    parameters: {
      type: "object",
      properties: {
        district: {
          type: "string",
          description: "Район Москвы для доставки пиццы",
          enum: MOSCOW_DISTRICTS,
        },
        size: {
          type: "number",
          description: "Размер пиццы в сантиметрах",
          enum: PIZZA_SIZES,
        },
      },
      required: ["district", "size"],
    },
  },
});

Вам потребуется использовать этот код внутри context scope, чтобы задать отдельный subject на каждый запрос пользователя. Более подробно по ссылке

посмотрите outlines. ollama его поддерживает из коробки.

про plutus не знал, надо смотреть

ОЧЕНЬ! Вредный совет

Если говнодвижок, в том числа в облаке с оплатой за токены, не может спарсить JSON, он буквально сделает while(true) на ваши деньги!

{"level":30,"time":1751304487079,"pid":17864,"hostname":"DESKTOP-UDO3RQB","logLevel":"log","createdAt":"2025-06-30T17:28:07.079Z","createdBy":"http_predict.log","args":["/predict_1m error",{"request":{"requestId":"16ea422e-33e9-404f-9c42-e330edd2a515","serviceName":"trend-app","symbol":"XRPUSDT"},"error":{"stack":"AI_NoObjectGeneratedError: No object generated: could not parse the response.\n    at processResult (file:///C:/Users/User/Documents/GitHub/signals/node_modules/ai/dist/index.mjs:2965:17)\n    at fn (file:///C:/Users/User/Documents/GitHub/signals/node_modules/ai/dist/index.mjs:2996:19)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n    at async file:///C:/Users/User/Documents/GitHub/signals/node_modules/ai/dist/index.mjs:470:22\n    at async predictCryptoTrend.U.maxExec (file:///C:/Users/User/Documents/GitHub/signals/build/index.mjs:1:24906)\n    at async execute (file:///C:/Users/User/Documents/GitHub/signals/node_modules/functools-kit/build/index.mjs:589:16)\n    at async InstanceAccessor.wrappedFn [as predictCryptoTrend] (file:///C:/Users/User/Documents/GitHub/signals/node_modules/functools-kit/build/index.mjs:621:13)\n    at async file:///C:/Users/User/Documents/GitHub/signals/build/index.mjs:1:327933\n    at async dispatch (file:///C:/Users/User/Documents/GitHub/signals/node_modules/hono/dist/compose.js:22:17)\n    at async dispatch (file:///C:/Users/User/Documents/GitHub/signals/node_modules/hono/dist/compose.js:22:17)","message":"No object generated: could not parse the response.","name":"AI_NoObjectGeneratedError","cause":{"name":"AI_JSONParseError", ... ,"response":{"id":"07575e7b-03d2-aa75-d762-2f81141fc60e","timestamp":"2025-06-30T17:25:05.000Z","modelId":"grok-3"},"usage":{"promptTokens":1711,"completionTokens":112,"totalTokens":1823},"finishReason":"stop"}}]}


Sign up to leave a comment.

Articles