AgentOps:让 AI Agent 在生产环境真正可控

2026-06-02 18 预计阅读时间:1 分钟
来源:aws.amazon.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:13 分钟

Agent 不再只是按脚本跑的流水线——它会推理、会变道、会自己拍板。这意味着传统 DevOps 的监控、回滚、成本管控手段,在 Agent 场景下几乎全部失效。一次"聪明"的自主决策,可能让调用链多绕三圈,Token 费用翻十倍,而日志里只留下一句模糊的 tool_invoked

Amazon 在 Bedrock AgentCore 上提出了一套 AgentOps 纪律:把 Agent 当作一类全新的运维对象,从部署、观测、成本到持续改进,全链路重新设计。下面拆开看。

Agent 的运维难题到底在哪

传统服务的故障是确定性的——请求超时、异常码、资源耗尽,根因可追踪。Agent 的故障是非确定性的:

  • 决策漂移:同一个输入,Agent 今天调了搜索工具,明天直接用内置知识回答,后天又组合了三个工具。不是 bug,是推理路径本身在变。
  • 成本失控:一个本该 2 步完成的任务,Agent "深思熟虑"后走了 8 步,每步都带上下文累积,Token 消耗指数级增长。
  • 黑箱失败:Agent 返回了错误结果,但中间推理过程只存在于一次会话的内存里,事后无法复现、无法调试。

这三件事叠加,让"上线一个 Agent"变成"上线一个你无法预测其行为的系统"。

AgentOps 的核心支柱

Bedrock AgentCore 把 AgentOps 拆成四个可落地的方向:

1. 可观测性——把推理链变成可审计的事件流

不是只看最终输出,而是记录 Agent 每一步的决策:选了哪个工具、传了什么参数、推理依据是什么、耗时多少。这要求 Agent 运行时主动 emit 结构化事件,而不是靠事后拼日志。

2. 护栏与成本控制——给自主决策加边界

Agent 可以自由推理,但不能自由花钱。设置单次任务最大步数、单步最大 Token、工具调用白名单——这些不是限制能力,是防止失控。Bedrock AgentCore 提供了 Guardrail 和 Invocation 级别的限额机制。

3. 评估与回归测试——对非确定性输出做确定性评判

传统 CI 测的是"输出等于期望值"。Agent 测试测的是"输出在可接受区间内,且推理路径合规"。需要定义评估维度:事实准确性、工具使用合理性、成本效率,然后跑批量回归。

4. 持续改进——从生产数据反哺 Agent 配置

收集真实任务的轨迹数据,分析哪些场景 Agent 走了弯路,然后调整提示词、工具描述、护栏阈值。这不是一次性调优,是持续循环。

实践:用 Bedrock AgentCore 搭一个可观测、有护栏的 Agent

下面给出一个最小可运行的示例,展示如何在 Bedrock AgentCore 上创建一个带步数限制和工具白名单的 Agent,并接入轨迹采集。你需要一个 AWS 账号,且已开通 Bedrock 权限。

前提:安装 boto3 并配置 AWS 凭证。

pip install boto3
aws configure  # 填入 Access Key、Secret Key、region(如 us-east-1)

创建 Agent 并设置护栏

import boto3
import time
import json

client = boto3.client("bedrock-agent", region_name="us-east-1")

# 1. 创建 Agent
create_resp = client.create_agent(
    agentName="ops-demo-agent",
    agentResourceRoleArn="arn:aws:iam::<YOUR_ACCOUNT_ID>:role/BedrockAgentRole",  # 替换为你的 IAM Role
    foundationModel="anthropic.claude-3-5-sonnet-20241022-v2:0",
    instruction="你是一个运维助手。用户报告服务异常时,先查询日志,再给出诊断建议。不要猜测,必须基于工具返回的数据回答。",
    idleSessionTTLInSeconds=600,  # 会话超时 10 分钟
)

agent_id = create_resp["agent"]["agentId"]
print(f"Agent ID: {agent_id}")

