Helix结合llm-ls实现AI辅助编程
llm-ls
Hugging Face 使用 Rust 开发了一个 huggingface/llm-ls ,提供基于LLM来实现的LSP服务器,负责将编辑器的补全请求发送给 ollama 等后端。
llm-ls提供的功能:
Prompt(提示): 使用当前文件作为上下文生成提示,根据需求可以选择是否使用"fill in the middle" (中间填充)
Telemetry(遥测): 收集有关请求和补全的信息,以便进行重新训练(llm-ls除了查询模型API时设置用户代理外,所有数据都存储在日志文件
~/.cache/llm_ls/llm-ls.log中,所以不会有数据泄露隐患。注意,日志级别设置为info)Completion(补全): llm-ls解析代码的抽象语法树(AST),以确定补全应该死多行、单行还是空(不补全)
多种后端:
Hugging Face's Inference API
Hugging Face's text-generation-inference
OpenAI compatible APIs (如 Python Bindings for llama.cpp 提供模拟OpenAI API的web服务器`)
服务端
docker exec -it ollama ollama run qwen3-coder-30b-a3b-instruct-q4_k_m
安装llm-ls
llm-ls官方GitHub只提供了 Windows / Linux 和 macOS 的二进制程序,由于我目前使用 FreeBSD 作为桌面,所以我需要自己编译安装 llm-ls
备注
当在Helix中同时为一个语言(如Python)指定了 pyright 和 llm-ls 时,这两种LSP是同时运行的: Helix作为客户端,会同时向 pyright 和 llm-ls 发送请求,并实时合并它们的结果:
pyright会根据源代码、类型定义、标准库文档来提供 语法纠错、变量转跳、函数名提示 ,所给出的建议是 ""绝对正确的** (例如确定有这个方法)llm-ls是生成式AI,所以根据上下文"猜"下一步写什么,所以不仅能补全变量名,还能补全整行代码甚至一段逻辑,但是需要注意llm-ls给出的建议仅是 看起来概率最高的 (不一定准确,甚至有幻觉)
FreeBSD安装llm-ls
在FreeBSD上安装 Rust :
sudo pkg install rust
下载源代码进行编译:
# 1. 克隆仓库
git clone https://github.com/huggingface/llm-ls.git
cd llm-ls
# 2. 编译(使用 Release 模式以获得最高性能)
# 如果你想针对你的 CPU 优化,可以添加 RUSTFLAGS
RUSTFLAGS="-C target-cpu=native" cargo build --release
编译生成的二进制文件是 target/release/llm-ls ,将这个文件复制到 /usr/local/bin/ 目录下
配置helix
排查
我在编辑 go 程序,发现没有出现 llm-ls 提示内容
由于 /usr/local/bin/llm-ls --help 显示只支持有限的参数:
Usage: llm-ls [OPTIONS]
Options:
--port <SOCKET> Wether to use a tcp socket for data transfer
-s, --stdio Wether to use stdio transport for data transfer, ignored because it is the default behaviour
-h, --help Print help
-V, --version Print version
所以尝试便携一个 /tmp/debug_llm.sh 脚本:
#!/bin/sh
# 显式导出路径
export PATH=$PATH:/usr/local/bin:/home/admin/go/bin
# 将 llm-ls 的 stderr 捕获到文件
/usr/local/bin/llm-ls "$@" 2> /tmp/llm-ls.err
设置脚本可执行 chmod +x /tmp/debug_llm.sh
然后修订 ~/.config/helix/languages.toml 如下:
[language-server.llm-ls]
command = "/tmp/debug_llm.sh"
其他配置内容不变
此时helix报错也可以通过 cat /tmp/llm-ls.err 获得 llm-ls 崩溃前的最后输出
再次编辑 go 程序,我发现 ~/.cache/helix/helix.log 显示 llm-ls 确实初始化失败:
2026-03-24T16:18:19.960 helix_lsp::transport [ERROR] llm-ls err: <- StreamClosed
2026-03-24T16:18:19.960 helix_lsp [ERROR] failed to initialize language server: server closed the stream
而 /tmp/llm-ls.err 居然提示是参数错误:
error: unexpected argument '--log-file' found
Usage: llm-ls [OPTIONS]
For more information, try '--help'.
不过上述报错实际上是我配置文件问题,改正之后还是没有解决helix访问llm-ls问题
gemini提供了一个debug建议,采用FreeBSD的 truss :
truss /usr/local/bin/llm-ls --stdio
此时输入一些字符回车,程序会退出并打印出最后的系统调用(来查看问题所在)
...
_umtx_op(0x22d9151e62c0,UMTX_OP_RW_UNLOCK,0x0,0x0,0x0) = 0 (0x0)
_umtx_op(0x22d9151e62c0,UMTX_OP_RW_WRLOCK,0x0,0x0,0x0) = 0 (0x0)
_umtx_op(0x22d9151e62c0,UMTX_OP_RW_UNLOCK,0x0,0x0,0x0) = 0 (0x0)
_umtx_op(0x22d9151e62c0,UMTX_OP_RW_WRLOCK,0x0,0x0,0x0) = 0 (0x0)
_umtx_op(0x22d9151e62c0,UMTX_OP_RW_UNLOCK,0x0,0x0,0x0) = 0 (0x0)
_umtx_op(0x22d9151e62c0,UMTX_OP_RW_WRLOCK,0x0,0x0,0x0) = 0 (0x0)
_umtx_op(0x22d9151e6240,UMTX_OP_RW_WRLOCK,0x0,0x0,0x0) = 0 (0x0)
madvise(0x4e83d5a00000,12288,MADV_FREE) = 0 (0x0)
_umtx_op(0x22d9151e62c0,UMTX_OP_RW_WRLOCK,0x0,0x0,0x0) = 0 (0x0)
madvise(0x4e83d6000000,12288,MADV_FREE) = 0 (0x0)
_umtx_op(0x22d9151e6240,UMTX_OP_RW_UNLOCK,0x0,0x0,0x0) = 0 (0x0)
_umtx_op(0x22d9151e6240,UMTX_OP_RW_WRLOCK,0x0,0x0,0x0) = 0 (0x0)
<thread 164006 exited>
<thread 164007 exited>
_umtx_op(0x22d9151e62c0,UMTX_OP_RW_UNLOCK,0x0,0x0,0x0) = 0 (0x0)
madvise(0x4e83d6600000,12288,MADV_FREE) = 0 (0x0)
<thread 164009 exited>
madvise(0x4e83d5800000,8192,MADV_FREE) = 0 (0x0)
madvise(0x4e83d5804000,16384,MADV_FREE) = 0 (0x0)
<thread 164005 exited>
_umtx_op(0x4e83d4866810,UMTX_OP_WAIT,0x280a5,0x0,0x0) = 0 (0x0)
madvise(0x4e83d6400000,12288,MADV_FREE) = 0 (0x0)
<thread 164008 exited>
_umtx_op(0x4e83d486b010,UMTX_OP_WAIT,0x280a8,0x0,0x0) = 0 (0x0)
sigaltstack(0x22d91256b6b8,0x0) = 0 (0x0)
munmap(0x4e83d4887000,38912) = 0 (0x0)
_exit(0x0)
process exit, rval = 0
日志中有密集的 _umtx_op(..., UMTX_OP_RW_WRLOCK, ...) 和 UNLOCK ,显示 llm-ls 的一部运行时(Tokio)在FreeBSD 15的Jail环境下,尝试获取读写锁时候陷入某种争用或异常。
gemini建议我为Jail设置 allow.sysvipc ,所以我尝试修改 /etc/jail.conf 添加:
allow.sysvipc...
# PERMISSIONS
allow.sysvipc;
allow.raw_sockets;
exec.clean;
mount.devfs;
...
我尝试设置后重启jail(实际上我是重启了操作系统),但是依然没有解决llm-ls向 ollama 发送请求的问题(实际上根本没有发送请求)
此外,gemini还有一个建议是尝试限制 Rust 程序的多线程,尝试用一个现成来避免死锁。也就是修订 ~/.config/helix/languages.toml :
[language-server.llm-ls]
command = "env"
# 强制 Rust 运行时只使用 1 个线程,避开复杂的线程锁争用
args = ["RAYON_NUM_THREADS=1", "TOKIO_WORKER_THREADS=1", "/usr/local/bin/llm-ls", "--stdio"]
[language-server.llm-ls.config]
# 之前的配置保持不变...
url = "http://192.168.6.200:11434/api/generate"
model = "qwen3-coder-30b-a3b-instruct-q4_k_m:latest"
但是我实践发现并没有解决问题
备注
遗留问题
目前我没有解决 FreeBSD 通过 llm-ls 调用Ollsms来实现AI辅助编程,问题出在FreeBSD的客户端 llm-ls 没有能够发起调用。
gemini推测可能是rust线程在FreeBSD上兼容的问题,我暂时没有时间继续排查。我准备先搞通Linux平台的AI辅助编程,至少 llm-ls 官方是支持Linux平台的,能够排除客户端不兼容的可能。
待续...`