ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于Embedding与聚类的Agent行为分析:从Trace到行为洞察

基于Embedding与聚类的Agent行为分析:从Trace到行为洞察 1. 从一堆看不懂的 Trace 说起为什么 Agent 行为分析这么难做过 Agent 开发的人都有一个共同的痛点上线跑了一段时间日志里堆了几十万条 Trace每条 Trace 里嵌套着十几层 Span每个 Span 又带着一堆属性字段。你想搞清楚用户到底在问什么Agent 在哪一步开始跑偏哪个工具调用最耗时为什么同一个问题有时候答得好有时候答得烂结果打开日志面板一看密密麻麻的 JSON 直接劝退。这不是个别现象。Agent 系统和传统后端服务最大的区别在于它的执行路径是非确定性的。同一个用户输入因为模型采样的随机性、工具返回结果的差异、上下文窗口里历史消息的不同Agent 可能走完全不同的推理链路。传统 APM 那套按接口名聚合、按状态码分类的思路放到 Agent 场景里基本失效——你没法用固定的维度去归类一个每次都不一样的东西。我自己的项目就踩过这个坑。早期做客服 Agent 的时候我们按tool_name和status做了简单的聚合看板结果发现 80% 的 Trace 都落在其他这个桶里根本看不出问题。后来才意识到Agent 的行为特征不在单个字段上而藏在整条 Trace 的语义结构里——它调用了哪些工具、按什么顺序调、每步的输入输出语义是什么、整个 Session 里多轮对话是怎么演化的。所以这篇文章要聊的核心思路就是把 Trace 当作文本来理解用 Embedding 把每条 Trace 映射成向量再用聚类算法自动发现行为模式。这套方法不需要你预先定义分类规则而是让数据自己说话把海量 Trace 自动分成若干有意义的簇每个簇代表一种典型的 Agent 行为模式。配合 Session 维度的聚合你还能看到同一类行为在多轮对话中是怎么演变的。这套东西适合谁如果你是大模型开发工程师、Agent 平台的建设者、或者正在做 Agent 可观测性相关的工作那这篇内容基本可以直接抄作业。如果你只是刚入门 Agent 开发也能从中理解为什么 Agent 的行为分析不能照搬传统监控这个根本问题。2. 整体设计思路为什么是 Embedding 聚类而不是规则匹配2.1 传统规则匹配为什么在 Agent 场景下会崩先说清楚为什么不能用老办法。传统做法是给 Trace 打标签比如调用了搜索工具、调用了代码执行、报错了、超时了。这套逻辑在确定性系统里很好用因为同样的输入必然产生同样的路径。但 Agent 不是这样。我举个实际例子。用户问帮我查一下北京明天天气如果下雨就提醒我带伞。Agent 可能的行为路径有路径 A调用天气 API → 拿到小雨 → 调用提醒工具 → 结束路径 B调用天气 API → 拿到小雨 → 直接生成文本回复记得带伞 → 结束路径 C先调用搜索工具确认北京指哪个城市 → 再调天气 API → 再调提醒工具 → 结束路径 D调用天气 API 失败 → 重试 → 成功 → 调提醒工具 → 结束这四条路径用规则匹配怎么归类按工具名A 和 B 工具序列不同但语义相近。按状态D 有重试但最终成功。按耗时C 明显更长但不代表有问题。你会发现任何单一维度的规则都无法捕捉行为模式这个整体概念。更麻烦的是Agent 的行为空间是开放的。你永远不知道用户会问出什么问题也就无法预先枚举所有可能的工具组合。规则匹配的维护成本会随着 Agent 能力扩展而指数级上升。2.2 Embedding 把行为变成可计算的向量Embedding 的核心价值在于它能把一段变长的、结构化的、语义复杂的Trace 文本压缩成一个固定维度的稠密向量而且这个向量保留了语义相似性——行为相似的 Trace向量距离就近行为差异大的 Trace向量距离就远。关键在于怎么把 Trace 文本化。我的做法是构造一个行为描述模板把 Trace 里的关键信息按固定格式拼成一段自然语言。比如上面路径 A 的 Trace可以转成用户意图: 天气查询与提醒 工具调用序列: weather_api - reminder_tool 工具调用次数: 2 是否重试: 否 最终状态: 成功 关键参数: city北京, date明天, condition小雨 响应长度: 短这段文本丢给 Embedding 模型得到的向量就编码了这条 Trace 的行为指纹。路径 B 的文本会是工具调用序列: weather_api - text_response向量距离就会和 A 拉开一点但比和路径 D有重试的距离要近。这就是我们想要的语义聚类效果。注意Trace 文本化的模板设计是整套方案里最需要打磨的部分。字段选多了会引入噪声选少了会丢失区分度。我的经验是优先保留工具调用序列最终状态关键参数摘要这三类其他字段作为可选补充。2.3 聚类算法选型为什么我最终选了 HDBSCANEmbedding 之后就是聚类。常见的 K-Means、DBSCAN、HDBSCAN、层次聚类我都试过最后稳定用的是HDBSCAN。原因有三个第一不需要预先指定簇数量。Agent 的行为模式数量是未知的K-Means 要求你拍脑袋定 K 值这在探索性分析里是致命的。HDBSCAN 自动决定簇的数量还能把不属于任何簇的样本标记为噪声。第二能处理密度不均的簇。Agent 行为分布天然不均匀——常见行为比如简单问答会形成一个大而密的簇罕见行为比如复杂多工具编排会形成小而疏的簇。DBSCAN 用固定半径很容易把稀疏簇当成噪声丢掉。HDBSCAN 通过层次化的密度估计能同时捕捉这两种簇。第三对噪声鲁棒。真实 Trace 里总有一些异常样本比如调试时手动触发的、测试环境的脏数据HDBSCAN 会把它们归为 -1 类噪声不污染正常簇的质心。参数上主要调两个min_cluster_size和min_samples。前者控制一个簇最少要有多少条 Trace 才算数我一般设成总样本量的 0.5%~2%后者控制核心点的邻域密度设小一点3~5能让簇更宽松。这两个参数需要根据你的数据量做几轮实验。2.4 Session 维度聚合从单条 Trace 到多轮行为演化单条 Trace 的聚类只能告诉你有哪些行为模式但 Agent 的真实价值往往体现在多轮 Session里。用户和 Agent 来回对话十几轮行为模式会演化——可能从简单问答逐渐变成复杂任务编排也可能因为某轮失败而陷入反复重试的循环。所以我在 Trace 聚类之上又加了一层 Session 聚合把同一个 Session 下的所有 Trace 按时间排序得到这个 Session 的行为模式序列。比如[简单问答, 工具调用, 工具调用, 报错重试, 成功回复]。这个序列本身就是一种更高层的特征可以用来分析哪些行为模式序列最容易导致用户满意度下降哪些序列代表任务成功完成的典型路径哪些序列是卡住的信号需要人工介入这层聚合让分析从静态分类升级到动态演化价值提升非常明显。3. 核心细节拆解Trace 文本化、Embedding 选型与聚类参数3.1 Trace 文本化模板的字段设计前面提到文本化模板是关键这里展开讲具体怎么设计。一条完整的 Trace 通常包含这些信息字段类别具体字段是否必选说明基础信息trace_id, session_id, timestamp必选用于关联和排序不进入文本用户意图首轮用户输入摘要必选用 LLM 压缩成一句话工具调用工具名序列、调用次数必选行为模式的核心特征执行状态最终状态、是否重试、错误类型必选区分成功/失败/异常路径参数摘要关键参数键值对可选增强语义区分度性能指标总耗时、token 消耗可选用于后续分层分析输出特征响应长度、是否含结构化数据可选辅助判断输出类型文本化的格式我建议用结构化自然语言而不是纯 JSON。原因是 Embedding 模型对自然语言的语义捕捉能力远强于对 JSON 结构的理解。比如这是一条 Agent 执行记录。 用户意图是查询天气并设置提醒。 Agent 依次调用了 weather_api 和 reminder_tool 两个工具共调用 2 次。 执行过程中没有发生重试最终状态为成功。 关键参数包括城市北京、日期明天、天气状况小雨。这段文本丢给 Embedding 模型得到的向量会很好地编码天气查询提醒成功这个语义。如果换成 JSON模型需要额外学习 JSON 结构效果会打折扣。实操心得用户意图摘要这一步我强烈建议用一个小模型比如 7B 级别的离线批量生成而不是实时调用。因为 Trace 分析是离线任务没必要为了实时性牺牲成本。批量跑一次几百万条 Trace用 vLLM 部署的小模型几个小时就能搞定。3.2 Embedding 模型选型中文场景下的实测对比Embedding 模型的选择直接决定聚类质量。我在中文 Agent 场景下实测过几个主流模型结论如下模型维度中文语义长文本推理速度我的评价BGE-large-zh1024优秀一般(512)中等中文场景首选性价比高BGE-m31024优秀优秀(8192)较慢长 Trace 场景推荐text-embedding-3-large3072良好优秀快(API)英文为主中文略弱GTE-large-zh1024优秀良好中等BGE 的有力竞品M3E-base768良好一般快轻量场景可用我的最终选择是BGE-m3。原因是 Trace 文本化之后长度可能到 500~1000 tokenBGE-m3 的 8192 上下文窗口能完整覆盖不需要截断。而且它支持多语言如果你的 Agent 有中英混合场景一个模型就够了。维度方面1024 维是个甜点。太低如 384会丢失语义细节太高如 3072会让聚类计算变慢且容易过拟合噪声。1024 维在效果和效率之间平衡得最好。注意Embedding 模型一定要和你的业务语料对齐。如果你的 Agent 大量使用特定领域的术语比如医疗、法律通用 Embedding 模型可能表现不佳。这时候可以考虑用领域语料做微调或者用 LLM 生成一批行为相似/不相似的样本对做对比学习。3.3 聚类参数调优min_cluster_size 怎么定HDBSCAN 的参数调优是门手艺。我总结了一套流程第一步先跑一版粗聚类。min_cluster_size设成总样本量的 1%min_samples设成 5。看聚类结果的簇数量和噪声比例。如果噪声超过 30%说明参数太严如果簇数量超过 50 个说明太松。第二步根据业务目标调整。如果你想要粗粒度行为分类就把min_cluster_size调大比如 3%让簇更少更稳定。如果你想要细粒度异常发现就调小比如 0.3%让罕见行为也能成簇。第三步用轮廓系数和业务可解释性双重验证。轮廓系数是纯数学指标但 Agent 场景下更重要的是每个簇能不能用一句话解释清楚。如果某个簇你看了半天说不出它代表什么行为那这个簇大概率是噪声或者参数不当的产物。我一般会做 5~8 轮参数扫描把每轮的结果导出成表格人工看几个代表性样本最后选定一组参数。这个过程听起来繁琐但一次调好之后可以复用很久。3.4 Session 序列构建时间窗口与状态机Session 聚合的难点在于如何定义 Session 边界。简单按session_id分组是最直接的但实际场景里会有问题有些 Session 可能跨越很长时间用户隔天回来继续有些 Session 可能因为超时被切断。我的做法是加一个时间窗口约束同一个session_id下相邻两条 Trace 的时间间隔超过 30 分钟就切分成新的逻辑 Session。这个阈值可以根据业务调整客服场景可能 10 分钟就够复杂任务编排场景可能需要 1 小时。切分完之后每个逻辑 Session 就是一个 Trace 序列。我把这个序列映射成行为模式序列用聚类标签替换然后统计序列的转移矩阵。这个转移矩阵能直观地告诉你从简单问答出发下一步最可能变成什么模式哪些模式是死胡同进去就出不来。4. 完整实操流程从原始 Trace 到行为洞察4.1 数据准备与清洗第一步是把原始 Trace 从存储里拉出来。不管你是用 Jaeger、Tempo 还是自建的 ClickHouse 表核心是把每条 Trace 的所有 Span 聚合成一条记录。这里有个坑Span 的父子关系要正确重建否则工具调用序列会乱序。我用的是 ClickHouse 存 Trace查询语句大概长这样SELECT trace_id, session_id, min(timestamp) AS start_time, max(timestamp) AS end_time, groupArray((span_id, parent_span_id, name, attributes)) AS spans FROM traces WHERE start_time now() - INTERVAL 7 DAY GROUP BY trace_id, session_id拿到数据后要做几件事过滤测试数据把user_id是测试账号的、environment是 dev 的剔除过滤不完整 Trace有些 Trace 因为采样或超时只有部分 Span这些会干扰聚类标准化工具名同一个工具可能有多个别名比如weather_api和get_weather要统一脱敏用户输入里可能含手机号、身份证等文本化之前必须脱敏实操心得脱敏这一步千万别偷懒。我见过有团队直接把原始 Trace 丢给 Embedding 模型结果向量里编码了用户隐私信息聚类结果里能反推出具体用户。合规风险极大。建议用正则NER 双重脱敏把手机号、邮箱、身份证、银行卡号都替换成占位符。4.2 Trace 文本化与批量 Embedding清洗完之后用前面设计的模板把每条 Trace 转成文本。这一步我建议用 Python 脚本批处理核心逻辑def trace_to_text(trace): intent summarize_intent(trace.user_input) # 小模型生成 tools - .join(trace.tool_sequence) status 成功 if trace.success else f失败({trace.error_type}) retry 有重试 if trace.retry_count 0 else 无重试 params , .join([f{k}{v} for k, v in trace.key_params.items()]) return f这是一条 Agent 执行记录。 用户意图是{intent}。 Agent 依次调用了{tools}共调用 {len(trace.tool_sequence)} 次。 执行过程中{retry}最终状态为{status}。 关键参数包括{params}。文本生成后用 BGE-m3 批量编码。这里有个性能优化点用 GPU 批推理batch_size 设成 64~128几百万条 Trace 几个小时能跑完。如果只有 CPU建议用 ONNX 加速速度能提升 3~5 倍。编码完得到的是一个(N, 1024)的矩阵N 是 Trace 数量。这个矩阵就是后续聚类的输入。4.3 降维与聚类执行1024 维直接聚类计算量不小而且高维空间里距离度量会退化维度灾难。我一般先用UMAP降到 50 维左右再做 HDBSCAN。UMAP 的好处是保留局部和全局结构降维后聚类效果比 PCA 好很多。import umap import hdbscan # 降维 reducer umap.UMAP(n_components50, n_neighbors15, min_dist0.1, metriccosine) embeddings_50d reducer.fit_transform(embeddings) # 聚类 clusterer hdbscan.HDBSCAN( min_cluster_sizeint(len(embeddings) * 0.01), min_samples5, metriceuclidean, cluster_selection_methodeom ) labels clusterer.fit_predict(embeddings_50d)跑完之后labels里每个值就是对应 Trace 的簇编号-1 表示噪声。统计一下簇数量和噪声比例如果噪声超过 30%就调大min_cluster_size重跑。4.4 簇的解读与命名聚类本身不产生意义意义需要人来赋予。对每个簇我会做这几件事抽样看典型样本从簇里随机抽 10 条 Trace看它们的文本描述提取高频特征统计簇内工具序列、状态、参数的分布用 LLM 生成簇描述把抽样样本丢给 LLM让它总结这个簇代表什么行为模式人工校验命名LLM 生成的描述需要人工确认确保准确比如某个簇的样本都是用户问简单事实性问题Agent 直接回答无工具调用成功那这个簇就可以命名为直接问答型。另一个簇都是多工具调用重试最终成功可以命名为复杂任务重试成功型。命名之后你就有了一个行为模式字典。后续所有新来的 Trace都可以通过 Embedding 最近邻查找快速归类到已知模式或者标记为新模式触发人工审查。4.5 Session 序列分析与可视化有了 Trace 级别的簇标签Session 级别的分析就水到渠成。把每个 Session 的 Trace 按时间排序用簇标签替换得到行为序列。然后统计序列长度分布大部分 Session 是几轮有没有异常长的模式转移矩阵从模式 A 到模式 B 的概率是多少终止模式分布Session 最后停在哪个模式是成功回复还是报错退出这些统计结果用热力图或桑基图展示一眼就能看出问题。比如我发现过某个 Agent 的 Session 有 40% 终止在反复重试模式追查下去发现是某个工具的超时配置不合理导致大量请求卡在重试循环里。5. 常见问题与排查技巧实录5.1 聚类结果全是噪声怎么办这是最常见的问题通常有三个原因原因一Embedding 质量差。检查你的文本化模板是不是把太多无关信息编码进去了。比如把trace_id、timestamp这种随机性强的字段也放进文本会让向量变得分散。解决方法是只保留语义相关的字段。原因二min_cluster_size 太大。如果你的数据量只有几千条min_cluster_size设成 1% 就是几十条可能超过了很多真实簇的大小。这时候要调小甚至设成 5~10 这种绝对值。原因三数据本身太分散。如果 Agent 的行为确实高度多样没有明显聚集那聚类效果差是正常的。这时候可以考虑先按业务维度比如用户类型、任务类型分层再在每层内聚类。5.2 簇的数量太多或太少怎么调簇太多比如超过 50 个说明min_cluster_size太小或者 Embedding 维度太高导致过拟合。调大min_cluster_size或者先做一次粗粒度降维比如降到 20 维。簇太少比如只有 3~5 个说明min_cluster_size太大把很多小簇合并了。调小参数或者换cluster_selection_method从eom改成leaf后者会保留更多细粒度簇。我的经验是对于百万级 Trace10~30 个簇是比较合理的范围。太少看不出细节太多没法人工解读。5.3 新 Trace 如何归类到已有簇生产环境里你不可能每次都重新跑全量聚类。我的做法是保存每个簇的质心向量簇内所有 Trace 向量的均值新 Trace 编码后计算它到各质心的余弦距离距离小于阈值的归入最近簇大于阈值的标记为未知模式定期比如每周把累积的未知模式样本重新聚类发现新行为模式这个流程能保证分析系统的持续演进同时控制计算成本。5.4 常见问题速查表问题现象可能原因排查方向解决方法噪声比例 50%文本化引入噪声检查模板字段移除随机性字段簇数量 50参数过松看簇大小分布调大 min_cluster_size簇数量 5参数过严看噪声比例调小 min_cluster_size簇内样本不相似Embedding 质量差抽样看文本换模型或微调聚类结果不稳定数据量太小看总样本数累积更多数据再跑新 Trace 归类不准质心漂移看质心更新频率定期重算质心避坑技巧聚类结果一定要做时间稳定性检查。同一个簇在连续两周的数据上应该保持相似的语义。如果某个簇这周代表天气查询下周变成订单查询说明 Embedding 或聚类参数有问题需要重新调优。6. 从行为洞察到产品优化这套方法能带来什么6.1 发现隐藏的失败模式传统监控只能告诉你成功率 95%但聚类能告诉你那 5% 的失败里有 3% 是同一个模式——工具调用超时后没有正确降级。这种细粒度的洞察是优化 Agent 稳定性的关键输入。我在一个项目里通过聚类发现Agent 在处理多轮追问场景时有 15% 的 Session 会陷入用户重复问同一个问题Agent 重复给相似答案的死循环。这个模式在传统指标里完全看不出来因为每轮都成功了但用户体验极差。定位到问题后我们在 Prompt 里加了检测到重复意图时主动澄清的逻辑死循环比例降到了 2% 以下。6.2 指导 Prompt 和工具优化聚类结果能直接指导优化方向。比如某个簇代表Agent 调用了不必要的工具那就可以优化 Prompt让模型更谨慎地决定是否调用工具。某个簇代表工具参数经常传错那就可以在工具定义里加更严格的参数校验和示例。6.3 建立行为基线做异常检测有了稳定的行为模式字典就可以建立行为基线。正常情况下各模式的分布应该是稳定的。如果某天某个模式的占比突然飙升那就是异常信号可能意味着上游数据源变了比如某个 API 返回格式改了模型版本更新引入了行为变化用户群体发生了变化比如来了大量新用户这种基于行为分布的异常检测比基于单条 Trace 的阈值告警要灵敏得多而且误报率低。6.4 支撑 Agent 的持续迭代Agent 系统是持续迭代的。每次改 Prompt、换模型、加工具都会影响行为模式。用这套聚类方法你可以量化每次迭代的影响新模式出现了吗旧模式消失了吗各模式的比例变化了多少这比上线后看几天数据感觉还行要科学得多。我现在的习惯是每次 Agent 版本更新后跑一次聚类对比生成一份行为变化报告作为迭代决策的依据。7. 一些实操中的个人体会这套方法我从最早的规则匹配一路踩坑过来中间试过不少弯路。最开始我想用 LLM 直接给每条 Trace 打标签结果成本高得离谱而且 LLM 的标签一致性很差——同一条 Trace 今天标查询类明天标信息获取类。后来才转向 Embedding 聚类的路线成本降了两个数量级一致性也好了很多。另一个体会是不要追求一次做到完美。聚类分析是个迭代过程第一版结果肯定不理想但只要能看到一些有意义的簇就说明方向对了。然后通过调整文本化模板、换 Embedding 模型、调聚类参数逐步优化。我现在的项目跑了半年多行为模式字典从最初的 8 个扩展到了 25 个覆盖了绝大部分业务场景。最后分享一个小技巧把聚类结果和业务指标关联起来。比如把每个簇的 Trace 和用户满意度评分、任务完成率做交叉分析你会发现某些行为模式和低满意度强相关。这些就是优先优化的目标。单纯看聚类结果容易陷入为了聚类而聚类只有和业务价值挂钩这套方法才真正有意义。后续如果数据量继续增长可以考虑用 Faiss 做向量索引加速最近邻查找或者用增量聚类算法比如 BIRCH支持流式更新。这些是规模化之后的优化方向初期用 UMAP HDBSCAN 的批处理方案完全够用。
返回列表