跳至主要內容

面经


面试题库(全量合并版)

更新时间:2026-06-29 覆盖岗位:字节 Coze AI Agent 开发工程师 / 零一万物 AI 全栈开发工程师 来源:questions.md + coze_interview_answers.md + me.md + mock_log.md + last_2_hours_review.md


目录(按技术专题)

共 62 题,分 10 个技术专题。点击跳转。

一、面试通用

二、上下文工程与产品设计

三、Multi-Agent 架构与协作

四、工具系统与协议

五、Agent 运行时深挖

六、LangGraph 与编排框架

七、工具执行引擎

八、推理引擎与基础设施

九、行业视野与场景设计

十、反问 + 速记


一、面试通用

A1. 自我介绍 + 为什么适合这个岗位

面试官意图:看你经历是否和岗位匹配,能否把产品、全栈、AI 平台负责人串成一个稳定画像。

推荐回答

面试官您好,我是郭鹏,目前在中电信数智做 AI 研发工程师,主要负责部门 AI 平台和 Agent 产品的研发工作。我的经历比较复合,既做过产品经理,也回到研发主导 AI 项目落地,所以我比较擅长从业务目标出发,拆到产品方案、技术架构、前后端实现和上线验证。

我最近最核心的两段项目经历是 FuserMate 和 FuserLab。

FuserMate 是一个类 Claude Code 的通用智能体平台,对内作为数字员工,对外承接政企场景 Agent。我在里面担任 AI 全栈和小组负责人,从 0 到 1 主导产品定义、UI/UX、技术架构和核心代码,重点做了 AgentLoop、工具体系、MCP、Skills、HITL、中断恢复、上下文压缩和工具安全。

FuserLab 是部门级 AI 中台,核心是把 Prompt 编排、版本发布、Trace 观测、评测实验和知识库能力平台化。我在里面负责 Prompt/评测/观测工具链、LangGraph 业务 Agent,以及 pgvector + unstructured 知识库能力。

我理解这个岗位需要的是上下文工程、MultiAgent 架构、工具 Skill 生态、高并发低延迟和高可用系统能力。我的这两段项目正好覆盖这些方向,而且我不是只做某个局部功能,而是从产品价值、平台边界到代码落地都实际负责过,所以我认为和岗位匹配度比较高。

Coze 版微调:结尾强调"上下文工程、MultiAgent 协作、Skill 生态、高并发推理",对应 Coze JD 四大方向。 零一万物版微调:结尾强调"B 端 Agent 平台、工具工程化、RAG、可观测评测和全栈交付能力"。

可能追问

  • 你从产品转研发后最大的优势是什么?→ 能从业务价值出发做技术取舍,而不是为了技术而技术
  • 你说效率提升 200%,怎么衡量?→ 200% 是团队交付效率的内部估算,主要来自需求拆解、代码生成、测试补齐的周期缩短。具体例子:以前一个设置页需要多人多天,现在 AI 协作可以压缩到一到两天

表达注意

  • "类 Claude Code"和"类 Claw"根据面试官熟悉度选一个,不要混用
  • 不要用"deepagent"这种没有业界定义的自造词
  • 不要说"内部使用风险小所以敢自研"→ 说"需要精细控制平台运行时所以自研"

A2. FuserMate 是什么,你负责了什么

面试官意图:确认你是否真的主导平台,而不是只做页面或零散功能。

推荐回答

FuserMate 是一个通用智能体平台,对内作为数字员工,对外作为可嵌入对讲/IM 业务流程的场景化 Agent。我的角色是 AI 全栈和小组负责人,负责从产品定义、交互设计、技术架构到核心编码的闭环。

技术上我主导了 AgentLoop、工具体系、MCP、Skills、HITL、中断恢复、上下文压缩、运行事件和工具安全。架构上把 Agent、Workspace、Session、AgentRun 分开——Agent 管身份和记忆,Workspace 管路径和能力,Session 管对话,AgentRun 管执行快照。这样平台既能支持实时对话,也能支持回放、审计、Cron、任务和后续多 Agent 协作。

可展开项目证据

  • backend/src/agent/core/agent_loop.py
  • backend/src/agent/core/tools/registry.py
  • backend/src/agent/core/tools/executor.py
  • docs/plan/detail/03-core-model.md
  • docs/plan/detail/README-command-safety-policy.md

可能追问

  • AgentRun 为什么要单独建模?→ 冻结执行上下文,支撑回放、审计、崩溃恢复
  • Workspace 为什么管 tools/skills,不放在 Agent 上?→ Agent 是身份,Workspace 是能力边界,解耦后同一 Agent 可以在不同场景有不同能力

A3. 为什么 FuserMate 自研 AgentLoop,FuserLab 用 LangGraph

面试官意图:考察你对框架取舍的理解。

推荐回答

两个项目的复杂度方向不一样。

FuserLab 的语音控制是一个业务上已经比较明确的状态机:agent 调用工具 → 工具返回结果 → agent 继续推理 → 多候选时走 ask_user 分支。这个流程用 LangGraph 的 StateGraph + ToolNode + checkpointer 刚好能表达,开发成本低,而且 LangGraph 的 Postgres checkpointer 天然解决了状态持久化的问题,不需要自己再造一遍。

FuserMate 的主要复杂度不在固定的图拓扑,而在平台运行时。我需要精细控制模型流式输出的每一步拆分——thinking delta、message delta、tool call 的识别——然后把这些语义事件通过 WebSocket 实时推给前端。工具执行也不是简单的 ToolNode 概念,而是有 started、completed、failed 的完整生命周期事件,需要配合 cancel 中断、结果截断、上下文压缩和快照冻结。这些平台级能力如果用 LangGraph 硬套,反而会被它的抽象层束缚住,不如自研轻量 AgentLoop 来得直接。

但自研不是没有代价——我需要自己处理迭代上限、异常兜底、流式中断、事件序等基础设施问题,LangGraph 在社区和稳定性上有优势。所以我的判断是:业务状态机明确的场景用 LangGraph,需要掌控平台运行时的场景自研。如果 FuserMate 后续要做复杂 DAG,我的演进思路是在上层引入图编排,底层工具执行和事件系统仍然复用自研的 AgentLoop,不是全量迁移到 LangGraph。

可能追问

  • 自研 AgentLoop 的风险是什么?→ 社区支持弱、需要自行维护基础设施、新人上手成本高
  • LangGraph checkpointer 带来了什么问题?→ 连接创建进了热路径,通过池化优化解决

不会时怎么说:如果问到 LangGraph 内核执行细节:我没有深入改过 LangGraph 内核,项目里主要使用 StateGraph、ToolNode 和 Postgres checkpointer。我更熟的是如何把它封装进业务 Agent,并通过 Trace 和 stage timing 定位性能问题。


A4. 传统 Web 应用和 AI Agent 应用有什么本质不同

面试官意图:考察对 Agent 应用范式的根本理解。

推荐回答

本质区别在三个层面:

确定性 vs. 概率性。传统 Web 应用的逻辑是确定性的——同一个请求走同一条代码路径,得到同一个结果。Agent 应用的每一步都是概率性的——模型可能选择工具 A 也可能选择工具 B,推理链可能正确也可能偏航。这意味着 Agent 的调试方式完全不同:传统应用看日志找哪行代码逻辑错了;Agent 需要 trace 贯穿模型推理、工具调用、上下文状态的完整链路。

请求-响应 vs. 持续执行。传统 Web 应用是一次 HTTP 请求-响应。Agent 的一次"请求"可能包含 10 轮模型调用 + 5 次工具执行 + 2 次人工确认,持续数分钟。这需要完全不同的架构:异步执行、流式推送、中断恢复、状态持久化。FuserMate 的 WebSocket 推流 + AgentRun 快照 + reconcile 崩溃恢复,都是为这种"持续执行"模式设计的。

代码即逻辑 vs. Prompt 即逻辑。传统应用的业务逻辑在代码里,修改逻辑 = 改代码 + 部署。Agent 应用的一部分逻辑在 Prompt 里,修改 Prompt 可能改变 Agent 的行为——但没有编译器和类型系统帮你检查 Prompt 的正确性。所以 Prompt 需要版本管理、评测回归、灰度发布——和代码一样的工程化流程。

一句话总结:Agent 应用不是"传统应用 + AI",而是一种新的应用范式,其不可靠性需要新的工程体系和产品设计来管理


A5. 你怎么理解"产品思维"在 Agent 开发中的体现

面试官意图:JD 第一条就强调产品思维,这是区别于纯工程岗位的关键。

推荐回答

我有一段特殊经历——从研发转到产品经理,做了两年产品,再回到研发主导 AI 平台。所以我对"产品思维"的理解不是抽象概念,而是实际的工作方式。

产品思维在 Agent 开发中体现在三个方面:

第一,定义 Agent 能做什么、不能做什么。技术人容易陷入"技术上能做到就去实现",但产品思维要先问:用户真的需要这个吗?Agent 全自动处理和半自动辅助的边界在哪里?FuserMate 做工具安全策略时,我没有把权限做得极细(每个工具都弹窗确认),因为那会把 Agent 变成"需要人盯着每一步的自动机器"——失去了 Agent 的核心价值。

第二,定义失败时的用户体验。Agent 出错了——比如工具调用失败、推理跑偏——用户看到的不能是 traceback 或技术错误码,而是"Agent 在做什么、为什么没做成、你现在可以怎么做"。FuserMate 的 27 种运行时事件和 cancel 中断的即时反馈,都是围绕这个目标设计的。

第三,用评测数据而非"感觉"来判断质量。产品思维不是"我觉得这个 prompt 效果不错",而是"这个 prompt 在 200 个 case 的评测集上,intent 准确率从 87% 提升到了 92%,但 slot 完整率下降了 1%——我们看看是哪些 case 在退化"。

总结:Agent 产品经理和传统产品经理最大的区别是——你需要理解"模型的不可靠性"是系统的一部分,而不是 bug。设计产品时要假设模型会出错,然后让系统在这个前提下仍然可用、可恢复。


二、上下文工程与产品设计

B1. 上下文工程的核心挑战是什么?你做过哪些上下文管理方案

面试官意图:考察你是否理解上下文不只是"塞进 prompt",而是系统工程问题。

推荐回答

上下文工程的核心挑战不是"窗口够不够大",而是信息密度和时效性的平衡。你把什么放进上下文、什么时候放、放进去之后什么时候清理——这三个问题比窗口大小重要得多。

我在 FuserMate 里做了两层压缩来解决这个问题。

第一层是 MicroCompactor,规则驱动的轻量压缩,在每轮模型调用前自动执行。逻辑很简单但有效:保留最近 5 个工具结果原文,其余替换为哨兵字符串 "[Old tool result content cleared]"。短结果(<200 字符)永不压缩。这样模型知道有工具执行过,但不消耗 token 记录完整历史输出。关键是这个压缩是零延迟的,不用调模型。

第二层是 CompactionService,模型驱动的深度压缩。当上下文接近 128K token 时自动触发。它不是简单地让模型自由摘要,而是用结构化的 summary prompt 要求保留五类信息:当前目标、重要决策、暂停约束、未决问题、文件路径和接口约定。压缩后的摘要作为 system message 注入到消息列表开头,模型看到的是"摘要 + 最近未压缩的 turns"。

这里有一个我特别想强调的设计点:上下文的核心是"状态"而非"聊天记录"。很多方案只保留滑动窗口的最近对话,但 Agent 跨越几十轮执行后,真正重要的是"当前目标是什么、哪些决策已经做了、哪些约束还在生效",而不是三小时前用户说了什么。MicroCompactor 保的是最近的操作语义,CompactionService 保的是全局任务状态——两层互补。

可能追问

  • 为什么 MicroCompactor 不用模型做摘要?→ 跑在每次模型调用前,如果在热路径上再加 LLM 调用会显著增加延迟。规则级压缩零延迟,只在真正逼近阈值时才触发模型级压缩
  • 128K 阈值怎么定的?→ 200K 窗口模型,128K 触发留 70K buffer 给当前轮次输出和工具结果

B2. 多 Agent 架构下主 Agent 和子 Agent 的通信链路怎么设计

面试官意图:考察多 Agent 协作的工程落地经验。

推荐回答

我实际做过的是 FuserMate 的 Mailbox 异步通知机制,它虽然不是完整的多 Agent 商业平台,但解决了多执行单元之间通信的核心问题。

Mailbox 的设计是:后台任务、Cron、子 Agent 完成后,写入 mailbox 消息,每条消息包含 source(来源)、wake_policy(是否唤醒主 Agent)、content(结构化内容)。消息消费后标记 consumed_at,如果运行被取消则回滚为未消费,保证不丢消息。这个机制可以自然扩展到多 Agent 场景——每个子 Agent 就是一个消息生产者,主 Agent 在下一轮模型调用前消费 mailbox。

除此之外,我研究过 Claude Code 和 Codex 的多 Agent 方案:

Claude Code 用的是 Master-Worker 模式。Coordinator 只保留 AgentTool + TaskStop 等少量工具,Worker 通过 runForkedAgent() 启动,共享 prompt cache(CacheSafeParams)。上下文传递比较"重"——Worker 直接拿到完整的 fork 上下文,适合编程场景的单次深度任务。

Codex 用的是 Actor Model,更灵活但更复杂。Agent 之间通过 Mailbox + TriggerTurn + SendMessage 通信,有 AgentRegistry 管理生命周期,agent_max_depth 限制防止递归爆炸。上下文传递有两种模式:FullHistory(完整历史)适合需要全局上下文的任务,LastNTurns(N) 适合独立子任务。

如果让我给 Coze 设计多 Agent 通信,我会融合这三家的经验:用 Mailbox 做异步消息总线(参考 FuserMate/Codex),用 AgentTool 做工具化调用(参考 Claude Code),上下文传递按任务类型灵活选择——独立子任务只传目标和约束,协作任务共享关键状态摘要。底层用 agent_max_depth + 超时 + 结果校验三层防护,防止级联故障。

可能追问

  • 子 Agent 执行失败了怎么处理?→ 可重试的指数退避 2 次;参数错误返回 error+Schema 让主 Agent 修正;不可恢复的返回清晰错误
  • 如何防止子 Agent 的幻觉污染主 Agent?→ 子 Agent 返回结果经过验证层:结构化输出校验、关键字段非空检查、结果长度阈值

B3. 多 Agent 协作中"记忆污染"怎么处理

