openJiuwen agent-core LSP 深度指南:用 LspRail 为 DeepAgent 赋予代码导航与诊断注入能力
人工智能AI AgentAgent 框架大模型工具调用RAG提示工程强化学习【免费下载链接】agent-coreopenJiuwen agent-core可提供AI Agent开发、运行、调优与演进相关的全套SDK能力项目地址https://gitcode.com/openJiuwen/agent-core点击查看免费下载本文是一份面向 AI Agent 开发者的技术实战指南围绕 openJiuwen agent-core 中的LspRail与 Language Server ProtocolLSP集成展开只需一行rails[LspRail()]即可让DeepAgent获得lsp工具——跳转定义、查找引用、列出文件符号、全局符号搜索、调用层级分析并在编辑文件后自动重分析、自动把 pyright 等语言服务器的诊断注入下一轮 LLM 上下文。读完本文你将掌握 LSP 子系统的完整生命周期、lsp工具的全部操作与参数语义、自动诊断注入流水线的原理以及如何通过InitializeOptions/CustomServerConfig定制语言服务器。前置条件在运行任何示例前需要先安装目标语言对应的语言服务器并配置 LLM 运行所需的环境变量。以 Python 的 pyright 为例可用以下任一方式安装# npm推荐 npm install -g pyright # pip pip install pyright # pipx隔离安装 pipx install pyright设置 LLM 所需的环境变量export API_KEY... export API_BASEhttps://api.openai.com/v1 export MODEL_NAMEgpt-5.2 export MODEL_PROVIDEROpenAI说明仓库内示例如 examples/lsp/deep_agent_lsp_demo.py默认从API_KEY、API_BASE、MODEL_NAME三个环境变量读取模型配置未设置时使用占位字符串。完整示例9 个 Demo 一次跑通仓库提供了可运行的全流程端到端示例 examples/lsp/deep_agent_lsp_demo.py覆盖 8 个 LSP 代码导航操作和 1 个自动诊断注入闭环Demo操作说明1goToDefinition跳转到函数的定义2findReferences查找符号的所有使用位置3documentSymbol列出文件内全部符号4workspaceSymbol跨整个项目搜索符号5goToImplementation查找抽象方法的具体实现6prepareCallHierarchy准备调用层级条目7incomingCalls查找调用某函数的所有调用方8outgoingCalls查找某函数调用的所有下游函数9before_model_call诊断注入edit_file→ 自动触发 LSP → 自动注入诊断 → Agent 修复全部错误运行方式需已安装 pyright 并配置好 API 环境变量uv run python examples/lsp/deep_agent_lsp_demo.py示例以examples/lsp/sample_code/为分析对象其中 test.py 故意包含类型错误如x: int not_an_integer用于 Demo 9 验证 Agent 的读取 → 编辑 → 诊断 → 修复闭环。示例还通过 diagnostic_params.py 提供的wait_for_diagnostics在 Demo 9 结束后等待 pyright 最终诊断确认诊断队列为空才算通过。工作原理LspRail 如何接入 Agent 生命周期LspRail是DeepAgentRail的一个具体实现源码见 openjiuwen/harness/rails/lsp_rail.py其priority 60中高优先级。它将 LSP 子系统接线进 Agent 的四个生命周期事件生命周期事件LspRail的行为init()— Agent 启动解析 LSP 选项并把LspTool注册到 Agent 的ability_manager上Agent 运行中LLM 可自由调用lsp工具语言服务器在首次请求时才懒加载启动publishDiagnostics通知被缓冲进LspDiagnosticRegistryafter_tool_call—edit_file/write_file之后向语言服务器发送textDocument/didChange触发重新分析新诊断异步fire-and-forget缓冲before_model_call— 每次 LLM 调用前异步初始化LSPServerManager首次调用前随后排空缓冲诊断并作为UserMessage注入LLM 无需显式调用诊断工具即可看到错误uninit()— Agent 停止从ability_manager移除LspTool关闭所有语言服务器进程从源码看init()中会做以下几件关键事lsp_rail.py类型守卫仅当 agent 是DeepAgent且存在deep_config、ability_manager时才生效否则仅记录 warning 并跳过cwd 解析优先级options.cwdworkspace.root_path最终写入InitializeOptionsverbose 日志文件若verboseTrue会在仓库根目录的logs/logs/lsp/下创建lsp_YYYYMMDD_HHMMSS.logUTC 时间戳每次运行新建异步初始化_start_lsp_initialization()通过loop.create_task启动_ensure_lsp_initialized()内含 15 秒超时保护避免阻塞主事件循环。LspRail()无参数时自动继承 Agent 的workspace路径并使用默认语言服务器设置。Agent 侧只看到一个名为lsp的工具其描述与完整操作 Schema 会通过工具元数据注册表自动注入系统提示词无需手动编写。快速开始给 DeepAgent 挂载 LSP最小可运行示例LspRail()无参数即可继承 workspaceimport asyncio import os from openjiuwen.core.foundation.llm import init_model from openjiuwen.core.single_agent.schema.agent_card import AgentCard from openjiuwen.core.runner import Runner from openjiuwen.harness.factory import create_deep_agent from openjiuwen.harness.rails.lsp_rail import LspRail from openjiuwen.harness.lsp import shutdown_lsp _API_KEY os.getenv(API_KEY, your api key here) _MODEL_NAME os.getenv(MODEL_NAME, your model here) _API_BASE os.getenv(API_BASE, your api base here) async def main(): await Runner.start() model init_model( providerOpenAI, model_name_MODEL_NAME, api_key_API_KEY, api_base_API_BASE, ) agent create_deep_agent( modelmodel, cardAgentCard( namecode_navigator, descriptionNavigates source code via LSP., ), system_promptYou are a code-navigation assistant. Use the lsp tool to answer questions., rails[LspRail()], # -- one line to add LSP workspace/path/to/repo, ) try: result await Runner.run_agent( agent, {query: What classes are defined in src/models.py?}, ) print(result) finally: await shutdown_lsp() await Runner.stop() asyncio.run(main())两点注意记得在finally中调用await shutdown_lsp()关闭所有语言服务器进程对应LspRail.uninit()中的清理逻辑含 10 秒超时保护见 lsp_rail.pyshutdown_lsp与initialize_lsp、get_pending_lsp_diagnostics、get_lsp_status等都在 openjiuwen/harness/lsp/init.py 中作为公共 API 导出。开启自动诊断注入仅挂LspRail只能获得代码导航能力要激活编辑文件 → 自动重分析 → 自动注入诊断的闭环需要同时挂载SysOperationRail它提供edit_file/write_file工具from openjiuwen.harness.rails.sys_operation_rail import SysOperationRail from openjiuwen.harness.rails.lsp_rail import LspRail from openjiuwen.harness.lsp import InitializeOptions rails [ SysOperationRail(), # provides edit_file / write_file LspRail(optionsInitializeOptions(cwd/path/to/repo), verboseTrue), # LSP auto-inject ]两个 rail 同时生效后每次edit_file/write_file调用都会自动触发语言服务器重新分析产生的诊断自动注入下一轮 LLM 上下文——Agent 全程无需显式调用任何诊断工具。自动诊断注入流水线深度解析LspRail通过两个互补的生命周期钩子构成编辑后的连续反馈回路。after_tool_call— 触发重分析每次 Agent 调用edit_file或write_file后LspRail.after_tool_call自动触发lsp_rail.py从ctx.inputs读取tool_name只有当其命中内部集合_WRITE_TOOL_NAMES {edit_file, write_file}时才继续解析编辑文件的绝对路径——相对路径按workspace.root_path或当前工作目录解析在调用线程上提前 resolve避免协程内 cwd 变化导致路径失效按扩展名推断language_id.py/.pyi映射为python其余取去掉点号的扩展名若文件从未打开过先发textDocument/didOpen再发textDocument/didChange——因为部分服务器如 pyright要求先didOpen才接受didChange立即返回重新分析为 fire-and-forgetpyright 异步运行并发布publishDiagnostics通知由LspDiagnosticRegistry缓冲。无需任何配置——只要挂载LspRail该钩子即默认生效。before_model_call— 把诊断注入上下文每次 LLM 调用前LspRail.before_model_call会调用get_pending_lsp_diagnostics()排空诊断注册表若有待交付诊断则格式化为文本并 append 一条UserMessage到消息列表lsp_rail.py。注入消息形如[LSP Diagnostics] The following issues were detected after the last file edit: File: src/models.py [Error] line 12, col 15 (reportArgumentType) Argument of type str cannot be assigned to parameter age of type int Please review and fix these issues.格式化逻辑由_format_diagnostics实现严重级别映射为1Error, 2Warning, 3Info, 4Hint行/列号由 LSP 的 0-indexed 转回 1-indexed并附带诊断 code。Agent 看到错误后直接修复无需显式调用诊断工具循环持续直到诊断队列为空。Verbose 日志给LspRail传verboseTrue每次before_model_call的诊断快照都会追加写入带时间戳的日志文件LspRail(verboseTrue)日志路径为仓库根目录下的logs/logs/lsp/lsp_YYYYMMDD_HHMMSS.log每次运行新建UTC 时间。内容包含[before_model_call] server... local_path...头以及每条诊断的[级别] line x, col y (code) 消息非常适合离线调试多轮修复循环。lsp工具10 个操作完整参考LspRail激活后LLM 可调用lsp工具执行下述操作。所有位置参数line、character均为1-indexed与编辑器显示一致工具会在发送给语言服务器前转换为 0-indexed见 openjiuwen/harness/tools/lsp_tool/_schemas.py 中ge1的字段约束以及 _tool.py 中validated.line - 1的转换逻辑。导航操作操作LSP 方法documentSymboltextDocument/documentSymbolgoToDefinitiontextDocument/definitionfindReferencestextDocument/referencesworkspaceSymbolworkspace/symbolgoToImplementationtextDocument/implementationprepareCallHierarchytextDocument/prepareCallHierarchyincomingCallscallHierarchy/incomingCallsoutgoingCallscallHierarchy/outgoingCalls诊断操作操作用途changeFile发送textDocument/didChange通知服务器新内容触发重新分析getDiagnostics排空缓冲的publishDiagnostics通知返回格式化后的错误/警告操作到 LSP 方法的映射集中在_operation_to_method()openjiuwen/harness/tools/lsp_tool/_tool.py。操作定义使用 Pydantic discriminated union 校验LspOperation枚举非法输入会直接返回Invalid input错误。documentSymbol— 列出文件内全部符号返回单个文件中定义的所有类、函数、方法、变量。无需位置参数。{ operation: documentSymbol, file_path: src/models.py }goToDefinition— 跳转符号定义{ operation: goToDefinition, file_path: src/app.py, line: 42, character: 15 }findReferences— 查找符号全部引用{ operation: findReferences, file_path: src/models.py, line: 10, character: 7, include_declaration: True # include the definition site in results (default: True) }该操作会把includeDeclaration写进 LSP 请求的context字段见_build_lsp_params。workspaceSymbol— 全项目搜索符号{ operation: workspaceSymbol, file_path: , # can be empty for workspace-wide search query: UserRepository }goToImplementation— 查找抽象方法实现{ operation: goToImplementation, file_path: src/base.py, line: 20, character: 9 }注意并非所有语言服务器都实现此操作例如 pyright 不支持textDocument/implementation。工具对不支持的操作会返回明确的错误信息识别Unhandled method或-32601错误码而不是静默失败见 openjiuwen/harness/tools/lsp_tool/_tool.py。prepareCallHierarchy— 解析符号为调用层级条目{ operation: prepareCallHierarchy, file_path: src/services.py, line: 55, character: 5 }incomingCalls— 查找函数的所有调用方工具会自动先执行prepareCallHierarchy因此只需提供位置{ operation: incomingCalls, file_path: src/services.py, line: 55, character: 5 }outgoingCalls— 查找函数调用的下游函数{ operation: outgoingCalls, file_path: src/services.py, line: 55, character: 5 }底层实现incomingCalls/outgoingCalls需要 LSP 请求中的item字段是CallHierarchyItem而非textDocument position。因此call_lsp_tool会先发textDocument/prepareCallHierarchy拿到条目再把参数替换为{item: call_item}后发callHierarchy/incomingCalls/callHierarchy/outgoingCalls见 openjiuwen/harness/tools/lsp_tool/_tool.py。changeFile— 通知服务器文件内容变化发送textDocument/didOpen若文件尚未打开后跟textDocument/didChange。语言服务器重新分析新内容并发布publishDiagnostics通知缓冲供下一次getDiagnostics使用。{ operation: changeFile, file_path: src/models.py, content: class User:\n name: str\n age: int\n # full file text }content必须是文件的完整新文本full-sync 模式不做 diff。这与LSPServerManager.change_file的实现一致——contentChanges仅含{text: content}、无range字段见 openjiuwen/harness/lsp/core/manager.py文件未预先打开时版本号从 1 开始每次didChange递增。getDiagnostics— 取回缓冲诊断排空自上次调用以来所有待交付的publishDiagnostics通知返回按严重级别排序的格式化列表。每次调用还会跨批次去重避免同一条错误重复出现。# All files with pending diagnostics { operation: getDiagnostics, file_path: } # Filtered to a single file { operation: getDiagnostics, file_path: src/models.py }诊断工作流显式与自动两种模式显式工作流通过lsp工具典型的显式检查模式changeFile → (wait for server re-analysis) → getDiagnostics示例 Agent 提示词result await Runner.run_agent( agent, { query: ( Change src/models.py so that the age field is typed as str instead of int, then get the diagnostics to see if pyright reports any type errors. ) }, )Agent 将依次调用lsp(operationchangeFile, file_pathsrc/models.py, content...)— 把新内容发给 pyrightlsp(operationgetDiagnostics, file_pathsrc/models.py)— 取回类型错误。自动工作流after_tool_callbefore_model_call同时挂载SysOperationRail后Agent 用edit_file编辑文件即可自动收到诊断反馈无需调用changeFile/getDiagnosticsresult await Runner.run_agent( agent, { query: ( Read src/models.py and fix all type errors. Keep editing until no errors remain. ) }, )每次edit_file触发after_tool_call向 pyright 发送textDocument/didChange下一轮 LLM 调用前before_model_call把新诊断作为UserMessage注入。Agent 看到错误后继续修复直至诊断队列清空。上限与去重配置默认值单文件最大诊断数Max diagnostics per file10诊断总数上限Max diagnostics total30诊断在应用上限前按严重级别排序Error → Warning → Info → Hint。跨调用去重会抑制上一轮已交付过的条目。这些语义在 openjiuwen/harness/lsp/core/diagnostic_registry.py 中完整实现LspDiagnosticRegistry是进程级单例内部维护_pendingUUID → 通知批次与_delivereduri → 已交付 key 集合两个结构。get_and_clear()依次执行按 URI 合并批次 → 批内去重key 由message|severity|line:char|code构成→ 跨轮去重 → 严重级别升序排序Error1 在前→ 单文件截断 → 全局截断 → 记录已交付历史。由于通知回调与get_and_clear都在 asyncio 事件循环线程执行无需额外加锁。自定义语言服务器InitializeOptions 与 CustomServerConfig通过InitializeOptions可覆盖默认服务器配置from openjiuwen.harness.lsp import InitializeOptions, CustomServerConfig rail LspRail( optionsInitializeOptions( cwd/path/to/repo, custom_servers{ pyright: CustomServerConfig( command/usr/local/bin/pyright-langserver, args[--stdio], env{PYRIGHT_PYTHON_PATH: /usr/bin/python3}, ) }, ), verboseTrue, # write diagnostic snapshots to logs/logs/lsp/ )LspRail构造参数参数类型默认值说明optionsInitializeOptions \| NoneNoneLSP 初始化选项cwd、自定义服务器默认继承 Agent 的 workspaceverboseboolFalse为True时每次before_model_call的诊断快照写入logs/logs/lsp/lsp_YYYYMMDD_HHMMSS.logCustomServerConfig字段字段类型默认值说明commandstr \| NoneNone服务器可执行文件路径argslist[str] \| NoneNone命令行参数envdict[str, str] \| NoneNone额外环境变量extensionslist[str] \| NoneNone要处理的文件扩展名如[.py]language_idstr \| NoneNoneLSP 语言标识符initialization_optionsdict \| NoneNone在initialize请求中传给服务器的选项disabledboolFalse设为True可完全禁用该服务器InitializeOptions字段字段类型默认值说明cwdstr \| NoneNone工作目录默认取 Agent 的 workspacecustom_serversdict[str, CustomServerConfig] \| NoneNone按服务器 ID 键控的逐服务器覆盖配置以上字段定义与 openjiuwen/harness/lsp/types.py 中的 dataclass 完全一致。自定义配置的合并逻辑见 openjiuwen/harness/lsp/servers/registry.pydisabledTrue的服务器会从配置列表中移除若服务器 ID 已存在内置服务器则仅覆盖command/args/initialization_options非空字段若不存在则按extensionslanguage_id新建配置。内置语言服务器支持服务器 ID语言文件扩展名可执行文件pyrightPython.py,.pyipyright-langservertypescriptTypeScript / JavaScript.ts,.tsx,.js,.jsxtypescript-language-serverrustRust.rsrust-analyzergoGo.gogoplsjavaJava.javajdtls内置服务器定义注册在BUILTIN_SERVERS全局注册表中见 openjiuwen/harness/lsp/servers/registry.py 及各语言的 servers/servers/python.py、typescript.py、rust.py、go.py、java.py。初始化时若找不到某个服务器二进制该服务器会被静默跳过注册为 command 为空的占位配置其余语言服务器不受影响。以 pyright 为例其解析逻辑_resolve_pyright_command在 Windows 上通过npm list -g --depth0 pyright获取全局 npm 前缀再构造node prefix/node_modules/pyright/langserver.index.js --stdio失败时回退到解析pyright-langserver.cmd包装脚本。同时_spawn_python会自动探测VIRTUAL_ENV、.venv、venv中的 Python 解释器并把pythonPath写入initialization_options让 pyright 正确解析项目虚拟环境。项目根目录发现支持 monorepoLSPServerManager采用扩展名 → 服务器 → 项目根的多级匹配见 openjiuwen/harness/lsp/core/manager.py按文件扩展名匹配候选服务器 ID 列表调用各服务器的find_root由nearest_root生成向上遍历目录寻找最近的包含pyproject.toml、setup.py、requirements.txt等标记文件的目录遇到.git目录则停止以(server_id, root)构成缓存键ServerInstanceKey从而让同一语言在不同子项目中各自拥有独立服务器实例。服务器启动过程包含完整的 LSP 握手等待与崩溃恢复实例以startup_timeout默认 45 秒等待启动完成僵尸实例running 但失联和 ERROR 实例会被清理并重启启动超时会取消任务并清理缓存条目防止残留僵尸缓存。所有服务器实例的启动、通知、请求统一由LSPServerInstance管理与LSPServerManager.send_request分发。无 Agent 场景底层initialize_lsp/call_lsp_toolAPI除了通过DeepAgentLspRail使用LSP 子系统也提供底层 API可直接在非 Agent 流程中调用initialize_lsp(options)初始化 LSP 子系统幂等、懒加载混合模式。初始化只构建配置映射服务器在首次 LSP 请求时才启动。实现位于 openjiuwen/harness/lsp/init.py底层走LSPServerManager.initialize。shutdown_lsp()关闭整个 LSP 子系统停止所有服务器进程含 5 秒的 spawn 任务取消等待。get_pending_lsp_diagnostics(max_per_file10, max_total30)从全局注册表读取并清空待交付诊断。get_lsp_status()返回LspStatus(initialized, servers)列出各服务器 ID、运行状态、根目录、崩溃次数与最近错误。call_lsp_tool(input_data, workspaceNone, operationNone)底层执行入口openjiuwen/harness/tools/lsp_tool/_tool.pyLspTool.invoke即封装它。它负责Pydantic 校验输入 → 解析/校验文件路径含 workspace sandbox 边界检查→ 若管理器未初始化则自动补初始化 → 获取/启动服务器 → 自动didOpen文件 → 组装 LSP 参数1-indexed → 0-indexed→ 对导航类操作做 gitignore 过滤 → 格式化结果。从源码看call_lsp_tool还会自动过滤位于 gitignored 目录如node_modules、__pycache__中的导航结果filter_git_ignored_locations并跳过超过 10MBMAX_LSP_FILE_SIZE_BYTES的大文件避免把超大文件发送给语言服务器。这些限制同时在 openjiuwen/harness/prompts/tools/lsp_tool.py 的双语工具描述中显式告知 LLM。测试与验证LSP 集成相关的单元测试可直接作为行为契约参考tests/unit_tests/harness/rails/test_lsp_rail.py覆盖LspRail的初始化、LspTool注册与uninit清理使用_FakeDeepAgent与 mock 隔离外部依赖tests/unit_tests/harness/tools/test_lsp_tool.py 与 test_lsp_diagnostics.py覆盖工具输入校验、参数转换、诊断注册表去重/封顶/排序语义。通过这些测试与上述源码路径可以完整追溯LspRail从工具注册、懒加载启动、didOpen/didChange触发、publishDiagnostics缓冲到before_model_call注入UserMessage的整条链路。这也正是 openJiuwen agent-core 把代码导航与诊断反馈两种能力以声明式 Rail 形式注入 Agent 生命周期的核心设计。赞分享人工智能AI AgentAgent 框架大模型工具调用RAG提示工程强化学习【免费下载链接】agent-coreopenJiuwen agent-core可提供AI Agent开发、运行、调优与演进相关的全套SDK能力项目地址https://gitcode.com/openJiuwen/agent-core点击查看免费下载相关推荐MariaDB 新 Binlog 实现详解innodb 存储引擎下的文件格式、GTID 复制与平滑迁移MariaDB 新 Binlog 实现详解innodb 存储引擎下的文件格式、GTID 复制与平滑迁移 本文围绕 MariaDB 仓库中的 Docs/repl人工智能AI AgentAgent 框架大模型工具调用RAG提示工程强化学习K8sGPT终极指南如何用AI为Kubernetes集群赋予超级诊断能力K8sGPT终极指南如何用AI为Kubernetes集群赋予超级诊断能力 K8sGPT是一款革命性的AI工具专门为Kubernetes集群管理而生。这款开源云原生运维AI 应用MCP 服务OpenManus 工具系统深度指南从 BaseTool 到 ToolCollection为 Agent 赋予行动能力OpenManus 工具系统深度指南从 BaseTool 到 ToolCollection为 Agent 赋予行动能力 本篇技术指南以 docs/OpenM人工智能AI 应用AI Agent上一篇大模型基础教材 PDF 获取指南1 条命令一次拿全 8 份下一篇终极窗口置顶神器AlwaysOnTop 完整使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考