统一采集方案助力AI编程工具数据分析与采纳率提升

阿里云开发者团队推出一套轻量、无感的 AI 编程工具代码采集方案,助力企业量化 AI 采纳率,优化开发流程。

原文标题:告别碎片化日志:一套方案采集所有主流 AI 编程工具

原文作者:阿里云开发者

冷月清谈:

本文介绍了一套基于 MCP 架构的多 AI 工具代码采集方案,旨在解决 AI 编程工具普及背景下,数据采集和分析的挑战。该方案具备以下特点:

1. 轻量化、用户无感:通过 Hook 机制、Telemetry机制和解析工具原生数据存储等方式,实现对 CLI 工具(如 claude-code、iflow、codex)和 IDE 工具(如 qoder)的无侵入式数据采集,不影响用户体验。
2. 多样化适配、标准化输出:针对不同类型的 AI 工具,采用最适合的采集策略,同时将异构数据标准化为统一格式,便于后续分析和处理。
3. 可靠性保障:通过本地缓存、重试机制、版本兼容性检查等手段,确保数据采集的可靠性。
4. 可扩展架构:采用插件化架构,易于扩展,支持新的 AI 工具,为生态的持续发展提供技术保障。

该方案的核心组件包括采集层、处理层和上报层,实现数据的采集、标准化和上报。目前,该方案已在阿里云 Aone 团队使用,用于 AI 采纳率的数据加工。未来,将继续扩大工具支持范围,增强数据分析能力,优化部署和运维体验,最终目的是通过数据报表统一加工,实现对 AI 代码采纳率的统一计算。高德在 Cursor 数据采集方面采取数据库文件解析方式,但在其他 AI 工具的采集上,高德选择了基于 MCP 在编辑文件前后记录 diff 数据的方案。AIDC 团队提供了基于 VSCode 插件的采集方案,这种方式相对比较重量级,需要用户主动安装和配置插件。

怜星夜思:

1、文章中提到的“AI 生码率(AGCR)”,你们觉得这个指标能够全面反映 AI 编程工具的价值吗?还有哪些其他指标可以用来评估 AI 编程工具的 ROI?
2、文章中提到了多种 AI 编程工具的数据采集方式,例如 Hook 机制、Telemetry 机制和解析工具原生数据存储等,你认为哪种方式更具有优势?为什么?
3、如果让你来设计一个 AI 编程工具代码采集方案,你会重点考虑哪些方面?

原文内容

在AI编程工具快速普及的今天,如何有效采集和分析AI代码生成数据成为了一个重要课题。我们设计并实现了一套基于MCP(Model Context Protocol)架构的多AI工具代码采集方案,支持claude-code、iflow、codex等CLI工具以及qoder IDE。这套方案具有轻量化、用户无感、可扩展等特点,目前已经和Aone团队合作,数据将自动采集到Aone的日志平台用作团队AI采纳率数据加工的基础。当前已支持claude-code、codex、gemini、ykcli、iflow/iflow-aone、qwen-code、qoder等工具,平台覆盖 macOS + Windows。

一、背景

行业现状与挑战

随着AI编程助手的快速普及,开发者的日常工作中涌现出了众多AI工具,在不同场景下发挥着重要作用。然而,AI工具的多样化也带来了新的挑战:

  • 缺乏统一的数据收口每个AI工具都有自己独特的数据格式和接口,缺乏统一的标准。开发团队难以全面了解AI工具在组织内的使用情况,无法建立统一的数据视图。

  • 平台支持不足大多数AI工具专注于功能实现,对数据采集和分析的支持有限。即使有数据输出,格式也往往不够标准化,难以进行跨工具的对比分析。

  • 度量数据缺失最关键的问题是,团队无法有效度量AI生成代码的采纳率、质量和影响。这些数据对于评估AI工具的ROI、优化开发流程、制定技术决策都至关重要。

基于这些痛点,我们团队决定建立一套统一的AI工具代码采集方案,为组织提供全面、准确的AI代码生成数据支撑。

二、业界方案分析与设计思路

