面灵AI→

恒生电子 AI 应用开发工程师 AI 面面经

轮次
AI面
时间
2026-10
来源
牛客网

《面试题目》

  1. Python 的装饰器机制是什么?结合一个实际场景说明。
  2. 被装饰函数同时包含位置参数、关键字参数等参数时,怎样处理?
  3. 有参数和无参数的装饰器有什么区别?
  4. InnoDB 执行 UPDATE 时,不同 Key 的加锁机制是什么?
  5. MySQL 为什么使用 B+ 树,而不是 B 树?
  6. B+ 树叶子页双向链表对范围检索有什么优势?
  7. MCP 一次交互的完整过程是什么?特别说明客户端流程。
  8. 客户端同时请求 MCP 的三类能力时,服务端如何决定响应顺序并协调?
  9. 常见的上下文管理机制有哪些?
  10. 上下文被删除后,用户偏好记忆怎么办?
  11. 陪伴类 Agent 中,长期记忆如何在合适时机检索?系统怎么知道此刻应该取出「用户喜欢吃苹果」?
  12. RAG、传统检索生成、模型微调有什么区别,各适合什么场景?
  13. 知识库存在过时知识和内容冲突,并且限定知识库无法治理时,要构建问答 Agent,应选择哪种策略?
  14. 用户 Query 明确指向旧知识时,怎样避免被改写到新知识库?
  15. 介绍一个项目,它解决了什么问题?
  16. 受控编辑如何解决 LLM 不安全写入?
  17. 为什么选择当前方案,是否考虑过其他方案,为什么放弃?
  18. 如何证明受控编辑比 LLM 直接修改效果更好?使用什么指标,怎样评估?
  19. 使用过哪些 Agent 框架或自研框架,它们有什么设计巧思?
  20. 为什么使用 LangGraph 状态图,不自研?
  21. LangGraph Checkpoint 是否与当前业务状态发生冲突,怎样处理?

《参考解析》

Python 装饰器与参数透传。装饰器是「接收函数、返回函数」的高阶函数,@deco 等价于 func = deco(func),在函数定义时执行一次,适合做缓存、鉴权、重试、埋点这类横切逻辑。参数透传的标准写法是 functools.wraps 加 def wrapper(*args, **kwargs),再原样 return func(*args, **kwargs),这样位置参数、关键字参数与默认值都能传下去;不加 wraps 会丢掉名字、文档与签名,靠 inspect.signature 校验参数的框架(如 FastAPI)会直接出错。有参数与无参数装饰器的区别是多一层工厂:无参版本直接接收函数,有参版本先接收配置再返回真正的装饰器,即三层嵌套。工程上的坑在于异常堆栈被包裹、原函数元信息被遮蔽、多个装饰器叠加时「自下而上生效、自上而下执行」,以及带状态的装饰器要防多线程共享与内存不释放。

InnoDB 的 UPDATE 加锁与 B+ 树。加锁范围取决于能否命中索引:命中唯一索引的等值更新退化为行锁;命中普通二级索引会给二级索引加 Next-Key Lock 并回主键索引加行锁,等于锁两份索引;没命中索引则扫描并锁住扫过的所有行,表现为接近锁表,是线上最危险的一种。RR 下为防幻读会加间隙锁,间隙锁互相兼容、与插入意向锁容易死锁;因此实践准则是更新必须走索引、事务尽量短、多行更新按固定顺序、失败快速重试。B+ 树相对 B 树,非叶子节点只存键、扇出更大、树更矮,三到四层即可撑起千万级数据,一次查询的磁盘 IO 更少;数据全在叶子层使查询路径等长、性能稳定;叶子用双向链表串联,范围查询定位起点后顺序右扫,排序与分页因此高效,这正是 MySQL 选它的根本原因。

