Better AI Code Comment Detector

title
Better AI Code Comment Detector
type
summary
summary
kqr rebuilds a calibrated human-vs-LLM code-comment classifier on public data; 77% balanced accuracy, POS-tag n-grams as the strongest features
tags
llm, stylometry, ai-disclosure, machine-learning
created
2026-09-13
updated
2026-09-14

kqr at entropicthoughts rebuilt an earlier code-comment classifier, this time on public data so it could be shared. It runs in the browser at xkqr.org/aicomment, nothing pasted leaves the page, and clicking any span of the input shows which features fired there and how much each pushed the verdict. The post is mostly about how it was built, and it is candid about what went wrong.

The numbers

Cross-validated balanced accuracy is 77%, assuming human and generated comments are equally likely. On known-human input it says human 73% of the time; on known-generated input it says generated 80% of the time. A smaller hand-checked set of real-world comments did better, at 88% accuracy (89% specificity, 86% recall), which the author reads as real-world cases being easier to separate than the synthetic training data. That second set is small and was checked by the author, so it is encouraging rather than conclusive.

The more useful property is calibration. The printed percentage tracks the actual chance of being right across comment lengths, with a temperature parameter fitted against the square root of input length so longer inputs get more confident predictions. So the aggregate error rate of about 25% matters less than it sounds: a verdict at 80% confidence or above has roughly a 5% false-positive risk, and a verdict near 50% is a coin flip and says so. Everything is measured on code comments only. It will classify other text, without any promise about accuracy.

Building the data set

The design is to pick permissively licensed repositories, check out their last commit from 2021 so the human comments predate LLM coding tools, take random files, strip all comments, and have several LLMs write new comments for the same files. Keeping token counts balanced per file across humans and each model is what stops the classifier from learning which repository a comment came from instead of who wrote it.

Done right, the author estimates the data costs about $30 and that classifier power scales with the log of money spent. It was not done right the first time, and the data was collected three times. The mistakes are the useful part: different files per model (subject leakage), files with too few human comments (leakage again), docstrings left in so the models imitated the existing style, comment syntaxes missed in some languages, very short human comments like "Alias" left in so the classifier learned that humans write badly, and a single fixed generation prompt narrowing the variety of generated text.

Features

Testing every subset of 15 candidate features would mean training over 30,000 classifiers, five times each. Instead each feature was scored alone on several pairwise tasks (human vs. model, and model vs. model) and ranked, with a graph showing where those rankings disagreed. Part-of-speech tag n-grams came out strongest regardless of tagging engine, because they capture how a source phrases things without being distracted by word choice. Character n-grams were also strong. Raw word frequencies, normally risky because they pick up topic, did acceptably here because the balanced data set left little topic to pick up. The final feature set was chosen, in the author's words, rather arbitrarily after the combination search got tedious.

Seven classes reduced to two

The model is a logistic regression over L1-normalized features, trained seven ways: humans, GPT 5.6, Gemini 3.7, Claude 5, Kimi K2.7, Grok 4.6 and GLM 5.2. Kimi and GLM kept showing up as likely sources for everything, and plotting feature centroids showed their styles overlap all the other models. Rather than retrain, the classifier redistributes any probability assigned to those two back to the models it drew from during training. The author adds, in a footnote, that he does not know how distillation works but it sounds like it might be relevant. Probabilities are then renormalized to a 50/50 human-versus-model prior, because the question that usually matters is whether a person wrote it, not which model did.

Shipping it in a browser forced two compromises. The model was quantized and the vocabulary pruned with a document-frequency cutoff at 0.05%, bringing it to 355 kB. And tagging had to use wink-nlp, weaker than nltk or spaCy, which the training pipeline then also used so classification input would not be out of sample; that meant Python calling a Node.js process.

The code, and why development stops

The implementation is on sourcehut, and the author says plainly that it is LLM-generated and he could not be bothered to rewrite it. He also says he will likely stop working on it because sourcehut's new terms of service, taking effect the day after the post, forbid hosting LLM-generated code.

Against the vault's detection pages

ai-detector-arms-race argues that surface detection fails in both directions and that no detector can recover how a text was made. This classifier does not refute that so much as put numbers on a narrow version of it. It works on one genre, against named model versions that will be superseded, still gets about one known-human comment in four wrong, and is honest about uncertainty on each verdict, which most commercial detectors are not. The drift the arms-race page predicts is built in: a classifier trained on Claude 5 and GPT 5.6 describes those models, not their successors. slop-marker-convention and human-made-disclosure remain the only ways to learn authorship with certainty.

The closest relative is llm-cross-entropy-similarity, which maps model-to-model style similarity with character trigrams; here POS-tag n-grams beat character n-grams for telling sources apart. The Kimi-and-GLM overlap is the same kind of family resemblance qwen-gpt-reasoning-prefills probes with a very different method. claude-vocabulary-clusters goes without labels altogether: it clusters GitHub pull request descriptions by vocabulary, with no human or LLM examples to learn from.