切换主题
3. 声明模型并启动后台
开始前: 应用和凭据已创建。本步目标: 声明一个只聊天的智能体,并让后台提供 /api/ai 标准接口。
1. 理解示例文件
| 文件(相对 tutorial/backend) | 你负责什么 |
|---|---|
build.gradle.kts | Spring MVC 运行时与业务后台 SDK 依赖 |
TutorialApplication.java | 普通 Spring Boot 启动类 |
ModelConfiguration.java | 提供商、模型、绑定、配置和智能体的清单 |
LocalIdentity.java | 本地业务令牌验证及平台 USER 凭据映射 |
application.yml | 地址、应用、声明归属、开发发布与聊天开关 |
Initialize.java | 上一步的独立初始化入口,不参与聊天启动 |
示例消费本地 Maven 制品,完整构建文件如下。已有 Spring 项目只需加入 SDK 依赖并提供 MVC Web 运行时,无需替换原工程:
kotlin
plugins { java; application }
repositories {
mavenCentral()
maven { url = uri("https://sagetripp.github.io/sparktide-docs/maven/") }
}
java { toolchain { languageVersion = JavaLanguageVersion.of(17) } }
tasks.withType<JavaCompile>().configureEach { options.encoding = "UTF-8" }
tasks.withType<JavaExec>().configureEach { jvmArgs("-Dfile.encoding=UTF-8") }
dependencies {
implementation(platform("org.springframework.boot:spring-boot-dependencies:4.0.8"))
implementation("org.springframework.boot:spring-boot-starter-webmvc")
implementation("dev.sparktide:sdk-spring-boot-starter:0.1.0")
// 独立初始化命令使用 Jackson 2,与 SDK 核心保持一致。
implementation("com.fasterxml.jackson.core:jackson-databind:2.20.1")
}
application { mainClass = "tutorial.TutorialApplication" }
tasks.register<JavaExec>("initialize") {
classpath = sourceSets.main.get().runtimeClasspath
mainClass = "tutorial.Initialize"
}
tasks.register<JavaExec>("credentials") {
classpath = sourceSets.main.get().runtimeClasspath
mainClass = "tutorial.Initialize"
args("credentials")
}
tasks.register<JavaExec>("selectAgent") {
classpath = sourceSets.main.get().runtimeClasspath
mainClass = "tutorial.SelectAgent"
args(providers.gradleProperty("agentVersion").getOrElse("1.1.0"))
}2. 用后台 SDK 声明模型与智能体
模型的五层配置分别回答:连到哪里 → 调哪个模型 → 哪个应用可用 → 如何调用 → 谁来执行任务。这里只建一个默认智能体,没有工具、知识和子智能体。
Java / Kotlin 二选一。下载示例已包含 Java 文件;Kotlin 项目需要安装 sdk-kotlin,启用 Kotlin JVM 插件,并用下面 Kotlin 文件替换 Java 同名配置,不能同时保留两份 Bean。
java
package tutorial;
import dev.sparktide.sdk.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import java.net.URI;
import java.util.List;
@Configuration
public class ModelConfiguration {
@Bean
RegistrationManifest models(@Value("${tutorial.model-endpoint}") URI endpoint,
@Value("${tutorial.model-id}") String modelId) {
String v = "1.0.0";
return new RegistrationManifest(List.of(
ModelDeclarations.openAIProvider("provider", v, endpoint, "MODEL_API_KEY", true),
ModelDeclarations.model("model", v, "provider", v, modelId,
ModelCapabilities.builder().supports(ModelCapabilities.Feature.STREAMING, true)
.supports(ModelCapabilities.Feature.TOOL_CALLING, true).build()),
ModelDeclarations.binding("binding", v, "model", v),
ModelDeclarations.profile("profile", v,
ModelProfile.builder("binding", v).maxOutputTokens(1024).build()),
ModelDeclarations.agent("main", v, "你是业务助手,请用中文回答。", "profile", v,
List.of(), List.of())
)).managedBy("tutorial-service");
}
}kotlin
package tutorial
import dev.sparktide.sdk.*
import dev.sparktide.sdk.kotlin.*
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.net.URI
@Configuration
open class ModelConfiguration {
@Bean
open fun models(@Value("\${tutorial.model-endpoint}") endpoint: URI,
@Value("\${tutorial.model-id}") modelId: String): RegistrationManifest {
val v = "1.0.0"
return RegistrationManifest(listOf(
openAIProvider("provider", v, endpoint, "MODEL_API_KEY", true),
modelDefinition("model", v, ReleaseBundle.Entry("provider", v), modelId,
ModelCapabilities.builder().supports(ModelCapabilities.Feature.STREAMING, true)
.supports(ModelCapabilities.Feature.TOOL_CALLING, true).build()),
modelBinding("binding", v, ReleaseBundle.Entry("model", v)),
modelProfile("profile", v, ModelProfile.builder("binding", v).maxOutputTokens(1024).build()),
agentDefinition("main", v, "你是业务助手,请用中文回答。",
ReleaseBundle.Entry("profile", v), emptyList(), emptyList())
)).managedBy("tutorial-service")
}
}完整请求地址和实际模型 ID 来自 business.env。Key 只由底座读取,代码中保存的是名称 MODEL_API_KEY。功能声明必须与服务实际能力匹配,不能靠填写 true 获得供应商不支持的功能。
3. 启用声明与聊天 HTTP API
src/main/resources/application.yml 已包含下面配置:
yaml
server:
address: 127.0.0.1
port: 8091
tutorial:
model-endpoint: ${TUTORIAL_MODEL_ENDPOINT}
model-id: ${TUTORIAL_MODEL_ID}
sparktide:
enabled: true
platform-url: ${SPARKTIDE_PLATFORM_URL}
app-id: tutorial
token: ${SPARKTIDE_TOKEN}
default-agent:
id: main
version: 1.0.0
deployment:
mode: DEVELOPMENT
source: tutorial-service
auto-sync: true
chat:
enabled: true
platform-url: ${SPARKTIDE_PLATFORM_URL}
app-id: tutorial
path: /api/aisparktide.enabled 控制能力注册;sparktide.chat.enabled 控制聊天 HTTP API。它们是独立开关。本教程把二者放在同一业务服务方便学习,生产可独立部署不持有管理凭据的聊天网关。
DEVELOPMENT + auto-sync=true 会预检并原子发布这份清单,设置默认入口;同内容再次启动不会新建版本。生产改成显式发布,第 7 步说明。
4. 接入用户身份
LocalIdentity.java 已提供完整的本地适配:先验证随机业务 Bearer 令牌,再产生固定可信 Principal,最后由 ChatCredentialResolver 返回底座 USER 凭据。只在 local profile 生效,后台绑定回环地址。
真实项目用原有 Spring Security / 登录过滤器产生 Principal,再将它映射到平台可验证凭据。不要从用户名请求头、Context 或模型文本获得可信身份;Cookie 登录还需要原业务 CSRF 校验。
你不需要编写聊天 Controller、转发会话数据库或自己转发 SSE,Starter 已提供它们。
5. 启动与检查
新开终端,在 tutorial/backend 执行:
powershell
& '..\scripts\use-env.ps1' '..\.local\business.env'
$env:SPRING_PROFILES_ACTIVE='local'
.\gradlew.bat run保持终端运行。另一终端在 tutorial 根目录执行:
powershell
& '.\scripts\use-env.ps1' '.\.local\business.env'
$headers=@{Authorization="Bearer $env:DEV_BROWSER_TOKEN"}
Invoke-RestMethod 'http://127.0.0.1:8091/api/ai/conversations' -Headers $headers检查点
首次应返回 items: [] 和 nextOffset: null。去掉 Authorization 应返回 401,表示业务登录校验在起作用。这个请求只检查身份和会话入口,模型将在第 5 步调用。
如果启动报告清单归属 / 版本冲突,先核对是否复用了他人 tutorial 应用;不覆盖或认领已有资源。配置变化要发布新版本,详见声明与发布。