MCP 一次交互的完整过程。MCP 是「宿主 - 客户端 - 服务端」结构,宿主管模型与用户交互,客户端由宿主创建、与服务端一对一连接。流程是:建立连接并协商协议版本与能力集;请求 tools/list、resources/list、prompts/list 拿到能力清单并注入模型上下文;模型决定调用后,客户端用 tools/call 传工具名与参数,服务端执行并回结构化结果;服务端若需要采样或向用户确认,通过 sampling/createMessage、elicitation、roots/list 这类反向请求完成;客户端再把结果转成模型消息继续对话。同时请求工具、资源、提示三类能力时,协议层不规定响应顺序——JSON-RPC 靠 id 配对、允许乱序返回;实际顺序由客户端调度决定,服务端只保证同一 id 请求与响应配对、并发请求互不影响。工程上还要处理超时与取消通知、大结果分页与会话恢复。

上下文管理与长期记忆的检索时机。常见机制有窗口截断、滚动摘要、按相关性检索历史片段、把关键事实抽成结构化画像、分层组织(系统约束常驻、近期原文保留、远期摘要),以及把状态落到外部存储按需加载。上下文删除要与记忆解耦:原始对话可以按窗口淘汰,但用户偏好属于长期事实,必须独立抽取、独立存储并带来源与时间戳。至于「此刻该不该取出用户喜欢吃苹果」,不能靠模型每次自行回忆,而要把触发条件显式定义:意图触发(当前问题或即将调用的工具命中饮食、聚餐、推荐这类标签)、实体触发(对话出现该偏好绑定的实体或近义说法)、生命周期触发(新会话开场注入高优先级画像摘要)。召回后还要按相关度与时效重排,并标注这条记忆的时间与是否被改过,避免把过期偏好当成当前事实。

RAG、传统检索生成与微调的取舍。差别在知识放在哪里、如何生效:传统检索生成依赖关键词与倒排索引,命中准但召回窄;RAG 用 embedding 做语义召回,能处理同义表述,代价是向量模型、索引与检索评测的工程负担,且对专有名词与数字不敏感,所以生产上多为关键词与向量混合加 rerank;微调改模型参数,把格式、风格与领域语感固化进去,适合行为对齐,不适合承载频繁更新的知识——知识一变就要重训,还会有灾难性遗忘。结论是:知识频繁变化、要求可溯源用 RAG;任务形态固定、要求输出稳定用微调;数据量小且结构规整直接用检索加规则。

知识库无法治理、且新旧知识冲突时的策略。前提是不能治理知识库,就不能假设有权威答案,只能把冲突显式暴露:检索保留全部候选并标注来源、版本与生效时间;生成侧要求模型先陈述存在多个版本再分别作答,禁止自行裁决谁对;答案里固定给出时间戳与出处,让用户判断。用户 query 明确指向旧知识时,关键是别让改写把旧术语映射到新知识:识别时间词、版本号、旧产品名,把它们作为强过滤条件先做精确匹配锁定范围,再做语义召回;改写时保留原词,并把改写前后的 query 一起检索取并集。最后用一批「指向旧知识」的问题集度量旧版本召回率与答案中的版本标注准确率。

受控编辑与 LangGraph 选型。LLM 直接写文件的不可控在于可能越权改无关内容、破坏格式,或解析失败时静默写半截,所以受控编辑把生成与写入拆开:模型只输出结构化补丁(锚点、替换内容、理由),代码做锚点唯一性校验、冲突检测与语法校验,通过才原子落盘,失败回灌错误重试一到两次,仍失败就标记该步失败而非硬写,并保留可回滚版本。要证明它优于直接改,指标分两层:结果层看首次成功率、需人工修复比例、写入后编译或测试通过率;过程层看误改次数、格式破坏率、重试与 token 成本,并用同一批任务、固定模型与温度做多轮对照。选 LangGraph 而不自研,是因为状态与 reducer、条件边与循环、人工介入、断点恢复、流式事件这些骨架已被验证,自研省下的只有依赖,却要自己踩并发与状态合并的坑。它的代价是 checkpoint 与业务状态成为两份真相,处理办法是让业务库为唯一真相源、checkpoint 只存控制流,恢复时用版本号校验,并按会话隔离 key、加 TTL 清理。