我叫赵一帆,干DevOps八年,经历过Kubernetes集群凌晨三点全挂、数据库主从切换失败导致数据不一致、CI/CD流水线把生产环境当测试环境给扬了——可以说监控和告警是我活下去的本能。最近公司非要搞什么“AI智能化转型”,业务方撂下一句“你们运维不是天天喊自动化吗,那AI Agent也该你们负责”,然后就丢过来一个需求:用无代码工具快速搭建智能客服,再加一套跨部门的复杂审批代理人系统,要求两周上线。我本能地打开Google Cloud控制台,开始评估Vertex AI Agent Builder。这玩意儿号称能拖拽式构建企业级Agent,还能无缝集成现有数据源和API,听起来像运维的春梦——不用写大量胶水代码,点几下就能把对话流和后台服务串起来。结果呢?第一版Agent确实没怎么写代码就搞定了,但上线当天晚上,Cloud Run的预算告警直接把我和财务总监同时叫醒,账单数字让我后背发凉。后面还有更刺激的:多智能体审批流程用ADK编排之后,状态同步差点把Pub/Sub打炸;一个IAM权限错误差点让HR数据库裸奔;而且Agent Builder生成的底层微服务监控盲区比我想象的深得多。这篇文章是我从原型到生产、从踩坑到填坑的全部实录,包括YAML配置、Terraform部署、监控告警策略,以及那些不写在文档里的硬伤。
30秒速览
- - Agent Builder的意图匹配不是堆训练短语越多越好,语义干扰会毁掉路由准确率;数据存储的分块错误能让RAG答非所问,必须亲手校验切片。
- - ADK多智能体编排如果没有max_turns和全局状态检查,会陷入无限循环;异步消息必须配死信队列和告警,否则丢消息丢到业务方崩溃。
- - 工具集成要显式设置超时、冷启动预热,并严格校验OIDC令牌的audience;安全护栏之外必须有速率限制和请求去重,否则会被刷到破产。
- - Terraform部署Agent资源要预授权服务账号,避免删除锁回滚;成本控制需从第一天开启日志级别过滤和预算告警,否则Logging账单就是地雷。
用Agent Builder拖拽出第一版客服Agent时,我以为无代码就是不用写代码
Agent Builder的界面模型:意图、工具、数据存储,我理解错了意图匹配的权重
Agent Builder的核心抽象其实挺清晰:一个Agent由一系列“意图(Intent)”驱动,每个意图可以触发特定的回复流程或调用工具(Tool)。工具可以是预置的搜索服务、数据存储连接器,或者你自己写的Cloud Function。界面确实是拖拽的,你可以在控制台里像搭乐高一样把“欢迎意图”“查询订单意图”“转人工意图”串起来,还能给每个意图添加训练短语,让它自动匹配用户问题。我刚上手时觉得这不就是个带NLP的Dialogflow ES升级版吗?于是直接照搬老思路,给每个意图塞了几十条训练短语,以为越多越好。结果集成测试时发现,用户说“我的订单到哪了”居然反复匹配到“转人工意图”,因为我在转人工意图里加了一句“我的投诉还没有人回复”,导致“订单”这个词被稀释了。Agent Builder的意图匹配不是简单的关键词加权,它底层是一套基于Transformer的语义理解模型,短语数量不等于覆盖度,甚至可能引入噪声。我必须缩减转人工意图的短语数量,并且用否定短语明确排除查询类表述。如果你不注意这点,你的Agent上线后会让客服团队接到大量本应自动处理的请求,然后他们就会对着你摔电话。
把知识库接进去后,对话测试突然开始胡言乱语——原来是文档切片没做对
智能客服总得能回答产品FAQ,我直接把公司Confluence里的知识库导出成50个PDF,丢进了Agent Builder的“数据存储(Data Store)”。数据存储本质是Vertex AI Search的托管索引,连接后Agent可以自动通过RAG(检索增强生成)回答知识性问题。测试阶段我随便问了句“退货政策是什么”,它回了一段完美的摘要。我暗自得意,觉得零代码果然香。直到PM丢进来一句“如果客户已经拆封的电子产品,但包装完整,还能退吗?” Agent顿了顿,回了一段关于生鲜退换货的政策,驴唇不对马嘴。查了日志才发现,PDF里的文档切片是Agent Builder自动生成的,默认会把一页内容切成若干个固定token数的chunk,但PDF页眉页脚混入了一些章节标题,导致“电子产品退货”那段正文因为页脚“生鲜政策”的标题被错拉进了同一个chunk,语义完全跑偏。解决方案不是去写代码,而是在Agent Builder界面里启用“高级分块配置”,指定基于HTML标题标签的分块策略,并上传结构化更好的文档。但这个过程让我意识到,无代码≠无脑,底层数据准备依然是决定RAG效果的核心,你省下的胶水代码,迟早会以数据清洗工作的形式还回来。
当审批流程涉及4个部门时,单个Agent根本搞不定——我用ADK编排多智能体,结果状态同步差点把Pub/Sub打炸
ADK的YAML定义:Agent、Tool、SubAgent,我踩了父Agent调用子Agent的循环依赖坑
客服Agent还算简单,难的是跨部门审批流程,比如员工出差申请需要经过直属经理、财务预算审核、行政机酒预订、法务合规确认,每个角色都有自己的判断逻辑和外系统调用。Agent Builder单Agent模式在这种场景下就像让一个接线员同时对接四个部门,意图交叉混乱。Google的Agent Development Kit(ADK)就是来解决这个问题的,它允许你用YAML定义多个Agent,并赋予每个Agent自己的工具和指令,然后通过所谓的“Agent Orchestration”把子Agent注册到主Agent下。下面是我们的审批主Agent和财务审核子Agent的简化版YAML定义:(延伸阅读:我花30天把Llama 3.1 405B微调压进4张RTX 4090,烧掉$1200后总结的量化与分布式策略)
# main_approval_agent.yaml
agent:
name: ApprovalOrchestrator
description: >-
处理员工出差申请,协调直属经理、财务、行政、法务子Agent。
instruction: >-
根据用户输入识别当前审批阶段,调用对应子Agent完成审核或数据查询。
tools:
- name: invoke_manager_approval
type: sub_agent
agent: ManagerAgent
- name: invoke_financial_check
type: sub_agent
agent: FinanceAgent
- name: invoke_travel_booking
type: sub_agent
agent: TravelAgent
- name: invoke_compliance_check
type: sub_agent
agent: LegalAgent
output:
response_format: FINAL_ANSWER
context:
retrieval:
datastore: projects/my-project/locations/global/collections/default_collection/dataStores/approval_policies
# finance_agent.yaml
agent:
name: FinanceAgent
description: >-
检查预算余量,确认出差费用不超额,必要时请求额外审批。
instruction: >-
收到费用明细后,查询BigQuery预算表返回余量,若超额则返回拒绝理由并触发高管审批工具。
tools:
- name: query_budget
type: api_tool
config:
url: "https://us-central1-my-project.cloudfunctions.net/checkBudget"
method: POST
timeout: 10s
- name: escalate_approval
type: sub_agent
agent: ExecutiveApprovalAgent
output:
response_format: JSON
context:
retrieval: {}
逻辑上看没问题,但我第一次部署后,只要财务Agent发现超额,调用escalate_approval子Agent,高管审批Agent又会因为缺少原始出差信息而再次调用主Agent的invoke_financial_check,形成无限循环。ADK目前的执行引擎不会自动检测子Agent间的环形依赖,我在日志里看到同一笔申请被处理了37次,直到Cloud Run实例内存耗尽被kill。最后不得不在ADK的Agent配置里加入一个全局上下文状态表,用Redis记录每个申请ID已调用过的Agent名字,遇到重复调用直接短路返回,并在ADK的orchestrator设置里加上max_turns=8。这个坑文档里只字未提,全靠看Logs Explorer里的死循环调用链才定位到。(延伸阅读:为什么Cursor 0.46的Agent终端让我重写了安全审计清单——内核沙箱、cgroup v2与Seccomp的三层防线拆解)
多Agent通信:用Cloud Pub/Sub做事件总线,但没设死信队列,丢消息丢到被业务方投诉
多Agent系统不能全靠同步RPC,子Agent处理外部系统调用时可能超时或被限流,所以我们设计了一套异步事件驱动架构:主Agent完成一轮推理后把需要子Agent处理的事件推送到Cloud Pub/Sub主题,子Agent订阅对应主题,处理完再将结果推回另一个主题。这套架构在生产上跑了三天,业务方开始频繁投诉“申请提交后就没下文了”。我查Pub/Sub指标发现消息确认率只有82%,有18%的消息因为没有订阅者确认而被遗弃。根源在于我们的TravelAgent订阅者在调用航司API时经常遇到第三方超时,Cloud Function默认重试策略是立即重试,但航司API那端已经有幂等键,导致重试时API返回冲突错误,Cloud Function抛异常后没有nack掉消息,而是直接崩溃,消息就被Pub/Sub认定已送达(因为订阅者连接断开了),于是消息丢失。我没设置死信队列,所以这些消息永远消失了。填坑方案是给每个订阅者绑定一个死信主题,设置max_delivery_attempts=5,并在Cloud Function里增加try-catch块,捕获异常后显式nack,让消息进入重试队列。同时我给死信队列加了一个Cloud Monitoring告警策略,只要死信消息数量大于0立刻触发PagerDuty通知。别问我为什么一开始没加监控,问就是赶上线催的。(延伸阅读:我半夜把Copilot Runtime塞进Surface Pro,NPU推理快得离谱,但矢量搜索差点让我把机器砸了)
集成企业数据和API时,一个IAM权限的错误配置差点让生产数据库直接暴露给外部Agent
工具集成:用Cloud Functions连接到内部HR系统,结果因为超时没设,把Cloud Functions冷启动延迟拖到了30秒
Agent Builder的工具集成本质是声明一个OpenAPI规范,然后指向你的服务端点。我写了个Cloud Function去调公司HR系统的员工信息查询接口,因为HR系统是内部部署的,需要走Cloud VPN。我在Agent Builder里添加了工具,指定了OpenAPI schema,然后信心满满地测试。第一次调用等了足足30秒才返回,紧接着第二次调用只需要2秒,典型的冷启动。Cloud Function没有设置最小实例数,默认缩容到零后每次冷启都得重新建立VPN隧道,加上HR系统那边握手慢,整个RTT爆炸。Agent Builder的工具调用默认超时是15秒,我直接在OpenAPI的扩展属性里把超时改成了45秒才能跑通,但这显然不是正常响应时间。最终解决办法是给Cloud Function配置了min_instances=1并加了个warmup请求,虽然多花点钱,但至少响应时间压到了3秒内。还有一个更隐蔽的问题:工具调用时Agent Builder会自动发送一个Bearer token(OIDC令牌),我在Cloud Function里验证时发现audience字段默认为Agent Builder服务账号的URL,而我的HR系统API网关要求audience必须是网关自己的client ID,导致认证失败。我只好在函数入口手动校验并替换令牌,但这个安全细节在官方文档里只给了个模糊的“请参考IAM章节”提示。(延伸阅读:我在Amazon Q和Copilot之间反复横跳30天,发现自己不是在换工具,是在赌AWS的下一手棋)
安全护栏:我配置了内容过滤和PII脱敏,但忘了给Output设置速率限制,导致一次被刷了几千次API调用
Agent Builder内置了Safety Settings,可以过滤有害内容和识别PII。我在Agent配置里开启了这些护栏,还对输入输出做了正则脱敏,把身份证号、手机号替换为占位符。我以为万事大吉,直到安全团队发来一封“异常流量告警”邮件:某个公网IP在凌晨2点到4点之间向我们的客服Agent发送了4700多次重复查询,每次都是“请问可以借多少钱”。查了一下,Agent Builder默认没有针对单个IP或会话的速率限制,这个攻击者用一个脚本无脑刷,虽然护栏过滤掉了恶意意图,但每次调用仍然触发了底层的LLM推理和工具调用,成本照样产生。我紧急在Google Cloud Armor里创建了一条安全策略,限制每个IP每分钟最多20次请求,并配置了Cloud Monitoring的request_count指标告警,超过阈值自动触发Cloud Function去更新Armor规则临时封禁。另外我还给Agent Builder的输出端加了一道Cloud Run Sidecar代理,在转发前用内存缓存去重,同一输入5分钟内只允许一次实际后端调用。这套组合拳上线后,恶意刷量攻击终于只会增加日志而不再烧钱了。(延伸阅读:放弃轮询,拥抱WebRTC:我在GPT-4o实时API上构建数学助手的48小时延迟攻坚战)
部署到Cloud Run并接入监控的那天,我才知道Agent Builder生成的背后是微服务集群的复杂度
部署:用Terraform部署Agent资源,但因为Agent Builder的IAM角色缺失,部署失败后回滚卡了半小时
为了保持Infrastructure as Code的惯例,我写了一套Terraform脚本来创建Agent Builder的Agent、数据存储以及关联的Cloud Run服务。下面是我当时的部分Terraform配置:
resource "google_discovery_engine_data_store" "knowledge_base" {
location = "global"
data_store_id = "customer-faq-store"
display_name = "Customer FAQ Data Store"
industry_vertical = "GENERIC"
content_config = "CONTENT_REQUIRED"
solution_type = "SOLUTION_TYPE_SEARCH"
create_advanced_site_search = false
}
resource "google_vertex_ai_agent" "customer_service_agent" {
display_name = "CustomerServiceAgent"
project = var.project_id
region = "us-central1"
agent_config {
data_store_connections {
data_store_id = google_discovery_engine_data_store.knowledge_base.data_store_id
}
default_language_code = "zh-CN"
supported_language_codes = ["zh-CN", "en"]
}
depends_on = [
google_project_service.enable_discoveryengine_api,
google_project_service.enable_vertexai_api,
]
}
第一次apply失败了,错误信息是“Permissions error: Service account vertex-ai-agent@… does not have discoveryengine.views.search access”。原来Agent Builder在创建Agent时会自动生成一个默认服务账号,但这个账号没有访问Data Store的权限,Terraform没有自动赋予。我手动加了一条IAM绑定,但apply的回滚机制卡在了Data Store删除上,因为Agent正在依赖它。等了半小时Terraform才通过强制删除锁释放资源。正确的做法是在创建Agent前先赋予服务账号roles/discoveryengine.viewer,并且用lifecycle规则避免删除冲突。这些都是在生产上挨了一闷棍才学会的。
监控与成本:我配置了Cloud Monitoring告警和预算通知,但Agent Builder的日志量比我想象中大10倍,Logging成本爆炸
Agent上线后我立刻补上了全套监控:基于Cloud Run的request_latency和error_count指标设置了SLO告警,并用Cloud Monitoring创建了Agent特定的自定义指标,比如per_intent_success_rate和tool_call_failure_rate。但千算万算没算到日志成本。Agent Builder默认会记录每一次对话的完整request/response和中间推理步骤,这些日志自动流向Cloud Logging,且没有采样率控制。第一周结束时我打开Billing报告,Logging费用比我预估的高了接近12倍,因为每次对话可能产生5-10条详细日志,包括检索片段的原文和工具调用的全量输出。后来我在Logging里加了一个排除过滤器,只保留ERROR级别的日志和经过脱敏后的结构化摘要,并设置了一个日志导出到BigQuery的管道来做成本归因分析。同时,我开了Budget Alert,设定月预算$500,当预测消费超过80%时通过Pub/Sub推送通知给Slack和邮件。现在每个新Agent上线前,我都有一个硬性的监控清单:1) 速率限制是否就位;2) 日志过滤器是否已配置采样或级别过滤;3) Agent的每个工具调用是否都有明确的超时和重试策略;4) 预算告警阈值是否已经调低到不会在半夜叫醒我但足够提前的量级。少任何一条,我都不会批准推生产。
这次经历教会我,所谓的“零代码”只是把代码复杂度转移到了配置、数据管道和基础设施的正确拼装上。Agent Builder和ADK的确让AI Agent的搭建速度快了几个数量级,但如果你是以运维的身份接手,就必须用看待一个分布式系统的眼光去看它:有状态管理问题、有依赖爆炸问题、有安全暴露面、有成本不可预测的坑。别被拖拽界面骗了——凌晨三点的告警,从来不管你是写代码还是拖控件。