Show HN:通过OpenJDK Panama FFM(Java 22)实现的低延迟本地LLM运行器
摘要
libargus 是一个零分配的原生 AI 推理运行时,它将 LLM、语音和视觉流水线整合到 Java 22+ 的 Project Panama FFM 边界之后,实现低延迟的本地执行。
查看缓存全文
缓存时间: 2026/07/15 22:46
projectargus-cc/libargus.cc 源码:https://github.com/projectargus-cc/libargus.cc
libargus
一个非托管的、零分配原生AI执行运行时,将视觉、语音和LLM计算管道整合在单个Project Panama FFM边界之后。
v1.0.0 稳定版 — 统一硬件编排与零分配ABI
libargus是一个超精简、高性能、模型无关的推理封装,旨在将LLM文本生成、基于Whisper的语音转文本(ASR)、Speech-LLM文本转语音(TTS)以及前沿的多模态(视觉、音频和视频)编码与评估管道整合到单个进程全局的原生执行运行时中。libargus直接构建在模块化的 GGML 和 llama.cpp (libmtmd) 计算引擎之上,提供一个统一的、线程安全的C API,专为与现代化非托管编排框架进行无摩擦、零拷贝编译而设计,并内置与 JDK 22+ Project Panama 外部函数与内存 (FFM) API 的结构对齐支持。
核心架构支柱
- 进程全局后端单一性: 通过跨文本、音频、语音和多模态子系统编排单一的共享初始化路径(
ggml_backend_load_all()),消除VRAM碎片化和多上下文驱动竞态条件。 - 解耦权重与执行: 将模型权重加载(
argus_model_t)与评估上下文内存状态(argus_context_t)分离,允许模型在多个并发会话中重复使用。 - 前沿多模态投影器: 集成新的
libmtmdC++ 引擎,以摄入原始位图、音频PCM数组和视频文件/流。将提示词和媒体分词为统一的块序列,在GPU上执行投影,并自动配置M-RoPE位置网格和非因果注意力矩阵。 - 非托管视频迭代管道: 使用内部FFmpeg子进程管道逐帧解码和流式传输视频文件,以指定的目标帧率产生原始RGB帧或带时间戳的文本块(例如,
[12m34s])。 - 仅指针的FFM对齐: 用严格对齐的、接受指针的扁平C函数替换传值和易失性C++多态边界。结构体内存布局手动打包,防止编译器注入对齐间隙。
- 绝对零拷贝内存边界: 消除热路径上的JVM堆原始数组(
int[]、float[])。直接集成Project Panama的MemorySegment参数,使得token磁带、音频波形和视频帧能够以零GC开销生成语音和文本。 - 选择性并发锁定: 集成上下文级别的互斥锁同步,允许线程安全的解码和上下文操作,同时在只读模型上实现完全无锁的并发tokenizer访问。
- 推测性与MTP加速: 在C++执行层内部集成传统推测草稿和多Token预测(
draft-mtp)的原生验证循环。 - KV缓存量化: 支持原生配置(
type_k和type_v缓存枚举),将内存占用转移到 Q8_0、Q4_0 或其他优化格式。 - 零分配词汇与GGUF元数据自省: 公开安全的非托管边界,用于查找特殊词汇token(BOS、EOS、EOT、PAD),验证生成结束(EOG)条件,并动态枚举GGUF字典条目。
代码库拓扑
libargus/
├── CMakeLists.txt # 第0层依赖隔离与优化矩阵
├── include/
│ └── libargus.h # 主C ABI稳定布局定义
├── src/
│ ├── argus_internal.h # 共享私有结构体(模型和上下文)
│ ├── argus_common.cc # 全局后端生命周期与硬件注册表
│ ├── argus_text.cc # Llama模型/上下文处理、推测循环与TTS
│ ├── argus_audio.cc # Whisper模型上下文与转录(ASR)
│ └── argus_multimodal.cc # 多模态上下文、媒体加载器、视频管道与评估
└── bindings/java/ # 地道的Project Panama FFM绑定模块
└── src/main/java/cc/projectargus/libargus/
├── ArgusBackend.java # 全局设备遥测与后端初始化
├── ArgusModel.java # 非托管GGUF权重管理器(AutoCloseable)
├── ArgusContext.java # 核心文本评估上下文会话
├── ArgusContextConfig.java # 文本上下文生成参数
├── ArgusAudioContext.java # Whisper语音转文本转录引擎
├── ArgusMultimodalContext.java # 已加载的多模态投影器上下文(AutoCloseable)
├── ArgusBitmap.java # 原始/解析的RGB像素或PCM音频样本缓冲区
├── ArgusVideo.java # 视频文件或缓冲区管道的帧迭代器
├── ArgusVideoItem.java # 视频处理的可重用帧/时间戳容器
├── ArgusInputChunks.java # 已分词的多模态提示块容器
└── internal/
├── ArgusLayouts.java # Panama C到Java结构布局定义
└── ArgusBindings.java # 动态共享库方法句柄加载器
构建与依赖架构
libargus 强制执行纯净的源代码配置。它通过使用CMake级别的构建层内部管理(FetchContent)丢弃臃肿的上游服务器实现、遗留CLI目标和不需要的依赖项。上游组件在非托管编译过程中被下载、配置并静态链接。
编译矩阵
要编译高度优化的共享二进制目标(libargus.so 或 argus.dll),请从根目录执行目标生成命令:
# 生成优化的非托管计算图项目(启用CUDA加速)
cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_CUDA=ON
# 编译最终的统一系统二进制文件
cmake --build build --config Release -j $(nproc)
快速入门:地道的Java开发者体验
libargus 保护JVM开发者免受复杂指针运算、结构对齐间隙和手动注意力掩码调度的困扰。以下是高级、内存安全且自动关闭的Java API使用模式。
文本与音频转录(ASR)
import cc.projectargus.libargus.*;
import java.lang.foreign.Arena;
import java.nio.file.Path;
public class Main {
public static void main(String[] args) {
// 初始化全局后端(CUDA/CPU)
ArgusBackend.init();
try (Arena arena = Arena.ofConfined();
ArgusModel model = ArgusModel.load(arena, Path.of("models/llama-3-8b.gguf"), 99, true)) {
// 初始化上下文配置
ArgusContextConfig config = new ArgusContextConfig.Builder(4096)
.cpuThreads(8)
.typeK(ArgusContextConfig.KV_TYPE_Q4_0) // 量化KV缓存
.typeV(ArgusContextConfig.KV_TYPE_Q4_0)
.build();
try (ArgusContext context = ArgusContext.init(arena, model, config)) {
// 运行文本评估与生成循环...
}
} finally {
// 关闭原生后端驱动程序
ArgusBackend.free();
}
}
}
前沿多模态提示(视觉/视频/音频)
使用视觉能力的GGUF模型及其多模态投影器(mmproj):
import cc.projectargus.libargus.*;
import java.lang.foreign.Arena;
import java.nio.file.Path;
import java.util.List;
public class MultimodalApp {
public static void main(String[] args) {
ArgusBackend.init();
try (Arena arena = Arena.ofConfined();
ArgusModel baseModel = ArgusModel.load(arena, Path.of("models/qwen2-vl-7b-it.gguf"), 99, true);
ArgusContext context = ArgusContext.init(arena, baseModel, new ArgusContextConfig.Builder(8192).build());
// 加载多模态适配器上下文
ArgusMultimodalContext mctx = ArgusMultimodalContext.init(arena, baseModel, Path.of("models/qwen2-vl-7b-it.mmproj"), 4, true)) {
// 1. 将图像或音频文件加载到非托管内存
try (ArgusBitmap image = ArgusBitmap.loadFile(arena, mctx, Path.of("media/cat.png"), false)) {
// 2. 将提示文本分词,替换媒体标记
String prompt = "<__media__>\nDescribe what you see in this image.";
try (ArgusInputChunks chunks = mctx.tokenize(arena, prompt, true, List.of(image))) {
// 3. 评估块(在GPU上处理图像投影和M-RoPE位置网格)
int newNPast = context.evalMultimodalChunks(mctx, chunks, 0, 0, 1024, true);
System.out.println("Prompt evaluated. Ready to sample output tokens! New position: " + newNPast);
}
}
} finally {
ArgusBackend.free();
}
}
}
逐帧视频流处理
// 从视频文件顺序迭代并读取帧/时间戳
try (ArgusVideo video = ArgusVideo.loadFile(arena, mctx, Path.of("media/video.mp4"), 4.0f, 5000);
ArgusVideoItem item = new ArgusVideoItem()) {
while (video.readNext(item)) {
if (item.bitmap() != null) {
// 处理提取的RGB帧位图(所有权由ArgusVideoItem管理)
ArgusBitmap frame = item.bitmap();
// ...
} else if (item.text() != null) {
// 收到视频时间戳块(例如"[00m05s]")
System.out.println("At timestamp: " + item.text());
}
}
}
提取语义文本嵌入
从专用模型(例如 jina-embeddings-v3)检索浮点嵌入向量:
import cc.projectargus.libargus.*;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.nio.file.Path;
public class EmbeddingsApp {
public static void main(String[] args) {
ArgusBackend.init();
try (Arena arena = Arena.ofConfined();
ArgusModel model = ArgusModel.load(arena, Path.of("models/jina-embeddings-v3-Q4_K_M.gguf"), 99, true)) {
// 初始化启用了嵌入的上下文配置
ArgusContextConfig config = new ArgusContextConfig.Builder(512)
.cpuThreads(4)
.embeddings(true)
.build();
try (ArgusContext context = ArgusContext.init(arena, model, config)) {
// 对提示进行分词和评估
String text = "text-matching: Retrieve semantic vector for this sentence.";
MemorySegment textSeg = arena.allocateFrom(text);
MemorySegment tokenBuf = arena.allocate(ValueLayout.JAVA_INT, 512);
int nTokens = context.tokenize(textSeg, tokenBuf, true);
context.decodeBatch(tokenBuf, nTokens, 0, 0, false);
// 检索嵌入向量(例如1024维)
int expectedDim = 1024;
MemorySegment embeddingsBuf = arena.allocate(ValueLayout.JAVA_FLOAT, expectedDim);
int nFloats = context.getEmbeddings(0, embeddingsBuf, expectedDim);
System.out.println("Retrieved embeddings containing " + nFloats + " floats.");
}
} finally {
ArgusBackend.free();
}
}
}
模型元数据与词汇自省
直接从非托管内存查询特殊token并遍历GGUF模型配置:
// 访问模型词汇元数据
int bos = model.vocabBos();
int eos = model.vocabEos();
int eot = model.vocabEot(); // 聊天模型的对话结束token
int nTokens = model.vocabNTokens(); // 词汇表容量
boolean isEog = model.vocabIsEog(sampledToken); // 原生生成结束验证
// 查询模型架构维度与参数
int nEmbd = model.nEmbd(); // 嵌入维度大小(例如1024)
int nCtxTrain = model.nCtxTrain();// 训练时的上下文长度上限
int nLayer = model.nLayer(); // Transformer层数
int nHead = model.nHead(); // 注意力头数
long nParams = model.nParams(); // 模型总参数数量
// 按键名检索元数据字符串
String modelArch = model.getMetadataValue("general.architecture"); // 例如"qwen2vl"
String modelName = model.getMetadataValue("general.name"); // 例如"Qwen2 VL 2B Instruct"
// 遍历并检查完整元数据字典
java.util.Map<String,String> metadata = model.getMetadataMap();
metadata.forEach((key, val) -> System.out.println(key + " -> " + val));
模型无关的Logit偏差采样
通过在生成会话开始时一次性分配偏差段,并在热路径采样步骤中重复使用,强制执行严格的零分配logit引导(例如屏蔽推理token或增强特定补全):
try (Arena sessionArena = Arena.ofConfined()) {
// 定义偏差token及其引导权重(例如 -Float.MAX_VALUE 表示屏蔽)
int[] steerTokens = new int[] { 151644, 151645 }; // <__think__> 标签
float[] steerValues = new float[] { -Float.MAX_VALUE, -Float.MAX_VALUE };
// 在热生成循环外部只分配一次非托管结构段
MemorySegment biasSeg = sessionArena.allocate(ArgusLayouts.LOGIT_BIAS, steerTokens.length);
for (int i = 0; i < steerTokens.length; i++) {
biasSeg.setAtIndex(ValueLayout.JAVA_INT, i * 2, steerTokens[i]);
biasSeg.setAtIndex(ValueLayout.JAVA_FLOAT, i * 2 + 1, steerValues[i]);
}
while (generating) {
context.decodeBatch(batch);
// 零拷贝、零分配的token生成下调用,传递原始指针
int token = context.sampleTokenWithBias(
seqId, temperature, repeatPenalty,
biasSeg, steerTokens.length
);
if (token == model.vocabEos()) break;
}
}
验证与测试套件
通过运行原生和Java集成测试管道,验证非托管张量边界合规性与多模型处理线程重入性:
# 运行原生C单元断言
./build/test_libargus
# 运行JUnit / Panama FFM集成测试
cd bindings/java && gradle test
工程方法论与开发速度
libargus 在一个连续的冲刺中完成架构设计、工程实现并发布稳定版。为了在不牺牲性能或内存安全的前提下实现这一开发速度,执行了明确的职责分工:
- 人类核心(架构与系统设计): 每个关键内存语义、低级约束和硬件优化边界均由人类工程师明确设计和驱动。这包括堆外Arena生命周期边界(
Arena.ofConfined)、严格的1:1手动结构对齐打包以防止跨编译器布局漂移、可变的堆外资源回收路径(ArgusVideoItem)以绕过JVM GC开销,以及 O(1) 零拷贝交错logit引导矩阵(argus_logit_bias_t)。 - AI核心(样板代码编译通道): 大型语言模型被严格用作高速语法编译器。AI被用于基于明确的工程蓝图,快速生成重复的非托管C到Java下调用绑定、参数构建器样板和繁琐的结构Java映射布局字符串。这种混合方法将AI视为高级文本编译器,而非无指导的代码生成器,从而加速了零分配、机械同情的系统代码交付,同时确保总体架构控制权仍掌握在人类手中。
上游集成与项目路线图
libargus 被严格设计为第0层(核心执行基石),面向低延迟、性能关键的JVM平台。它提供了通过Project Panama进行零分配原生张量编排所需的原始计算基础。该引擎是更广泛认知平台的高吞吐量基础设施。要查看关于此运行时块如何与即将推出的第1层有状态认知核心(L-TABB)及统一系统仪表板交互的高级路线图,请访问主项目组织登陆页面 ProjectArgus.cc。
许可与归属
libargus 在 MIT 许可证下开源发布。本软件集成并链接了源自 llama.cpp(包括 libmtmd)和 whisper.cpp 的计算张量原语。
相似文章
NASA 测试本地 LLM 推理用于未来太空任务
NASA 正在测试 Red Hat 的开放源代码工具 RamaLama,以在深空任务中为医疗 AI 助手运行本地 LLM 和 VLM 推理,实现无需与地球通信的自主实时诊断。
lyogavin/airllm
AirLLM 是一个开源库,能够在单个 4GB GPU 上运行大型语言模型(最高可达 405B),无需量化、蒸馏或剪枝,显著降低了本地 LLM 推理的硬件门槛。
Show HN: 在慢速电脑上运行GLM 5.2
Colibrì是一个纯C推理引擎,通过从磁盘流式加载专家,在约25GB RAM的消费级硬件上运行744B参数的GLM-5.2 MoE模型,配合推测解码可实现约2.2-2.8 token/秒的速度。
构建 Conifer:一款开源本地推理运行时(免费 + 开源)
Conifer 是由普林斯顿团队打造的全新开源本地推理运行时,针对 Apple Silicon 优化并采用自定义 Rust 内核。其目标是在小型模型上超越 llama.cpp 和 MLX,支持具有操作系统级权限执行的完全本地化智能体,目前进入限 100 人测试阶段。
ggml-org/llama.cpp
llama.cpp 是一个开源 C/C++ 库,用于在本地硬件上高效运行 LLM 推理,支持多种量化方法和多后端(CPU、GPU 等)。