使用 AI SDK 构建 Playground(体验中心)
使用 Vercel 的 Core SDK 与 AI SDK 的教程
本教程将向你展示如何结合 lybic Core SDK 与 Vercel AI SDK 构建一个 Playground AI SDK by Vercel.
Lybic Core SDK 是一个使用 TypeScript 与 Lybic API 交互的库。AI SDK 是一套 TypeScript 工具包,旨在帮助开发者使用 React、Next.js、Vue、Svelte、Node.js 等技术栈构建 AI 应用与智能体。
通过本教程,你将学习如何:
- 使用 lybic Core SDK 与 Lybic API 进行交互
- 受益于 Lybic 易用的沙箱环境
- 使用 AI SDK 构建一个即用型的 Computer Use 智能体 Playground
准备工作
和常规的 Playground 一样,你需要将应用拆分为服务端和客户端。你可以选择任意服务端或全栈框架,因为本教程不会涉及具体的框架实现细节。
为了方便起见,本教程将服务端和客户端合并为一个仅前端的 SPA React 应用。这种方式会将凭证暴露在客户端侧,强烈不推荐用于生产环境,但后续可以非常容易地改造成全栈应用。
你可以在项目中安装 lybic Core SDK、AI SDK 以及一些辅助库:
bash npm install @lybic/core ai @ai-sdk/openai-compatible @ai-sdk/react bash yarn add @lybic/core ai @ai-sdk/openai-compatible @ai-sdk/react bash pnpm install @lybic/core ai @ai-sdk/openai-compatible @ai-sdk/react 电脑端使用(Computer Use)智能体执行循环拆解
作为一个极度精简版的 Computer Use 智能体执行循环,整体逻辑如下:
- 获取用户输入
- 获取截图
- 将用户输入与截图合并为一条 user 消息并加入历史
- 使用当前构建的历史调用 LLM
- 解析 LLM 响应
- 判断是否存在 action / tool call
- 如果存在,则执行
- 根据成功或失败更新 UI
- 如果需要用户接管,则请求用户接管
- 如果不存在 action,则中断并报告错误
- 回到步骤 2 继续执行
创建新的模型提供方(Model Provider)
我们将使用火山引擎(VolcEngine)的 Ark 作为模型提供方。doubao-1.5-ui-tars 是目前 GUI Grounding 任务的 SOTA 模型,由其团队开发。
使用以下代码片段,可以为 AI SDK 创建一个新的模型提供方,并启用相应的传输能力。
import { } from '@ai-sdk/openai-compatible'
import { , } from 'ai'
// create a openai compatible model provider
const = ({
: ..!,
: ..!,
: 'ark',
: true,
})
// use the ark model provider to create a new model provider
const = ({
: {
'doubao-1-5-ui-tars-250428': ({
: ('doubao-1-5-ui-tars-250428'),
: [],
}),
'doubao-1-5-thinking-vision-pro-250428': ({
: ('doubao-1-5-thinking-vision-pro-250428'),
: [],
}),
},
})
// we only uses languageModel
export const = .你可以根据需要添加任意数量的模型,本教程中我们仅关注这两个模型。
构建服务端 Transport
useChat 的 transport 系统可以精细控制消息如何发送到 API 端点以及响应如何被处理,这对我们这种特殊后端集成非常有用。
首先,我们需要创建一个自定义 transport。可参考 AI SDK 中的 Building Custom Transports 文档。 Building Custom Transports
This is a tutorial only example, you should not use it in production.
在真实生产环境中,应将 sendMessages 中的逻辑拆分到服务端响应处理器中,并将响应流传递给客户端,客户端无需自定义 transport。
为了快速构建前端原型,本教程直接在客户端使用自定义 transport 发起请求。
import { } from '@lybic/core'
import {
,
,
,
,
,
,
,
} from 'ai'
import { } from './ark-provider'
export class implements <> {
public constructor(private readonly : ) {}
public async (
: {
: 'submit-message' | 'regenerate-message'
: string
: string | undefined
: []
} & ,
): <<>> {
// create a UI message stream
const = <>({
: async ({ }) => {
// convert the client side UI messages to server side messages
const = (.)
const =
const = this.
// call the LLM
const = ({
: ('doubao-1-5-ui-tars-250428'),
: ,
: ,
})
// stream the LLM response to the client side
.(.())
},
})
return
}
public async (
: { : string } & ,
): <<> | null> {
return null
}
}
// the system prompt for ui-tars model
const = `## Role
You are a GUI Agent, proficient in the operation of various commonly used software on Windows, Linux, and other operating systems.
Please complete the user's task based on user input, history Action, and screen shots.
You need to complete the entire task step by step, and output only one Action at a time, please strictly follow the format below.
## Output Format
Action_Summary: ... // Please make sure use English in this part.
Action: ...
Please strictly use the prefix "Action_Summary:" and "Action:".
Please use English in Action_Summary and use function calls in Action.
## Action Format
### click(start_box='<bbox>left_x top_y right_x bottom_y</bbox>')
### left_double(start_box='<bbox>left_x top_y right_x bottom_y</bbox>')
### right_click(start_box='<bbox>left_x top_y right_x bottom_y</bbox>')
### drag(start_box='<bbox>left_x top_y right_x bottom_y</bbox>', end_box='<bbox>left_x top_y right_x bottom_y</bbox>')
### type(content='content') // If you want to submit your input, next action use hotkey(key='enter')
### hotkey(key='key')
### scroll(direction:Enum[up,down,left,right]='direction',start_box='<bbox>left_x top_y right_x bottom_y</bbox>')
### wait()
### finished()
### call_user() // Submit the task and call the user when the task is unsolvable, or when you need the user's help.
### output(content='content') // It is only used when the user specifies to use output, and after output is executed, it cannot be executed again.
`在上述示例中,我们创建了一个使用 ark 模型提供方的新 transport,并暴露了可注入的多个环节。在 LLM 返回结果后,我们将解析输出并执行对应动作。
现在,我们可以使用useChat hook来构建一个简单的前端界面。
构建聊天 UI
你可以参考 Chatbot 来构建一个简单的聊天界面,这里不再展开说明。
一个最小示例如下,只需创建 transport 并传入useChat
import { useChat } from '@ai-sdk/react'
const chat = useChat({
transport: new LybicChatTransport({
coreClient: client,
}),
})与 Lybic 沙箱集成
现在你已经有了一个基础的聊天机器人,但它还无法操作计算机。接下来需要集成 Lybic 沙箱。
创建沙箱
你需要在服务端创建一个新的沙箱并获取 sandbox id.
import { } from '@lybic/core'
// create a new client
const = new ({
: 'your-base-url',
: 'your-org-id',
: 'your-api-key',
})
// create a new sandbox
const { : } = await .({
: 'My Sandbox',
: 60 * 60 * 24 * 30,
: 'beijing-2c-4g-cpu',
})连接沙箱实时流
Get started with Lybic UI SDK
请参考Lybic UI SDK 文档,了解如何与沙箱建立实时流连接.
获取沙箱截图
在向 LLM 发送消息时,应始终获取当前截图,并将其与用户输入或历史消息一起发送。
在 transport 中加入如下代码:
const = this.
// takes screenshot of the sandbox
const = await .()
const = .!
// get the last message
const = .[.. - 1]?.
const = [. - 1]
// append the screenshot to the last message
. = [
{
: 'text',
: .,
},
// adds the screenshot to the last message
{
: 'file',
: 'image/webp',
: new (.!),
},
]
某些模型提供方可能不支持 https URL,此时你也可以下载截图并转换为 base64 data URL。
解析 LLM 输出并执行动作
在 streamText 中添加 onFinish 回调,并调用 parseLlmOutput 解析 LLM 响应。
解析出动作后,将其流式返回给客户端,并在沙箱中执行。
: async () => {
// parse the LLM response
const { data: } = await coreClient.parseLlmOutput({
: 'ui-tars',
: .,
})
if (?.actions && .actions.length > 0) {
// stream the parsed actions to the client side
writer.write({
: 'data-parsed',
: {
: .actions,
: [.thoughts, .unknown].().('\n'),
},
})
// execute the actions on the sandbox
for (const of ?.actions) {
await coreClient.executeComputerUseAction(sandboxId, {
,
: false,
: false,
})
}
}
},
客户端处理更新
当服务端通过 writer.write 推送数据后,可在 useChat 的 onData 回调中处理这些更新。
: () => {
if (.type === 'data-screenShot') {
.setMessages(
produce(() => {
const = .findIndex(() => .id === .data.messageId)
if ( === -1) {
return
}
const = []
?.parts.push({
: 'file',
: 'image/webp',
: .data.url,
})
return
}),
)
}
},
更多细节可参考 AI SDK 文档中的 Streaming Custom Data 。
总结
你已经学会了如何结合 AI SDK 与 Lybic Core SDK 构建一个 Playground。现在,你可以基于这个 Playground 构建属于你自己的 AI 应用。
我们已在 GitHub 的lybic/typescript 仓库中开源了该 Playground,你可以在其中查看完整示例代码。