ARTICLE DETAIL

资讯详情

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

从百万级Agent运行日志中自动提取任务:工程实践与思考

从百万级Agent运行日志中自动提取任务:工程实践与思考 如果你手上有一个Agent平台那么你每天最不缺的东西大概就是Agent Run日志。Agent Runs和Task Extraction这两个词在最近的工程讨论里出现频率非常高但真正把“从百万条运行记录里提取任务”这件事落地并跑通的人其实并不多。我自己从去年底开始带团队把一条分析链路从“能看单条轨迹”做到“能从百万级轨迹里自动提取任务”中间踩过的坑、沉淀下来的工程经验都整理在这篇里。这篇文章适合正在做Agent可观测性、用户行为分析、想给Agent产品搭运营看板或者准备做Agent数据基础设施的朋友。当时我们平台每天新增的Agent Run数量已经到大几十万数据结构还五花八门——有LangChain的标准事件有自研SDK打的点还有一批是用户手动触发的后台作业。早期我们只能从数据库里捞出来人工筛但规模和多样性上来之后这条老路彻底走不通。Task Extraction从一个可选的AI功能变成了必须拿下的工程问题。下面的内容不会有太多漂亮的架构图更多是我自己实际遇到的问题、取舍逻辑以及可以直接拿去复用的设计。我把项目拆成六个部分先讲清楚“任务”到底指什么再讲数据管道怎么搭然后进入任务提取的核心算法与提示词设计接着是评估和人工校验最后是问题排查实录和后续扩展方向。1. 项目概述与整体设计思路1.1 任务不等于意图这是整个项目里最容易被误解的一点。很多人一开始把它当成意图分类Intent Classification来做但实际走下来会发现Task Extraction和意图分类根本是两码事。先说意图用户发给Agent的第一条消息想干什么比如“帮我查一下明天的天气”这是一次性意图。而任务Task是用户通过一次或多次Agent Run最终想拿到的一个可交付结果。举个例子用户想“整理一批产品文档并汇总成周报”这中间可能发生了四次Agent运行第一次让Agent扫描PDF目录第二次让它抽取重点第三次让它比对两份文档的差异第四次让它生成PDF格式的周报。四次运行如果按意图拆它们是四个不同的意图如果按任务拆它们是同一个任务的不同阶段。这个区分直接决定了数据模型的形态。我给任务定的定义是用户期望获得的一个可交付结果可以由一次或多次Agent运行共同完成通常对应一个明确的完成状态。在这条定义下任务提取不只是给单条运行打标签而是要把分散在运行日志里的“家长里短”串成完整的业务故事。还有一层更实际的考虑用户在同一个任务上反复失败、反复重试也是Agent平台最常见的场景。比如用户让Agent抓取某个网站第一天抓不动第二天换了个提示词接着抓第三天终于成功。如果只按单条运行做意图分类这三条记录会被分成三个互不相干的意图产品经理就会误以为“抓取失败”是个低频问题。但实际上这是一个持续了三天的强需求。所以我们的提取逻辑从一开始就考虑到了跨运行的聚合而不是单条运行的自娱自乐。1.2 为什么要下决心做这件事这个项目立项时老板给的理由很朴素运营团队月底要看汇报问“我们这个月Agent都帮用户干了什么”当时没人答得上来。只能靠抽样看日志几百条地看看完了还得靠人脑分类。这个状态显然维持不了因为样本量一大结论就开始打架。真正让管理层下定决心的是两笔账。第一笔是成本账我们想优化Agent调用成本但成本高在哪一个任务环节完全说不清楚。有的运行输入文本只有十个字背后却跑了长达三万token的上下文有的任务看着不起眼但全网调用占比极高。没有任务层面的标签就没有办法做成本归因。第二笔是产品账要做Agent的留存分析和用户运营必须知道高价值任务是什么高频低价值任务是什么用户在哪里放弃。这些分析都建立在“任务”这个业务单元上而不是建立在底层API调用日志上。这笔账算清楚之后我们定了一个非常朴素的北极星指标每天新增运行的90%能自动拿到一个准确度可验收的任务标签。剩下10%允许进入人工标注或者低置信度通道。这个指标不高调但它能落地能每周复盘。1.3 整体架构的分层思路架构上我始终觉得不用一上来就搞得特别重但分层是必须的。我们最终落地的结构是三层加两个通道第一层是标准化事件层。不管底层跑的是哪个Agent框架统一落成一套标准事件格式包含run_id、session_id、时间戳、输入、工具调用序列、模型事件、最终输出、状态等字段。这一层做不好后面所有提取都无从谈起。第二层是特征与摘要层。从标准化事件里生成两类数据一类是结构化特征比如运行时长、工具数、成败状态、token消耗另一类是文本摘要把Agent跑过的完整轨迹压缩成适合交给大模型处理的中等长度文本。这一层直接决定了提取成本和效果。第三层是任务提取与刻画层。运行特征和摘要进来之后经过规则闸门、大模型提取、聚类归并这三个环节产出“任务名、任务族、置信度、证据链”并写入任务注册表。两个通道则对应两种应用场景在线通道在运行结束后的数十秒内快速提取给实时看板和单条运行详情页提供标签离线通道每天凌晨对全量运行做一次重算用更完整的上下文修正在线通道不准确的标签并更新任务聚类中心。两套逻辑可以共用一套核心代码只在计算预算和上下文长度上做参数区分。这个分层看似简单但它有一个很重要的作用把“提取任务”和“怎么用任务”彻底解耦。算法团队可以在离线通道里随时重跑提取模型而不会影响在线看板的稳定性产品团队拿到的永远是任务注册表里的稳定ID而不是每天变来变去的文本标签。2. 数据管道的核心细节2.1 不同Agent框架的事件统一我们当时接进来的Agent运行来源至少有四种LangChain的流式事件、OpenAI Agents SDK的Run记录、AutoGen的对话消息还有自研SDK打的定制点。每种框架的日志字段都不一样如果不做统一Task Extraction的输入就会非常杂提取质量会崩。统一事件模型是我在整个项目里要求最严格的部分。最终落地的标准事件大概长这样{ run_id: run_20250121_abc123, session_id: sess_889012, user_id: u_123456, ts_start: 2025-01-21T03:22:11Z, ts_end: 2025-01-21T03:22:41Z, status: success, framework: langchain, input_text: 把这份产品说明整理成一页PPT摘要, tool_calls: [ {tool: file_read, action: read, path: product.md, status: 200, duration_ms: 180}, {tool: summarizer, action: generate, status: ok, duration_ms: 3400}, {tool: ppt_builder, action: create, status: ok, duration_ms: 4100} ], model_events: [ {seq: 1, input_tokens: 220, output_tokens: 80, duration_ms: 700, model: gpt-5-mini}, {seq: 2, input_tokens: 640, output_tokens: 210, duration_ms: 1300, model: gpt-5-mini} ], final_output: 已完成PPT保存在 /workspace/output/xxx.pptx, error: null, cost_usd: 0.0042, total_tokens_in: 860, total_tokens_out: 290 }各框架到这个格式的映射主要难点不在字段复制而在语义对齐。比如OpenAI Agents SDK里有个handoff概念表示Agent把控制权交给另一个Agent这本质上是运行链路上的一个节点而不是一次完整的运行。我们统一成tool_calls里的handoff类型而不是把它当成一次新Run的开始。又比如AutoGen里agent之间互相发消息每条消息可能只是对话过程中的一步但它对应的其实是模型事件而不是工具调用。如果在映射时不注意这两类区别后面做任务边界判断时就会错得很离谱。这个标准化工作花了团队大概三周时间期间反复核对三种框架的真实输出样例。我想强调一点事件标准化不值得用自动化的方式去“猜”更靠谱的做法是先把三种框架的每种事件类型各捞500条真实数据人肉看一遍再写映射规则。规则的适用范围远比你想象的大。2.2 会话切分与任务边界判断有了标准事件下一个难点是切分。无论怎么定义任务第一步总是要把属于同一个任务的多条运行串起来也就是Session和Task的划分。我会区分两层Session是用户与Agent的一次连续交互周期Task则可以横跨多个Session也可能在一个Session内包含多个Task。我们的切分策略并不复杂优先看三个信号一是session_id这是框架或者网关打的ID相对可信二是时间间隔同一用户前后两条运行之间超过30分钟且没有上下文引用关系就断成新会话三是引用关系用户发消息提及“刚才那份文档”“继续上一个任务”等字眼时即使间隔超过30分钟也会强行并入上一会话。但Task的边界和Session边界并不完全对齐。同一Session里最常见的现象是用户临时切换了目标。先让Agent查资料查完资料又让它做PPT中间没有明确的一句话切换但工具调用和上下文明显变了。如果按Session切两个任务就会被错误地并到一起。这个在在线提取阶段比较难处理因为我们看不到后续运行的信息。我们的解法是在线阶段先粗切宁可把任务切细一点也不要合并错离线阶段再用“多运行上下文窗口”做二次归并把明显属于同一目标的多段记录重新并回同一个Task。关于失败运行还要多说一句失败的运行不是没有任务恰恰相反失败运行往往是任务提取里最有价值的信息源。用户花了五分钟跟Agent来回对话就为了让它搞定一个任务结果失败这必然是强需求。所以我们的切分边界里失败运行不能被当成孤立垃圾丢弃它必须被保留在任务上下文窗口里并参与离线聚合。2.3 在线与离线两种处理通道的对比在线通道和离线通道很多人以为只是“快慢”的区别其实它们的上下文预算、考核指标和失败处理方式都不一样。我整理过一张对比表这里贴出来维度在线通道离线通道延迟要求运行结束后30秒内给出标签每日凌晨批量计算无强延迟要求可用上下文单条运行摘要 极简历史跨运行完整上下文 任务注册表主要成本约束每次调用预算控制在百毫秒级推理可用更长文本、更强模型但总量控制输出用途实时看板、单条详情、风控拦截任务数据仓库、运营分析、聚类更新错误容忍度可以低置信度、可纠错必须高准确度作为样本复核基准在线通道的工程实现上我最注重的就是兜底当大模型超时或提取结果不满足JSON schema时系统必须能降级到规则提取输出一个“挂起”状态而不是直接报错更不能阻塞用户请求本身。离线通道的核心目标则是修正在线通道的错误它可以用当天的全量数据做一个更乐观的全局判断。两条通道跑完以后所有提取结果都写进同一张任务表但多了一个source字段区分在线与离线。产品端读数据时永远优先读离线结果在线结果只是临时标签。3. 任务提取的关键实现路径3.1 先用规则闸门挡掉一部分流量最开始我在这个项目里犯过一个典型错误以为任务提取全靠大模型所有运行的文本都直接抛给LLM去解析。结果第一轮线上测试成本直接爆了而且很多明显无意义的运行也被生硬地“提取”出了任务。后来我加了一层规则闸门Rule Gate先在大模型之前把流量分流。规则闸门的核心目的不是提取而是减少无效计算。它处理三类运行第一类是明确可以跳过或低优先级处理的运行比如status为cancelled、duration小于1秒、没有工具调用、没有模型事件、用户输入为空。这些运行通常没有实际业务含义直接标记为task_type: noop不进大模型。第二类是失败但信息残缺的运行。比如整个run只有一行报错错误API Key无效上下文太少大模型也没有足够信息可提取。我们会先用规则识别出失败模式认证失败、配额超限、框架异常、超时把reason保存下来然后再决定是否需要大模型补充。第三类是高频重复运行。同一用户在同一个task_id下短期内重复同一操作只有微小的参数变化规则引擎可以直接承接不需要重新跑大模型。规则闸门的效果非常显著。我们的线上流量里最终大约35%的运行被规则闸门直接处理不需要任何模型调用。剩下的65%才进入大模型提取管道。这一步省下来的不是百分之几的成本而是每天几十万次模型调用的量级。3.2 提示词设计从轨迹摘要到任务抽取进入大模型管道的运行也不是把原始日志全塞给模型。原始轨迹可能包含大量的工具调用细节、HTTP状态码、中间推理过程这些对任务提取来说都是噪声。我们采用了两段式设计第一段生成轨迹摘要第二段基于摘要做任务抽取。第一段摘要的Prompt我写得比较克制要求模型只保留“用户在做什么、Agent调用了哪些关键工具、中间有哪些转折点、最终输出是什么”这四类信息。摘要长度控制在300~500个中文字符以内。示例模板是这样你是运行日志压缩器。下面是一条Agent运行的详细记录请压缩成一段结构化摘要。 必须保留 - 用户的输入意图 - Agent调用过的关键工具及顺序 - 是否发生重试、报错或异常分支 - 最终输出结果或失败原因 不要保留 - 具体HTTP状态码 - 低层实现细节 - 与业务无关的日志花絮 输出格式纯文本200~500字。第二段才是任务抽取的核心。Prompt关键点是“任务”的定义必须非常明确并且要给出“任务”和“意图”的区别同时要求输出JSON以方便程序消费。我们用过一版效果比较稳定的你是任务标注器。下面是一条Agent运行的轨迹摘要请提取出用户真正想要完成的“任务”。 任务定义用户希望获得的一个可交付结果一个任务可以由一次或多次Agent运行共同完成。 注意事项 1. 不要把单个工具调用当成任务。 2. 不要把“Agent做了什么”当成用户任务要写“用户最终想得到什么”。 3. 如果摘要中有失败和重试仍以最终目标为准。 4. 任务名不超过15个字尽量使用动宾短语。 请输出严格JSON { task_name: 简短任务名, task_family: 所属任务族, confidence: 0.0到1.0之间的小数, evidence: [摘要中支持该任务判断的一句原文] }这个模板看起来简单但里面的几个限定都是拿真实样本调过的。比如“任务名不超过15个字”这个约束能显著减少长尾碎片标签的数量“task_family”字段用来做粗分类避免几百种task_name把后续聚类空间撑爆。调用时模型温度设成0使用JSON output mode并对输出做schema校验不合法就重试一次重试仍失败就降级到规则提取。3.3 基于聚类的任务归并Prompt提取出的task_name是一堆自由文本直接当维度做聚合一定会爆炸今天叫“整理文档”明天叫“文档整理”后天叫“汇总文档到PPT”。所以我加了一个聚类归并层把语义相同的task_name归并到同一个任务族并分配稳定task_id。离线聚类我用的方案是句子嵌入加DBSCAN。先把每天提取出的task_name向量化确保同一个任务的不同写法在向量空间里离得近。DBSCAN的eps参数我调了一阵子最终取0.6min_samples取2。这个参数组合对中英文混合文本效果都不错也不会把差异很大的任务硬绑在一起。聚类完成后每个簇取置信度最高的一条task_name作为代表就是这个任务族的标准名。在线通道没法跑完整的DBSCAN我用的方案是“嵌入最近邻匹配”把用户当前运行提取到的task_name嵌入向量与任务注册表里所有已知簇的中心向量做相似度计算。取最高相似度如果超过0.82就把当前运行归入该任务族低于0.82则把当前运行标记为new_task_candidate并写入缓冲队列等离线通道重新聚类后决定要不要新建任务族。0.82这个阈值是踩过坑之后调出来的设低了会把“翻译合同”和“翻译论文”粘在一起设高了又会导致大量新任务候选增加运营压力。聚类归并层带来一个额外好处任务注册表会越用越准。随着一天天的聚类和人工纠偏注册表里的任务族数量趋于稳定在线提取的命中率也在提高。到了项目后期在线通道约有75%的运行能直接匹配上已有任务簇只有25%需要进入候选通道。3.4 流式提取的工程细节在线通道听起来简单落地时却有不少坑。最开始的版本是每来一条运行就同步等大模型返回结果在高流量时段把后端服务拖垮了。后来我把它改成异步任务队列运行结束事件写入消息队列工作服务消费任务提取完后把结果写回任务表。这个改动虽然让标签延迟从1秒增加到10秒左右但换来了系统的稳定性和弹性扩缩容能力。另一个重要细节是重试与幂等。大模型偶发超时总是会有的任务提取不像用户请求不能直接给用户看到底失败所以我们的设计是每条提取任务最多重试3次每次休眠时间递增同时用run_id做去重确保同一运行不会产生重复标签。消费端的处理要幂等即使同一run_id被消费两次也只能写出一条提取结果后写覆盖前写。还有缓存处理同一个task_id下的同类型运行我们只对第一条完整跑大模型后续的用缓存标签直接出结果。为了让缓存不过期得太慢每个task_id的缓存TTL定为24小时。4. 评估与质量保障4.1 评价指标不能只看准确率做完提取器后最难的不是上线而是做评估。一开始我照搬文本分类的套路人工标注一批样本跑准确率和召回率结果发现这个做法并不能反映真实效果因为任务提取的问题是开放式的没有“唯一正确答案”。比如一条运行摘要写的是“用户让Agent抓取网页并提取产品价格”标注的人可能标成“价格监控”也可能标成“竞品价格调研”。我们不能说谁错了。所以我采用的评估框架是三级匹配第一级是严格匹配要求模型输出的任务名与人工标注的任务名完全相同或者属于同一个标准任务ID。第二级是语义匹配任务名不同但经过一小段人工复核确认指向同一任务族比如“查天气”和“获取天气信息”就属于语义匹配。第三级是错误匹配即模型和人工标注明显指向不同任务或者模型根本没有提取出有效任务。在这个框架下我们的每日迭代看板只追踪两个指标严格匹配率和语义匹配率。模型迭代时前者可以低一点但后者必须保持在高位否则就说明提取器的能力不稳定。到项目稳定期我们的语义匹配率能维持在86%左右严格匹配率大约71%这个水平对运营分析来说已经够用。4.2 黄金数据集的构建与迭代人工标注这部分我踩过很多坑最大的坑是直接拿原始日志给标注同学看。Agent运行日志动辄几千行标注员根本看不下去标注质量快速下降还会产生大量不一致。后来我换成了“摘要工具序列最终输出”的三段式视图把原始日志折叠成一张卡片标注员只需要看卡片、选任务族、填任务名。这个改动把单条标注时间从8分钟降到了2分钟一致性也明显提升。黄金数据集也不是标注一次就结束的。我设置了每周一次的“漂移检查”从最新一周的提取结果里按置信度从低到高抽样200条重新交给标注同学复核。如果低置信度区间里的错误率超过30%说明模型遇到了一批新的任务形态必须补充到黄金数据集里作为下一轮微调或Prompt迭代的样本。这其实就是一个人机回环的迭代机制好模型的成果不是一次性训练出来的是每天人工复核一点点喂出来的。4.3 在线离线的质量对比与收敛有了黄金数据集后我每周都会做一次在线通道和离线通道同一批样本的对比评估。这个对比非常有价值因为在线通道受上下文限制只能看到单条运行准确率天然低于离线通道。我期望的状态是离线通道比在线通道高出5到10个百分点的语义匹配率。如果两条通道的准确率差距突然缩小通常意味着两类问题一是离线通道没有用够多运行上下文形成不了信息优势二是任务注册表更新不及时聚类中心漂移离线通道匹配错簇。这两种情况我都遇到过排查思路也简单拉出同一批run_id分别打印在线和离线的提取输入看看离线通道是不是拿到了完整的多运行上下文。另外一个工程上的小技巧离线通道重新聚完类之后不要马上全量更新在线匹配用的向量索引而是先跑一天灰度。因为聚类中心的突然变化可能导致大批在线结果从一个任务族跳变到另一个任务族产品侧会出现标签抖动。我们后来改成“聚类结果先落影子表跑满一天差异小于5%才切主表”基本消灭了跳变投诉。5. 常见问题与排查实录5.1 任务切分过细碎片化严重项目上线后的前两周我发现一个很不正常的现象任务统计表里有大量只出现一次的任务名占比甚至超过40%。按理说多数用户的任务高度集中在少部分类别上不应该这么碎。排查出来有两个原因。第一在线通道太保守为了不合并错把很多同一任务的多步骤运行切成了多个不同task_name第二聚类参数太严格eps设得太小语义相近的task_name没有归并到一起。解决办法是同时改两处在线通道增加“同一用户、同一Session、时间间隔小于10分钟”的多运行合并逻辑离线聚类把eps从0.55调回0.6并且在聚类前先做一次“任务名归一化”把一些明显的同义表达比如“做个PPT”“生成演示文稿”“创建幻灯片”先用词典映射到同一个别名。调整后两周碎片任务占比从40%降到了18%左右虽然还有下降空间但已经不会影响分析结论。5.2 大模型输出不一致导致任务名抖动第二个高频问题是同名任务在不同时间的提取结果不断变化。用户今天提取出“整理会议纪要”明天再跑一遍同一段摘要模型却返回了“会议记录整理”。这不是模型坏了而是大模型生成的文本天然不稳定。解决这个问题我们用了三重手段。第一任务名不做精确匹配展示展示层统一接任务注册表的标准名模型输出的task_name只作为聚类输入不直接对外。第二在任务注册表里维护一个同义词表模型输出命中同义词表时直接映射到标准名。第三对同一run_id的重试结果做投票三次重试如果有两次返回同一个task_name就用它否则走人工复核队列。这套组合下来标签抖动基本被控制在可接受范围内。5.3 新任务类别识别滞后新任务永远会出现。用户群体大了以后一周里总会出现几个从未见过的任务形态。如果离线聚类只在每天凌晨跑一次新任务的“曝光”就会滞后12到24小时会影响那些很在意实时性的运营看板。我的做法是增加一个新任务候选队列的监控每个小时统计一次被标记为new_task_candidate的运行量如果某个未归并task_name在1小时内出现超过5次就触发一次即时小规模聚类把这个新候选归入最相近的已有任务簇或创建一个新簇。这个设计不是完美的它会让少量新任务在没有足够人工确认的情况下提前进入注册表但因为每次都会记录created_by: online_auto后续人工复核可以快速纠正利大于弊。5.4 提取成本失控的优化过程前文提到规则闸门拦掉了35%的流量但在上线初期剩下的65%流量依然让模型调用成本到了月账单上非常扎眼的程度。后来我又做了几轮优化才把成本压到原来的50%以下。第一轮优化是摘要降级把长轨迹只保留用户输入、首个工具调用、最后三个工具调用、最终输出。事实证明对大多数任务提取场景中间过程细节用处不大模型真正依赖的是“头尾”。第二轮优化是模型路由简单任务用便宜的小模型复杂任务才用更强的模型。判断复杂度的信号包括工具调用数量、是否有重试、模型事件数量。第三轮优化是做缓存复用同一task_id下的同类型运行只跑一次模型其余直接用缓存。三轮优化以后单条运行的提取成本降到了原来的约三分之一而语义匹配率只掉了0.8个百分点这个代价完全可以接受。6. 事后思考与可扩展的方向6.1 前期的“错路”与现在回头看这个项目做了大半年回头看有几个方向如果一开始就想清楚可以省掉不少返工。第一事件标准化应该先于任何提取算法设计我当时顺序反了先搭了一版提取器才发现输入字段不统一被迫全部重写。第二任务定义不要自己闭门造车应该和产品团队、运营团队一起开会定我们对“任务”的理解一开始太工程化导致提取结果虽然准确但产品侧不好用。第三评估标准最好从项目第一天就定下来不要等到上线前才找标注同学加班赶黄金数据集。最后再分享一个小技巧把在线通道的提取结果全部落一份影子表不要直接覆盖正式任务表。这个小表看起来多余实际上调试问题时价值极高因为你可以随时回放过去任何一天的提取输入和输出定位某类标签抖动到底是哪一环节导致的。我踩过的几个印象最深的大坑最后都是靠影子表里的历史输入复盘定位的。6.2 任务提取之后还能做什么Task Extraction本身不是终点它只是Agent数据基础设施的一块地基。任务标签稳定之后我这边已经着手在接两件事一是基于任务维度的成本归因与预算控制运营可以给不同任务族设置不同的模型调用策略二是基于任务完成率的Agent质量看板把“任务成功完成”作为产品指标而不是只看API调用成功率。再往下还可以做任务间的流转分析比如用户完成A任务后是否倾向于紧接着做B任务这会给Agent产品设计带来很直接的指引。任务提取也会反哺Agent本身的运行质量当某类任务的失败率连续多天偏高时系统可以自动触发一轮离线重跑和原因分析相当于给Agent运维团队装了一个预警雷达。这些都依赖同一个基础能力能稳定地从海量Agent Runs里抽取出干净、一致、可信的任务标签。
返回列表