封面图

Demo 到生产差的不是一次升级,是三个必须补的层。第一句给结论:Agent 上生产之前,先把它的手捆住,再谈它聪不聪明。这篇按我自己跑的顺序,从权限、失败、观测三处拆开讲,每一处都给出我今天会动手的那一步。

先泼一盆冷水

今天 InfoQ 推了一门课,标题是 Agent 从 Demo 到生产还差几步,四周实战。我看到这个标题的第一反应是,四周够不够。

不是怀疑课程,是怀疑这件事本身的体量。我手上这套 Agent 从能回答到敢让它碰线上资源,中间隔了将近两个月,而且大部分时间不是在调模型。

公开信息里只有课程上线的消息,没有课程大纲,没有涉及哪类 Agent,也没有说清四周之后交付什么形态。这些都没有披露,我不猜。

我能写的是另一件事:一个 Agent 从演示环境挪到真实业务链路里,会依次撞上哪些墙。顺序很重要,撞错顺序会白干。

这篇不是课程笔记,是我自己踩坑的顺序复盘。如果你手上正好有一个准备上生产的 Agent,可以对着看。

Demo 里它像个天才

先说清楚 Demo 为什么看起来那么好。

因为 Demo 的边界是人替它画好的。你给它三个工具,它只会用这三个;你给它一份干净数据,它不会遇到脏的;你在旁边盯着,它跑偏你立刻喊停。

这三件事在线上全都不成立。工具会变成三十个,数据会带上历史遗留字段,旁边没有人。

我最早那版就是把工具全挂上去,心想模型自己会选。结果它在一段本可以用只读接口完成的查询里,调了写接口。

没造成事故,因为那个写接口当时是空表。但我后背凉了一下。

问题不在模型选错,在我没给它一个「选错也不会出事」的环境。这是我后来所有改造的起点。

所以后面我讲的每一层,都不是为了让 Agent 更聪明,是为了让它犯错的时候,代价可控。

第一道墙是权限

我把工具按危险程度重新分了类,不是按功能分。

只读查询一类,写操作一类,对外发消息一类。三类走三套凭证,只读那套没有写权限,写那套必须带审批标记。

这里的关键不是最小权限四个字,是权限要落在凭证上,不能落在提示词上。你在 system prompt 里写「不要调用删除接口」,那不是控制,那是请求。

凭证层面拒绝,模型再想调也调不动。这是唯一可靠的边界。

代价是工具体验变差。有些本来一步能完成的动作,现在要分两步,先申请再执行。用户会抱怨慢。

我的判断是,这个慢必须接受。Agent 的效率优势不该建立在「它可以随便动」上面。

今天就能动手的一步:把你所有工具列出来,按读、写、对外通信分成三组,给写操作单独开一套凭证。这件事不需要改模型,一两天能做完。

按危险程度分凭证(示意代码,证明权限要落在凭证上,不能落在提示词里。)

READ_ONLY_TOOLS = {"query_order", "get_user", "list_logs"}
WRITE_TOOLS = {"update_order", "send_message", "delete_record"}

def get_client(tool_name):
    if tool_name in READ_ONLY_TOOLS:
        return read_client  # 只读凭证,无写权限
    if tool_name in WRITE_TOOLS:
        if not current_task.approved:
            raise PermissionError("写操作缺少审批标记")
        return write_client  # 独立写凭证
    raise ValueError("未登记的工具")

def call_tool(tool_name, payload):
    client = get_client(tool_name)
    return client.invoke(tool_name, payload)

权限拆完才看得见链路

权限一拆,整条链路反而清楚了。原来是一坨,现在能画出层次。

我把它分成四层:接入层收请求,编排层决定调什么,工具层真正碰资源,审计层记录每一次动作。

Demo 阶段只有中间两层。接入和审计是上生产才补的。

补完之后我发现,出问题最多的不是编排层的模型判断,是工具层和审计层之间的缝隙。有些动作执行成功了,但审计没记上,因为异常路径绕过了记录点。

这个缝隙很隐蔽,因为主流程日志看起来一切正常。只有当你去查某一次具体会话时,才会发现中间少了几条记录。

我的处理是把审计点下沉到工具层内部,而不是放在编排层外面。工具层每执行一次,无论成功失败都写一条,这样异常路径也绕不过去。

第二道墙是失败

Demo 假设一切顺利。生产假设一切都会失败。

工具超时、返回格式变了、第三方接口限流、模型输出了一段没法解析的 JSON,这些在四周里我全遇到过。

最早我的处理是让模型重试。这个做法很危险,因为重试一个写操作等于执行两次。

后来改成按操作类型分开处理:只读操作允许自动重试两次,写操作一律不自动重试,转为人工确认。

还有一个坑是部分成功。一个任务要改五个字段,改到第三个失败了,前两个已经落库。这时候回滚还是继续,得由业务决定,不能由 Agent 决定。

我给每个写操作加了补偿动作的定义,失败时执行补偿,而不是简单报错。这块工作量比调模型大得多。

公开信息里没有关于补偿动作设计的通用标准,我只能按自己的业务逻辑来定。如果你的业务里写操作之间有依赖,补偿的设计会更麻烦。

失败路径得画出来才看得清

光在脑子里想失败路径不够,我把它画出来了。

