主题
第四章 Opencode 工具链与会话模型
← 返回目录 | 上一章:Opencode Agent 系统 | 下一章:OmO 插件架构 →
4.1 内置工具注册表
Opencode 的工具系统采用注册表模式(tool/registry.ts),每个工具是一个包含 name、description、parameters(Zod schema)、execute 函数的对象。Agent 通过工具白名单控制可用工具。
Opencode 内置 19 个工具,覆盖文件操作(read/write/edit/apply_patch)、搜索(grep/glob)、Shell 执行(bash)、Web 访问(webfetch/websearch/codesearch)、任务管理(todowrite/question)、Agent 协作(task/skill)、代码智能(lsp)、控制流(batch/plan_exit/invalid)等领域。部分工具受条件开关控制(如 LSP、WebSearch 需要启用对应实验特性)。
完整工具清单、参数说明、使用场景和 OmO 扩展对比:详见 工具系统完整解析。
4.2 MCP 协议支持
MCP (Model Context Protocol) 是 Anthropic 提出的标准化协议,用于 LLM 与外部工具/数据源的通信。Opencode 实现了完整的 MCP 客户端:
支持的传输方式:
- stdio:通过子进程的标准输入/输出通信(本地工具)
- StreamableHTTP:基于 HTTP 的流式传输(远程服务)
- SSE (Server-Sent Events):基于 HTTP SSE 的传输(旧版远程协议)
OAuth 2.0 支持:对于需要身份验证的远程 MCP 服务器,Opencode 实现了完整的 OAuth 2.1 + PKCE 流程,包括:
- 自动发现授权服务器元数据
- 动态客户端注册
- 本地回调服务器接收授权码
- Token 缓存与刷新
关键设计:Opencode 本身不内置任何 MCP 服务器。所有 MCP 工具都是用户通过配置文件添加的。这与 OmO 形成鲜明对比——OmO 内置了 3 个 MCP 服务器。
4.3 LSP 服务器管理
Opencode 内置了对 22+ 种编程语言的 LSP (Language Server Protocol) 支持,这是其代码理解能力的核心:
支持的语言服务器(部分):
| 语言 | LSP 服务器 | 自动下载 |
|---|---|---|
| TypeScript/JavaScript | typescript-language-server | ✅ |
| Go | gopls | ✅ |
| Rust | rust-analyzer | ✅ |
| Python | basedpyright | ✅ |
| Java | jdtls | ✅ |
| C/C++ | clangd | ✅ |
| C# | OmniSharp | ✅ |
| Ruby | solargraph | ✅ |
| PHP | intelephense | ✅ |
| Kotlin | kotlin-language-server | ✅ |
| Zig | zls | ✅ |
| Lua | lua-language-server | ✅ |
LSP 提供的能力(通过 LspTool 暴露给 Agent):
textDocument/definition— 跳转到定义textDocument/references— 查找所有引用textDocument/documentSymbol— 文件符号大纲workspace/symbol— 全项目符号搜索textDocument/publishDiagnostics— 错误/警告诊断textDocument/rename— 安全重命名textDocument/prepareRename— 重命名预检查
自动下载机制:当用户打开包含特定语言文件的项目时,Opencode 会自动检测并下载对应的 LSP 服务器二进制文件(通常下载到 ~/.opencode/lsp/ 目录),实现开箱即用。
4.4 Skill 系统
Skill 是一种轻量级的能力扩展机制——本质上是一个 SKILL.md 文件,包含结构化的指令和上下文信息:
markdown
---
name: my-skill
description: 做某件事的专家指南
tools: [BashTool, ReadTool] # 可选:需要的工具
mcp: # 可选:嵌入式 MCP 服务器
my-server:
command: npx
args: ["-y", "my-mcp-server"]
---
# 指令内容
当用户要求做 X 时,按以下步骤操作:
1. 首先检查 ...
2. 然后执行 ...发现路径(按优先级):
~/.claude/— 全局 Claude 风格配置目录~/.agents/skills/— 全局 Agent Skill 目录.opencode/skills/— 项目级 Skill 目录- 配置文件中指定的自定义路径/URL
加载流程:Agent 调用 SkillTool → 读取 SKILL.md → 解析 frontmatter → 将指令内容注入到当前对话的系统提示中 → 如有嵌入式 MCP,启动对应服务器并注册工具。
4.5 SQLite 持久化模型
Opencode 使用 SQLite 作为本地数据库(基于 better-sqlite3),存储所有会话数据。数据模型设计简洁但完备:
Session (会话)
├── id: string (ULID)
├── title: string
├── created_at / updated_at
│
├── Message (消息) ×N
│ ├── id: string (ULID)
│ ├── session_id: FK
│ ├── role: "user" | "assistant"
│ ├── created_at
│ │
│ └── Part (消息片段) ×M
│ ├── type: "text" | "reasoning" | "tool" | "file" |
│ │ "step-start" | "step-finish" | "patch" |
│ │ "compaction" | "subtask"
│ ├── content: JSON (类型特定的数据)
│ └── tool_invocation_id: string (工具调用关联)
│
└── Todo (待办) ×K
├── id: string
├── content: string
├── status: "pending" | "in_progress" | "completed" | "cancelled"
└── priority: "high" | "medium" | "low"Part 类型详解:
| Part 类型 | 内容 | 说明 |
|---|---|---|
text | 文本内容 | Agent 的文字回复 |
reasoning | 推理过程 | 模型的思考过程(如 Claude 的 thinking) |
tool | 工具调用+结果 | 包含工具名、输入参数、执行状态、输出结果 |
file | 文件路径 | 用户上传的文件 |
step-start/finish | 步骤标记 | 标记一轮 LLM 调用的开始和结束 |
patch | 代码补丁 | 结构化的代码变更记录 |
compaction | 压缩摘要 | 上下文压缩后的摘要内容 |
subtask | 子任务引用 | Task 工具创建的子会话引用 |
4.6 上下文窗口管理
AI 模型有固定的上下文窗口大小(通常 128K-200K tokens)。当对话过长时,Opencode 需要在不丢失关键信息的前提下压缩对话。
压缩策略(session/compaction.ts):
工具输出裁剪:优先删除旧工具调用的详细输出
- 保护最近 40K tokens 内的工具输出不被裁剪
- 单个工具输出超过 20K tokens 的优先裁剪
- 裁剪后替换为
[output truncated — old tool output removed to save context]
摘要压缩:如果裁剪不够,使用专门的
compactionAgent 生成对话摘要- 摘要 Agent 收到完整对话历史
- 生成关键信息的浓缩总结
- 原始消息被替换为
compactionPart
自动触发:当 token 使用率接近上下文窗口上限时自动触发
4.7 消息处理流水线
从用户输入到最终响应,消息经过以下处理流水线:
用户输入
↓
┌─ 消息持久化到 SQLite
├─ 组装消息序列:System Prompt + 历史消息 + 新消息
├─ 插件 Hook: chat.message(可修改消息)
├─ 插件 Hook: chat.params(可修改请求参数)
├─ 上下文窗口检查(是否需要压缩)
├─ 插件 Hook: experimental.chat.messages.transform(可变换整个消息序列)
↓
调用 LLM API(流式)
↓
┌─ 解析流式响应(文本 chunk / 工具调用 / 推理内容)
├─ 实时推送到 UI
├─ 持久化每个 Part
├─ 如有工具调用:
│ ├─ 插件 Hook: tool.execute.before
│ ├─ 权限检查(敏感操作需用户确认)
│ ├─ 执行工具
│ ├─ 插件 Hook: tool.execute.after
│ ├─ 将结果追加到消息序列
│ └─ 再次调用 LLM(循环)
├─ 插件 Hook: event(事件流通知)
↓
最终响应 → 持久化 → 推送到 UI关键观察:这个流水线中有 6 个插件 Hook 注入点。OmO 正是通过这些注入点实现了其全部增强功能——从后台任务通知到上下文工程,全部无需修改 Opencode 核心代码。