面试官意图:考察你对多 Agent 系统级风险的认知深度。

推荐回答

记忆污染在单 Agent 场景只是单点问题——一个错误的工具结果或推理结论影响后续决策。但在多 Agent 系统中,污染会通过共享上下文或协调消息传播,变成系统性风险。

最危险的场景是:A Agent 产生了一个错误但"看起来合理"的判断,通过共享状态传给 B Agent,B 基于这个错误判断继续推理并产生新结论,C Agent 再基于 B 的结论行动——形成围绕错误信念的共识,比单一错误更难检测。

我的应对思路有三层:

第一层是独立验证路径。关键的中间结论不依赖单一 Agent 的判断,而是让另一个独立 Agent(或规则引擎)做交叉验证。比如 FuserMate 的工具安全策略就是这种思路——模型输出不可信,由运行时独立做策略评估。

第二层是信息的可信度标记。不同来源的信息标注不同的信任级别:用户直接输入 > 工具确定性结果 > Agent 推理结论 > Agent 推测。高风险的决策不能仅依赖低可信度信息。

第三层是状态的可回滚性。FuserMate 的 AgentRun 快照思路可以扩展——每个关键决策点前保存 checkpoint,如果后续发现有污染,可以回滚到安全点重新执行。

可能追问:怎么发现记忆污染?→ 通过 trace 和评测。离线评测集设计"对抗样本"——故意插入错误中间结果,验证系统能否抵抗污染。


B4. 两个 Agent 产生不可调和的分歧时怎么处理

面试官意图:考察 Multi-Agent 架构中的冲突解决设计。

推荐回答

我的核心观点是:分歧不是错误,是不确定性的信号。处理方式取决于分歧发生在什么类型的操作上。

对于不可逆或高风险操作——比如发消息给用户、修改数据库、调用外部付费 API——不应该由某个 Agent "赢"来裁决,而应该触发升级(escalation)。把分歧信息、各 Agent 的理由、影响范围打包,交给人类决策或更高层的协调者 Agent。这类似于分布式共识系统中的原则:安全性优先于活性(Safety > Liveness),在不确定时,宁可不决策也不要错误决策。

对于低风险、可回滚操作,可以让其中一个 Agent 先执行,同时记录分歧和回滚预案。执行后收集反馈,如果结果有问题就回滚。

协调者 Agent 的职责不是"比 Worker 更聪明地决策",而是执行决策纪律——要求每个 Agent 明确自己的假设,拒绝欠规范的方案,在不确定性超过阈值时延迟执行。

具体实现上分三步:

  1. 识别分歧来源:是假设差异、约束差异、还是目标理解差异
  2. 判断操作可逆性:不可逆 → 升级;可逆 → 先执行并收集反馈
  3. 记录并学习:分歧本身就是评测信号,持续出现分歧的环节说明系统设计或 prompt 有问题

B5. Agent 的上下文漂移怎么解决

面试官意图:考察长任务中 Agent 稳定性的工程经验。

推荐回答

上下文漂移本质上是 Agent 在长任务执行中逐渐偏离原始目标。核心原因是模型在每次推理时注意力的分布会变化——早期的约束和子目标在长上下文中被"稀释"了。

我在 FuserMate 中通过三个机制来对抗漂移:

第一,CompactionService 的结构化摘要。摘要 prompt 明确要求保留"当前目标"和"暂停约束"——比如"用户要求代码必须兼容 Python 3.10"这种约束,如果只在第一轮提过一次,几十轮后模型很容易遗忘。摘要让关键约束始终在模型注意力范围内。

第二,AgentRun 快照的冻结机制。每次运行启动时,把当前的目标、workspace 配置、启用的工具集、memory 都冻结成快照。这样即使上下文被压缩,外层的运行时状态不会漂移——Agent 能做什么、不能做什么、目标是什么,这些不在 LLM 的上下文里,而在系统层控制。

第三,关键约束的 Pin 机制。用户标记为"重要"的指令或约束,不参与 MicroCompactor 的清理,始终保留在上下文中。不是所有消息都平等,有些约束必须"钉"在上下文里。

还有一个设计原则:Agent 永远不应从自身意图推断执行状态。Agent 不能说"我认为我做了 X,所以 X 已经完成了"——这就是漂移的根源。工具交互必须外部可验证、幂等、可恢复。


B6. Prompt 工程的稳定性如何保障

面试官意图:考察 Prompt Engineering 的工程化思维,而非调参经验。

推荐回答

换个说法效果差十倍,根本原因是 LLM 在语义空间中对不同表述的敏感度差异很大。对同一个指令,"请你做 X"和"你的任务是 X"在模型的 embedding 空间里激活的路径可能不同,尤其当 X 涉及多步推理或工具选择时,措辞的微小差异会被后续链式推理放大。

解决问题的思路不是"找到一个 magic prompt",而是把 prompt 当作代码来管理。我在 FuserLab 做的 Prompt 编排系统就是基于这个思路:

  1. 版本管理:Prompt 用 Jinja2 模板定义,每次修改有版本记录,支持回滚。改 prompt 和改代码一样走版本流程。
  2. 评测闭环:改 prompt 之前,先跑离线 benchmark(RexUniNLU),看 intent、slots、complexity 各维度是否有回归。没有评测就改 prompt 等于蒙着眼睛开车。
  3. Badcase 回灌:线上 trace 发现的错误 case,直接写进离线评测集变成固定 regress case。这样每次改 prompt 都能看到会不会引入老问题。这个闭环是保证质量不退化的关键。
  4. LFU 缓存:编译后的 prompt template 用 LFU 策略缓存,避免每次请求都重新渲染。

另外,System Prompt 和 User Prompt 的职责要严格分离。System Prompt 放"规则"——工具使用规范、安全约束、输出格式要求;User Prompt 放"任务"——具体要做什么。规则层面的措辞要经过充分测试,任务层面允许灵活变化。


B7. Coze 的拖拽式编排 vs. Agent 化编排

面试官意图:考察对 Coze 产品本身的理解,以及编排模式的选择判断力。

推荐回答

Coze 的两个版本代表了两种 Agent 编排哲学:

拖拽式编排(旧版)适合流程稳定、路径清晰的场景。比如固定的审批流程、FAQ 问答、标准信息抽取——输入明确、步骤确定、输出结构定义好。拖拽式的优势是可控——每一步做什么、分支条件是什么,开发者心里有数,出问题容易定位。

Agent 化编排(新版)适合目标不稳定、路径需要动态决策的场景。比如多轮任务执行、跨工具协作、用户意图不明确的开放式对话。Agent 化的优势是灵活——模型根据中间结果自行判断下一步做什么,不需要人工穷举所有分支。

但 Agent 化的代价是不可预测——你没法穷举模型会走什么路径,出了问题也难以复现。

我判断用哪个的标准是:任务的不确定性有多大。不确定性低用拖拽式(稳定可控),不确定性高用 Agent 化(灵活应变)。实际项目中两者经常混合——整体流程用拖拽式保证主干稳定,局部决策节点用 Agent 化处理模糊输入。

还有一个设计原则:节点设计不要按功能菜单拆,要按状态边界和失败恢复边界拆。每个节点应该做到"输入清晰、输出结构化、失败可恢复",这样不管用哪种编排方式,系统都是健壮的。


B8. Coze 有哪些不足?如果你是工程师会改进什么

面试官意图:这是必问题——考察你对产品的理解和改进思路。

推荐回答

我从开发者的视角来讲几个方向:

第一,上下文工程的可配置性。Coze 目前的知识库 + 变量管理解决了"静态知识"的注入,但对"动态上下文"的管理——跨轮次的记忆压缩、关键约束的长期保持、工具结果的智能裁剪——还比较基础。开发者需要更细粒度的控制:哪些记忆应该持久化、什么时候触发压缩、压缩保留什么信息。

第二,工具生态的质量保障。Coze 的插件市场很丰富,但插件质量参差不齐。缺少标准化的测试框架——开发者在发布插件前没法自动验证插件的 Schema 正确性、异常处理、性能基线。建议引入插件评测机制(类似 App Store 的审核)+ 开发者的沙盒测试环境。

第三,可观测性和调试体验。Agent 出问题时,开发者需要知道"模型在哪一步想错了、工具为什么没调对、上下文是什么状态"。目前 Coze 的 Trace 更多是调用链展示,缺少根因分析——比如自动标记"这次失败可能是因为工具 X 的 Schema 描述不够清晰,模型填充参数时漏了必填字段"。

第四,多 Agent 协作的深度。目前 Coze 的多 Agent 模式还比较线性——一个主 Bot 调用子 Bot。更复杂的模式(并行 Agent + 投票、Agent 辩论、层级式 Agent 树)还支持不够。尤其缺少 Agent 间冲突检测和协调机制

如果让我选一个最有价值的方向先做,我会选可观测性和调试体验——因为这是 Agent 从"demo 能跑"到"生产可用"之间最大的鸿沟。


三、Multi-Agent 架构与协作

本章基于网上 MultiAgent 面试真题 + 学术 Benchmark 研究 + 你的 FuserMate Mailbox/Claude Code Coordinator/Codex Actor 研究经验编写。 多 Agent 架构是 Coze JD 的核心关键词之一,也是字节面试的深度追问方向。

M1. 多 Agent 协作模式有哪些?Orchestrator-Worker vs A2A 怎么选

面试官意图:考察你对多 Agent 拓扑结构的系统认知。

推荐回答

多 Agent 协作有六种主流拓扑,按控制集中度从高到低排列:

拓扑控制方式优势劣势适用场景
Supervisor(Orchestrator-Worker)中心化易审计、任务分配清晰单点瓶颈、Coordinator token 开销大结构化任务拆解(退款处理、巡检流程)
Hierarchical层级化分层管理、局部自治层间延迟、Manager prompt 复杂大型 Agent 团队、跨域协作
Pipeline/Sequential链式协调开销最低无并行、上游阻塞下游线性任务(提取→转换→验证)
Peer-to-Peer(Debate)去中心多视角、无单点难以收敛、token 爆炸验证、创意头脑风暴
Swarm/Handoff交接式上下文自然传递难以回滚、交接条件苛刻客服升级(通用→专业→人工)
Graph/Mesh网状灵活、分布式决策协调复杂度高研究任务、复杂问题求解

Orchestrator-Worker(星型拓扑)vs A2A(网状拓扑)的选择标准

维度Orchestrator-WorkerA2A
任务可预分解✅ 任务结构事先已知❌ 需要 Agent 自行发现协作方式
控制需求强审计、单点可控去中心、弹性优先
通信模式Worker → Coordinator 单向汇报Agent ↔ Agent 双向对等通信
上下文传递Coordinator 统一管理各 Agent 自行维护
典型协议Supervisor 分配任务A2A Agent Card + Task 生命周期

我的实践:FuserMate 的 Mailbox 机制更接近 Orchestrator-Worker——主 Agent 通过 Mailbox 收集后台任务/Cron/子 Agent 的结果。Claude Code 的 Coordinator Mode 也是 SuperVisor 模式——Coordinator 只保留 AgentTool + TaskStop。Codex 走的是 Actor Model(网状)——AgentRegistry + Mailbox + TriggerTurn。

选择建议:任务可预分解 → Supervisor;需要 Agent 自行协商 → A2A。实际生产中 Supervisor 模式最常见,因为大多数多 Agent 场景本质上是"分解得好就能做好",不需要 Agent 自由协商


M2. Tool 调用 vs 多 Agent 架构,本质区别在哪?什么时候必须拆 Agent

面试官意图:这是高频陷阱题——面试官想看你能不能讲清楚"拆分 Agent 的真正理由"。

推荐回答

本质区别有六个维度:

维度Tool 调用多 Agent
上下文隔离工具结果直接拼进主 Agent prompt子 Agent 有独立上下文,不受主 Agent 历史污染
独立规划LLM 在主上下文中规划,工具参与执行子 Agent 独立做子任务内的规划
并行性单个 Agent 串行调用多个 tool多个 Agent 可并行执行独立子任务
容错粒度一个 tool 失败,Agent 需要自行处理子 Agent 崩溃可被 Coordinator 独立恢复
System Prompt共享同一个 system prompt每个 Agent 有独立的 system prompt + 工具集
成本低(单上下文)高(多个上下文 + 协调开销),通常 4-5 倍

什么时候必须拆 Agent 而不是加 Tool?三条判断标准

  1. 子任务需要独立的 System Prompt 和工具集。比如"代码安全审计 Agent"需要安全领域的 system prompt + 专用的漏洞扫描工具,和主 Agent 的"帮用户写代码"是完全不同的角色。

  2. 子任务的上下文会和主任务互相干扰。比如主 Agent 正在排查一个数据库问题,同时需要分析一段日志——如果把日志分析的结果全部拼进主 Agent 上下文,会把排查思路污染掉。拆成子 Agent,返回结构化的分析结论,不污染主上下文。

  3. 子任务可以并行且有时间收益。比如同时审查 3 个微服务的代码,3 个 Agent 并行跑 vs 1 个 Agent 串行跑 3 次,有明显的延迟收益。

反模式:多 Agent 不是银弹。如果只是为了"看起来高级"而拆分,引入的协调开销(Coordinator 的 token 消耗、通信延迟、失败处理复杂度)可能远超收益。

一句话:Tool 解决"能做更多事",多 Agent 解决"能以不同角色思考和决策"。


M3. 多 Agent 通信:A2A 协议 vs MCP 是什么关系

面试官意图:考察对 Agent 协议生态的理解,2025-2026 年热点。

推荐回答

A2A(Agent-to-Agent)和 MCP(Model Context Protocol)是互补关系,不是竞争关系。

维度MCPA2A
解决的问题Agent ↔ Tool/Data 标准接口Agent ↔ Agent 互操作协议
类比USB 接口(连接外设)HTTP/gRPC(服务间调用)
状态无状态(每次调用独立)有状态(Task 生命周期管理)
关系主从(Agent 控制 Tool)对等(Agent 间协商)
核心概念Tool、Resource、PromptAgent Card、Task、Artifact

A2A 的核心概念

  • Agent Card:每个 Agent 的"名片",JSON 描述能力、输入输出 Schema、认证方式
  • Task:原子工作单元,有完整的生命周期(submitted → processing → completed/failed)
  • Streaming(SSE):实时推送中间结果
  • Push Notification:长任务完成后的异步回调
  • Artifact:Agent 产出的文件/数据,可直接传给下游 Agent

