Go LLM镜头
    
MCP(模型上下文协议)服务器,使Claude等LLM能够通过完整的类型检查AST分析来导航和理解Go代码库。
LLM可以调用结构化工具来探索包、查找符号、检查函数签名和类型定义,以及发现接口实现,而不是逐行读取原始源文件。
主要目标是在不断重新学习代码库时节省令牌。
运作原理
索引器使用 golang.org/x/tools/go/packages 在启动时对整个代码库执行完整的类型检查加载,然后构建所有包、函数、类型、变量和常量的内存索引。索引由MCP工具查询,无需重新解析源文件。
在何处保存代币:
- 与其读取整个文件来查找函数,
get_function仅返回该函数的源代码 - 与其通过grepping+读取多个文件来理解一个类型,
get_type直接返回定义 find_implementations替换多步骤grep→ read → 解析工作流- 结构化结果比带有行号的原始文件内容更紧凑
- 项目记忆工具在会话中保存代码库知识——每次对话开始时都没有重复的方向
在没有多大帮助的地方:
- 在小文件中进行简单查找——对已知文件的读取具有可比性
- 需要理解周围环境(注释、相邻函数)的任务
- 该工具调用自身+其响应仍然会消耗令牌,因此非常小的查询会产生开销
更大的胜利可能是更少的往返——更少的黑暗搜索,更少的“读这个文件,现在读那个文件”链。这意味着总体上上下文积累较少,这就是令牌成本真正复合的地方。
基准测试
go llm镜头vs Glob/Grep——代币使用基准
- 任务: 描述示例代码库(github.com/tender bandar/gniot)
- 型号: claude-opus-4-6
- 跑: 3(冷基线——没有先前的项目记忆)
- 日期: 2026-02-23
结果
| 公制 | Glob/Grep | go llm镜头 |
|---|---|---|
| 有效标记(平均值±标准差)\* | 42900±3976 | 33356±965 |
| 成本美元(平均值±标准差) | $0.2545 ± $0.0208 | $0.1967 ± $0.0028 |
\* input + output + cache_read × 0.1 + cache_creation × 1.25 (反映Opus 4.6计费权重)
裁决
go llm镜头在冷会话中使用的有效代币减少了约22%,成本降低了约23%(节省了约0.06美元/次)。
一致性差距同样显著:透镜的变异系数约为3%,而Glob/Grep的变异系数为约9%。结构化工具方法采用可预测的路径——完成少数有针对性的调用、紧凑的结构化结果。Glob/Grep让模型每次都能即兴制定搜索策略,因此成本会随着它决定读取的文件数量而波动。
内存摊销
上面的数字反映了一次没有项目知识的会议。这 write_memory / list_memories 工具在重复的会话中会显著改变情况:第一次会话需要探索并写出其发现;后续课程阅读笔记,完全跳过重新发现。
在同一代码库上连续三次执行基准测试时观察到(Glob/Grep始终稳定在42000个有效令牌):
| 会话 | 镜头效果。代币(平均值) | 与Glob/Grep |
|---|---|---|
| 1--寒冷 | 33356 | -22% |
| 2--温暖 | ~25700 | ~-39% |
| 3--温暖 | ~14600 | ~-66% |
到第三次会话时,单个运行在短短几分钟内完成了相同的“描述代码库”任务 约8000个有效代币 --大约是冷Glob/Grep会话的5倍。
有无跑步 --no-memory 查看这种摊销效应对您自己的代码库的影响(见下文)。
备注
- 结果可能因任务类型而异;在小型代码库上进行简单的符号查找是Grep最具竞争力的地方,可以与go-llm镜头相匹配
- go-llm透镜化合物在更大的代码库和多步探索任务中的优势,其中Glob/Grep需要读取许多文件来构建上下文
运行自己的基准测试
tests/benchmark/compare-tokens.sh 背靠背跑两次 claude -p 会话——一个仅限于Glob/Grep,一个用于llm镜头——在同一任务上,然后打印并排的令牌和成本比较。
# Single comparison, keep raw JSON output:
./tests/benchmark/compare-tokens.sh --target ~/projects/mylib --keep "describe the codebase structure"
# Run 3 times each, report mean ± stddev (memories accumulate between lens runs):
./tests/benchmark/compare-tokens.sh --target ~/projects/mylib --runs 3 "describe the codebase structure"
# Same, but with memory tools disabled — isolates structural tool savings:
./tests/benchmark/compare-tokens.sh --target ~/projects/mylib --runs 3 --no-memory "describe the codebase structure"| 标志 | 默认值 | 描述 |
|---|---|---|
--model | claude-opus-4-6 | 用于两个会话的模型 |
--runs/-n | 1 | 每种方法的运行次数;当>1时,报告平均值±stddev |
--no-memory | off | 从镜头会话中排除内存工具;有助于将结构性工具节省与内存摊销隔离开来 |
--target/-t | . | 转到项目目录进行基准测试 |
--keep/-k | off | 保留原始JSON输出文件,而不是删除它们 |
要求: claude PATH中的CLI, jq,并在当前项目中将go llm lens配置为MCP服务器。
安全
自从引入人工智能辅助编码安全以来,似乎就成为了事后的想法。很多人理所当然地害怕将人工智能引入他们的工作流程。因此,我在这个项目中特别注意使用适当的安全措施。
go-llm-lens 设计用于与AI助手一起安全运行:
- 最小书写面积。 服务器只写入
.llm-lens/memories.json在项目根目录(项目内存工具)中。它从不执行shell命令或进行网络调用。所有其他操作都是只读的。
- 无网络表面。 交通工具仅限stdio。没有HTTP服务器,也没有开放端口。
- 范围至
--root. 索引器仅处理物理上位于您指定目录下的源文件。那棵树外的文件永远不会被读取。
- 最小的代币占用空间。 工具返回的结构化JSON仅包含LLM所需的字段——签名、类型、位置、文档注释——而不是原始源文件。默认情况下省略未导出的符号和函数体(
include_unexported/include_bodies选择加入)。这使得上下文窗口的使用是可预测的,并且无论代码库大小如何,都很小。
- 输入长度限制。 在任何处理程序逻辑运行之前,代码库查询工具的字符串参数上限为2048字节,以防止因输入过大而导致资源耗尽。记忆工具值没有上限,可以存储更长的笔记。
- 依赖漏洞扫描。 CI运行
govulncheck在每次推送时,都会捕获依赖关系中的已知CVE。
- 安全检查。
gosec在golangci lint配置中启用。
- 固定CI行动。 GitHub Actions的每个步骤都固定在一个不可变的提交SHA上,以防止通过可变标签进行供应链攻击。
# Verify signature
cosign verify-blob \
--certificate-identity-regexp='github.com/tender-barbarian/go-llm-lens' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
--bundle checksums.txt.bundle \
checksums.txt
# Verify SLSA provenance
gh attestation verify checksums.txt \
--repo tender-barbarian/go-llm-lens局限性
- 必须构建代码库。 完整类型检查需要编译代码。损坏的包裹会被跳过并发出警告。
- 依赖关系必须可用。 跑
go mod download在启动服务器之前,在目标代码库中。 - 索引是在启动时建立的。 对代码库的更改需要重新启动服务器。
- 每个服务器实例一个代码库。 为多个代码库使用多个服务器实例。
- 标准库未编入索引。 仅模块根目录下的包(
./...)被编入索引。
先决条件
- 转到1.25+
- 目标代码库必须编译干净(
go build ./...通行证) - 必须下载依赖项(
go mod download已运行)
安装
下载预构建的二进制文件 从 最新版本 适用于您的平台(Linux、macOS、Windows-amd64和arm64)。
或者使用Go安装:
go install github.com/tender-barbarian/go-llm-lens/cmd/server@latest或者从源代码构建:
git clone https://github.com/tender-barbarian/go-llm-lens
cd go-llm-lens
go build -o go-llm-lens ./cmd/server用法
go-llm-lens --root /path/to/your/go/repo服务器通过以下方式进行通信 标准 使用MCP协议。
旗帜
| 标志 | 默认值 | 描述 |
|---|---|---|
--root | . | 要索引的Go代码库的根目录 |
大语言模型集成
go-llm-lens 是一个MCP服务器,因此它可以与任何AI编码工具一起使用,但它是用Claude Code开发和测试的,所以下面是如何设置它的。
添加到克劳德代码
claude mcp add --scope user --transport stdio go-llm-lens -- /path/to/go-llm-lens鼓励克劳德使用它
默认情况下,Claude不会比Glob/Grep/Read更喜欢这些工具。将以下内容添加到您的 CLAUDE.md (全球 ~/.claude/CLAUDE.md 或项目级别):
## Codebase exploration
**ALWAYS use `go-llm-lens` MCP tools for Go symbol lookup. NEVER use Glob/Grep/Read to explore Go code structure.**
- `list_packages` — list all indexed packages
- `get_package_symbols` — browse all symbols in a package
- `get_file_symbols` — list symbols defined in a specific file
- `find_symbol` — locate any function/type/var/const by name (supports prefix/contains match)
- `get_function` — read full function/method definition including body
- `get_type` — read full struct or interface definition
- `find_implementations` — find all concrete types implementing an interface
Call `list_memories` at the start of every session and `write_memory` proactively to persist codebase knowledge across sessions.
Only fall back to Glob/Grep/Read for non-Go files or when the MCP server is unavailable.MCP工具
所有工具都返回JSON编码的结果。
list_packages
列出所有带有摘要统计信息的索引包。
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
filter | string | no | 导入路径上的可选前缀筛选器 |
输出: 一大批 { import_path, name, dir, file_count, func_count, type_count }
get_package_symbols
返回包中的所有符号:函数、类型、变量和常量。
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
package | string | yes | 包导入路径 |
include_unexported | bool | no | 包含未导出的符号(默认值:false) |
include_bodies | bool | no | 包含函数体(默认值:false) |
输出: { funcs: [...], types: [...], vars: [...] } 每份都有签名和文件注释。
find_symbol
在整个索引代码库中按名称搜索符号。
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
name | string | yes | 要搜索的符号名称 |
kind | string | no | 按种类筛选: func, method, type, var, const (空=全部) |
match | string | no | 匹配模式: exact (默认), prefix,或 contains |
输出: 包含包、种类、签名、接收者(用于方法)和位置的匹配数组。
get_function
返回特定函数或方法的完整详细信息。
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
package | string | yes | 包导入路径 |
name | string | yes | 函数名,或 TypeName.MethodName 方法 |
输出: 完整签名、参数名称和类型、返回类型、文档注释、实现体、, is_promoted (适用于从嵌入式类型升级的方法)、文件和行。
get_type
返回类型(结构或接口)的完整定义。
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
package | string | yes | 包导入路径 |
name | string | yes | 键入名称 |
输出:
- 对于结构体:具有类型、结构体标签和注释的字段;所有方法(与
is_promoted嵌入式类型的方法标志);嵌入式类型 - 对于接口:具有参数和返回类型的方法签名;嵌入式接口
- 文件注释、文件和行
get_file_symbols
返回特定文件中定义的所有符号。
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
file | string | yes | 文件路径(绝对或相对) |
include_unexported | bool | no | 包含未导出的符号(默认值:false) |
include_bodies | bool | no | 包含函数体(默认值:false) |
输出: { funcs: [...], types: [...], vars: [...] } 仅限于给定的文件。
find_implementations
查找索引代码库中实现给定接口的所有具体类型。
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
package | string | yes | 接口的包导入路径 |
interface | string | yes | 接口类型名称 |
输出: 一大批 { name, package, location, implements_via } 哪里 implements_via 是 "value" 或 "pointer".
用途 types.Implements 从 go/types 以获得精确的、类型系统精确的结果。
项目记忆
这四个工具提供了一个持久的键/值记事本,存储在 .llm-lens/memories.json 在项目的根。该文件是纯JSON格式——人类可读、可编辑,可以安全地提交到Git,这样整个团队就可以共享相同的积累知识。
推荐用法: 呼叫 list_memories 在每次会话开始时,并呼叫 write_memory 每当你学习到关于代码库的可重用信息时,都要主动出击。
list_memories
将此项目的所有内存注释作为键/值对返回。
没有参数。
输出: { "key": "value", ... }
write_memory
创建或更新命名记忆笔记。
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
key | string | yes | 注释名称 |
value | string | yes | 注意内容 |
输出: "ok"
read_memory
按键检索单个内存笔记。如果密钥不存在,则返回错误。
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
key | string | yes | 注释名称 |
输出: 存储的字符串值。
delete_memory
删除记忆笔记。如果密钥不存在,则返回错误。
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
key | string | yes | 注释名称 |
输出: "ok"
许可证
看 许可证.
