面试官您好,我叫吴洪磊,目前在 Shopee 做开发,有 8 年工作经验,最近 3 年主要往全栈和 AI 工程方向深耕。过去一年多,我独立从零到一搭建并上线了一个 AI 对话平台,支持多模态输入和工具调用,整体全栈架构、部署、运维是我自己完成的。
我这次面试的是 Agent 全栈工程师,我觉得自己的核心优势在于:能独立完成一个 AI 系统的架构设计、前后端开发、生产部署和认证接入的全链路,而不是只做其中某一个环节。
能独立完成一个 AI 系统的架构设计、前后端开发、生产部署和认证接入的全链路。
这是一个面向开发者的 AI 对话平台,由我独立设计和实现。当时我发现市面上的通用 ChatBot 有两个痛点:一是多轮聊久了上下文会爆,模型开始"失忆";二是给了工具之后,模型很容易"上头"——比如反复搜同一个东西、反复抓同一个页面,自己不会收手。所以我整个项目的核心攻关就是围绕这两件事展开的。
上下文管理我拆成了两个阶段来看:单轮问答里的工具链路控量,和多轮问答之间的历史记忆治理。
每次工具返回结果后,不是无脑全塞给模型,而是先看结果 token 有多大。如果比较小,直接保留;但如果超过 6k token,就会先做压缩。这个压缩不是简单截断,而是先把内容切成块,然后算每一块和用户问题的相关性,只保留最相关的片段,目标压到 5k token 左右。这样能保证模型拿到的是精简但有效的信息。
另外,如果有多轮工具调用,我会在每轮工具调用结束后估算累计的上下文总大小。一旦超过模型上限的 80%,就强制结束工具循环,进入最后一轮只回答、不调工具。相当于给工具链路装了一个熔断器,防止它把上下文拖爆。
这块的相关性筛选本质上是向量检索,不是简单关键词匹配。
具体流程是先把工具返回的长 Markdown 按块切开,然后把"用户问题"和"每个内容块"都用 embedding 模型转成向量,再用 FAISS 做相似度检索,按相关性从高到低选片段。
选片段时我还加了一层 token 预算控制:只保留预算内最相关的块,避免上下文继续膨胀。
另外如果原始内容本来就不大、没超过容忍阈值,就不压缩,直接用原文,避免为了压缩而压缩带来的信息损失。
我的做法是先按"轮"来截窗口,一轮就是 user + assistant 的完整对话。保留最近若干轮,然后按 token 预算从新到旧整轮累加,超预算就停。窗口内更早的工具结果优先用 summary,没 summary 且过长就截断;但最新一轮尽量保留原文,避免影响当前对话的连续性。
滑到窗口外面的消息我也不会扔掉,而是会给它们生成一段摘要,放进提示词里告诉模型"前面咱们聊过这些"。
后续对于新加进来的消息,我会把"原来的摘要 + 这批新消息"一起丢给模型,让它输出一个新的完整摘要。相当于让模型基于上一次的总结继续压缩,而不是每次都从零开始重新总结全部历史。这样计算成本低很多,而且摘要风格稳定,不会每次都在变。
这个是我花了很多心思的地方。模型有了工具之后,真的很容易"上瘾"。比如用户问一个技术问题,模型可能先联网搜索一下,然后觉得不够,又搜一个近义词,再抓几个页面,陷入循环。我治理的思路是硬约束兜底 + 软引导收敛,两层一起上。
单次请求里,工具调用最多走 10 轮,每个工具最多用 5 次,到了就从可用列表里剔除,执行层也会拦住。上下文超过模型上限 80% 强制熔断。
还有防重复机制:新搜索词和历史搜索词相似度超过 0.7,或者要抓的 URL 和已抓 URL 重叠率超过 0.7,都会触发停止。
另外还有一些全局阈值:已经搜过 2 次、抓过 2 次、累计提取 URL 超过 5 个、总工具调用超过 6 次,都会建议模型别再调工具了。
执行层还有一层兜底:网页提取在调工具之前会先过滤已抓过的 URL。如果这一轮传进来的全是旧 URL,就不会真的外呼,只返回一条提示——相当于"走了一次流程,但没有产生外部调用"。
这块我没有上 embedding,而是用了一个轻量的关键词重叠算法,主要考虑到在线链路里要快、可解释、成本低。具体是把 query 做小写分词,去掉停用词,拿两个关键词集合算"交集 / 较大集合大小"的分数。阈值设在 0.7,超过就认为重复并触发停止。比较对象是"当前新 query"对比"历史所有联网搜索的 query",取最高分判定。
我每轮都会往用户消息里拼提示,让模型被动收敛。具体有几种触发场景:
第一,如果某个工具已经触发了 5 次上限,下一轮提示里会点名禁用,比如"以下工具已达到最大调用次数上限,当前不可再调用:联网搜索"。同时工具列表里也会移除,模型想调也调不到。
第二,如果已经调用过联网搜索,会追加温和提示:已经搜过了,结果里通常已有关键片段,除非确实要看全文,否则不要马上转去调网页提取。比如用户问"某新闻是否属实",搜过一次后应该先利用摘要信息,不要立刻继续搜或大批抓全文。
第三,如果已经调用过网页提取且累计提取 URL 达到 3 个,会追加带实时数字的提醒,比如"已提取了 3 个 URL 的内容,若信息已够,请停止工具并直接最终回答。" 数字是动态计算的,模型能看到自己"已经抓了多少"。
第四,如果继续条件判断不应该继续——比如近义重复搜、URL 重叠、或者总工具次数到了——会把具体的 stop 文案写进提示,并且带上具体数字。比如"当前查询与历史查询相似度 75%,建议停止搜索",或者"当前 URL 列表中有 3 个已提取过,重叠率 80%"。
第五,如果满足上面这些条件,而且当前已经是第 2 轮及以后,我还会再追加一段"信息可能已经足够"的固定提示,核心就是提醒模型先检查历史工具结果,够回答就直接 final,避免相似 query 重复搜、避免重复抓老 URL。相当于给模型一个"自检清单"。
除了这两个核心难点,平台还做了 SSE 流式传输,基于 FastAPI StreamingResponse 实现,前端支持自动重连与续传,断连期间用内存队列做消息补偿。工具生态接入了天气、搜索、代码执行、文档查询 4 类 MCP 工具。代码执行用的是 Piston 沙箱,叠加应用层超时控制和默认禁止网络访问,做双层安全。生产部署也全链路打通了,Docker Compose + Nginx,接入了微信 OAuth 登录。
后续我计划把上下文压缩从规则策略演进为语义化评分,对历史消息按重要性排序后选择性保留。SSE 断连恢复也会从内存队列升级到 Redis Stream。
这是我在 Shopee 做的另一个项目。传统 SEO 优化的是搜索引擎排名,但 ChatGPT 这种生成式引擎不一样,它会综合多信源直接生成答案,并在回复里推荐商品。GEO 的目标就是提高我们品牌在 AI 回复里的曝光比例。
我主要负责的是自动化采样这一块的稳定性和可恢复性建设,同时也承担了 Portal 前端的核心模块。
AI 搜索的回复是生成式的,同一个问题每次回复的内容和推荐商品都可能不同,而且推荐位次、商品卡片这些结构化信息,在标准 API 返回的纯文本 Markdown 里是拿不到的。所以我们必须用 Playwright 模拟真实用户交互,采集浏览器渲染后的完整回复,才能提取品牌曝光和推荐位次。
但这就带来一个问题:为了消除生成随机性,同一个关键词要多次采样,这对采集流程的稳定性要求极高。项目初期这条链路的任务成功率大概只有 60%。
第一,先把 ChatGPT 等多平台采样链路统一成一套平台化执行流程;
第二,再把 Kafka 消费的并发阈值和暂停恢复机制补齐;
第三,最后在账号池里落地一套冷却策略,采样后从固定冷却改成根据账号过去一段时间成功率动态调整冷却时长,同时按失败类型做差异化重试和代理切换。最终任务成功率从 60% 提升到了 80%。
同时我还搭了 Prometheus/Grafana 的监控面板,跟踪成功率、失败原因分布和耗时分布,缩短异常定位的时间。
我们这套 Playwright 采集不是单脚本跑完就结束了,而是 Kafka 任务驱动 的一套完整流程。任务进来之后,先做校验和并发控制,然后 从 Redis 账号池预占 一个带登录态的账号,接着打开浏览器,模拟真人去搜、去看、把结果抓回来。
浏览器这块我做了个复用加隔离的设计:底层 Chromium 进程是复用的,不用每次都重启,但每个任务独立开 context,互相不干扰。而且每次新建 context 的时候,都会 注入代理、随机 UA、时区和反检测脚本,让它看起来更像真人操作,而不是机器在批量跑。
不同平台页面结构不一样,比如 ChatGPT 和 Google 的界面完全不同,所以我给每个平台写了独立的采集模板,把"打开页面、模拟交互、提取数据"这些步骤标准化。采集成功后,会 回写最新登录态到 Redis,这样这个账号下次还能接着用;同时截图和结构化结果也会一并回传。
反爬这块我们遇到的麻烦主要有四类:一是页面直接弹验证拦你(比如 Cloudflare、验证码);二是账号本身被风控(登录态失效、账号受限);三是代理网络质量差,超时或跳验证;四是页面虽然打开了,但关键数据(推荐商品、引用来源)没加载全。
应对上分三步走:第一步快速判断,页面一打开就识别是不是被拦了,被拦了立刻标记失败,不浪费时间死等;第二步自动恢复,换代理端口、换账号、重新排队执行;第三步持续治理,监控哪些账号经常挂、哪些代理线路不稳定,自动把失效账号下线,同时校验结果质量,确保传回来的数据是完整的。
核心思路就是把反爬当成常态故障来处理,而不是采不到就无脑重跑。
这个提升不是靠一个参数调优,而是把失败做了分层治理。原来链路虽然有重试、换代理、账号冷却,但策略比较粗,比如连续重试会反复撞同一个风控窗口,代理也是纯随机,账号冷却不区分质量。
我主要做了三件事:第一,重试策略从"立即重试"改成"指数退避 + 抖动",并按失败类型分流:网络超时这类可恢复错误继续重试;登录失效、人机验证这类不可恢复错误直接终止,避免无效消耗。第二,代理轮换从纯随机改成短窗口健康度治理:某代理连续失败就临时摘除几分钟,减少坏代理反复命中。第三,账号池从固定冷却改成"动态分层 + 小幅随机":动态根据账号近期成功率定基线冷却时长,高质量账号更快回池,低质量账号延长冷却,连续失败就长冷却或下线;随机只做小抖动打散回池时间,避免同一批账号同时回来。
熔断上我做的是分级熔断:任务级在不可恢复错误出现时立即终止,避免把单任务打穿;通道级在某代理或账号短时失败率过高时临时隔离,等冷却后再放回。最后通过三组指标做闭环:整体成功率、平均重试次数、失败原因占比。上线后一段时间,成功率稳定从 60% 左右提升到 80% 左右,而且波动也明显变小。
这个项目是解决业务团队回归测试的痛点:手写脚本门槛高、维护成本大。我们的目标是录制即可回放,让测试同学不用写代码就能构建用例。
我负责的是全链路研发,从 Chrome 插件采集端,到服务端,再到 Playwright 执行端,整个链路都是我打通的。
核心思路是:用户在页面上操作,Chrome 插件捕获这些操作并结构化为可执行步骤;录制过程中可以直接配置断言体系,覆盖文本、属性、可见性、API 参数校验和图像相似度比对。执行端我维护了 Playwright 引擎,支持失败重试、中断控制、实时状态回传和截图定位。
这里有个细节我觉得值得一提:页面版本迭代后,原来的定位器很容易失效。我引入了 相似性定位策略,通过元素的属性相似度来做容错定位,显著提升了跨版本回放的稳定性。
最后的效果是,自动化用例的产出周期从 2 天缩短到 0.5 天,平台落地了 11 个业务项目,关键流程的回归覆盖率提到了 80%。
这个技术的核心思想是"找最像的",不是"找完全一样的"。传统定位器像 ID、XPath 是精确匹配,页面一改就容易失效;Similo 是把目标元素的多维特征提出来,再去新页面里找相似度最高的候选。
具体做法是先把旧元素抽成特征画像,比如标签、文本、位置、大小、链接、类名、XPath 等十几项;然后把新页面可见元素都当候选,逐个算相似度。每个特征先有一个子分数,再按权重加权求总分,稳定特征权重大一些,容易漂移的特征权重低一些。
最后选总分最高的元素作为定位结果。这样即使页面结构微调、路径变化、属性小改,也能靠"综合相似度"找到正确元素,跨版本回放稳定性会明显更高。
我当时落地它的主要价值就是:把"页面改一点脚本就挂"的情况降下来,减少回归用例的维护成本。
插件端的核心思路是"content script 做页面级监听器"。我们把 content script 注入用户打开的页面,在 document 上监听点击、输入、键盘、滚动、resize 等事件,而且大多放在捕获阶段,尽量先拿到原始事件,避免被页面业务脚本吞掉。
用户一操作,我们就把浏览器事件翻译成平台命令,比如点击变成 mouseDown/mouseUp,输入变成 input/sendKeys,下拉是 select,滚动是 scrollTo/scrollElementTo。每条命令都会补齐上下文:DOM path、元素特征、坐标、文本、frame 信息。然后通过 chrome.runtime.connect 建立的 port 发给 background,由 background 做统一队列、截图补充,再通过 WebSocket 落到录制服务。
数据结构上我用的是三层设计。第一层是统一信封(type + delay + data),保证调度器只关心"动作类型和执行时机";第二层是动作载荷,不同动作字段各自扩展,比如 click 有 path/x/y,input 有 text,scroll 有 scrollX/scrollY;第三层是回放兜底信息,比如 properties、viewpoint、clientX/clientY、screenshotFile,用来做定位容错和故障回溯。核心目标是"协议稳定、动作可扩展",既能录得全,也能放得稳。
这是我在 Shopee 早期做的项目,负责 Facebook 投放编辑器的前端建设。核心挑战是 Campaign / AdSet / Ad 三层树结构的状态联动和跨层级校验,异步校验和表单状态触发时机很容易不一致。我通过建立跨层级联动校验机制,支持了新建、复制、编辑、部分成功回写的双向模型转换。
另外我主导了一个通用批量编辑组件的抽象,通过配置驱动,在 13 个以上的业务场景复用,降低了同类需求的交付周期。
这种复杂表单我一般用"一棵树 + 两条通道 + 三层职责"来设计。先定义统一领域模型,把 Campaign/AdSet/Ad 直接建成一棵 treeData 作为单一数据源;每个节点除了业务字段,还带 meta,比如 isChanged、isValid、errorType,这样改动状态、合法性和错误定位都能在一套模型里表达。
交互上是两条通道并行:输入通道负责把用户改动落到当前节点,并触发依赖重算(显隐、默认值、候选项、字段重置);校验通道负责按层跑校验规则,必要时扩散到子树或兄弟节点,再把结果回写到节点 meta,做到树上即时反馈。
职责上我会分三层:视图层只管渲染和采集输入;规则层负责联动和校验(纯函数);编排层负责时序,比如切节点先收集、变更后校验、发布前全量校验。这样做的好处是状态不分裂、跨层联动可控,发布前也能做全局一致性检查。落地时我还会特别避免两个坑:一是 UI 状态和提交状态分离导致脏写,所以切节点前强制收集;二是跨层依赖散在组件里,所以联动和校验都抽成纯函数规则层,组件只做编排和展示。
这个组件我做的是"流程通用化 + 字段配置化"。以前每个业务都要重复写一套弹窗、拉数、编辑、发布、轮询、失败展示,代码重复很多。我把这套共性流程收敛进 BatchEditModal,业务方只需要提供配置和接口,不再重复造轮子。
我主要抽象了五类能力:第一是通用流程能力,内置查询、弹窗、发布任务、轮询进度、失败明细和未保存离开提醒;第二是字段配置能力,左侧每个菜单项都是一个配置对象,定义字段怎么渲染、怎么批量改、怎么判断是否变更;第三是批量操作能力(Edit All),每个字段可挂自己的批量编辑器;第四是校验和发布门禁,按菜单维度跟踪校验状态,只有"有变更 + 无错误"才允许发布;第五是可扩展能力,比如行级禁用原因、自定义比较函数、底部汇总。
配置结构上可以分三层:弹窗层(title/modal/footer)、接口层(query/update/polling/失败解析/发布前校验)、编辑项层(items 的 columns/editAll/namePathList/isEqual/disableDesc/renderFooter)。一句话总结就是:把批量编辑从"页面逻辑"升级为"配置驱动组件",业务只关心字段规则和接口,流程一致性和复用效率都会明显提升。
以上就是我的主要经历。我觉得从传统前端到 AI 全栈的转型过程中,最大的收获是学会了在快速迭代和系统稳定性之间做权衡——比如 Chat Agent 项目里,我先用了内存队列保证上线,再规划 Redis Stream 的演进路径;工具调用的硬约束也是先上线兜底,再逐步细化软引导策略。
如果您对我自研的 ReAct Loop 上下文压缩方案,或者 GEO 的 Playwright 采集方案感兴趣,我可以展开详细讲讲。
| 钩子位置 | 预期追问 | 核心答案要点 |
|---|---|---|
| 模型反复搜不会收手 | 怎么让它收手? | 硬约束 + 软引导两层:硬约束是多道闸门(10轮/5次/相似度0.7/重叠率0.7/上下文熔断),软引导是每轮拼提示让模型被动收敛 |
| 6k→5k 切块+相关性 | 相关性怎么算? | 内容切块后,按与用户问题的相关性筛选,保留最相关片段,目标压到 5K token |
| 80% 阈值熔断 | 阈值怎么定的? | 经验值,留 20% buffer 给 final round 的回答生成,避免上下文顶满 |
| 增量摘要机制 | delta怎么merge? | 窗口外消息没变就复用旧摘要,新增内容做delta摘要再merge,保证语义连贯 |
| 相似度0.7停止 | 相似度怎么算? | embedding 相似度或文本特征匹配(根据你实际实现补充) |
| 执行层兜底过滤旧URL | 用户要重新抓怎么办? | 执行层过滤,但可以通过参数强制刷新或绕过缓存(根据实际实现补充) |
| 每轮往user message拼提示 | 会不会影响原始问题理解? |
用
\n\n注意:
格式追加,放在原内容后面,实际测试对模型理解影响很小
|
| 第2轮才追加自检清单 | 为什么第2轮? | 第一轮信息通常不足,第2轮后模型已经有一定工具结果,此时提醒收敛最有效 |
| 语义化评分演进 | 具体怎么做? | 计划引入重要性评分模型,对历史消息按语义重要性排序后选择性保留 |
| 简历原文 | 口语化怎么说 | 作用 |
|---|---|---|
| "单轮工具链路控量 + 多轮历史记忆治理" | "我拆成两个阶段来看:单轮内的工具链路,和多轮之间的历史记忆" | 结构化表达,显逻辑 |
| "6k→5k token,超模型上限 80% 强制熔断" | "超过 6K 就做压缩,目标压到 5K,超过 80% 就熔断" | 数字具象化,显工程精细度 |
| "硬约束(10轮/5次/0.7去重)与软引导(每轮动态拼收敛提示)" | "硬约束兜底 + 软引导收敛,两层一起上" | 先给框架,再展开细节 |
| "执行层兜底过滤已抓 URL" | "走了一次流程,但没有产生外部调用" | 口语化比喻,易理解 |
这份口语化版本现在和修改后的简历逐句对齐,面试时你可以放心展开,不会出现"简历写了但讲不清"或"讲了很多但简历没写"的脱节问题。
如需我再补一页技术深挖版,可优先整理"相关性筛选算法"与"delta 摘要 merge 逻辑"。