Agents using Strands


title: “使用 Strands 构建 Agents” weight: 200


在本模块中,我们将使用 Strands Agents SDK 为一个名为 AnyCompany Shop 的虚构在线零售商店构建并部署一个 AI 驱动的客户服务 Agent。该 agent 与运行在我们集群中的 Qwen2.5-3B 模型进行交互,帮助客户处理订单、产品问题和退货。

为什么选择客户服务 Agent?

一个 agent,在每个模块中不断演进。

模块 Agent 如何受益
Strands(本模块) 核心循环 - 接收查询、推理、响应
Langfuse 追踪每一次 LLM 调用和工具调用,以调试缓慢或错误的响应
Milvus 将产品目录 + FAQ 存储为嵌入向量,用于 RAG 驱动的产品答复
MCP 让 agent 能够访问工具:查询订单、检查库存、发起退货
A2A 拆分为协同工作的专家 agent

我们从简单开始:一个由 LLM 驱动、带有一个工具的对话循环。每个模块都会叠加一个新能力。

架构

  1. 客户查询 → Strands Agent
  2. Agent 发送提示 → LiteLLM → 通过 vLLM 的 Qwen2.5-3B
  3. 模型决定调用 lookup_order
  4. Agent 执行工具,将结果返回给模型
  5. 模型生成最终响应 → 客户

什么是 Strands SDK?

一个用于构建 agent 的开源 Python SDK。由 LLM 决定调用哪些工具以及调用的顺序。关键概念:

  • Agent:向 LLM 发送消息并执行工具调用的主循环
  • Model:任何兼容 OpenAI 的端点(此处为路由到 vLLM 的 LiteLLM
  • Tools:用 @tool 装饰的 Python 函数,agent 可以调用它们
  • System prompt:塑造 agent 行为的指令

步骤 1:代码讲解

完整源代码位于 ~/environment/modules/20-self-managed/200-strands-agents/customer-agent

cd ~/environment/modules/20-self-managed/200-strands-agents/customer-agent

该项目由六个文件组成:agent.pytools.pyserver.pyDockerfilerequirements.txtk8s.yaml。前两个是核心教学文件。它们包含 agent 逻辑和工具定义。其余文件是基础设施:server.py 是一个精简的 FastAPI 封装,通过 HTTP 将 agent 暴露给聊天 UI,Dockerfilerequirements.txt 处理容器化和依赖项,k8s.yaml 定义 Kubernetes 部署。

model = OpenAIModel(
    client_args={
        "base_url": litellm_base_url,
        "api_key": os.environ.get("LITELLM_API_KEY", "not-needed"),
    },
    model_id="qwen2-5-3b-neuron",
    params={"max_tokens": 1024, "temperature": 0.3},
)
  • model_id="qwen2-5-3b-neuron":LiteLLM 将其解析为 vLLM Service。切换到 "nova-lite",我们就能在 Bedrock 上运行,无需任何其他代码更改
  • Qwen3 的扩展思考在 LiteLLM 代理层被禁用(模型配置中的 extra_body.chat_template_kwargs.enable_thinking = false),因此 agent 无需处理它
  • LITELLM_BASE_URL 来自 Terraform 预置的 agent-config ConfigMap

@tool
def lookup_order(order_id: str) -> dict:
    """Look up an order by its order ID and return the order details.

    Args:
        order_id: The order ID to look up (e.g., ORD-12345)

    Returns:
        A dictionary containing order details including items, status, tracking number,
        and estimated delivery date. Returns an error message if the order is not found.
    """

Strands 根据类型提示和 docstring 构建工具 schema。LLM 会读取所有这些内容来决定何时调用工具。编写 docstring 时要像 LLM 会阅读它们一样,因为它确实会。

模拟的 ORDERS 字典将在 Agent Tool Access (MCP) 实验中被真实的 MCP server 替换。

步骤 2:构建并推送到 ECR

我们已经为 customer-agent 创建了 Amazon ECR 仓库。一次性构建并推送:

cd ~/environment/modules/20-self-managed/200-strands-agents/customer-agent
IMG=$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/customer-agent:strands
docker build --push -t $IMG .

:strands 是特定于模块的标签。每个实验使用不同的标签名称,因此 kubectl describe pod 会准确告诉我们正在运行哪个模块的代码,而一次全新的 kubectl apply 就足以滚动 Deployment(spec 已更改,无需 rollout restart)。

这是唯一一个我们手动构建并推送镜像的实验,因此我们可以看到完整的 源代码 → 容器镜像 → ECR → EKS 循环的端到端过程。对于之后的每个实验,我们都已预先构建并将镜像推送到 ECR,因此我们可以直接部署。任何时候想发布我们自己的更改,都可以重新构建并推送。

步骤 3:部署到 EKS

k8s.yaml 是一个 Deployment + 在 8080 端口上的 ClusterIP Service。envFrom 挂载 agent-config ConfigMap,因此 LITELLM_BASE_URL 会自动到位。

envsubst < k8s.yaml | kubectl apply -f -
kubectl rollout status deployment/customer-agent --timeout=120s

步骤 4:与它对话

打开聊天 UI 标签页(来自 Sample Application 步骤)。从配置列表中选择 Customer Agent (Self-managed GenAI),然后尝试:

Hi, I ordered a laptop last week and it still hasn't arrived. My order ID is ORD-12345. Can you help?
I want to return the headphones I bought. Order ORD-11111.
Can you check on order ORD-99999?

最后一个测试了"订单不存在"的路径,优秀的 agent 会优雅地处理失败。

我们构建了什么

  • 一个 通过 LiteLLM(一个位于 vLLM 前面的通用兼容 OpenAI 的代理)连接到自托管 Qwen2.5-3B 模型的 agent
  • 一个处理客户服务对话的 Strands agent 循环
  • 一个模型调用来获取订单信息的 lookup_order 工具
  • 一个运行在 EKS 上、供聊天 UI 交互的长期运行 HTTP 服务

缺少什么

  • 没有可观测性:目前 agent 是一个黑盒 → Langfuse 模块
  • 没有产品知识:只能查询订单 → Milvus 模块
  • 仅有模拟数据:硬编码的订单 → MCP 模块
  • 单一 agent:身兼太多角色 → A2A 模块

接下来:可观测性,追踪每一次 LLM 调用和工具调用。