关于 Sezzlee Local LLM
Sezzlee Local LLM 是供 Codex、Claude Code 和 Cursor 等规划 Agent 使用的 MCP 服务,可将有限范围的语言任务交给 Ollama 模型处理。文件内容不会进入调用方 Agent 的上下文,并设有输入和输出限制。
主要功能
- 01分类、提取、摘要和转换等限界任务
- 02按模式返回 JSON 或文本
- 03使用 local_map 标注 CSV 行
- 04将文件访问限制在工作区内
- 05限制输入与输出,不静默截断
- 06查看模型连通性和排队状态
适用场景
- 01摘要或提取工作区文件内容,避免载入 Agent 上下文
- 02用本地模型分类或转换有限文本
- 03标注 CSV 行并写入单独的输出文件
MCP 相关
向 MCP 客户端提供 local_task、local_map 和 local_status。服务连接 Ollama,并在调用方 Agent 的上下文之外执行模型请求。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y @sezzlee/llm-mcpSEZZLEE_LLM_MODELOllama model to callSEZZLEE_LLM_BASE_URLOllama base URLSEZZLEE_LLM_NUM_CTXContext window in tokensSEZZLEE_LLM_TIMEOUT_MSPer-call timeout in millisecondsSEZZLEE_LLM_KEEP_ALIVEHow long Ollama keeps the model loadedSEZZLEE_LLM_ROOTWorkspace folder paths resolve againstSEZZLEE_LLM_OUTPUT_DIRFolder local_map writes its CSV into本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
3收录的工具列表 · 更新于 3 周前
此工具无需参数。
详细介绍
@sezzlee/llm-mcp
一个 MCP 服务,可让规划 Agent(Codex、Claude Code、Cursor)将有界语言任务交给本地模型处理。它基于 @sezzlee/mcp-core 构建,且不会命名其他 @sezzlee/* 包。
快速开始
{
"mcpServers": {
"local": {
"command": "npx",
"args": ["-y", "@sezzlee/llm-mcp"],
"env": {
"SEZZLEE_LLM_BASE_URL": "http://127.0.0.1:11434",
"SEZZLEE_LLM_MODEL": "qwen3:8b"
}
}
}
}
在 Codex 中,同一代码块应放在 mcp_servers.local 下。如果运行时 approval_policy = "never",还必须设置 default_tools_approval_mode = "auto",否则 Codex 会拒绝调用。
工具
共三个工具。其中两个为只读;local_map 只会向服务自身的输出目录添加新文件。
| 工具 | 功能说明 | 关键参数 |
|---|---|---|
local_task | 在本地模型上运行一项有界语言任务:classify、extract、summarize、transform 或 free。服务会自行读取所有文件参数——其内容绝不会进入调用方 Agent 的上下文。使用 jsonSchema 时,答案为符合该架构的 JSON(作为 result 返回);否则答案为文本(作为 answer 返回)。summarize 和 extract 会将超大输入拆分成多个块并读取全部内容(最多 32 个块,总计 1 MiB);其他类型遇到超大输入时都会以 input_too_large 拒绝处理。绝不会静默截断内容。 | kind、instruction、text?、files?(最多 8 个,路径相对于工作目录)、jsonSchema? |
local_map | 使用给定标签为 CSV 文件的每一行添加标签。Agent 不会看到这些行:它只需传入路径,服务便会在输出目录下写入一个新文件(包含原始列以及一列标签),并返回文件路径、每个标签对应的计数,以及每个标签最多 4 个示例行。最多支持 2,000 行。 | file、instruction、labels(2–50 个)、labelColumn?(默认为 label) |
local_status | 报告本地模型是否可访问且已加载、其上下文窗口、单次调用的输入预算,以及当前排队的调用数。如果不确定本地模型是否可用,请在委派任务前调用此工具。 | 无 |
配置
SEZZLEE_LLM_MODEL=qwen3:8b
SEZZLEE_LLM_ROOT=. # default: the working directory
SEZZLEE_LLM_OUTPUT_DIR=.llm-mcp/out # default; relative to root, must stay inside it
SEZZLEE_LLM_BASE_URL=http://127.0.0.1:11434 # default
SEZZLEE_LLM_NUM_CTX=16384 # default; at least 4096
SEZZLEE_LLM_KEEP_ALIVE=30m # default
SEZZLEE_LLM_TIMEOUT_MS=300000 # default; one request
npx -y @sezzlee/llm-mcp
| 变量 | 默认值 | 含义 |
|---|---|---|
SEZZLEE_LLM_MODEL | 无 — 必填 | Ollama 模型名称。没有内置默认值;必须显式设置(参见模型选择)。 |
SEZZLEE_LLM_ROOT | 工作目录 | 工作区根目录。工具接受的每个文件参数都会相对于此目录解析。 |
SEZZLEE_LLM_OUTPUT_DIR | .llm-mcp/out | local_map 写入输出的位置。相对于根目录,且必须位于根目录之内。 |
SEZZLEE_LLM_BASE_URL | http://127.0.0.1:11434 | Ollama 主机的基础 URL。 |
SEZZLEE_LLM_NUM_CTX | 16384 | 以 token 为单位的上下文窗口。必须是至少为 4096 的整数。 |
SEZZLEE_LLM_KEEP_ALIVE | 30m | 转发给 Ollama 的 keep_alive 值。 |
SEZZLEE_LLM_TIMEOUT_MS | 300000 | 单次模型请求的超时时间。 |
只有模型名称是必填项。解析失败会导致程序终止,但无法连接主机不会:即使主机不可用,服务仍会启动并响应 tools/list,而 local_status 会报告 reachable: false。服务开始运行后会立即预热模型。
将 SEZZLEE_LLM_NUM_CTX 设置为 GPU 实际能提供的值。Ollama 不会拒绝超出限制的输入——而是静默丢弃输入开头部分——输入预算也由此值计算得出。
限制
| 限制 | 值 | 原因 |
|---|---|---|
| 输入预算 | 上下文窗口的 45% | Ollama 会静默丢弃超长提示词的开头,而不是拒绝处理,因此服务会先行拒绝。 |
| 输出预算 | 上下文窗口的 40% | 用于限制失控的补全;实测发现,如果没有带 ID 的架构,模型会循环生成 12,600 个 token 的垃圾内容。 |
| 每个字符对应的 token 估算值 | ~1.8 | 基于土耳其语文本测得;英语通常每个 token 对应近 4 个字符,因此此估算倾向于拒绝处理,而非截断内容。 |
local_task files | 最多 8 个 | 每次调用的文件参数上限。 |
local_task 长输入(summarize/extract) | 最多 32 个分块,共 1 MiB | 其他类型会直接拒绝超出预算的输入,并返回 input_too_large。 |
| 分块合并轮数 | 最多 3 轮 | 如果合并经过 3 轮后仍无法适配,则会失败并返回 input_too_large。 |
local_map 行数 | 最多 2,000 行 | 实测标注每行约需 270 ms——处理 2,000 行约需 9 分钟,在 Codex 允许的 900 s 工具超时范围内。 |
local_map 文件大小 | 最多 8 MiB | 提前拒绝,避免处理中途超时且没有任何内容写入。 |
规则
- 三层由 lint 强制执行,三个根入口。
src分为platform/(mcp-core 和 Node 边界)、backend/(模型宿主)和tools/(MCP 接口)。tools/只能通过backend/port.ts访问宿主;具体的后端在cli.ts中接入。 - 只有
src/backend/ollama.ts会访问网络,且只能通过全局fetch。其他所有位置都禁止使用该全局对象以及node:http/net/tls系列。 - 只有
cli.ts会读取process.env,且每个变量都按名称单独访问。 - 文件路径以工作区为基准解析。 请求的路径和它的
realpath会分别检查,因此指向工作区外的符号链接也会被拒绝。readText只接受已通过这两项检查、带有WorkspacePath品牌的路径。在错误封装中,工作区的绝对路径显示为.。 - 由服务器而非模型统计 token。 超过
num_ctx × 0.45的输入不会传给宿主;无论如何都无法容纳的输入甚至不会被读取,就会被拒绝。回答上限为num_ctx × 0.4。 - 只有
src/platform/workspace.ts会访问文件系统,并由 lint 强制执行。唯一的写入器是createOutput:它会重新检查输出目录的realpath;服务器自行选择文件名,将其转换为安全字符集并将扩展名固定为.csv,然后以wx模式打开——因此绝不会覆盖现有文件或符号链接,也绝不会删除任何内容(mcp-core)。 local_map会将行号传给模型并取回。 不带 ID 的格式会丢失行顺序。遇到跳过的行时会再次询问一次;若仍然缺失,就不为其添加标签并将其计数——绝不猜测。- 分块输入也不会被截断。 分块时依次在段落边界、行边界处切分;每个块都会生成备注,再合并这些备注并最后应用一次指令。如果合并后的备注仍超出预算,就重复合并轮次(最多 3 轮);如果仍然超出,调用会以
input_too_large被拒绝。 - 每个宿主一次只处理一个请求。
createSerialBackend会将请求排队:GPU 不会并行运行这些请求,而 Codex 会并发发起调用。probe不经过队列。 - 新增一种后端类型,只需实现
Backend接口。 服务器的其余部分无需更改。
设计说明
以下测量数据是上述规则的依据。
为什么使用 MCP 服务,而不是让 Codex 直接连接 Ollama
我们尝试了四种让 Codex 使用本地模型的方法,只有 MCP 服务奏效。
| 方法 | 结果 |
|---|---|
| Codex 直接连接 Ollama | Ollama 0.34.2 提供 /v1/responses,连接也成功了,但本地模型不了解 Codex 的工具协议——它调用了一个不存在的 Read 工具,而 Ollama 在下一次请求中回复了 no user query found。真正的障碍是:Codex 每轮开始时都会产生约 37k 个 token 的基础开销,无法装入 16k 的上下文 |
Codex shell 运行 curl | 沙盒会阻断网络访问;开放网络意味着让 Codex 可以访问所有网络,而不仅是本地模型 |
Codex 的 spawn_agent | 只接受 OpenAI 模型 |
| MCP 服务 | 在沙盒外运行,并自行连接 Ollama。只有提示词会发送给本地模型(测得为:196 个 token)——Codex 每轮约 37k 个 token 的开销不会离开 Codex |
服务的工作目录是 Codex 的工作区:工具接受的每个文件参数都会相对于该目录解析,解析到工作区外的路径会被拒绝。
模型选择
测量结果显示,gpt-oss:20b 在带 ID 的 JSON 架构 local_map 上得分为 0/100,而结构化的 local_task 调用依赖该架构——模型无法掌握该架构。本仓库中的测量使用的是 qwen3 模型(上述示例中为 qwen3:8b);SEZZLEE_LLM_MODEL 没有内置默认值,因此必须显式设置。
仅使用 Codex 处理表格数据
在一项包含 400 行、线索明确的标注任务中,Codex 独立工作与委派一样准确(14/14 正确),速度则约快 3.5 倍:48 秒,而通过 local_map 处理需要 166 秒。这就是为什么 DELEGATION_INSTRUCTIONS(products/chat/api/src/codex/delegation-instructions.ts)允许 Codex 在数据为表格且模式明确时编写脚本,而不是进行委派——只有面对长篇或非结构化文本时,委派才有优势;对于脚本已经能够解析的行数据,则没有必要委派。
被否决的替代方案
- 名称
ollama-mcp。 后端变得可插拔后,这个名称就太狭窄了;llm-mcp遵循仓库的命名惯例:根据服务背后的资源命名,而不是根据当前后端命名。 - 消费级产品中的私有工作进程。 该服务不包含任何聊天专用内容——工具接收文件路径和指令,MCP 接口上的架构是纯 JSON Schema。将其封装在单一产品中会阻碍外部贡献,也会迫使我们重新实现
@sezzlee/mcp-core已经提供的载荷预算功能。 - 使用现有的开源仓库。 审查过的大多数仓库使用 Python,其中一个仓库的许可证禁止修改;它们都不了解 Codex 的审批模式、工作区边界或载荷预算。借鉴了经得起检验的思路:按类型提供现成提示词(来自
ollama-handoff)、接收文件路径的工具和故障转移(来自LocalTokens),以及“工作进程生成有界输出,能力更强的一侧负责验收”(来自 Shahriar/ollama-mcp-server)。 - 每种任务类型配备一个工具。 每增加一个工具架构,Codex 每轮都要付出令牌成本;一个带有
kind枚举的local_task工具就能以更低成本覆盖相同范围。 - 提高并发,而不是排队。 向一块 GPU 并行发送 4 个请求,只比顺序发送 4 个请求快 1.1 倍——GPU 不会并行处理请求,因此只有队列才能确保调用在自身超时前完成。
不在范围内(有意为之)
- 让本地模型运行 Agent 循环。 审查过的某个仓库中的小型模型通过完整的 Agent 框架运行时,完成简单任务耗时 5–7 分钟,而且有一次声称完成了实际上并未完成的工作。Agent 工作已有 Codex 自带的
spawn_agent。 - 嵌入。 主机上有
bge-m3,但尚未使用;如果出现搜索需求,再添加即可。 - 直接读取
.xlsx。 v1 只读取 CSV 和纯文本。.xlsx输入由调用方通过@sezzlee/excel-mcp处理;目前不在本服务中直接支持该格式。
测量结果
以下所有 Codex 运行均使用 gpt-5.6-luna,推理强度为 low,并在 Ollama 0.34.2 上将 num_ctx 设为 16,384。“未命中缓存的输入”是指 input_tokens − cached_input_tokens。
| 运行 | 数据 | 设置 | 用时 | 未命中缓存的输入 | 输出 | 结果 |
|---|---|---|---|---|---|---|
| S0 | 400 行,含提示线索 | 仅 Codex | 53 s | 31.1k | 1.8k | 14/14 天 |
| S1 | 400 行,含提示线索 | 仅 local_task | 68 s | 32.5k | 2.4k | 14/14(本地调用触发 input_too_large,Codex 改用 Python) |
| S2 | 400 行,含提示线索 | local_map,自动批准 | 155 s | 41.4k | 1.5k | 14/14,Codex 从未读取这些行 |
| H0 | 400 行,不含提示线索 | 仅 Codex | 48 s | 25.8k | 1.9k | 14/14(读取 200 行并写出一条规则) |
| H2 | 400 行,不含提示线索 | local_map,盲目指令 | 162 s | 18.4k | 1.7k | 2/14(已标注 376/400 行) |
| H3 | 400 行,不含提示线索 | local_map,抽样 + 验证 | 166 s | 24.5k | 2.2k | 14/14(已标注 400/400 行) |
| D0 | 17 份文档 | 仅 Codex | 65 s | 34.1k | 2.9k | 17 条记录(每份文档仅读取前 240 行) |
| D2 | 17 份文档 | local_task + 分块 | 226 s | 25.7k | 3.0k | 17 条记录,Codex 本身未读取任何文档;17 次调用同时到达,队列在 157 s 内清空,未超时 |
开发
pnpm turbo run test --filter=@sezzlee/llm-mcp
此处不需要本地模型——fetch 已模拟。
test/live.spec.ts 会针对真实的 Ollama 实例运行:
SEZZLEE_LLM_LIVE=1 SEZZLEE_LLM_BASE_URL=http://127.0.0.1:11434 SEZZLEE_LLM_MODEL=qwen3:8b \
pnpm turbo run test --filter=@sezzlee/llm-mcp -- test/live.spec.ts
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。