尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

移动端接入 Vellum 工作流:从环境搭建到双端实战全攻略

移动端接入 Vellum 工作流:从环境搭建到双端实战全攻略 最近在将基于 Vellum 工作流的 AI 能力迁移到移动端时踩了不少环境配置、API 鉴权和流式响应处理的坑。网上关于 Vellum 的资料大多停留在 Web 端或纯后端调用专门针对 iOS、Android 接入的开源实战内容比较零散。这篇文章整理了一套从环境准备、技术选型到双端编码的闭环方案既有可复制的代码也有线上问题排查思路。如果你正在做 AI 应用开发或者想把 LLM 工作流能力集成进 App这篇内容可以直接参考。1. 背景Vellum 与移动端 AI 应用开发1.1 Vellum 是什么Vellum 是一款面向 AI 应用开发的工具链平台核心目标是把提示词工程、LLM 编排、版本管理和效果评估串成一条完整的开发流水线。你可以把它理解成一个“AI 应用的后端调度中心”先设计好工作流Workflow把多个模型调用、分支逻辑、RAG 查询和输出解析组合起来然后像发布普通服务一样发布成一个可调用的 API 接口。和直接写一段调用 GPT 接口的代码不同Vellum 这种开源工具链更强调“工程化管理”它解决了几个实际痛点提示词版本混乱每次改动没有记录线上出问题无法回滚。模型切换成本高今天用 A 模型明天换 B 模型代码要改一大片。工作流不可视化多个模型和逻辑步骤叠在一起可读性差。评估体系缺失改了一版提示词到底有没有变好缺少量化指标。因此Vellum 在实际项目中很适合作为“AI 应用的中控层”。1.2 为什么要在 iOS/Android 端接入 Vellum移动端接入 Vellum 的场景很常见比如在 App 里做一个 AI 问答助手通过 Vellum 工作流调用多个模型后统一返回结果。做一个内容生成工具用户在手机上提交需求后端工作流负责调度模型并生成文案。做一个企业知识库应用由 Vellum 完成 RAG 流程App 只负责展示最终答案。在这种架构下App 端不需要关心底层是哪个模型、提示词怎么写的、上下文怎么组织的只需要负责把用户输入提交给 Vellum 工作流然后把结果渲染出来就可以了。这样的好处很明显后续修改提示词或调整模型无需重新发版 App。1.3 本文能帮你解决的问题阅读完这篇文章你可以掌握以下内容Vellum 工作流的基本概念和 API 调用模型。在 AndroidKotlin和 iOSSwift中接入 Vellum API 的完整代码示例。流式输出、超时处理、API Key 安全等工程细节。移动端接入过程中的高频问题和排查方法。2. 环境准备与技术选型2.1 开发环境说明本文示例以常见开发环境为例不同版本的差异不影响整体思路关键是掌握接入流程。Android 端Android Studio建议使用 Hedgehog 2023.1.1 或更新版本Kotlin 1.9minSdk 26 或以上。iOS 端Xcode 14 或更高版本Swift 5.7最低支持 iOS 15 或以上。后端服务Vellum 平台账号已创建并部署一个工作流拿到 API Key。网络调试工具可以选择 Postman 或 Apifox先验证 API 连通性再写客户端代码。如果你还没有在 Vellum 平台创建好工作流可以先在平台上创建一个简单的“用户提问 → 大模型回答”的流程确保测试时可以正常返回结果。2.2 移动端技术栈选择在移动端接入 Vellum 时技术栈主要有三种选择方案优点缺点适用场景原生 AndroidKotlin性能好系统 API 调用方便只覆盖 Android单端应用原生 iOSSwift性能好动画和交互体验佳只覆盖 iOS单端应用跨平台Flutter / React Native一套代码双端复用流式长连接等场景需要额外封装双端都需要快速落地如果你的项目已经定了技术栈这篇文章主要讲的是“对接 Vellum 的思路”跨平台方案在拿到本文的 API 调用示例后同样可以照搬逻辑。2.3 接入方式对比直连 API 与服务端中转移动端接入 Vellum 有两种主流方式App 直连 Vellum APIApp 保存 API Key直接发起 HTTPS 请求。优点是链路短、开发快缺点是 API Key 存在客户端风险较大容易被提取。服务端中转App 请求自己的后端后端再调用 Vellum API 把结果返回 App。优点是安全缺点是增加一道网络链路。从安全角度强烈推荐第二种方式尤其是生产环境。本文为了便于讲解会先演示 App 直连的完整流程然后在“最佳实践”部分给出改造为服务端中转的方案。3. Vellum 核心概念与接入原理3.1 Vellum 的核心概念读懂 Vellum 的 API必须先理解几个核心概念概念说明Workflow工作流把多个步骤编排起来的完整流程比如“意图识别 → 知识检索 → 模型生成 → 结果格式化”Prompt / Node提示词 / 节点工作流中的某一个执行节点可以是大模型调用也可以是普通代码逻辑Deployment部署将工作流发布为可调用 API 的版本每次调用都对应某个部署版本Inputs输入参数调用工作流时传入的参数一般是 JSON 格式Outputs输出工作流执行完成后的返回结果可能是字符串、JSON、流式事件等在调用侧我们关心的核心就两个如何传参数、如何拿结果。3.2 API 调用模型同步执行与流式输出Vellum 的 API 调用模型和其他大模型平台类似主要分两种同步执行发送请求后一直等待直到工作流执行完成并返回完整结果。适合耗时较短、不需要实时输出的场景。流式执行发送请求后服务端会通过流式方式不断返回中间结果App 端可以边接收边渲染。适合交互感强的问答类应用。流式输出的本质是 HTTP 长连接上持续推送数据块客户端需要按特定格式解析。在移动端流式响应对网络库的超时设置、内存处理和 UI 更新方式都有更高要求。3.3 认证与密钥管理Vellum API 的认证方式通常是在请求头中携带 API Key。示例如下X-API-KEY: your_api_key_here Content-Type: application/json需要特别强调的是API Key 是敏感信息。在生产环境中不要把 API Key 直接写在客户端代码里而应该由服务端代为请求并分发短期凭证。这一点会影响整体架构设计建议文章后面结合自身后端一起看。4. 实战AndroidKotlin接入 Vellum下面我们从零开始在 Android 端接入 Vellum 工作流。本示例使用 OkHttp 作为网络库以最直观的方式展示请求构造、参数传递和结果解析。4.1 创建 Android 项目并添加依赖打开 Android Studio创建一个空项目包名建议为com.example.vellumclient。然后在app/build.gradle中添加网络库依赖dependencies { implementation(com.squareup.okhttp3:okhttp:4.12.0) implementation(org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3) implementation(com.google.code.gson:gson:2.10.1) }同步依赖后在AndroidManifest.xml中添加网络权限uses-permission android:nameandroid.permission.INTERNET /如果你的测试环境是 HTTP 明文请求还需要在AndroidManifest.xml的application节点中配置application android:usesCleartextTraffictrue ...注意生产环境必须使用 HTTPS不能允许明文流量。4.2 封装 API 客户端为了方便维护我们创建一个VellumClient类统一处理请求构造和结果解析。// app/src/main/java/com/example/vellumclient/VellumClient.kt import okhttp3.Call import okhttp3.Callback import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import java.io.IOException import java.util.concurrent.TimeUnit import org.json.JSONObject class VellumClient( private val apiKey: String, private val baseUrl: String https://api.vellum.ai ) { private val client OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(120, TimeUnit.SECONDS) .build() fun executeWorkflow( workflowId: String, inputs: MapString, Any, onSuccess: (String) - Unit, onFailure: (Exception) - Unit ) { val payload JSONObject().apply { put(workflow_id, workflowId) put(inputs, JSONObject(inputs)) } val request Request.Builder() .url($baseUrl/v1/execute-workflow) .addHeader(X-API-KEY, apiKey) .addHeader(Content-Type, application/json) .post(payload.toString().toRequestBody(application/json.toMediaType())) .build() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { onFailure(e) } override fun onResponse(call: Call, response: Response) { val body response.body?.string() if (response.isSuccessful body ! null) { onSuccess(body) } else { onFailure(RuntimeException(HTTP ${response.code}: $body)) } } }) } }这段代码做了三件事把工作流 ID 和输入参数封装成 JSON 请求体。在请求头中携带 API Key。用 OkHttp 的异步回调发起请求避免阻塞主线程。注意上面的 URL 路径是示例写法实际要以你所用 Vellum 版本的接口文档为准。如果你的后端已经封装了自己的接口将 URL 替换为后端地址即可。4.3 在 ViewModel 中管理状态为了在界面中展示加载状态和结果我们创建一个 ViewModel。// app/src/main/java/com/example/vellumclient/MainViewModel.kt import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel class MainViewModel : ViewModel() { private val vellumClient VellumClient(apiKey 你的_API_Key) private val _result MutableLiveDataString() val result: LiveDataString _result private val _loading MutableLiveDataBoolean() val loading: LiveDataBoolean _loading fun runWorkflow(userInput: String) { _loading.value true val inputs mapOf(user_input to userInput) vellumClient.executeWorkflow( workflowId 你的_Workflow_ID, inputs inputs, onSuccess { text - _result.postValue(text) _loading.postValue(false) }, onFailure { e - _result.postValue(请求失败: ${e.message}) _loading.postValue(false) } ) } }这里用LiveData来驱动 UI 更新注意postValue可以在子线程中安全调用。4.4 编写界面并验证在 Activity 的布局中放一个输入框、一个按钮和一个文本显示区域然后在代码中绑定 ViewModel。// app/src/main/java/com/example/vellumclient/MainActivity.kt import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModelProvider import com.example.vellumclient.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private lateinit var viewModel: MainViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) viewModel ViewModelProvider(this)[MainViewModel::class.java] binding.btnSubmit.setOnClickListener { val input binding.etInput.text.toString() if (input.isNotBlank()) { viewModel.runWorkflow(input) } } viewModel.loading.observe(this) { loading - binding.tvResult.text if (loading) 加载中... else binding.tvResult.text } viewModel.result.observe(this) { result - binding.tvResult.text result } } }运行 App输入内容点击提交正常情况下会显示工作流返回的结果。如果遇到问题打开 Logcat 查看网络请求日志或者先把同样的请求放在 Postman 里调试。5. 实战iOSSwift接入 Vellum接下来是 iOS 端的接入流程。我们使用 Swift 5.7 和 URLSession 实现不依赖第三方网络库。5.1 创建 iOS 工程并配置 ATS在 Xcode 中创建一个 SwiftUI 或 UIKit 项目然后在Info.plist中确保 App Transport Security 配置正确。由于 Vellum API 使用 HTTPS默认是可以直接访问的如果你在开发阶段需要访问 HTTP 测试地址需要临时添加 ATS 例外keyNSAppTransportSecurity/key dict keyNSAllowsArbitraryLoads/key false/ /dict注意生产环境不允许设置NSAllowsArbitraryLoads为true。5.2 编写 Vellum 请求客户端我们创建一个VellumClient.swift文件使用URLSession发送请求。// Sources/VellumClient.swift import Foundation public struct VellumClient { public let apiKey: String public let baseURL: URL public init(apiKey: String, baseURL: URL URL(string: https://api.vellum.ai)!) { self.apiKey apiKey self.baseURL baseURL } public func executeWorkflow( workflowId: String, inputs: [String: Any], completion: escaping (ResultString, Error) - Void ) { var request URLRequest(url: baseURL.appendingPathComponent(v1/execute-workflow)) request.httpMethod POST request.addValue(apiKey, forHTTPHeaderField: X-API-KEY) request.addValue(application/json, forHTTPHeaderField: Content-Type) let body: [String: Any] [ workflow_id: workflowId, inputs: inputs ] do { request.httpBody try JSONSerialization.data(withJSONObject: body) } catch { completion(.failure(error)) return } let task URLSession.shared.dataTask(with: request) { data, response, error in if let error error { completion(.failure(error)) return } guard let httpResponse response as? HTTPURLResponse, httpResponse.statusCode 200, let data data, let text String(data: data, encoding: .utf8) else { completion(.failure(NSError(domain: VellumClientError, code: -1))) return } completion(.success(text)) } task.resume() } }这段代码实现了和 Android 端完全相同的逻辑构造请求体、携带 API Key、异步回调返回结果。5.3 在视图层调用下面用一个简单示例展示如何在 SwiftUI 中使用这个客户端。// Views/ContentView.swift import SwiftUI struct ContentView: View { State private var inputText: String State private var resultText: String 等待输入... State private var isLoading: Bool false private let client VellumClient(apiKey: 你的_API_Key) var body: some View { VStack(spacing: 20) { TextField(请输入问题, text: $inputText) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding() Button(调用工作流) { guard !inputText.isEmpty else { return } isLoading true resultText 加载中... client.executeWorkflow( workflowId: 你的_Workflow_ID, inputs: [user_input: inputText] ) { result in DispatchQueue.main.async { isLoading false switch result { case .success(let text): resultText text case .failure(let error): resultText 请求失败: \(error.localizedDescription) } } } } .buttonStyle(.borderedProminent) .disabled(isLoading) Text(resultText) .padding() .frame(maxWidth: .infinity, alignment: .leading) .background(Color.gray.opacity(0.1)) .cornerRadius(8) } .padding() } }运行 App输入内容后点击按钮结果会显示在文本区域中。需要特别提醒在 iOS 中涉及 UI 更新时一定要切回主线程否则 Xcode 会输出警告并且可能出现界面卡顿。5.4 双端联调说明在联调时建议先使用 Postman 验证 Vellum 工作流是否能正常返回。然后分别跑通 Android 端和 iOS 端。两端请求体的字段名必须保持一致比如工作流定义的输入参数是user_input那两端都必须使用同一个字段名否则会出现参数不匹配的报错。6. 常见问题与排查思路移动端接入 Vellum 过程中高频问题集中在认证、网络、参数和流式解析几个方面。下面用表格列出常见问题然后对部分重点问题详细展开。问题现象常见原因解决思路返回 401 UnauthorizedAPI Key 错误或已失效检查 API Key、确认平台端配置返回 404 页面不存在API 路径写错核对文档中的接口路径请求超时工作流内部耗时长或网络不稳定调大readTimeout增加重试机制参数类型不匹配工作流定义的类型与请求类型不一致在平台端查看 Inputs 定义按类型传参返回结果包含异常字符响应格式解析错误打印原始响应体确认返回的是 JSON 还是流式文本Release 包无法访问网络平台商禁用了非 HTTPS 流量确保所有请求使用 HTTPS界面不更新没有切回主线程Android 使用postValueiOS 使用DispatchQueue.main.async6.1 401 认证失败如果你在调试时收到 401 响应先按下面几个方向排查确认 API Key 是否复制完整注意尾部的空格。确认请求头的 Key 名称是否正确比如是X-API-KEY还是Authorization。如果 API Key 是刚创建的确认平台侧是否已经激活并绑定相关权限。后端服务如果使用了网关确认网关有没有把认证头透传。6.2 流式响应解析问题如果你使用流式输出服务端返回的格式通常是由多个事件组成的文本流。客户端拿到原始数据后不能直接整体当成 JSON 解析而需要按分隔符拆分成多个事件然后逐个解析。这里给一个 Android 端的流式解析思路val streamText data: {...}\n\ndata: {...}\n\n val lines streamText.split(\n\n) for (line in lines) { if (line.startsWith(data:)) { val json line.removePrefix(data:).trim() // 解析单个事件 } }流式处理的核心是“准备好一个缓冲区持续接收等到完整事件后立刻解析”。无论双端都建议使用专门的流式读取 API不要用response.body?.string()一次性读取。6.3 参数类型不匹配排查Vellum 工作流的输入参数是有类型定义的有的字段是字符串有的是数字有的是数组。如果你传的 JSON 类型与定义不一致平台会返回校验错误。排查时在平台端打开工作流的 Inputs 面板确认每个参数的类型和必填情况。错误示例工作流定义 age 为 number请求传了 age: 25 正确示例工作流定义 age 为 number请求应该传 age: 257. 最佳实践与工程建议代码跑通只是第一步真正上线到生产环境还有几个关键点需要做好。7.1 API Key 安全边界千万不要把 API Key 硬编码在移动端源码中也不要放在 Android 的BuildConfig或 iOS 的UserDefaults里。客户端一旦被反编译或抓包API Key 就会泄露。推荐做法是移动端请求自己的后端接口携带用户登录凭证。后端根据用户身份判断是否有权限调用 Vellum。后端在服务端保存 Vellum API Key每次调用时动态读取。如果必须由移动端直连建议使用短期临时令牌来替代长期 API Key。最小权限原则也很重要尽量为工作流创建专用 API Key而不是使用平台管理员账号的 Key。7.2 网络层健壮性移动端网络环境复杂弱网、断网、切换网络都是常态。建议从以下层面增强健壮性超时时间分层连接超时 10 到 30 秒读超时取决于工作流耗时可以调到 120 秒或更长。自动重试对网络抖动导致的失败采用指数退避重试最多重试 2 到 3 次注意不要重复提交用户请求。错误提示友好不要直接把异常堆栈展示给用户应该提供“请求失败请稍后重试”之类的人性化提示。7.3 缓存与离线兜底如果工作流返回的是比较固定的内容比如产品介绍、操作说明可以考虑在本地做缓存。缓存策略建议相同请求参数在短时间内重复请求时直接返回本地缓存。用户主动刷新时强制请求最新数据。工作流结果带有版本号时可以根据版本号判断缓存是否过期。离线状态下尽量展示历史结果并在界面上提示“当前为离线数据”。7.4 日志与可观测性移动端接入 Vellum 后问题定位依赖完整日志。日志中应记录请求发起时间、工作流 ID、API 路径。输入参数和输出结果的摘要。请求耗时、HTTP 状态码、错误信息。设备型号、系统版本、App 版本。注意日志中不能包含完整 API Key 和敏感用户数据需要对关键字段做脱敏处理。7.5 上架与合规注意事项在 App Store 和安卓应用市场上架时AI 类应用需要注意隐私政策中必须说明数据收集和使用方式尤其是用户输入内容会发送给第三方 AI 服务。应用内如果涉及用户生成内容需要提供内容过滤或申诉通道。部分应用市场要求 AI 生成内容有显著标识。测试环境与生产环境使用不同的 API Key防止测试数据污染线上工作流。8. 总结与下一步学习方向本文围绕 Vellum 开源工具的移动端接入场景从核心概念入手逐步完成了 Android 和 iOS 双端的环境搭建、API 客户端封装、请求调用和结果展示并总结了常见问题与生产环境的最佳实践。如果你还想继续深入下一步可以重点关注流式输出的完整编码实现包括事件解析、UI 流式渲染和中断恢复。服务端中转架构的搭建用 Spring Boot 或 Node.js 封装一层 Vellum 网关。结合本地上传、图片多模态输入等工作流类型扩展移动端的输入格式。在 Flutter 或 React Native 中复用本文的请求逻辑实现一次开发双端上线。实际项目落地时最需要优先防范的风险是 API Key 泄露和流式响应处理不完善。建议先按本文的示例跑通最小闭环再逐步加入安全加固和异常处理。如果这篇文章对你有帮助可以收藏备用后续做 AI 应用开发时直接拿出来参考。
返回列表