自定义 LLM 提供商
如何与其他 Flutter 功能集成。
连接 LLM 与 LlmChatView 的协议由 LlmProvider 接口
表达:
abstract class LlmProvider implements Listenable {
Stream<String> generateStream(String prompt, {Iterable<Attachment> attachments});
Stream<String> sendMessageStream(String prompt, {Iterable<Attachment> attachments});
Iterable<ChatMessage> get history;
set history(Iterable<ChatMessage> history);
}
LLM 可在云端或本地,可托管于 Google Cloud Platform 或其他云提供商,可以是专有或开源 LLM。任何可实现该接口的 LLM 或类 LLM 端点都可作为 LLM 提供商接入聊天视图。
AI 工具包自带两个提供商,均实现接入所需的 LlmProvider 接口:
-
Firebase AI Logic provider,封装
firebase_aipackage -
Echo provider,可作为最简 provider 示例
实现
#构建自有提供商时,实现 LlmProvider 接口需注意:
提供完整配置支持
处理历史记录
将消息与附件转换为底层 LLM 格式
调用底层 LLM
-
配置要在自定义提供商中支持完整可配置性,应让用户创建底层模型并作为参数传入,如
MyLlmProvider:
class MyLlmProvider extends LlmProvider ... {
@immutable
MyLlmProvider({
required GenerativeModel model,
...
}) : _model = model,
...
final GenerativeModel _model;
...
}
这样无论底层模型未来如何变化,自定义提供商的用户仍可使用全部配置项。
-
历史记录
历史记录是任何提供商的重要部分——不仅需支持直接操作历史,还须在变更时通知监听者。此外,为支持序列化与更改提供商参数,构造过程中还须支持保存历史。
Firebase provider 的处理方式如下:
class MyLlmProvider extends LlmProvider with ChangeNotifier {
@immutable
MyLlmProvider({
required GenerativeModel model,
Iterable<ChatMessage>? history,
...
}) : _model = model,
_history = history?.toList() ?? [],
... { ... }
final GenerativeModel _model;
final List<ChatMessage> _history;
...
@override
Stream<String> sendMessageStream(
String prompt, {
Iterable<Attachment> attachments = const [],
}) async* {
final userMessage = ChatMessage.user(prompt, attachments);
final llmMessage = ChatMessage.llm();
_history.addAll([userMessage, llmMessage]);
final response = _generateStream(
prompt: prompt,
attachments: attachments,
contentStreamGenerator: _chat!.sendMessageStream,
);
yield* response.map((chunk) {
llmMessage.append(chunk);
return chunk;
});
notifyListeners();
}
@override
Iterable<ChatMessage> get history => _history;
@override
set history(Iterable<ChatMessage> history) {
_history.clear();
_history.addAll(history);
_chat = _startChat(history);
notifyListeners();
}
...
}
你会注意到代码中的几点:
-
使用
ChangeNotifier实现LlmProvider接口对Listenable的要求 可将初始历史作为构造参数传入
-
出现新的用户提示词/LLM 回复对时通知监听者
手动更改历史时通知监听者
历史变更时用新历史创建新聊天
本质上,自定义提供商管理单次聊天会话与底层 LLM 的历史。历史变更时,底层聊天需自动保持同步(如 Firebase provider 在调用聊天专用方法时),或手动重建(如 Firebase provider 在手动设置历史时)。
消息与附件
附件必须从 LlmProvider 暴露的标准 ChatMessage 类映射到底层 LLM 所处理的类型。例如,Firebase provider 将 AI 工具包的 ChatMessage 映射为
Firebase Logic AI SDK 的 Content 类型,如下所示:
import 'package:firebase_ai/firebase_ai.dart';
...
class MyLlmProvider extends LlmProvider with ChangeNotifier {
...
static Part _partFrom(Attachment attachment) => switch (attachment) {
(final FileAttachment a) => DataPart(a.mimeType, a.bytes),
(final LinkAttachment a) => FilePart(a.url),
};
static Content _contentFrom(ChatMessage message) => Content(
message.origin.isUser ? 'user' : 'model',
[
TextPart(message.text ?? ''),
...message.attachments.map(_partFrom),
],
);
}
每当需要向底层 LLM 发送用户提示词时会调用 _contentFrom 方法。每个提供商都需自行实现映射。
调用 LLM
如何实现 generateStream 与 sendMessageStream 取决于底层 LLM 暴露的协议。
AI 工具包中的 Firebase provider 处理配置与历史,但对 generateStream 与 sendMessageStream 的调用最终都会落到 Firebase Logic AI SDK 的 API:
class MyLlmProvider extends LlmProvider with ChangeNotifier {
...
@override
Stream<String> generateStream(
String prompt, {
Iterable<Attachment> attachments = const [],
}) =>
_generateStream(
prompt: prompt,
attachments: attachments,
contentStreamGenerator: (c) => _model.generateContentStream([c]),
);
@override
Stream<String> sendMessageStream(
String prompt, {
Iterable<Attachment> attachments = const [],
}) async* {
final userMessage = ChatMessage.user(prompt, attachments);
final llmMessage = ChatMessage.llm();
_history.addAll([userMessage, llmMessage]);
final response = _generateStream(
prompt: prompt,
attachments: attachments,
contentStreamGenerator: _chat!.sendMessageStream,
);
yield* response.map((chunk) {
llmMessage.append(chunk);
return chunk;
});
notifyListeners();
}
Stream<String> _generateStream({
required String prompt,
required Iterable<Attachment> attachments,
required Stream<GenerateContentResponse> Function(Content)
contentStreamGenerator,
}) async* {
final content = Content('user', [
TextPart(prompt),
...attachments.map(_partFrom),
]);
final response = contentStreamGenerator(content);
yield* response
.map((chunk) => chunk.text)
.where((text) => text != null)
.cast<String>();
}
@override
Iterable<ChatMessage> get history => _history;
@override
set history(Iterable<ChatMessage> history) {
_history.clear();
_history.addAll(history);
_chat = _startChat(history);
notifyListeners();
}
}
示例
#Firebase provider 实现是构建自定义提供商的良好起点。若想看去掉所有底层 LLM 调用的示例实现,请参阅 Echo 示例应用:它将用户提示词与附件格式化为 Markdown 作为回复返回。
除非另有说明,本文档之所提及适用于 Flutter 3.44.0 版本。本页面最后更新时间:2026-06-12。查看文档源码 或者 为本页面内容提出建议。