Skip to content

08 口袋速背卡片 ​

用法:面试当天早上、或进场前 10 分钟过一遍。只背这一篇,不要翻别的。 全部结论都基于项目源码核对,数字可直接报。


一、一句话定位 ​

MewCode / EasyCoding:Go 从零手写的终端 AI 编程助手(对标 Claude Code)。单二进制、Anthropic + OpenAI 双协议、多轮 ReAct 闭环、五层权限防御、MCP 工具生态、两层上下文压缩、JSONL 会话存档、流式 TUI。 「从零」= 不依赖 Agent 框架(无 LangChain / Eino);协议解析、MCP、TUI 三处用了官方 SDK。


二、数字底牌(必须报准) ​

项值
规模101 个非测试文件 / 14,082 行;17 个测试文件 / 3,902 行;合计 17,984 行
结构18 个 internal 包 + cmd/mewcode + cmd/smoke
迭代上限25 轮(maxIterations)← 不是 10!
幻觉熔断连续 3 轮「整轮全未知工具」(maxUnknownRun)
内置工具6 个:read_file / write_file / edit_file / bash / glob / grep
工具超时Agent 统一 30s;bash 自身默认 120s;bash 环境变量只留 4 个
权限5 层 / 4 档模式 / 10 条黑名单正则 / 3 层 YAML 规则
规则语法=精确 / ~正则 / !取反 / 缺省 glob;层内 deny 优先;层间 local > project > user
MCP 命名空间mcp__<server>__<tool>(双下划线)
MCP 超时连接 30s / 调用 30s;关闭兜底 5s
压缩阈值ContextWindow − 20000(SummaryReserve) − 13000(AutoSafetyMargin) = 窗口的 83.5%
第一层阈值单条工具结果 50KB;单条 tool 消息聚合 200KB
摘要熔断连续 3 次失败跳闸
摘要 PTL 重试前 3 次各丢 1 组;之后按 20% 丢
保留近期原文≥ 10,000 token 且 ≥ 5 条消息
恢复段最近 5 个文件 × 每文件 5,000 token
Token 估算3.5 字符/token + 真实 usage 锚点
会话清理启动时 goroutine 清理 30 天以上会话
Plan 提醒每 4 轮一次完整版(第 1/5/9/13/17/21/25 轮)
系统提示模块固定 7 个(优先级 10–70)+ 可选 3 个(80/90/100)
会话恢复阈值估算 > ContextWindow − 8000 时立刻压缩

三、五层权限(背这条链) ​

① 黑名单(10 条正则,仅命令类,不可配置,Bypass 也拦)
      ↓ 未命中
② 沙箱(仅文件类工具;先 EvalSymlinks 再比 root+sep;不存在则回退最近祖先)
      ↓ 未命中
③ 规则引擎(local > project > user,就近命中即返回;层内 deny 优先)
      ↓ 未命中
④ 模式兜底(只产 Allow/Ask,绝不产 Deny)
      Default: 读写 Allow / 写 Ask / 执行 Ask
      AcceptEdits: 写 Allow / 执行 Ask
      Plan: 仅只读工具可见(同 default 兜底)
      Bypass: 全 Allow(①② 仍生效)
      ↓ 判 Ask
⑤ 人在回路(Respond chan 缓冲 1;三选一:本次/永久/拒绝;永久 → 写 settings.local.yaml)