# 等待 Agent 创建完成
time.sleep(10)

# 2. 创建 Guardrail —— 限制输出内容 + 限制工具调用范围
guard_resp = client.create_guardrail(
    name="ops-demo-guardrail",
    description="限制 Agent 只讨论运维话题,拒绝无关请求",
    contentPolicyConfig={
        "filtersConfig": [
            {
                "type": "DENIED",
                "inputStrength": "HIGH",
                "outputStrength": "HIGH",
                "topics": ["金融投资", "医疗诊断"]  # 拒绝这些话题
            }
        ]
    },
    contextualGroundingPolicyConfig={
        "filtersConfig": [
            {
                "type": "GROUNDING",
                "threshold": 0.75  # 要求输出至少 75% 有依据
            },
            {
                "type": "RELEVANCE",
                "threshold": 0.80  # 要求输出与问题相关度 ≥ 80%
            }
        ]
    },
    blockedInputMessaging="抱歉,我只能协助处理运维相关的问题。",
    blockedOutputsMessaging="输出被护栏拦截:内容缺乏充分依据或偏离运维领域。",
)

guardrail_id = guard_resp["guardrailId"]
print(f"Guardrail ID: {guardrail_id}")

# 3. 将 Guardrail 绑定到 Agent
client.update_agent(
    agentId=agent_id,
    guardrailConfiguration={
        "guardrailId": guardrail_id,
        "guardrailVersion": "DRAFT",
    },
)

print("Agent 已绑定护栏,准备进入 DRAFT 版本测试。")

关联工具并准备运行

# 4. 创建一个模拟的日志查询工具(Lambda 函数)
lambda_client = boto3.client("lambda", region_name="us-east-1")

lambda_client.create_function(
    FunctionName="ops-log-query-tool",
    Runtime="python3.12",
    Role="arn:aws:iam::<YOUR_ACCOUNT_ID>:role/LambdaExecutionRole",  # 替换
    Handler="index.handler",
    Code={"ZipFile": b"""
import json
def handler(event, context):
    # 模拟日志查询结果
    service = event.get("service", "unknown")
    return {
        "statusCode": 200,
        "body": json.dumps({
            "service": service,
            "last_errors": [
                {"time": "2025-01-15T08:32:00Z", "message": "Connection timeout to DB"},
                {"time": "2025-01-15T08:33:15Z", "message": "Retry succeeded"},
            ],
            "error_rate_last_1h": "0.03%"
        })
    }
"""},
)

time.sleep(5)

# 5. 注册工具到 Agent
tool_resp = client.create_agent_action_group(
    agentId=agent_id,
    agentVersion="DRAFT",
    actionGroupName="LogQuery",
    actionGroupExecutor={
        "lambda": "arn:aws:lambda:us-east-1:<YOUR_ACCOUNT_ID>:function:ops-log-query-tool"
    },
    functionSchema={
        "functions": [
            {
                "name": "query_service_logs",
                "description": "查询指定服务的最近错误日志和错误率",
                "parameters": {
                    "service": {
                        "type": "string",
                        "description": "服务名称,如 order-service、payment-service",
                        "required": True,
                    }
                },
            }
        ]
    },
)

print(f"工具已注册: {tool_resp['actionGroup']['actionGroupId']}")

# 6. 准备 Agent 版本(DRAFT → 可调用)
prepare_resp = client.prepare_agent(agentId=agent_id)
print(f"Agent 版本已准备: {prepare_resp['preparedAgent']['agentVersion']}")

运行 Agent 并采集轨迹

runtime = boto3.client("bedrock-agent-runtime", region_name="us-east-1")

# 7. 发起一次对话,启用轨迹记录
resp = runtime.invoke_agent(
    agentId=agent_id,
    agentAliasId="TSTALIASID",  # 测试别名
    sessionId="ops-session-001",
    inputText="order-service 最近有异常吗?帮我查一下日志。",
    enableTrace=True,  # 关键:开启轨迹采集
)

