# Step 70: 统一内核 createCore
一句话导读:把工具、记忆、MCP、LSP、搜索、沙箱、hooks 的装配收成一个
createCore(),终端和 Web 只注入前端差异。
# 一、这一步做了什么(What)
step70 解决一个架构问题:终端 index.ts 装了完整能力,Web session.ts 只装了少量工具,导致 Web 不能联网搜索、功能不完整。于是新增 core/createCore.ts,把全套 agent 内核装配统一起来。
终端和 Web 都调用它,差异通过参数注入。
# 二、面试官视角:为什么要做?(Why)
面试题:终端和 Web 都能跑,为什么还要重构出 createCore?
因为两份装配必然漂移。工具系统最怕“新增能力忘了另一个入口”:终端有 WebSearch,Web 没有;终端接 MCP,Web 不接;权限、记忆、skills 都可能不一致。
| 两份装配 | 单一 createCore |
|---|---|
| 加工具要改两处 | 只在一处注册 |
| Web/终端功能漂移 | 多前端共享能力 |
| bug 难定位 | 前端差异显式注入 |
# 三、原理:它是怎么工作的(How)
入口在 step70/core/createCore.ts:
export async function createCore(opts: CoreOptions): Promise<Core> {
// registry / hooks / sandbox / memory / MCP / LSP / plugins / skills / engine
}
2
3
前端差异变成 getter 或回调:
createCore({
canUseTool,
getModel,
getStyle,
getWebSearch,
approvePlan,
connectMcp,
connectLsp,
});
2
3
4
5
6
7
8
9
Web 的 server/session.ts 只负责传 Web 风格参数:
const core = await createCore({
canUseTool: opts.canUseTool,
getWebSearch: () => true,
connectMcp: false,
connectLsp: false,
});
2
3
4
5
6
架构图:
终端 readline 权限 ─┐
├─ createCore ─ QueryEngine + 全套工具
WebSocket 权限 ───┘
2
3
# 四、深入追问(面试常见 follow-up)
Q:createCore 会不会变成上帝函数?
A:它确实是装配层,但不是业务逻辑层。它的职责是 wiring,把工具和依赖接起来;具体能力仍在各模块。
Q:为什么 Web 默认关 MCP/LSP?
A:启动慢、资源重。教学版先保证 Web parity 的核心能力,MCP/LSP 可按需打开。真实系统会做资源池和生命周期管理。
Q:canUseTool 为什么必须注入?
A:权限交互属于 surface:终端问 y/n,Web 弹窗,SDK 可能走 control message。内核只 await 一个布尔结果。
Q:这个抽象和真实 Claude Code 对得上吗?
A:真实源码不一定叫 createCore,但 QueryEngineConfig、bootstrap state、bridge/sdk surface 都体现了同一思想:内核无头,surface 注入差异。
# 五、踩坑 / 设计权衡
重构后要验证两个方向:Web 是否补齐搜索,终端是否没有回退。README 里实测了 Web 出现 web_search 事件,终端仍保留 44 工具。
# 六、与真实源码的对照
| 我们的实现 | Claude Code 源码 |
|---|---|
createCore() 单一装配 | bootstrap + QueryEngineConfig + surface 注入 |
| canUseTool/getter 参数 | 终端/IDE/Bridge 各注入权限和状态 |
| Web/终端共用工具 | 多入口共享 agent runtime |
| 简化资源生命周期 | 会话隔离、资源池、feature flags |
# 七、一句话总结
createCore() 把“一个内核,多张脸”真正落到代码上:能力只装一次,前端只负责注入交互方式。
# 下一节预告
下一篇是 step71:在统一内核上继续打磨 Web,加入 token/cost 状态栏和多会话管理。