画完才发现,最危险的不是彻底失败,是静默降级。工具返回了一个空结果,模型把它当成「没有数据」,继续往下走,最后给出一个看起来合理的错误答案。

这种错误最难查,因为日志里全是成功的。

所以我在工具层加了一条规则:空结果和调用失败必须用不同结构返回,模型必须显式处理这两种情况。

这条规则加完,错误答案少了一大半。

今天就能动手的第二步:检查你的工具返回结构,把空结果和失败分开。如果现在两者都返回 None 或者空列表,先改这个。

区分空结果与失败(示意代码,让模型显式处理空结果,避免静默降级。)

def wrap_tool_result(ok, data, err=None):
    if not ok:
        return {"status": "error", "error": err, "data": None}
    if data is None or data == []:
        return {"status": "empty", "error": None, "data": None}
    return {"status": "ok", "error": None, "data": data}

def handle(result):
    if result["status"] == "error":
        raise ToolFailure(result["error"])
    if result["status"] == "empty":
        return "没有查到记录,请换条件重试"
    return result["data"]

第三道墙是观测

Demo 不需要观测,你在旁边看着就行。生产不行。

我要求每一次 Agent 动作都能回答三个问题:谁触发的,调了什么,结果是什么。

听起来简单,做起来麻烦,因为模型的一次决策可能对应好几次工具调用,这些调用之间没有天然关联。

我加了一个贯穿全程的 trace id,从接入层生成,一路带到审计记录。这样一次会话里所有动作能串起来。

没有这个,出了问题只能靠猜。有次线上一个答案不对,我翻了四十分钟日志才对上号,加完 trace 之后同类问题五分钟定位。

这块投入产出比最高,我建议放在权限之后立刻做。

今天就能动手的第三步:在接入层生成一个 trace id,透传到所有工具调用和审计日志里。哪怕只做这一步,排查效率也会明显不一样。

三件事的先后不能乱

我的顺序是权限、观测、失败处理。

权限最先,因为没有它后面都是空谈;观测第二,因为没有它你根本不知道失败处理该补哪里;失败处理最后,因为它最耗时间,而且需要前两步提供的信息。

有人会反过来,先做重试和兜底,再做权限。我不建议,你会把大量精力花在给一个本来就不该发生的调用做善后。

这个顺序不是标准答案,是我这套业务形态下的选择。如果你的 Agent 只读不写,权限那块可以轻很多,顺序也要调。

如果你今天只有两个小时,我建议的顺序是:先花一小时把工具按读写分类,再花一小时把 trace id 加上。失败处理可以明天再说。

说点实在的,我没做完的

四周够不够,我的答案是:够把这三层搭出骨架,不够把它们磨到放心。

我到现在还没解决的是多 Agent 协作时的权限传递。一个 Agent 调另一个 Agent,凭证怎么传,权限怎么收敛,公开资料里没有成熟做法,我也没跑通。

还有一个是成本观测。单次调用的 token 成本我能算,一次会话的总成本我算不准,因为重试和补偿的消耗不好归集。

这两块我标为未完成,不装作已经解决。

如果你也在做类似的事,欢迎交流这两块的思路。我暂时没有答案,但知道问题卡在哪。

今天就该动手的那一步

如果你手上有一个准备上生产的 Agent,今天先做一件事:把它的工具按读写分类,给写操作换一套独立凭证。

这一步不需要改模型,不需要改提示词,一两天能做完,但它把最坏情况的后果压下去一大截。

动不了的话,过渡办法是给所有写操作加一道人工确认,哪怕只是点一下。有这道确认,你至少能在它跑偏时拦住。

第二件事是加 trace id,第三件事是区分空结果和失败。这三步做完,你的 Agent 至少不会在你看不见的地方乱动。

四周课能教你方法,教不了你这个业务里哪个操作算危险。那个判断只能你自己做。

其他值得留意

  • 微软本轮修复接近 1100 个漏洞,其中一个 Outlook 0Day 已被在野利用,打开恶意文件即可泄露 Windows 身份验证哈希。补丁量大,先看 Outlook 那条。
  • Fortinet 披露 FortiManager、FortiAnalyzer 与 FortiOS 多个高危漏洞,管理平台未授权即可远程执行代码。管理面暴露在公网的要先查。
  • OpenSSH 10.6p2 之前版本在 musl libc 且未启用 PAM 的环境下存在密码认证绕过,编号 CVE-2026-123158。特定环境才成立,别一概而论。
  • Windows Advanced Rasterization Platform 一批堆溢出、栈溢出、双重释放与越界读漏洞,最高 8.8,均需本地授权。本地提权链的一环,优先级靠后。
  • Azure CycleCloud 存在 OS 命令注入,评分 8.4;Azure NFS mount helper 存在不可信搜索路径,评分 7.8。云上自建集群的留意这两条。
  • VS Code 渲染层限制不当,评分 8.6,可本地绕过安全特性。开发机场景,影响面看策略。
  • 一个月内十几家 AI 平台被扒,攻击目标集中在 API 密钥而非业务数据。密钥管理该重新过一遍。
  • CISA 联合多家机构提示工业制冷与冷链监控系统加强网络安全。关基行业合规动作,非技术细节。

写在最后

四周课能不能把你送到生产,我不下结论。但你可以今晚花十分钟,把自己的 Agent 工具列一遍,标出哪些是写操作。标完你就知道差几步了。

Logo

更多推荐