Genkit 框架发布 Agents API,内置消息历史、工具循环、流式处理。降低构建多轮对话 Agent 的开发复杂度。
宣布 Genkit Agents:对话式 AI 的全栈基础
Genkit 是一个开源框架,用于为任何平台构建全栈、AI 驱动和 agentic 应用程序,支持 TypeScript、Go、Dart 和 Python。一些最引人注目的 AI 功能是对话式的,比如能记住 ticket 的支持助手,或者可以跨多个回合工作的 copilot。每一个都需要的不仅仅是一个 generate() 调用,今天构建一个这样的应用意味着要手动连接消息历史、工具循环、流传输、持久化和前端协议。这些繁琐的工作在每个项目中都会重复,与你应用程序的独特之处几乎没有关系。
Genkit 通过 Agents API 解决了这个问题,它将所有这些都打包在一个接口后面。你在服务器上定义一个 agent,然后用相同的 chat() API 驱动它,无论它在进程中运行还是在 HTTP 端点后面运行。
Agents API 目前在 TypeScript 和 Go 中处于预览阶段。在次要版本发布中可能会引入破坏性变更。
一个 agent 需要一个名称和一个 system prompt 来开始。从那里开始,随着功能的增长,你可以添加工具、状态和会话存储。
import genkitx "github.com/firebase/genkit/go/genkit/exp"
g := genkit.Init(ctx,
genkit.WithPlugins(&googlegenai.GoogleAI{}),
genkit.WithExperimental(), // Enables preview features like Agents API.
)
assistant := genkitx.DefineAgent(g, "assistant",
aix.InlinePrompt{
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithSystem("You are a helpful assistant."),
},
)
out, err := assistant.RunText(ctx, "Hello. What can you do?")
if err != nil {
log.Fatal(err)
}
fmt.Println(out.Message.Text())
同一个 agent 对象是灵活的,可以处理一次性回复、流式回合、暂停的工具调用和多回合对话。随着功能的增长,你不需要采用不同的抽象。
每个对话都需要在回合之间保持连续性,而你决定谁拥有它。
添加一个存储,agent 就变成了服务器管理的。服务器将消息、自定义状态和工件作为快照保持,客户端通过发送回会话 ID 来继续。选择这个用于持久聊天应用、共享设备以及任何客户端不应该携带整个对话的工作流。
import firebasex "github.com/firebase/genkit/go/plugins/firebase/exp"
import genkitx "github.com/firebase/genkit/go/genkit/exp"
store, err := firebasex.NewFirestoreSessionStore[WeatherState](ctx, g,
firebasex.WithCollection("snapshots"),
firebasex.WithCheckpointInterval(10),
)
if err != nil {
log.Fatal(err)
}
weatherAgent := genkitx.DefineAgent(g, "weatherAgent",
aix.InlinePrompt{
ai.WithSystem("Answer weather questions. Ask for a location when one is missing."),
ai.WithTools(getWeather),
},
aix.WithSessionStore(store),
)
你配置的存储决定了快照存放在哪里。对于生产环境,Firestore 为你提供了一个托管的、多实例的数据库,多个服务器实例可以共享。Genkit 还为本地工作提供了更轻量级的存储,并允许你实现自己的存储,下面的部分将介绍这一点。
不配置存储,agent 就是客户端管理的:服务器返回完整状态,客户端在下一个回合时将其发送回去。当你的应用已经拥有持久化或你需要无状态的服务器部署时,使用这个方式。
每个成功的服务器管理的回合都会写入一个快照,所以你可以通过 sessionId 恢复最新状态,或通过 snapshotId 从历史中的精确点分支。分支让用户从任何保存的时刻探索替代方案,而不会破坏原始线程。
// Continue the latest state in a conversation.
out, err := weatherAgent.RunText(ctx, "Continue where we left off.",
aix.WithSessionID[WeatherState]("user-session-123"),
)
// Or branch from a specific saved point.
branch, err := weatherAgent.RunText(ctx, "Revise this plan for a smaller budget.",
aix.WithSnapshotID[WeatherState](approvedPlanSnapshotID),
)
除了消息历史外,agent 还携带两种其他类型的状态。自定义状态是你的类型化应用数据,是驱动下一个回合的紧凑控制和 UI 值,例如工作流状态、任务列表或选定的实体。工件是生成的输出,用户可以检查、下载或自己版本化,例如报告、补丁或行程单。工具通过活跃会话更新其中任何一个,Genkit 在更改发生时将其流式传输到客户端。
每个 agent 已经是一个可服务的操作,所以将一个放在 HTTP 端点后面只需几行代码。路由助手返回你挂载在标准 http.ServeMux 上的描述符,它们为你连接 turn 端点以及快照和中止伴随项。
import genkitx "github.com/firebase/genkit/go/genkit/exp"
mux := http.NewServeMux()
for _, route := range genkitx.AllAgentRoutes(g) {
mux.HandleFunc(route.Pattern(), route.Handler())
}
log.Fatal(http.ListenAndServe(":8080", mux))
下面的客户端使用的就是同一个 wire 协议,所以 JavaScript 或 Go 后端以相同的方式服务任何客户端。
连接你的服务器和客户端的部分是远程 agent。remoteAgent() 返回一个与本地 agent 相同的 chat() 接口的句柄,因此在后端测试中驱动 agent 的代码就是从浏览器驱动它的代码。不需要设计单独的请求和响应协议,也不需要发明流传输格式。
我们正在推出一个 JavaScript 客户端,因此 web 前端可以与同一个 agent 端点通信。以下是如何从 TypeScript 前端连接到远程 agent 的示例。
import { remoteAgent } from 'genkit/beta/client';
const agent = remoteAgent<WeatherState>({
url: 'http://localhost:8080/api/weatherAgent',
});
const chat = agent.chat();
const res = await chat.send('Weather in Tokyo?');
console.log(res.text);
客户端通过 agent 路由使用一个 wire 协议通信,因此它对 JavaScript 或 Go 后端的工作方式相同。它为每个请求解析动态身份验证头,应用流式状态补丁,并使用会话 ID、快照 ID 或客户端管理的状态继续下一个回合,无论你的 agent 使用哪一个。
流传输内置在同一个接口中。sendStream() 为你提供一个块流和最终响应,每个块可以在生成时携带文本、自定义状态或工件。
const turn = agent.chat().sendStream('Write a long report.');
for await (const chunk of turn.stream) {
if (chunk.text) process.stdout.write(chunk.text);
if (chunk.custom) updateStatus(chunk.custom);
if (chunk.artifact) renderArtifact(chunk.artifact);
}
const res = await turn.response;
如果你已经有使用 Vercel AI SDK UI 库的应用,@genkit-ai/vercel-ai 包为其 useChat 钩子提供了一个适配器。GenkitChatTransport 适配器将 useChat 连接到你的 Genkit agent,所以你可以从 Vercel 的 AI Elements 组件组装接口,同时获得后端 Genkit 的所有好处。
工具可以暂停 agent 并将控制权交还给用户。模型决定需要外部输入,工具中断,客户端批准、拒绝或提供缺失的值,然后回合继续。这是你在支付、部署或任何你不想自动运行的操作之前将人员纳入循环的方式。
import genkitx "github.com/firebase/genkit/go/genkit/exp"
import "github.com/firebase/genkit/go/ai/exp/tool"
runShell := genkitx.DefineInterruptibleTool(g, "run_shell",
"Run a shell command after a safety check.",
func(ctx context.Context, input ShellInput, confirm *Confirmation) (ShellOutput, error) {
if isRisky(input.Command) {
if confirm == nil {
return ShellOutput{}, tool.Interrupt(ShellInterrupt{
Command: input.Command,
Reason: "The command can modify files.",
})
} else if !confirm.Approved {
return ShellOutput{}, errors.New("user rejected shell command execution")
}
}
return execute(input.Command)
},
)
回合以中断原因和响应中的暂停请求结束。一旦用户回答,客户端就会恢复,运行时根据会话历史验证恢复有效负载,以防止工具被欺骗以伪造输入运行。
某些回合耗时比用户想等待的时间更长。通过服务器管理的状态,客户端可以分离一个回合,关闭标签页,并稍后通过快照 ID 重新连接。agent 继续在服务器上工作,将进度写入待处理快照,另一个会话可以轮询、等待或中止。
const chat = reportAgent.chat({ sessionId: 'report-123' });
const task = await chat.detach('Write the quarterly market report.');
// Persist this so any client can reconnect to the work later.
savePendingSnapshot(task.snapshotId);
for await (const snapshot of task.poll({ intervalMs: 1000 })) {
renderStatus(snapshot.status);
if (snapshot.status === 'completed') renderMessages(snapshot.state.messages);
}
这使得长期研究工作、多步骤规划和工具繁重的工作流实际可行,而无需保持连接打开或构建单独的工作队列。
当一个 prompt 无法很好地完成所有工作时,你可以将工作分散到专门的 agent 中,并让一个协调器组合它们的结果。Agents 中间件为每个子 agent 注入一个委派工具,因此协调器模型可以将请求的部分路由到正确的专家。使用 Genkit 的子 agent 为你提供完全的控制和实现自己的编排的能力。
import middlewarex "github.com/firebase/genkit/go/plugins/middleware/exp"
coordinator := genkit.DefineAgent(g, "coordinator",
aix.InlinePrompt{
ai.WithSystem("Delegate to specialists, inspect their results, then answer the user."),
ai.WithUse(
&middlewarex.Agents{
Agents: []aix.AgentRef{researcher.Ref(), coder.Ref()},
MaxDelegations: 5,
ArtifactStrategy: middlewarex.ArtifactStrategySession,
},
&middlewarex.Artifacts{Readonly: true},
),
},
)
委派在协调器的流中显示为普通的工具活动,专家工件可以合并到父会话中,因此最终答案可以建立在每个专家生成的基础上。
Genkit agents 是一个应用原始元素,内置于全栈、面向用户的应用中。在以下情况下改为考虑 Agent Development Kit (ADK):
多 agent 编排是整个系统,而不仅仅是一个功能。ADK 是为复杂的 agent 拓扑而专门构建的,其中 Genkit 的委派中间件故意更轻量,不内置在 agent 抽象的核心中。
你想要一个托管运行时,而不仅仅是一个库。ADK 与 Gemini Enterprise Agent Platform 上的 Agent Runtime 配对,用于托管、扩展和托管会话。
服务器管理的 agent 通过会话存储存储快照,Genkit 提供了几个,所以你可以将存储与你运行的地方相匹配:
内存中存储用于测试、演示和单进程实验。
文件存储用于本地开发和需要快照在重启后保留的单主机应用。
Firestore 用于 Google Cloud 或 Firebase 上的生产应用,这些应用需要托管的、多实例的数据库,无需编写存储代码。
当你需要使用自己的数据库、授权或有特定的保留策略时,使用自定义。你可以使用 store 接口实现自己的持久化层。
Agents 在 Genkit Developer UI 中是一等公民。新的 Agent Runner 让你启动对话、发送回合、观看流式输出和状态更新、驱动工具中断和检查快照,所有这些都无需编写客户端。这是在构建 agent 时锻炼它的最快方式,也是调试对话时重现对话的最快方式。
Agents API 将对话式、全栈 AI 的重复繁琐工作转变为你配置而不是重建的东西。在服务器上定义一个 agent,在你想要持久化时给它一个存储,并通过 remoteAgent() 从你的前端用相同的 chat() 接口驱动它。
前往全栈 agents 文档深入了解,或如果你是框架新手,可以开始使用 Genkit。该 API 处于 Beta 版本,所以我们想要你的反馈:使用你构建的内容和你想改变的内容提交问题。