文章目录

牛客面经整理:AI Agent 挂面经(创业公司,系统/协议/工程杂项)(二十三)

收录日期:2026-04-28
关键词:LangChain、TextSplitter、LangGraph、Kafka、顺序一致性、Zigzag 遍历、OSI、HTTP/2、HPACK、Rust OOM、Redis SDS

1. LangChain 里的 TextSplitter 有哪些?怎么选?

参考答案

  • 常见类型(概念层面):
    • Character/Recursive splitter:按字符/分隔符递归切,兼顾 chunk 大小与语义边界。
    • Token splitter:按 token 计数切,能更贴近模型上下文预算。
    • Markdown/HTML/Code splitter:按结构(标题/节点/语法)切,语义更完整。
  • 选择原则:
    • 以检索粒度为目标:chunk 太小召回噪声大,太大命中率低且浪费上下文。
    • 设置 overlap:保留跨段上下文,常用 10%~20%(视文本结构调整)。
    • 对“强结构文档”(API 文档/代码)尽量结构化切分。

2. LangGraph 的数据传输机制是什么?和 Arrow 有什么区别?

参考答案

  • LangGraph 更像“有状态的有向图工作流”:
    • 节点之间传递的是“状态(state)”,每个节点读 state、写 state,按边与条件路由推进。
    • 支持循环、分支、并行(取决于实现)与检查点(checkpoint)做恢复。
  • Arrow(Apache Arrow)是“列式内存数据格式/跨语言数据交换标准”,解决的是高性能数据表示与零拷贝传输问题。
  • 对比要点:一个是工作流/执行模型(LangGraph),一个是数据格式/内存布局(Arrow),关注点完全不同。

3. Kafka 里让两个 consumer group 互换消息,并保持原顺序、不重复,怎么做?

参考答案

  • 先澄清:Kafka 的顺序只在“单 partition 内”严格成立;跨 partition 无全局顺序。
  • 如果目标是“从某一时刻起互换各自消费进度”:
    • 核心操作是交换两个 group 在各 partition 的 offset。
    • 做法:暂停消费 → 确认已提交 offset → 使用管理工具读取/导出两个 group 的 offsets → 互换后写回(再恢复消费)。
  • 风险与边界:
    • 事务/幂等:要保证下游处理幂等,否则即使 offset 精准也可能因重试导致重复副作用。
    • 写回 offset 的原子性:需要确保所有 partition 的 offsets 一致切换,避免半切换造成错乱。
  • 更稳妥方案:不直接“互换”,而是在下游增加路由层/按 key 重分配;或新建 group 重新消费并做对账迁移(成本更高但可控)。

4. Zigzag(之字形)层序遍历怎么做?复杂度?

参考答案

  • 思路:BFS 层序遍历 + 每层根据方向决定输出顺序。
  • 实现:
    • 用队列按层收集 level
    • 方向为反向时对 level 反转,或用双端队列按方向 push。
  • 复杂度:时间 O(n);空间 O(w)(w 为最大层宽)。
  • 适用:需要按层处理且交替方向展示/计算的场景(打印、题目要求)。

5. 设计“OSI 第八层”你会怎么设计?现有七层有什么问题?

参考答案

  • 典型回答方向:在七层之上抽象“意图/语义/策略层”(application semantics & policy),用于:
    • 身份与权限、合规与审计、策略路由、上下文与会话治理、可观测性规范等。
  • 七层模型的“问题”更多是工程落地时边界模糊:
    • 现实协议栈并不严格分层(例如 TLS 与 HTTP 的耦合、代理与网关混合职责)。
    • 现代系统还需要零信任、服务治理、可观测性、策略控制等跨层能力。

6. HTTP/2 的 HPACK 头部压缩怎么实现?(高层概括)

参考答案

  • 目标:减少重复 header 的传输开销。
  • 机制:
    • 静态表(static table):预定义常见头字段。
    • 动态表(dynamic table):连接级别维护,后续请求可引用索引。
    • Huffman 编码:对字符串进一步压缩(可选)。
  • 安全点:历史上与压缩相关的侧信道风险(如 BREACH 类),一般通过约束敏感内容与实现细节来规避。

7. Rust 程序 OOM 怎么排查?如果要自定义 GC 怎么说?

参考答案

  • OOM 排查:
    • 先定位:日志 + 指标(RSS、heap、分配速率)、复现压力;
    • 工具:heaptrack/valgrind massifjemalloc profiling(若使用)、pprof(视集成);
    • 代码层:检查无界缓存、Vec 过度增长、循环引用(Rc/Arc + RefCell)、大对象拷贝。
  • “自定义 GC”:
    • Rust 默认无 GC;一般用所有权/借用做内存管理。
    • 若业务确需 GC,常见做法是引入带 GC 的运行时/库或在特定子系统做 arena/region 分配与统一回收(更贴近工程现实)。

8. Redis SDS 是什么?核心结构怎么实现?

参考答案

  • SDS(Simple Dynamic String):Redis 自己实现的动态字符串,记录长度与可用空间,避免 C 字符串的 O(n) strlen 与缓冲区溢出问题。
  • 核心点(概念):
    • header 存 lenalloc(或等价信息);
    • buf 末尾仍保持 \\0 便于兼容 C API;
    • 扩容策略:预分配减少频繁 realloc。