news 2026/9/6 0:13:08

LSP与LLM结合:为AI编程助手注入语义上下文

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LSP与LLM结合:为AI编程助手注入语义上下文

LSPs for LLMs 这个题目,最近在 AI 辅助编程和 LLM Agent 方向讨论得越来越多。LSP 本意是 Language Server Protocol,也就是语言服务器协议,它负责让编辑器拿到类型、补全、诊断、跳转定义这些 IDE 级能力。LLM 则是大语言模型。把两组词放在一起,指的不是协议本身变成了大模型,而是一条实用工程路径:当 LLM 需要理解或修改代码时,不要只把文件文本和选中片段塞进 Prompt,而是先通过 LSP 从语言服务器取回结构化的语义信息,再把这些信息作为上下文交给模型。

很多编程助手给人的第一印象是“不够聪明”,其实不一定是模型能力不足,而是上下文太薄。模型拿到的内容往往只是当前文件里的一段代码,看不到函数定义的类型、调用点、诊断信息、引用关系。它只能靠自己训练时积累的先验知识去猜。而 LSP 背后是一套已经成熟的编译器级代码分析能力,恰好能补上这块空白。

这篇文章会用一个本地可跑的最小 Demo 演示整条链路:安装 pyright 语言服务器,用 Python 写一个极简 LSP 客户端,从当前文件里取出诊断和符号信息,组装成 Prompt,再调用一个 OpenAI 兼容接口拿到修复建议。读者对象是正在开发编程助手、IDE 插件、LLM Agent 工具链的开发者。

1. LSP 与 LLM 的组合到底解决什么问题

1.1 LSP 先解决了编辑器与语言服务器之间的通信问题

LSP 最早要解决的是编辑器生态分裂的问题。在 LSP 出现之前,一种语言要在一个编辑器里获得代码补全、跳转、诊断能力,通常需要为该编辑器单独写插件。编辑器数量一多,维护成本会迅速膨胀。

LSP 的思路是把“语言智能”从编辑器里抽出来,放进一个独立进程,称为语言服务器。编辑器是客户端,语言服务器是服务端,双方通过标准协议通信。协议底层的传输格式是 JSON-RPC 2.0,所有请求、通知、响应都是结构化 JSON。

一份最典型的 LSP 交互流程大致是这样的:

客户端 -> 服务器:initialize 请求 服务器 -> 客户端:initialize 响应,告知服务器支持哪些能力 客户端 -> 服务器:initialized 通知 客户端 -> 服务器:textDocument/didOpen 通知,告诉服务器文件内容 客户端 -> 服务器:textDocument/hover 或 textDocument/definition 等请求 客户端 -> 服务器:shutdown 请求 客户端 -> 服务器:exit 通知

对 LLM 应用来说,LSP 最大价值在于:你不需要自己去解析 AST、做类型推导、维护跨文件索引。语言服务器已经把这件事做了很多年,并且做得比普通脚本更可靠。你只需要把它的结果读出来,组织成 Prompt 或工具调用结果即可。

1.2 纯文本上下文会让 LLM 丢失太多语义

现在很多 AI 编程功能的实现方式是:用户在编辑器里选中一段代码,插件把文本发给模型,问“这段代码有没有问题”或“帮我改一下”。这种方式在简单场景下有效,但只要代码涉及类型、跨文件调用、编译错误,纯文本就明显不够。

举个例子,给你看两行代码:

def add(a, b): return a + b result = add(1, "2")

模型大概能猜出这里有问题,但它不知道类型检查器具体报了什么,不知道add是在哪个类里定义的,不知道这个函数在同一个项目里被其他模块以什么类型调用。如果 prompt 里只给这段文本,模型给出的建议很容易变得空泛,比如“请确保传入相同类型”之类。

换成 LSP 增强后的上下文,你可以在 Prompt 中直接给出这类信息:

pyright 诊断: 第 5 行,第 10 列: Argument of type "str" cannot be assigned to parameter "b" of type "int" in function "add"

模型看到的是确定的问题描述,而不是靠猜。它可以直接从“类型不匹配”这个结论出发,给出把"2"改为2或对b做类型转换的方案。这就是 LSP 对 LLM 的最直接价值:把“模型需要推断的信息”变成“语言服务器已经计算好的信息”。

1.3 LSP 能为 LLM 提供的信息类型

LSP 并不是只提供诊断。一个完整的语言服务器可以暴露很多能力,通常通过初始化时返回的capabilities字段声明。

下面这张表整理了与 LLM 上下文构建关系最密切的几类 LSP 能力:

LSP 方法返回的信息对 LLM 的价值典型使用场景
textDocument/diagnostictextDocument/publishDiagnostics错误、警告、提示信息让模型知道代码当前有哪些确定问题代码评审、自动修复
textDocument/documentSymbol文件中的类、函数、变量符号树让模型快速了解文件结构和职责代码概览、重构、补全
textDocument/hover符号的类型签名和文档注释让模型获得准确类型,减少猜测分析函数行为、生成调用代码
textDocument/definition符号定义的位置让模型跳转到真正的实现跨文件分析、代码修复定位
textDocument/references符号被引用的位置列表让模型知道改动影响范围重构、影响面分析
textDocument/completion当前位置可用的补全候选结合模型生成更符合上下文的代码代码生成、自动补全

这里要注意的是,LSP 返回的是结构化 JSON,这对 LLM 场景很友好。你可以直接把它转成 JSON 片段塞进 Prompt,也可以先做过滤,只保留诊断和符号。相比直接塞整段源文件,结构化信息在 Token 利用率和准确率上都有优势。