消息头的三层结构

  1. 路由层:目标 Agent ID、源地址、全局唯一 Message ID(幂等去重)
  2. 语义层:消息类型(任务派发、结果回传、状态更新、错误报告、心跳)
  3. 安全层:源签名、认证 Token、加密标记

工程实践:用 A2A 做 Agent 间任务分发,用 MCP 做每个 Agent 内部调用工具。两者在同一系统里分层协作。


M4. 如何防止多 Agent 死循环?循环检测与熔断机制怎么设计

面试官意图:考察生产级多 Agent 系统的稳定性设计。

推荐回答

多 Agent 死循环的典型场景:Agent A 把任务委派给 Agent B,Agent B 不理解又回给 Agent A → 无限循环。解决方案分三层:

第一层:循环检测(DFS on Call Graph)

维护全局调用图。每次 Agent A 要调用 Agent B 时,检查 B 是否已经在当前调用链中:

def detect_cycle(callee_id: str, call_chain: list, max_depth: int) -> bool:
    if callee_id in call_chain:
        return True  # 检测到环
    if len(call_chain) >= max_depth:
        return True  # 深度超限,视为环
    return False

调用链在每次跨 Agent 调用时追加当前 Agent ID,传递时带 call_chain + max_depth

第二层:深度限制

设置 agent_max_depth(参考 Codex)。任何 Agent 不能调用超过 N 层子 Agent(通常 N=3)。超过深度直接返回错误,不继续。这个机制简单但极有效——阻止了 90% 的级联调用失控。

第三层:三级恢复策略

级别策略触发条件
降级(Degrade)返回缓存/已知结果,不继续下游调用单次循环检测触发
降频(Rate Limit)降低调用频率,加等待队列短时间内多次循环检测
熔断(Circuit Break)彻底切断此调用链,标记子任务为"不可用"连续 3 次循环 → 熔断

收敛保证max_rounds + 进度检测——连续 2 轮没有实质性进展(State 无变化),强制终止并返回"无法完成"。

我的实践:FuserMate 的 AgentLoop 有 max_iterations=200 上限。Codex 有 agent_max_depth 防止递归爆炸。Claude Code 有 Terminal 枚举(11 种终止原因)——max_turnsprompt_too_long 等,这些都是收敛保证机制。在实际系统中,简单粗暴的上限比复杂的启发式更可靠


M5. 多 Agent 系统的上下文窗口管理怎么做

面试官意图:考察大规模多 Agent 系统的上下文工程。

推荐回答

多 Agent 的上下文窗口管理比单 Agent 复杂得多——Coordinator 需要同时关注多个 Worker 的结果,token 爆炸是常见故障。

Coordinator 的五条设计原则

  1. Single Writer:只有 Coordinator 写 goal / plan / completed_steps。Worker 只 append observation。避免状态写入冲突。

  2. Structured Communication:Worker 返回固定 JSON Schema,不是自由文本。自由文本会导致 Coordinator 的上下文被低信息密度的内容占满。

  3. Summary Compression:Coordinator 只读最近 N 轮 + 全局 completed_list。当上下文 > 80K token,用渐进式摘要把前半段对话压缩成 500 字摘要。

  4. Minimal Information Delivery:Worker 只收自己的任务上下文,不收全局 plan。不给 Worker 不需要的信息,减少 token 消耗。

  5. Instruction Bumping:关键规则追加到消息列表末尾(对抗 Recency Bias)——LLM 天然更关注消息末尾的内容。

渐进式摘要 vs. 结构化状态

  • 渐进式摘要(自然语言):保留语义、但 token 效率低
  • 结构化状态(JSON):{"completed": [...], "failed": [...], "pending": [...]},比自然语言省 70-80% token
  • 建议:生产环境优先用结构化状态 + 关键信息用自然语言补充

M6. 多 Agent 的评估体系怎么搭建?如何证明多 Agent 比单 Agent 好

面试官意图:考察评估工程的系统思维——"你不能管理你无法测量的东西"。

推荐回答

评估分四层:

评估什么核心指标
L1: 单 Agent 质量每个 Worker 的任务完成能力准确率、延迟、tool_call_success_rate
L2: 协调质量Coordinator 的拆解和分配能力Plan precision/recall、routing accuracy
L3: 端到端质量用户目标达成Completion rate、用户满意度
L4: 成本效率同等质量下的 token 消耗Cost per completion、avg rounds

多 Agent 准入 Gate(核心):

Stage 1 → Stage 2 准入条件:
  ✅ 单 Agent eval ≥ 80%
  ✅ Checkpoint + Trace + 离线 case ≥ 30

Stage 2 留存条件:
  ✅ 多 Agent eval ≥ 单 Agent + 5%(效果有显著提升)
  ✅ 多 Agent 成本 ≤ 单 Agent × 2(成本可控)
  ✅ avg_rounds ≤ 5(不会无限循环)
  ✅ timeout_rate ≤ 5%(大部分任务能完成)

失败 → 连续 3 天 eval 低于阈值 → 降低流量 → 回滚到单 Agent

关键原则单 Agent baseline 没做到 80% 之前,不要上多 Agent。多 Agent 是在 baseline 之上解决"一个 Agent 搞不定"的问题,不是用来掩盖单 Agent 质量差的。多 Agent 的成本通常是单 Agent 的 4-5 倍,completion 提升至少 ≥15% 才值得。

学术 Benchmark 的启示

  • **CooperBench(ICLR 2026)**发现:多 Agent 协作比单 Agent 独立完成成功率下降 30%——"协调的诅咒"。说明多 Agent 不是天然更好,设计不好反而更差
  • SILO-BENCH 发现:"Communication-Reasoning Gap"——Agent 自发形成了正确的通信拓扑,但在推理整合阶段系统性失败。通信是成功的,但综合判断是失败的
  • GEMMAS 发现:两个系统准确率只差 2.1%,但过程指标(信息多样性、冗余路径比)差 12.8% 和 80%。只看结果指标会漏掉关键问题

我的实践:FuserLab 的评测体系——离线 benchmark(RexUniNLU)+ case-level failure 分析 + badcase 回灌闭环。这套方法论可以直接扩展到多 Agent 评估。


M7. 多 Agent 的分布式 Tracing 与成本归因怎么做

面试官意图:考察生产环境可观测性。

推荐回答

多 Agent 的 Tracing 核心挑战是:一次用户请求可能被 Coordinator 拆成 N 个子任务,每个子任务被不同的 Worker 执行,Worker 之间可能还有嵌套调用。追踪链必须完整覆盖这个"一对多 + 嵌套"的拓扑。

Trace 链路设计

User Request (root span)
  └─ Coordinator.plan (span, parent=root)
       ├─ Worker_A.execute (span, parent=plan, agent_id="A")
       │    ├─ Worker_A.llm_call (span)
       │    └─ Worker_A.tool_call (span)
       ├─ Worker_B.execute (span, parent=plan, agent_id="B")
       └─ Coordinator.synthesize (span, parent=root)

每个 span 除了常规的 trace_id/span_id/parent_id/duration/token/ttft/error,还要加:

  • agent_id:哪个 Agent 产生的
  • agent_role:Coordinator / Worker / Reviewer
  • task_id:属于哪个子任务
  • round_number:第几轮协调

成本归因:token 消耗按 agent_id 分组,可以看到每个 Agent 的 token 占比。如果 Coordinator 的 token 占到了 50% 以上——说明任务拆得太碎、协调开销过大,该合并一些 Agent。

关键指标看板

  • 每个 Agent 的 P50/P99 延迟
  • 每个 Agent 的 token 占比
  • Agent 间调用次数分布(识别热点通信路径)
  • 失败率按 Agent 分组(识别"问题 Agent")
  • Loop detection 触发次数

我的实践:FuserLab 的 TraceCallbackHandler 把 trace_id 贯穿全链路,span 之间通过 parent_id 串联。扩展到多 Agent 时,只需要在 span 上加 agent_id 和 task_id 两个字段——核心的 trace 基础设施不需要推翻重建。


M8. 什么情况下「不应该」上多 Agent

面试官意图:考察工程判断力——"知道什么时候不做"比"知道怎么做"更难。

推荐回答

多 Agent 只在三个条件同时满足时才有意义:

  1. ✅ 子任务可并行且上下文可隔离
  2. ✅ 角色技能差异显著(需要不同的 system prompt + 工具集)
  3. ✅ 单 Agent 完成率 < 目标(baseline ≥ 80% 后再考虑)

不应该上多 Agent 的六种情况

  1. 任务简单、步骤固定:一个 Chain 就能搞定的事,引入多 Agent 是杀鸡用牛刀
  2. 单 Agent 效果还没做扎实:baseline 不到 80%,先优化 prompt、工具、RAG,别急着加 Agent
  3. 上下文强耦合:子任务之间有大量共享状态,拆开反而需要花更多 token 来传递上下文
  4. 延迟敏感的场景:多 Agent 的协调开销(Coordinator 推理 + 消息传递 + 结果汇总)会增加端到端延迟
  5. 团队没有多 Agent 运维经验:多 Agent 的出问题路径是指数级的——2 个 Agent 有 A→B 和 B→A 两条错误路径,5 个 Agent 有 20 条
  6. Tool 调用就能解决:如果给模型更好的 tool description 和更多 tool 就能搞定,加 Agent 是过度设计

一句话总结:多 Agent 解决的是"角色维度"的问题——不同的 Agent 以不同的身份思考。如果你的场景不需要不同的身份,给一个 Agent 加更多 tool 通常是更优解。


四、工具系统与协议

C0. MCP vs Function Calling 深度对比:原理、架构与选择

面试官意图:这是字节面试的深度追问——不仅要知道"区别是什么",还要讲清楚两者的底层原理。

推荐回答

一、一句话定性

Function Calling 是模型厂商的"内置特性",MCP 是跨模型的"开放协议"。 Function Calling 解决"模型怎么调用工具"的问题,MCP 解决"工具怎么被 Agent 生态系统发现和管理"的问题。它们处于不同抽象层,互补而非竞争。


二、Function Calling 的底层原理

核心:LLM 通过特殊 Token 被训练成"工具调用状态机"。

① 训练层面:模型不是靠 prompt 学会调用工具的,而是通过微调(SFT + RL)在学习阶段就内化了工具调用模式。

模型微调时使用特殊 Token 标记不同阶段:

Token 类型示例作用
工具声明<|im_system|>tool_declare包裹 function schema 注入 system prompt
工具调用<start_function_call> / [TOOL_CALLS]标记模型要输出一个结构化调用
工具结果[TOOL_RESULTS] / <|tool_result|>包裹外部执行结果,回传给模型
质量条件<|high_reward|>RL 训练时用于区分优/劣轨迹

② 推理层面:Thought → Act → Observe 三段循环

用户输入 → 模型内部推理(Thought:需要调什么工具?)
         ↓
模型输出 <start_function_call> + 结构化 JSON(Act:精确的工具名+参数)
         ↓
系统执行工具,拿到真实结果
         ↓
结果注入回上下文 [TOOL_RESULTS](Observe:模型理解结果)
         ↓
模型判断:继续调工具 or 输出最终回答?
         ↓ 需要更多信息
         回到 Thought,循环

③ tool_choice 参数控制

含义
auto模型自行判断是否需要调工具
none禁止调工具,纯文本回复
required必须调工具
{"type": "function", "function": {"name": "xxx"}}强制调指定工具

④ 底层数据结构:工具定义以 JSON Schema 形式注入 messages 数组的 system/tool 消息。模型输出的 tool_calls 数组包含 idfunction.namefunction.arguments(JSON 字符串)。下一个 turn 以 role: "tool" + tool_call_id 回传结果。

⑤ 关键局限:每个模型厂商有自己的 function calling 格式——OpenAI 用 parameters + strict 模式,Anthropic 用 input_schema,Google 用 OpenAPI 子集。工具定义不能跨模型复用,切换模型 = 重写工具定义。


三、MCP 的底层原理

核心:基于 JSON-RPC 2.0 的三层 Client-Host-Server 架构。

① 三层架构

Host(AI 应用) — 管理者
  ├── 管理安全策略、用户同意
  ├── 协调 LLM 集成
  └── 创建多个 Client 连接不同的 Server

Client(连接器) — 1:1 连接一个 Server
  ├── 每个请求附带协议版本 + 能力声明
  └── 与 Server 建立双向 JSON-RPC 通道

Server(工具提供方) — 暴露 Tools / Resources / Prompts
  ├── 独立部署、独立版本、独立生命周期
  └── 不知道对话上下文(Host 隔离)

② JSON-RPC 2.0 消息格式

请求(有 id → 需要响应):
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {"name": "weather", "arguments": {"city": "Beijing"}}
}

响应(匹配请求 id):
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {"content": [{"type": "text", "text": "Beijing: 22°C"}]}
}

通知(无 id → 不需要响应):
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {"progress": 50}
}

③ 连接生命周期

1. Initialize → Client 发 capabilities + protocol version
2. Server 回传 capabilities(有哪些 tools/resources/prompts)
3. Client 发 initialized 通知
4. 双向通信(工具调用 / 资源读取 / 提示词获取)
5. Shutdown(graceful disconnect)

④ 传输层

传输方式适用场景原理
stdio本地进程子进程 stdin/stdout 走 JSON-RPC,延退最低
Streamable HTTP(推荐)远程服务单一 HTTP endpoint 支持双向流式 JSON-RPC
HTTP + SSE⚠️弃用双通道模式,被 Streamable HTTP 取代

⑤ 核心设计原则

  • 无状态:每个请求自包含(协议版本 + 客户端身份 + 能力声明),Server 不推断历史状态
  • 动态发现:Client 通过 tools/list 实时查询 Server 有哪些工具,而非硬编码
  • Server 看不到对话上下文:Host 只发送必要的参数给 Server,对话历史不泄露到工具提供方

四、核心差异对比表

维度Function CallingMCP
本质LLM 推理能力的一环(特殊 Token + 微调)工具生命周期管理协议(JSON-RPC 2.0)
抽象层模型层:"模型怎么输出工具调用"架构层:"工具怎么被注册、发现、调用、管理"
工具发现静态:每次请求显式传入 tools 数组动态:tools/list 实时查询 Server 能力
跨模型❌ 每家的 FC 格式不同✅ 同一 MCP Server 对接任何兼容的 Agent
状态完全无状态,每次请求独立协议无状态,但支持持久连接
热插拔❌ 改工具 = 改代码部署✅ Server 独立部署,动态注册/卸载
安全模型依赖模型层的 policy协议层:OAuth 2.1 / RBAC / 审计
工具定义JSON Schema(内联在 API 请求中)JSON Schema(在 Server 侧定义,tools/list 返回)
M×N 问题M 个应用 × N 个数据源 = M×N 次集成M 个应用 + N 个 MCP Server = M+N 次集成
延迟零额外开销(模型直出)多一层 JSON-RPC 转发,额外 1-5ms

