这是一份专门为 Cursor (使用 Claude-3.5-Sonnet 或 GPT-4o) 设计的详细执行计划。
要让 Cursor 高效完成这个 CGO 项目,关键策略是:分步投喂上下文。不要尝试一次性让它写完,由于 CGO 涉及 C++ 编译环境,Cursor 容易产生幻觉。
请按照以下步骤,依次将 【提示词 (Prompt)】 复制给 Cursor 的对话框(推荐使用 Cmd+I Composer 模式或 Chat 模式)。
项目预设信息 (第一复制这段发给 Cursor)
Context for Project:
我正在开发一个名为 my/go-llama-engine 的 Golang 项目。
目标:创建一个 Golang 工具包(SDK),通过 CGO 封装 llama.cpp 的核心功能。
核心需求:
它可以被其他 Go 项目引用。
支持加载 GGUF 格式模型。
支持通过切换 GGUF 文件路径来热更新模型。
提供简单的 Predict(prompt) 和 Stream(prompt) 接口。技术栈:Golang 1.22+, CGO, llama.cpp (作为子模块或库)。
第一阶段:环境搭建与库编译 (Phase 1: Setup)
目的:确保目录结构正确,且 llama.cpp 的 C++ 库文件编译成功。
步骤 1.1:初始化项目结构
【给 Cursor 的提示词】:
Plaintext
请帮我初始化项目目录结构。我需要以下文件结构:
- /llama.cpp (我不直接修改它,作为 submodule 或 clone 下来的源码目录)
- /pkg/binding (存放 CGO 绑定代码,处理 C <-> Go 类型转换)
- /pkg/engine (存放高层 Go 逻辑,如 Model 加载管理)
- /cmd/example (存放测试用的 main.go)
- go.mod (module name: github.com/lichv/go-llama-engine)
请执行 shell 命令创建目录并初始化 go mod。
步骤 1.2:编译 C++ 动态库 (这一步提议你手动辅助 Cursor 确认)
【给 Cursor 的提示词】:
Plaintext
目前我们需要编译 llama.cpp 以生成动态链接库。
请注意:当前环境是 [MacOS/Linux/Windows,请修改此处为你的实际系统]。
请给出具体的 shell 命令,在 `llama.cpp` 目录下使用 cmake 编译出 `libllama` 动态库(.so 或 .dylib 或 .dll)。
编译完成后,请告知我如何将生成的库文件和头文件(llama.h, ggml.h 等)移动到我的项目根目录下的 `/lib` 和 `/include` 文件夹中,以便 CGO 引用。
(注意:Cursor 给出的命令运行后,请务必检查 /lib 目录下是否有生成好的库文件)
第二阶段:编写底层 CGO 绑定 (Phase 2: Low-Level Binding)
目的:打通 Go 和 C 的通信桥梁。这是最难的一步,需要 Cursor 读取头文件。
步骤 2.1:生成 CGO 桥接代码
【给 Cursor 的提示词】(请先在 Cursor 中打开/引用 include/llama.h 文件,让它看到内容):
Plaintext
@include/llama.h
参考上述 llama.h 头文件。请在 `/pkg/binding/binding.go` 中编写 CGO 代码。
你需要做以下几件事:
1. 配置 #cgo CFLAGS 和 LDFLAGS,指向项目根目录的 /include 和 /lib。
2. 定义一个 Go 结构体 `LLamaBinding`。
3. 封装 `llama_load_model_from_file` 和 `llama_new_context_with_model` 函数。
4. 确保处理好 C String 和 Go String 的转换,以及 C 指针的内存释放(Free)。
请只生成最基础的加载模型和释放内存的代码,先不要做推理逻辑。
步骤 2.2:实现 Tokenizer 和推理基础
【给 Cursor 的提示词】:
Plaintext
继续完善 `/pkg/binding/binding.go`。
我需要实现文本推理的基础功能。请添加以下 CGO 函数的封装:
1. `Tokenize(text string) []int`:将文本转为 token id 数组。
2. `Eval(tokens []int)`:调用 `llama_decode` 或相关函数进行推理。
3. `Sample()`:从 logits 中采样下一个 token id。
4. `TokenToPiece(token int) string`:将 token id 转回文本。
请确保代码中包含必要的 unsafe.Pointer 转换,并处理好错误返回值。
第三阶段:封装高层逻辑 (Phase 3: High-Level Engine)
目的:让调用者不需要关心 C 指针,只关心业务逻辑。
步骤 3.1:实现 Engine 结构体与模型加载
【给 Cursor 的提示词】:
Plaintext
目前编写 `/pkg/engine/engine.go`。
这是一个纯 Go 的封装层。
1. 定义 `Engine` 结构体,内部持有 `binding.LLamaBinding` 的实例。
2. 实现 `NewEngine(modelPath string) (*Engine, error)`。
3. 实现 `Engine.ReloadModel(newPath string) error`,这个方法需要先释放旧模型的 C 内存,然后加载新模型,实现热更新。
步骤 3.2:实现对话与流式输出
【给 Cursor 的提示词】:
Plaintext
在 `/pkg/engine/chat.go` 中实现推理逻辑。
1. 实现 `Predict(prompt string, options ...Option) (string, error)`:输入提示词,返回完整回复。
2. 实现 `Stream(prompt string) (<-chan string, error)`:实现流式输出,利用 Go channel 逐步返回生成的字符。
3. 也就是在循环中调用 binding 层的 Eval 和 Sample,直到遇到 EOS token。
第四阶段:测试与验证 (Phase 4: Verification)
目的:验证一切是否工作正常。
步骤 4.1:编写测试程序
【给 Cursor 的提示词】:
Plaintext
请在 `/cmd/example/main.go` 中编写一个完整的调用示例。
1. 初始化 Engine,加载一个本地的 GGUF 模型(路径通过命令行参数传入)。
2. 打印 "Model loaded successfully".
3. 调用 `Stream` 方法,输入 "Hello, explain Golang to me.",并将结果实时打印到控制台。
4. 模拟一次模型热更新:5秒后,调用 `ReloadModel` 重新加载模型(或加载另一个)。
步骤 4.2:运行与调试 (Debug 助手)
(如果运行时报错 “symbol not found” 或 “image not found”)
【给 Cursor 的提示词】:
Plaintext
我在运行的时候遇到了报错:[粘贴你的报错信息]。
这似乎是动态链接库路径的问题。
请帮我写一个 Makefile,包含 `run` 指令,确保在运行 go run 之前,设置好 `LD_LIBRARY_PATH` (Linux) 或 `DYLD_LIBRARY_PATH` (Mac),指向项目的 /lib 目录。
关键注意事项 (给开发者的 Tips)
- 文件路径:Cursor 有时会搞混相对路径。在 CGO 的 #cgo LDFLAGS 中,最好使用 ${SRCDIR} 宏来指定库文件的路径,这样更稳健。
- 硬件加速:
- 如果你是 Mac M1/M2/M3:告知 Cursor 在编译 cmake 时必须加 -DLLAMA_METAL=ON。
- 如果你是 Nvidia 显卡:告知 Cursor 加 -DLLAMA_CUBLAS=ON。
- 如果不加这些,模型跑在 CPU 上会超级慢。
- 模型获取:你需要自己去 HuggingFace 下载一个 GGUF 文件(推荐 Qwen2.5-1.5B-Instruct-GGUF 这种小模型先用来测试,才几百兆,跑得快)。
你可以目前打开 Cursor,从 第一阶段 的提示词开始操作。如果有某一步报错,请把报错贴给我,我来调整下一条指令。





