最近跟团队聊规划,有个话题绕不开:市面上标着"AI Agent"的产品,绝大多数还是给软件加了个对话框,真正的智能体只能做点填空和改写,在角落里瑟瑟发抖。真正让我觉得风向变了的是另一批产品——它们首页几乎没有表单,甚至没有界面,你丢一个任务描述进去,它自己会拆步骤、调工具、跑流程,跑完把结果甩给你。这种"为智能体设计,而不是为人类操作设计"的思路,圈子里叫agent-native。
这个概念值得认真对待,不只是因为它时髦,而是它会改变你我的开发习惯。以前我们关心按钮放哪里,现在要关心工具签名怎么写;以前我们写状态机,现在写推理循环;以前我们思考"用户会点什么",现在要思考"agent会误解什么"。我从今年年初开始把一个内部工单系统朝agent-native方向重构,中间踩了不少坑,趁着记忆还热,把设计思路、组件拆解、代码实现和排查经验完整整理出来。
这篇文章的目标读者很明确。一类是正在评估要不要做agent-native重构的技术负责人,可以重点看第4章的设计方法论;另一类是已经写过一两个demo、但不知道下一步怎么办的开发者,可以直接跳到第5章的代码,回头再看第3章的原理。无论哪种,我都尽量把"为什么这么干"讲透,而不是只丢结论。
1. agent-native到底是什么:先别急着对标
1.1 从本地软件到cloud-native,第四代交互协议换了
我习惯把软件架构的演进看作"运行时"和"交互协议"的双重迭代。第一代本地软件,运行时是操作系统,交互协议是鼠标键盘加窗口;第二代是web-native,运行时是浏览器,交互协议变成超链接加表单;第三代cloud-native,运行时是容器编排平台,交互协议成了API;第四代就是agent-native,运行时是agent runtime,交互协议变成自然语言加结构化指令。
为什么说"换交互协议"比"换UI"更本质?举个例子,web-native时代,如果只是把桌面窗口改成网页,而不理解HTTP无状态、超链接跳转、表单提交这些新的协作方式,做得再炫也是"套壳"。同样,现在很多产品把表单换成聊天框,但底层还是每个按钮对应一个死接口,用户依然被流程绑架,这不叫agent-native,这叫套了个AI皮。
在agent-native架构下,自然语言不是用来替代搜索框的输入方式,而是成为程序与程序之间的编排语言。用户说一句"帮我把华东区上周的销售数据汇总,再跟库存对比",应用里真正发生的事情是:模型理解意图,拆解成多个子任务,依次调用报表工具、数据查询工具、对比工具,最后把结果组合成回答。整条链路上,没有人为每个步骤点击操作,人的角色收缩为"给出目标"和"验收结果"。
1.2 判断agent-native的三个硬指标
我见过太多团队把"加了AI功能"包装成agent-native,所以后来我总结了一套判断标准。一套应用到底算不算agent-native,我认为有三个硬指标,缺一不可。
第一,智能体是主要使用者,而不是辅助入口。用户提交意图之后,执行路径由模型实时规划,每一步调用哪个工具、什么顺序、要不要回退,都是动态决策,不是写死在流程图里的。
第二,应用能力以工具接口暴露,而不是以页面暴露。底层是function signature加schema,页面只是调试器和展示层。也就是说,产品对外的主要能力边界是"能被agent调用的API集合",人类能用的界面反而是次要的。
第三,推理循环是运行时核心。应用自身在编排,而不是把几个API串成一条死流程。传统系统的运行时是请求-响应循环,agent-native的运行时是"推理-行动-观察"循环。
提示:区分"AI增强应用"和"agent-native应用",最简单的办法是看一个问题——去掉所有人为点击步骤之后,流程还能闭环吗?如果还能独立完成目标,那它就是agent-native;如果某一步必须等人在界面里点一个"确认"按钮才能继续,那它就还在传统应用范畴。
2. 驱动agent-native的三股力量
2.1 模型推理能力过了临界点
2023年以前的agent大部分是玩具,主要原因不是工程不够好,而是模型太笨。当时的模型做多步推理不稳定,工具调用更是稀有功能,调用三步就开始前言不搭后语,根本无法承担任何真实业务。
现在情况变了。主流大模型普遍支持标准的function calling协议,能输出结构化的工具调用参数;推理链路变长,遇到错误还能自我纠错;上下文窗口动辄几十万token,能容纳完整的任务轨迹。语言模型从"分词接龙"进化成"具备基础规划能力的推理器",agent-native才有了地基。
这个临界点也改写了技术分工。以前我们纠结"要不要让LLM做决策",现在纠结"哪些决策可以放心交给LLM,哪些必须保留规则兜底"。推理能力底座的提升,把问题从"能不能"变成了"该不该"。
2.2 用户从操作软件到表达意图
软件发展史,本质上是一个"操作复杂度不断下沉、用户操作不断减少"的过程。最早的命令行要求记住指令,GUI把菜单摊开降低记忆负担,后来搜索框和推荐系统进一步把"找功能"变成"给需求"。agent-native是这个趋势的极端版:用户连功能都不用找,直接表达意图。
我举一个真实场景。运营要出一份周报,传统SaaS里的路径是:打开报表模块、选时间范围、选维度、拖字段、设置排序、导出、再写一段分析结论。这一套操作熟手也要五分钟,涉及七八个界面。换成agent-native的交互,用户只需要说一句"按渠道生成上周的GMV报表,顺便把转化率低于2%的渠道标出来"。后面的事情全部由agent调度完成。
这种转变不是体验优化,而是产品形态的变化。当"表达意图"成为唯一入口,产品的信息架构就不需要按照人类视觉习惯组织菜单和层级,而要按照"意图空间"组织工具和权限。所有流程设计,都从"用户阅读屏幕"转变为"模型理解任务"。
2.3 商业模式从订阅转向结果付费
架构往agent-native走,商业模式的账也要重新算。传统SaaS按人头收订阅费,成本主要在服务器和人力,边际成本低,所以毛利率高。agent-native应用不一样,每一次业务跑通都会产生模型推理成本、工具调用成本、可能还有第三方API费用,边际成本不再趋近于零。
这带来两个直接变化。第一个变化是计费方式变得丰富:按任务数计费、按结果计费、按节省的人力成本分成,这些模式在agent-native场景下都比纯订阅更合理,因为用户付费的锚点从"使用权限"变成了"交付结果"。第二个变化是成本模型必须前置。设计一项agent能力之前,先估算单次任务的平均token消耗和工具调用次数,再倒推定价和毛利,而不是等功能上线以后发现做一单亏一单。
注意:agent-native意味着每笔业务都有实时模型调用成本。别用传统SaaS的毛利率模型去估算,要把token成本作为一级成本科目来核算,否则很容易做完一个爆款功能,月底账单把人看傻。
3. 拆解agent-native的四大核心组件
3.1 Agent Runtime:推理循环才是真正的运行时
agent-native应用的"运行时"不是Web服务器,而是一个推理循环。这个循环的骨架长这样:读取任务与上下文,让语言模型决策下一步是直接回答还是调用某个工具;如果是调用工具,执行函数并把结构化结果放回上下文;然后继续循环,直到满足终止条件。
这个循环有一个经典理论来源——ReAct模式,也就是Reason加Act交替进行。模型先想"要解决这个任务,我需要知道什么信息",再决定"我得调用哪个工具去获取信息",拿到结果后继续思考下一步。更复杂的方案比如plan-then-execute,把规划和执行拆成两个阶段;multi-agent编排把一个大循环拆成多个小循环的互相调用。但无论怎么变,底层都是同一个循环结构。
所以我建议做agent-native的团队,第一件事不是去选agent框架,而是把"推理循环"当成运行时基础设施来设计。这个运行时至少要处理三件事:循环的终止条件,比如设定最大步骤数,防止agent陷入死循环;异常恢复,当工具调用报错时,把错误信息作为新观察喂回模型,让它自己修正;并发调度,当多个任务同时运行时,怎么隔离上下文和状态。
3.2 Tools:函数签名就是新的UI
在传统应用里,用户靠界面理解产品能做什么;在agent-native应用里,模型靠工具理解产品能做什么。所以工具接口就是新的UI,工具定义的质量直接决定agent能不能正确完成任务。
我见过大量翻车案例,问题都出在接口设计上。工具名起得太泛,比如query_data,模型根本不知道这个工具能查什么;参数schema的描述写得稀里糊涂,模型传了错误格式的参数;最要命的是工具返回的错误信息也是给程序员看的堆栈,模型读完更迷茫。这些都是把传统API直接扔给模型用的结果,没有做语义化转译。
设计工具接口时我遵循几条经验。工具名用"动词加名词"的清晰结构,比如get_order_status比query好得多;工具描述里写清楚"什么时候用这个工具、不要用这个工具做什么",负面约束比正面描述更能减少误调用;参数schema要标注清楚格式和取值范围,日期用ISO,金额用分;工具的返回结果要尽量结构化,让模型能直接读取关键字段。
3.3 Memory:短期与长期的分工
没有记忆的agent只能完成单轮、无状态的碎片任务,稍复杂一点的多步骤流程就撑不住。记忆系统在agent-native架构里分成三层。
短期记忆就是当前任务的工作上下文,包括用户的历史消息、模型的推理轨迹、工具调用和返回结果。它的特点是量大、易变、持久性要求低,实现上就是messages数组加滚动窗口。需要注意上下文窗口是有限的,完整的工具返回结果会迅速撑爆上下文,所以要么截断,要么让工具直接返回摘要版结果。
长期记忆是沉淀下来的事实性知识与用户偏好,比如用户常用的报表口径、组织架构信息、历史偏好设置。实现上通常用向量数据库做语义检索,必要时先用摘要模型把原始内容压缩再入库。这一层决定了agent对具体场景的适配度,是产品差异化的来源。
还有一层经常被忽略——工作记忆的压缩。当多轮对话或者长流程跑下来,上下文接近上限时,不是粗暴地删除旧消息,而是让模型对已有信息做一次摘要,把关键事实、已完成步骤、待办事项提炼出来,作为新的系统上下文。这个操作比简单截断对任务连贯性的影响小得多。
3.4 Guardrails:别让agent裸奔
agent-native意味着模型在替你执行真实世界的操作,权限和安全的收口方式必须跟着变。传统应用里,权限校验发生在用户点击按钮的瞬间,人可以感知到自己触发了什么操作;而agent会自动发起一连串工具调用,用户可能只在最后看了一眼结果。所以权限必须下沉到工具调用这一层。
我实行的是最小权限原则加工具级授权。每个工具都声明需要的权限范围,agent运行时在执行工具调用前强制校验,没有权限就直接拦截并返回错误,错误信息会促使模型调整策略。变更类工具还要额外加上二次确认机制,agent在调用之前必须触达人工审批接口,等结果返回后才真正执行。
审计日志同样重要。agent的每一步决策、工具调用、参数值、返回结果,都要留痕并可回放。一旦出了事故,我们需要的不是模型的黑箱解释,而是完整的调用链证据。这套日志系统在排查问题和优化prompt时价值极大,我后面第6章会讲到具体怎么用日志定位故障。
4. 设计方法论:从零规划一个agent-native应用
4.1 判断边界的三条标准
不是所有业务都适合agent-native,硬套架构只会给自己挖坑。我判断一个业务场景是否适合转向agent-native,用三条标准做筛选。
第一条,任务是否有明确目标与可验证的结果。比如"查一下某个订单的物流状态"目标明确,结果可以验证;但"帮我设计一个策略"这种开放性问题,结果无法量化,agent很难自己判断有没有完成。可验证性是agent自我纠错的前提,没有它,整个循环就容易失控。
第二条,执行过程是否存在清晰的前后依赖和多步决策。如果任务是一条直线,调一个API就能完成,那不需要agent;如果中间要根据中间结果决定下一步走哪个分支,agent的规划能力才有发挥空间。举个例子,客服工单的分诊流转就比"查天气"更需要agent,因为分诊条件复杂、后续动作随结果变化。
第三条,人工兜底的成本是否可以接受。再强的模型也有失误率,业务方愿不愿意在关键节点保留人工审核,或者接受一定比例的失败任务转交人工处理。这个约束越严格,架构上的规则写死和控制门槛就要越多,agent的自由度相应收紧。
4.2 为意图设计API,不是为页面设计API
传统API设计围绕资源实体建模,比如订单、用户、商品,CRUD一套打完。agent-native的API设计应该围绕"意图"建模。同一份订单数据,传统API是create_order、get_order、update_order,agent-native的工具则倾向于切成query_order_by_query、create_order_with_approval这种带语义约束的粒度和表达。
这里的关键技巧是:把动词作为工具名,名词作为参数,约束条件写进描述和schema。比如工单系统里,工具名是assign_engineer_to_ticket,参数是ticket_id、engineer_id、reason,描述里写上"仅当工单状态为待分配且工程师负载低于阈值时才能调用"。模型看到这个工具,就会把用户模糊的表述自动映射到精确参数上。"上周"会被转成具体的日期范围,"华东区"会被映射到地区编码,这些转换压力的确落在模型身上,但工具的定义给它提供了明确的语义锚点。
还有一个设计原则我反复踩出来:查询类工具和变更类工具必须分开。查询可以放心让agent自由调用,变更类工具一定要带上影响范围和后果说明。agent每次打算执行变更操作之前,模型会先看到工具描述里的风险提示,从而主动向用户确认。
4.3 人机协作:审批流兜底与信任边界
agent-native不等于全自动无人化,至少现在远远不是。真正可靠的生产系统,都设计了清晰的人机协作点。我的做法是横向按部门角色拆权限,纵向把任务流程切成"查询—决策—执行—确认"四个层级。查询层全自动,决策层允许agent提出建议但关键权限由人拍板,执行层在权限范围内自动跑,确认层把重要结果推送给用户验收。
失败兜底同样要设计成机制而不是口头约定。我定的规则是:agent连续两次尝试同一个工具都失败,或者推理循环达到最大步数,就自动转人工处理,并且把之前的完整决策轨迹打包给接手的人。磨刀不误砍柴工,这套兜底逻辑看起来增加了一步,实际上大幅减少了用户对agent的不信任感。
很多团队不敢上agent-native,就是怕失控。我的体会是,控制感不来自限制模型的自由,而来自把"哪些能做、哪些不能做、做错了怎么拉回来"这三件事定义清楚。规则越明确,agent的自主空间反而可以越大。
5. 实操:写一个最小的agent-native后端(附代码)
5.1 技术选型:自己能写循环,就能看透框架
我刻意不用LangChain那张复杂抽象的框架写这个demo,不是框架不好,而是新手很容易被框架包装迷惑,搞不清agent到底是怎么跑起来的。用任何一个兼容OpenAI chat/completions协议的大模型服务,配合Python的requests库,几十行就能把核心循环写出来。看懂这个最小实现,再去看框架源码,一眼就能认出人家是在你的循环外面加了多少糖。
我建议模型选择支持tools参数、也就是原生function calling能力的。国内海外各家主流模型都支持,协议基本兼容,你只需要把base URL、模型名和密钥换成自己的。
5.2 核心代码:工具定义与ReAct循环
先定义工具。以天气查询为例,工具schema必须包含名称、描述和参数结构:
TOOLS = [{ "type": "function", "function": { "name": "get_weather", "description": "查询指定城市当前天气。当用户提到城市天气时使用该工具。", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,比如北京、上海。" } }, "required": ["city"] } } }]然后实现一个最朴素的推理循环。每次请求把完整messages和TOOLS一起发给模型,模型返回的内容有两种可能:一种是普通的文字回答,另一种是要求调用工具的结构化指令。如果是后者,执行对应的本地函数,把结果追加进messages再回传给模型,继续下一轮:
import json import requests class AgentRuntime: def __init__(self, base_url, api_key, model): self.base_url = base_url self.api_key = api_key self.model = model def step(self, messages): payload = { "model": self.model, "messages": messages, "tools": TOOLS, } resp = requests.post( f"{self.base_url}/chat/completions", json=payload, headers={"Authorization": f"Bearer {self.api_key}"}, timeout=60 ) resp.raise_for_status() return resp.json()["choices"][0]["message"] def dispatch(name, arguments): if name == "get_weather": city = arguments["city"] # 实际会调用真实天气服务,这里用模拟数据 return {"city": city, "temperature": 22, "condition": "晴"} raise ValueError(f"未知工具: {name}") def run_agent(question, max_steps=5): messages = [{"role": "user", "content": question}] for _ in range(max_steps): msg = agent.step(messages) messages.append(msg) if not msg.get("tool_calls"): return msg["content"] for tc in msg["tool_calls"]: func_name = tc["function"]["name"] func_args = json.loads(tc["function"]["arguments"]) result = dispatch(func_name, func_args) messages.append({ "role": "tool", "tool_call_id": tc["id"], "content": json.dumps(result, ensure_ascii=False) }) return "任务超时,已转人工处理"循环终止条件在这里就是max_steps。当模型连续调用工具后,最终会给出一段不再带tool_calls的回答,那就是最终结果。你问"北京天气怎么样",模型大概率会先调用get_weather并传入city=北京,而不是自己编一个温度,这就是工具调用的核心价值——把知识获取变成代码执行,从根源上减少幻觉。
5.3 加一点记忆和权限控制
上面的demo已经包含了短期记忆,messages列表就是。长期记忆通常需要向量库,但最小版本可以退一步:把用户的历史偏好保存在一个字典里,在构造对话时作为系统提示注入。如下面这样:
user_preference = {"temperature_unit": "摄氏度", "report_format": "简洁"} system_prompt = ( "你是企业AI助理。请严格使用工具获取实时数据,不要凭记忆编造。" f"用户偏好:{json.dumps(user_preference, ensure_ascii=False)}" ) messages = [{"role": "system", "content": system_prompt}, {"role": "user", "content": question}]权限控制在demo里可以做成装饰器,给敏感工具加校验。例如一个发送邮件的工具,在dispatch函数里先检查上下文中的用户权限标识,没有授权就直接抛错,让"错误信息作为观察"机制生效,模型会自行调整策略或者向用户索要必要的授权。这就是最小版本里的guardrail。
5.4 错误信息也是推理的一部分
这是我最想强调的一个实战细节。工具调用失败时,不要默默吞掉异常,更不要把堆栈原样抛给用户看。正确做法是把结构化的错误信息作为工具调用结果返回给模型,让它理解发生了什么并尝试修正。比如dispatch里捕获到权限不足,返回{"error": "permission_denied", "detail": "当前用户无发送邮件权限,需要管理员审批"},模型看到后知道不是参数问题,而是权限边界,它会向用户说明或者改用其他工具。这条经验让我在调试agent时省了无数心力,因为大多数"agent卡死"并不是模型笨,而是错误信息根本没给到模型手里。
6. 常见问题与排查经验
6.1 上下文爆炸:不是模型问题,是设计问题
上下文窗口快速增长,但我见过太多团队依然被上下文问题卡住。症状很典型:任务跑到后半段,模型开始"失忆",忘记最初的指令,回复逻辑混乱。排查后才发现,工具返回的大段JSON、历史消息、中间推理结果全都堆在上下文里。
对策有几个层次。第一层是工具的返回结果要做裁剪,只返回模型决策必需的最小字段,比如列表查询只返回id和标题,需要详情再调详情工具;第二层是历史消息滚动裁剪,超出窗口时把旧对话做摘要压缩;第三层是规划与执行分离,在plan阶段生成的任务清单可以单独持久化,不需要每轮都完整带回上下文。从这几层做下来,大部分上下文问题都能缓解。
6.2 工具调用失灵:schema的锅,不是模型的锅
模型报"无法调用工具"或者"参数格式错误",九成是工具定义的问题。最常见的坑有三个:schema没有标明required参数,模型就拿不准哪些字段必须传;枚举字段没有写可选值,模型猜了一个不存在的值;说明里没写负面约束,模型在不该用工具的场景下强行调用。
排查工具调用失灵有个固定套路:打开审计日志,看模型当时收到了什么。如果同一个工具时而成功时而失败,对比成功和失败两条日志里的工具描述和传入参数,很快能找出差异。大部分情况下,给工具的description补一句"不要用于A场景"就能解决一半的误调用。
6.3 成本失控与性能优化
agent-native的账单焦虑是真实的。我见过一个报表助手,月账单高达数万元,成本大头不是模型推理,而是工具返回大结果集反复在循环里回传。优化先算清楚成本分布:每次循环调用模型产生了多少输入token、多少输出token,工具结果占多少比例。
性能优化的常用手段我在下表里整理过,按性价比从高到低排列:
| 优化手段 | 原理 | 适用场景 |
|---|---|---|
| 工具结果裁剪与摘要 | 减少输入token,降本直接 | 工具返回长列表、长文本 |
| 最大步数限制 | 防止死循环空转 | 所有任务 |
| 语义缓存 | 相同或相似请求复用结果 | 查询类高频任务 |
| 小模型做前置分类 | 用便宜模型判断意图归属 | 任务分诊、工具选择 |
| 流式输出 | 提升首字响应体验 | 面向用户的长文本生成 |
成本优化永远不要以牺牲工具质量做代价。工具描述省了几个字,模型多误调两次,省下的token全吐回去,得不偿失。
6.4 安全与审计:出事了能不能说清楚
agent跑在生产环境,出事故不可怕,说不清才可怕。我给所有工具调用都加了审计日志,记录调用时间、传入参数、返回结果、消费token数、模型决策理由。一旦任务结果异常,回放日志就能定位是模型决策错了、工具实现错了还是数据源错了。
权限上要记住一个容易被忽略的细节:agent可能把用户A的数据传给工具B。工具级权限只能校验"能否调用",数据级权限还得校验"能碰哪些数据"。同一个查询工具,普通员工和管理员能查的范围完全不同,这个逻辑不能写在prompt里,必须下沉到工具实现层,作为硬校验执行。这是我反复强调的:prompt约束是软性的,代码约束才是可靠的。
结尾的个人体会
踩过这么多坑之后,我最大的心得是:agent-native不是让你把产品做没,而是让你把产品做薄。传统软件的厚度在功能列表,agent-native的厚度在意图理解和工具可靠性。我现在做新功能,顺序变成了先写工具签名,再写prompt,最后才考虑用户界面长什么样。这个习惯改过来之后,团队讨论的焦点也随之变化:我们不再争按钮放哪里,而是争"这个意图会不会被误解""这个约束够不够硬"。我觉得这个转变本身就是agent-native带给我最大的价值。
如果你也准备动手,我的建议是从一个内部工具开始,挑一个目标明确、有可验证结果的小场景,跑通一个完整的推理循环。先别追求多人协作和长期记忆,先把工具定义和循环控制做扎实。等你能用代码解释清楚每一轮模型调用的流转逻辑,再回头做架构升级,会发现很多之前觉得玄乎的问题其实都是纸老虎。