
AI 编程工具可以让我在十分钟内生成一个接口的骨架AI Agent 能自动整理邮件、调用 API、批量产出文件。这种“无摩擦”体验对开发者的吸引力确实很大。真正进入 AI 应用开发和模型部署后我却见过大量事故代码能跑但没人说得清逻辑Agent 执行到一半把所有临时文件写进了生产目录模型一上线连最后一次生成内容的依据都找不到。问题大多不是模型能力不够而是流程太顺滑把该有的验证、确认、审计和回滚全跳过了。我们把这种现象叫 AI 的“无摩擦之路”如果不主动加刹车它很容易从便利滑向事故现场。接下来我会按 AI 工程实践的落地顺序从 AI 编程、AI Agent 容易踩的坑开始讲到如何给流程保留摩擦点再给出一套可复现的带控制措施的工作流最后聊参数边界和故障排查。1. “无摩擦”为什么会在 AI 工程里变成事故放大器1.1 AI 编程越顺滑代码审查越不能被省掉用 Cursor 这类 AI 编程工具的时候最大的诱惑就是 Tab 补全。你在编辑器里写几行注释它能补全整个函数甚至顺手给出一版单元测试。单点看确实快但把这种代码直接推到主干测试环境很快就会暴露问题接口签名和依赖版本不一致SQL 里引用了不存在的字段异常分支被悄悄吞掉甚至有人把本应放在配置里的密钥直接写进了源码。这些不是 AI 故意挖坑而是它从海量模式里推断出“看起来合理”的代码并没有经过真实运行环境的确认。传统开发里一个人敲每一行代码的时候会对变量名、类型、边界、异常路径有肌肉记忆。这些过程就是摩擦。AI 编程工具把摩擦取消了只保留最终补全结果。于是验证责任没有消失而是转移到了 review 和测试环节。如果你没有在 CI 里保留编译、单测、静态检查就等于亲手删掉了最关键的一道摩擦。我一般建议AI 生成的代码提交前至少过三道检查。第一编译或解释是否通过。第二关键分支是否有单测覆盖。第三diff 里有没有我们自己看不懂、却自动生成的逻辑。看不懂的代码宁可不要也不要靠“多跑几次”碰运气。很多人以为把提示词写得更长AI 就能生成更好的代码。真实落地中提示词只是第一层约束真正可靠的约束是验收标准。把需求拆成用户故事和测试用例先生成测试再生成实现看起来多了一步但能把错误拦截在更早阶段。1.2 Agent 自动执行越顺滑风险半径越大AI Agent 是比 AI 编程更典型的无摩擦场景。用户只需要描述目标Agent 自己规划步骤、调用搜索、读取文件、发送请求。链路顺畅的时候你会觉得像多了一名实习生。可很多 Agent 项目在 Demo 里什么问题都没有一接入真实业务就失控。失控的常见模式有三种。第一种是循环重复。任务没达到预期Agent 反复执行同一个动作像没有退出条件的 for 循环。第二种是工具参数张冠李戴。本来应该读测试库因为上下文里出现了“production”这个词环境变量又恰好暴露在工具列表里Agent 就把数据写到了正式路径。第三种是把中间结果当成最终结果。模型在上下文里生成了一份摘要Agent 认为摘要就是可交付输出实际上这份摘要的数据源已经过期或者只覆盖了部分输入。传统软件里危险操作通常需要二次确认代码中会有 if environment prod: require_approval。AI Agent 如果默认允许所有工具自动执行等于把安全栅栏全拆了。错误一旦传导到下游系统你才会发现所有环节都被平滑地连接了起来唯独没有地方让它停下来。不要因为 Agent 能完成任务就放弃任务状态机和人工确认点。1.3 幻觉在长链路里会被逐级放大单次模型生成的内容有小问题人肉眼扫描时往往能纠正。但在 Agent 链路中幻觉输出可能被当成下一步输入继续传给更多工具。比如 Agent 阅读一份合同模型幻觉出一个错误日期系统拿这个日期去生成审批任务最后流程就在一个不存在的日期上跑完了。每一步出错的概率看起来不高但当链路很长、所有环节全部自动执行时风险会从加法变成乘法。要控制这种放大最直接的办法是给链路加摩擦点。每个步骤产物进入下一步前先做结构化校验字段缺失、类型不对、超出枚举值的输出直接拦截。这比让模型自己反思更可靠。模型反思仍然依赖模型模型不会真正记得刚才有没有犯错规则校验却不受幻觉影响。所以真正成熟的 AI 工程实践不是把所有环节都自动化到极致而是在“自动”和“可控”之间找平衡。只看“能不能生成”“能不能跑通”是不够的还要关心“出错了能不能停”。这个停止能力就是需要保留的摩擦。2. 真正需要保留的摩擦点验证、权限、审计、回滚2.1 验证是摩擦但它保护的是交付质量AI 输出不像传统函数那样有固定返回。同一个提示词在不同时间可能得到不同结果。因此“生成即所得”的做法非常危险。无论生成过程多顺畅后续都应该有一段固定验证流程语法是否正确字段是否完整业务口径是否一致是否存在安全风险。举个例子生成 SQL 不能只看有没有返回结果要看返回结果是否真的符合查询条件。生成代码不能只看能不能运行要看是否存在注入风险。生成文案不能只看通顺要看有没有把内部数据泄露进对外输出。更稳妥的做法是先建一个小样本验证集把 AI 输出和预期结果放在一起比较。验证集不用很大二三十条就够。确认稳定后再进入批量生成。批量任务也要注意失败重跑。任务失败后不要盲目恢复恢复可能会产生重复写入。每条任务需要唯一 ID用订单号、文件路径或业务主键做幂等键。哪怕是生成报告也要保证同一条输入重复执行时不会产生两份重复付款通知之类的副作用。2.2 权限控制给 Agent 最小权限而不是最小阻力很多人在搭 Agent 时会图省事把 API Key、数据库账号、文件系统写入权限全部配好让 Agent 自由发挥。开发阶段的 Demo 也许没问题上了生产就是灾难入口。Agent 的每一步工具调用都应该有权限边界。只读工具可以放开写工具要分级发布、删除、发送消息这类操作默认拒绝。通过角色或 token 实现最小权限测试环境可以放开写生产环境默认拒绝写除非显式人工提权。AI 应用开发中还要警惕一种情况用户输入会干扰 Agent 规划。如果系统让模型直接拼接命令行外部输入可能变成额外指令导致 Agent 执行预期之外的动作。工具参数要尽量走白名单校验而不是把原始字符串直接传给执行器。同样的道理也适用于文件路径。Agent 使用文件工具时先把允许访问的目录列表传进来任何路径都要先规范化再拼接防止路径穿越。这些看起来是安全团队的事但做 AI 应用研发的人只要哪一步没守住后面就会变成线上事故。2.3 日志与审计给 AI 执行过程保留现场无摩擦系统最容易被忽略的是可观测性。很多 Agent 平台只在界面上显示最终结果中间每一步调了哪个工具、传了什么参数、拿到了什么返回全部不记录。结果出了问题只能看到一句“任务失败”。AI 产品需要把执行过程做成可审计日志。至少包含这些字段时间戳、用户标识、会话 ID、模型名称、输入摘要、输出摘要、工具名称、工具参数、返回结果、耗时、token 消耗、是否需要人工确认、人工确认结果。如果面向合规要求较高的业务还要能回答为什么这个 Agent 会调用某个高危接口是用户明确授权还是模型自己判断的。日志不是等出事后临时补的而是产品迭代初期就要定义。我先记原始请求和响应再做日志脱敏。有些日志只要记录输入输出有些字段本身包含个人数据就需要先脱敏再入库。这个摩擦点能让故障排查从“猜”变成“查”。2.4 回滚最后一脚必须能踩代码有版本管理提示词和模型参数也需要有版本管理。很多团队训练了代码仓库却把 system prompt 直接写在数据库配置表里改一次就覆盖一次上线后根本不知道线上跑的是哪一版。这会产生一个后果模型行为变得不可预测出问题后想还原却找不到旧版本。正确做法是把 system prompt、few-shot 示例、模型名称、temperature、top_p、工具定义都纳入版本管理随应用版本一起发布。模型本身升级后要在测试环境对比新旧模型在同一批验证集上的表现再决定是否切换。对 Agent 来说回滚不只是恢复代码版本。如果它执行了写操作例如修改文件、写入数据库就要判断是否需要数据恢复。建议在写操作之前先把变更内容暂存在一个可回滚区域等人工确认后再应用到正式位置。低成本任务可以直接用 Git 分支数据类任务用临时表。提前想清楚“这一步错了怎么回去”比等 Agent 跑完整条链路再检查要稳得多。3. 从 AI 编程到 Agent 落地我是这样搭一条“有摩擦”的流水线3.1 先跑最小闭环不要一上来就接真实数据很多朋友拿到 Agent Demo 以后第一个想法就是接入企业微信或者直连主库报表。我建议反过来。先做一个最小闭环一个函数、一条输入、一个非生产数据源手工调用模型把返回打印到日志。这个阶段的目的不是炫技而是确认模型在你当前的网络、依赖版本和鉴权方式下能正常工作。很多失败不是因为模型能力不够而是环境变量没配全、版本冲突、代理超时、ssl 校验失败。如果第一步就失败后面的业务逻辑和工具调用会全叠在一起排查难度会翻好几倍。最小闭环成功以后再加一个容易出错的分支比如空值、乱码、超长文本。把模型返回结果解析成结构化数据记录 token 消耗、响应延迟和格式稳定性。这些数据后面做容量规划时非常有用。没有这些基础数据遇到线上延迟高你根本说不清是模型自己慢还是并发把服务打满了。3.2 给 Agent 套上可用代码表达的边界在 Agent 工作流里不要把它想成一个神秘的黑盒。它其实是一个由“解析器 工具函数 循环控制”组成的外部循环。我们要做的是在这个循环的每一步都插入检查点。def run_agent(goal, max_iterations5): step_result parse_goal(goal) for i in range(max_iterations): action decide_next_action(step_result) if not is_action_allowed(action): log(blocked_action, action) return action not allowed if needs_approval(action): if not wait_for_approval(action): log(rejected_action, action) return user rejected result execute_with_timeout(action, timeout60) log(executed_action, action, result) if not validate_result(result): rollback(action) return validation failed if action.is_final: return result return max_iterations_reached这段伪代码里is_action_allowed、needs_approval、validate_result、rollback 都是边界。实现时可以结合 LangChain、Spring AI 或自研编排层把这些方法替换成真实业务逻辑。Agent 失控通常是因为这些函数被省掉了直接让模型在一个万能 execute() 里循环。模型拥有人类语言赋予的目标之后并不擅长判断停止边界容易不断优化到一个并不需要的程度。因此显式控制迭代次数、工具白名单和终止条件才是最基本的防护。3.3 人工确认要分级不能天天让用户点按钮如果 Agent 每执行一步都要用户确认人很快会疲惫最后直接全部点通过。这不是好设计。更好的做法是分级控制。低风险操作可以自动执行比如只读查询、写临时目录。中风险操作只对关键参数变化做确认比如写文件时可以提示“将写入文件 /tmp/output.csv允许吗”。高风险操作默认拒绝或人工审批比如删除文件、对外发送消息、修改生产配置。这样用户只需要关注真正的风险节点既保留了摩擦又不至于烦到无法使用。做生产化时还需要把任务放进队列。每个任务要有唯一 ID支持中断续跑。大批量任务不可能要求“一次成功”。例如批量翻译 500 个文件跑到第 200 个时模型服务超时不能从头再来。正确做法是把每个文件的处理状态记录成 pending、running、done、failed失败重试时只处理 failed 项。判断一个系统是否成熟就看它能不能断点续跑。只能从零开始跑的任务说明流程还停在 Demo 阶段。4. 关键参数与判断标准不是让 AI 更“聪明”而是让它更可控4.1 模型参数先降随机性再谈创造力如果你做的是结构化任务例如代码生成、数据抽取、表单填写temperature 建议从 0 或 0.1 开始调。它能让模型采样时的随机性更低输出更稳定。很多刚接触大模型的人会把 temperature 调到很高结果一个简单的分类任务每次返回格式都不一样解析脚本反复崩。格式稳定性往往比内容多样性更值钱。创意文案场景可以把温度调高但不要同一套高随机参数处理所有任务。top_p 也能限制采样范围但它和 temperature 最好不要同时大幅调整否则输出分布会更难预测。max_tokens 要留余量防止输出被截断。处理长文本时模型响应延迟会明显上升。如果输出经常顶到 max_tokens不要简单把上限拉大更好的做法是把任务拆成多段分步生成再汇总成完整结果。这些参数没有绝对的“正确答案”。你需要跑一组小样本记录不同参数组合下的输出差异。可以先固定 temperature0.1、top_p0.9 跑一轮再固定 temperature0.7 跑一轮看哪组的格式错误率更低。4.2 Agent 迭代和并发参数怎么设参数推荐起点说明max_iterations3~10防止 Agent 陷入无效循环超过后直接结束任务concurrency1~4低配置环境从 1 开始观察资源占用后再增加timeout_per_tool30~60 秒外部接口慢容易误判长任务可以按需放宽retry_times1~3限流可重试业务参数错误不要重试auto_executefalse生产环境默认 false高危操作必须人工确认这组数值只是起点。如果模型服务高峰期响应就要 5 秒timeout 30 秒足够如果使用长文本生成单次可能超过 60 秒timeout 就要调到 120 秒以上。重点不是照抄表格而是记录每个任务耗时的分布再决定超时和重试策略。并发数也不宜一开始就拉高。低配环境里并发一多显存、内存