Show HN:通过OpenJDK Panama FFM(Java 22)实现的低延迟本地LLM运行器

Hacker News Top 工具

摘要

libargus 是一个零分配的原生 AI 推理运行时,它将 LLM、语音和视觉流水线整合到 Java 22+ 的 Project Panama FFM 边界之后,实现低延迟的本地执行。

我希望在 JVM 内部运行 AI。最初我采用了标准的 REST sidecar 方式,但为了直接与 llama.cpp 交互,我改用新版 JDK 中的 Project Panama(外部函数与内存 API)。然而我对这种方式仍不满意,于是构建了 libargus.cc,以获得一个干净的 ABI,并在 JVM 环境中暴露结构化的 API。它仍然使用 Project Panama 直接与 llama.cpp、whisper.cpp 和 ggml 计算图进行交互。<p>我在热点路径上实现了零分配,提示和令牌的内存段在受限的 Arena 中只分配一次。原始指针直接传递到底层 C 级别。这避免了原始数组克隆和堆内存抖动。<p>我映射了来自 llama.cpp 和 whisper.cpp 的原生结构,同时匹配编译器的填充方式以确保安全的内存访问。<p>我在 jar 中捆绑了预编译的原生二进制文件,以便于部署。<p>这个执行引擎为我正在进行的时空记忆层(L-TABB)工作(用于替代 RAG)提供了基础。我非常希望获得技术反馈,以便在继续开发下一层时改进任何问题。任何深入探讨 Project Panama 或现代 JDK 中低延迟系统的人都将受到高度赞赏!<p>我写代码比写散文更擅长,所以让代码来说话吧。<p>祝编程愉快! &#x2F;David<p>代码:<a href="https:&#x2F;&#x2F;libargus.cc" rel="nofollow">https:&#x2F;&#x2F;libargus.cc</a> 项目主页:<a href="https:&#x2F;&#x2F;projectargus.cc" rel="nofollow">https:&#x2F;&#x2F;projectargus.cc</a>
查看原文
查看缓存全文

缓存时间: 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 直接构建在模块化的 GGMLllama.cpp (libmtmd) 计算引擎之上,提供一个统一的、线程安全的C API,专为与现代化非托管编排框架进行无摩擦、零拷贝编译而设计,并内置与 JDK 22+ Project Panama 外部函数与内存 (FFM) API 的结构对齐支持。


核心架构支柱

  • 进程全局后端单一性: 通过跨文本、音频、语音和多模态子系统编排单一的共享初始化路径(ggml_backend_load_all()),消除VRAM碎片化和多上下文驱动竞态条件。
  • 解耦权重与执行: 将模型权重加载(argus_model_t)与评估上下文内存状态(argus_context_t)分离,允许模型在多个并发会话中重复使用。
  • 前沿多模态投影器: 集成新的 libmtmd C++ 引擎,以摄入原始位图、音频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_ktype_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.soargus.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 推理用于未来太空任务

Reddit r/LocalLLaMA

NASA 正在测试 Red Hat 的开放源代码工具 RamaLama,以在深空任务中为医疗 AI 助手运行本地 LLM 和 VLM 推理,实现无需与地球通信的自主实时诊断。

lyogavin/airllm

GitHub Trending (daily)

AirLLM 是一个开源库,能够在单个 4GB GPU 上运行大型语言模型(最高可达 405B),无需量化、蒸馏或剪枝,显著降低了本地 LLM 推理的硬件门槛。

Show HN: 在慢速电脑上运行GLM 5.2

Hacker News Top

Colibrì是一个纯C推理引擎,通过从磁盘流式加载专家,在约25GB RAM的消费级硬件上运行744B参数的GLM-5.2 MoE模型,配合推测解码可实现约2.2-2.8 token/秒的速度。

构建 Conifer:一款开源本地推理运行时(免费 + 开源)

Reddit r/artificial

Conifer 是由普林斯顿团队打造的全新开源本地推理运行时,针对 Apple Silicon 优化并采用自定义 Rust 内核。其目标是在小型模型上超越 llama.cpp 和 MLX,支持具有操作系统级权限执行的完全本地化智能体,目前进入限 100 人测试阶段。

ggml-org/llama.cpp

GitHub Trending (daily)

llama.cpp 是一个开源 C/C++ 库,用于在本地硬件上高效运行 LLM 推理,支持多种量化方法和多后端(CPU、GPU 等)。