Redis 缓存缓存穿透缓存击穿缓存雪崩缓存穿透没有打到缓存上面且DB也没有而且量级还不小可能是恶意攻击或者参数没有完整校验解决方案参数校验、缓存空值【节约DB浪费缓存】、布隆过滤器缓存击穿热点key突然过期导致大量请求打在DB上。解决方案key设置为不过期、异步任务去更新key。缓存雪崩大量key同时过期大量请求打在DB上。解决方案随机的TTL。多级缓存【当然问题可能会更多】。熔断降级。缓存更新机制讲讲布隆过滤器【滴滴一面】Redis集群模式是如何存Key的一个Redis集群固定有16384个哈希槽。通过CRC16(key) % 16384算出具体的逻辑槽位然后redis集群会存一个全局映射对应逻辑槽位到物理机器。*如果加了新的节点会发生什么ReshardingRedis 持久化AOF类似binlog的日志方式存储变更。占空间执行慢所以恢复慢但安全RDB将内存的全部数据压缩二进制存储。不占空间恢复快不太安全因为时间间隔长全双工和半双工是什么redis pipeline时全双工还是半双工半双工是指沟通时必须单向通信比如对讲机。全双工是指可以同时通信比如现在的电话说回来pipeline应该是半双工因为指令是一起发过去的。Redis的跳表数据结构使用场景底层是链表可以理解为是加了索引的链表使用场景定时任务Score作为时间戳排序执行排行榜根据用户访问量、点赞等更新Score。Redis为什么这么快纯内存操作单线程操作避免了频繁的上下文切换采用了非阻塞I/O多路复用机制【什么是非阻塞I/O多路复用机制】Redis的删除策略过期key的策略定时删除 惰性删除定时删除Redis 每隔一段时间默认每秒 10 次随机抽取一批设置了 TTL 的键删除其中已过期的键。惰性删除拿key的时候做判断如果过期返回空值。同时做删除。【节约CPU】假设Redis只有5G但是你写入了10G多余的是如何处理的呢【Redis的内存淘汰策略】取决于用户配置的淘汰方式具体分为以下几种全局淘汰所有键allkeys-lru淘汰最近最少使用的键最常用。allkeys-lfu淘汰访问频次最低的键。allkeys-random随机淘汰任意键。仅淘汰过期键设置 TTL 的键volatile-lru淘汰最近最少使用的过期键。volatile-lfu淘汰访问频次最低的过期键。volatile-random随机淘汰过期键。volatile-ttl优先淘汰剩余存活时间最短的键。拒绝写入noeviction默认策略内存不足时新写入操作直接报错Redis哨兵模式、主从模式、Cluster模式【阿里高德一面】主从模式是写主一个Master读从多个Slave主节点的数据会同步到从节点。缺少自动故障转移能力需要手动将一个从节点升级为主节点哨兵模式是主从模式的升级版除了Master和Slave还有哨兵哨兵会检测Master的状态如果有问题会投票出一个SLAVE升级为Master。【局限在于还是只有一个Master写入QPS受限且存储量级受单个实例内存限制】集群模式是哨兵模式的升级版在哨兵模式的基础是Redis的大key问题【很多一面都考了】为什么大key会有问题Redis本身是单线程的如果要GET/DEL这个大keyRedis要保证本次请求处理完才会处理下一个会导致后续请求阻塞问题。Redis的热key问题【高德一面】引入local cache降低redis 压力缓存备份比如给key 一个后缀让他分布在多个节点上。方案2可能存在缓存不一致的问题Redis渐进式hash、和golang MAP一致分布式 高可用SetNX是怎么用的比如分布式的一个任务系统那么SETNX的key一般是任务IDvalue是 发起锁的ownerIDvalue的意义在于避免锁被其他人释放。锁的释放用的是CAD命令注意CAD命令本身并不存在redis原生命令中是一段lua脚本。分布式锁SetNX如果某一个任务执行时间长过期了怎么办【阿里高德一面】面试官应该想听锁续期开启守护线程然后定时延长过期时间。等任务执行完之后再释放还有一种成本更低的就是永不过期结束再释放。但是相对不安全【因为可能服务宕机导致锁无法释放。】为什么需要分布式锁分布式系统中多个节点可能会同时访问和修改共享资源这可能导致数据不一致或其他问题。为了避免这种情况需要一种机制来确保在任何时候只有一个节点能够访问和修改共享资源这就是分布式锁的作用。【同时还有分布式事务】限流是什么怎么做限流计数器固定窗口算法比如在一分钟内做计数大于阈值了就直接拒接请求。临界问题例如在时间窗口切换瞬间可能允许两倍阈值请求通过。滑动窗口限流按滑动窗口统计请求量而不是固定窗口。子窗口数据的增减可以理解为是固定窗口的升级版。漏桶算法将请求放在桶里以恒定的速率从桶里拿数据如果桶满了就拒绝。【更类似于MQ消费的场景】令牌桶算法桶里面会以固定速率生存令牌每次接受请求会拿令牌当拿不到令牌时请求拒绝。Kafka、RocketMQ、RabbitMQ有什么区别消息队列是如何保证消息不丢失的可靠性三阶段生产者端ACK机制发送失败则重试MQ Broker端持久化将消息写入磁盘配置多副本保证数据宕机数据不丢失。消费者端业务逻辑处理成功后手动ACK。如果处理异常返回error。让上游重试。消息队列如何保证消息不被重复消费MQ只能保证“至少投递一次”。需要业务方保证唯一性。比如redis SETNX分布式锁。消息队列如何保证顺序消费难以保证全局有序只保证局部顺序RocketMQ底层 时间轮是怎么实现的得看看你如何实现延迟队列简单场景可以用 ZSet或者死信队列得看看频繁Update 1会怎么样MYSQL索引理解B树和B数【滴滴一面】如图具体表现为B树只有最底层节点保存了记录B树是每一个节点都存了记录B树的叶子结点之间是链表连接的遍历时可以走最底层的链表查询B树必须要做中序遍历B树更适合IO类型的操作系统存储是按块存储的主键索引和唯一索引的区别聚簇索引主键索引和非聚簇索引又叫二级索引【滴滴一面】聚簇索引和非聚簇索引的区别就是聚簇索引的叶子结点都存了整个数据行非聚簇索引的叶子结点存的是主键如果需要获取数据行还需要回表查询一次数据行。MySQL的底层存储引擎EXPLAIN 语句分析SQL语句怎么看如何进行慢查询分析type的类型重要【性能从上往下依次拉垮】类型含义例子system/const命中主键或者唯一索引SELECT * FROM USERS WHERE id 1eq_ref唯一性关联一般是连表JOINSELECT * FROM users u JOIN orders o ON o.user_id u.idref命中索引但是不是唯一索引SELECT * FROM users WHERE name 张三不是只有一个人叫做张三range只检索指定范围的行在索引列上使用了,,BETWEEN,IN,LIKE abc%等范围操作。index全索引扫描走了索引但是要把索引全部走一遍SELECT age FROM USERSage已经建立好索引了。但是要全部走一遍索引ALL扫描了全表索引都没走太多了key/possible keys/rows/key_lenpossible keys代表MySQL觉得哪些索引可能有用key代表MYSQL真正要走的索引可以是多个比如有Index merging的场景rows代表命中的预估行数越小越好key_len代表走了索引的字节数Extra类型含义说明Using index覆盖索引大礼包说明查询的数据在索引树里全都有不需要回表性能极佳。Using index condition索引下推 (ICP)说明存储引擎正在帮你过滤数据减少了回表次数。Using where说明过滤动作发生在 Server 层如果 type 是 ALL 且有这个通常意味着你没有走索引。Using filesort文件排序杀手说明 MySQL 无法利用索引完成排序只能在内存或磁盘里折腾。通常需要给 ORDER BY 的字段加索引。Using temporary使用了临时表大杀手常见于 GROUP BY 或 DISTINCT 没走索引。对内存和 CPU 压力巨大。联合索引CREATE INDEX idx_age_name ON users(age, name);这个SQL为例建立了一个联合索引也是B树存储内容区别如下联合索引的好处是针对特定场景下可以不用回表。索引失效的场景不满足最左匹配原则。给字符串列加了索引但开头不定(比如 “%XX”WHERE 条件中有一些函数、计算或者类型转换有OR查询且OR条件中有一些没有索引ICP索引下推是什么mysql 5.5之后支持的新特性主要是针对在二级索引的场景下比如假设你有索引 (zip_code, last_name, first_name)执行 SQLSELECT * FROM employees WHERE zip_code 10001 AND last_name LIKE %Zhang% AND address LIKE %Main St%;在没有ICP之前Server层去存储引擎InnoDB层会走zip_code索引拿到第一个数据之后然后Server每一次都会去主键索引聚簇索引拿到对应的数据行再去判断last_name。有ICP后Server层会去存储引擎层走zip_code索引然后存储引擎层会在走zip_code索引时额外在这个索引里面判断一下last_name是否满足这个like条件。如果满足的话才把这一行拿出来否则跳过这一行。目的是减少回表次数。事务MySQL的事务隔离级别读未提交 (Read Uncommitted)事务 A 可以读到事务 B 还没提交的数据。 - 脏读读已提交 (Read Committed, RC): 只能读到别人提交的的数据 - 不可以重复读可重复读 (Repeatable Read, RR): 在同一个事务内不管你读多少次看到的数据都是一致的就像给数据拍了张快照。脏读能读到其他事务启动中没有commit的数据。不可重复读在事务A中如果事务B对事务A读取的数据做了修改会导致事务A两次读取到的数据不一致幻读 是指在同一事务中多次执行相同的范围查询如 WHERE age 20由于其他事务的 插入INSERT或删除DELETE 操作导致前后读取到的 结果集行数不一致 的现象。【是数据行数的DIFF其他的都是数据值的DIFF】为什么可重复读能解决「不可重复读」的问题如果进行写操作会有行级的排他锁。阻塞其他事务对该行事务的修改。如果进行读操作没有使用For Update利用的MVCC机制【每一行数据会存储一个DB_TRX_ID字段还有DB_ROLL_PTR指向undo log的旧数据来得知这个一行最新的数据是被哪个事务ID修改的。】每个事务启动时生成一个事务ID然后在读数据时会查询第一个 当前事务ID DB_TRX_ID字段的数据返回。唯一索引是否可以为NULL可以而且可以为多个NULL在B树中这个都在树的最左侧统一存着。如果用IS NULL的逻辑的话这个时候不会走索引查询是ONMySQL的日志文件SlowQuery Log配置一个慢查询时间超过多少秒就会定为慢查询。BinlogMYSQL SERVER层记录了修改数据库状态的操作DML和DDL都有。如INSERT、UPDATE、DELETE、CREATE TABLE不包括SELECT 和 SHOW。用来做主从同步和数据恢复比如闪回Redo logInnoDB引擎层:MySQL是先写内存里的数据然后写内存的buffer log redo buffer事务commit之后如果没commit系统也会将redo buffer比如buffer满了或者每隔1s的东西写到磁盘但是如果没有commit系统断电了undo log会将写入磁盘的内容撤销掉然后写Redo log。后台线程定时会将内存数据写到磁盘。Undo logInnoDB引擎层: 存的是事务修改前的老数据用于回滚和MVCC快照读。关于Binlogbinlog有三种记录方式现在工业用的都是Row这个很安全就是很浪费空间锁MYSQL有哪些锁从粒度上来讲全局锁锁整个实例只能读表锁行锁没有命中索引会锁全表行锁。从操作上来讲读锁SELECT … LOCK IN SHARE MODE写锁UPDATE, DELETE, INSERT 语句会自动加 X 锁查询时用 SELECT … FOR UPDATE; 强制加写锁。。InnoDB 的行锁具体有记录锁锁住某一条精准的记录。当你使用唯一索引或主键索引进行精准等值匹配时。间隙锁锁住两个索引记录之间的“空隙”但不包含记录本身它是一个开区间 ()。它的唯一目的就是为了防止其他事务在这个空隙里 INSERT 新数据解决幻读。当你在RR隔离级别下 当使用范围查询或者不存在的记录时临键锁Next-Key Lock默认的算法区间是]前开后闭乐观锁和悲观锁的区别是什么悲观锁是从物理级别加上的锁。所谓“悲观”就是做最坏的打算它拿数据的时候别人一定会来修改乐观锁本质不是锁是业务自己加的比如自己在DB 加了个version字段第一次读的时候拿出version后面更新也加上version的条件当affect 0 row代表有冲突。所谓“乐观”就是觉得别人不会更新讲讲MySQL的 共享锁又叫S锁读锁用的是LOCK IN SHARE MODE多个事务可以同时读但是不能写。讲讲MySQL的 排他锁又叫X锁写锁用的是For Update语句注意写操作会自动加写锁只有SELECT 要加FOR UPDATE给加上写锁mysql会给命中的行数加上X锁。这些行数不可以被其他事务使用当前读比如UPDATE、INSERT、DELETE或者SELECT FOR UPDATE但是可以用普通的SELECT做快照读MySQL的For Update的底层原理【滴滴一面】比如SELECT * FROM XX WHERE ID 1 FOR UPDATE这个语句会给这一行加上一个排他锁又叫X锁写锁不允许其他事务对这一行进行修改。直到事务提交或者回滚。其他事务尝试进行操作时会被阻塞。OS【操作系统】线程、进程、协程的区别一个进程可以创建多个线程一个线程可以创建多个协程【对于go而言】线程是CPU进行调度的最小单位。共享进程的内存和资源。进程是资源分配的最小单位。每个进程独享系统的资源。协程不是操作系统的概念比如go其实是runtime控制的一个轻量级线程。Linux通信同机器跨进程通信有哪些方式管道、共享内存、Socket、甚至RPC也可以跨机器的进程通信的方式Socket、RPC【比如gRPC、thrift】计算机七层协议应用层、TCP和UDP的区别DNSDNS是负责将用户输入的域名地址转换为具体的IP地址。具体流程如下HTTP keep-aliveHTTP 1.0 keep-alive默认关闭HTTP 1.1 后就默认开启了。开启的话允许客户端和服务端复用TCP连接进行多次的HTTP请求Golang内存泄漏和goroutine泄漏怎么排查GPM是什么【滴滴一面】GGoroutine、P处理器、M机器可以理解为就是线程。G由用户创建P本身有一个队列P的数量GOMAXPROCS控制队列容量固定为256。M对应的就是线程每个M会绑定一个P当用户使用go去创建Goroutine时runtime会将G分配到当前M绑定的P的队列里面【如果P的队列满了会将50%放在全局队列中】。然后M会去执行该P中的G如果该P中的G被执行完了M可能会去执行全局队列的G或者去执行其他P的G【50%】或者进入空闲状态【可能自旋线程减少切换开销】。Golang 并发安全的数据类型Channel、Sync.Map、Sync.WaitGroup、Sync.Mutex、atomic.Value讲讲map的实现*hmap存的map的元数据扩容因子、新桶和老桶的指针【渐进式扩容时使用】等数据bmap8个键值对一个溢出桶指针如果满8个了通过链地址法解决hash冲突讲讲Slice的实现type SliceHeader struct { Data uintptr // 指向底层连续数组的指针 Len int // 切片的当前长度 Cap int // 切片的容量底层数组的实际长度 }机制当用append 操作且cap不足时runtime会自动开辟一个更大的内存将原有数据拷贝过去同时更新Data指针指向新数组。具体的扩容机制预估容量CapCap小于256翻倍大于256时是翻倍随后平稳接近1.25倍内存对齐第一步计算的并不是最终容量比如现在的数组是int32 cap5扩容后变成了cap10一个int32是4个字节那么预估内存应该是40个字节但是底层有一个固定的size【8、16、24、32、48、64等等】这里最终给你分配48个字节所以实际cap应该是12而不是10。最后就是按这个分配的字节来开辟内存然后将原有数据copy过去返回新的SliceHeaderData指向的就是新分配的这块内存Golang的GC【滴滴一面】三色标记法白色还没有被访问到的对象。【在标记完成之后如果还是白色就被回收】灰色对象本身被扫描到还被引用但是子对象的情况未知黑色父象被扫描到还被引用且子对象也被引用GC流程触发GC的时机主动调用runtime.GC堆内存到达阈值定时触发兜底两分钟一次GC的流程先STW、暂停用户的Goroutine开始进行标记首先遍历根对象【栈、全局变量、寄存器】将这些设置为灰色。然后并发标记此时用户的程序会正常执行【混合写屏障记录用户程序修改的对象引用避免漏标。】。标记完成之后将标记为白色的变量给清理掉。Golang的内存逃逸内存逃逸一般讲的是本来应该分配到栈上面的数据被分配到堆上面一般是以下几个场景函数返回了局部变量的地址为了确保函数外能够正确访问闭包场景。【大切片数组】Go协程泄露如何排查讲一下Golang的ContextGolang实现两个协程交互打印1-100的数字【阿里高德一面】Golang实现workpool5个协程完成20个任务AI/Agent开发模板角色定义你是一个智能助手负责…任务描述你的任务是…约束条件不要猜测如果不知道请说“不知道”。上下文信息用户当前的问题是…历史记录是…输出格式请以 JSON 格式输出字段包括…Few-Shot 示例下面给出几个例子…ReAct思维模式ReAct 的运行逻辑是一个极其经典的 While(true) 状态机包含三个核心步骤Thought (思考) LLM 分析当前目标和已有信息决定下一步该干嘛。Action (行动) 根据 ThoughtLLM 决定调用某个具体的 Tool也就是我们上节课讲的 Function Calling 输出 JSON。后端捕获后去执行真实代码。Observation (观察) 后端把 Tool 执行的结果比如真实的 DB 返回值、报错的 Exception塞回给 LLM。LLM “观察”到结果后进入下一轮的 Thought。类似于ctxcontext(prompt用户输入)max_iterations5# 可修改最大思考步数current_iteration0whilecurrent_iterationmax_itertions:# 【核心1Thought Action】把上下文发给大模型让大模型决定下一步干嘛llm_responsecall_llm(ctx)ifllm_response.is_finish():# 根据LLM结果判断是否已经完成returnllm_response.answer()# 返回LLM的结果elifllm_response.need_call_tool():# Action场景,调用Tooltry:# 【核心2Action 执行】后端实际去调用底层的 RPC 或 APIresultexecute_tool(tool_name,arguments)# 【核心3Observation】执行成功将结果塞入上下文ctx.append_observation(result)exceptExceptionase:error_msgf调用{tool_name}失败err:{str(e)}ctx.append_observation(error_msg)current_iteration1return系统内部错误繁忙超出思考最大步数短期记忆模型的上下文其实每次都会将历史记录发送给LLM假设我初次发送了100个字第二次发送了200个字那么模型第二次发送会消耗100 LLM回复结果 200 token假设不考虑token压缩的情况下常见的几种短期记忆的淘汰机制对话内容的滑动窗口最简单、粗暴小业务用的。只记录N轮对话内容后端逻辑类似于Redis固定长度List比如通过 LTRIM 裁剪LTRIM chat_list:123 -20 -1只保留最新的20条。基于Token长度裁剪其实也是对话内容的滑动逻辑不过是基于token数来也就是将token的词汇作为滑动窗口的内容。因为有些对话很长有些会很短。摘要记忆引擎 —— 高阶方案滑动窗口的改良版本后端依然保留N段对话对于超出N轮对话之前的对话利用LLM压缩保存在历史message中。思考题当用户的对话达到 20 轮后端触发了“生成历史摘要”的逻辑。调用大模型生成摘要需要耗时 2~3 秒。如果这段逻辑写在用户发消息的主链路里用户发完一句话会卡顿 3 秒以上才能收到回复。如何保证极低的用户延迟核心要义滚动摘要。我自己想到的【也够用】用户对话轮数 19轮将全部的对话发送给LLM。用户对话轮数 19轮将全部的对话发送给LLM。同时发送一条MQ异步生成历史记录总结以user_id chat_id存入redis。用户对话轮数 20轮每次都会读取Redis的历史总结干两件事情调用LLMprompt 当前Redis历史总结 List的后10条对话异步发送MQ拼装List的前10条对话 Redis总结 进行MERGE并再次存入Redis。*最佳优化版【分批次总结而不是每次都滚动更新Redis总结】这里假设历史全部messages是一个MySQL用户每次发送消息都会发送而短期记忆context cache是redis的List。总结的redis key为user_id chat_idvalue为历史内容短期记忆List长度 20prompt redis总结内容【如果有】 redis List的全部对话发送给LLM。短期记忆List长度 20prompt redis总结内容【如果有】 redis List的全部对话发送给LLM。此时还同时发送一条MQMQ内容为 List的前10条发送消息后用LTRIM清理掉List的前10条。消费者在消费时拿MQ中的内容 Redis总结内容【如果有】让LLM进行MERGE总结并再次存入Redis。长期记忆 —— 向量数据库和RAG向量数据库是DBRAG是一种架构我们用向量数据库来实现RAG架构。向量数据库和正常的DB差不多一般有三个核心字段主键keyUUID之类的唯一标识符向量字段Vector索引Embed模型计算之后的向量Payload【大宽表】page_content(原始文本*读出来后给LLM看的)其他你想用/存的字段。RAG链路写链路主要是清洗数据的链路大模型说这是比较脏的活如何将知识库的内容存到向量DB里面。分一个合理的chunk为什么很重要如果chunk太小向量索引非常精准但是可能会出现截断问题比如上一句的场景还没结束这一句开头已经没有这个场景了会导致LLM乱编如果chunk太大喂给LLM的上下文很完美但是在数据检索的时候由于特征太多太长会导致向量索引的效果非常差。所以下面会讲述如何进行chunk拆分规则切分【最基础的】比如设置字符串截断chunk的方式或者是根据换行符等分chunk走Embed模型向量化然后写入向量DB。*重叠切分用基本的规则切分可能会正好将一段连续的话给截断所以可以防御型的将前后两端拓展一些比如设置 chunk_size 500每块 500 字同时设置一个 chunk_overlap 50前后重叠 50 字。 切分结果 Chunk A (0 - 500字): “...第一步是校验 Token第二步是比对 Redis 中的 Session 信息” (假设刚好到这里 500 字) Chunk B (450 - 950字): “第一步是校验 Token第二步是比对 Redis 中的 Session 信息第三步是返回鉴权结果。” (注意前 50 个字是从 Chunk A 的尾巴抄过来的)结构化拆分比如交给专门的分词库或者Markdown的语法解析树来拆分语句。而不是简单的substring这种方式去做截断父子文档双写链路向量数据库用来具体细分到具体文档做检索拿payload的文档id去 关系数据库查全文再交给LLM去参考多Agent架构单Agent的弊端1.大型系统中Tool量级很大那么Token消耗会非常多。2.Token多也会导致延迟极高。 3.严重幻觉Tool太多了可能不知道用哪个工具多Agent的好处多Agent可以理解为我们的微服务干活更加专注每个Agent只需要专注某个垂直领域的一些Tool就可以。Prompt 都极简、专注错误率大幅下降。多Agent架构应该类似于一个Router层 多个子Agent。这个Router层一般是两种方式纯语义路由 —— 将Agent的描述提前离线向量化用户在输入Prompt之后也用EmbeddingAPI转成向量然后去查询大模型分类器 —— 稍微追求一点精准度。比如这个大模型的Prompt可以是你的任务是意图识别。请在以下50个Agent ID中选择最合适的一个。输出格式{target_agent: agent_id}然后完成路由AI交互页面的「打字机模式」是如何实现的【腾讯二面】SSE常见「Sever send-event」或者「Socket」这里主要讲一下Sever send-event在HTTP上用的Content-type场景题大模型输出格式失控比如要返回一个Json再去做处理结果大模型返回了好的你的结果如下{a: 1}希望能帮到你如果代码直接处理这个resp会导致panic或者Exception之类的。方案1: prompt优化比如在system prompt层级要求严格按照JSON格式输出方案2: 防御型解析的中间件比如用正则表达式或者AST解析器单独剔出JSON再做后续处理方案3: 目前各大厂商在模型推理底层支持「原生结构化输出」在调用API时直接加上response_format: {type: json_object}这样的格式。思考题如今的LLM支持百万Token为什么还需要搞RAG和向量数据库直接把PDF全部喂给LLM就行了算法题滑动窗口题解万能模版遇到新题时你只需要思考以下 3 个灵魂拷问就能把模版填满window 里面到底装什么 (是维护区间和还是维护每个字符出现的频率字典)收缩条件是什么 (什么时候窗口变得“不合法”了逼迫 left 必须往右走)结果 ans 应该在哪里更新如果是求最长子串/子数组通常在 while 结束之后窗口恢复合法时更新。如果是求最短子串/子数组通常在 while 内部破坏条件即将恢复合法时更新。defsliding_window_template(s:str)-int:# 1. 初始化left0right0ans0# 根据题目要求初始化求最大值写 0求最小值写 float(inf)window...# 用于记录窗口内状态的数据结构可以是字典、集合、整数求和等# 2. 遍历整个数组/字符串right 指针主动向右扩张whilerightlen(s):# 移入窗口的元素c_ins[right]# --- [A. 更新窗口内的数据状态] ---# 例如window[c_in] 1或者 window_sum c_in# 3. 判断窗口是否满足收缩条件 (破坏了题目要求的合法性)while窗口需要收缩(例如重复字符出现或者和大于 target):# 移出窗口的元素c_outs[left]# --- [B. 恢复窗口内的数据状态] ---# 例如window[c_out] - 1或者 window_sum - c_out# left 指针被迫向右移动缩小窗口left1# 4. 更新最终答案# --- [C. 更新答案] ---# 注意求“最长”通常在这里更新求“最短”通常在上面的 while 循环内部更新。ansmax(ans,right-left1)# right 指针继续向前right1returnans前26个素数 —— 应用在异位词[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]二分查找工作内容面试官你好我叫XXX本科毕业于湖南工业大学之前在字节非中国区商业化实习然后实习转正后来到字节中国区商业化目前工作3.5年主要负责广告平台自动规则、评论管理、推广管理服务的业务迭代和系统优化。最近主要在做的项目是自动规则的业务迭代和系统优化在讲我做的事情之前给您简单介绍一下我们这个业务做的事情和技术架构我们这个业务支持广告主在我们平台上创建一系列规则他可以设置规则的条件规则的行为规则的执行时间等等我们后台会在每一个执行时间间隔去轮询这些规则然后去执行可以理解为是一个离线任务的调度系统。底层存储是异构存储的 MySQL ES、FaaS、后端服务。那在这块业务主要涉及到的事情如下削峰无效规则的检测和运行机制可能拓展讲一下分时任务的运行服务减负ES存储治理和ES写入治理增加系统并发度拆成子扫描任务。【如何保证上线没有问题的】然后还有一个是毕业后负责的项目推广管理的配置化重构反问环节工作时间和节奏对我的评价有哪里需要改善的吗部门涨薪晋升的机制大概是怎么样的公司基建如何接入AI了吗您在团队中的职位小组长吗部门写代码有强制单测的习惯吗代码CR是什么样的规范XHS建议
面经题目,
Redis 缓存缓存穿透缓存击穿缓存雪崩缓存穿透没有打到缓存上面且DB也没有而且量级还不小可能是恶意攻击或者参数没有完整校验解决方案参数校验、缓存空值【节约DB浪费缓存】、布隆过滤器缓存击穿热点key突然过期导致大量请求打在DB上。解决方案key设置为不过期、异步任务去更新key。缓存雪崩大量key同时过期大量请求打在DB上。解决方案随机的TTL。多级缓存【当然问题可能会更多】。熔断降级。缓存更新机制讲讲布隆过滤器【滴滴一面】Redis集群模式是如何存Key的一个Redis集群固定有16384个哈希槽。通过CRC16(key) % 16384算出具体的逻辑槽位然后redis集群会存一个全局映射对应逻辑槽位到物理机器。*如果加了新的节点会发生什么ReshardingRedis 持久化AOF类似binlog的日志方式存储变更。占空间执行慢所以恢复慢但安全RDB将内存的全部数据压缩二进制存储。不占空间恢复快不太安全因为时间间隔长全双工和半双工是什么redis pipeline时全双工还是半双工半双工是指沟通时必须单向通信比如对讲机。全双工是指可以同时通信比如现在的电话说回来pipeline应该是半双工因为指令是一起发过去的。Redis的跳表数据结构使用场景底层是链表可以理解为是加了索引的链表使用场景定时任务Score作为时间戳排序执行排行榜根据用户访问量、点赞等更新Score。Redis为什么这么快纯内存操作单线程操作避免了频繁的上下文切换采用了非阻塞I/O多路复用机制【什么是非阻塞I/O多路复用机制】Redis的删除策略过期key的策略定时删除 惰性删除定时删除Redis 每隔一段时间默认每秒 10 次随机抽取一批设置了 TTL 的键删除其中已过期的键。惰性删除拿key的时候做判断如果过期返回空值。同时做删除。【节约CPU】假设Redis只有5G但是你写入了10G多余的是如何处理的呢【Redis的内存淘汰策略】取决于用户配置的淘汰方式具体分为以下几种全局淘汰所有键allkeys-lru淘汰最近最少使用的键最常用。allkeys-lfu淘汰访问频次最低的键。allkeys-random随机淘汰任意键。仅淘汰过期键设置 TTL 的键volatile-lru淘汰最近最少使用的过期键。volatile-lfu淘汰访问频次最低的过期键。volatile-random随机淘汰过期键。volatile-ttl优先淘汰剩余存活时间最短的键。拒绝写入noeviction默认策略内存不足时新写入操作直接报错Redis哨兵模式、主从模式、Cluster模式【阿里高德一面】主从模式是写主一个Master读从多个Slave主节点的数据会同步到从节点。缺少自动故障转移能力需要手动将一个从节点升级为主节点哨兵模式是主从模式的升级版除了Master和Slave还有哨兵哨兵会检测Master的状态如果有问题会投票出一个SLAVE升级为Master。【局限在于还是只有一个Master写入QPS受限且存储量级受单个实例内存限制】集群模式是哨兵模式的升级版在哨兵模式的基础是Redis的大key问题【很多一面都考了】为什么大key会有问题Redis本身是单线程的如果要GET/DEL这个大keyRedis要保证本次请求处理完才会处理下一个会导致后续请求阻塞问题。Redis的热key问题【高德一面】引入local cache降低redis 压力缓存备份比如给key 一个后缀让他分布在多个节点上。方案2可能存在缓存不一致的问题Redis渐进式hash、和golang MAP一致分布式 高可用SetNX是怎么用的比如分布式的一个任务系统那么SETNX的key一般是任务IDvalue是 发起锁的ownerIDvalue的意义在于避免锁被其他人释放。锁的释放用的是CAD命令注意CAD命令本身并不存在redis原生命令中是一段lua脚本。分布式锁SetNX如果某一个任务执行时间长过期了怎么办【阿里高德一面】面试官应该想听锁续期开启守护线程然后定时延长过期时间。等任务执行完之后再释放还有一种成本更低的就是永不过期结束再释放。但是相对不安全【因为可能服务宕机导致锁无法释放。】为什么需要分布式锁分布式系统中多个节点可能会同时访问和修改共享资源这可能导致数据不一致或其他问题。为了避免这种情况需要一种机制来确保在任何时候只有一个节点能够访问和修改共享资源这就是分布式锁的作用。【同时还有分布式事务】限流是什么怎么做限流计数器固定窗口算法比如在一分钟内做计数大于阈值了就直接拒接请求。临界问题例如在时间窗口切换瞬间可能允许两倍阈值请求通过。滑动窗口限流按滑动窗口统计请求量而不是固定窗口。子窗口数据的增减可以理解为是固定窗口的升级版。漏桶算法将请求放在桶里以恒定的速率从桶里拿数据如果桶满了就拒绝。【更类似于MQ消费的场景】令牌桶算法桶里面会以固定速率生存令牌每次接受请求会拿令牌当拿不到令牌时请求拒绝。Kafka、RocketMQ、RabbitMQ有什么区别消息队列是如何保证消息不丢失的可靠性三阶段生产者端ACK机制发送失败则重试MQ Broker端持久化将消息写入磁盘配置多副本保证数据宕机数据不丢失。消费者端业务逻辑处理成功后手动ACK。如果处理异常返回error。让上游重试。消息队列如何保证消息不被重复消费MQ只能保证“至少投递一次”。需要业务方保证唯一性。比如redis SETNX分布式锁。消息队列如何保证顺序消费难以保证全局有序只保证局部顺序RocketMQ底层 时间轮是怎么实现的得看看你如何实现延迟队列简单场景可以用 ZSet或者死信队列得看看频繁Update 1会怎么样MYSQL索引理解B树和B数【滴滴一面】如图具体表现为B树只有最底层节点保存了记录B树是每一个节点都存了记录B树的叶子结点之间是链表连接的遍历时可以走最底层的链表查询B树必须要做中序遍历B树更适合IO类型的操作系统存储是按块存储的主键索引和唯一索引的区别聚簇索引主键索引和非聚簇索引又叫二级索引【滴滴一面】聚簇索引和非聚簇索引的区别就是聚簇索引的叶子结点都存了整个数据行非聚簇索引的叶子结点存的是主键如果需要获取数据行还需要回表查询一次数据行。MySQL的底层存储引擎EXPLAIN 语句分析SQL语句怎么看如何进行慢查询分析type的类型重要【性能从上往下依次拉垮】类型含义例子system/const命中主键或者唯一索引SELECT * FROM USERS WHERE id 1eq_ref唯一性关联一般是连表JOINSELECT * FROM users u JOIN orders o ON o.user_id u.idref命中索引但是不是唯一索引SELECT * FROM users WHERE name 张三不是只有一个人叫做张三range只检索指定范围的行在索引列上使用了,,BETWEEN,IN,LIKE abc%等范围操作。index全索引扫描走了索引但是要把索引全部走一遍SELECT age FROM USERSage已经建立好索引了。但是要全部走一遍索引ALL扫描了全表索引都没走太多了key/possible keys/rows/key_lenpossible keys代表MySQL觉得哪些索引可能有用key代表MYSQL真正要走的索引可以是多个比如有Index merging的场景rows代表命中的预估行数越小越好key_len代表走了索引的字节数Extra类型含义说明Using index覆盖索引大礼包说明查询的数据在索引树里全都有不需要回表性能极佳。Using index condition索引下推 (ICP)说明存储引擎正在帮你过滤数据减少了回表次数。Using where说明过滤动作发生在 Server 层如果 type 是 ALL 且有这个通常意味着你没有走索引。Using filesort文件排序杀手说明 MySQL 无法利用索引完成排序只能在内存或磁盘里折腾。通常需要给 ORDER BY 的字段加索引。Using temporary使用了临时表大杀手常见于 GROUP BY 或 DISTINCT 没走索引。对内存和 CPU 压力巨大。联合索引CREATE INDEX idx_age_name ON users(age, name);这个SQL为例建立了一个联合索引也是B树存储内容区别如下联合索引的好处是针对特定场景下可以不用回表。索引失效的场景不满足最左匹配原则。给字符串列加了索引但开头不定(比如 “%XX”WHERE 条件中有一些函数、计算或者类型转换有OR查询且OR条件中有一些没有索引ICP索引下推是什么mysql 5.5之后支持的新特性主要是针对在二级索引的场景下比如假设你有索引 (zip_code, last_name, first_name)执行 SQLSELECT * FROM employees WHERE zip_code 10001 AND last_name LIKE %Zhang% AND address LIKE %Main St%;在没有ICP之前Server层去存储引擎InnoDB层会走zip_code索引拿到第一个数据之后然后Server每一次都会去主键索引聚簇索引拿到对应的数据行再去判断last_name。有ICP后Server层会去存储引擎层走zip_code索引然后存储引擎层会在走zip_code索引时额外在这个索引里面判断一下last_name是否满足这个like条件。如果满足的话才把这一行拿出来否则跳过这一行。目的是减少回表次数。事务MySQL的事务隔离级别读未提交 (Read Uncommitted)事务 A 可以读到事务 B 还没提交的数据。 - 脏读读已提交 (Read Committed, RC): 只能读到别人提交的的数据 - 不可以重复读可重复读 (Repeatable Read, RR): 在同一个事务内不管你读多少次看到的数据都是一致的就像给数据拍了张快照。脏读能读到其他事务启动中没有commit的数据。不可重复读在事务A中如果事务B对事务A读取的数据做了修改会导致事务A两次读取到的数据不一致幻读 是指在同一事务中多次执行相同的范围查询如 WHERE age 20由于其他事务的 插入INSERT或删除DELETE 操作导致前后读取到的 结果集行数不一致 的现象。【是数据行数的DIFF其他的都是数据值的DIFF】为什么可重复读能解决「不可重复读」的问题如果进行写操作会有行级的排他锁。阻塞其他事务对该行事务的修改。如果进行读操作没有使用For Update利用的MVCC机制【每一行数据会存储一个DB_TRX_ID字段还有DB_ROLL_PTR指向undo log的旧数据来得知这个一行最新的数据是被哪个事务ID修改的。】每个事务启动时生成一个事务ID然后在读数据时会查询第一个 当前事务ID DB_TRX_ID字段的数据返回。唯一索引是否可以为NULL可以而且可以为多个NULL在B树中这个都在树的最左侧统一存着。如果用IS NULL的逻辑的话这个时候不会走索引查询是ONMySQL的日志文件SlowQuery Log配置一个慢查询时间超过多少秒就会定为慢查询。BinlogMYSQL SERVER层记录了修改数据库状态的操作DML和DDL都有。如INSERT、UPDATE、DELETE、CREATE TABLE不包括SELECT 和 SHOW。用来做主从同步和数据恢复比如闪回Redo logInnoDB引擎层:MySQL是先写内存里的数据然后写内存的buffer log redo buffer事务commit之后如果没commit系统也会将redo buffer比如buffer满了或者每隔1s的东西写到磁盘但是如果没有commit系统断电了undo log会将写入磁盘的内容撤销掉然后写Redo log。后台线程定时会将内存数据写到磁盘。Undo logInnoDB引擎层: 存的是事务修改前的老数据用于回滚和MVCC快照读。关于Binlogbinlog有三种记录方式现在工业用的都是Row这个很安全就是很浪费空间锁MYSQL有哪些锁从粒度上来讲全局锁锁整个实例只能读表锁行锁没有命中索引会锁全表行锁。从操作上来讲读锁SELECT … LOCK IN SHARE MODE写锁UPDATE, DELETE, INSERT 语句会自动加 X 锁查询时用 SELECT … FOR UPDATE; 强制加写锁。。InnoDB 的行锁具体有记录锁锁住某一条精准的记录。当你使用唯一索引或主键索引进行精准等值匹配时。间隙锁锁住两个索引记录之间的“空隙”但不包含记录本身它是一个开区间 ()。它的唯一目的就是为了防止其他事务在这个空隙里 INSERT 新数据解决幻读。当你在RR隔离级别下 当使用范围查询或者不存在的记录时临键锁Next-Key Lock默认的算法区间是]前开后闭乐观锁和悲观锁的区别是什么悲观锁是从物理级别加上的锁。所谓“悲观”就是做最坏的打算它拿数据的时候别人一定会来修改乐观锁本质不是锁是业务自己加的比如自己在DB 加了个version字段第一次读的时候拿出version后面更新也加上version的条件当affect 0 row代表有冲突。所谓“乐观”就是觉得别人不会更新讲讲MySQL的 共享锁又叫S锁读锁用的是LOCK IN SHARE MODE多个事务可以同时读但是不能写。讲讲MySQL的 排他锁又叫X锁写锁用的是For Update语句注意写操作会自动加写锁只有SELECT 要加FOR UPDATE给加上写锁mysql会给命中的行数加上X锁。这些行数不可以被其他事务使用当前读比如UPDATE、INSERT、DELETE或者SELECT FOR UPDATE但是可以用普通的SELECT做快照读MySQL的For Update的底层原理【滴滴一面】比如SELECT * FROM XX WHERE ID 1 FOR UPDATE这个语句会给这一行加上一个排他锁又叫X锁写锁不允许其他事务对这一行进行修改。直到事务提交或者回滚。其他事务尝试进行操作时会被阻塞。OS【操作系统】线程、进程、协程的区别一个进程可以创建多个线程一个线程可以创建多个协程【对于go而言】线程是CPU进行调度的最小单位。共享进程的内存和资源。进程是资源分配的最小单位。每个进程独享系统的资源。协程不是操作系统的概念比如go其实是runtime控制的一个轻量级线程。Linux通信同机器跨进程通信有哪些方式管道、共享内存、Socket、甚至RPC也可以跨机器的进程通信的方式Socket、RPC【比如gRPC、thrift】计算机七层协议应用层、TCP和UDP的区别DNSDNS是负责将用户输入的域名地址转换为具体的IP地址。具体流程如下HTTP keep-aliveHTTP 1.0 keep-alive默认关闭HTTP 1.1 后就默认开启了。开启的话允许客户端和服务端复用TCP连接进行多次的HTTP请求Golang内存泄漏和goroutine泄漏怎么排查GPM是什么【滴滴一面】GGoroutine、P处理器、M机器可以理解为就是线程。G由用户创建P本身有一个队列P的数量GOMAXPROCS控制队列容量固定为256。M对应的就是线程每个M会绑定一个P当用户使用go去创建Goroutine时runtime会将G分配到当前M绑定的P的队列里面【如果P的队列满了会将50%放在全局队列中】。然后M会去执行该P中的G如果该P中的G被执行完了M可能会去执行全局队列的G或者去执行其他P的G【50%】或者进入空闲状态【可能自旋线程减少切换开销】。Golang 并发安全的数据类型Channel、Sync.Map、Sync.WaitGroup、Sync.Mutex、atomic.Value讲讲map的实现*hmap存的map的元数据扩容因子、新桶和老桶的指针【渐进式扩容时使用】等数据bmap8个键值对一个溢出桶指针如果满8个了通过链地址法解决hash冲突讲讲Slice的实现type SliceHeader struct { Data uintptr // 指向底层连续数组的指针 Len int // 切片的当前长度 Cap int // 切片的容量底层数组的实际长度 }机制当用append 操作且cap不足时runtime会自动开辟一个更大的内存将原有数据拷贝过去同时更新Data指针指向新数组。具体的扩容机制预估容量CapCap小于256翻倍大于256时是翻倍随后平稳接近1.25倍内存对齐第一步计算的并不是最终容量比如现在的数组是int32 cap5扩容后变成了cap10一个int32是4个字节那么预估内存应该是40个字节但是底层有一个固定的size【8、16、24、32、48、64等等】这里最终给你分配48个字节所以实际cap应该是12而不是10。最后就是按这个分配的字节来开辟内存然后将原有数据copy过去返回新的SliceHeaderData指向的就是新分配的这块内存Golang的GC【滴滴一面】三色标记法白色还没有被访问到的对象。【在标记完成之后如果还是白色就被回收】灰色对象本身被扫描到还被引用但是子对象的情况未知黑色父象被扫描到还被引用且子对象也被引用GC流程触发GC的时机主动调用runtime.GC堆内存到达阈值定时触发兜底两分钟一次GC的流程先STW、暂停用户的Goroutine开始进行标记首先遍历根对象【栈、全局变量、寄存器】将这些设置为灰色。然后并发标记此时用户的程序会正常执行【混合写屏障记录用户程序修改的对象引用避免漏标。】。标记完成之后将标记为白色的变量给清理掉。Golang的内存逃逸内存逃逸一般讲的是本来应该分配到栈上面的数据被分配到堆上面一般是以下几个场景函数返回了局部变量的地址为了确保函数外能够正确访问闭包场景。【大切片数组】Go协程泄露如何排查讲一下Golang的ContextGolang实现两个协程交互打印1-100的数字【阿里高德一面】Golang实现workpool5个协程完成20个任务AI/Agent开发模板角色定义你是一个智能助手负责…任务描述你的任务是…约束条件不要猜测如果不知道请说“不知道”。上下文信息用户当前的问题是…历史记录是…输出格式请以 JSON 格式输出字段包括…Few-Shot 示例下面给出几个例子…ReAct思维模式ReAct 的运行逻辑是一个极其经典的 While(true) 状态机包含三个核心步骤Thought (思考) LLM 分析当前目标和已有信息决定下一步该干嘛。Action (行动) 根据 ThoughtLLM 决定调用某个具体的 Tool也就是我们上节课讲的 Function Calling 输出 JSON。后端捕获后去执行真实代码。Observation (观察) 后端把 Tool 执行的结果比如真实的 DB 返回值、报错的 Exception塞回给 LLM。LLM “观察”到结果后进入下一轮的 Thought。类似于ctxcontext(prompt用户输入)max_iterations5# 可修改最大思考步数current_iteration0whilecurrent_iterationmax_itertions:# 【核心1Thought Action】把上下文发给大模型让大模型决定下一步干嘛llm_responsecall_llm(ctx)ifllm_response.is_finish():# 根据LLM结果判断是否已经完成returnllm_response.answer()# 返回LLM的结果elifllm_response.need_call_tool():# Action场景,调用Tooltry:# 【核心2Action 执行】后端实际去调用底层的 RPC 或 APIresultexecute_tool(tool_name,arguments)# 【核心3Observation】执行成功将结果塞入上下文ctx.append_observation(result)exceptExceptionase:error_msgf调用{tool_name}失败err:{str(e)}ctx.append_observation(error_msg)current_iteration1return系统内部错误繁忙超出思考最大步数短期记忆模型的上下文其实每次都会将历史记录发送给LLM假设我初次发送了100个字第二次发送了200个字那么模型第二次发送会消耗100 LLM回复结果 200 token假设不考虑token压缩的情况下常见的几种短期记忆的淘汰机制对话内容的滑动窗口最简单、粗暴小业务用的。只记录N轮对话内容后端逻辑类似于Redis固定长度List比如通过 LTRIM 裁剪LTRIM chat_list:123 -20 -1只保留最新的20条。基于Token长度裁剪其实也是对话内容的滑动逻辑不过是基于token数来也就是将token的词汇作为滑动窗口的内容。因为有些对话很长有些会很短。摘要记忆引擎 —— 高阶方案滑动窗口的改良版本后端依然保留N段对话对于超出N轮对话之前的对话利用LLM压缩保存在历史message中。思考题当用户的对话达到 20 轮后端触发了“生成历史摘要”的逻辑。调用大模型生成摘要需要耗时 2~3 秒。如果这段逻辑写在用户发消息的主链路里用户发完一句话会卡顿 3 秒以上才能收到回复。如何保证极低的用户延迟核心要义滚动摘要。我自己想到的【也够用】用户对话轮数 19轮将全部的对话发送给LLM。用户对话轮数 19轮将全部的对话发送给LLM。同时发送一条MQ异步生成历史记录总结以user_id chat_id存入redis。用户对话轮数 20轮每次都会读取Redis的历史总结干两件事情调用LLMprompt 当前Redis历史总结 List的后10条对话异步发送MQ拼装List的前10条对话 Redis总结 进行MERGE并再次存入Redis。*最佳优化版【分批次总结而不是每次都滚动更新Redis总结】这里假设历史全部messages是一个MySQL用户每次发送消息都会发送而短期记忆context cache是redis的List。总结的redis key为user_id chat_idvalue为历史内容短期记忆List长度 20prompt redis总结内容【如果有】 redis List的全部对话发送给LLM。短期记忆List长度 20prompt redis总结内容【如果有】 redis List的全部对话发送给LLM。此时还同时发送一条MQMQ内容为 List的前10条发送消息后用LTRIM清理掉List的前10条。消费者在消费时拿MQ中的内容 Redis总结内容【如果有】让LLM进行MERGE总结并再次存入Redis。长期记忆 —— 向量数据库和RAG向量数据库是DBRAG是一种架构我们用向量数据库来实现RAG架构。向量数据库和正常的DB差不多一般有三个核心字段主键keyUUID之类的唯一标识符向量字段Vector索引Embed模型计算之后的向量Payload【大宽表】page_content(原始文本*读出来后给LLM看的)其他你想用/存的字段。RAG链路写链路主要是清洗数据的链路大模型说这是比较脏的活如何将知识库的内容存到向量DB里面。分一个合理的chunk为什么很重要如果chunk太小向量索引非常精准但是可能会出现截断问题比如上一句的场景还没结束这一句开头已经没有这个场景了会导致LLM乱编如果chunk太大喂给LLM的上下文很完美但是在数据检索的时候由于特征太多太长会导致向量索引的效果非常差。所以下面会讲述如何进行chunk拆分规则切分【最基础的】比如设置字符串截断chunk的方式或者是根据换行符等分chunk走Embed模型向量化然后写入向量DB。*重叠切分用基本的规则切分可能会正好将一段连续的话给截断所以可以防御型的将前后两端拓展一些比如设置 chunk_size 500每块 500 字同时设置一个 chunk_overlap 50前后重叠 50 字。 切分结果 Chunk A (0 - 500字): “...第一步是校验 Token第二步是比对 Redis 中的 Session 信息” (假设刚好到这里 500 字) Chunk B (450 - 950字): “第一步是校验 Token第二步是比对 Redis 中的 Session 信息第三步是返回鉴权结果。” (注意前 50 个字是从 Chunk A 的尾巴抄过来的)结构化拆分比如交给专门的分词库或者Markdown的语法解析树来拆分语句。而不是简单的substring这种方式去做截断父子文档双写链路向量数据库用来具体细分到具体文档做检索拿payload的文档id去 关系数据库查全文再交给LLM去参考多Agent架构单Agent的弊端1.大型系统中Tool量级很大那么Token消耗会非常多。2.Token多也会导致延迟极高。 3.严重幻觉Tool太多了可能不知道用哪个工具多Agent的好处多Agent可以理解为我们的微服务干活更加专注每个Agent只需要专注某个垂直领域的一些Tool就可以。Prompt 都极简、专注错误率大幅下降。多Agent架构应该类似于一个Router层 多个子Agent。这个Router层一般是两种方式纯语义路由 —— 将Agent的描述提前离线向量化用户在输入Prompt之后也用EmbeddingAPI转成向量然后去查询大模型分类器 —— 稍微追求一点精准度。比如这个大模型的Prompt可以是你的任务是意图识别。请在以下50个Agent ID中选择最合适的一个。输出格式{target_agent: agent_id}然后完成路由AI交互页面的「打字机模式」是如何实现的【腾讯二面】SSE常见「Sever send-event」或者「Socket」这里主要讲一下Sever send-event在HTTP上用的Content-type场景题大模型输出格式失控比如要返回一个Json再去做处理结果大模型返回了好的你的结果如下{a: 1}希望能帮到你如果代码直接处理这个resp会导致panic或者Exception之类的。方案1: prompt优化比如在system prompt层级要求严格按照JSON格式输出方案2: 防御型解析的中间件比如用正则表达式或者AST解析器单独剔出JSON再做后续处理方案3: 目前各大厂商在模型推理底层支持「原生结构化输出」在调用API时直接加上response_format: {type: json_object}这样的格式。思考题如今的LLM支持百万Token为什么还需要搞RAG和向量数据库直接把PDF全部喂给LLM就行了算法题滑动窗口题解万能模版遇到新题时你只需要思考以下 3 个灵魂拷问就能把模版填满window 里面到底装什么 (是维护区间和还是维护每个字符出现的频率字典)收缩条件是什么 (什么时候窗口变得“不合法”了逼迫 left 必须往右走)结果 ans 应该在哪里更新如果是求最长子串/子数组通常在 while 结束之后窗口恢复合法时更新。如果是求最短子串/子数组通常在 while 内部破坏条件即将恢复合法时更新。defsliding_window_template(s:str)-int:# 1. 初始化left0right0ans0# 根据题目要求初始化求最大值写 0求最小值写 float(inf)window...# 用于记录窗口内状态的数据结构可以是字典、集合、整数求和等# 2. 遍历整个数组/字符串right 指针主动向右扩张whilerightlen(s):# 移入窗口的元素c_ins[right]# --- [A. 更新窗口内的数据状态] ---# 例如window[c_in] 1或者 window_sum c_in# 3. 判断窗口是否满足收缩条件 (破坏了题目要求的合法性)while窗口需要收缩(例如重复字符出现或者和大于 target):# 移出窗口的元素c_outs[left]# --- [B. 恢复窗口内的数据状态] ---# 例如window[c_out] - 1或者 window_sum - c_out# left 指针被迫向右移动缩小窗口left1# 4. 更新最终答案# --- [C. 更新答案] ---# 注意求“最长”通常在这里更新求“最短”通常在上面的 while 循环内部更新。ansmax(ans,right-left1)# right 指针继续向前right1returnans前26个素数 —— 应用在异位词[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]二分查找工作内容面试官你好我叫XXX本科毕业于湖南工业大学之前在字节非中国区商业化实习然后实习转正后来到字节中国区商业化目前工作3.5年主要负责广告平台自动规则、评论管理、推广管理服务的业务迭代和系统优化。最近主要在做的项目是自动规则的业务迭代和系统优化在讲我做的事情之前给您简单介绍一下我们这个业务做的事情和技术架构我们这个业务支持广告主在我们平台上创建一系列规则他可以设置规则的条件规则的行为规则的执行时间等等我们后台会在每一个执行时间间隔去轮询这些规则然后去执行可以理解为是一个离线任务的调度系统。底层存储是异构存储的 MySQL ES、FaaS、后端服务。那在这块业务主要涉及到的事情如下削峰无效规则的检测和运行机制可能拓展讲一下分时任务的运行服务减负ES存储治理和ES写入治理增加系统并发度拆成子扫描任务。【如何保证上线没有问题的】然后还有一个是毕业后负责的项目推广管理的配置化重构反问环节工作时间和节奏对我的评价有哪里需要改善的吗部门涨薪晋升的机制大概是怎么样的公司基建如何接入AI了吗您在团队中的职位小组长吗部门写代码有强制单测的习惯吗代码CR是什么样的规范XHS建议