五、协作模式:分层而非对立

两者在实际架构中处于不同层次,可以且经常同时使用:

┌─────────────────────────────────────────┐
│  LLM 推理层                              │
│  Function Calling: 模型输出 tool_call    │
│  "我需要调用 weather 工具,城市=北京"     │
└──────────────────┬──────────────────────┘
                   ↓ tool_call JSON
┌─────────────────────────────────────────┐
│  MCP 协议层                              │
│  Client → tools/call → Server           │
│  标准化传输、认证、审计                   │
└──────────────────┬──────────────────────┘
                   ↓ JSON-RPC
┌─────────────────────────────────────────┐
│  工具执行层                              │
│  weather("Beijing") → {"temp": 22}      │
└─────────────────────────────────────────┘

一句话:Function Calling 是"模型的嘴",MCP 是"工具的插座"——嘴说想用什么工具,插座保证工具能插上。


六、选型指南

场景方案
快速原型、单一模型直接用 Function Calling,简单快速
多模型切换、跨平台MCP 统一工具接口,一次集成到处能用
企业级多工具系统MCP + Function Calling 分层:MCP 管工具生命周期,FC 管意图理解
本地工具(文件系统、数据库)MCP stdio transport,最低延迟
外部 API(天气、搜索、支付)MCP Streamable HTTP + OAuth 2.1
需要严格审计合规MCP 的 OAuth + RBAC + 审计日志

C1. MCP 协议在 Agent 系统中起什么作用

面试官意图:考察对行业标准的理解和实际集成经验。

推荐回答

MCP(Model Context Protocol)解决的是 Agent 工具生态的标准化问题。在 MCP 之前,每个 Agent 平台都要自己定义工具的注册、发现、调用协议——Coze 有 Coze 的插件协议,LangChain 有 LangChain 的 Tool 抽象,各自不互通。MCP 的价值在于让工具提供方只实现一次,所有兼容 MCP 的 Agent 平台都能用。

我在 FuserMate 中直接集成了 MCP server。MCP 工具和内置工具通过 ToolRegistry 统一管理,模型调用时无差别使用。

但 MCP 目前也有不成熟的地方:工具的 input_schema 描述质量参差不齐,直接影响模型正确调用率;没有标准化的工具测试和评测机制;安全模型还不完善。对比 Coze 的插件生态,Coze 的优势是插件经过了平台审核和质量把控,但代价是只能在 Coze 内使用。MCP 是开放标准,生态更广但质量不可控。长期看,MCP 应该会成为 Agent 工具的标准协议。

可能追问:MCP 和 Function Calling 是什么关系?→ MCP 是工具发现和生命周期管理协议,Function Calling 是模型调用工具的具体接口。它们是不同抽象层的东西,互补而非竞争。


C2. Skill 和 Tool 的区别是什么?怎么设计 Skill 体系

面试官意图:考察对 Agent 能力封装的抽象能力。

推荐回答

Tool 是原子能力——做一件事,比如读文件、执行 bash、调 API。Skill 是可复用的提示词 + 工具组合——封装了一个领域知识或工作流。

举个例子:一个"代码审查"Skill,内部包含 System Prompt(审查标准和关注点)、工具白名单(Read、Grep、Glob)、输出格式要求(问题分级、修复建议)。用户说"/review",Skill 被加载,注入到上下文,模型在这个 Skill 框架下工作。

FuserMate 的 Skill 系统参考了 Claude Code 的 /skill 机制。Skill 放在 ~/.claude/skills/ 目录下,每个 Skill 是一个 Markdown 文件,包含触发关键词、system prompt 扩展、推荐工具集。

Skill 体系的设计要点:

  1. 触发方式:命令式(用户手动 /skill-name)和自动匹配式(根据用户意图自动加载)
  2. 作用域:全局 Skill 和项目 Skill
  3. 权限控制:Skill 声明需要的工具,但实际能否调用仍受安全策略控制
  4. 组合性:Skill 之间不冲突,可以同时加载多个

理想情况下,Skill 底层可以调用 MCP 工具,形成"Skill → Tool (MCP) → 执行"的层次。


C3. 工具调用失败了怎么办?重试策略怎么设计

面试官意图:考察 Agent 工程化的容错设计。

推荐回答

FuserMate 的 ToolExecutor 对工具失败做了三层处理:

第一层:可重试错误。 比如网络超时、临时资源不可用。策略是指数退避重试 2 次(1s → 2s),失败后进入第二层。

第二层:参数错误。 比如模型传了错误的参数类型、缺少必填字段。不会重试——因为重试同样的参数大概率还是失败。而是把 error message + 工具的 input_schema 一起返回给模型,让模型理解"哪里错了、正确格式是什么",然后修正参数重新调用。

第三层:不可恢复错误。 比如权限拒绝、资源不存在、系统硬拒绝。返回清晰的 error,附带建议替代方案——比如"文件不存在,可能是路径错误?试试用 Glob 搜索类似文件名"。

这里有一个重要设计:错误信息要结构化返回。工具异常统一抓取,转成 {"error": "...", "suggestion": "...", "retryable": false} 格式。模型更容易理解结构化错误,而不是自由文本的 traceback。

还有一个预防性保护:工具结果截断。大结果(bash/read/grep 等)默认截断到 50,000 字符,保留 truncated: true 标记——防止一个 cat 大文件 撑爆上下文窗口。

可能追问:怎么防止模型死循环重试同一个工具?→ max_iterations=200 上限 + 连续调用同一工具且连续失败超过 3 次,主动插入系统提示让模型换策略。


C4. 工具太多时怎么管理?工具路由怎么做

面试官意图:考察大规模工具体系的架构能力。

推荐回答

工具太多时,核心问题不是"存不下"——模型上下文窗口越来越大——而是模型在选择工具时的注意力分散。30 个工具的定义塞进 system prompt,模型容易选错工具,而且 token 消耗大幅增加。

FuserMate 的做法是按维度做工具分层注入

  1. 按 toolset 分组:每个工具标记所属的 toolset(如 file_opswebdatabase),Agent 配置时按场景选择注入哪些 toolset
  2. 按 risk_level 过滤:高风险工具(如 bash、write)在特定场景可以不注入
  3. 按 enabled_by_default 控制:只有通用工具默认启用,领域专用工具需要显式开启

如果要做得更细,可以引入工具路由器——一个轻量级分类模型在调用前判断用户意图属于哪个领域,然后只注入该领域的工具集。这个分类器的延迟必须很低(<100ms),否则得不偿失。

Coze 在这方面的做法是工作流节点级别的工具绑定——每个节点只暴露需要的工具,天然避免了工具爆炸。但代价是灵活性下降。所以这是一个经典的"灵活 vs. 可控"的权衡。


C5. 你们怎么处理高并发下的模型推理延迟

面试官意图:考察大规模 AI 系统的性能优化实战经验。这是你的最强差异化武器。

推荐回答

我实际部署和调优过 vLLM-Ascend 推理集群,用的是华为 910B 8 卡 NPU,部署了 4 个千问系列模型。

有一次线上报 sequence length exceeds max_model_len——集成了 RAG 知识库后,system prompt 膨胀,加上工具定义数量增加,单次输入超过 5000 token,超过了当时的 max-model-len 5500。

调整分三步:

第一步,拉 max-model-len 到 12000,同时把 gpu-memory-utilization 从 0.9 降到 0.85。因为长上下文的 KV cache 需求翻倍了,不降显存利用率会 OOM。

第二步,调并发参数。max-num-seqs 从 64 降到 32——单个请求的显存占用变大,并发要收敛。

第三步,确认 chunked-prefill 和 prefix-caching 开着。chunked-prefill 防止长 prompt 一次性 prefill 时显存峰值过高;prefix-caching 复用 system prompt 和工具定义的 KV cache,长上下文场景收益非常明显。

还有一个重要参数是 block-size。vLLM 默认 16,我们改成了 128。长上下文场景下 block_size=16 会切出上千个块,block manager 调度开销很大。128 把块数降到 1/8,910B NPU 对大块连续内存也更友好。代价是内部碎片——短回复会有一些浪费,但在 8k-12k 输入场景下这点碎片可以忽略。

网关层并发控制:模型前面还有一层 OpenResty + Redis 网关。Qwen3-30B-A3B 设了全局并发上限 64、生产 40、测试 24,max_wait 60 秒。模型层面的参数调整和网关层面的并发控制要一起看,不能只调一边。

另外在 FuserLab 的语音控制中,我定位过一个非模型延迟问题。通过 stage timing 把链路拆开计时后发现,延迟不在 LLM 推理,而在 LangGraph Postgres checkpointer 的连接创建。我把 AsyncConnectionPool 初始化移到应用启动阶段,请求时直接复用,延迟明显下降。Agent 系统的延迟瓶颈经常不在模型本身,而在基础设施的初始化开销

参数速查

  • sequence length exceeds → max-model-len 不够
  • 报 OOM/device memory failed → gpu-memory-utilization 太高
  • 请求排队超时 → 并发数太高

可能追问

  • block-size 为什么是 128 不是 256?→ 256 碎片率太高,128 是长上下文和碎片之间的平衡点
  • max-model-len 为什么不直接设 32K?→ 显存有限,设越大并发越低。12K 够覆盖绝大部分请求。参数调优不追求极值,追求当前负载下的最优平衡

C6. Agent 系统的"千人千面"个性化怎么实现

面试官意图:考察个性化架构设计能力。

推荐回答

"千人千面"在 Agent 系统里本质是三个维度的个性化:

第一个维度是配置级个性化。FuserMate 的核心模型拆分——Agent(身份+memory)、Workspace(路径+tools+skills)、Session(对话)——天然支持个性化。每个用户的 Agent 有不同的 system prompt、memory、启用的工具集和 skills。这层是个性化的基础。

第二个维度是记忆级个性化。用户的偏好、习惯、历史决策通过 memory 系统持久化。不是把原始对话存下来,而是结构化提取:用户偏好哪些工具、喜欢简短还是详细回复、有哪些重复性任务模式。下次对话时按相关性检索注入上下文。

第三个维度是行为级个性化。这层最复杂。比如同一个"帮我分析数据"的请求,数据科学家希望直接跑 Python 脚本,产品经理希望在 Excel 里呈现,管理者希望一个高层摘要。系统需要根据用户的角色和历史行为模式,自动选择不同的执行策略和输出格式。

技术实现上,三层递进:配置是最确定的(用户显式设置),记忆是半结构化的(从对话历史中提取),行为是最模糊的(需要模式识别)。明确的归系统、模糊的归模型——这是个性化架构的核心原则


C7. Agent 执行链路如何保证长任务的正确性

面试官意图:考察长任务可靠性的工程经验。

推荐回答

长任务的核心挑战是:执行可能跨分钟甚至小时,期间进程可能重启,工具可能有副作用,模型可能遗忘早期目标。

FuserMate 通过以下机制保证长任务正确性:

执行快照(AgentRun):每次运行启动时,冻结当前的 Agent 配置、Workspace 路径、启用的 tools/skills、memory 状态为一个不可变快照。运行过程中即使外部配置发生了变化,当前运行不受影响。

崩溃恢复(reconcile_startup):服务启动时扫描所有 session,所有 status="running" 的 AgentRun 全部标记为 "failed",记录错误"服务重启或运行进程丢失"。这保证了不会出现"僵尸运行"。

增量持久化:Bash 后台任务每 1000ms 持久化一次输出快照。即使进程崩溃,已经产生的输出不丢失。

Mailbox 消息去重:Mailbox 消息消费后标记 consumed_at。如果运行被取消但后台任务实际已完成,consumed_at 回滚为 None,下次运行时重新消费。

如果要做 K8s 多副本高可用,我的设计方案(docs/agent_checkpoint_interrupt_ha_design.md)是将运行状态拆为 runs、checkpoints、writes、events 四层持久化到 Postgres,用 lease + fencing token 防止多 worker 同时执行同一个 run。

设计原则:Agent 永远不能从自身意图推断执行状态。工具交互必须外部可验证、幂等、可恢复。


C8. 如何降低推理成本?效率 vs 准确性的权衡

面试官意图:考察成本意识和工程权衡能力。

推荐回答

推理成本优化我从三个层面来做:

缓存层面:vLLM 的 prefix-caching 是性价比最高的优化。System prompt 和工具定义在大多数请求中完全一致,这些 token 的 KV cache 可以直接复用,节省 prefill 阶段的计算。在语音控制场景,这部分能节省约 20-30% 的 prefill 时间。

模型路由层面:不是所有请求都需要最大的模型。简单意图分类、关键词提取用 Qwen3-4B 完全够用,复杂推理才路由到 Qwen3-30B。

上下文压缩层面:FuserMate 的两层压缩同时也是成本优化。MicroCompactor 减少了每轮模型调用的输入 token,CompactionService 让长对话不会无限增长。

效率 vs 准确性的权衡:我的原则是——关键路径上不省。意图识别、工具选择、安全校验这些环节如果出错,后续全部白费。但辅助环节可以省:历史消息的摘要可以用小模型做、非关键检索可以降级到更简单的策略。就像系统工程里的"瓶颈在约束上"——非约束环节的优化空间远大于约束环节。


五、Agent 运行时深挖(AgentLoop / 安全 / Trace / RAG)

D1. AgentLoop 怎么设计(源码级全链路)

面试官意图:考察 Agent 运行时、工具循环和事件模型。

推荐回答(基于源码 backend/src/agent/core/agent_loop.py):

AgentLoop 的入口是 stream(messages),核心是一个 for 循环包裹的迭代过程,默认 max_iterations=200。

每轮迭代做了四件事:

第一步:准备消息。 _prepare_iteration_messages 先调用 context_update_provider 注入动态上下文(比如 system prompt、memory 刷新),然后走 micro_compactor 做上下文压缩——消息太多时自动裁剪,防止超出模型窗口。

