Flutter集成Anthropic技术插件anthropic_sdk_dart的使用
Flutter集成Anthropic技术插件anthropic_sdk_dart的使用
简介
anthropic_sdk_dart
是一个非官方的 Dart 客户端,用于与 Anthropic API(也称为 Claude API)进行交互。该客户端支持所有平台,包括在 Web 上的流式传输,并且完全类型安全、文档齐全且经过测试。
特性
- 完全类型安全:所有请求和响应都是类型安全的。
- 多平台支持:支持所有平台,包括 Web 上的流式传输。
- 自定义配置:支持自定义基础 URL、头信息和查询参数(例如 HTTP 代理)。
- 自定义 HTTP 客户端:支持自定义 HTTP 客户端(例如 SOCKS5 代理或高级用例)。
支持的端点
- 消息:支持工具和流式传输。
- 消息批次:支持批量处理消息。
使用指南
认证
Anthropic API 使用 API 密钥进行认证。请访问 Anthropic 控制台 获取您的 API 密钥。
final apiKey = Platform.environment['ANTHROPIC_API_KEY'];
final client = AnthropicClient(apiKey: apiKey);
消息
创建消息
发送结构化的消息列表,模型将生成对话中的下一条消息。
final res = await client.createMessage(
request: CreateMessageRequest(
model: Model.model(Models.claude35Sonnet20241022),
maxTokens: 1024,
messages: [
Message(
role: MessageRole.user,
content: MessageContent.text('Hello, Claude'),
),
],
),
);
print(res.content.text);
// Hello! It's nice to meet you. How are you doing today?
流式传输消息
final stream = client.createMessageStream(
request: CreateMessageRequest(
model: Model.model(Models.claude35Sonnet20241022),
maxTokens: 1024,
messages: [
Message(
role: MessageRole.user,
content: MessageContent.text('Hello, Claude'),
),
],
),
);
await for (final res in stream) {
res.map(
messageStart: (MessageStartEvent e) {},
messageDelta: (MessageDeltaEvent e) {},
messageStop: (MessageStopEvent e) {},
contentBlockStart: (ContentBlockStartEvent e) {},
contentBlockDelta: (ContentBlockDeltaEvent e) {
stdout.write(e.delta.text);
},
contentBlockStop: (ContentBlockStopEvent e) {},
ping: (PingEvent e) {},
error: (ErrorEvent v) {},
);
}
// Hello! It's nice to meet you. How are you doing today?
工具使用
Claude 可以与外部客户端工具和函数交互,允许您为 Claude 配备自定义工具以执行更广泛的任务。
定义工具
Map<String, dynamic> _getCurrentWeather(
final String location,
final String unit,
) {
const temperature = 22;
const weather = 'Sunny';
return {
'temperature': unit == 'celsius' ? temperature : (temperature * 9 / 5) + 32,
'unit': unit,
'description': weather,
};
}
const tool = Tool.custom(
name: 'get_current_weather',
description: 'Get the current weather in a given location',
inputSchema: {
'type': 'object',
'properties': {
'location': {
'type': 'string',
'description': 'The city and state, e.g. San Francisco, CA',
},
'unit': {
'type': 'string',
'description': 'The unit of temperature to return',
'enum': ['celsius', 'fahrenheit'],
},
},
'required': ['location'],
},
);
使用工具
final request1 = CreateMessageRequest(
model: Model.model(Models.claude35Sonnet20241022),
messages: [
Message(
role: MessageRole.user,
content: MessageContent.text(
'What’s the weather like in Boston right now?',
),
),
],
tools: [tool],
toolChoice: ToolChoice(
type: ToolChoiceType.tool,
name: tool.name,
),
maxTokens: 1024,
);
final aiMessage1 = await client.createMessage(request: request1);
final toolUse = aiMessage1.content.blocks.firstOrNull;
if (toolUse == null || toolUse is! ToolUseBlock) {
return;
}
// 调用您的工具
final toolResult = _getCurrentWeather(
toolUse.input['location'],
toolUse.input['unit'],
);
final request2 = CreateMessageRequest(
model: Model.model(Models.claude35Sonnet20241022),
messages: [
Message(
role: MessageRole.user,
content: MessageContent.text(
'What’s the weather like in Boston right now in Fahrenheit?',
),
),
Message(
role: MessageRole.assistant,
content: aiMessage1.content,
),
Message(
role: MessageRole.user,
content: MessageContent.blocks([
Block.toolResult(
toolUseId: toolUse.id,
content: ToolResultBlockContent.text(json.encode(toolResult)),
),
]),
),
],
tools: [tool],
maxTokens: 1024,
);
final aiMessage2 = await client.createMessage(request: request2);
print(aiMessage2.content.text);
// Based on the current weather information for Boston, here's what it's like right now:
//
// The temperature in Boston is 71.6°F (Fahrenheit).
// The weather conditions are described as sunny.
流式传输工具输入
final stream = client.createMessageStream(request: request);
await for (final res in stream) {
res.map(
messageStart: (MessageStartEvent v) {},
messageDelta: (MessageDeltaEvent v) {},
messageStop: (MessageStopEvent v) {},
contentBlockStart: (ContentBlockStartEvent v) {},
contentBlockDelta: (ContentBlockDeltaEvent v) {
stdout.write(v.delta.inputJson);
},
contentBlockStop: (ContentBlockStopEvent v) {},
ping: (PingEvent v) {},
error: (ErrorEvent v) {},
);
}
// {"location": "Boston, MA", "unit": "fahrenheit"}
计算机使用
Claude 3.5 Sonnet 模型可以与工具交互,这些工具可以操作计算机桌面环境。
const request = CreateMessageRequest(
model: Model.model(Models.claude35Sonnet20241022),
messages: [
Message(
role: MessageRole.user,
content: MessageContent.text(
'Save a picture of a cat to my desktop. '
'After each step, take a screenshot and carefully evaluate if you '
'have achieved the right outcome. Explicitly show your thinking: '
'"I have evaluated step X..." If not correct, try again. '
'Only when you confirm a step was executed correctly should '
'you move on to the next one.',
),
),
],
tools: [
Tool.computerUse(displayWidthPx: 1024, displayHeightPx: 768),
Tool.textEditor(),
Tool.bash(),
],
maxTokens: 1024,
);
final res = await client.createMessage(request: request);
提示缓存
提示缓存是一个强大的功能,可以通过从特定前缀恢复来优化 API 使用,从而显著减少处理时间和成本。
final request = CreateMessageRequest(
model: Model.model(Models.claude35Sonnet20241022),
system: CreateMessageRequestSystem.blocks([
Block.text(
text:
'You are an AI assistant tasked with analyzing literary works. '
'Your goal is to provide insightful commentary on themes, characters, and writing style.',
),
Block.text(
cacheControl: CacheControlEphemeral(),
text: '<The whole text of the book>',
),
]),
messages: [
Message(
role: MessageRole.user,
content: MessageContent.text("What's the theme of the work?"),
),
],
maxTokens: 1024,
);
final res1 = await client.createMessage(request: request);
print(res1.usage?.cacheCreationInputTokens); // 5054
print(res1.usage?.cacheReadInputTokens); // 0
final res2 = await client.createMessage(request: request);
print(res2.usage?.cacheCreationInputTokens); // 0
print(res2.usage?.cacheReadInputTokens); // 5054
消息批次
消息批次 API 是一种强大且经济高效的方式,用于异步处理大量消息请求。这种方法适用于不需要立即响应的任务,可以减少 50% 的成本并提高吞吐量。
准备和创建批次
const batchRequest = CreateMessageBatchRequest(
requests: [
BatchMessageRequest(
customId: 'request1',
params: CreateMessageRequest(
model: Model.model(Models.claudeInstant12),
temperature: 0,
maxTokens: 1024,
messages: [
Message(
role: MessageRole.user,
content: MessageContent.text(
'List the numbers from 1 to 9 in order.'),
),
],
),
),
BatchMessageRequest(
customId: 'request2',
params: CreateMessageRequest(
model: Model.model(Models.claudeInstant12),
temperature: 0,
maxTokens: 1024,
messages: [
Message(
role: MessageRole.user,
content: MessageContent.text(
'List the numbers from 10 to 19 in order.'),
),
],
),
),
],
);
var batch = await client.createMessageBatch(request: batchRequest);
print(batch.id);
跟踪批次
do {
await Future<void>.delayed(const Duration(seconds: 5));
batch = await client.retrieveMessageBatch(id: batch.id);
} while (batch.processingStatus == MessageBatchProcessingStatus.inProgress);
获取批次结果
batch = await client.retrieveMessageBatch(id: batch.id);
print(batch.resultsUrl);
高级用法
默认 HTTP 客户端
默认情况下,客户端使用 https://api.anthropic.com/v1
作为 baseUrl
,并使用以下实现的 http.Client
:
- 非 Web:
IOClient
- Web:
FetchClient
(支持 Web 上的流式传输)
自定义 HTTP 客户端
您可以提供自己的 http.Client
实现以进行进一步的自定义:
final client = AnthropicClient(
apiKey: 'MISTRAL_API_KEY',
client: MyHttpClient(),
);
使用代理
HTTP 代理
您可以使用自己的 HTTP 代理,通过覆盖 baseUrl
并提供所需的 headers
:
final client = AnthropicClient(
baseUrl: 'https://my-proxy.com',
headers: {
'x-my-proxy-header': 'value',
},
);
如果需要进一步自定义,您可以提供自己的 http.Client
。
SOCKS5 代理
要使用 SOCKS5 代理,您可以使用 socks5_proxy
包:
final baseHttpClient = HttpClient();
SocksTCPClient.assignToHttpClient(baseHttpClient, [
ProxySettings(InternetAddress.loopbackIPv4, 1080),
]);
final httpClient = IOClient(baseClient);
final client = AnthropicClient(
client: httpClient,
);
致谢
该客户端的生成得益于 openapi_spec 包。
许可证
anthropic_sdk_dart
采用 MIT 许可证。
示例代码
// ignore_for_file: avoid_print
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:anthropic_sdk_dart/anthropic_sdk_dart.dart';
Future<void> main() async {
final client = AnthropicClient(
apiKey: Platform.environment['ANTHROPIC_API_KEY'],
);
await _createMessage(client);
await _createMessageStream(client);
await _toolUse(client);
await _toolUseStreaming(client);
client.endSession();
}
Future<void> _createMessage(final AnthropicClient client) async {
final res = await client.createMessage(
request: const CreateMessageRequest(
model: Model.model(Models.claude35Sonnet20240620),
maxTokens: 1024,
messages: [
Message(
role: MessageRole.user,
content: MessageContent.text('Hello, Claude'),
),
],
),
);
print(res.content.text);
// Hello! It's nice to meet you. How are you doing today?
}
Future<void> _createMessageStream(final AnthropicClient client) async {
final stream = client.createMessageStream(
request: const CreateMessageRequest(
model: Model.model(Models.claude35Sonnet20240620),
maxTokens: 1024,
messages: [
Message(
role: MessageRole.user,
content: MessageContent.text('Hello, Claude'),
),
],
),
);
await for (final res in stream) {
res.map(
messageStart: (MessageStartEvent e) {},
messageDelta: (MessageDeltaEvent e) {},
messageStop: (MessageStopEvent e) {},
contentBlockStart: (ContentBlockStartEvent e) {},
contentBlockDelta: (ContentBlockDeltaEvent e) {
stdout.write(e.delta.text);
},
contentBlockStop: (ContentBlockStopEvent e) {},
ping: (PingEvent e) {},
error: (ErrorEvent e) {},
);
}
// Hello! It's nice to meet you. How are you doing today?
}
Future<void> _toolUse(final AnthropicClient client) async {
final request1 = CreateMessageRequest(
model: const Model.model(Models.claude35Sonnet20240620),
messages: [
const Message(
role: MessageRole.user,
content: MessageContent.text(
'What’s the weather like in Boston right now?',
),
),
],
tools: [tool],
toolChoice: ToolChoice(
type: ToolChoiceType.tool,
name: tool.name,
),
maxTokens: 1024,
);
final aiMessage1 = await client.createMessage(request: request1);
final toolUse = aiMessage1.content.blocks.firstOrNull;
if (toolUse == null || toolUse is! ToolUseBlock) {
return;
}
// 调用您的工具
final toolResult = _getCurrentWeather(
toolUse.input['location'],
toolUse.input['unit'],
);
final request2 = CreateMessageRequest(
model: const Model.model(Models.claude35Sonnet20240620),
messages: [
const Message(
role: MessageRole.user,
content: MessageContent.text(
'What’s the weather like in Boston right now in Fahrenheit?',
),
),
Message(
role: MessageRole.assistant,
content: aiMessage1.content,
),
Message(
role: MessageRole.user,
content: MessageContent.blocks([
Block.toolResult(
toolUseId: toolUse.id,
content: ToolResultBlockContent.text(json.encode(toolResult)),
),
]),
),
],
tools: [tool],
maxTokens: 1024,
);
final aiMessage2 = await client.createMessage(request: request2);
print(aiMessage2.content.text);
// Based on the current weather information for Boston, here's what it's like right now:
//
// The temperature in Boston is 71.6°F (Fahrenheit).
// The weather conditions are described as sunny.
}
Future<void> _toolUseStreaming(final AnthropicClient client) async {
final request = CreateMessageRequest(
model: const Model.model(Models.claude35Sonnet20240620),
messages: [
const Message(
role: MessageRole.user,
content: MessageContent.text(
'What’s the weather like in Boston right now in Fahrenheit?',
),
),
],
tools: [tool],
toolChoice: ToolChoice(
type: ToolChoiceType.tool,
name: tool.name,
),
maxTokens: 1024,
);
final stream = client.createMessageStream(request: request);
await for (final res in stream) {
res.map(
messageStart: (MessageStartEvent v) {},
messageDelta: (MessageDeltaEvent v) {},
messageStop: (MessageStopEvent v) {},
contentBlockStart: (ContentBlockStartEvent v) {},
contentBlockDelta: (ContentBlockDeltaEvent v) {
stdout.write(v.delta.inputJson);
},
contentBlockStop: (ContentBlockStopEvent v) {},
ping: (PingEvent v) {},
error: (ErrorEvent v) {},
);
}
// {"location": "Boston, MA", "unit": "fahrenheit"}
}
Map<String, dynamic> _getCurrentWeather(
final String location,
final String unit,
) {
const temperature = 22;
const weather = 'Sunny';
return {
'temperature': unit == 'celsius' ? temperature : (temperature * 9 / 5) + 32,
'unit': unit,
'description': weather,
};
}
const tool = Tool.custom(
name: 'get_current_weather',
description: 'Get the current weather in a given location',
inputSchema: {
'type': 'object',
'properties': {
'location': {
'type': 'string',
'description': 'The city and state, e.g. San Francisco, CA',
},
'unit': {
'type': 'string',
'description': 'The unit of temperature to return',
'enum': ['celsius', 'fahrenheit'],
},
},
'required': ['location'],
},
);
希望这些内容能帮助您更好地理解和使用 anthropic_sdk_dart
插件。如果您有任何问题或需要进一步的帮助,请随时提问!
更多关于Flutter集成Anthropic技术插件anthropic_sdk_dart的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter集成Anthropic技术插件anthropic_sdk_dart的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter项目中集成并使用Anthropic技术的插件anthropic_sdk_dart
的代码示例。请注意,由于Anthropic的技术和API可能会随时间变化,因此下面的代码示例基于一个假设的插件接口,实际使用时请参考最新的官方文档。
首先,确保你已经在Flutter项目的pubspec.yaml
文件中添加了anthropic_sdk_dart
依赖:
dependencies:
flutter:
sdk: flutter
anthropic_sdk_dart: ^latest_version # 替换为最新版本号
然后,运行flutter pub get
来获取依赖。
接下来,在你的Flutter项目中,你可以按照以下步骤使用anthropic_sdk_dart
插件:
-
初始化Anthropic客户端:
通常,你需要提供API密钥或其他认证信息来初始化客户端。这里假设
anthropic_sdk_dart
提供了一个AnthropicClient
类。
import 'package:anthropic_sdk_dart/anthropic_sdk_dart.dart';
void main() async {
// 替换为你的API密钥
String apiKey = 'your_anthropic_api_key';
// 初始化Anthropic客户端
AnthropicClient client = AnthropicClient(apiKey: apiKey);
runApp(MyApp(client: client));
}
-
创建Flutter应用并使用Anthropic服务:
在你的Flutter应用的主文件中(通常是
main.dart
),你可以创建一个使用Anthropic服务的组件。
import 'package:flutter/material.dart';
import 'package:anthropic_sdk_dart/anthropic_sdk_dart.dart';
void main() async {
String apiKey = 'your_anthropic_api_key';
AnthropicClient client = AnthropicClient(apiKey: apiKey);
runApp(MyApp(client: client));
}
class MyApp extends StatelessWidget {
final AnthropicClient client;
MyApp({required this.client});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Anthropic Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Anthropic Example'),
),
body: Center(
child: AnthropicTextGenerator(client: client),
),
),
);
}
}
class AnthropicTextGenerator extends StatefulWidget {
final AnthropicClient client;
AnthropicTextGenerator({required this.client});
@override
_AnthropicTextGeneratorState createState() => _AnthropicTextGeneratorState();
}
class _AnthropicTextGeneratorState extends State<AnthropicTextGenerator> {
String generatedText = '';
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () async {
try {
// 假设有一个generateText方法接受prompt和options
String prompt = 'Write a story about a magical forest.';
Map<String, dynamic> options = {}; // 根据需要设置选项
String result = await widget.client.generateText(prompt: prompt, options: options);
setState(() {
generatedText = result;
});
} catch (e) {
print('Error generating text: $e');
}
},
child: Text('Generate Text'),
),
Text(generatedText),
],
);
}
}
在上面的代码中,我们创建了一个简单的Flutter应用,其中包含一个按钮,用于触发Anthropic文本生成服务。当用户点击按钮时,应用会向Anthropic服务发送一个请求,生成与提供的prompt
相关的文本,并在UI中显示生成的文本。
请注意,由于anthropic_sdk_dart
插件的具体API和实现细节可能有所不同,因此上述代码是基于假设的API设计的。在实际使用中,请查阅anthropic_sdk_dart
的官方文档以获取最新的API信息和用法示例。