- 示例工程
- 教程
- 后端
【免费下载链接】aws-doc-sdk-examples
Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below.
本篇技术指南以仓库rustv1/examples/bedrock-runtime目录下的官方示例为核心,系统讲解如何用 AWS SDK for Rust 对接 Amazon Bedrock Runtime 的基础模型:包括单轮 Converse 调用、ConverseStream 流式输出,以及一个完整的"模型 + 外部天气工具"的工具调用(Tool Use)场景。读完本文,你将掌握 Converse API 的请求构造、流式分块解析、JSON Schema 工具声明、多轮对话与工具结果回填的完整实现方式,可直接在本仓库代码基础上运行和二次开发。
一、概述:这是关于什么的示例集
Amazon Bedrock Runtime 是一项全托管的 AWS 服务,它让开发者能够方便地使用来自第三方提供商和 Amazon 自家的基础模型(Foundation Models)。本示例集展示了如何通过AWS SDK for Rust与该服务交互,对应的官方描述位于 rustv1/examples/bedrock-runtime/README.md,其代码全部位于 rustv1/examples/bedrock-runtime/src/bin 目录下。
整个示例集只包含一个 Cargo 包(package 名为bedrock-runtime,参见 rustv1/examples/bedrock-runtime/Cargo.toml),由三个独立的二进制程序组成:
| 二进制 | 源文件 | 说明 |
|---|---|---|
converse | converse.rs | 使用 Converse API 发起一次单轮文本对话 |
converse-stream | converse-stream.rs | 使用 ConverseStream API 以流式方式接收模型输出 |
tool-use | tool-use.rs | 场景示例:用 Converse API 结合外部天气工具,实现"应用—模型—工具"三方协作 |
二、运行前提(Prerequisites)
根据 rustv1/README.md,运行这些示例需要满足:
- AWS 账户与凭证:已按 AWS SDK for Rust 官方入门指南配置好默认凭证(Credentials)和默认区域(Region),可通过
AWS_PROFILE、AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY、AWS_REGION等环境变量注入(rustv1/README.md); - Rust 工具链:安装 Cargo(通常随 rustup 一并安装);
- 区域与模型权限:示例源码中硬编码了区域
us-east-1与模型 IDanthropic.claude-3-haiku-20240307-v1:0(如 converse.rs)。运行前需确认你的账号在该区域已开通 Anthropic Claude 模型的访问权限,否则会收到模型访问相关的错误响应。
示例的依赖在 Cargo.toml 中声明,核心包括:
[dependencies] aws-config = "1.5.4" # 加载 SDK 默认配置 aws-sdk-bedrockruntime = "1.40.0" # Bedrock Runtime 服务 SDK aws-smithy-runtime-api = "1.7.1" # Smithy 运行时 API(HTTP Response 类型等) aws-smithy-types = "1.2.0" # Smithy 数据类型(如 Document) reqwest = "0.12.5" # 用于工具场景中请求外部天气 API serde = "1.0.204" serde_json = "1.0.120" tokio = { version = "1.38.1", features = ["full"] } # 异步运行时 tracing = "0.1.40" tracing-subscriber = "0.3.18" # 结构化日志输出三、示例一:Converse 单轮对话
converse.rs 演示了最基础的 Bedrock Runtime 调用方式:通过ConverseAPI 向 Claude 发送一条用户消息并打印模型回复。
3.1 请求构造
核心调用逻辑在main函数中(converse.rs#L43-L77):
let sdk_config = aws_config::defaults(BehaviorVersion::latest()) .region(CLAUDE_REGION) .load() .await; let client = Client::new(&sdk_config); let response = client .converse() .model_id(MODEL_ID) .messages( Message::builder() .role(ConversationRole::User) .content(ContentBlock::Text(USER_MESSAGE.to_string())) .build() .map_err(|_| "failed to build message")?, ) .send() .await;关键点解读:
aws_config::defaults(BehaviorVersion::latest()):按最新行为版本加载 SDK 配置,随后通过.region(...)显式指定区域(这里固定为us-east-1);Client::new(&sdk_config):基于配置构建 Bedrock Runtime 客户端;converse()请求链:.model_id()指定基础模型 ID,.messages()传入对话消息列表;每条消息由Message::builder()构造,通过.role(ConversationRole::User)声明角色、.content(ContentBlock::Text(...))填充文本内容;.send().await:发起请求并等待响应,返回Result<ConverseOutput, SdkError<ConverseError>>。
3.2 解析响应文本
响应解析通过辅助函数get_converse_output_text完成(converse.rs#L79-L92):
fn get_converse_output_text(output: ConverseOutput) -> Result<String, BedrockConverseError> { let text = output .output() // 取 ConverseOutput 的 output 字段 .ok_or("no output")? // 可能是空 .as_message() // 转换为 Message 联合类型 .map_err(|_| "output not a message")? .content() // 取消息内容块列表 .first() .ok_or("no content in message")? .as_text() // 只接受纯文本内容块 .map_err(|_| "content is not text")? .to_string(); Ok(text) }这里体现了 Converse 响应模型的结构:ConverseOutput → Message → 内容块列表 → ContentBlock::Text,逐层解包并做类型检查,任何一层不符合预期都会返回明确的中文化错误信息。
3.3 错误处理模式
示例自定义了BedrockConverseError错误类型(converse.rs#L19-L40),并针对ConverseError的服务端错误做了映射:
impl From<&ConverseError> for BedrockConverseError { fn from(value: &ConverseError) -> Self { BedrockConverseError::from(match value { ConverseError::ModelTimeoutException(_) => "Model took too long", ConverseError::ModelNotReadyException(_) => "Model is not ready", _ => "Unknown", }) } }也就是说,当模型响应超时或模型尚未就绪时,程序会给出可读的错误提示;其余未知错误统一归类为 "Unknown"。这种"自定义错误类型 +From转换 + 服务端错误枚举匹配"的写法,是 SDK for Rust 项目中推荐采用的错误处理范式。
四、示例二:ConverseStream 流式输出
converse-stream.rs 演示了流式调用:模型一边生成一边返回内容增量(token),适合构建打字机式输出体验。
4.1 发起流式请求
调用入口同样在main中(converse-stream.rs#L70-L98),与 Converse 的唯一区别是使用converse_stream()方法:
let response = client .converse_stream() .model_id(MODEL_ID) .messages( Message::builder() .role(ConversationRole::User) .content(ContentBlock::Text(USER_MESSAGE.to_string())) .build() .map_err(|_| "failed to build message")?, ) .send() .await; let mut stream = match response { Ok(output) => Ok(output.stream), // ConverseStreamOutput 中携带事件流 Err(e) => Err(BedrockConverseStreamError::from( e.as_service_error().unwrap(), )), }?;4.2 逐块消费事件流
拿到output.stream后,通过recv().await循环接收流式事件(converse-stream.rs#L100-L116):
loop { let token = stream.recv().await; match token { Ok(Some(text)) => { let next = get_converse_output_text(text)?; print!("{}", next); // 增量打印,形成打字机效果 Ok(()) } Ok(None) => break, // 流结束 Err(e) => Err(e .as_service_error() .map(BedrockConverseStreamError::from) .unwrap_or(BedrockConverseStreamError( "Unknown error receiving stream".into(), ))), }? }Ok(Some(event)):收到一个流事件,立即提取文本并print!(注意用print!而非println!,因为内容是连续增量,最后再统一补一个换行);Ok(None):流已结束,跳出循环;Err(e):流传输过程中出错,转换为自定义错误类型。
4.3 增量事件的类型匹配
流事件的类型是ConverseStreamOutput,需要在辅助函数中按事件变体匹配(converse-stream.rs#L123-L133):
fn get_converse_output_text( output: ConverseStreamOutputType, ) -> Result<String, BedrockConverseStreamError> { Ok(match output { ConverseStreamOutputType::ContentBlockDelta(event) => match event.delta() { Some(delta) => delta.as_text().cloned().unwrap_or_else(|_| "".into()), None => "".into(), }, _ => "".into(), // 其他事件类型(如消息开始/结束标记)不产出文本 }) }只有当事件是ContentBlockDelta(内容块增量)且其delta携带文本时才输出内容;其余事件类型返回空字符串,从而保证只打印模型真正生成的文字。
4.4 流式专用错误处理
流式场景的错误分为两类,示例分别处理(converse-stream.rs#L37-L67):
ConverseStreamError:请求阶段的错误,同样区分ModelTimeoutException、ModelNotReadyException;ConverseStreamOutputError:流内事件级别的错误,细分ValidationException、ThrottlingException等,并通过message()方法提取具体错误文本(该类型要求引入error::ProvideErrorMetadatatrait)。
五、场景示例:使用 Converse API 的工具调用(Tool Use)
工具调用场景(核心逻辑起始于 tool-use.rs#L242)是本示例集中最完整、也最有实战价值的示例。它展示了"应用程序—生成式 AI 模型—外部工具/API"之间典型的交互闭环:模型本身不直接访问外界,而是通过声明好的工具(本例为一个天气查询工具)向外部世界获取实时数据,从而基于用户输入提供真实、实时的天气信息。
5.1 场景架构与调用闭环
整个tool-use场景在ToolUseScenario结构体中封装(tool-use.rs#L246-L251),它持有四份关键状态:
struct ToolUseScenario { client: Client, // Bedrock Runtime 客户端 conversation: Vec<Message>, // 完整的多轮对话历史 system_prompt: SystemContentBlock, // 系统提示词 tool_config: ToolConfiguration, // 工具配置(声明可用工具) }其运行闭环如下(由run→send_to_bedrock→process_model_response→handle_tool_use→invoke_tool组成):
- 交互式读取用户输入(get_input,输入以
x开头即退出); - 把用户消息追加进
conversation,连同系统提示词与工具配置一起发送给模型; - 检查响应中的
stop_reason(tool-use.rs#L333-L342):StopReason::ToolUse:模型要求调用工具,进入工具执行分支;StopReason::EndTurn:模型已给出最终答复,打印文本并结束本轮;
- 执行工具后将
ToolResultBlock以User角色的消息回填给模型,继续下一轮对话(循环上限为MAX_RECURSIONS = 5,防止无限循环)。
5.2 工具声明:JSON Schema 输入定义
工具通过ToolConfiguration+ToolSpecification声明(tool-use.rs#L253-L274):
let tool_config = ToolConfiguration::builder() .tools(Tool::ToolSpec( ToolSpecification::builder() .name(TOOL_NAME) // "Weather_Tool" .description(TOOL_DESCRIPTION) // 工具用途说明 .input_schema(ToolInputSchema::Json(make_tool_schema())) .build() .unwrap(), )) .build() .unwrap();其中make_tool_schema()以aws_smithy_types::Document构造标准的 JSON Schema(tool-use.rs#L53-L91),完整声明了工具入参结构:
{ "type": "object", "properties": { "latitude": { "type": "string", "description": "Geographical WGS84 latitude of the location." }, "longitude": { "type": "string", "description": "Geographical WGS84 longitude of the location." } }, "required": ["latitude", "longitude"] }这段 schema 是模型决定"何时调用、用什么参数调用"的依据,required字段确保模型必须同时给出经纬度两个参数。
5.3 系统提示词约束
SYSTEM_PROMPT常量为模型的行为划定了边界(tool-use.rs#L30-L44),例如:只使用Weather_Tool获取数据、由模型自行从地点名推断经纬度、绝不编造或猜测信息、只回答天气相关问题等。这类提示词约束是让工具调用结果可控的关键工程实践。
5.4 工具执行:对接 Open-Meteo 天气 API
模型一旦发起ToolUse,程序便执行invoke_tool(tool-use.rs#L376-L398),根据tool.name()分发到fetch_weather_data(tool-use.rs#L188-L240):
const ENDPOINT: &str = "https://api.open-meteo.com/v1/forecast"; async fn fetch_weather_data( tool_use: &ToolUseBlock, ) -> Result<ToolResultBlock, ToolUseScenarioError> { // 1. 从模型的 tool_use 输入中解析 latitude / longitude let input = tool_use.input(); let latitude = input.as_object().unwrap().get("latitude").unwrap().as_string().unwrap(); let longitude = input.as_object().unwrap().get("longitude").unwrap().as_string().unwrap(); // 2. 用 reqwest 请求 Open-Meteo 预报接口 let params = [ ("latitude", latitude), ("longitude", longitude), ("current_weather", "true"), ]; let response = reqwest::Client::new() .get(ENDPOINT) .query(¶ms) .send() .await .map_err(|e| ToolUseScenarioError(format!("Error requesting weather: {e:?}")))? .error_for_status() .map_err(|e| ToolUseScenarioError(format!("Failed to request weather: {e:?}")))?; // 3. 将响应文本封装为 ToolResultBlock 返回 let result = String::from_utf8(bytes.to_vec())?; Ok(ToolResultBlock::builder() .tool_use_id(tool_use.tool_use_id()) // 关联到本次工具调用 .content(ToolResultContentBlock::Text(result)) .build()?) }执行过程中会打印工具调用与响应信息(如Executing tool: Weather_Tool with input: ...),方便观察模型与工具的交互过程。
5.5 把工具结果回填给模型
handle_tool_use负责组装工具结果消息(tool-use.rs#L350-L374):遍历模型返回的内容块,遇到ContentBlock::Text就打印给用户,遇到ContentBlock::ToolUse就执行工具并把ToolResult收集起来,最后以User角色构造一条新消息(ContentBlock::ToolResult)追加进对话历史,再次调用send_to_bedrock()把结果交回模型,让模型基于真实天气数据生成最终答复。
注意:tool_use_id必须与模型发来的工具调用 ID 保持一致,Bedrock 才能把结果与对应的工具请求正确关联。
5.6 递归上限保护
MAX_RECURSIONS: i8 = 5(tool-use.rs#L48)用于限制单轮用户请求触发的最大"模型→工具→模型"循环次数,防止模型陷入反复调用工具的无限循环;若超过上限,程序会以Exceeded MAX_ITERATIONS when calling tools错误终止。
5.7 交互体验
程序启动与结束时分别打印header()与footer()(tool-use.rs#L156-L186),支持诸如以下自然语言查询:
What's the weather like in New York?Current weather for latitude 40.70, longitude -74.01Is it warmer in Rome or Barcelona today?
输入x并回车即可退出程序。
六、如何运行示例与测试
6.1 运行二进制
根据 rustv1/README.md 的说明,每个示例对应src/bin下的一个二进制,可在rustv1/examples/bedrock-runtime目录下用cargo run --bin执行:
cargo run --bin converse cargo run --bin converse-stream cargo run --bin tool-use6.2 运行测试与日志控制
- 单元测试:
cargo test不会对 AWS 账号产生任何变更或费用; - 集成测试:
cargo test -- --ignored,此类测试可能产生费用,具体以各示例目录说明为准; - 日志级别(RUST_LOG):示例使用
tracing_subscriber输出结构化日志(rustv1/README.md),常用取值包括:info:显示程序常规输出;{crate_name}=debug:显示更有用的逐操作细节;aws_smithy_http_tower::dispatch=trace:打印每次 AWS API 调用的完整 HTTP 请求;aws_smithy_http::middleware=trace:打印每次调用的完整 HTTP 响应。
6.3 费用与安全注意事项
按照官方 README 的明确提醒(rustv1/examples/bedrock-runtime/README.md):
- 运行这些代码可能产生 AWS 费用,运行测试同样可能产生费用,具体请查阅 AWS 定价与免费套餐说明;
- 建议为代码授予最小权限(least privilege),只授予完成任务所需的最低权限;
- 这些代码未经所有 AWS 区域测试,请结合各区域的服务可用情况使用;
- 由于工具场景会请求 Open-Meteo 这一外部服务,运行
tool-use时还需要具备访问外部网络的网络环境。
七、小结与进一步探索
从 converse.rs 的单轮调用,到 converse-stream.rs 的流式输出,再到 tool-use.rs 完整的工具调用场景,这套 Rust 示例覆盖了 Bedrock Runtime 最常用的三种交互形态。核心要点可以归纳为:
- 统一入口:
Client::converse()与Client::converse_stream()承担全部文本交互,模型由model_id指定; - 消息结构:一切交互都围绕
Message(role + content)展开,ContentBlock支持文本、工具调用、工具结果等多种变体; - 工具调用闭环:用 JSON Schema 声明工具 → 模型返回
StopReason::ToolUse→ 程序执行工具 → 以User角色回填ToolResultBlock→ 继续对话,直至EndTurn; - 工程化细节:自定义错误类型、流式事件匹配、递归上限与系统提示词约束,都是生产级应用的必备要素。
如需继续深入研究,可对照阅读:
- rustv1/README.md(全部 Rust 示例的通用前提、测试与日志说明)
- rustv1/examples/bedrock-runtime/Cargo.toml(依赖与版本)
- rustv1/examples/bedrock-runtime/src/bin/tool-use.rs(工具调用场景完整实现)
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0
- 示例工程
- 教程
- 后端
【免费下载链接】aws-doc-sdk-examples
Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below.
相关推荐
使用 AWS SDK for JavaScript (v3) 调用 Amazon Bedrock Runtime:Converse、流式输出与工具调用实战指南
使用 AWS SDK for JavaScript v3 调用 Amazon Bedrock Runtime:Converse、流式输出与工具调用实战指南 本文
示例工程教程后端使用 AWS SDK for .NET 调用 Amazon Bedrock Runtime:Converse、工具调用与图像生成实战指南
使用 AWS SDK for .NET 调用 Amazon Bedrock Runtime:Converse、工具调用与图像生成实战指南 导读 本文以开源仓库
示例工程教程后端AWS SDK for Go V2 调用 Amazon Bedrock Runtime:Converse 与 InvokeModel 实战指南
AWS SDK for Go V2 调用 Amazon Bedrock Runtime:Converse 与 InvokeModel 实战指南 Amazon B
示例工程教程后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考