第二步:流式调用模型。 _stream_model_response 调用 model_client.stream(messages, tools=tool_definitions),用 _handle_model_chunk 逐 chunk 分类处理:

  • kind="message" → 发 message_delta 事件,累积 assistant_parts
  • kind="thinking" → 发 thinking_delta 事件,累积 reasoning
  • kind="tool_call" → 收集到 tool_calls 列表

每个 chunk 之后都检查 cancel_event,一旦被外部 set 就立即中断并发送 run_cancellingrun_cancelled

第三步:判断结果。 流结束后如果没有 tool_calls,走 message_display hook 处理显示内容,发 message_done,外层收到后发 run_completed 结束。如果有 tool_calls,走第四步。

第四步:执行工具。 先把 assistant 消息(含 tool_calls)append 到消息历史,发 assistant_tool_call_done。然后逐个执行工具,_execute_one_tool 做了五层处理:

  1. 执行前检查 cancel
  2. pre_tool_use hook,hook 可以 deny(直接返回失败)或修改工具参数
  3. tool_call_started
  4. 通过 ToolExecutor 调用 execute_async——异步 handler 直接 await,同步 handler 用 asyncio.to_thread 放到线程池,避免阻塞事件循环
  5. 结果发 tool_call_completedtool_call_failed,走 post_tool_use hook

工具结果作为 role="tool" 消息 append 回 current_messages,进入下一轮模型调用。

保护机制有五层:

  • max_iterations:默认 200,超出发 run_failed 并返回
  • cancel_event:在迭代前、每个 chunk 后、工具执行前/后都检查,支持用户随时中断
  • 工具结果截断:ToolExecutor 对 bash/read/grep/glob 等大结果默认截断到 50,000 字符,保留 truncated 标记
  • 错误 JSON 化:工具异常和 handler 异常统一抓取,转成 {"error": "..."} 格式返回给模型,不让异常直接炸掉 loop
  • 顶层 try/except:任何漏网异常被 catch,脱敏后发 run_failed,不会让 Agent 处在不可知状态

这里有一个重要的设计决策:AgentLoop 本身不做持久化,只生产语义事件。持久化和快照交给外层的 runtime adapter。这样 AgentLoop 保持纯粹,方便测试和替换。


D2. FuserMate Agent 全链路技术细节

面试官意图:深入考察 Agent 平台架构——不只是讲单个模块,而是全链路串联。

整体架构层次

API (FastAPI) → RuntimeManager → SessionRunner → AgentLoop
                                         ↓
                              CompactionService / MicroCompactor
                              HookDispatcher / CronService
                              Mailbox / PostDispatch / Task

1. 用户输入:HTTP POST 或 WebSocket(支持 input/control/ping/resume 四种帧)。请求到达 SessionRunnerService,进入并发控制逻辑。

2. SessionRunner 并发模型:每次运行是独立 asyncio.Task,受 asyncio.Lock 保护。同时间只有一个活跃运行,优先级链:用户在后台运行期间发消息 → 抢占取消后台 → 入队项 FIFO → 后台唤醒。

3. AgentLoop 主循环:max_iterations=200 的 for 循环,每轮四步(详见 D1)。

4. 中断与恢复:中断由 asyncio.Event 实现。服务器崩溃恢复:reconcile_startup() 启动时扫描所有 session,把所有 running 状态标记为 failed。WebSocket 断线恢复:客户端发 resume + cursor,服务端重放事件。

5. 上下文压缩(两层):MicroCompactor 规则级(每轮自动)+ CompactionService 模型驱动(128K 触发)。

6. Mailbox 异步通知机制:后台任务/cron/subagent 完成后写入 mailbox,根据 wake_policy 决定是否唤醒 Agent。消息消费后标记 consumed_at,运行取消则回滚。

7. Hook 系统:基于 RunHookDispatcher 的观察者模式,8 个活跃 hook 事件。merge_hook_decisions() 合并多观察者输出:block 最先、deny 次之、allow/ask 继续。

8. Runtime 事件系统:27 种事件类型,写入 JSONL 文件(每个 session 一个),自动分配递增 cursor。

9. Task / Cron / Background:CronService(croniter + IANA 时区)、PostDispatchService(产物分发)、Background task(bash 子进程 + 1s 快照持久化)。

可能追问

  • AgentLoop 为什么不做持久化?→ AgentLoop 只负责生产语义事件,持久化交给外层 runtime adapter,保持 AgentLoop 纯粹、可测试、可替换
  • MicroCompactor 为什么是规则级?→ 跑在每次模型调用前,如果用模型做摘要会增加延迟。规则级零延迟
  • Mailbox 怎么避免消息丢失?→ 持久化在 FileRuntimeStore,写完后才标记投递。崩溃时回滚未完成运行状态

D3. 工具安全怎么防 Prompt Injection

面试官意图:考察 Agent 安全意识,JD 明确要求。

推荐回答

我的核心原则:模型输出不可信,工具执行必须由运行时兜底

FuserMate 的安全策略没有写在 prompt 里(因为 prompt injection 可以绕过 prompt 约束),而是实现在工具执行入口。具体分四层:

第一层 system_hard_deny:系统级硬拒绝。比如 Bash 命令解析后,识别 git reset --hardgit clean -fdxcurl | bashrm -rf / 等危险命令,直接拦截。用户 allow 不能覆盖这一层。

第二层 user_deny:用户配置的黑名单。

第三层 user_ask:需要用户确认。中等风险的工具调用触发 pre_tool_use hook,弹窗让用户确认。

第四层 user_allow:默认允许的低风险操作(读文件、搜索等)。

Bash 命令解析是安全的关键。我不是简单做字符串匹配,而是把 Bash 输入解析成 AST:识别 simple command、pipeline(|)、command substitution($(...))、shell -c 等结构,然后逐条命令匹配安全策略。路径工具也会校验:workspace 边界、symlink 追踪、敏感路径限制。

可能追问

  • 为什么 user_allow 不能覆盖 system_hard_deny?→ 因为 system_hard_deny 是对系统完整性的保护,用户可能被社会工程学攻击诱导点击 allow,但系统必须兜底
  • 如果模型把危险命令拆成多段怎么办?→ 更彻底的方案是沙盒执行(参考 Codex 的 bwrap/landlock)。FuserMate 选了策略评估而非沙盒,是因为私有化部署不能引入系统依赖——场景驱动的取舍
  • 工具调用审计怎么做?→ 全部记录在 runtime events 里,JSONL 持久化,按 session 组织,可以完整回溯

D4. FuserLab 的 Trace 和评测怎么做

面试官意图:考察可观测和评测平台经验。

推荐回答

Trace 这边,我们没有从零造轮子,而是利用 LangChain/LangGraph 的 callback 机制。我写了一个 TraceCallbackHandler,继承 BaseCallbackHandler,在模型调用的关键节点把运行过程转成 span。每个 span 里记录了 trace_id、span_id、parent_id、span_type、span_name、start_time、duration、app_id、input、output、model、token、ttft 和 error_message。trace_id 在请求入口生成或透传,同一次请求的所有 span 共享一个 trace_id。这样一次用户请求的链路从 root 到 LLM 到 tool 到 prompt 全部串起来,慢在哪里、错在哪一步直接定位。

评测这边,我实际做过语音控制 RexUniNLU 的 benchmark。评测集是按 case 组织的,每个 case 标记了 complexity——简单还是复杂——以及预期的 intent、slots。评估的时候不会只看总分,而是看 case-level failure:intent 有没有漂移、slot 有没有漏抽、复杂指令有没有被简单 schema 误吞。我特别看重的一点是线上 badcase 的回灌——线上 trace 里发现的错误,把它写进离线评测集变成固定 regress case,这样每次改 prompt 或切模型,跑一遍 benchmark 就能看到会不会引入新问题。这个闭环是保证质量不退化的关键。


D5. 你怎么优化过 LangGraph 语音控制延迟

面试官意图:考察端到端性能优化能力。

推荐回答

整体链路是 ASR → LLM → TTS 三段。ASR 用了流式识别减少等待,但流式的准确度会下降,所以在关键搜索接口上用了 AC 自动机对输入文本做关键词标注,直接告诉大模型"这是一个实体词",减少模型误判。TTS 也是实时的,LLM 边输出 TTS 边播,减少用户体感延迟。

LLM 部分,对于多候选反问用户这种固定场景,没有让模型去驱动产生文本,而是用规则直接生成候选展示文本,然后把上下文手动拼回对话,省掉一次 LLM 调用。

除了端到端链路的优化,我还专门定位过 LangGraph 自身的热路径问题。最初拿到慢请求,我没有直接猜是 LLM 慢了,而是给链路加了 stage timing,把 prompt build、LLM prepare、LLM stream、tool node、after_tools route、checkpointer get、workflow compile、graph invoke 全部拆开计时。结果发现不是 LLM 慢,而是 Postgres checkpointer 的连接创建和 setup 进入了请求热路径——每次请求都要建连接、初始化 saver。我把 AsyncConnectionPool 和 AsyncPostgresSaver 的初始化移到了应用启动阶段,请求时通过 get_langgraph_checkpointer() 直接复用连接池,关闭时统一 cleanup。这样基础设施开销被移出了用户请求路径。

之所以用 Postgres 而不用内存 checkpointer,是因为语音控制需要状态持久化——用户可能中断对话后回来继续,内存会丢状态。


D6. RAG 你实际做到了哪一步

面试官意图:确认 RAG 落地深度,区分实做和方案。

推荐回答

我实际落地更熟的是 FuserLab 的 pgvector + unstructured 知识库中台。链路上包括文档解析、切分、向量化入库、按 dataset_id/query/top_k/score_threshold 检索,返回内容、文档名和页码命中,用于智能客服、热词匹配等业务。

对于 JD 里 Milvus + ES 的混合检索,我不把它说成已深度落地。我理解的方案是向量召回和全文召回并行,候选合并后用权重或 rerank 排序;引用溯源要保留 document、page、chunk;权限过滤尽量前置到 dataset/document 范围,最终返回前再强校验。

为什么选 pgvector 不用 Milvus+ES?

  1. 业务评估两年内知识库规模不大,pgvector 够用
  2. 团队人少,天翼云有 pgvector 托管服务可直接采,Milvus 需要自建维护
  3. ES 尝试过 pgvector 插件分词但版本不兼容,BM25 预留接口但优先级后移
  4. 检索客户端已抽象,将来切 Milvus 不改业务代码

可能追问

  • pgvector 和 Milvus 怎么选?→ 规模 < 千万级用 pgvector(免运维),规模更大用 Milvus(性能更优)
  • chunk 怎么切?→ 默认递归分割,高精度用语义分割,表格/代码单独处理
  • 权限过滤放检索前还是检索后?→ 尽量前置到 candidate set 生成阶段,最终返回前再做强校验

六、LangGraph 与编排框架

本章基于网上 LangGraph 面试真题 + 你的 FuserLab LangGraph 实战经验编写。 LangGraph 是字节 Coze JD 明确提到的框架,也是面试中最高频的技术深挖方向。

L1. LangChain 和 LangGraph 的核心区别是什么

面试官意图:这是字节面试最高频的问题。面试官想区分"用过 Demo"和"真正落地过生产项目"的候选人。

推荐回答

一句话总结:LangChain 是零件库(组件),LangGraph 是装配线(编排)。LangChain 做简单线性流程,LangGraph 做复杂有状态的图编排。

维度LangChainLangGraph
核心抽象Chain(链式调用)StateGraph(有向状态图)
流程类型线性 / 顺序执行支持循环、分支、条件跳转
状态管理隐式(黑盒)显式 State 管理,每个节点都能读写
可控性低——AgentExecutor 是黑盒高——每个 Node/Edge 由你定义
失败处理不可定制完全可定制重试/回滚/中断
可观测性难以追溯内部决策节点级日志 + 状态快照
适合场景简单文档问答/直线流程复杂 Agent/多步骤推理/生产级系统

我两个项目正好体现了这两种模式的适用场景:

FuserLab 的语音控制用 LangGraph,因为业务流程是 agent → tools → agent → ask_user,有明确的状态流转和循环,用 StateGraph + ToolNode + checkpointer 刚好匹配。

但我没有在 FuserMate 用 LangGraph——因为 FuserMate 的核心复杂度不在图拓扑,而在平台运行时:精细控制流式事件拆分(thinking/message/tool_call 的语义拆分)、工具生命周期管理、WebSocket 推流、Hook 系统。LangGraph 的 ToolNode 是一个黑盒,不会给你中间的事件粒度。所以自研 AgentLoop 更合适。

加分表达

"LangGraph 的核心价值不是让流程更复杂,而是让失败路径变得可设计。状态不可见的 Agent,本质上是不可治理的。80% 的价值体现在失败路径上,而不是成功路径。"

可能追问

  • 两者能结合使用吗?→ 完全可以。LangChain 提供组件(ChatModel、Tool、PromptTemplate),LangGraph 负责编排这些组件。FuserLab 就是这样用的——LLM 调用走 LangChain 的 ChatOpenAI,流程编排走 LangGraph 的 StateGraph
  • 什么时候只用 LangChain 就够了?→ 简单 QA、单次文档总结、不需要循环和分支的直线流程

L2. StateGraph 三要素:State / Node / Edge 怎么设计

面试官意图:考察对 LangGraph 核心抽象的深入理解。

推荐回答

State:共享状态容器,是 Graph 的"记忆"。每个 Node 读取当前 State,返回部分更新,LangGraph 自动合并。State 的定义可以用 TypedDict 或 Pydantic BaseModel。关键设计原则:State 是所有节点的唯一通信渠道——节点之间不能直接调用,只能通过 State 传递信息。

Node:执行单元。接收当前 State,返回 {"key": "new_value"} 格式的部分更新。职责必须单一——一个 Node 失败时,失败原因必须是单一可解释的。我在 FuserLab 语音控制里拆了 agent node(LLM 推理)、tools node(工具执行)、ask_user node(多候选时人工确认),每个 Node 只做一件事。

Edge:控制流载体。普通边(固定 A→B)和条件边(根据 State 的某个字段动态路由)。条件边的价值在于把模型的决策范围限制在结构化选项里——模型产出的是结构化结果(比如 next: "tools" or next: "ask_user"),规则路由做判断,不是让模型自由决定下一步做什么。这样可以防止 Agent 跑偏。

这三者的关系类比:State 是共享内存,Node 是 CPU 指令,Edge 是程序计数器——决定了执行什么、以什么顺序、在什么条件下跳转。