顺序不可换的本质:前两层不可协商(用户改不了),后三层可协商(用户配置决定)。

  • 黑名单必须在规则前 → 否则 Bash(rm -rf *) 能放行 rm -rf /
  • 黑名单必须在模式前 → 否则 Bypass 等于关掉所有安全
  • 沙箱必须在规则前 → 否则 Write(/etc/**) 能写系统文件

关键边界:Engine.Check() 只做前四层,第五层由 agent 包编排(包注释原话)。


四、ReAct 循环(背这 5 条停止条件) ​

停止条件触发
① 自然完成本轮 len(calls) == 0
② 连续幻觉unknownRun >= 3(整轮全未知才计数,中途命中即清零)
③ 迭代上限25 轮
④ 用户取消ctx.Done()
⑤ 请求出错sErr != nil(PTL 会先走紧急压缩重试一次)

5 条路径全部调 ensureAssistantTail —— 历史末尾必须是 assistant,否则下次请求 Anthropic 400。

保序分批并发算法:

游标 i 扫过 calls:
  只读 → 吃入连续区间 [i, j)
         ① hook + 权限逐个检查(被拒记 preDenied,不阻塞并发)
         ② 按序 emit 所有 Start(UI 显示「都在跑」)
         ③ 被拒项预填错误结果
         ④ WaitGroup 并发执行(写 results[idx],不同索引 → 无需锁)
         ⑤ 按序 emit 所有 End + PostToolUse
  有副作用 → 单个串行,走完整 Ask 流程

五、压缩两层(背这个顺序) ​

每轮循环开头 → ManageContext
  ├─ 自动路径:先 layer1(零成本)→ 重估 token → 超阈值才 layer2
  ├─ 手动 /compact:跳过 layer1 和阈值,直接摘要(不计熔断)
  └─ 紧急(收到 PTL):强制 layer1 + 无条件摘要(不计熔断),只重试一次

Layer1(不打 LLM):单条 > 50KB 或聚合 > 200KB → 落盘 + 20 行/2048 字节预览
                    + 账本 ContentReplacementState:决策一次不可翻转(保缓存、稳视野)

Layer2(LLM 摘要):两阶段 prompt(analysis 丢弃 + summary 保留,9 个小节)
                    + 三段恢复(最近 5 个文件 / 当前工具集 / 「不要凭摘要猜」边界提示)
                    + 保留近期原文(≥10K token 且 ≥5 条)
                    + PTL 重试丢组 + 连续 3 次失败熔断

为什么两层:第一层零成本无损、第二层有成本有损 → 先用便宜的,不够再用贵的。 为什么 Layer1 要账本:同一份历史在不同轮次内容跳变会破坏 Prompt Cache,也让模型视野悄悄变化。


六、高频问答一句话版(20 条) ​

问一句话答
Provider 接口为什么只有 3 个方法接口应贴着唯一调用方的真实需求长;加 CountTokens 会有实现退化成「假装不支持」
流式为什么用 channel取消语义统一 + 无缓冲天然背压;代价是 ~30 个 emit 调用点都要处理失败分支
ToolCall.Input 为什么用 json.RawMessage免双重编解码 + 不丢数字精度 + 参数错误集中到工具层报
Prompt Cache 怎么做系统提示拆 Stable(打断点)/ Environment(不打);模块空则跳过,避免多一个字节
两家工具结果格式差异Anthropic 塞进一条 user 消息的 tool_result block;OpenAI 每个结果一条 tool 消息
扩展思考何时关历史含工具交互就关(保守),避免 400;代价是只有第一轮有 thinking
只读并发怎么保证权限不退化只读工具在模式兜底里恒 Allow,永不触发 Ask → 权限检查可以同步跑完
工具被拒为什么不中断拒绝原因是给模型的有效信息,它会自己换路径重试
ensureAssistantTail 干什么异常退出时补 assistant 尾消息,否则下一次请求 400
沙箱怎么防软链接逃逸EvalSymlinks 解析后比对;root 本身也要解析(macOS /tmp→/private/tmp)
路径前缀比较的坑必须比 root+sep,否则 /proj 会匹配上 /project2
模式为什么只产 Allow/Ask拒绝权只留给不可协商层和用户显式规则;模式是档位不是否决
「永久允许」会泛化吗不会——escapeGlob 转义 *?[];否则 rm -rf build/* 会变成无限授权
人在回路 Respond 为什么缓冲 1TUI 在 UI 线程同步发,无缓冲会阻塞 UI;缓冲 1 又保证「一次请求一次应答」
MCP 命名空间为什么双下划线单下划线在 server 名或工具名含 _ 时无法反解
MCP 只读为什么严格只信 readOnlyHint它同时影响并发安全和权限放行,声明错了后果严重;默认不可信
为什么用 JSONL 不用 SQLite追加写天然崩溃安全 + 零依赖 + tail -f 可观测;查询需求几乎为零
为什么每次写都 fsync会话历史就是用户成果;1–10ms 对一轮几秒的对话无感
Token 估算为什么锚点+增量误差不累积,每轮被真实 usage 重新校准;CacheRead 也要算进窗口占用
压缩会破坏 Prompt Cache 吗会,重写历史前缀全失效;所以阈值留 33K 余量尽量晚触发,且 Layer1 带账本保稳定

七、⚠️ 必须主动纠正 / 主动暴露的 6 件事 ​

#内容一句话
1迭代上限代码是 25 轮,简历写的 10 轮是抄了未同步的 README,以代码为准
2「从零手写」指不依赖 Agent 框架;协议/MCP/TUI 用官方 SDK,核心逻辑全手写
3沙箱对 bash 无效沙箱是参数级不是进程级;现在靠「bash 默认 Ask」兜底
4MCP 通配规则失效规则语法不支持通配工具名,mcp__github__* 被当成字面工具名
5compact/session/llm 无单测恰好是简历第 1、5 条;这三块核心是纯函数,是取舍失误
6记忆自动更新不可达SetProvider 无调用点 → UpdateAsync 恒早退;「功能写了 ≠ 功能能跑」

八、三个收尾金句 ​

  1. 「简历和代码不一致时,以代码为准。」
  2. 「我知道我的边界在哪 —— 沙箱停在参数层、安全配置是 fail-open、测试覆盖是错位的,这些不是『不知道怎么做』,而是『当时判断错了投入方向』。」
  3. 「功能写了 ≠ 功能能跑。」

九、最后 30 秒 ​

  • 说 25 轮,不说 10 轮
  • 说 mcp__server__tool(双下划线)
  • 权限先讲顺序,再讲前两层不可协商
  • 被质疑时先讲事实、再讲判断,不辩解
  • 不会就说不会,然后推测 + 说清哪里可能错

出声念三遍 01-项目口述稿-多版本.md 的三分钟版,比再看一遍题库更有用。

持续学习,持续构建。