牛客面经整理: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 massif、jemallocprofiling(若使用)、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 存
len与alloc(或等价信息); buf末尾仍保持\\0便于兼容 C API;- 扩容策略:预分配减少频繁 realloc。
- header 存