面试易错点

  • 不要把 Node 说成"就是函数封装"——Node 的核心是 State 读写契约
  • Edge 不能只说"连接节点"——必须提条件路由和循环控制
  • 不要忽略 State 的不可变性约束——Node 返回的是更新而非直接修改

L3. Checkpoint 机制:状态持久化与断点恢复怎么实现

面试官意图:Checkpoint 是 LangGraph 最核心的机制,面试必追问。

推荐回答

Checkpoint 的本质是 State 在时间维度上的版本快照机制。每个 superstep(一个或多个 Node 执行完成)后,Graph Engine 自动触发一次 Checkpoint 写入,将当前 State 序列化后存入持久化层。

触发时机:每个 step 边界触发(不是每个 Node——一个 step 可以包含多个 Node 的并行执行)。这意味着即使进程崩溃,也能恢复到最近的安全点。

持久化后端选型

后端写入延迟进程重启保留并发支持适用场景
MemorySaver<1ms单进程本地调试/PoC
Redis1-3ms高并发生产高并发
PostgreSQL5-15ms高并发企业多租户/合规审计
SQLite2-5ms单进程边缘部署

我在 FuserLab 语音控制里选择了 PostgreSQL checkpointerAsyncPostgresSaver)。原因有两个:第一,语音控制需要状态持久化——用户可能中断对话后回来继续,内存会丢状态;第二,Postgres 是我们已有的基础设施,不需要引入新的存储组件。

恢复机制:通过 thread_id 定位会话,checkpoint_id 指定恢复到哪个版本。checkpoint 之间通过 parent_checkpoint_id 串成历史链,支持回溯到任意历史版本——类似 Git commit。

踩过的坑:最初 Postgres checkpointer 的连接创建和 setup 进入了请求热路径——每次请求都要建连接、初始化 saver,拖慢了整体延迟。优化方式是把 AsyncConnectionPoolAsyncPostgresSaver 的初始化移到应用启动阶段,请求时通过 get_langgraph_checkpointer() 直接复用连接池。

可能追问

  • 最大快照数有限制吗?→ 理论上无限,取决于存储成本。生产环境建议按 TTL 清理旧 checkpoint
  • 并发写入冲突怎么解决?→ LangGraph 内部通过 thread_id + checkpoint_id 的乐观锁机制,写入前检查版本号
  • Redis 不可用怎么降级?→ 降级到 MemorySaver 并打告警,但会牺牲持久性——只适用于短期容错

L4. Human-in-the-Loop 怎么设计?三种插入范式

面试官意图:考察 HITL 的工程落地能力。

推荐回答

HITL 的核心是在 Agent 自主执行的关键节点插入人工决策,而不是每一步都让人点确认。LangGraph 支持三种 HITL 插入范式:

范式一:条件边中断(Conditional Edges) 在 Edge 层面做路由。比如 Node 输出 confidence_score,条件边判断:score > 0.9 → 继续执行;score < 0.9 → 路由到人工审核节点。适合低风险、需要"软把关"的场景。

