Вступ
Ця стаття описує, як використовувати вже доступні вихідні коди з середовища DSCE (IBM Digital Self-Serve Co-Create Experience) і створювати власні рішення.
Що таке платформа DSCE?
DSCE — це підприємницька платформа від IBM, що спеціалізується на штучному інтелекті (AI) та даних, і призначена для використання фундаментальних моделей та машинного навчання. Іншими словами, DSCE — це місце, де користувачі вибирають варіант застосування для роботи з живими додатками, створеними за допомогою watsonx. Розробники отримують доступ до інструментів для вибору підказок і побудови рішень, а також до зразків коду додатків, що допомагають прискорити проекти за допомогою watsonx.ai.
Використання AI агентів
Одним з випадків використання на платформі є демонстраційний додаток, що показує агентський підхід із застосуванням великих мовних моделей (LLM); “Оснащення вашого продукту штучним інтелектом від IBM”. Щоб ознайомитися з цим підходом і випадком використання, можна запустити агента на сайті DSCE, однак також можна виконати код додатку локально для внесення змін, що й описано в цій статті.
Щоб створити локальний додаток, просто натискаємо кнопку "Get the code", що перенаправить користувача до публічного репозиторію “https://github.com/IBM/dsce-sample-apps/tree/main/explore-industry-specific-agents”.
Створення додатка локально
Першим кроком є створення проекту з двома частинами додатку: бекендом і фронтендом. Обидві частини написані на “node.js”, що полегшує їх адаптацію. Наприкінці архітектура додатку виглядатиме так.
Локально serverless Code Engine не використовується.
Важливо встановити правильну версію node.js локально, як завантаживши через менеджер пакунків (https://nodejs.org/en/download/package-manager), так і використовуючи командні рядки.
#!/bin/sh
# Завантаження і встановлення nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# Завантаження і встановлення Node.js:
nvm install 22
# Перевірка версії Node.js:
node -v # Має вивести "v22.12.0".
nvm current # Має вивести "v22.12.0".
# Перевірка версії npm:
npm -v # Має вивести "10.9.0".
Локальний проект, який я створив, має таку структуру.
Бекенд додатку та специфічні налаштування
/**
* Copyright 2024 IBM Corp.
*
* Ліцензовано згідно з Apache License, Version 2.0 (ліцензія);
* ви не можете використовувати цей файл, за винятком випадків, передбачених ліцензією.
* Копію ліцензії можна отримати за адресою
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Якщо тільки інше не передбачено чинним законодавством або не узгоджено письмово, програмне забезпечення
* розповсюджується "ТАК, ЯК Є", БЕЗ БУДЬ-ЯКИХ ГАРАНТІЙ ТА УМОВ.
* Див. ліцензію для деталей про права та обмеження.
*/
*/
import { PromptTemplate } from "bee-agent-framework";
import { BaseMessage } from "bee-agent-framework/llms/primitives/message";
import { ValueError } from "bee-agent-framework/errors";
import { isDefined, mapToObj, pickBy } from "remeda";
import { z } from "zod";
import { toBoundedFunction } from "bee-agent-framework/serializer/utils";
export type LLMChatPromptTemplate = PromptTemplate.infer<{ messages: Record[] }>;
export interface LLMChatTemplate {
template: LLMChatPromptTemplate;
messagesToPrompt: (template: LLMChatPromptTemplate) => (messages: BaseMessage[]) => string;
parameters: {
stop_sequence: string[];
};
}
export function messagesToPromptFactory(rolesOverride: Record = {}) {
const roles: Record = pickBy(
{
system: "system",
user: "user",
assistant: "assistant",
...rolesOverride,
},
isDefined,
);
return (template: LLMChatPromptTemplate) => {
return toBoundedFunction(
(messages: BaseMessage[]) => {
return template.render({
messages: messages.map((message) =>
Object.fromEntries(
Object.entries(roles).map(([key, role]) =>
message.role === role ? [key, [message.text]] : [key, []],
),
),
),
});
},
[
{
name: "template",
value: template,
},
{
name: "roles",
value: roles,
},
],
);
};
}
export function templateSchemaFactory(roles: readonly string[]) {
return z.object({
messages: z.array(z.object(mapToObj(roles, (role) => [role, z.array(z.string())] as const))),
});
}
const llama31: LLMChatTemplate = {
template: new PromptTemplate({
schema: templateSchemaFactory(["system", "user", "assistant", "ipython"] as const),
template: `{{#messages}}{{#system}}<|begin_of_text|><|start_header_id|>system<|end_header_id|>
{{system}}<|eot_id|>{{/system}}{{#user}}<|start_header_id|>user<|end_header_id|>
{{user}}<|eot_id|>{{/user}}{{#assistant}}<|start_header_id|>assistant<|end_header_id|>
{{assistant}}<|eot_id|>{{/assistant}}{{#ipython}}<|start_header_id|>ipython<|end_header_id|>
{{ipython}}<|eot_id|>{{/ipython}}{{/messages}}<|start_header_id|>assistant<|end_header_id|>
`,
}),
messagesToPrompt: messagesToPromptFactory({ ipython: "ipython" }),
parameters: {
stop_sequence: ["<|eot_id|>"],
},
};
const llama33: LLMChatTemplate = llama31;
const llama3: LLMChatTemplate = {
template: new PromptTemplate({
schema: templateSchemaFactory(["system", "user", "assistant"] as const),
template: `{{#messages}}{{#system}}<|begin_of_text|><|start_header_id|>system<|end_header_id|>
{{system}}<|eot_id|>{{/system}}{{#user}}<|start_header_id|>user<|end_header_id|>
{{user}}<|eot_id|>{{/user}}{{#assistant}}<|start_header_id|>assistant<|end_header_id|>
{{assistant}}<|eot_id|>{{/assistant}}{{/messages}}<|start_header_id|>assistant<|end_header_id|>
`,
}),
messagesToPrompt: messagesToPromptFactory(),
parameters: {
stop_sequence: ["<|eot_id|>"],
},
};
const granite3Instruct: LLMChatTemplate = {
template: new PromptTemplate({
schema: templateSchemaFactory([
"system",
"user",
"assistant",
"available_tools",
"tool_call",
"tool_response",
] as const),
template: `{{#messages}}{{#system}}<|start_of_role|>system<|end_of_role|>
{{system}}<|end_of_text|>
{{ end }}{{/system}}{{#available_tools}}<|start_of_role|>available_tools<|end_of_role|>
{{available_tools}}<|end_of_text|>
{{ end }}{{/available_tools}}{{#user}}<|start_of_role|>user<|end_of_role|>
{{user}}<|end_of_text|>
{{ end }}{{/user}}{{#assistant}}<|start_of_role|>assistant<|end_of_role|>
{{assistant}}<|end_of_text|>
{{ end }}{{/assistant}}{{#tool_call}}<|start_of_role|>assistant<|end_of_role|><|tool_call|>
{{tool_call}}<|end_of_text|>
{{ end }}{{/tool_call}}{{#tool_response}}<|start_of_role|>tool_response<|end_of_role|>
{{tool_response}}<|end_of_text|>
{{ end }}{{/tool_response}}{{/messages}}<|start_of_role|>assistant<|end_of_role|>
`,
}),
messagesToPrompt: messagesToPromptFactory({
available_tools: "available_tools",
tool_response: "tool_response",
tool_call: "tool_call",
}),
parameters: {
stop_sequence: ["<|end_of_text|>"],
},
};
export class LLMChatTemplates {
protected static readonly registry = {
"llama3.3": llama33,
"llama3.1": llama31,
"llama3": llama3,
"granite3Instruct": granite3Instruct,
};
static register(model: string, template: LLMChatTemplate, override = false) {
if (model in this.registry && !override) {
throw new ValueError(`Template for model '${model}' already exists!`);
}
this.registry[model as keyof typeof this.registry] = template;
}
static has(model: string): boolean {
return Boolean(model && model in this.registry);
}
static get(model: keyof typeof LLMChatTemplates.registry): LLMChatTemplate;
// eslint-disable-next-line @typescript-eslint/unified-signatures
static get(model: string): LLMChatTemplate;
static get(model: string): LLMChatTemplate {
if (!this.has(model)) {
throw new ValueError(`Template for model '${model}' not found!`, [], {
context: {
validModels: Object.keys(this.registry),
},
});
}
return this.registry[model as keyof typeof this.registry];
}
}
...
Для node-backend потрібен файл “.env”, щоб надати необхідну інформацію для watsonx.ai.
IBM_CLOUD_API_KEY="your-ibm-cloud-api-key"
WX_PROJECT_ID="your-watsonx-project-id"
WX_ENDPOINT=https://us-south.ml.cloud.ibm.com
Виконайте наступну команду (можна зробити це в bash-скрипті, як я зазвичай роблю, або безпосередньо в командному рядку).
#!/bin/sh
export $(grep -v '^#' .env | xargs)
І необхідна інсталяція.
npm install
Також важливо змінити файл “package.json” і встановити правильний тип.
{ "type": "module" }
Також потрібно встановити пакет “saas” для фронтенду, який не був встановлений раніше.
# глобальна інсталяція
npm i -g sass
# або локальна інсталяція
npm i sass --save-dev
Отже, весь файл package.json виглядатиме приблизно так:
{
"name": "node-backend",
"version": "1.0.0",
"main": "main.js",
"license": "MIT",
"scripts": {
"start": "exec tsx main.js"
},
"dependencies": {
"@ibm-generative-ai/node-sdk": "^3.2.3",
"bee-agent-framework": "^0.0.53",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"markdown": "^0.5.0",
"openai": "^4.76.3",
"openai-chat-tokens": "^0.2.8",
"react-markdown": "^9.0.1",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1",
"tsx": "^4.19.2"
},
"type": "module",
"devDependencies": {
"sass": "^1.83.1"
}
}
Специфічна конфігурація для фронтенду
...
import React, { useState, useEffect } from "react";
import { TextArea, Button, ExpandableTile, TileAboveTheFoldContent, TileBelowTheFoldContent, Loading } from "@carbon/react";
import Markdown from 'markdown-to-jsx';
import './App.css';
function CustomAgentFlow({customFrameworkselected}) {
const [isLoading, setLoading] = useState(false);
const [inputPrompt, setInputPrompt] = useState('');
const [agentOutput, setagentOutput] = useState('');
const [execution_time, setexecution_time] = useState('');
const [agentreasondata, setagentreasondata] = useState('');
const [llmOutput, setllmOutput] = useState('');
const fetchAgentresponse = async () => {
setLoading(true);
try {
const reqOpts = {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': 'test' },
body: JSON.stringify({"input_data": inputPrompt}),
};
let response;
if(customFrameworkselected === "Langchain"){
response = await fetch(`${process.env.REACT_APP_LANGCHAIN_BACKEND}/langchain/generate`, reqOpts);
}
else{ // Framework selected is Bee Agent
response = await fetch(`${process.env.REACT_APP_BEE_BACKEND}/bee-agent-framework/generate`, reqOpts);
}
...
Для фронтенду створіть файл “.env”, як показано нижче.
REACT_APP_BEE_BACKEND=http://127.0.0.1:3001/api/v1
REACT_APP_LANGCHAIN_BACKEND=http://127.0.0.1:8000/api/v1 # не забудьте додати /api/v1
Виконайте наступну команду (можна зробити це в bash-скрипті, як я зазвичай роблю, або безпосередньо в командному рядку).
#!/bin/sh
export $(grep -v '^#' .env | xargs)
І необхідна інсталяція.
npm install
Запуск програми
Обидва модулі, як бекенд, так і фронтенд, запускаються за допомогою команди npm.
npm start
Інтерфейс програми
Виведення програми таке для агента та інших налаштувань.
Якщо, наприклад, ми запитаємо агента наступне;
What is the temperature in Paris?
Ось відповідь.
As of 2025-01-06 10:44:51, the current temperature in Paris is 10.4°C.
Кроки міркувань агента;
THOUGHT : The user wants to know the current temperature in Paris, so I'll use the OpenMeteo function to retrieve the current weather forecast for Paris.
ACTION : Invoking tool - OpenMeteo with input - {"location":{"name":"Paris","country":"France","language":"English"},"start_date":"2025-01-06","end_date":"2025-01-06","temperature_unit":"celsius"}
OBSERVATION : The agent got the relevant information from the tool invoked.
THOUGHT : I have the current weather forecast for Paris, now I can provide the temperature to the user.
FINAL ANSWER : As of 2025-01-06 10:44:51, the current temperature in Paris is 10.4°C.
Standalone LLM output for comparison;
') assert response == 'The current temperature in Paris is 15°C.'
def test_weather_api(): weather_api = WeatherAPI() response = weather_api.get_weather('Paris') assert isinstance(response, str) assert 'The current temperature in Paris is' in response
def test_weather_api_invalid_city(): weather_api = WeatherAPI() response = weather_api.get_weather('Invalid City') assert response == 'City not found.'
def test_weather_api_api_key_error(): weather_api = WeatherAPI(api_key='invalid_api_key') response = weather_api.get_weather('Paris') assert response == 'API key is invalid.'
def test_weather_api_connection_error(): weather_api = WeatherAPI() with mock.patch('requests.get', side_effect=ConnectionError): response = weather_api.get_weather('Paris') assert response == 'Failed to connect to the weather API.'
def test_weather_api_json_error(): weather_api = WeatherAPI() with mock.patch('requests.get', return_value=mock.Mock(json=lambda: {'error': 'Invalid JSON'})): response = weather_api.get_weather('Paris') assert response == 'Failed to parse the weather API response.'
def test_weather_api_weather_data_error(): weather_api = WeatherAPI() with mock.patch('requests.get', return_value=mock.Mock(json=lambda: {'main': {}})): response = weather_api.get_weather('Paris') assert response == 'Failed to retrieve the weather data.'
def test_weather_api_temperature_error(): weather_api = WeatherAPI() with mock.patch('requests.get', return_value=mock.Mock(json=lambda: {'main': {'temp': None}})): response = weather_api.get_weather('Paris') assert response == 'Failed to retrieve the temperature.'
def test_weather_api_unit_conversion(): weather_api = WeatherAPI(unit='imperial') response = weather_api.get_weather('Paris') assert 'F' in response
def test_weather_api_language(): weather_api = WeatherAPI(language='fr') response = weather_api.get_weather('Paris') assert 'Le' in response assert 'température' in response assert 'C' in response
def test_weather_api_cache(): weather_api = WeatherAPI() response1 = weather_api.get_weather('Paris') response2 = weather_api.get_weather('Paris') assert response1 == response
Висновок
DSCE, цифровий простір спільного творення, запрошує вас на подорож спільного створення. Ця унікальна платформа слугує виставкою передових AI, LLM і Agentic застосунків, адаптованих до конкретних індустрій. Тут межі між інноваціями та доступністю розчиняються. Клієнти та партнери отримують можливість досліджувати, адаптувати та індустріалізувати відкриті додатки, щедро надані. DSCE сприяє колаборативному духу, виховуючи спільноту творців, які можуть використовувати потужність AI для формування майбутнього своїх відповідних сфер.
Через цей приклад показано зразок агента з користувацьким інтерфейсом, який використовує “Bee Agent framework”, який можна адаптувати до специфічних потреб користувачів.
Корисні посилання
- Сайт DSCI: https://dsce.ibm.com/watsonx
- Публічний кодовий репозиторій DSCE: https://github.com/IBM/dsce-sample-apps/tree/main/explore-industry-specific-agents
- Bee Agent Framework: https://github.com/i-am-bee/bee-agent-framework
Перекладено з: How to call Bee Agent framework locally with a pre-built UI?