aria / blog

Agent 面试八股,我拿生产项目答一遍

面试官问 Agent 的记忆管理、检索、工具调用、输出可靠性时,背概念和讲实现是两种印象。用一个真实上线的备课 Agent 回答这些题:双层上下文压缩、工具检索替代向量 RAG 的取舍、权限不放在提示词里、模型无权直写数据库。

Agent 相关的面试题这几年高度趋同:上下文怎么管理、检索怎么做、工具调用怎么防失控、输出怎么保证可靠。背概念的人很多,讲得出生产实现的人少。这篇文章用一个真实上线的备课 Agent(面向教师,聊天产知识点和题目)把这些题答一遍,每个答案都来自代码里跑着的东西。

备课 Agent 的分层架构

第一题:多轮对话爆上下文怎么办

标准答案会说”做摘要”。项目里的完整版是两层管理器套着用:

外层是剪枝:教师上传的教材资料、历史草稿列表这类大块内容,一旦进过一轮对话就替换成占位符,原文放在服务端,需要时模型自己用工具取回。它们不需要每轮都占着窗口。

内层是主动摘要压缩。关键词是”主动”:不是等上下文满了报错再救,而是每次调用模型前先估算 projected tokens,占窗口比例超过阈值(配置 0.25)就提前压缩。压缩保留最近几轮原文(默认 4 条),更早的交给一个领域摘要提示词处理,这个提示词有一条硬性要求:不改写结构化事实和教师指令。教师上周说”这套题不要出现计算题”,这种话在被压缩三轮之后丢了,是比丢上下文更糟的事故。

配套的还有水位日志:每次调用记 estimated_tokens 和占用比例,线上可以直接看到上下文健康度。另外会话实例做了 LRU 淘汰,冷会话的 Agent 对象释放,再进来时从 PostgreSQL 的事件记录重建对话——内存里只有热会话。

第二题:检索用向量 RAG 吗

这个项目没有用向量检索,用的是工具检索:给模型四个资料工具(列文件、搜文件、读文件、看图),教材资料登记入库后,模型像人翻资料一样按需去查。Skill 里的参考文件也走同一套逻辑:按逻辑 ID 从白名单分页读取正文,不接受路径、不接受 URL、不接受上传资料——顺手把提示词注入的口子也堵了。

什么时候选哪条路,我的经验是看两点。资料量:教师一次上传几份教材,总量小到模型翻得动,工具检索的准确率和可解释性都更好,答错时能定位到”它读的是哪份文件哪一页”。如果是几十万条客服问答,那只能走向量召回,我在另一个医疗客服项目里做的是回合前预载加按需检索的双轨 RAG。两条路我都做过,选型依据是数据规模和错误能不能解释,不是哪个更新。

顺带一个被问到的点:教材解析的耗时。解析结果按文件内容寻址缓存(sha256 加解析器版本做缓存键),同一份教材第二次进来直接命中,只有解析器升级(缓存版本号从 v3.1 改到 v3.2)才会全量重解析。缓存失效是版本化的,不是拍时间。

第三题:工具调用怎么防失控

一句话版本:权限不在提示词里,在代码注册表里。

Skill 的 frontmatter 里有 allowed-tools 字段,但它只是给模型的说明,不是权限开关。模型看得见”我应该用哪些工具”,但真正能用什么,由代码里显式注册的工具表决定。提示词能被绕过,注册表不能。

工具的设计还遵循一条读写分离:request_* 类工具只负责请求展示一张卡片(比如”我要把这三道题保存为草稿”),真正的保存、放弃、重试由教师点卡片上的按钮触发。模型可以提议,不能执行。写数据库的路径只有一条,按钮在后端,提示词够不着。

Skill 本身的组织也值得说:五项工作法(资料检索、知识点创作、出题、原题导入、草稿编辑)各自一个目录,description 声明适用场景,正文只保留操作判断,大块的参考资料放在 references 里按需读取。激活时只附参考文件的短目录,用到了再读正文——省 token 的同时,也让”模型在什么场景该想起什么方法”变成了可维护的配置而不是玄学。

第四题:输出可靠性怎么保证,幻觉怎么办

这个项目的场景把错误成本放得很大:错的知识点进了题库,学生考试就会考到错的。所以输出侧的防御做了三层。

第一层是类型化产物。模型的输出不是自由文本,是按题槽结构化生成的(题型、题干、选项、答案、解析各有位置),再由一个 Token 编译器转成类型化内容。展示用的 Markdown 只由类型化产物单向渲染,不能反向恢复——杜绝”改展示文本改坏了机器产物”。

第二层是草稿快照加确认。所有 AI 产出以草稿形式存在,带一份独立的需求快照(记录”教师当时要求了什么”),初稿、改稿、确认都按这份快照验收。模型没有直接写正式库的权限,唯一通道是教师点确认卡片。

第三层是提示词运行合同。生成任务的提示词、输出协议、生产配置打包成带版本号的合同(现在到 v8),下发任务时冻结。改提示词就是出新版本合同,旧任务按旧合同验收,出了问题可以回滚。提示词从”线上直接改的字符串”变成”有版本的东西”,是我做过最值的一次改造。

第五题:任务和存储怎么架构

聊天进程和生成进程分开:web 进程管对话,Worker 进程领任务跑生成,中间是 PostgreSQL 的任务表(领取加租约,前面写过一篇专门讲这个)。生成是长任务(一次出几十道题),放对话进程里会把会话拖死。

多账号限流用分片解决:多个 LLM 账号进 key 池,Worker 按分片序号各领一部分账号,请求数按账号在数据库统一计数。另外每个 Worker 启动时带代码指纹(git 提交号),排查过一个只在生产出现的 bug:web 发了新合同的任务,旧 Worker 按旧模型解析直接拒收。对一行日志,两边指纹是不是一致,十分钟定位。

还有一个容易被忽略的档位设计:模型分快慢两档。浏览、查资料这类轻请求走快速档(关思考),出题走慢档(开思考)。快速档没配置时显式报错,不做静默回落——静默回落意味着性能问题会伪装成随机慢请求,排查的时候根本想不到是档位没配。

收个尾

这些题背概念都能答个大概,但面试官追问两句就见底:“压缩的摘要怎么保证不丢关键约束?""为什么不用向量检索?“——答案都在具体的约束里。这个项目的约束是教学场景的错误成本,所以每层防御都往”宁可慢、不可错、人能兜底”的方向长。你的项目约束是什么,答案就该长成什么样,这大概是我对”Agent 工程化”能给出的最诚实的总结。

← 返回列表