2. 准备工作区:依赖、测试项目和 LSP 通信基础

2.1 为什么选择 pyright 作为第一个接入的语言服务器

学习 LSP 有一个心理门槛:不想从零开始写语言服务器,又怕协议太复杂。这里建议先把现成语言服务器作为“黑盒”接入,等跑通了再深入协议内部。

选择 pyright 有几个原因。第一,它的类型检查能力很强,产生的诊断信息非常清晰。第二,Python 测试项目足够简单,前后代码都不需要额外编译。第三,pyright 的包发布在 PyPI 上,用pip安装即可,不需要在一开始接触 Node.js 工具链。

同一个思路也适用于其他语言,比如 C/C++ 用 clangd,Go 用 gopls,Java 用 jdtls。它们都遵循同一套 LSP 协议,换语言只是换启动命令和文件 URI,核心链路不变。

2.2 安装依赖并验证

先准备一个干净的工作目录,然后安装 pyright。

python -m pip install pyright

安装完成后做两步验证:

pyright --version which pyright-langserver

如果pyright --version能正常输出版本号,说明主命令可用。which pyright-langserver用于确认语言服务器二进制也在 PATH 中,后面 Python 客户端需要通过它启动子进程。

环境要求可以参考这张表:

项目最低要求说明
操作系统Linux、macOS 或 Windows不同平台主要影响 file URI 写法
Python3.10 或更高文章示例使用现代类型和语法
pyright最新稳定版通过pip install pyright安装
网络可访问 package index安装 Python 依赖时需要

2.3 创建一个带语义信息的测试项目

在同一个目录下创建sample_project,里面放两个文件。

sample_project/ calculator.py main.py

calculator.py定义了一个简单的计算器类:

class Calculator: def add(self, a: int, b: int) -> int: return a + b def multiply(self, a: int, b: int) -> int: return a * b

main.py故意写了一个类型错误,用来验证 LSP 诊断能否被读到:

from calculator import Calculator def main() -> None: calc = Calculator() result = calc.add(1, "2") print(result) if __name__ == "__main__": main()

先不用 LSP,直接在命令行运行静态检查,确认 pyright 能看到这个类型错误:

pyright sample_project

正常情况下会看到类似这样的输出:

sample_project/main.py:6:19 - error: Argument of type "str" cannot be assigned to parameter "b" of type "int" in function "add"

这一步很关键。如果命令行静态检查已经看不到错误,后面 LSP 客户端拿不到诊断,问题出在文件本身或 pyright 配置,而不是协议交互。

2.4 JSON-RPC 消息格式和 Content-Length 帧

LSP 客户端和语言服务器之间通过标准输入和标准输出通信,但并不是一行一个 JSON。每个消息都有固定的帧格式:header 加 body。

header 里最重要的字段是Content-Length,表示 body 的字节数。body 是 JSON 文本。一个完整的消息长这样:

Content-Length: 152\r\n \r\n {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"processId":null,"rootUri":"file:///path/to/sample_project
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 3:54:59

Dopamine越狱工具深度解析:rootless如何重塑iOS 15系统隔离架构

越狱圈这几年的节奏,确实比早几年安静了很多。但 2023 年春天,一个叫 Dopamine 的项目突然打破了这种沉默。它由知名开发者 opa334 发布,面向 iOS 15.0 到 15.4.1 的 A12 及以上设备,但真正让技术社区兴奋的,并不是&qu…

作者头像 李华
网站建设 2026/9/1 8:40:11

程序员也迎“高考”?我出的一套《程序员卷》完整复盘

说实话,每年六月那几天,我的朋友圈总会被各种“高考作文题”“高考数学压轴题”刷屏。作为一个写了快十年代码、混过外包也进过大厂、白天改Bug晚上接私活的程序员,我经常忍不住想:如果程序员也有一场全国统一考试,卷子…

作者头像 李华
网站建设 2026/8/31 9:11:06

腾讯云从业者真题zip解压全攻略:修复、密码与备考用法

简介:Zip压缩包是网络资料分发中最常见的文件形式,尤其在下载备考资料、跨平台传输场景中,用户常遇到解压失败、文件名乱码、密码保护或分卷合并等问题。这些问题的根源往往不在于文件本身,而在于zip格式的中央目录(EO…

作者头像 李华
网站建设 2026/9/1 4:03:39

AI写基因组:用语言模型设计新型噬菌体

如果 AI 不仅能写代码、写文章,还能直接写出一段几十 kb 的病毒基因组,并且这段 DNA 被合成之后,在实验室里真的能组装成具有感染能力的噬菌体颗粒——你会怎么评估这件事的分量?这正是斯坦福大学等机构发表在 Science 上的工作给…

作者头像 李华
网站建设 2026/8/31 9:07:45

Python零基础入门:从基础语法到爬虫与数据分析实战

1. 背景与核心概念1.1 为什么 Python 适合零基础入门很多刚开始学编程的朋友都有过类似困扰:网上资料太多、知识点太散、教程之间重复度高,今天看一集变量、明天看一集循环,学了一个月还是只能打印字符串。Python 之所以适合零基础入门&#…

作者头像 李华
网站建设 2026/8/31 22:55:37

当拆解过AI Berkshire后,原来用的AI投研结论都像在说“正确的废话”

作 者:老余捞鱼 原创不易,转载请标明出处及原作者。 文中示例仅用于技术讨论,不构成任何操作建议。 量化策略开发应以学习和技术交流为目的。 本号不荐股、不卖课、不承诺收益。 市场有风险,请合法合规投资。 本文约 2800 字 | 预计阅读 5-8 分钟,后有开源项目地址,建议先…

作者头像 李华