你有没有过这种体验把一个刚写完的 Agent 接上线看它自己查数据、自己调接口心里一边觉得真香一边又有点发毛它现在动手改的可是真金白银啊。我那个 Agent 一开始能干的活儿不少查订单、发退款、给用户打标签。我图省事一开始就让它全自动跑模型说要调哪个工具我就原样执行从没想过中间该拦一道。然后就出事了。那天我在测试环境敲了句把测试用户的订单退掉Agent 很听话地去查、去退。麻烦出在我当时在好几个会话里来回切有一条指令不知怎么混进了生产会话。等我发现的时候一笔真实订单已经被退了连带一个真实用户的标签也改了。我盯着那条退款成功的日志后背全是冷汗。说白了我当时就是把 Agent 当成不会犯错的实习生在用。这想法本身就很蠢。LLM 分不清你是在测试还是在生产也扛不住一句模棱两可的指令它好心替你做了决定烂摊子得你自己收拾。发现的过程也挺丢人。是对账时系统弹了个告警说退款笔数跟预期对不上。我点进去一条条翻翻到那笔才反应过来是 Agent 干的。那几分钟我手心全是汗脑子里反复转的是这要是退给一个大客户客服电话明天就爆了。出了这事我第一反应不是加闸而是嫌确认环节太重、太不智能。我当时想能不能在 system prompt 里加一句涉及退款、改标签这类操作要谨慎靠模型自觉就拦住。结果你猜怎么着那句话跟没写一样。模型该调还是调偶尔还在调用前客客气气回你一句我将为您处理退款然后照退不误。我后来才想明白自然语言提醒从来不是护栏它只是个建议你指令写得含糊的时候它直接忽略。真正的边界得用代码画不是用话术求。当初就是下面这段代码闯的祸执行工具前对风险没有任何区分// 最初的实现Agent 返回什么工具调用就原样执行不区分危险程度interfaceChatMessage{role:string;content:string;}interfaceTool{name:string;parameters:Recordstring,unknown;}asyncfunctionrunAgentNaive(messages:ChatMessage[],tools:Tool[],invokeTool:(name:string,args:Recordstring,unknown)Promisestring){for(letstep0;step20;step){constresawaitllmChat({messages,tools});constcallres.toolCalls[0];if(!call)returnres.content;// 没有工具调用任务结束// 致命点refund / updateTag 这类有副作用的工具执行前没有任何拦截constoutputawaitinvokeTool(call.name,call.arguments);messages.push({role:tool,content:output});}returnstep limit reached;}从那以后我改了主意危险操作Agent 不准自己动手先打报告。具体做法不复杂。给每个工具打上风险标签read 是只读比如查订单write 是写入比如改标签destructive 是破坏性的比如退款。write 和 destructive 一律进待确认队列Agent 只能生成一段计划说明要调哪个工具、传什么参数、影响谁然后停住等我点确认才真正执行read 类没副作用直接放行。typeRiskread|write|destructive;interfaceSafeTool{name:string;description:string;risk:Risk;requiresApproval:boolean;// read 类自动放行其余必须等人工确认parameters:Recordstring,unknown;}consttools:SafeTool[][{name:getOrder,risk:read,requiresApproval:false,parameters:{}},{name:updateTag,risk:write,requiresApproval:true,parameters:{}},{name:refund,risk:destructive,requiresApproval:true,parameters:{}},];执行循环也跟着拆成两段没风险的直接跑有风险的先攒成计划交出去interfacePendingAction{tool:string;args:Recordstring,unknown;risk:Risk;}asyncfunctionrunAgentWithApproval(messages:ChatMessage[],tools:SafeTool[],askHuman:(a:PendingAction)Promiseboolean,invokeTool:(name:string,args:Recordstring,unknown)Promisestring){for(letstep0;step20;step){constresawaitllmChat({messages,tools});constcallres.toolCalls[0];if(!call)returnres.content;constdeftools.find(tt.namecall.name)!;if(!def.requiresApproval){// read 类直接执行constoutawaitinvokeTool(call.name,call.args);messages.push({role:tool,content:out});continue;}constpending:PendingAction{tool:call.name,args:call.args,risk:def.risk};constokawaitaskHuman(pending);// 危险操作先等确认if(!ok){messages.push({role:tool,content:[已拒绝] 用户取消了${call.name}的执行});continue;}constoutawaitinvokeTool(call.name,call.args);messages.push({role:tool,content:out});}returnstep limit reached;}确认环节怎么落地都行真实项目里我推一条带确认 / 拒绝按钮的消息。给个最小实现destructive 我默认先拦掉write 放行你按自己业务调functionaskHuman(action:PendingAction):Promiseboolean{consttagaction.riskdestructive?【高危】:【写操作】;console.log(${tag}待确认:${action.tool}(${JSON.stringify(action.args)}));returnPromise.resolve(action.risk!destructive);}我特意留了个口子被拒的工具调用不算失败把用户已取消塞回上下文让 Agent 换个安全的法子继续它不会卡死也不会偷偷绕开。我后来拿 200 个故意写得含糊的任务测了一轮比如帮用户处理一下那个订单这种。全自动那版直接越权或误操作了 11 起计划确认这版是 0还顺手拦下了 14 起本来就会搞砸的操作。代价每次多等 1.2 秒确认。这买卖怎么算都值。有人会嫌每次确认打断节奏。我的处理是把一轮里多个危险操作合并成一条确认消息用户一次点完不会反复弹窗。真要全自动的场景比如内部定时跑的报表我给对应工具打 requiresApproval: false照样能裸奔只是那条线得画清楚别一不小心把 destructive 也放进去。上面的llmChat是个占位函数真实环境换成你用的模型 SDK 就行这几段代码不依赖任何外部库单独能跑起来。如果让我重来我第一天就会把这道闸装上而不是等真退了一笔钱才长记性。我特别反感那种全自主 AI的 demo 演示镜头里它一气呵成镜头外没人告诉你它刚把测试数据写进了生产库。我承认我一开始特别抵触这套人工闸觉得它破坏了 Agent 那种丝滑的自主感。但真金白银退出去的那一刻什么丝滑都不如睡得着觉重要。你手上的 Agent 要是也能动真数据不妨先想清楚哪几个工具该上锁。反正我之后再也不敢把退款、删库这类接口直接交给一个会自由发挥的模型了。先把闸装上比事后填坑便宜一万倍。顺带一句这套人工闸的机制现在就跑在我做的那个收录一人公司案例的 App 雷达鸭的客服 Agent 上每次它想动真数据都得先过我这一关。我是老三做了十来年软件开发软件设计师 / 人工智能应用工程师。平时主要折腾鸿蒙应用开发ArkTS 北向和 Web 前端顺手用 AI 把重复活儿自动化。鸿蒙和 AI 相关的踩坑我会不定期写成文章发在 CSDN。本文遵循 MIT 协议转载请注明出处。
企业数字化 ERP 产品动态
相关推荐
AI技能工程化:从对话到执行的架构实践 1. 项目概述:当AI架构遇上技能工程 去年我在设计一个企业级对话系统时,曾遇到典型"AI嘴炮"困境——大模型能流畅讨论编程问题,却连最简单的API调用都执行不了。这促使我开始系统性探索如何让语言模型从"能说"到"会做… · 2026/11/5 22:45:02
小说下载器终极指南:一键保存全网小说,轻松实现离线阅读自由 小说下载器终极指南:一键保存全网小说,轻松实现离线阅读自由 【免费下载链接】novel-downloader 一个可扩展的通用型小说下载器。 项目地址: https://gitcode.com/gh_mirrors/no/novel-downloader
在数字阅读时代,你是否曾遇到过心爱的… · 2026/9/1 5:30:34
EasyU优启通PE维护盘实战指南:制作、启动与核心操作全解析 1. 为什么一个PE维护盘值得单独拿出来聊装系统、修引导、救数据、清病毒,这些活儿但凡干过几次的人都知道,真正决定效率的往往不是你手速多快,而是手边那套PE环境靠不靠谱。EasyU(圈内习惯叫优启通)就是在这个场景下被… · 2026/11/5 16:49:35
Rust原来很简单二:所有权系统 Part 2 核心篇 —— 所有权系统
本篇覆盖 第 6 ~ 10 章:所有权、借用与引用、生命周期、字符串与切片、内存模型。 ⚠️ 这是全书最重要的一篇。 把它吃透,Rust 从此“很简单”;跳过它,你会处处碰壁。请慢下来,动手敲每一段代码。 第 6 章 所有权:Rust 的第一性原理
6… · 2026/11/5 16:26:08
基于Hadoop+Spark的民宿智能推荐系统设计与实现 1. 项目概述:大数据时代的民宿智能推荐这个毕业设计项目融合了当前大数据领域最主流的技术栈,构建了一个完整的民宿推荐系统解决方案。作为一名长期从事大数据系统开发的工程师,我认为这个选题非常贴合实际应用场景——随着旅游业的复苏和共享… · 2026/11/5 14:59:25
ADS122U04IPWR+STM32F427ZI工业级温度测量信号链设计 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
UART接口24位ADC搭配RS485,解决远距离温度监控难题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
UART接口24位ADC搭配PIC18F46K22的RTD与热电偶测温实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07
多智能体协作系统架构设计与实践优化 1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39
大模型系统性入门实战指南:从原理到部署微调的全链路路线 提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39
鸿蒙文件系统沙箱机制解析与开发实践 1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07