在调研过程中,我们发现业界主要存在以下几类AI代码采集方案:

高德采集方案

高德团队在Cursor数据采集方面采用数据库文件解析方式。但在其他AI工具的采集上,高德选择了基于MCP在编辑文件前后记录diff数据的方案。这种实时拦截的方式虽然能够获取到相对完整的编辑历史,但存在明显的体验问题。由于需要在每次文件编辑时进行MCP调用和数据记录,会显著影响AI工具的响应速度,用户在使用过程中能明显感受到延迟。同时,这种方案依赖上下文保障数据一致性,在复杂的编辑场景下容易出现数据不准确的问题,特别是当编辑操作被中断或异常时,很难保证数据的完整性。

AIDC的VSCode插件方案

AIDC团队提供了基于VSCode插件的采集方案,这种方式相对比较重量级,需要用户主动安装和配置插件。目前该方案仅支持cursor,扩展性有限。插件形式虽然功能完整,但对用户的侵入性较强,需要用户感知并配置相关设置。

我们的设计思路

基于对业界方案的深入分析,我们确定了以下设计原则:

  • 轻量化优先采集机制必须对原有工具完全无侵入,不能影响用户的正常使用体验。

  • 多样化适配针对不同类型的AI工具(CLI工具、IDE工具、Web工具),采用最适合的采集策略。

  • 标准化输出虽然采集方式多样,但最终输出的数据格式必须标准化,便于后续分析和处理。

  • 可靠性保障通过本地缓存、重试机制、版本兼容性检查等手段,确保数据采集的可靠性。

三、技术方案

整体架构设计

我们的采集方案基于MCP架构设计,采用了分层解耦的思路。整个系统由三个核心组件构成:

  • 采集层(Collection Layer)负责从不同AI工具获取原始数据。对于CLI工具(claude-code、iflow、codex),我们通过Hook机制在工具执行时拦截相关事件;对于IDE工具(qoder),我们通过解析其历史快照文件获取代码变更信息。

  • 处理层(Processing Layer)将来自不同工具的异构数据标准化为统一格式。每种工具都有对应的Collector组件,负责解析原始数据并转换为标准的事件格式。这一层还包含了Git信息解析、文件路径规范化等通用处理逻辑。

  • 上报层(Reporting Layer)将标准化后的数据上报到SLS日志系统。这一层实现了数据的批量处理、重试机制和错误恢复,确保数据上报的可靠性。

统一的加工指标

AI 生码率(AI-Generated Code Ratio, AGCR):  AI有效提交行数/分支Diff总行数*100%

图片

CLI工具采集机制

Hook类型

对于claude-code和iflow这类CLI工具,我们采用了Hook机制进行数据采集。系统会自动在用户的配置文件中注入Hook脚本,当工具执行文件操作时,Hook脚本会捕获相关事件并将数据写入本地日志文件。

#!/usr/bin/env bash
set -euo pipefail

