落地过的智能体场景
云成本助手
自然语言问「上月哪个服务涨得最多」,自动查 Cost Explorer 并给出归因与优化建议。
工单智能分派
读工单内容自动分类、判定优先级、关联历史相似单,直接分派到对口小组。
运维排障助手
告警触发后自动拉指标、查日志、比对变更记录,产出初步根因假设推给值班群。
合同与文档审阅
对照审阅清单逐条检查,标出风险条款与缺失项,输出结构化审阅意见。
数据查询助手
自然语言转 SQL,在只读副本上执行,返回结果与图表,全程记录审计日志。
内部知识问答
结合 RAG 与工具调用,既能回答制度问题,也能直接帮你提申请、查进度。
工具调用循环(含权限与审批)
"""智能体工具调用循环。
设计要点:
1. 工具分级:只读工具直接执行,写操作工具需人工审批
2. 幂等键:每次写操作带幂等键,防止重试造成重复执行
3. 步数上限:防止模型陷入死循环烧钱
4. 全链路审计:每步的输入输出都落库
"""
from __future__ import annotations
import json
import uuid
from typing import Any, Callable
import boto3
BEDROCK = boto3.client("bedrock-runtime", region_name="ap-northeast-1")
MODEL = "anthropic.claude-3-5-sonnet-20241022-v2:0"
MAX_TURNS = 8
# ---------- 工具注册表 ----------
TOOL_SPECS = [
{
"toolSpec": {
"name": "query_monthly_cost",
"description": "查询指定月份、指定维度的 AWS 成本。只读操作。",
"inputSchema": {"json": {
"type": "object",
"properties": {
"month": {"type": "string", "description": "格式 YYYY-MM"},
"group_by": {"type": "string", "enum": ["SERVICE", "ACCOUNT", "TAG"]},
},
"required": ["month", "group_by"],
}},
}
},
{
"toolSpec": {
"name": "create_optimization_ticket",
"description": "创建一张成本优化工单。写操作,需人工审批。",
"inputSchema": {"json": {
"type": "object",
"properties": {
"title": {"type": "string"},
"detail": {"type": "string"},
"estimated_saving": {"type": "number"},
"owner": {"type": "string"},
},
"required": ["title", "detail", "owner"],
}},
}
},
]
READ_ONLY_TOOLS = {"query_monthly_cost"}
def execute_tool(
name: str,
args: dict[str, Any],
registry: dict[str, Callable],
approve: Callable[[str, dict], bool],
idem_key: str,
) -> dict[str, Any]:
"""执行工具,写操作先过审批。"""
if name not in registry:
return {"error": f"未注册的工具:{name}"}
if name not in READ_ONLY_TOOLS:
if not approve(name, args):
return {"error": "人工审批未通过,操作已取消", "requires_approval": True}
args = {**args, "idempotency_key": idem_key}
try:
return {"result": registry[name](**args)}
except Exception as exc: # noqa: BLE001 - 工具异常需回传给模型
return {"error": f"{type(exc).__name__}: {exc}"}
def run_agent(
user_input: str,
registry: dict[str, Callable],
approve: Callable[[str, dict], bool],
audit: Callable[[dict], None],
) -> dict[str, Any]:
"""主循环。返回最终答复与完整调用轨迹。"""
messages: list[dict[str, Any]] = [
{"role": "user", "content": [{"text": user_input}]}
]
trace: list[dict[str, Any]] = []
session_id = str(uuid.uuid4())
for turn in range(MAX_TURNS):
resp = BEDROCK.converse(
modelId=MODEL,
messages=messages,
toolConfig={"tools": TOOL_SPECS},
inferenceConfig={"maxTokens": 2000, "temperature": 0.2},
system=[{"text": (
"你是云成本运营助手。先查数据再给结论,"
"涉及创建工单等写操作时,必须先说明理由和预估收益。"
)}],
)
msg = resp["output"]["message"]
messages.append(msg)
stop = resp["stopReason"]
audit({
"session": session_id, "turn": turn,
"stop_reason": stop, "usage": resp["usage"],
})
if stop != "tool_use":
text =".join(b.get("text",") for b in msg["content"])
return {"answer": text, "trace": trace, "turns": turn + 1}
tool_results = []
for block in msg["content"]:
if "toolUse" not in block:
continue
tu = block["toolUse"]
idem = f"{session_id}:{turn}:{tu['name']}"
out = execute_tool(tu["name"], tu["input"], registry, approve, idem)
trace.append({"tool": tu["name"], "input": tu["input"], "output": out})
tool_results.append({"toolResult": {
"toolUseId": tu["toolUseId"],
"content": [{"json": out}],
"status": "error" if "error" in out else "success",
}})
messages.append({"role": "user", "content": tool_results})
return {"answer": "达到最大步数上限,任务未完成。", "trace": trace, "turns": MAX_TURNS}
MAX_TURNS 是必须的:没有步数上限的智能体,一个死循环能在几分钟内烧掉不少 token 费用。
智能体上线前必查
权限边界
- 工具对应的 IAM 权限最小化,只读与写操作分开角色
- 写操作强制人工审批或二次确认
- 所有写操作带幂等键,防止重试造成重复执行
- 敏感数据字段在返回给模型前脱敏
可靠性
- 设置步数上限与总 token 预算上限
- 工具超时与重试策略明确
- 工具异常以结构化错误返回给模型,而不是直接抛出
- 关键流程有回滚路径
可观测性
- 每次会话记录完整调用轨迹(输入、工具、输出)
- token 消耗按用户与业务线归属
- 异常率、平均步数、审批通过率做成看板
- 用户反馈按钮,负反馈自动进人工复核队列