范式二:工具节点拦截(interrupt_before / interrupt_after 在 Node 执行前后暂停。interrupt_before=["tools"] 会在执行工具节点前暂停,等人工审批后再执行。适合中等风险的工具调用(发消息、修改数据库)。我在 FuserMate 里通过 pre_tool_use hook 实现了类似效果——hook 可以 deny 单个工具调用。

范式三:状态机断点(Checkpoint + interrupt) 在 Graph 的任意节点通过 interrupt() 函数主动暂停,整个 State 被持久化到 Checkpoint。等人工操作完成后,通过 Command(resume=...) 恢复执行。适合长周期异步审核——审核可能跨小时甚至跨天,进程可以重启,状态不丢。

设计原则

  • 异步优先:审核不阻塞主流程,不要让 P99 被审核拖爆
  • 按操作风险分级:低风险自动、中风险审批、高风险升级
  • 审核超时兜底:超过 N 小时未审批,自动拒绝或降级处理

面试大坑:不要把 HITL 等同"加个确认按钮"。HITL 是架构级设计——什么操作需要人介入、以什么粒度介入、超时怎么处理、审核队列积压怎么办——这些都要系统性考虑。


L5. State Schema 设计:TypedDict vs Pydantic 怎么选

面试官意图:考察生产级 State 设计经验。

推荐回答

维度TypedDictPydantic BaseModel
校验能力无运行时校验完整运行时类型校验
默认值不支持Field(default=...)
可观测性无 descriptionField(description="...") 出现在 LangSmith Trace
依赖Python 标准库需安装 pydantic
性能极轻量有微小序列化开销
适用场景原型 / ≤5 字段生产 / 多 Agent / 复杂嵌套

选型建议:原型阶段用 TypedDict 快速验证 → 进入生产或多人协作迁移到 Pydantic。迁移成本不高——主要是加 Field(description=...) 做可观测性增强。

State 设计的三层拆分(我在 FuserLab 的实际做法):

  1. 业务输入user_querycontext_idsession_id——用户请求直接映射
  2. 过程状态messages(对话历史)、tool_results(工具结果)、intermediate_steps——Agent 执行中的中间产物
  3. 决策结果next_action(下一步路由)、confidence_score(置信度)、final_response——用于 Edge 路由和最终输出

这个拆分的价值在于:不同层有不同的更新策略。业务输入基本不变(默认覆盖即可),过程状态需要追加(messages 用 append Reducer),决策结果是单值覆盖。


L6. Reducer 机制与状态合并策略

面试官意图:考察对 LangGraph 状态合并机制的深入理解。

推荐回答

Reducer 决定了多个 Node 或并行分支对同一个 State key 写入时,最终值怎么合并。

默认 Reducerlast-write-wins(后写入覆盖先写入)。适合单值字段——next_actionfinal_responseconfidence_score

自定义 Reducer 的三种模式

  1. append(列表追加)messages 字段最常用。每个 Node 返回新的消息片段,Reducer 自动追加到消息列表末尾,而不是覆盖整个列表。这样 Agent 的多轮对话逐步累积。

  2. deep merge(深度合并):适合嵌套 dict 结构。比如 agent_state = {"plan": {"step1": "done"}, "context": {...}},不同 Node 更新 plan 的不同 key,Reducer 自动合并而非覆盖。

  3. 自定义聚合:比如多个并行分支对同一个 score 字段投票,Reducer 取平均值或最大值。

并发写入问题:多个并行分支同时写同一个 key 时,默认 Reducer 的执行顺序不确定。解决方案:① 用条件边分流,保证同一时刻只有一个分支写;② 自定义 Reducer 做确定性合并(如 timestamp merge)。

实际踩坑:LangGraph 的 State 默认是覆盖式更新。如果你在 Node A 里设了 state["context"] = {...},Node B 如果不显式返回 context,State 里的 context 会保持 Node A 的值——但如果你在 Node B 里返回了 {"context": None},它会覆盖掉。这就是为什么每个 Node 的返回值要精确——只返回你真正想改的字段。


L7. Node 粒度如何控制?Conditional Edge 如何限制 Agent 决策

面试官意图:考察 LangGraph 流程设计的工程判断力。

推荐回答

Node 粒度原则:一个 Node 失败时,失败原因必须是单一可解释的。如果你问"这个 Node 为什么失败了",答案不能是"可能是 LLM 调用超时,也可能是工具 Schema 解析错误,也可能是业务逻辑 bug"。这意味着你要把 LLM 调用、工具执行、校验逻辑拆成不同的 Node。

反模式:把整个 Agent 逻辑塞进一个巨大的 Node(类似 LangChain 的 AgentExecutor)。这样做确实能跑,但出了问题时你完全不知道是哪一步出了问题。

Conditional Edge 的核心价值限制模型的决策范围。不是让模型自由说"接下来做什么",而是让模型产出结构化结果,然后规则路由做判断:

# 模型产出结构化决策
def agent_node(state):
    response = model.invoke(state["messages"])
    return {"next": response.next_action}  # 只能是 "tools" / "ask_user" / "finish"

# 规则路由做判断——不是模型说了算
graph.add_conditional_edges("agent", lambda s: s["next"], {
    "tools": "tools_node",
    "ask_user": "ask_user_node",
    "finish": END
})

避免无限循环:在 State 里显式记录执行次数(loop_count),条件边强制校验——超过阈值直接路由到 END 或 error handler。不要依赖模型自己判断"是不是该停了"——模型可能会一直说"还需要再检查一下"。


L8. LangGraph 生产环境的可观测性与失败处理

面试官意图:考察生产级 LangGraph 运维经验。

推荐回答

可观测性三层设计

  1. 节点级日志:每个 Node 记录输入 State、输出 State、耗时、异常。这不是靠 print 做的——LangGraph 的 callback 机制(BaseCallbackHandler)可以拦截每个 Node 的开始/结束事件。我在 FuserLab 的 TraceCallbackHandler 就是基于这个机制,把每个 Node 的执行转成 span,记录 trace_id/span_id/parent_id/duration/token/ttft/error。

  2. Checkpoint 快照:每次出问题时,不仅看日志,还要能回放——拿到当时的 State 快照,用相同的输入重跑,看能不能复现。LangGraph 的 checkpoint 天然支持这个。

  3. LangSmith / 自建 Trace 平台:LangSmith 是 LangGraph 的原生可观测平台,但我实际用的是自建 Trace 系统——因为部门有自己的数据合规要求,不能把 trace 数据发到外部服务。

失败处理的分级策略

  • 工具级失败(网络超时、API 限流)→ 重试 2 次,指数退避
  • 模型级异常(输出格式错误、JSON 解析失败)→ 校验后重跑一次,带上 format instruction
  • 业务级风险(置信度低、权限不足)→ 中断 + 升级人工处理
  • 基础设施故障(DB 连接断开、Redis 不可用)→ 降级 + 告警

生产环境常驻问题

  • 长流程 Agent 的 State 会越来越大 → 定期 checkpoint 清理 + 摘要压缩
  • 并发场景下同一个 thread_id 被多次触发 → 用 config["configurable"]["thread_id"] 的幂等性保证
  • P99 延迟被 checkpoint 写入拖慢 → checkpoint 异步写入(队列 + 批量 flush)

L9. 从 LangChain Agent 迁移到 LangGraph 最大的思维转变

面试官意图:这是字节 Agent 平台组的高频题——考察你是否理解两种框架的根本差异。

推荐回答

最大的思维转变:从「黑盒循环」到「显式流程」

LangChain 的 AgentExecutor 是一个黑盒——Think → Act → Observe 循环被封装死。你不知道工具被调用了几次、在哪一步失败的、Agent 为什么选了工具 A 而不是工具 B。出了问题时只能猜。

LangGraph 把这一切打开了:

  • 每个 Node 做什么一目了然
  • 每条 Edge 的跳转条件由你定义
  • 可以在任意节点插入日志、审核、人工审批
  • State 是所有决策的完整记录——事后可以从 State 快照完整复现一次执行
  • 故障定位从 30 分钟降到 5 分钟

我再补充一个核心观点:LangChain Agent 的上下文和决策是隐式堆在 prompt 里的。流程一复杂(多步分析 + 工具调用 + 校验节点),失败路径根本兜不住。一旦某一步返回异常,Agent 要么继续胡编,要么直接走偏,而且你很难知道是在哪个决策点开始出问题的。

LangGraph 把"模型怎么想"和"系统怎么管"分开了——模型负责局部推理(在 Node 里),系统负责全局约束(在 Edge 和 State 里)。这就是我在 FuserLab 选 LangGraph 做语音控制的核心原因——状态机的确定性 + 模型的灵活性,两者各司其职。


L10. 什么情况下「不应该」使用 LangGraph

面试官意图:考察框架选型的判断力,而不是盲目吹捧。

推荐回答

LangGraph 不是银弹。以下场景不应该用它:

  1. 简单 RAG / 一次性问答:用户问一个问题,检索文档,生成答案,结束。没有循环、没有分支、没有状态管理需求。这种场景 LangChain 的 Chain 或者直接调 LLM SDK 就行。引入 LangGraph 反而增加了代码量和维护成本。

  2. 无失败兜底需求的场景:如果你的业务能接受"偶尔出错就重试整个流程",不需要精细的失败恢复——那 AgentExecutor 够用。

  3. 团队 LangGraph 经验不足且时间紧:LangGraph 的学习曲线比 LangChain 陡——你需要理解 State、Node、Edge、Reducer、Checkpoint、Conditional Edge 等一系列概念。如果团队不熟悉且项目时间紧,用 LangGraph 的风险是被抽象层困住。

  4. 需要极致低延迟的实时场景:每次 Node 执行后 LangGraph 都会触发 checkpoint 写入。对于毫秒级延迟敏感的实时场景,这个开销可能不可接受。虽然可以调优(异步写入、Redis 后端),但本质上 checkpoint 是有代价的。

  5. 模型自主决策比流程可控更重要的场景:有些场景下 Agent 的创造力比可控性更重要——比如开放式创作、头脑风暴。LangGraph 的显式状态机反而会限制模型的灵活性。

总结一句话LangGraph 解决的是"可控性"问题,如果你的场景不需要精细的可控性,引入 LangGraph 是过度设计


七、工具执行引擎(串行/并行/中断/恢复/去重)

本章参考 Claude Code 的 runTools + partitionToolCalls、Codex 的 ToolOrchestrator + sandbox escalation、以及 2025 年学术界 SHIELDA/ALAS 等错误处理框架。 工具执行引擎是 Agent 运行时最核心的工程问题——串并行调度、中断恢复、幂等去重——面试官深挖这个方向时,考察的是你对 Agent "操作系统级"能力的理解。

T1. 工具执行串行 vs 并行怎么设计?Claude Code 和 Codex 是怎么做的

面试官意图:考察 Agent 运行时调度能力——"不是调个 function 就完了"。

推荐回答

Claude Code 和 Codex 在这个问题上选择了不同的安全策略,但核心分叉逻辑一致。

Claude Code:贪心分区 + 并发上限 10

核心函数 partitionToolCalls() 做贪心分区:

模型输出 N 个 tool_use blocks
        ↓
partitionToolCalls() 扫描:
  连续并发安全工具 → 合并到同一批次
  遇到不安全工具 → 切断,独占一个批次
        ↓
  并发批次 → runToolsConcurrently(max=10)
  串行批次 → runToolsSerially()

关键设计决策:

  • 并发安全判定isConcurrencySafe——Read/Glob/Grep 只读类是 true;Edit/Write/Bash 修改状态类是 false
  • 并发上限默认 10:通过环境变量 CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY 可覆盖
  • 延迟上下文修改(关键!):并发执行期间,每个工具的上下文修改先收集到队列,等整批结束后按原始顺序统一应用——"先记流水,后统一入账",防止竞态
  • 串行立即提交:每个工具执行完成后立即提交上下文修改,保证下一个工具看到更新后的状态

Codex:ToolOrchestrator 统一编排 + OS 沙箱

ToolOrchestrator
  ├── Compute ExecApprovalRequirement(策略 + 权限 + 沙箱上下文)
  ├── Check cached approvals
  ├── Guardian 子 Agent 自动审批(可选,低风险)
  ├── Fallback 到交互式用户审批
  ├── Select sandbox(Seatbelt/Bubblewrap/Restricted Token)
  ├── Execute via concrete runtime(ShellRuntime / UnifiedExecRuntime)
  └── Retry with relaxed sandboxing on denial

Codex 的并行策略更保守——所有 mutating 工具串行,只有读操作可以并行。sandbox 类型决定权限边界(ReadOnly → WorkspaceWrite → DangerFullAccess),retry 时可以 escalation 提权。

我的实践(FuserMate):工具是逐个执行的(for 循环),没有做并行批处理。这是有意为之——FuserMate 的部署目标是私有化 Docker 单镜像,不需要处理 10 个工具并发的复杂场景。但如果要做并行优化,我会参考 Claude Code 的 isConcurrencySafe 标记 + 延迟上下文提交方案——这是工程上最清晰的做法。

核心原则

  • 只读工具 → 并行(join_all
  • 修改状态工具 → 串行
  • 并行时延迟提交上下文修改,串行时立即提交
  • 并发上限防止资源耗尽(默认 10)

T2. 工具中断怎么恢复?如何防止中断导致重复执行

面试官意图:考察中断恢复——Agent 系统最容易被忽视的工程难题。

推荐回答

工具中断的典型故障场景(来自 LiveKit Agents 的真实 bug):

1. 用户通过语音中断(VAD 触发)
2. 工具实际上已成功执行(比如创建了一条数据库记录)
3. 但中断导致 tool result 未被保存到 chat history
4. 下一轮 LLM 推理时,模型不知道工具已执行
5. Agent 重新执行同一个工具 → 重复创建资源

这个 bug 暴露了三个核心问题:① 中断时机;② 中间状态持久化;③ 幂等性。

中断恢复策略分三层

第一层:中断检查点的粒度

在哪里检查中断,决定了能恢复到什么程度。Claude Code 在以下节点检查 cancel/abort:

每轮迭代前 → 每个 message/thinking/tool_call chunk 后 → 工具执行前 → 工具执行后

FuserMate 的 cancel_eventasyncio.Event)也是同样的粒度。检查点越密集,中断响应越快,但恢复越复杂。

第二层:已完成步骤的持久化(Journaling)

关键原则:工具结果必须在中断发生前持久化,而不是在"一切正常"的流程末尾

Codex 的做法:每个 tool call 完成后,ToolOrchestrator 立即将其结果写入 journal,不等到整批结束。Claude Code 的做法:_execute_one_tool 完成后立即 append 到 current_messages

FuserMate 的做法:

  • 工具结果通过 RunEventBroker.emit_event() 实时写入 JSONL(每个 session 一个文件)
  • 每个事件自动分配递增 cursor
  • 即使 run 被 cancel,已经执行的工具结果已持久化,恢复时可通过 cursor 重放

第三层:恢复时的去重

恢复时(比如 WebSocket 断线重连,或进程崩溃后重启),需要判断哪些工具已经执行过了:

  • Cursor 重放:FuserMate 的 WebSocket resume 帧传入 cursor,服务端从该 cursor 重放事件。客户端已经收到的事件不会重复处理
  • 幂等 Key:对于有副作用的工具(write、bash、数据库操作),每次调用带 idempotency_key(= tool_call_id),服务端 TTL 缓存(15 分钟),重复 key 返回缓存结果
  • Reconcile 扫描:启动时 reconcile_startup() 扫描所有 status="running" 的 run,全部标记为 "failed",不会继续执行未完成的工具

设计原则先持久化,再返回结果。永远不要让"持久化"和"返回结果"之间存在中断风险窗口。


T3. 工具错误如何分类?不同错误类型怎么区别处理

面试官意图:考察你是否能对不同错误做精细化的恢复策略,而非"失败就重试"。

推荐回答

学术界 SHIELDA 框架定义了 36 种异常类型,落地到工程实践我归纳为四类:

错误类型典型场景能否重试处理策略
瞬态错误(Transient)网络超时、API 限流、临时不可用✅ 可以指数退避重试 2 次(1s → 2s)
参数错误(Parameter)模型传了错误类型、缺少必填字段❌ 不应重试返回 error + Schema,让模型修正参数
持久错误(Permanent)权限拒绝、资源不存在、系统硬拒绝❌ 不应重试返回清晰 error + suggestion,建议替代方案
状态冲突(State Conflict)并发写入冲突、版本不匹配⚠️ 条件重试重新读取当前状态 → 重新计算 → 重试 1 次

Codex 的错误升级策略(参考):

Tool execution fails
  ↓
Sandbox denial? → retry with relaxed sandboxing
  ↓
Still denied? → Guardian 子 Agent 自动审批(低风险操作)
  ↓
Still denied? → fallback to 用户交互审批
  ↓
连续 3 次拒绝 → Circuit Breaker 熔断,强制 Agent 换方案

错误信息的结构化返回

FuserMate 的 ToolExecutor 不对模型返回原始 traceback,而是返回结构化错误:

{
  "error": "File not found: /path/to/file",
  "suggestion": "检查路径是否正确,或使用 Glob 搜索类似文件名",
  "retryable": false,
  "error_type": "permanent"
}

为什么结构化?因为 LLM 理解结构化 JSON 比理解自由文本 traceback 更准确——后者容易让模型"误解"错误原因并做出错误的重试决策。

防止同一工具死循环:连续调用同一工具且连续失败 ≥3 次 → 系统层自动注入 "You have called tool X multiple times without success. Consider a different approach." 到上下文。


T4. 工具幂等性怎么设计?如何防止工具重复执行

面试官意图:考察对分布式系统中"恰好执行一次"语义的理解。

推荐回答

Agent 系统中工具重复执行的根因有三种:

  1. 中断恢复导致:工具已执行但结果未保存 → 恢复后重新执行
  2. LLM 重复调用:模型不记得已经调用过,又调了一次
  3. 并发冲突:多个 Agent 或 run 同时执行同一个有副作用的操作

方案一:幂等 Key(Idempotency Key)

每个有副作用的工具调用带 idempotency_key(= tool_call_id)。服务端维护 TTL 缓存(15 分钟):

async def execute(self, tool_name, params, idempotency_key):
    cached = await idempotency_cache.get(idempotency_key)
    if cached:
        return {**cached, "cached": True}  # 不重复执行

    result = await actual_execute(tool_name, params)
    await idempotency_cache.set(idempotency_key, result, ttl=900)
    return result

适用工具:write、edit、bash、数据库操作等有副作用的工具。只读工具(read/glob/grep)不需要幂等 key。

方案二:Lease + Version Token

对于需要并发安全的有状态操作(如修改同一文件),用乐观锁:

async def edit_file(path, content, expected_version):
    current = await get_version(path)
    if current != expected_version:
        raise StateConflictError(f"Version mismatch: expected {expected_version}, got {current}")
    await write_file(path, content)
    await bump_version(path)

Codex 的 sandbox 隔离天然提供了文件系统级别的并发保护——每个 Agent 在自己的 workspace 内操作,不会冲突。

方案三:意图-执行分离

一个更激进的架构:Agent 不直接执行工具,而是"提议"执行,由 Engine 层统一做幂等/去重/重试:

Agent → 发出 Intent(含 correlation_id + preconditions + compensation plan)
  ↓
Execution Engine → 检查幂等 key → 执行 → 记录 journal
  ↓
失败 → Engine 自动重试或触发 compensation

这个方案的好处是 Agent 侧完全不需要关心中断恢复——Engine 层提供 durable execution 保证。缺点是架构复杂度上升。

LLM 层面的防护:除了系统层机制,还可以在上下文里做轻量防护——记录最近 N 次的 tool_call,MicroCompactor 对 COMPACTABLE_TOOLS 只保留最近 5 个结果的哨兵字符串。这样至少让模型知道"我用过这些工具"。


T5. 长时间运行的工具怎么管理?后台任务 + 快照持久化

面试官意图:考察长任务(几分钟到几小时)的工程管理能力。

推荐回答

FuserMate 的后台任务系统专为长时间运行的工具设计:

架构

BackgroundTaskService
  ├── Bash → asyncio.create_subprocess_shell(流式捕获输出)
  │    └── 每 1000ms 持久化输出快照到 FileRuntimeStore
  ├── Read/Grep/Glob → ThreadPoolExecutor
  └── 完成时 → 写入 Mailbox + 可选唤醒 Agent

关键设计点

  1. 增量快照持久化:Bash 后台任务每 1 秒持久化一次 stdout/stderr。即使进程崩溃,已经产生的输出不丢失,恢复时可以从最后快照继续。

  2. Mailbox 异步通知:后台任务完成后写入 Mailbox,wake_policy 决定是否唤醒 Agent。Agent 在下一轮模型调用前消费 Mailbox 消息。如果 run 被取消,Mailbox 消息的 consumed_at 回滚为 None,下次 run 重新消费。

  3. Agent 不阻塞等待:Agent 不轮询后台任务状态。后台任务完成后通过 Mailbox 主动通知——事件驱动而非轮询驱动。

Codex 的对比:Codex 用 UnifiedExecProcessManager 管理交互式进程。长任务在 sandbox 内运行,ToolOrchestrator 支持 retry with escalation。Codex 更侧重 sandbox 隔离——每个长任务在 OS 级 sandbox 里执行,主进程不直接接触。

Claude Code 的对比:Claude Code 的 bash 子进程有 subprocessTracking 机制——即使主 Agent 被 cancel,已启动的 bash 子进程可以被追踪和恢复。

设计原则

  • 长任务一定异步执行(不阻塞 Agent Loop)
  • 增量持久化(不等到结束才保存)
  • 完成后主动通知(不轮询)
  • 崩溃后可以恢复或标记失败(不留僵尸进程)

T6. 工具执行的可观测性怎么做

面试官意图:考察工具执行的监控和调试能力。

推荐回答

工具执行的可观测性需要回答四个问题:什么被调了 / 结果是什么 / 花了多久 / 为什么失败。

工具执行的 Trace Span 设计

{
  "span_type": "tool_execution",
  "tool_name": "bash",
  "tool_call_id": "toolu_xxx",
  "input": {"command": "ls -la"},
  "output": {"stdout": "...", "stderr": "", "exit_code": 0},
  "output_truncated": false,
  "output_size": 1234,
  "duration_ms": 450,
  "permission_decision": "user_ask",
  "permission_duration_ms": 1200,
  "error": null,
  "retry_count": 0
}

关键字段:tool_call_id(与 LLM 输出的 tool_use id 对应)、permission_decision(是 allow/ask/deny/hard_deny)、permission_duration_ms(人工审批花了多久——这是 HITL 的性能瓶颈指标)、output_truncated(结果是否被截断)、retry_count(第几次重试)。

FuserMate 的 27 种运行时事件覆盖了工具的完整生命周期tool_call_startedtool_call_completed / tool_call_failed。每个事件写入 JSONL 文件(按 session 组织),带递增 cursor,支持按时间回放。

告警指标

  • tool_failure_rate 按工具分组(识别"问题工具")
  • tool_permission_deny_rate(用户拒绝频率异常可能表示安全策略过严或模型滥用)
  • tool_retry_rate(重试率过高表示工具稳定性差)
  • tool_output_truncation_rate(大结果比例高可能需要调整截断阈值)
  • tool_duration_p99(P99 延迟突然恶化)

八、推理引擎与基础设施

E1. 私有化交付你做过哪些

面试官意图:考察交付能力。注意:不要说"没做私有化",你的 Docker 部署就是私有化雏形。

推荐回答

我实际做过的是 Docker 级别的交付,Helm/K8s/离线包不说成已深度落地。

FuserMate 有两种形态:Electron 桌面端前后端一起打包;Docker 形态一个镜像含 React 静态站点 + FastAPI,Nginx 反代 /api/* 和 WebSocket,后端不直接暴露端口。运行时配置和数据挂载到宿主机 /data/fuser,API Key 不入镜像。部署后通过前端访问、/api/v1/mates/api/v1/models 做 smoke test。

FuserLab 是前端 Nginx Alpine + 后端 Python Slim 两个镜像分开部署。后端实际可拆 8 个微服务,但线上 3 个 Pod 没遇到性能瓶颈,优先把研发时间放在业务功能上。

K8s 这边我们日常用的是天翼云 CCSE 容器服务,底层是 K8s 但日常操作不需要裸写 kubectl,主要用云平台管理,命令行只会用到 log 查看。Helm chart 如果要做,思路和 Docker Compose 一脉相承:values、Secret、ConfigMap、PVC、init migration job、readiness/liveness 和 rollback。


E2. 多租户和配额怎么设计

面试官意图:考察 B 端平台能力。

推荐回答

我不把它包装成完整大规模 SaaS 多租户落地经验。

实际做过的:FuserMate 因为是数字员工,多租户就是多容器多部署,每个实例独立隔离。FuserLab 当时考虑过两个方案:一是全部加 tenant_id 索引(类似 Coze 的做法),二是直接用数据库隔离。我们选了后者,因为 toB/toG 客户天然偏好物理隔离,独立数据库更符合他们的安全和合规预期。公有云 SaaS 场景用 tenant_id 索引更经济,但 Lab 的客户场景决定了数据库隔离是更合适的方案。

设计方案:完整多租户体系的话,tenant_id 要贯穿用户、workspace、agent、tool_policy、model_config、trace、artifact 和 quota,在 repository 和 API 层强制注入。配额按模型 token、工具调用次数、并发任务做维度,Redis 令牌桶限流,超限记录审计日志。


E3. 自建推理你了解多少

面试官意图:这是强项,充分展示。

推荐回答

我实际做过。用的是华为 910B 8 卡(DaVinci NPU),部署了 4 个千问系列模型:

模型NPU关键参数
Qwen3-32B0-3tensor-parallel-size 4
Qwen3-30B-A3B4-5TP=2, enable_expert_parallel(MoE)
Qwen3-14B6单卡
Qwen3-4B7单卡

推理引擎是 vLLM-Ascend,Ascend 量化,所有模型通过 OpenAI 兼容 API 暴露。

关键参数:--tensor-parallel-size 2--enable_expert_parallel(MoE)、--max-model-len 5500(后调至 12000)、--max-num-seqs 64(后调至 32)、--enable-chunked-prefill--enable-prefix-caching--gpu-memory-utilization 0.9(后调至 0.85)、--block-size 128--async-scheduling--tool-call-parser hermes

Ascend 特有配置:HCCL_OP_EXPANSION_MODE=AIV(集合通信优化)、VLLM_ASCEND_ENABLE_PREFETCH_MLP=1(MLP 预取)、VLLM_ASCEND_ENABLE_FLASHCOMM1=1(Flash 通信)、pa_shape_list: [48,64,72,80](形状预分配减少动态编译开销)。

模型前面是一层 OpenResty + Redis 网关,做 OpenAI 兼容路由和并发控制。Qwen3-30B-A3B 全局并发上限 64,生产环境 40,测试环境 24,max_wait 60 秒。

向量模型也部署了 BGE-M3 做 embedding、BGE-Reranker-v2-m3 做重排,用的是华为 Ascend 优化版的 MIS-TEI 镜像。


E4. 你们有完整 Helm chart 和离线交付吗

状态:高风险(如实承认边界)

推荐回答

我目前能确定的实做是 FuserMate 的 Docker Compose 私有化形态,包含镜像构建、Nginx 反代、配置挂载和 smoke test。Helm chart 和完整离线交付我不会说已经主导落地。如果要做,我会把 Docker Compose 的经验迁移到 K8s:应用镜像、values、Secret、ConfigMap、PVC、init/migration job、健康检查、离线镜像仓库、依赖包和回滚预案。


E5. 你做过企业 SSO/LDAP 吗

状态:高风险(如实承认)

推荐回答

没有做过。FuserLab 有基础的 auth/role/permission/key 权限模块,但完整的 SSO/LDAP/AD 集成不是我的实战经验。如果要做,思路是把外部认证作为接入层,统一映射到内部的 user/role/permission 体系,再结合审计日志和租户隔离控制资源访问。


九、行业视野与场景设计

F1. 你怎么看 LangGraph vs. Eino vs. 自研 AgentLoop

面试官意图:这是 Coze 面试的核心题——考察对 Agent 框架的深度理解和选型判断力。

推荐回答

这三个代表了 Agent 框架的三种路线,我两个项目正好覆盖了其中两种。

LangGraph 的核心是状态机 + 图编排。用 StateGraph 定义节点和边,每个节点是纯函数,状态通过 checkpoint 持久化。优势是:社区成熟、checkpoint 机制完善、适合业务状态机明确的场景。我在 FuserLab 的语音控制里用了 LangGraph——agent、tools、ask_user 三个节点的状态流转非常清晰。

Eino 是字节开源的 Go 语言 Agent 框架,设计思想和 LangGraph 类似——用 Graph 编排组件。差异化在于:Go 语言(更适合字节技术栈)、更轻量的抽象(不像 LangChain 那么重)、字节内部已经在生产环境大规模验证。如果团队技术栈是 Go,Eino 是比 LangGraph 更自然的选择。但我没用过,这是基于文档和社区反馈的判断。

自研 AgentLoop 是我在 FuserMate 上的选择。原因很明确:FuserMate 的核心复杂度不在固定的图拓扑,而在平台运行时——需要精细控制流式事件的每一步拆分,工具执行有完整的生命周期事件,需要配合 cancel 中断、结果截断、上下文压缩和快照冻结。LangGraph 的 ToolNode 是一个黑盒——工具调了就调了,不会给你中间的事件粒度。

我的判断

  • 业务状态机明确的场景(工作流、审批、语音对话)→ LangGraph / Eino
  • 需要掌控平台运行时的场景(通用 Agent 工作台)→ 自研 AgentLoop
  • 不存在"最好"的框架,只有"最适合当前复杂度"的选择

如果 FuserMate 后续要做复杂 DAG,我的演进思路是在上层引入图编排,底层工具执行和事件系统仍然复用自研的 AgentLoop,不是全量迁移到 LangGraph。


F2. Claude Code / Codex / FuserMate 三方技术对比

面试官意图:考察对行业主流 Agent 架构的理解深度和设计判断力。

一句话定位

Claude CodeCodexFuserMate
定位通用编程助手企业级 Agent 平台通用智能体工作台
语言TypeScript (Bun)RustPython (FastAPI)
核心特色深度 OS 集成 + IDE沙盒安全 + 多 Agent平台运行时 + 全栈

Agent Loop 对比

Claude CodeCodexFuserMate
实现while(true)双层循环:Session Task + Turnfor max_iterations=200
状态机Terminal(11种)/Continue(7种) 枚举指数退避重试四步迭代:prepare→stream→tools→loop
工具循环并发安全工具并行(max 10)approval→sandbox→retry逐个执行,结果 append → 下一轮
中断恢复stopHooks + abort controller + snapshot 回滚CancellationToken + Thread forkasyncio.Event + AgentRun 快照 + reconcile_startup
并发Coordinator ModeActor Modelasyncio.Lock 排队

工具系统对比

Claude CodeCodexFuserMate
安全模型Permission deny rulesSandbox 兜底(bwrap/landlock)运行时策略 system_hard_deny > user_deny > user_ask > user_allow
MCPassembleToolPool(built-in + MCP 去重)codex-mcp crate直接集成 MCP server

上下文压缩对比:三家都做了两层压缩。FuserMate 的 CompactionService 在 prompt 设计上更结构化——明确要求保留当前目标、重要决策、未决问题、文件路径和接口约定。

多 Agent 对比

  • Claude Code:Master-Worker (Coordinator Mode),Coordinator 仅保留 AgentTool + TaskStop
  • Codex:Actor Model,Mailbox + AgentRegistry + agent_max_depth 限制
  • FuserMate:单 Agent 为主,AgentRun 快照预留 team/subagent 扩展空间

一句话总结

Claude Code 是我做 FuserMate 的直接参考——它的 AgentLoop、Hook、MCP、Skills、HITL 模式都影响了 FuserMate 的设计。但 Claude Code 是编程场景专用,FuserMate 把它抽象成了通用 Agent 运行时,在核心模型拆分(Agent/Workspace/Session/AgentRun)和安全策略(运行时强制)上做了差异化的设计决策。

Codex 的沙盒方案是它最大的技术亮点,但部署复杂度也高。FuserMate 选择不依赖沙盒而是用策略兜底,是因为我们的部署目标是私有化 Docker 单镜像,不能引入 bwrap/landlock 这些系统依赖。这是场景驱动的取舍。


F3. 对 Manus、DeepSeek 这些新产品怎么看

面试官意图:考察前沿视野和技术判断力。

推荐回答

Manus 代表了通用 AI Agent 产品化的方向。它的核心价值主张是"给 AI 一个目标,它自己完成所有步骤"——这和 Coze 的 Agent 化编排是一个方向。但 Manus 面临的核心挑战是可靠性——开放域任务的完成率很难做到用户期望的水平。Agent 产品最大的敌人不是竞品,是用户的"失望一次就不再信任"。

DeepSeek 改变了行业两个认知。第一,证明了顶尖模型能力不需要天文数字的投入;第二,开源模型和闭源模型的差距在加速缩小。这对 Agent 平台是利好——推理成本在持续下降,意味着我们可以给 Agent 更多"思考预算"。但 DeepSeek 的 Function Calling 和工具使用能力相比 GPT/Claude 还有差距,这是 Agent 场景的关键能力。

我的整体判断:模型层在快速收敛,差异化会越来越小。Agent 平台的价值不在"用哪个模型",而在上下文工程、工具生态、评测体系、安全控制这些平台级能力——这些才是护城河。


F4. 设计一个全自动化的 Agent 进行创作(场景设计题)

面试官意图:考察架构设计和任务拆解能力。

推荐回答(以 AI 漫剧创作为例):

第一步:任务拆解 → 子 Agent 划分。不是按功能菜单拆,而是按独立决策边界拆:

  • 世界观设定 Agent
  • 剧情规划 Agent
  • 角色 Agent
  • 分镜 Agent
  • 评审 Agent(独立验证角色一致性、剧情连贯性、对白质量)

第二步:定义输入/输出 Schema。每个子 Agent 的输入输出必须是结构化的。

第三步:协作流程设计。串行流程:世界观 → 剧情大纲 → 逐章生成(角色+分镜并行) → 评审 → 修改 → 定稿。评审不通过时回退到对应 Agent 重新生成,最多 3 轮。

第四步:一致性保障——最难的部分。不能只靠 prompt 里说"保持角色一致"。需要:

  • 角色拆为不可变属性(年龄、身份、说话风格底色)、弱可变属性(好恶、互动方式)、动态状态(当前情绪、目标、已知信息)
  • 每轮生成前注入角色状态,每轮生成后评审 Agent 做一致性校验
  • 维护全局 Story State:角色关系图、时间线、关键伏笔、已用设定、未回收线索

第五步:评测标准:角色一致性、剧情连贯性、对白自然度、创作效率。


F5. 设计旅行攻略 Agent,怎么测评(场景设计题)

面试官意图:考察 Agent 评测体系的完整思维。

推荐回答

Agent 架构:意图解析 → 信息检索 Agent → 行程编排 Agent → 输出格式化 Agent

最大的三个问题

  1. 幻觉——推荐不存在的景点、餐厅。这是最致命的,因为用户会实际去执行
  2. 信息过时——API 数据可能是旧的
  3. 个性化不足——攻略"大路货"

评测体系

  • 离线评测集覆盖不同维度(目的地类型、天数、预算、偏好)
  • 评测维度:事实准确性(权重最高)、行程合理性、个性化程度、信息完整性
  • 评测方式:规则校验 + LLM-as-judge(多次打分取平均,与人工标注校准)+ 人工抽检 10%

持续改进闭环:线上用户反馈 → badcase 回灌到离线评测集 → 每次改 prompt 或切数据源跑回归评测。


十、反问 + 速记

反问面试官

Coze 版

  1. Coze 目前的 Agent 上下文工程,更多是开发者自己管理还是平台提供自动化能力?未来方向是什么?
  2. 多 Agent 协作目前在 Coze 上的主要使用场景是什么?最常遇到的工程挑战有哪些?
  3. Coze 的工具/Skill 生态,目前是更偏平台自建插件还是鼓励开发者通过 MCP 接入?
  4. 团队目前在 AI Agent 架构上最大的技术投入方向是什么?MultiAgent 协作、上下文工程、还是推理性能优化?
  5. 这个岗位日常工作中,和产品、前端、模型团队的协作模式是怎样的?

零一万物版

  1. 这个岗位当前更需要从 0 到 1 搭 Agent 平台,还是在已有平台上做工程化增强?
  2. 现在 Agent 工具体系是已经有 registry/schema/审计,还是还在 prompt function call 阶段?
  3. RAG 当前主要问题是召回质量、权限过滤、引用溯源,还是数据接入和增量更新?
  4. 私有化交付目前是 Docker Compose 为主,还是已经要求 Helm/K8s 和离线包?
  5. 评测体系现在有没有固定离线集和线上 badcase 回归机制?

考前速记卡

核心画像(必背)

我是能从产品价值出发,把 Agent 平台从 0 到 1 落地的 AI 全栈负责人。优势不是只会某个框架,而是能把产品目标拆成平台边界、运行时、工具链、观测评测、前端交互和交付闭环。

两个项目一句话

FuserMate:通用智能体平台,类 Claude Code。我主导产品/架构/核心代码——AgentLoop、工具体系、MCP、Skills、HITL、上下文压缩、工具安全。

FuserLab:部门级 AI 中台。Prompt 编排、Trace 观测、评测实验、LangGraph Agent、pgvector 知识库。

核心模型拆分

Agent(身份/模型/prompt/memory)→ Workspace(路径/skills/tools/规则)→ Session(对话历史)→ AgentRun(执行快照)

AgentLoop 流程

stream(messages) → for 0..max_iterations:
  ① _prepare_iteration_messages(注入上下文 + micro_compactor)
  ② _stream_model_response → 逐 chunk: message_delta / thinking_delta / tool_call
     ↔ 每个 chunk 后检查 cancel_event
  ③ 无 tool_call → message_done → run_completed
  ④ 有 tool_call → _execute_one_tool → 结果 append → 下一轮

五层保护:max_iterations(200) / cancel_event / 结果截断(50k) / 错误 JSON 化 / 顶层 try-except

安全优先级

system_hard_deny > user_deny > user_ask > user_allow 原则:模型输出不可信,运行时兜底

vLLM 关键参数

block-size 128 / max-model-len 12000 / gpu-memory-utilization 0.85 / max-num-seqs 32 / chunked-prefill / prefix-caching

高风险边界

  • ⚠️ Milvus+ES:实做 pgvector,混合检索讲方案
  • ⚠️ Helm/K8s 离线包:Docker 实做,Helm 讲方案
  • ❌ SSO/LDAP:没用过,如实承认
  • ✅ vLLM-Ascend:强项,全链路有代码
  • ✅ 多租户:数据库隔离理由真实

常见口误提醒

  • "项目经理" → "项目经历"
  • "类 Claw"和"类 Claude Code"选一个,不要混用
  • 不要用"deepagent"这种没有业界定义的自造词
  • 不要说"内部使用风险小所以敢自研" → 说"需要精细控制平台运行时所以自研"
  • 不要把"理解方案"包装成"落地经验"——Milvus/ES/Helm/SSO 永远先说实做边界
  • 不要先说"我都没做私有化交付" → 你的 Docker 部署就是私有化雏形

回答优先级速查

优先级题目核心策略
S级A1, A3, B1, C5, D1, D3, F1, F2自我介绍 + 框架选型 + vLLM 调优 + 安全 + AgentLoop 是杀手组合
A级A2, B2, B4, B6, C3, C7, D4, D5高频追问,项目证据充分
B级A4, A5, B3, B5, B7, B8, C1, C2, C4, C6, C8, F3展示广度和产品思维
场景题F4, F5结构化拆解框架
风险题E4, E5如实承认边界,讲设计思路