CLIENT_TYPE=“claude”
resolve_cache_dir() {
  if [[ -n “” ]]; then
    printf’%s’“$R2C_CACHE_DIR”
    return
  fi
  local home=“”
  local target=“.r2c”
  if [[ -n “$home” ]]; then
    target=“$home/.r2c”
  fi
  if mkdir -p “$target"2>/dev/null; then
    printf’%s’”$target"
    return
  fi
  local fallback=“./.r2c”
  mkdir -p “$fallback”
  printf’%s’“$fallback”
}
CACHE_DIR=“$(resolve_cache_dir)”
HISTORY_DIR=“$CACHE_DIR/logs/$CLIENT_TYPE/history”
mkdir -p “$HISTORY_DIR”
read_payload() {
  if [[ -t 0 ]]; then
    printf’’
    return
  fi
  cat || true
}
compact_json() {
  local payload
  payload=“$(cat)”
  if [[ -z “$payload” ]]; then
    return 1
  fi
  if command -v python3 >/dev/null 2>&1; then
    if printf ‘%s’“$payload” | python3 -c ‘import json,sys;\nprint(json.dumps(json.loads(sys.stdin.read()), ensure_ascii=False, separators=(“,”, “:”)))‘2>/dev/null; then
      return0
    fi
  fi
  if command -v node >/dev/null 2>&1; then
    ifprintf’%s’“$payload” | node -e “let data=‘’;process.stdin.on(‘data’,c=>data+=c).on(‘end’,()=>{try{const parsed=JSON.parse(data);process.stdout.write(JSON.stringify(parsed));}catch{process.exit(1);}});“2>/dev/null; then
      return0
    fi
  fi
  if command -v jq >/dev/null 2>&1; then
    ifprintf’%s’”$payload” | jq -c ‘.‘2>/dev/null; then
      return0
    fi
  fi
  printf’%s’“$payload” | tr -d ‘\n’ | tr -d ‘\r’
  return0
}
PAYLOAD_RAW=“$(read_payload)”
PAYLOAD_RAW=“${PAYLOAD_RAW//$‘\r’/}”
TRIMMED=“$(printf ‘%s’ “$PAYLOAD_RAW” | sed ‘s/[1]//;s/[[:space:]]$//’)”
if [[ -z “$TRIMMED” ]]; then
  exit0
fi
FIRST_CHAR=“${TRIMMED:0:1}”
LAST_CHAR=“${TRIMMED: -1}”
if [[ “$FIRST_CHAR” != “{” && “$FIRST_CHAR” != “[” ]]; then
  exit0
fi
if [[ “$LAST_CHAR” != “}” && “$LAST_CHAR” != “]” ]]; then
  exit0
fi
detect_hook_event(){
  case"$1" in
    ‘“hook_event_name”:“PostToolUse”’) echo “PostToolUse”; return ;;
    “"hook_event_name":"PreToolUse"”) echo “PreToolUse”; return ;;
    ‘“hookEvent”:“PostToolUse”’) echo “PostToolUse”; return ;;
    ‘“hookEvent”:“PreToolUse”’) echo “PreToolUse”; return ;;
  esac
  echo “unknown”
}
HOOK_EVENT=“$(detect_hook_event “$TRIMMED”)”
if ! DATA_COMPACT=“$(printf ‘%s’ “$TRIMMED” | compact_json 2>/dev/null)”; then
  exit0
