ashlr-core-efficiency:面向 AI 编程智能体的 token 效率工具库
ashlrai/ashlr-core-efficiency
ashlr-core-efficiency 发布,为 AI 编程智能体提供 token 效率基础模块,包含 genome RAG、三层上下文压缩、按 provider 感知的预算与 prompt 缓存辅助,由 ashlrcode CLI 和 Claude Code 的 ashlr-plugin 共用。
Token-efficiency primitives for AI coding agents — genome RAG, multi-tier context compression, provider-aware budgeting, and prompt-caching helpers.
Extracted from ashlrcode and shared by the ashlrcode CLI and the ashlr-plugin for Claude Code.
Platform support: macOS / Linux / Windows on Bun >= 1.0 and Node >= 20.
Modules
| Subpath | LOC | Purpose |
|---|---|---|
/genome |
~2,342 | Self-evolving project specs via RAG + scribe protocol. Manifest CRUD, TF-IDF/Ollama retrieval, fitness-based strategy evolution, mutation audit trail. |
/compression |
~470 | 3-tier context compression: autoCompact (LLM summarize old turns), snipCompact (truncate tool results > 2 KB), contextCollapse (drop short/dup). PromptPriority enum. |
/budget |
~50 | Provider-aware prompt budgeting. getProviderContextLimit, systemPromptBudget. |
/tokens |
~50 | Token estimation: estimateTokensFromString, estimateTokensFromMessages. |
/anthropic |
~200 | Anthropic SDK helpers: withGenome, cacheBreakpoints, ashlrMcpConfig. |
/session-log |
~150 | Structured session event log (tool calls, costs, savings). |
/local |
~120 | Context-window manager for small-context local models. |
/types |
~60 | Shared types: Message, ContentBlock, LLMSummarizer, StreamEvent. |
Install
# Bun (primary runtime) bun add @ashlr/core-efficiency # npm / pnpm / yarn npm install @ashlr/core-efficiency
For local development against a checkout:
bun add file:../ashlr-core-efficiency
The package ships TypeScript source in src/. Bun runs it directly. For Node.js, compile first with bun run build (outputs to dist/).
Quickstart
compression
import { autoCompact, snipCompact, contextCollapse, } from "@ashlr/core-efficiency/compression"; // Truncate tool results over 2,000 chars (keeps the first and last 800). const trimmed = snipCompact(messages); // Drop short or duplicate messages to reduce prompt size. const collapsed = contextCollapse(messages); // LLM-summarize old turns when approaching the context limit. // summarizer: any LLMSummarizer (an object with `stream(request)`, e.g. your provider client). const compacted = await autoCompact(messages, summarizer, { maxContextTokens: 50_000, recentMessageCount: 10, });
budget
import { getProviderContextLimit, systemPromptBudget, } from "@ashlr/core-efficiency/budget"; const limit = getProviderContextLimit("anthropic"); // 200_000 const budget = systemPromptBudget("anthropic", 0.05, 50_000); // 5% floor, 50K cap
tokens
import { estimateTokensFromString, estimateTokensFromMessages, } from "@ashlr/core-efficiency/tokens"; const n = estimateTokensFromString("Hello, world!"); const total = estimateTokensFromMessages(messages); // walks ContentBlock[] incl. tool results
genome
import { retrieveSectionsV2, injectGenomeContext, genomeExists, } from "@ashlr/core-efficiency/genome"; if (genomeExists(process.cwd())) { // retrieveSectionsV2(cwd, query, maxTokens) → relevant sections within budget. const sections = await retrieveSectionsV2( process.cwd(), "architecture overview", 2000, ); // Or inject directly into a prompt builder: // injectGenomeContext(builder, cwd, taskDescription, maxTokens) → tokens added. const added = await injectGenomeContext( builder, process.cwd(), "architecture overview", 2000, ); }
anthropic
import Anthropic from "@anthropic-ai/sdk"; import { withGenome, cacheBreakpoints } from "@ashlr/core-efficiency/anthropic"; const client = new Anthropic(); const system = await withGenome("You are a senior engineer.", process.cwd()); const req = cacheBreakpoints({ system, messages: [ { role: "user", content: projectContext, cache: true }, { role: "user", content: "What does login.ts do?" }, ], }); await client.messages.create({ ...req, model: "claude-sonnet-4-6", max_tokens: 1024 });
For stdio MCP tools via the Agent SDK:
import { query } from "@anthropic-ai/claude-agent-sdk"; import { ashlrMcpConfigRecord } from "@ashlr/core-efficiency/anthropic"; const mcpServers = ashlrMcpConfigRecord({ plugins: ["efficiency"] }); for await (const msg of query({ prompt: "...", options: { mcpServers } })) { // ... }
See examples/anthropic-sdk/ for runnable scenarios.
session-log
import { append, read, tail, rotate } from "@ashlr/core-efficiency/session-log"; // Append one entry (ts is auto-filled if omitted). await append({ agent: "claude-code", event: "tool_call", tool: "ashlr__read", meta: { inputTokens: 120 }, }); // Read the most recent entries (most-recent-first). const recent = read({ limit: 10 }); // Follow new entries as they're appended (async iterator). for await (const entry of tail({ agent: "claude-code" })) { console.log(entry.event, entry.tool); break; } // Force a rotation (renames the log to <path>.1 and starts fresh). rotate();
local
import { LocalContextWindow, buildResumeContext } from "@ashlr/core-efficiency/local"; // Configure for a small-context model (e.g. a 32K model with 4K reserved). const ctx = new LocalContextWindow({ maxTokens: 28_000, compactThreshold: 0.75 }); // Add messages; add() returns false once compaction is needed. for (const message of allMessages) { if (!ctx.add(message)) { await ctx.compact(summarizer); // summarizer optional; tiers snip/collapse first } } const messages = ctx.getMessages(); // messages ready for the next LLM call console.log(ctx.stats()); // { totalTokens, used, utilization, messageCount, compactions } // Rebuild a compact orientation block when resuming a session. const resume = await buildResumeContext(process.cwd());
Root barrel export (all subpaths re-exported):
import { autoCompact, getProviderContextLimit, retrieveSectionsV2, estimateTokensFromString, } from "@ashlr/core-efficiency";
Compatibility
| Runtime | macOS | Linux | Windows |
|---|---|---|---|
| Bun >= 1.0 | Yes | Yes | Yes |
| Node >= 20 | Yes (compile first) | Yes (compile first) | Yes (compile first) |
Path separators are normalized internally; no Unix-only assumptions.
Development
bun install bun test # full suite (~2,300 tests) bun run typecheck bun run build # emit dist/ for Node consumers
Integration tests live in the ashlrcode repo where 700+ tests run against real-world consumers.
Design notes
LLMSummarizerinterface:autoCompactand genomescribedepend on a minimal{ stream(ProviderRequest): AsyncGenerator<StreamEvent> }contract, not a concrete router. Consumers inject their own provider.PromptPriorityenum: 12 named slots (Core=0throughUndercover=95). Numeric values are stable — raw-int callers continue to work across versions.estimateTokens: previously duplicated in three places. Now one implementation, two entry points:FromStringandFromMessages(walksContentBlock[]includingtool_use/tool_result).- Source-first exports:
mainandexportspoint tosrc/. Bun resolves.tsimports directly. For Node.js, runbun run buildand consume fromdist/. Amodulefield mirrorsmainfor bundlers that inspect it.
Versioning
Follows semver. Breaking changes (removed exports, changed interfaces) go to major versions. Additive exports and bug fixes are minor/patch.
License
MIT — see LICENSE.
来源:GitHub · github.com