- 人工智能
- 搜索引擎
- 本地部署
- 后端
- MCP 服务
【免费下载链接】xerj
XERJ is the new way for AI to search data. Its autoindex capability activates agents to know your data without the token waste of grep and sed. One command indexes code, docs, logs and PDFs for search, RAG, security audits and agent memory, using 40x fewer tokens than grep. Elasticsearch compatible, so existing clients just work.
本文以仓库中 user-feedback/kibana/categories/11-ml-and-anomaly/README.md 这份「ML 与异常检测」用户反馈分类报告为骨架展开:它记录了 54 条来自 Reddit、GitHub、Hacker News、Stack Overflow 等公开来源的真实 Kibana/Elasticsearch ML 使用反馈,是 XERJ 决定"该建什么、不该建什么"的输入之一。全文结合 XERJ 开源仓库中
_ml异常检测的完整实现(detector、datafeed、评分算法、权限边界)与两个可运行 recipe,说明这套反馈如何落成一套免费、透明、可离线自托管的统计异常检测 API,并给出可直接照做的实战步骤。
这份文档:Kibana 用户反馈语料库中的「ML 与异常检测」分类
仓库的user-feedback/kibana/目录是一个结构化、可复现的 Kibana 用户反馈语料库:从五个公开主源(GitHub Issues、Stack Overflow、Hacker News、Reddit、Elastic 官方 Discourse 论坛)抓取真实反馈,再用确定性关键词分类器归入 17 个产品相关分类。user-feedback/kibana/README.md 明确写道,这套语料是"the input we use to decide what XERJ.ai should and shouldn't build"——即决定 XERJ 功能去留的输入,而不是一篇泛泛的产品介绍。
截至 2026-04-14,语料总量为15,282 条,情绪分布为 negative 5,801(38.0%)/ neutral 7,760(50.8%)/ positive 1,721(11.3%),完整统计见 user-feedback/kibana/INDEX.md。其中11-ml-and-anomaly分类共54 条,负向 18 条、中性 27 条、正向 9 条,来源分布为github=23、hackernews=16、reddit=11(其余为 Stack Overflow / Discourse 的次要归属)。
该分类的官方定义见 user-feedback/kibana/TAXONOMY.md:
11-ml-and-anomaly · UX— ML features — anomaly detection, data frame analytics, paywall complaints, accuracy problems, job setup. Keywords:
anomaly detection、machine learning、ml job、data frame、transform、paywall.
也就是说,这个分类收集的是用户在 ML 功能上感受到的具体摩擦:异常检测的配置与准确率、Data Frame Analytics、以及把功能锁在付费墙(paywall / platinum license)后面引发的抱怨。分类器在 user-feedback/kibana/pipeline/classify.mjs 中的对应规则是:
| 关键词 | 权重 |
|---|---|
anomaly detection | 4 |
ml job | 4 |
kibana ml | 4 |
data frame | 3 |
transform job | 3 |
platinum license | 3 |
machine learning | 2 |
paywall | 2 |
ml(软匹配) | 0.5 |
outlier/regression model | 1 / 2 |
本分类的条目指针文件是 user-feedback/kibana/categories/11-ml-and-anomaly/pointers.jsonl,每条只存id / source / score / posted_at / tags / title的投影;完整正文需按id回查user-feedback/kibana/sources/<src>/enriched.jsonl(分类器每次重跑都会从raw.jsonl重建指针,不会漂移,见 user-feedback/kibana/SCHEMA.md)。
本分类的 Top tags 汇总如下(即原文档中的表格):
| Tag | Count |
|---|---|
ml | 32 |
anomaly | 20 |
license | 3 |
discover | 2 |
saved-objects | 2 |
apm | 1 |
canvas | 1 |
time-picker | 1 |
ml+anomaly两个标签合计覆盖了本分类绝大多数条目,而license的出现印证了付费墙是 ML 功能被诟病的重要主题。
Top 20 榜单:用户在 ML/异常检测上到底在关心什么
原文档的核心内容是按 score 排序的 Top 20 条目(score 即原平台的点赞/热度)。这里完整保留 20 条,标题取自指针文件中的完整标题(原始 URL 一律不列出,正文回查指针与 enriched 文件即可):
| # | Source | Score | Title |
|---|---|---|---|
| 1 | 139 | Open-Source-SaaS | Curated list to get started building quickly | |
| 2 | 20 | How to get started in Data Engineering ? | |
| 3 | 12 | I deal with a few hundred million time-series data points but don't need streaming - should I still use Kafka? | |
| 4 | github | 9 | [BUG] Index Pattern Filtering when searching in Dashboards |
| 5 | 9 | Article: A digestible introduction to anomaly detection using Elasticsearch and Kibana without diving too deep into data science | |
| 6 | github | 8 | Best practices for rule executions |
| 7 | github | 7 | [Dashboard] Redesign Add Panel Experience |
| 8 | 7 | An Audit How To: Using Logstash with Active Directory/Windows Server | |
| 9 | 5 | Have you bought platinum license of elastic/kibana or other enterprise license just because of SSO? | |
| 10 | 5 | Would love to pick your brain for a sec | |
| 11 | github | 4 | [Meta] Saved Object Tagging |
| 12 | hacker | 4 | Datastream.io – Real-Time Anomaly Detection Using Python, Elasticsearch and Kibana |
| 13 | github | 3 | EuiInMemoryTable - request ability to set pageIndex and pageSize externally |
| 14 | 3 | Article: A digestible introduction to anomaly detection using Elasticsearch and Kibana without diving too deep into data science | |
| 15 | stacko | 2 | How random cut forest deals with periodicity / seasonality? |
| 16 | github | 1 | Implement new navigation hierarchy for stateful cloud and serverless Kibana |
| 17 | github | 1 | [EuiSuperDatePicker] Refresh controls not hidden ifdisableAutoRefreshSelectorset to true |
| 18 | 1 | Kibana Categorization Anomaly Detection | |
| 19 | 1 | Kibana as a source data stream. how do you guys manage the ingestion? | |
| 20 | stacko | 1 | Kibana graph for anomaly detection |
把 20 条归拢成几类信号,就能看出用户在 ML 与异常检测上真实痛点的全貌:
- 入门与文档(#2、#5、#12、#14、#18、#20):异常检测的科普文章与开源实现(Datastream.io、Kibana Categorization Anomaly Detection、Kibana graph for anomaly detection)热度最高,说明"异常检测怎么做"本身是高频诉求——用户要的是一个能讲得清原理的方案,而不是黑盒。
- 付费墙 / License(#9):有人在问"你会不会只为了 SSO 就买 platinum 许可证",
license标签在本分类出现 3 次。这是把 ML 能力锁在订阅后面的直接反噬信号。 - Job 配置与准确性(#3、#15、#6):从"几亿条时间序列要不要上 Kafka",到 Random Cut Forest 如何处理周期/季节性,到规则执行的最佳实践——用户在为模型选择和参数配置发愁,想要可控、可解释的检测。
- 周边 UI 与集成(#4、#7、#11、#13、#16、#17、#19):Saved Object Tagging、Dashboard 面板体验、导航层级、EuiInMemoryTable/EuiSuperDatePicker 这些其实都是 ML 应用落地时的"工程摩擦"。
负向条目的原文引文被汇总在 user-feedback/kibana/themes/top-pains.md 的11-ml-and-anomaly · 18 negative items一节(真实引语、逐字保留),是阅读原始抱怨最快的入口;user-feedback/kibana/themes/design-inputs.md 则把 Top 50 可行动痛点映射到 XERJ 各模块的回应。核心结论是:用户要的是免费、透明、能自己掌控参数的异常检测,而不是藏在付费墙后的黑盒模型。
XERJ 的回应:_ml异常检测的设计取向
对照上述反馈,XERJ 在开源仓库里落地了一套完整的_ml异常检测 API。它的实现注释(engine/crates/xerj-api/src/es_compat.rs)第一段就点明了设计取向:
A detector buckets a time-series source index over its time field, reduces each bucket to one number (count/mean/min/max/sum), builds a moving mean+stddev baseline over theprior normalbuckets, and flags buckets whose value deviates more than
anomaly_thresholdstddevs. The whole thing is deterministic — no RNG, no opaque model — and the scoring is a plain z-score normalised to 0..100.
对应到用户反馈里的每一条抱怨:
- "黑盒模型" → 透明统计:算法就是移动均值 + 标准差的 z-score,完全确定、无随机数、无不可解释的模型,正好回应 #15(Random Cut Forest 周期性问题)这类"想知道原理"的诉求。
- "付费墙" → 免费内置:
_ml是二进制内置的普通 REST 端点,recipe 直接对默认启动的 XERJ 调用即可,不需要任何 license 配置(recipes/README.md 明确说明全部 recipe 零依赖、零 API key、可完全离线运行)。 - "Job 配置繁琐" → 一个 JSON 搞定:detector 只是一个可持久化的 JSON 配置对象。
Detector 的持久化结构定义在 engine/crates/xerj-api/src/state.rs(MlDetector),字段与默认值如下:
| 字段 | 含义 | 默认值 / 取值 |
|---|---|---|
detector_id | 检测器 ID(路径段) | 必填,全局唯一 |
source_index | 要分析的索引或索引模式 | 必填 |
time_field | 分桶时钟字段 | 默认@timestamp |
function | 每桶规约函数 | count(默认)/mean/min/max/sum |
field | 函数作用的字段 | count时可省略;其余必填 |
bucket_span | 桶跨度(ES interval 语法) | 默认1h,如"5m"、"1d" |
anomaly_threshold | 偏离几个标准差算异常 | 默认3.0 |
create_time_ms | 创建时间(epoch 毫秒) | 服务端生成 |
description | 可选描述 | 可选 |
配置持久化在<data_dir>/_ml/detectors.json,datafeed 持久化在<data_dir>/_ml/datafeeds.json,重启后仍可加载(加载失败会记 ERROR 日志但拒绝启动反而是更糟的选择,见 state.rs 的设计说明)。
实战一:一次性打分(detector +_score)
先看"检测一次"的完整闭环,对应 recipe recipes/log_anomaly.py:它把仓库自带的真实 OpenSSH 登录日志(engine/demo-data/ssh_one.ndjson,65.5 万条真实生产事件)灌进索引,然后用_ml找出失败认证数量飙升的小时——即日志里真实存在的暴力破解。
启动与运行:
xerj --insecure --data-dir ./data & # 启动 XERJ python3 recipes/log_anomaly.pyrecipe 的流程分两步。
第一步:建索引并批量灌数据。只保留三类认证结果事件(failed_password/invalid_user/auth_failure),mapping 里有@timestamp(date)与event、src_ip、user(keyword),每 5000 条一批走/_bulk:
{ "mappings": {"properties": { "@timestamp": {"type": "date"}, "event": {"type": "keyword"}, "src_ip": {"type": "keyword"}, "user": {"type": "keyword"} }} }第二步:创建 detector 并打分。这里的关键是function: "count"——不需要field,每个小时桶的"值"就是失败认证的事件数;bucket_span: "1h"把时间轴切成小时桶;anomaly_threshold: 3.0表示偏离移动基线 3 个标准差才报警:
# PUT /_ml/anomaly_detectors/ssh-bruteforce { "source_index": "ssh-auth", "time_field": "@timestamp", "function": "count", "bucket_span": "1h", "anomaly_threshold": 3.0, "description": "failed ssh auth volume vs moving baseline" } # POST /_ml/anomaly_detectors/ssh-bruteforce/_score_score响应返回records(逐桶记录)与anomalies(按anomaly_score降序的异常子集),每个记录形如:
{ "timestamp": 1751760000000, "timestamp_iso": "2026-07-06T00:00:00.000Z", "actual": 482.0, "expected": 51.2, "std_dev": 12.7, "z_score": 33.9, "anomaly_score": 92.5, "is_anomaly": true }recipe 随后把is_anomaly的记录按分数排序打印,并用一条普通的 ES terms 聚合归因最坏小时里排名前三的攻击者 IP——异常检测和常规聚合无缝混用:
{ "size": 0, "query": {"range": {"@timestamp": {"gte": 1751760000000, "lt": 1751763600000}}}, "aggs": {"attackers": {"terms": {"field": "src_ip", "size": 3}}} }_score请求体还支持两个可选覆盖项(源码见 es_compat.rs):{"query": {…}}限制参与评分的文档范围,{"anomaly_threshold": N}临时覆盖本次打分阈值——不改 detector 配置即可做"更敏感/更不敏感"的敏感性分析。
实战二:连续检测(datafeed)
一次性打分只能回答"过去的数据里有没有异常";要回答"现在的数据有没有异常",就需要 datafeed——它把同一个打分器挂到定时器上,每frequency秒重跑一次,只把新增的异常桶追加进任务的 results store,供你轮询。对应 recipe recipes/anomaly_datafeed.py。
先造一个带两个突刺的 CPU 序列(每分钟一条,基线约 20%,第 12 分钟突刺到 96),建索引并灌入:
{ "mappings": {"properties": { "@timestamp": {"type": "date"}, "cpu": {"type": "double"} }} }创建 detector(这里用function: "mean"+field: "cpu",桶跨度为 1 分钟)和 datafeed(每 2 秒重打分一次):
# PUT /_ml/anomaly_detectors/cpu-spike { "source_index": "cpu_datafeed", "time_field": "@timestamp", "function": "mean", "field": "cpu", "bucket_span": "1m", "anomaly_threshold": 3.0 } # PUT /_ml/datafeeds/cpu-spike-feed { "job_id": "cpu-spike", "frequency": "2s" }然后POST /_ml/datafeeds/cpu-spike-feed/_start:启动瞬间会同步跑一次打分(立即可读到第一条异常记录),随后进入后台定时模式。关键验证步骤是——在第 20 分钟向活跃索引注入第二个突刺(88),然后完全不碰 datafeed,只轮询结果:
# GET /_ml/anomaly_detectors/cpu-spike/results/records后台打分器会在下一个 2 秒 tick 自己发现它,追加一条新记录(actual: 88.0),无需第二次_start。最后POST /_ml/datafeeds/cpu-spike-feed/_stop干净收尾,recipe 用 4 条断言验证:第一个突刺在、连续发现的第二个突刺在、记录数 ≥ 2、datafeed 状态回到stopped。
datafeed 的配置参数(源码 es_compat.rs):
| 字段 | 含义 | 默认值 |
|---|---|---|
job_id | 引用的 detector ID | 必填,必须已存在 |
indices | 读取的索引(字符串或数组) | 默认取 detector 的source_index |
query | 限定评分的查询 | 默认match_all |
frequency | 重打分周期 | 默认60s,最小1s(ES 时间语法如"2s") |
后台机制的实现细节同样值得注意(es_compat.rs):
- 共享打分核心:datafeed 与
_score共用同一个score_detector_records,两条路径逐桶一致,永远不会漂移; fresh=true绕过查询缓存:XERJ 查询缓存以(body_hash, dataset_version)为键,未落盘的写入不会 bump 版本号,datafeed 每 tick 用相同 body 重查活跃索引,因此强制fresh读取,避免吃到陈旧结果;- high-water cursor 去重:每轮只追加
timestamp > since_ms的异常桶,游标推进到已打分的最新桶,天然幂等; - 记录上限:每个 job 最多保留 10,000 条异常记录(
ML_MAX_RECORDS_PER_JOB),超限后最旧的先被逐出; - 异常不污染基线:被打成异常的桶不会折叠进基线,防止一次突刺"毒化"后续桶的期望值。
评分算法的源码细节:移动均值 z-score 是怎么算的
打分核心在 es_compat.rs 的score_detector_records,整条链路是:
- 分桶:对
source_index跑date_histogram(fixed_interval = bucket_span、min_doc_count: 0),需要field时再挂一个 metric 子聚合(value_count/avg/min/max/sum,映射见ml_metric_agg); - 规约:每个桶变成一个数——
count用doc_count,其余用子聚合value;空桶直接跳过; - 预热:前
MIN_BASELINE = 4个桶不评分,只作为基线种子,记录携带"note": "insufficient_baseline"(anomaly_score: 0、is_anomaly: false); - 打分:用当前"正常桶集合"算总体均值与标准差(
ml_mean_std,ddof=0),z = (actual - mean) / std;当 std 塌缩到 0 时用符号 ×1e6 兜底并截断展示; - 归一化:
anomaly_score = 100 * (1 - exp(-ln2 · |z| / threshold)),截断到 0..100——跨过阈值时映射到约 50 分,偏差越大单调逼近 100,完全确定; - 阈值判定:
is_anomaly = |z| > anomaly_threshold,异常桶不回填基线。
整条链路复用引擎自身的date_histogram与 metric 聚合,因此"异常检测"本质上是对普通查询与聚合能力的组合——这解释了为什么 recipe 能随手在异常桶上再叠一个terms聚合做攻击者归因。
权限边界:datafeed 不是绕过 per-index 边界的后门
连续打分器的后台任务是一个tokio::spawn出的独立任务,这曾经是一个真实的安全漏洞,仓库里有完整的攻防回归测试 engine/crates/xerj-api/tests/ml_datafeed_cannot_read_a_brain.rs。
问题(issue #79,per-index 边界)在于:引擎侧可见性规则是tokio任务局部变量,_start的同步打分跑在请求任务上、受规则约束,但tokio::spawn出的后台 tick 却丢了规则——攻击者用一把无任何role_descriptors的 key 启动 datafeed,同步阶段被拒(返回空结果),几秒后的后台 tick 却无守卫地读到了他人命名空间(.xerj-memory-bob-edges)里的字段值并写进了 results store。
修复是双重防线,测试分别验证了每一半:
- 配置时拒绝:ML job 或 datafeed 命名了创建者无权读取的索引,
PUT直接返回403 Forbidden,而不是 200 收下; - 运行时兜底:即使配置由超级用户提前种好(配置时无可拒绝),
_start时会把发起请求者的可见性规则捕获并重新安装到后台任务上(spawn_datafeed_task在 spawn 之前读取index_guard::current(),再scoped_opt包住整个循环),打分器只能看到启动者本应看到的索引;越权时Engine::get_index回答 "not found",datafeed_score_pass按"无新记录"处理,不会报错也不会泄露。
同文件还有两个对照组:超级用户启动的 datafeed 后台 tick 后依旧正常打分(None规则 = 不限制);有作用域 key 对自己持有的索引配置 ML 依然被放行,但_score/_start这类不指名索引的动词仍按既有集群规则失败关闭。对使用者的启示:_ml的所有端点都遵守与普通读写相同的授权模型,datafeed 的"异步"不构成越权的捷径。
语料库的诚实边界与再生成
这份反馈语料本身也声明了诚实的限制(user-feedback/kibana/README.md):
- 情绪标注是朴素词典分类器,会混淆反讽与"这是个 bug"之类的技术性表述——绝对计数要谨慎,跨分类的相对排名才可靠;
99-noise桶只能过滤掉部分 CI 失败类 issue(约 146 条),并不穷尽;- 部分分类(如 maps)因语言太泛而代表性不足;
- 分类器是关键词规则而非 LLM,换用 XERJ 自己的 embed proxy 只需十分钟;
- 只采集英文语料,只使用公开、可署名来源的数据。
整个语料可以零依赖地再生成(Node 22 ESM,仅内置fetch):
cd user-feedback/kibana GH_TOKEN=$GH_TOKEN node pipeline/collect.mjs # 从 5 个 API 拉取,写入 sources/<src>/raw.jsonl node pipeline/classify.mjs # 确定性、幂等地重写 enriched.jsonl + 指针文件 node pipeline/stats.mjs # 重写 INDEX.md + 各分类 README.md node pipeline/themes.mjs # 重写 themes/*.md(top-pains / top-asks 等) node pipeline/design-inputs.mjs # 重写 design-inputs.md + 前端引用 JS 模块JSONL 文件按id追加并去重,重跑只抓增量,GitHub 源按仓库分桶落盘,中断也不会丢已完成部分。
结语:一份 54 条的反馈如何长成一个_ml功能
从 user-feedback/kibana/categories/11-ml-and-anomaly/README.md 的 54 条真实反馈出发,XERJ 给出的答案是一条清晰的因果链:用户抱怨黑盒、抱怨付费墙、抱怨 job 配置难 → 于是有了完全确定、免费内置、一个 JSON 即可配置的 detector;用户抱怨"检测一次不够" → 于是有了基于共享打分核心、带权限边界的连续 datafeed;甚至数据feed 的后台任务都因为一次真实越权漏洞而补上了配置时 + 运行时双重防线,并留下了端到端的攻防测试。想亲手验证整条链路,启动二进制后直接跑python3 recipes/log_anomaly.py与python3 recipes/anomaly_datafeed.py即可——这正是这份反馈文档"从用户痛点到可运行代码"的完整落地。
- 人工智能
- 搜索引擎
- 本地部署
- 后端
- MCP 服务
【免费下载链接】xerj
XERJ is the new way for AI to search data. Its autoindex capability activates agents to know your data without the token waste of grep and sed. One command indexes code, docs, logs and PDFs for search, RAG, security audits and agent memory, using 40x fewer tokens than grep. Elasticsearch compatible, so existing clients just work.
相关推荐
用 XERJ 检测指标序列中的尖峰异常:_ml 异常检测配方与源码级解读
用 XERJ 检测指标序列中的尖峰异常:_ml 异常检测配方与源码级解读 XERJ 内置了一个按需(on demand)的异常检测器,接口采用 Elastics
人工智能搜索引擎本地部署后端MCP 服务XERJ 连续异常检测实战:用 _ml/datafeeds 让指标监控自动跑起来
XERJ 连续异常检测实战:用 _ml/datafeeds 让指标监控自动跑起来 本篇指南讲解 XERJ 的 _ml/datafeeds 连续异常检测能力:在已
人工智能搜索引擎本地部署后端MCP 服务Android异常检测终极指南:系统异常与用户行为异常识别
Android异常检测终极指南:系统异常与用户行为异常识别 Android开发过程中,异常检测是确保应用稳定性的关键环节。无论是系统层面的崩溃还是用户行为引发的
文档教程移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考