fi
UTC_DAY=“$(date -u +”%Y-%m-%d")"
LOG_FILE=“$HISTORY_DIR/${CLIENT_TYPE}-${UTC_DAY}.jsonl”
LOG_TIME=“$(date -u +”%Y-%m-%dT%H:%M:%SZ")"
uuid_fallback() {
  local bytes
  bytes=“$(LC_ALL=C dd if=/dev/urandom bs=16 count=1 2>/dev/null | hexdump -v -e ‘/1 “%02x”’)”
  printf’%s-%s-%s-%s-%s\n’“${bytes:0:8}”“${bytes:8:4}”“${bytes:12:4}”“${bytes:16:4}”“${bytes:20:12}”
}
UUID=“”
if command -v uuidgen >/dev/null 2>&1; then
  UUID=“$(uuidgen | tr ‘A-Z’ ‘a-z’)”
else
  UUID=“$(uuid_fallback)”
fi
if [[ “$HOOK_EVENT” != “unknown” ]]; then
  RECORD=“{"uuid":"$UUID","logTime":"$LOG_TIME","reported":false,"clientType":"$CLIENT_TYPE","hookEvent":"$HOOK_EVENT","data":$DATA_COMPACT}”
else
  RECORD=“{"uuid":"$UUID","logTime":"$LOG_TIME","reported":false,"clientType":"$CLIENT_TYPE","data":$DATA_COMPACT}”
fi
exec 9>>“$LOG_FILE”
if command -v flock >/dev/null 2>&1; then
  flock 9
elif command -v lockf >/dev/null 2>&1; then
  lockf -k 9
fi
printf’%s\n’“$RECORD” >&9
exec 9>&-


Hook脚本的设计非常轻量化,主要功能是接收工具传递的JSON数据,进行基本的格式验证和清理,然后以JSONL格式写入按日期分割的日志文件。脚本使用bash实现,具有良好的兼容性,支持多种JSON处理工具的降级策略。

采集到的数据会暂存在本地的历史日志文件中,后台服务会定期扫描这些文件,解析其中的事件数据并进行标准化处理。这种设计的好处是完全不影响CLI工具的执行性能,用户在使用过程中完全无感知。

Telemetry类型

对于Gemini/YKCLI/Qwen这类工具,我们采用了Telemetry机制进行数据采集。同样也会在用户的配置文件中注入Telemetry配置,使其工具在生成代码时,能够将相关事件数据写入到采集服务日志文件中。虽然流程和落盘目录上有些区别,但本质上类似都是对过程数据的收集和存储。

{
  "telemetry": {
    "enabled": true,
    "target": "local",
    "outfile": "/Users/xxx/.r2c/logs/gemini/raw-telemetry.log",
    "otlpEndpoint": "",
    "logPrompts": false,
    "useCollector": false
  }
}


我们利用其内置的telemetry配置,可以让我们接收工具内部的所有遥测数据。但是由于其遥测数据量级比较大(每次操作均可输出MB级别的文件大小日志内容),我们对采集工具日志的过滤和控制体积大小方面做了一些优化,使其能够在捕获目标日志后解析组装成我们需要的Write工具调用日志格式,在随后上报后同步清理掉原始日志中废弃和无关的其他日志,保障日志落档文件的IO效率。

Qoder采集机制

Qoder的采集机制需要解析其本地历史快照与工作区存储数据。Qoder会将编辑历史保存在本地的History与workspaceStorage目录中,我们通过解析这些快照来重建代码变更事件。

我们实现了QoderCollector组件,能够读取Qoder的快照格式,提取文件路径、变更类型与diff等关键信息。系统同样维护SnapshotStore来记录已处理快照,避免重复上报。Qoder采集器也支持工作区过滤配置,可按指定项目采集,避免无关数据进入日志。

Codex OTLP采集机制

对于Codex工具,我们实现了OTLP(OpenTelemetry Protocol)HTTP接收器,能够接收Codex发送的遥测数据。Codex本身支持OTLP协议,我们只需要在其配置文件中指定我们的接收端点即可。

OTLP接收器会解析接收到的日志数据,提取出工具调用信息,特别是apply_patch事件。通过分析patch内容,我们能够准确识别出文件变更的类型、影响范围和具体内容。

通用采集机制

为支持非标准工具或轻量接入场景,我们在 Codex 遥测 HTTP 服务中扩展了通用“Code Report”入口(/v1/code)。该入口仅接收 JSON 请求、限制体积,并通过 clientType 白名单做准入控制,确保不会被随意注入无效数据。请求进入后会触发processCodeReportPayload完成日志数据统一标准化。

核心逻辑分三步:

1. 校验与归一化:检查 clientType / toolName / content,若 filePath 缺失则尝试从 diff 内容推断;再结合 cwd 归一化路径。

2. 工具分类与解析:将工具映射为 create_file / replace_in_file 两类。新增文件会把正文转换为统一 diff 结构;修改类则解析 unified diff,统计增删行数并构建 inline diff。

3. 统一上报:补齐 repo 信息(从 cwd 解析,缺失则给默认值),生成 sessionId/uuid,最终拼成标准 SLS 上报 payload,并落盘日志与统计处理结果。

最终只要能按约定把“文件路径 + diff/内容 + 工具类型”通过 HTTP 上报,就可以复用现有的标准化与上报链路,实现更多生码工具接入的采集诉求。

服务部署与管理

整个采集系统以后台服务的形式运行,我们提供了完整的服务管理脚本。在macOS平台上,系统会自动创建LaunchAgent服务,确保采集服务在用户登录后自动启动并持续运行。

服务安装脚本会自动检测系统环境,创建必要的日志目录,配置环境变量,并注册系统服务。服务支持自动重启、日志轮转等企业级特性,确保长期稳定运行。卸载过程同样简单,只需要运行卸载脚本即可完全清理服务和相关文件,不会在系统中留下任何残留。

自动更新机制

为了降低维护成本,我们引入独立的AutoUpdater守护进程,负责定期检查并自动升级采集服务版本。AutoUpdater与采集主服务分离运行,通过统一的Service Manager在macOS/Windows上安装为系统服务,避免影响用户日常工作。

自动更新在后台定时每小时检查仓库源版本号,若有新版本就停采集服务 → npx @ali/ai-coding-trace@latest 自动升级 → 启动校验,失败则回滚旧版;状态/锁写入日志文件。自动更新服务采用延迟 60 秒后台启动,除了不阻塞 CLI,还用于给采集服务的安装/启动与旧版 AutoUpdater 的停止/锁释放留出缓冲,避免在服务切换窗口内并发启动导致的锁冲突或状态不一致。等采集服务稳定后再接管更新,整体更稳定。

平台兼容性

当前采集服务已支持 macOS 与 Windows。两端差异主要体现在:

为此我们做了统一抽象与兼容处理,保证在采集路径、日志落盘和服务生命周期上行为一致、可控:

  • 通过统一的路径解析函数屏蔽差异,在Windows上将日志根目录放在ProgramData中以保证服务可写,在启动时从env中自动识别USERPROFILE/HOME/APPDATA等路径,避免后续路径偏移。

  • 服务安装层统一走platform-service-manager模块来适配不同系统平台。

  • Hook 注入时自动根据平台选择合适的脚本。

方案技术优势

在轻量化设计方面,我们的方案彻底摆脱了传统侵入式修改的弊端。CLI工具的Hook脚本仅有几十行代码,执行时间控制在毫秒级别,对原有工具的运行性能几乎没有任何影响。用户不需要维护任何定制版本,当工具升级时采集功能能够自动保持兼容。这种设计哲学让我们避免了重量级插件方案的复杂性,用户只需要一条简单的命令就能完成整个接入过程,极大地降低了使用门槛。

用户体验的无感化是我们方案的另一个突出特点。与高德团队采用的MCP拦截方案不同,我们选择了异步采集策略,所有的数据处理工作都在后台静默进行,不会在文件编辑时产生任何可感知的延迟。用户完全不需要改变现有的工作习惯,也不需要主动安装配置任何插件,整个采集过程对用户来说是完全透明的。这种设计让开发者能够专注于代码创作本身,而不会被采集机制所干扰。

在可靠性保障方面,我们构建了多层次的防护机制。本地文件缓存确保了数据的持久性,即使在网络异常的情况下也不会丢失重要信息。重试机制和版本兼容性检查进一步增强了系统的鲁棒性,能够有效应对各种异常情况。特别值得一提的是,我们通过解析工具的原生数据存储(比如Qoder的SQLite数据库)来获取信息,这种方式比MCP拦截方案更能保证数据的完整性和准确性。同时,各个工具的采集模块相互独立,当某个工具出现问题时不会影响其他工具的正常工作,系统具备了良好的故障隔离能力。

扩展性设计是我们方案的长远考虑。整个系统采用了插件化架构,当需要支持新的AI工具时,开发者只需要实现对应的Collector组件,而无需对核心框架进行任何修改。我们将来自不同工具的异构数据统一转换为标准格式,这不仅便于后续的分析处理,也为未来的功能扩展打下了坚实基础。系统同时支持Hook机制、数据库解析、OTLP协议等多种采集方式,能够灵活适配各种类型的AI工具,为生态的持续发展提供了技术保障。

采集效果预览

四、总结与展望

经过近期的线上运行,我们的采集系统已经稳定采集了大量的AI代码生成数据。这些数据为我们分析AI工具的使用模式、评估代码生成质量、优化开发流程提供了重要支撑,最终会在数据报表上统一加工,实现对AI代码采纳率的统一计算。

我们未来计划在以下几个方向继续优化:一是扩大工具支持范围,覆盖更多的AI编程工具;二是增强数据分析能力,提供更加丰富的统计和洞察功能;三是优化部署和运维体验,让更多团队能够便捷地使用我们的采集方案。


  1. [:space:] ↩︎

我更看好那些能够根据团队特定需求进行定制化的 AI 编程工具。因为每个团队都有自己的代码风格、技术栈和业务逻辑,通用的 AI 工具很难满足所有团队的需求。如果 AI 工具能够根据团队的实际情况进行定制化,那就能更好地融入团队的开发流程,提高开发效率。

所谓的“无感”,其实是一种“假装无感”。数据采集肯定会消耗一定的系统资源,即使消耗很小,也会对用户体验产生影响。只不过这种影响可能很小,用户感觉不到而已。

所以,我觉得应该在“无感”和“知情权”之间找到一个平衡点。可以给用户提供一个开关,让用户可以选择是否开启数据采集功能。这样既能满足一部分用户对隐私的保护需求,又能让另一部分用户享受到数据采集带来的好处。

AGCR指标只是一个初步的量化尝试,肯定存在局限性。我觉得评估AI代码采纳率,更应该关注最终的业务价值。举个例子,如果AI生成的代码能显著减少bug数量,那么即使AGCR不高,也应该认为AI的价值很高。

从这个角度出发,可以考虑以下指标:

1. 缺陷率降低:比较使用AI前后,代码的缺陷率变化。
2. 开发效率提升:比较使用AI前后,完成相同任务所需的时间。
3. 用户满意度:通过用户反馈,评估AI生成代码的质量和实用性。

说白了,就是要把AI代码的采纳率和实际的业务收益联系起来。

在可靠性方面,解析原生数据存储应该是最靠谱的,只要工具不升级,采集代码就不需要修改,毕竟数据就在那里,跑不掉。hook和telemetry都有一定的风险,比如hook可能会失效,telemetry可能会被关闭。当然,具体选哪种方式,还是要看实际情况,综合考虑各方面的因素。

我会重点考虑可扩展性,保证方案能够快速支持新的 AI 编程工具。 此外,数据清洗和标准化也很重要,只有高质量的数据才能为后续的分析提供价值。 还有一点,就是可视化,将采集到的数据以图表的形式展示出来,让开发者能够直观地了解 AI 工具的使用情况。

我觉得 Hook 机制最灵活,可以自定义采集的内容,但是实现起来也比较复杂,需要对目标工具的内部实现有深入的了解。Telemetry 机制最省事,直接利用工具自带的功能,但是数据格式可能不太统一,需要做额外的处理。解析工具原生数据存储的方式最可靠,但是对存储格式的依赖性太强,一旦存储格式发生变化,采集程序就需要跟着改。

AGCR 肯定是不够的,毕竟 AI 生成的代码最终还是要人来 review 和修改。我觉得可以引入一个 ‘AI 辅助开发效率提升率’,通过对比使用 AI 工具前后,完成相同任务所需的时间来评估。当然,这个评估需要排除其他变量的干扰,比如开发者的经验水平等。

我更倾向于 Telemetry 机制,毕竟是工具自带的,兼容性应该更好。而且,现在很多开源项目都支持 Telemetry,可以更容易地集成到现有的监控系统中。Hook 机制虽然强大,但是也容易引入 bug,维护成本比较高。

AGCR 有点像 KPI 导向,容易让大家为了提高这个指标而忽略了代码质量。除了楼上提到的效率提升率,还可以考虑开发者对 AI 工具的满意度调查,毕竟好用才是王道,用户体验也很重要。

首先,我会考虑数据安全,确保采集到的代码不会泄露敏感信息。其次,我会注重用户隐私,尽量只采集必要的数据,并且对数据进行匿名化处理。最后,我会考虑性能,确保采集过程不会影响开发者的工作效率。

我会从经济角度考量,采集哪些数据能够最大化 ROI,哪些数据是冗余的。另外,采集方案本身也要尽可能简单,降低开发和维护成本。最好的方案不是功能最强大的,而是最适合当前需求的。

我认为 AGCR 只是一个维度,它反映了 AI 在代码生成上的贡献。但是,代码的质量、可维护性、安全性等方面同样重要。可以考虑引入缺陷率、代码审查时长、以及开发者对 AI 生成代码的修改比例等指标。