Flutter集成Anthropic技术插件anthropic_sdk_dart的使用

发布于 1周前 作者 phonegap100 来自 Flutter

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

  • 非 WebIOClient
  • WebFetchClient(支持 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

1 回复

更多关于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插件:

  1. 初始化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));
}
  1. 创建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信息和用法示例。

回到顶部