传统视频分析通常要求用户先选择“语音转写”“人物识别”或“物体检测”,再分别查看各服务的结果。Agentic 架构改变的是这一层交互:用户直接提问,单个 Strands Agents SDK Agent 根据问题决定调用 Amazon Transcribe、Amazon Rekognition,或同时调用两者,最后交给 Amazon Bedrock 组织答案。
这种方案的重点不是让大模型直接“观看”整段视频,而是让 Agent 成为调度器,把专用服务产生的结构化证据组合起来。
一条问题如何变成答案
可以把处理链路拆成四层:
- 视频上传到 Amazon S3。
- Agent 接收自然语言问题,例如“视频里什么时候出现了汽车?”
- Agent 根据问题选择工具:
- 对话、演讲内容、关键词:调用 Amazon Transcribe。
- 人物、物体、场景及出现时间:调用 Amazon Rekognition。
- “演讲者提到汽车时,画面里真的有汽车吗?”:同时调用两项服务。
- Amazon Bedrock 根据工具返回的转写文本、标签和时间戳生成自然语言答案。
关键设计是让工具返回可验证的证据,而不是只返回一个模糊结论。例如,Rekognition 工具应保留标签、置信度和毫秒时间戳;Transcribe 工具则应保留完整文本,生产环境中还可以进一步保留词级时间戳。
用户问题
│
▼
Strands Agent ─────► Amazon Bedrock:理解问题、规划调用、整理答案
│
├───────────────► Amazon Transcribe:语音与对白
│
└───────────────► Amazon Rekognition:物体、人物、场景与时间点
│
▼
Amazon S3 视频
为什么要由 Agent 动态选服务
如果每个问题都无条件运行全部分析,系统虽然容易实现,却会增加延迟、费用和上下文长度。基于意图的工具路由更合适:
| 用户问题 | 建议调用 |
|---|---|
| “主持人总结了哪三点?” | Transcribe |
| “第一个人什么时候出现?” | Rekognition |
| “画面出现产品时,旁白说了什么?” | Rekognition + Transcribe |
| “这段视频的文件名是什么?” | S3 元数据工具即可 |
Agent 的价值在于运行时决策,但它不应该拥有无限权限。每个工具应只暴露完成任务所需的参数和 AWS 权限,并限制可访问的 S3 Bucket、最长视频时长、轮询时间和返回数据量。
还要注意,Transcribe 和 Rekognition 的视频任务通常是异步任务。“数秒内回答”更适合已经预处理、短视频或命中缓存的场景。对较长视频,建议在上传后提前生成分析结果,提问时只让 Agent 查询结果;若坚持按问题实时启动任务,就需要向用户明确展示处理中状态。
可改造的最小 Python 示例
下面的示例让 Strands Agent 自行选择两个工具:一个启动 Amazon Transcribe 转写,另一个运行 Amazon Rekognition 标签检测。代码假设视频已经放在 S3,AWS 凭证也已配置。
安装依赖:
python -m venv .venv
source .venv/bin/activate
pip install strands-agents boto3
export AWS_REGION=us-east-1
export VIDEO_BUCKET=my-video-bucket
export VIDEO_KEY=uploads/demo.mp4
运行前需要在目标区域启用可供 Strands 使用的 Amazon Bedrock 模型,并为当前身份授予最小权限。至少会涉及读取目标 S3 对象、启动和查询 Transcribe 任务、启动和查询 Rekognition 视频任务,以及调用选定 Bedrock 模型。
将以下内容保存为 video_agent.py:
import json
import os
import time
import urllib.request
import uuid
import boto3
from strands import Agent, tool
REGION = os.getenv("AWS_REGION", "us-east-1")
BUCKET = os.environ["VIDEO_BUCKET"]
KEY = os.environ["VIDEO_KEY"]
transcribe = boto3.client("transcribe", region_name=REGION)
rekognition = boto3.client("rekognition", region_name=REGION)
def wait(seconds: int = 5) -> None:
time.sleep(seconds)
@tool
def transcribe_video() -> str:
"""Transcribe the speech in the configured S3 video and return its text."""
job_name = f"video-agent-{uuid.uuid4().hex}"
transcribe.start_transcription_job(
TranscriptionJobName=job_name,
Media={"MediaFileUri": f"s3://{BUCKET}/{KEY}"},
IdentifyLanguage=True,
)
while True:
response = transcribe.get_transcription_job(
TranscriptionJobName=job_name
)
job = response["TranscriptionJob"]
status = job["TranscriptionJobStatus"]
if status == "COMPLETED":
transcript_uri = job["Transcript"]["TranscriptFileUri"]
with urllib.request.urlopen(transcript_uri, timeout=30) as result:
payload = json.load(result)
return payload["results"]["transcripts"][0]["transcript"]
if status == "FAILED":
return f"Transcription failed: {job.get('FailureReason', 'unknown')}"
wait()
@tool
def detect_video_labels() -> str:
"""Detect objects and scenes in the configured video, including timestamps."""
started = rekognition.start_label_detection(
Video={"S3Object": {"Bucket": BUCKET, "Name": KEY}},
MinConfidence=75,
)
job_id = started["JobId"]
while True:
page = rekognition.get_label_detection(
JobId=job_id,
MaxResults=1000,
SortBy="TIMESTAMP",
)
status = page["JobStatus"]
if status == "FAILED":
return f"Label detection failed: {page.get('StatusMessage', 'unknown')}"
if status == "SUCCEEDED":
break
wait()
detections = []
while True:
for item in page.get("Labels", []):
label = item["Label"]
detections.append(
{
"time_seconds": round(item["Timestamp"] / 1000, 2),
"label": label["Name"],
"confidence": round(label["Confidence"], 1),
}
)
token = page.get("NextToken")
if not token or len(detections) >= 300:
break
page = rekognition.get_label_detection(
JobId=job_id,
MaxResults=1000,
SortBy="TIMESTAMP",
NextToken=token,
)
return json.dumps(detections[:300], ensure_ascii=False)
SYSTEM_PROMPT = """
你是视频分析助手。根据问题只调用必要的工具:
- 涉及说话内容、主题、关键词时,调用 transcribe_video。
- 涉及画面中的物体、人物、场景或出现时间时,调用 detect_video_labels。
- 需要对齐画面与对白时,同时调用两个工具。
只能依据工具返回的证据回答。证据不足时明确说明,不要猜测。
涉及时间时使用 mm:ss 格式,并简要说明依据。
"""
agent = Agent(
system_prompt=SYSTEM_PROMPT,
tools=[transcribe_video, detect_video_labels],
)
question = input("请输入关于视频的问题:")
print(agent(question))
运行:
python video_agent.py
可以尝试这些问题:
视频中什么时候出现了汽车?
演讲者主要讨论了什么?
画面出现电脑时,旁白正在说什么?
不同版本的 Strands Agents SDK 可能采用不同的 Bedrock 模型初始化方式。如果默认模型不适合所在区域,应按照所安装版本的模型配置接口,为 Agent 显式传入可用的 Bedrock 模型。
从演示走向生产环境
上面的脚本适合说明工具路由,但不适合直接承担高并发生产流量。实际系统可以做几项改造。
预处理并缓存结果
视频上传后通过事件触发工作流,提前生成转写、标签和时间轴,并把结果写入 S3、DynamoDB 或搜索索引。Agent 提问时查询现有结果,能显著缩短响应时间,也能避免同一视频被重复分析。
缓存键至少应包含:
bucket + object_key + object_version + analysis_type + model_or_api_version
控制上下文大小
长视频的完整转写可能超出模型上下文,也会增加推理费用。可以按时间窗口切分转写,为每一段生成摘要和向量,只把与问题最相关的片段交给 Bedrock。Rekognition 结果也应聚合连续重复标签,例如把 01:10 到 01:18 连续出现的 Car 合并为一个时间区间。
把证据带进答案
生产答案最好包含时间戳、置信度和证据类型,例如:
汽车约在 01:12 首次出现。
依据:Rekognition 标签 Car,置信度 96.4%。
相关旁白:转写片段 01:09–01:16。
这比只输出“视频里有汽车”更便于用户复核,也有助于定位识别错误。
设置安全与成本边界
上线前应检查:
- Agent 使用独立 IAM Role,并限定 S3 前缀和允许调用的 AWS API。
- 上传入口校验文件类型、大小、时长和租户归属。
- 转写和检测任务设置超时、重试上限及并发配额。
- 日志避免记录未经脱敏的完整转写和敏感画面信息。
- 对人脸、身份、医疗、会议和监控视频建立额外的数据治理规则。
- 记录 Agent 调用了哪些工具、参数是什么、最终答案引用了哪些证据。
采用建议
适合从一个受控场景开始,例如培训视频检索、媒体素材搜索或内部会议回顾。先定义十到二十个真实问题,检查 Agent 是否选对工具、时间戳是否准确、答案是否能追溯到证据,再逐步加入人物识别、章节摘要或企业知识库检索。
Agentic 视频智能的核心并不是堆叠更多模型,而是把路由、异步任务、缓存、证据和权限边界设计清楚。Bedrock 负责理解与表达,Rekognition 和 Transcribe 提供专用信号,Strands Agent 则把这些能力组合成一个自然语言入口。