# 8. 解析轨迹数据
trace_data = []
for event in resp.get("completion", []):
    if "trace" in event:
        trace = event["trace"]
        trace_data.append(trace)
        # 打印每一步决策
        if "orchestrationTrace" in trace:
            ort = trace["orchestrationTrace"]
            if "modelInvocationOutput" in ort:
                reasoning = ort["modelInvocationOutput"].get("rawResponse", "")
                print(f"[推理] {reasoning[:200]}...")
            if "actionInvocationOutput" in ort:
                action = ort["actionInvocationOutput"]
                print(f"[工具调用] function={action.get('functionName')} input={action.get('actionInput')}")

    if "chunk" in event:
        answer = event["chunk"].get("bytes", b"").decode("utf-8")
        print(f"\n[最终回答] {answer}")

# 9. 将轨迹保存为 JSON,供后续分析
with open("agent_trace_session_001.json", "w") as f:
    json.dump(trace_data, f, indent=2, ensure_ascii=False)

print(f"\n轨迹已保存,共 {len(trace_data)} 个决策节点。")

运行后你会得到两样东西:Agent 的最终回答,以及一份完整的推理轨迹 JSON。轨迹里包含每一步的模型推理原文、工具选择理由、调用参数和返回值——这就是 AgentOps 可观测性的基础。

成本护栏的补充配置

上面的 Guardrail 控制了内容边界。步数和 Token 限制需要在 Agent 别名级别配置:

# 10. 创建带执行限制的 Agent Alias
alias_resp = client.create_agent_alias(
    agentAliasName="prod-alias",
    agentId=agent_id,
    routingConfiguration=[
        {
            "agentVersion": "1",  # 使用已发布的版本
        }
    ],
    # Bedrock AgentCore 支持在会话级别设置 maxSteps 等参数
    # 通过 session-level 配置传入:
    #   maxSteps: 5        # 单次任务最多 5 步
    #   maxTokensPerStep: 4096
)

print(f"生产别名已创建: {alias_resp['agentAlias']['agentAliasId']}")

实际调用时,在 invoke_agentsessionState 里可以传入执行限制:

resp = runtime.invoke_agent(
    agentId=agent_id,
    agentAliasId=alias_resp["agentAlias"]["agentAliasId"],
    sessionId="ops-session-002",
    inputText="payment-service 响应变慢了,帮我排查。",
    enableTrace=True,
    sessionState={
        "invocationId": "inv-002",
        # 限制单次对话最多 5 轮工具调用
        # (具体参数名以 Bedrock AgentCore 最新 SDK 文档为准)
    },
)

落地前要想清楚的几件事

决策点 建议
护栏粒度 先从粗粒度入手:话题拒绝 + 步数上限。精细化护栏(如特定关键词过滤)等有真实数据再调。过早收紧会让 Agent 变笨。
轨迹存储 不要只存最终输出。每条轨迹的完整 JSON 存到 S3 或 DynamoDB,按 sessionId + timestamp 组织。这是后续评估和改进的唯一数据源。
评估体系 上线前至少定义三个维度:事实准确率(输出是否基于工具数据)、路径效率(步数是否合理)、成本比率(Token/步数是否在预期区间)。用批量测试集跑回归,而不是手动试几条。
成本监控 Bedrock 的 Token 计费是后置的。在 AgentOps 层面,你需要从轨迹数据里实时统计 input_tokens + output_tokens,设告警阈值,而不是等月底账单出来才发现超标。
版本策略 Agent 的提示词、工具描述、护栏阈值都是"代码"。每次修改走 DRAFT → 测试 → 发布流程,不要在生产别名上直接改。

AgentOps 不是给 Agent 加一层监控面板就完事——它要求你重新定义"什么是正常行为"。传统服务的正常是"返回 200",Agent 的正常是"在允许步数内、基于工具数据、给出合规回答"。这个标准需要你根据业务场景自己定,Bedrock AgentCore 提供的是采集和执行基础设施,判断标准得你自己写。


相关推荐