这篇文章说明了如何使用来自DSCE(IBM 数字自助创作平台环境中的)已有源代码来构建自己的解决方案。
DSCE平台是什么呢?DSCE 是 IBM 的为企业准备的 AI 和数据平台,旨在利用基础模型和机器学习技术。换句话说,DSCE 是一个用户可以挑选用例来体验使用 watsonx 构建的应用程序的平台。开发人员可以访问提示选择和构建指导,同时也可以得到示例应用程序代码,从而加快他们在 watsonx.ai 上的项目进度。
平台提供的用例之一是一个演示应用程序,用于展示基于大型语言模型(LLM)的代理方法;“使用IBM的人工智能增强您的产品”。要了解这种方法和用例,可以在DSCE网站上运行这个代理程序,或者您也可以在本地环境中运行应用程序的代码进行即时修改,本文将详细介绍这一过程。
为了制作一个本地应用,我们可以直接点击“获取代码按钮”,这将直接带用户到此GitHub仓库。
第一步是用应用的这两部分建立一个项目;一个后端部分和一个前端部分。这两部分都用“node.js”(一种JavaScript运行环境)编写,因此很容易使用。最终,该应用程序将采用以下架构。
本地运行时,没有无服务器代码运行部分。
重要的是安装正确版本的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"。结果应为 "v22.12.0"。
nvm current # 应该显示 "v22.12.0"。结果应为 "v22.12.0"。
# 验证 npm 版本:
npm -v # 应该显示 "10.9.0"。结果应为 "10.9.0"。
我本地这个项目的结构是这样的。
/**
* Copyright 2024 IBM Corp.
*
*
* 一个软件按以下许可协议授权:
*
* 本软件按以下许可协议授权;除非遵守许可协议,否则不得使用此文件。
* 您可以在以下网址获取许可协议副本:
*
* 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<string, string[]>[] }>;
export interface LLMChatTemplate {
template: LLMChatPromptTemplate;
messagesToPrompt: (template: LLMChatPromptTemplate) => (messages: BaseMessage[]) => string;
parameters: {
stop_sequence: string[];
};
}
export function messagesToPromptFactory(rolesOverride: Record<string, string | undefined> = {}) {
const roles: Record<string, string> = 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(`模型模板 '${model}' 已存在!`);
}
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(`模型模板 '${model}' 未找到!`, [], {
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" # IBM云API密钥
WX_PROJECT_ID="your-watsonx-project-id" # Watsonx项目ID
WX_ENDPOINT=https://us-south.ml.cloud.ibm.com # IBM Cloud服务的美国南部区域机器学习端点
运行下面的命令,我通常在 bash 文件中运行,或者直接在命令行中输入。
#!/bin/sh
# 设置环境变量,忽略以#开头的行
export $(grep -v '^#' .env | xargs)
并且必要的安装。
npm install``(安装依赖包)
同时还需要修改“package.json”文件以及所有相关的“type”。
{ "type": "模块" }
你也得安装用于前端的“saas”包。
# 全局(全局性地)安装,
npm 安装 sass -g
# 或者本地(本地化地)安装,
npm 安装 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{ // 选择了蜂代理框架
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命令来启动的。
npm start
运行 Node.js 应用程序的命令 (`npm start`)
# 应用界面
以下是应用程序为代理及其他配置输出的内容。

如果我们例如这样向代理人提问;
巴黎现在的温度是多少?
答案如下。
截至2025-01-06 10:44:51,现在的气温是10.4°C。
代理的推理过程。
思考 : 用户想知道巴黎当前的温度,所以我将使用OpenMeteo函数来获取巴黎的实时温度。
行动 : 调用工具 - OpenMeteo,输入参数为巴黎,日期为2025年1月6日,单位为摄氏度。
观察 : 工具返回了巴黎的实时温度信息。
思考 : 我已经得到了巴黎的实时温度,现在可以告诉用户了。
最终答案 : 截至2025年1月6日10点44分51秒,巴黎当前的温度是10.4°C。
单独的大语言模型输出用于比较。
') assert response == '巴黎当前的温度是15度摄氏度。'
def test_weather_api(): weather_api = WeatherAPI() response = weather_api.get_weather('Paris') assert isinstance(response, str) assert '巴黎当前的温度是' in response
def test_weather_api_invalid_city(): weather_api = WeatherAPI() response = weather_api.get_weather('Invalid City') assert response == '找不到城市。'
def test_weather_api_api_key_error(): weather_api = WeatherAPI(api_key='invalid_api_key') response = weather_api.get_weather('Paris') assert response == '无效的API密钥。'
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 == '无法连接到天气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 == '无法解析天气API响应数据。'
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 == '无法获取天气信息。'
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 == '无法获取当前温度。'
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 == response2

# 结论:
DSCE,一个数字共创空间,邀请您一起踏上共创的旅程。这个独特的平台汇集了前沿的人工智能、大语言模型和代理技术,这些技术都是为特定行业量身定制的。在这里,创新与可访问性之间的界限变得越来越模糊。客户和合作伙伴都可以探索、适应并利用这些开源应用来推动工业化进程。DSCE倡导协作精神,培养了一个由创作者组成的社区,在这里大家可以利用人工智能的力量塑造各自领域的未来。
通过这个例子,我们展示了如何使用一个带有UI的代理来调用或访问“**Bee Agent框架**”,该框架可以适应用户的具体需求,从而满足不同用户的需求。
一些有用的链接.
* DSCI 网站: <https://dsce.ibm.com/watsonx>
* DSCE 公开代码库: <https://github.com/IBM/dsce-sample-apps/tree/main/explore-industry-specific-agents>
* Bee 代理框架(一种代理工具集): <https://github.com/i-am-bee/bee-agent-framework>
共同学习,写下你的评论
评论加载中...
作者其他优质文章