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