Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@

엔진이 이해하는 언어(심볼, 호출 관계, 지도, 영향 반경): Rust, Python, TypeScript/JavaScript, Java, Go, C#. 그 밖의 파일도 코드 색은 나오지만 심볼은 없습니다.

비공개 프로젝트(코드 파일 1,772개, TypeScript/JavaScript)에서 정답 파일을 직접 정한 질문 20개로, 같은 8,000 토큰 예산에서 비교했습니다:
**공개 벤치마크.** [Flask](https://github.com/pallets/flask)(Python), [Hono](https://github.com/honojs/hono)(TypeScript), [Apache Commons Lang](https://github.com/apache/commons-lang)(Java)의 실제 커밋 60개로 쟀습니다. 질문은 커밋 메시지, 정답 파일은 그 커밋이 고친 코드 파일이고, 각 질문은 그 커밋의 부모 시점에서 잽니다. 정답을 사람이 고르지 않습니다. 같은 8,000 토큰 예산, 모델 호출 없음:

| | 키워드 검색 (grep 후 파일 통째로 읽기) | Lantern |
|---|---|---|
| 정답 파일 적중률 (평균) | 24% | **67%** |
| 정답 파일을 하나라도 가져온 질문 | 35% | **90%** |
| | 키워드 검색 (grep 후 파일 통째로) | 키워드 검색 (grep, 맞은 곳 앞뒤만) | Lantern |
|---|---|---|---|
| 정답 파일 적중률 (평균) | 13% | 41% | **88%** |
| 처음 3개 파일 안에 정답 | 15% | 30% | **65%** |
| 같은 질문을 한국어로: 적중률 / 처음 3개 | 13% / 13% | 38% / 27% | **76% / 43%** |

방법과 한계: [eval/결과.md](eval/결과.md). 평가 스크립트는 `eval/`에 있으니 자기 프로젝트와 질문으로 돌려 볼 수 있습니다 ([형식](eval/README.md)). 오픈소스 코드베이스로 만든 공개 벤치마크를 준비하고 있습니다.
Lantern이 가져온 것과 같은 수의 파일을 무작위로 고르면 정답을 가져올 확률은 10%입니다. 한국어 질문은 같은 커밋 메시지를 사람이 옮긴 것입니다. `node eval/bench/run.mjs`로 재현할 수 있습니다 ([결과](eval/bench/결과.md), [방법](eval/README.md)). 자기 프로젝트와 질문으로는 `eval/retrieval.mjs`를 쓰면 됩니다.

맥락 엔진은 CLI와 MCP 서버로 따로 쓸 수도 있습니다:

Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ The core is a local context engine (`crates/lantern-context`, Rust + tree-sitter

Languages the engine understands (symbols, calls, map, impact radius): Rust, Python, TypeScript/JavaScript, Java, Go, C#. Other files still open with syntax colors, but have no symbols.

On a private 1,772-file TypeScript/JavaScript project with 20 hand-labelled questions, at the same 8,000-token budget:
**Public benchmark.** 60 real commits from [Flask](https://github.com/pallets/flask) (Python), [Hono](https://github.com/honojs/hono) (TypeScript), and [Apache Commons Lang](https://github.com/apache/commons-lang) (Java). The question is the commit message, the answer files are the code files that commit changed, and each question is measured at the commit's parent. Nobody hand-picks the answers. Same 8,000-token budget, no model calls:

| | Keyword search (grep, then read whole files) | Lantern |
|---|---|---|
| Answer files retrieved (mean recall) | 24% | **67%** |
| Questions with at least one answer file | 35% | **90%** |
| | Keyword search (grep, read whole files) | Keyword search (grep, snippets around matches) | Lantern |
|---|---|---|---|
| Answer files retrieved (mean recall) | 13% | 41% | **88%** |
| An answer file among the first 3 files | 15% | 30% | **65%** |
| Same questions in Korean: recall / first 3 | 13% / 13% | 38% / 27% | **76% / 43%** |

Method and caveats: [eval/결과.md](eval/결과.md) (Korean). The evaluation script is in `eval/`; bring your own project and question set ([format](eval/README.md)). A public benchmark on an open-source codebase is planned.
Picking as many files at random as Lantern returns would hit an answer file 10% of the time. Korean questions are human translations of the same commit messages. Reproduce with `node eval/bench/run.mjs` ([results](eval/bench/결과.md), [method](eval/README.md)). Bring your own project and questions with `eval/retrieval.mjs`.

The engine also runs on its own as a CLI and an MCP server, so you can use it from other agents:

Expand Down
15 changes: 15 additions & 0 deletions eval/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

맥락 엔진이 "답하려면 꼭 봐야 하는 파일"을 얼마나 가져오는지 잽니다. 결과 해석은 [결과.md](결과.md).

## 공개 벤치마크 (`eval/bench`, 누구나 재현)

```bash
cargo build --release -p lantern-context
node eval/bench/run.mjs --dir <저장소를 받을 폴더>
```

오픈소스 저장소 3개(flask, hono, commons-lang)를 받아 고정한 커밋으로 맞추고, 영어·한국어 질문 60개씩으로 잰 뒤 [bench/결과.md](bench/결과.md)를 새로 씁니다.

- **질문과 정답은 실제 커밋에서** (`bench/make.mjs`): 질문 = 커밋 메시지 제목, 정답 파일 = 그 커밋이 고친 코드 파일(테스트 제외), 평가 시점 = 그 커밋의 부모. 사람이 정답을 고르지 않아 엔진을 아는 사람의 편향이 들어가지 않습니다
- **거르는 커밋**: 병합, 버전·문서·빌드·린트 손질, 새로 만든·지운·옮긴 파일이 있는 커밋, 코드 파일 4개 이상, 메시지에 정답 파일 이름이 나오는 커밋, 같은 정답 파일 묶음은 2개까지
- **한국어 질문**(`q_ko`): 같은 커밋 메시지를 사람이 옮긴 것. 식별자는 영어 그대로 둡니다
- **비교 방식**: 키워드 검색(파일 통째로), 키워드 검색(맞은 줄 앞뒤 12줄씩), Lantern. 그리고 Lantern과 같은 수의 파일을 무작위로 골랐을 때의 기대 적중률
- **한계**: 커밋 메시지는 짧고 영어라 실제 사용자 질문과 다릅니다. 정답 파일은 '고친 파일'이라, 읽어야 했지만 고치지 않은 파일은 정답에 없습니다

## 맥락 적중률 (모델 호출 없음, 무료)

```bash
Expand Down
190 changes: 190 additions & 0 deletions eval/bench/commons-lang.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
{
"project": "commons-lang",
"url": "https://github.com/apache/commons-lang",
"ref": "29624cdb50ecd794207d561345b2fc9ca3a9d326",
"made_by": "eval/bench/make.mjs",
"questions": [
{
"q": "Fix mutable time zone keys in the FastDateFormat cache",
"q_ko": "FastDateFormat 캐시의 변경 가능한 시간대 키 수정",
"golden": [
"src/main/java/org/apache/commons/lang3/time/AbstractFormatCache.java"
],
"commit": "55dcbc4bb1449aa9f7c6ee422cc22b465a35fa2f",
"parent": "d86ec875bdc7676d76c162d393e81db7dee01f2d"
},
{
"q": "Reflection builders' cycle registry unregisters per-visit: cycle-SAFE but DAG-exponential, a 40-level reference diamond drives ~2^40 traversals and unbounded output (f032).",
"q_ko": "리플렉션 빌더의 순환 참조 등록이 방문마다 해제되어 깊은 참조 구조에서 순회가 지수적으로 늘어나는 문제",
"golden": [
"src/main/java/org/apache/commons/lang3/builder/ToStringStyle.java"
],
"commit": "4303a36de3a8b29d19b88e7ca5901f78a536de72",
"parent": "11e2dad29e2ae58d3bd604233af55183ccee1d17"
},
{
"q": "StrBuilder.indexOf materializes the whole builder as a String per call; deleteAll/replaceAll multiply it into ~750 GB churn on a 1 MB builder (f026).",
"q_ko": "StrBuilder.indexOf가 호출마다 전체를 String으로 만들어 deleteAll과 replaceAll이 메모리를 과하게 쓰는 문제",
"golden": [
"src/main/java/org/apache/commons/lang3/CharSequenceUtils.java"
],
"commit": "4d053dc896df7c1165673637607fcc86a146eb10",
"parent": "2e16df6260fb207cf45ae456e25e391cb7ef9bf8"
},
{
"q": "Attacker-controlled exception message forges frames in getRootCauseStackTrace output AND suppresses all real frames, beyond cosmetic log spoofing (f025).",
"q_ko": "공격자가 만든 예외 메시지가 getRootCauseStackTrace 출력에 가짜 프레임을 끼워 넣고 진짜 프레임을 숨기는 문제",
"golden": [
"src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java"
],
"commit": "2e16df6260fb207cf45ae456e25e391cb7ef9bf8",
"parent": "2dfaafa2576afc5534575fa27967b622e86580b9"
},
{
"q": "default caches the first failure forever, has no size bound or eviction, and runs the user computation inside the ConcurrentHashMap bin lock (blocking unrelated keys, deadlocking reentrant use) (f011).",
"q_ko": "기본값이 첫 실패를 영원히 캐시하고 크기 제한도 없으며 ConcurrentHashMap 잠금 안에서 사용자 계산을 실행하는 문제",
"golden": [
"src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java"
],
"commit": "1aef131265a6b2c8a5ded4301dd92468c7d826c5",
"parent": "fd6361792d1e021462215386d69dd3e0d1b59229"
},
{
"q": "keep stripStart and stripEnd off surrogate pair boundaries",
"q_ko": "stripStart와 stripEnd가 서로게이트 쌍 경계를 자르지 않게",
"golden": [
"src/main/java/org/apache/commons/lang3/StringUtils.java"
],
"commit": "c747711133c3e13d8d2bdab27a2eb7b11f1c6d7f",
"parent": "8bf8b78700c778b10049131f51b687195e039da9"
},
{
"q": "Keep chop from splitting a trailing surrogate pair",
"q_ko": "chop이 끝의 서로게이트 쌍을 쪼개지 않게",
"golden": [
"src/main/java/org/apache/commons/lang3/StringUtils.java"
],
"commit": "62f6edfd96008fd10cd22bf854cf4e8d2c008ae2",
"parent": "9ed5d0ba95652ffcbb912894ce67d81a2fbd60df"
},
{
"q": "Handle ParsePosition index beyond source string length cleanly",
"q_ko": "ParsePosition 인덱스가 원본 문자열 길이를 넘을 때 깔끔하게 처리",
"golden": [
"src/main/java/org/apache/commons/lang3/time/FastDateParser.java"
],
"commit": "e0dba35f9e41dfdbbbb646fcd377b8af8ef7344e",
"parent": "94ad3a756aad97add12e48ed8ff54d630a927d4b"
},
{
"q": "fix getMatchingMethod false ambiguity for boxed primitive args",
"q_ko": "박싱된 기본형 인자에서 getMatchingMethod가 잘못 모호하다고 하는 문제 수정",
"golden": [
"src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java"
],
"commit": "aae7f0e61c51040dbdc7556f9eca5786eb9f63bd",
"parent": "dfaea97de4dd175781eeb552ff9eb29db767acc3"
},
{
"q": "fix unescapeCsv bounds error on a single quote field",
"q_ko": "따옴표 하나뿐인 필드에서 unescapeCsv 범위 오류 수정",
"golden": [
"src/main/java/org/apache/commons/lang3/StringEscapeUtils.java"
],
"commit": "6dbbbb308bb77ba9c0ddab1ae400ee2ff76026b4",
"parent": "c4c294b0e04024d361d710985f8500f7d2412232"
},
{
"q": "keep initials from splitting a supplementary code point",
"q_ko": "initials가 보조 코드 포인트를 쪼개지 않게",
"golden": [
"src/main/java/org/apache/commons/lang3/text/WordUtils.java"
],
"commit": "6bef246b2f10699a56a3010bbc6fa11d241430c6",
"parent": "226bf3975afd430b6b74c23a0c9cbae9f9b90de5"
},
{
"q": "strip L suffix for oversized hex literals in createNumber",
"q_ko": "createNumber에서 너무 큰 16진수 리터럴의 L 접미사 제거",
"golden": [
"src/main/java/org/apache/commons/lang3/math/NumberUtils.java"
],
"commit": "18063c1f6d935d60d0e7341d68434ab4c88a34ee",
"parent": "e10003978ba6bacb7b32c27816b68b858404e967"
},
{
"q": "Add tests to ensure nulls compare correctly",
"q_ko": "null이 올바르게 비교되는지 확인하는 테스트 추가",
"golden": [
"src/main/java/org/apache/commons/lang3/compare/ObjectToStringComparator.java"
],
"commit": "60d4c9843891f07596d2caf52fb8689d80179344",
"parent": "3e111f128a1f5a2f1e7df78c5cd6e8206eada959"
},
{
"q": "reject doubled leading sign in createBigInteger",
"q_ko": "createBigInteger에서 앞에 부호가 두 번 오면 거부",
"golden": [
"src/main/java/org/apache/commons/lang3/math/NumberUtils.java"
],
"commit": "060ab31e517d856292e97428345821b5b289249a",
"parent": "44298fe0c0df0688827bcbf07677c3706de00f88"
},
{
"q": "fix lookup translator code point count for supplementary keys",
"q_ko": "보조 문자 키에 대한 lookup translator의 코드 포인트 개수 수정",
"golden": [
"src/main/java/org/apache/commons/lang3/text/translate/LookupTranslator.java"
],
"commit": "80bf07c61721520ef12679fc394ac5584c1e9536",
"parent": "1e071b3dd5a16cd74b3dd991b7d0b03e581c8d7a"
},
{
"q": "NaN bypass in primitive double range validators.",
"q_ko": "기본형 double 범위 검증에서 NaN이 통과하는 문제",
"golden": [
"src/main/java/org/apache/commons/lang3/Validate.java"
],
"commit": "864dd6ea887fd394f15e3b55203d6f52f717373b",
"parent": "4b09471db039085a3114f79ece97ca7fd9bd6f1e"
},
{
"q": "Annotation lookup matches unrelated overloads via assignable-parameter resolution",
"q_ko": "애너테이션 조회가 대입 가능한 파라미터 해석 때문에 관련 없는 오버로드와 매칭되는 문제",
"golden": [
"src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java"
],
"commit": "daa17b8629a7a9921ffe42e7c2c0f0051af42571",
"parent": "0c7942f416ec7dbf163e72ce0a62a138f11822ad"
},
{
"q": "No need to end exception messages with an exclamation!",
"q_ko": "예외 메시지를 느낌표로 끝내지 않기",
"golden": [
"src/main/java/org/apache/commons/lang3/concurrent/BackgroundInitializer.java",
"src/main/java/org/apache/commons/lang3/concurrent/MultiBackgroundInitializer.java",
"src/main/java/org/apache/commons/lang3/concurrent/TimedSemaphore.java"
],
"commit": "23a730b8ed392ba424cc275df793dcae88f4cebe",
"parent": "7138b8c001b9c60176598a21c0d15db43f8d8a1f"
},
{
"q": "Make stopInstant be in sync with stopTimeNanos for split",
"q_ko": "split할 때 stopInstant를 stopTimeNanos와 맞추기",
"golden": [
"src/main/java/org/apache/commons/lang3/time/StopWatch.java"
],
"commit": "aa47951bd0223d674d2b85d06ccc6f7f1749a682",
"parent": "185edc4b8594e20d81baad65a199a211e2137616"
},
{
"q": "Use a single return in indexesOf() methods",
"q_ko": "indexesOf() 메서드에서 return을 하나만 쓰기",
"golden": [
"src/main/java/org/apache/commons/lang3/ArrayUtils.java"
],
"commit": "becbcffffe673ad062b2b389a2a31dce7565841e",
"parent": "a4cf528306f96784ed9c8e4cacb59e128fea92b3"
}
]
}
Loading