1. 从“玩具”到“工具”:我对AI Agent认知的两次转变
第一次接触AI Agent这个概念时,我和很多人一样,觉得它就是个“能聊天的机器人plus版”——无非是能多轮对话、能记住上下文、能调用几个API。直到我在一个真实项目里被逼着用它解决了一个具体问题,才意识到自己之前的理解有多浅。那是一个需要从多个来源抓取信息、清洗、比对、再生成结构化报告的任务,纯手工做要花大半天,用传统脚本写又极其脆弱,因为数据源的格式三天两头变。当时我抱着试试看的心态搭了一个Agent,结果它跑通了,而且在我没预料到的边界情况下也给出了合理处理。那一刻我才真正理解:AI Agent的核心价值不在于“智能”,而在于“自主决策下的任务闭环”。
这篇文章想聊的,就是我在实际使用AI Agent过程中攒下来的一些小经验。不是那种“从零搭建一个Agent”的教科书式教程,而是那些你真正上手之后才会遇到的、文档里不会写的细节。比如为什么你的Agent总是陷入死循环、为什么工具调用的参数总是差一点、为什么明明逻辑没问题但结果就是不稳定。这些问题的答案,往往不在框架文档里,而在你对Agent运行机制的理解深度里。
适合谁看?如果你已经尝试过至少一个Agent框架(不管是LangChain、AutoGPT还是自己手搓的),跑通过至少一个Demo,但总觉得“能用但不好用”,那这篇内容应该能帮你省下不少试错时间。如果你还没开始,也可以先看看,至少能知道哪些坑值得提前避开。
2. Agent的“大脑”到底在做什么:拆解一次完整的决策链路
2.1 从用户输入到第一个动作:意图解析的隐藏成本
很多人以为Agent收到用户输入后,第一步是“理解意图”,然后“规划步骤”,最后“执行”。这个描述没错,但太粗了。实际运行中,从原始输入到第一个可执行动作之间,Agent内部至少经历了三层转换,每一层都可能出问题。
第一层是意图归一化。用户说“帮我看看这个文件里有没有敏感信息”,Agent需要把这句话转成可操作的目标,比如“读取指定文件内容,逐段检查是否包含预定义的敏感词列表,输出匹配结果”。这个转换过程依赖的是模型对任务类型的分类能力。我实测下来,如果不在系统提示里明确限定任务范围,模型很容易把“看看”理解成“简单浏览”而不是“逐项检查”,导致后续步骤偷工减料。
第二层是工具可用性匹配。Agent需要知道自己手头有哪些工具,以及每个工具能做什么。这里有个容易被忽略的细节:工具的描述文本质量直接决定了匹配准确率。我试过同一个功能,用“读取文件”和“读取本地文件内容并返回纯文本,支持txt/md/csv格式,单次最大100KB”两种描述,后者被正确调用的概率高出将近四成。原因很简单,模型在做工具选择时,本质上是在做语义相似度匹配,描述越具体,匹配越精准。
第三层是执行顺序规划。当任务需要多个工具配合时,Agent需要决定先调哪个、后调哪个、哪些可以并行。这一步最考验模型的逻辑推理能力。我的经验是,对于步骤超过三步的任务,最好在系统提示里给出一个粗略的流程框架,比如“先获取数据,再清洗,再分析,最后生成报告”,而不是完全让模型自由发挥。完全自由发挥的结果往往是它会在第一步就尝试调用最后一步的工具,然后因为缺少输入而失败。
2.2 工具调用中的参数传递:为什么总是差一点
工具调用是Agent最核心的能力之一,也是最容易出问题的地方。我统计过自己遇到的Agent执行失败案例,大约六成以上都发生在工具调用环节,其中绝大部分又是参数问题。
参数问题分三类。第一类是格式错误,比如工具要求传入JSON对象,模型传了一个字符串;或者要求日期格式是YYYY-MM-DD,模型传了“今天”。这类问题相对好解决,在工具定义里把参数类型和格式写清楚,再在系统提示里强调一遍,基本能压下去。
第二类是值域越界,比如工具要求传入1到100之间的整数,模型传了0或者-1。这类问题往往出现在模型对业务逻辑理解不深的时候。我的做法是在工具描述里不仅写“参数范围1-100”,还加一句“超出范围会导致工具报错并中断任务”,用后果描述来强化约束。
第三类是语义偏差,这是最麻烦的。比如工具需要的是“文件路径”,模型传了一个“文件名”;工具需要的是“查询关键词”,模型传了一整句话。这类问题的根源在于模型对参数语义的理解和工具设计者的预期不一致。解决办法是在工具描述里给出正例和反例,比如“正确示例:/data/input.csv;错误示例:input.csv(缺少路径)”。别小看这一句话,它能把参数准确率提升一大截。
还有一个实操技巧:对于关键参数,可以在Agent执行前加一个参数校验节点。这个节点不调用外部工具,只是用模型自己检查一遍参数是否符合要求,如果不符合就重新生成。虽然多了一次模型调用,但能避免很多因为参数错误导致的任务中断。
2.3 循环与终止:Agent什么时候该停下来
Agent的终止条件设计是个容易被轻视但极其重要的环节。我见过太多Agent要么停不下来(无限循环),要么停得太早(任务没完成就结束)。
无限循环的典型场景是:Agent调用工具A,得到结果,发现不满足条件,决定重试,又调用工具A,又得到同样结果,再重试……如此往复。根本原因在于Agent没有“这次结果和上次一样”的感知能力。解决办法是在系统提示里加入一条规则:“如果连续两次工具调用返回相同或高度相似的结果,停止重试并输出当前状态。”这条规则看起来简单,但能解决大部分死循环问题。
停得太早的情况则更隐蔽。比如任务要求“检查所有文件”,Agent检查了前三个就认为“已经覆盖主要部分”然后结束了。这类问题的根源是模型对“完成”的定义和人类不一致。我的做法是在任务描述里把完成条件量化,比如“必须检查目录下所有扩展名为.log的文件,数量以实际文件列表为准”,而不是说“检查日志文件”。量化条件能显著降低提前终止的概率。
另外,设置一个最大步数限制是必要的兜底措施。我一般会根据任务复杂度设置20到50步不等,超过就强制终止并输出中间结果。这个限制不是为了限制Agent的能力,而是为了防止它在异常情况下消耗过多资源。
3. 提示词不是“写清楚”就行:Agent场景下的提示工程差异
3.1 系统提示与任务提示的分工逻辑
在普通对话场景里,提示词工程的核心是“把需求说清楚”。但在Agent场景下,这个思路需要调整,因为Agent的提示词通常分为两层:系统提示和任务提示。这两层的分工如果不明确,会导致Agent行为混乱。
系统提示定义的是Agent的身份、能力和行为边界。它应该包含:Agent的角色定位(比如“你是一个数据分析助手”)、可用工具列表及使用说明、通用行为规则(比如“每次工具调用前先说明调用目的”)、异常处理原则(比如“遇到无法解析的输入时,输出错误信息并停止”)。系统提示是相对稳定的,不随具体任务变化。
任务提示定义的是当前要完成的具体目标。它应该包含:任务描述、输入数据、期望输出格式、特殊约束条件。任务提示是每次调用时动态生成的。
我踩过的一个坑是:把太多任务相关的细节写进了系统提示,导致Agent在处理不同任务时被无关信息干扰。比如系统提示里写了“处理CSV文件时注意编码问题”,结果Agent在处理JSON任务时也在纠结编码。后来我把这类细节移到任务提示里,只在相关任务出现时才注入,Agent的表现明显更稳定。
另一个经验是:系统提示里的工具说明要按功能分组,而不是按字母顺序排列。比如把“文件操作类工具”放在一起,“网络请求类工具”放在一起,“数据处理类工具”放在一起。这样模型在选择工具时,会先定位到功能组,再在组内选择具体工具,准确率更高。
3.2 少样本示例在Agent任务中的正确用法
少样本示例(Few-shot)在普通对话里很好用,但在Agent场景下需要谨慎。原因是Agent的任务往往涉及多步工具调用,如果你给的示例太具体,模型会倾向于照搬示例的步骤,而不是根据实际情况灵活调整。
我的做法是:给格式示例,不给步骤示例。比如我需要Agent输出一个JSON格式的分析报告,我会在提示里给一个JSON结构示例,标明每个字段的含义和格式要求,但不会给一个“先调用A工具,再调用B工具,然后生成报告”的完整流程示例。这样模型知道最终要输出什么,但具体怎么走到那个输出,由它自己规划。
如果确实需要给步骤示例,我会把它抽象成“模式”而不是“步骤”。比如不说“先调用read_file,再调用parse_csv”,而是说“对于文件处理类任务,通常需要先读取原始内容,再进行格式解析,最后执行目标操作”。这种模式描述既给了方向,又保留了灵活性。
还有一个细节:示例中的工具名称要和实际工具名称完全一致。我遇到过因为示例里写的是“read_file”但实际工具注册名是“readFile”,导致模型在模仿示例时调用了不存在的工具。这种错误很隐蔽,因为模型不会报“工具不存在”,而是会尝试用其他方式模拟,结果就是任务失败但原因不明。
3.3 约束条件的表达方式对执行结果的影响
约束条件在Agent任务里无处不在:时间限制、数据范围、输出格式、禁止操作等等。这些约束如果表达方式不对,要么被模型忽略,要么被过度执行。
我总结了一个原则:正向约束比负向约束有效,具体约束比抽象约束有效。
负向约束比如“不要修改原始文件”,模型有时候会理解成“不要直接修改,但可以复制一份再修改”,然后它真的去复制了。正向约束“所有操作都在副本上进行,原始文件保持只读”就明确得多。
抽象约束比如“输出要简洁”,模型对“简洁”的理解可能是一句话,也可能是三段话。具体约束“输出不超过200字,只包含结论和建议两部分”就清晰得多。
另外,约束条件的位置也有讲究。我习惯把最重要的约束放在任务提示的开头和结尾各一次。开头放是为了让模型在规划阶段就考虑到,结尾放是为了在生成最终输出前再确认一遍。实测下来,这种“首尾呼应”的约束方式比只写一次的执行率高不少。
还有一个反直觉的发现:约束太多反而会降低执行质量。当约束条件超过七八条时,模型会出现“约束疲劳”,开始随机忽略一些。这时候更好的做法是把约束分层:核心约束(必须遵守)放在任务提示里,次要约束(尽量遵守)放在系统提示里作为通用规则。这样模型在处理具体任务时,注意力会集中在核心约束上。
4. 工具设计与集成:那些让Agent“好用”与“难用”的分水岭
4.1 工具粒度的取舍:粗一点还是细一点
设计Agent工具时,第一个要做的决策就是粒度:是把一个功能做成一个大工具,还是拆成多个小工具?
粗粒度工具的例子:一个“process_data”工具,接收原始数据,内部完成清洗、转换、分析、输出全流程。细粒度工具的例子:拆成“clean_data”“transform_data”“analyze_data”“format_output”四个独立工具。
粗粒度的好处是Agent的决策负担轻,只需要调一次;坏处是灵活性差,如果中间某一步需要调整,整个工具都要改。细粒度的好处是灵活,Agent可以根据实际情况组合;坏处是决策链变长,出错概率增加。
我的经验是:对于流程固定的任务,用粗粒度;对于流程多变的任务,用细粒度。判断标准很简单:如果你自己手动做这个任务时,步骤每次都一样,那就粗粒度;如果步骤经常根据输入不同而变化,那就细粒度。
还有一个折中方案:中等粒度+参数控制。比如一个“process_data”工具,但接受一个“steps”参数,指定要执行哪些子步骤。这样既保持了单次调用的简洁性,又保留了一定的灵活性。不过这个方案对工具实现的要求更高,需要内部做好步骤编排。
4.2 工具返回值的结构化处理
工具返回值的设计直接影响Agent的后续决策质量。我见过很多工具返回的是纯文本,比如“操作成功,共处理15条记录”。这种返回值对人类友好,但对Agent不友好,因为Agent需要从自然语言里提取信息,容易出错。
更好的做法是返回结构化数据,比如JSON格式:{"status": "success", "processed_count": 15, "errors": []}。这样Agent可以直接读取字段,不需要做文本解析。如果工具本身返回的就是结构化数据,那最好保持原样,不要为了“好看”而转成自然语言。
还有一个细节:返回值里要包含足够的上下文信息。比如一个查询工具返回了结果列表,最好同时返回“总条数”“当前页码”“是否有下一页”等元信息。这样Agent在决定下一步动作时,不需要额外调用工具来获取这些信息。
我踩过的一个坑是:工具返回了错误信息,但错误信息太笼统,比如“操作失败”。Agent拿到这个返回值后,不知道是参数错了、网络超时了、还是权限不够,只能盲目重试。后来我在工具里加了错误码和错误详情字段,Agent就能根据错误类型决定是重试、修改参数、还是放弃。这个改动的成本很低,但效果提升很明显。
4.3 工具之间的依赖关系与调用顺序
当Agent有多个工具可用时,工具之间的依赖关系会影响调用顺序。有些依赖是显式的,比如“必须先获取token才能调用API”;有些依赖是隐式的,比如“必须先读取文件才能分析内容”。
对于显式依赖,我习惯在工具描述里直接写明前置条件,比如“调用此工具前需要先通过auth工具获取访问令牌”。这样Agent在规划时会自动把前置工具排到前面。
对于隐式依赖,处理起来更麻烦,因为Agent不一定能自己推理出来。我的做法是在系统提示里加一段“工具使用原则”,用自然语言描述常见的依赖模式。比如“处理文件类任务时,通常需要先读取内容,再进行解析或分析;处理网络类任务时,通常需要先检查连接状态,再发起请求”。这些原则不针对具体工具,而是描述一类任务的通用流程,模型在规划时会参考这些原则。
还有一个技巧:给工具打标签。比如给所有“读取类”工具打上“read”标签,给所有“写入类”工具打上“write”标签。然后在系统提示里说明“read类工具通常应在write类工具之前调用”。这样模型在选择工具时,会有一个粗略的顺序感。
5. 记忆与状态管理:让Agent不再“失忆”
5.1 短期记忆的存储与检索策略
Agent的短期记忆通常指的是当前任务执行过程中的上下文,包括用户输入、工具调用记录、中间结果等。这部分记忆的管理直接影响Agent能否在多步任务中保持连贯性。
最直接的做法是把所有历史记录都塞进上下文窗口。但这样做有两个问题:一是token消耗大,二是当历史记录太长时,模型对早期信息的注意力会下降。我实测过,当上下文超过模型窗口的70%时,Agent对早期步骤的回忆准确率明显降低。
我的策略是分层存储+按需检索。把记忆分成三层:核心层(当前任务的目标和约束)、操作层(最近N步的工具调用记录)、归档层(更早的历史记录)。核心层始终保留在上下文中;操作层保留最近5到10步;归档层不主动加载,只在Agent明确需要回忆某步操作时,通过一个“检索记忆”的工具来获取。
这个策略的关键是归档层的索引设计。我会给每条归档记录打上时间戳、工具名称、关键参数等标签,这样检索时可以按标签过滤,而不是全文搜索。比如Agent想回忆“之前读取的那个文件路径是什么”,它可以通过“工具名称=read_file”来检索,比全文匹配快得多也准得多。
5.2 长期记忆的写入时机与内容筛选
长期记忆指的是跨任务保留的信息,比如用户的偏好、常用路径、历史任务的成功模式等。长期记忆的价值在于让Agent越用越“懂你”,但写入时机和内容筛选如果做不好,反而会变成干扰。
写入时机方面,我遵循任务完成后写入的原则,而不是任务执行中写入。原因是任务执行中写入的信息往往是中间状态,可能后续会被修正或推翻。等任务完成后再写入,写入的是经过验证的最终结果,质量更高。
内容筛选方面,我只写入三类信息:用户明确表达的偏好(比如“以后报告都用Markdown格式”)、反复出现的模式(比如“这个用户经常处理CSV文件”)、任务成功的关键决策(比如“上次用分块读取解决了大文件内存问题”)。其他信息,比如具体的工具调用参数、中间计算结果,都不写入长期记忆。
还有一个细节:长期记忆需要定期清理。我一般每积累50条长期记忆就做一次回顾,把过时的、矛盾的、不再适用的条目删掉。否则长期记忆会越来越臃肿,检索效率越来越低,甚至出现新旧信息冲突导致Agent行为不一致。
5.3 状态冲突的检测与消解
当短期记忆和长期记忆出现冲突时,Agent需要知道以哪个为准。比如长期记忆里写着“用户偏好JSON输出”,但当前任务提示里明确要求“输出Markdown格式”。如果Agent不处理这个冲突,可能会随机选一个,或者把两种格式混在一起。
我的做法是在系统提示里加一条优先级规则:当前任务提示 > 短期记忆 > 长期记忆。也就是说,如果当前任务有明确要求,以当前任务为准;如果当前任务没提,参考本次执行过程中的短期记忆;如果短期记忆也没有,才使用长期记忆。
但光有规则还不够,还需要冲突检测机制。我会在Agent生成最终输出前,加一个“一致性检查”步骤,让模型自己检查一遍:当前输出是否符合任务提示的要求?是否和短期记忆中的关键信息矛盾?是否和长期记忆中的偏好冲突?如果发现冲突,输出冲突点并请求人工确认,而不是自行决定。
这个机制看起来增加了步骤,但实际上减少了很多“Agent自作主张”导致的问题。尤其是在处理敏感操作时,宁可多问一句,也不要擅自行动。
6. 性能与成本:让Agent跑得又快又省
6.1 Token消耗的主要来源与压缩空间
Agent的Token消耗通常比普通对话高得多,因为每次工具调用都需要把完整的上下文发给模型。我统计过一个中等复杂度的任务(大约10步工具调用),Token消耗是普通对话的15到20倍。
Token消耗的大头有三个:系统提示、历史记录、工具返回值。系统提示通常是固定的,但可以通过精简描述来压缩。比如把“你是一个专业的文件处理助手,擅长处理各种格式的文件,包括但不限于txt、csv、json、xml……”压缩成“文件处理助手,支持txt/csv/json/xml”。意思没变,但Token少了将近一半。
历史记录的压缩空间更大。我的做法是只保留关键步骤的摘要,而不是完整记录。比如一次工具调用返回了100行数据,我不把100行都放进上下文,而是放一个摘要:“read_file返回:共100行,前3行示例为……,文件大小2KB”。这样既保留了关键信息,又大幅减少了Token。
工具返回值的压缩原则是:Agent决策需要什么,就保留什么。如果Agent下一步只需要知道“操作是否成功”,那就只保留状态字段;如果需要知道具体数值,就保留数值字段。其他无关字段一律裁掉。
6.2 模型调用的批处理与缓存策略
Agent执行过程中,有些模型调用是可以合并的。比如多个独立的检查步骤,可以合并成一次调用,让模型同时输出多个检查结果。这样虽然单次调用的Token多了,但总调用次数少了,整体延迟和成本都会下降。
我常用的合并场景有两个:一是参数校验,把多个工具的参数校验合并成一次调用;二是结果汇总,把多个工具的执行结果合并成一次分析调用。合并的边界是:如果合并后的提示长度不超过模型窗口的50%,就合并;超过就拆分。
缓存策略方面,我主要缓存两类内容:工具描述和系统提示。这两部分在同一个Agent实例的生命周期内是不变的,可以缓存起来避免重复计算。有些模型服务商支持提示缓存功能,能显著降低重复部分的成本。如果没有这个功能,也可以自己实现一个简单的缓存层,把系统提示和工具描述的嵌入向量缓存起来,每次调用时直接复用。
还有一个容易被忽略的优化点:工具调用的并行化。如果多个工具之间没有依赖关系,可以让Agent同时调用它们,而不是串行执行。比如“读取文件A”和“读取文件B”可以并行,“查询数据库”和“调用外部API”也可以并行。并行化不仅能降低延迟,还能减少上下文切换带来的Token开销。
6.3 失败重试的成本控制
Agent任务失败是常态,但重试的成本需要控制。我见过一些Agent在失败后无限重试,每次重试都消耗大量Token,最后任务没完成,成本却高得离谱。
我的重试策略是分级重试+成本上限。分级重试指的是:第一次失败,原样重试一次;第二次失败,修改参数后重试;第三次失败,换一种工具或方法重试;三次都失败,放弃并输出错误报告。成本上限指的是:给每个任务设置一个Token预算,比如50万Token,超过预算就强制终止,不管任务是否完成。
还有一个技巧:重试时不要带上完整的失败历史。有些Agent会把之前所有失败尝试的完整记录都放进上下文,导致每次重试的Token消耗越来越大。我的做法是只保留最近一次失败的摘要信息,比如“上次尝试调用read_file失败,错误码404,文件不存在”。这样重试时的上下文保持精简,成本可控。
另外,对于可预见的失败,最好在工具层面就处理好,而不是让Agent去重试。比如文件不存在时,工具直接返回一个明确的错误码和提示,Agent看到这个错误码就知道应该换一个文件,而不是重试同一个文件。这种“工具层容错”比“Agent层重试”成本低得多,效果也好得多。
7. 我踩过的三个典型坑与修复过程
7.1 工具描述歧义导致的“答非所问”
有一次我设计了一个“search”工具,描述是“搜索并返回匹配结果”。结果Agent在需要查询数据库时调用了它,在需要查找文件时也调用了它,甚至在想获取网页内容时也调用了它。每次调用都返回不符合预期的结果,但Agent不知道问题出在哪,只是反复重试。
排查过程:我把Agent的执行日志打出来,发现它在不同任务场景下都选择了同一个工具。根本原因是“搜索”这个词太泛了,模型无法区分它到底搜什么。修复方案是把工具拆成三个:“search_database”“search_files”“search_web”,每个描述里明确写清楚搜索范围和返回格式。改完之后,工具选择准确率从不到50%提升到90%以上。
这个坑给我的教训是:工具名称和描述要具体到无法被误解。宁可名字长一点,也不要为了简洁而牺牲明确性。
7.2 上下文过长导致的“中间遗忘”
有一个任务需要处理大约30个文件,Agent需要逐个读取、分析、记录结果。跑到第20个文件左右时,Agent开始出现异常:它忘记了前面已经处理过哪些文件,开始重复处理;或者忘记了任务的总目标,把中间步骤当成了最终目标。
排查过程:我检查了上下文长度,发现已经接近模型窗口的上限。模型对早期信息的注意力确实下降了。修复方案是引入“进度摘要”机制:每处理5个文件,就让Agent生成一个摘要,记录已处理的文件列表和关键发现,然后把之前的详细记录从上下文中移除,只保留摘要。这样上下文长度始终控制在窗口的50%以内,Agent的表现恢复了稳定。
这个坑的教训是:不要假设模型能记住所有东西。该摘要的时候要摘要,该归档的时候要归档,上下文管理是Agent稳定运行的基础。
7.3 工具返回值格式不一致引发的“解析失败”
我有一组工具,有的返回JSON,有的返回纯文本,有的返回XML。Agent在处理这些返回值时,经常出现解析失败,因为它不确定当前返回值是什么格式。
排查过程:我让Agent在每次工具调用后输出它接收到的原始返回值,发现它在面对纯文本返回值时,会尝试用JSON解析,然后失败,然后重试,然后再次失败。修复方案是统一所有工具的返回值格式为JSON,即使某些工具原本返回纯文本,也在工具内部包一层JSON结构。比如{"status": "success", "data": "原始文本内容"}。这样Agent永远只需要处理一种格式,解析失败率降到了零。
这个坑的教训是:Agent的“舒适区”是结构化数据。如果工具返回值格式不统一,Agent就需要额外的心智负担去判断格式,这个负担会直接转化为错误率。统一格式是最简单也最有效的优化。
8. 关于Agent能力边界的一些个人判断
用了这么久AI Agent,我越来越觉得它的能力边界不在于“能不能做”,而在于“能不能稳定地做”。一个任务,Agent跑一次成功,跑十次成功七次,那它在生产环境里就是不可用的。而要把成功率从70%提到95%,需要的往往不是更强大的模型,而是更细致的工程:更清晰的工具描述、更合理的上下文管理、更完善的错误处理、更严格的约束条件。
另一个体会是:Agent最适合的场景是“流程相对固定但输入多变”的任务。比如数据清洗、格式转换、信息提取、报告生成。这些任务的特点是步骤明确,但每次的输入数据不一样,用传统脚本写要处理大量边界情况,用Agent则可以用自然语言描述规则,让模型去处理边界。反过来,如果任务流程本身就在不断变化,那Agent的规划能力反而会成为不稳定因素,不如人工介入或者用更确定性的方案。
最后说一个我经常被问到的问题:Agent会不会取代某些工作?我的看法是,Agent目前更像是一个“执行力很强但需要明确指令的助手”。它能帮你把重复性的、规则明确的工作自动化,但前提是你能把规则说清楚。如果你自己都说不清楚要做什么,Agent大概率也做不好。所以与其担心被取代,不如先想想自己能不能把任务拆解清楚——这个能力,在Agent时代反而更值钱了。