leecodecode【回溯子集】【2026.6.4打卡-java版本】
电话号码的字母组合要点dfs什么时候返回这一步要干什么下一步是什么选或者不选传递ipathsclass Solution { private static final String[] MAPPING { , // 0 , // 1 abc, // 2 def, // 3 ghi, // 4 jkl, // 5 mno, // 6 pqrs, // 7 tuv, // 8 wxyz // 9 }; public ListString letterCombinations(String digits) { //回溯 int n digits.length(); ListString ans new ArrayList(); char[] path new char[n]; dfs(0,path,ans, digits.toCharArray()); return ans; } public void dfs(int i, char[] path, ListString ans, char[] digits){ if(i digits.length ){ ans.add(new String(path)); return; } String temp MAPPING[digits[i] - 0]; for(char c : temp.toCharArray()){ path[i] c; dfs(i1,path,ans,digits); } } }子集要点方法1 pathans什么时候ans。addans)dfsi 不选的dfsi1 选择则加然后df还要回溯class Solution { private final ListListInteger ans new ArrayList(); private final ListInteger path new ArrayList(); private int[] nums; public ListListInteger subsets(int[] nums) { this.nums nums; dfs(0); return ans; } private void dfs(int i){ if(i nums.length){ ans.add(new ArrayList(path)); return; } //不选 dfs(i1); //选择 path.add(nums[i]); dfs(i1); path.remove(path.size() - 1); } }方法2从答案的视角c里面有forji,开始循环dfsclass Solution { private final ListListInteger ans new ArrayList(); private final ListInteger path new ArrayList(); private int[] nums; public ListListInteger subsets(int[] nums) { //要恢复现场之前怎么样现在就是怎么样 this.nums nums; dfs(0); return ans; } private void dfs(int i){ ans.add(new ArrayList(path)); if(i nums.length){ return; } for(int j i; j nums.length; j){ //选择 path.add(nums[j]); dfs(j1); path.remove(path.size() - 1); } } }分割回文串要点判断回文选dfs要判断现在是否回文还是不选【dfsi1】传参是istartclass Solution { public ListListString partition(String s) { //选不选的视角 ListListString ans new ArrayList(); ListString path new ArrayList(); dfs(0, 0,path, ans, s); return ans; } public void dfs(int i, int start, ListString path, ListListString ans, String s){ if(i s.length()){ ans.add(new ArrayList(path)); return; } //不分割 if(i s.length() -1){ dfs(i1, start,path,ans, s); } //分割 if(ishuiwen(s,start,i)){ path.add(s.substring(start,i1)); dfs(i1,i1,path,ans,s); path.removeLast(); } } public boolean ishuiwen(String s, int left, int right){ while(left right){ if(s.charAt(left) ! s.charAt(right)){ return false; } left; right--; } return true; } }要点2从答案视角forji之间是否回文class Solution { public ListListString partition(String s) { //选不选的视角 ListListString ans new ArrayList(); ListString path new ArrayList(); dfs(0, path, ans, s); return ans; } public void dfs(int i, ListString path, ListListString ans, String s){ if(i s.length()){ ans.add(new ArrayList(path)); return; } //答案视角枚举子串结束的位置 for(int j i; js.length(); j){ if(ishuiwen(s,i,j)){ path.add(s.substring(i, j1)); dfs(j1,path,ans,s); path.removeLast(); } } } public boolean ishuiwen(String s, int left, int right){ while(left right){ if(s.charAt(left) ! s.charAt(right)){ return false; } left; right--; } return true; } }随机知识Agent Harness 在设计和实现上确实学习借鉴了很多计算机操作系统中的核心概念。虽然两者的最终目标不同一个管理硬件资源一个编排智能体与环境的交互但解决复杂系统问题时面临的挑战是相似的因此很多设计思想和术语都被“借用”了过来。下面我具体分析一下Harness 从操作系统中“学习”了哪些概念以及它们是如何映射的。核心借鉴点对照表操作系统概念Agent Harness 中的对应说明进程/线程Episode / Rollout / Trial一次独立的 Agent-环境交互执行流有独立的状态、上下文。Harness 管理多个 Episode 的并发或顺序执行。调度器步进循环Step Loop / 批处理调度控制 Agent 和环境之间 step 的顺序、频率、优先级。例如多个 Agent 共享一个环境时需要调度谁先行动。系统调用step(action) - (obs, reward, done, info)这是 Harness 提供给 Agent 的统一抽象接口屏蔽底层环境差异就像系统调用屏蔽了硬件差异。内存管理 地址空间观察缓存Observation Buffer / 重放缓冲区Replay BufferHarness 管理历史轨迹的存储、分配、回收支持随机采样类似页置换或堆管理。中断/异常处理done标志 / 超时处理 / 错误回调当 episode 结束环境抛出 done或发生错误环境崩溃、超时Harness 会捕获并触发相应处理逻辑。设备驱动程序环境适配器Environment Adapter / Wrapper将不同环境Atari、机器人、Web API的私有接口转换为 Harness 的统一接口如同驱动隐藏硬件细节。文件系统实验日志 / 指标存储 / 检查点Harness 提供持久化机制保存 Agent 的状态、轨迹、评估结果支持按时间或标签检索。权限/安全隔离动作屏蔽Action Masking / 沙箱限制 Agent 的非法动作如越界、危险指令防止破坏环境或数据泄露类似操作系统保护进程不互相干扰。进程间通信 (IPC)Agent-环境之间的数据传递通过 Harness 的中介队列Harness 常使用通道channel或共享内存来高效传递观测和动作解耦决策与执行。死锁检测 超时步超时 / 环境无响应处理Harness 会监控每一步的执行时间若 Agent 卡住或环境无响应则重置或报错避免整个实验僵死。更深入的两个例子1. “资源管理”的相似性操作系统管理有限的物理资源CPU时间、内存页、磁盘带宽调度算法CFS、FIFO、优先级确保公平和效率。Harness管理有限的计算资源GPU显存、API调用次数、环境仿真时间和数据资源回放缓冲区大小。需要决定何时从缓冲区删除旧数据类似页面置换LRU常用如何优先处理高回报的 episode类似调度优先级。2. “抽象层”的分层设计操作系统有清晰的用户态/内核态分层系统调用是唯一跨越边界的方式。Harness 也有类似分层用户态Agent 层策略网络、价值函数只关心(obs, reward)不知道底层环境的实现。内核态Harness 层环境接口、数据收集、错误恢复提供稳定的执行环境。这种分层强制隔离了关注点使得 Agent 可以更换环境而无需修改代码——正如应用程序可以运行在不同硬件上而无需重写。为什么 Harness 需要借鉴操作系统概念因为两者都要解决“管理一个复杂、异步、并发、易出错的执行环境”这个通用问题。操作系统为此积累了数十年的成熟方案如调度、缓冲、错误隔离、资源抽象Harness 工程直接复用这些思想比自己从头发明要可靠和高效。一个具体的开源例子OpenAI Gym 的Env接口 运行脚本其实就是一个极简的 Harness。但它没有内置复杂的调度和缓存。更高级的框架如RLlib(Ray) 中的RolloutWorker和PolicyEvaluator就明显借鉴了操作系统的概念它使用Actor 模型类似进程来并行执行多个 episode。它有一个中心调度器来分配 CPU/GPU 资源给不同的 RolloutWorker。它实现了优先级重放缓冲区允许更高 TD-error 的样本被更频繁采样类似 I/O 优先级调度。总结是的Harness 工程大量借鉴了计算机操作系统的设计概念尤其是在抽象接口、资源管理、调度、并发控制、错误处理和持久化方面。理解操作系统原理能帮助你更快地设计出高效、健壮的 Agent Harness。反过来研究 Harness 也能让你更生动地体会到操作系统核心概念在另一个领域的应用价值。KV Cache 隔离在大模型中的含义一、什么是 KV Cache大模型在生成文本时是一个token 一个 token 地自回归生成的。比如生成“我爱北京”输入“我” → 计算所有层的 Key、Value → 生成“爱”输入“我爱” → 按理说需要重新计算“我”和“爱”的所有 Key/Value但这样计算量会随长度平方增长极慢。KV Cache 的核心思想把已经计算过的 token 的 Key 和 Value 缓存下来生成新 token 时只计算新 token 的 Key/Value然后与缓存的拼接再计算注意力。这样计算量从 O(n2)O(n2) 降到 O(n)O(n)。KV Cache 就是每一层中已经处理过的 token 的 Key 向量和 Value 向量在内存中的存储。二、什么是“隔离”在多用户、多请求或者复杂生成场景下不同请求的 KV Cache不能混在一起否则会发生数据串扰、逻辑错误甚至安全泄露。隔离就是确保每个独立上下文的 KV Cache 被单独存储和管理互不干扰。三、为什么需要 KV Cache 隔离1. 多用户服务SaaS 场景用户 A 和用户 B 同时向同一个模型发请求。如果不隔离A 的缓存会被 B 的覆盖或者互相污染。必须为每个请求/会话分配独立的缓存空间通常是显存中的一块区域。2. 并行生成Beam Search / 多候选Beam Search 中每个 beam 有自己独立的生成历史。每个 beam 的 KV Cache 需要分开存储不能混用。如果隔离不当不同 beam 的注意力会串位生成乱码。3. 长上下文 / 滚动窗口一些系统只保留最近若干 token 的 KV Cache滑动窗口。需要能够独立管理每个请求的窗口边界。4. 安全与隐私用户 A 的对话历史缓存不应被用户 B 读取。内存访问权限需要隔离。四、隔离的工程实现方式方式描述适用场景请求级独立缓存每个请求在显存中分配新的缓存区域如 PyTorch 的past_key_values列表。最常用如 Hugging Face Transformers 的use_cacheTruePagedAttentionvLLM将 KV Cache 分页存储不同请求的页物理隔离逻辑上独立。支持高并发、动态扩容。高吞吐量推理服务Logical KV Cache每个请求有一个逻辑 ID映射到物理存储不同 ID 的缓存不可互相访问。分布式推理内存池 引用计数同一 prompt 前缀可以共享缓存如多个用户问相同长文档但一旦分支则复制/隔离。前缀缓存优化五、不隔离会发生什么内容混乱用户 A 看到用户 B 的对话片段。生成错误Beam 之间相互影响导致重复或无效输出。性能下降缓存相互覆盖失去加速效果。安全漏洞敏感信息可能泄露给其他用户。六、面试中可能追问的延伸点QKV Cache 隔离与 Function Calling 或 MCP 有直接关系吗A没有直接关系但属于大模型推理系统工程的核心问题。MCP 关注的是工具调用协议而 KV Cache 隔离关注的是模型推理时的内存管理。两者都是构建高性能、可靠 Agent 的基础。QvLLM 如何实现高效的 KV Cache 隔离AvLLM 使用PagedAttention将缓存分成固定大小的块pages每个请求只持有指向这些块的逻辑映射。物理块可动态分配、重用但不同请求的块不会混用。隔离由页表保证。Q在 Agent 系统中KV Cache 隔离会影响工具调用的上下文吗A会的。如果 Agent 需要并行执行多个工具调用或同时维护多个对话分支必须为每个分支独立管理 KV Cache否则上下文会串扰。七、一句话总结KV Cache 隔离 在大模型推理中为确保不同请求、不同 beam 或不同用户的生成历史互不干扰对每一组 Key/Value 缓存进行独立存储和访问控制的技术。它是实现高并发、安全、稳定的大模型服务的基石之一。Hook 是一个非常常见且强大的编程概念中文常翻译为“钩子”。简单理解Hook 是一种在现有代码流程中“挂载”自定义代码的机制允许你在某个事件发生前、后或替换其行为而不用修改原有代码。一、生活中的比喻想象你有一台自动咖啡机它有一个固定的工作流程放豆子 → 磨粉 → 加水 → 萃取 → 出杯。你想在“加水”之后增加一步“加糖”。不拆机器改电路而是利用咖啡机预留的一个“定制接口”把你的“加糖”动作挂上去。咖啡机每次走到“加水”后自动执行你的“加糖”。这个“定制接口”就是钩子 (Hook)你写的“加糖”代码就是钩子函数。二、技术上的定义Hook是一种允许开发者在不修改核心源代码的情况下拦截、监听或扩展系统行为的技术手段。通常通过在特定位置如函数调用前后、事件触发时预留回调机制实现。核心特征非侵入不修改原有逻辑可扩展动态添加额外行为解耦核心系统与扩展功能分离三、常见使用场景1.框架/库的生命周期钩子很多框架提供钩子让用户在特定时机插入代码。python# React 类组件生命周期钩子 componentDidMount() { // 组件挂载后执行 } # Vue 的 mounted 钩子 mounted() { console.log(组件已挂载) }2.操作系统 / 底层编程Windows 消息钩子可以监听全局键盘/鼠标事件。LD_PRELOADLinux动态链接库劫持可“钩住” libc 函数如open、read并添加日志或过滤。3.Web 开发Git Hooks在commit、push等操作前后执行脚本例如自动格式化、运行测试。4.大模型 / Agent 开发中的回调钩子在调用 LLM 时可以设置钩子来钩子位置典型用途请求发送前注入统一系统提示词、记录开始时间、修改参数响应返回后自动记录 token 消耗、过滤敏感词、缓存结果工具调用前后记录工具输入输出、实现重试机制、审计日志LangChain 的回调示例pythonfrom langchain.callbacks.base import BaseCallbackHandler class MyCallback(BaseCallbackHandler): def on_llm_start(self, serialized, prompts, **kwargs): print(LLM 开始调用prompt:, prompts) def on_llm_end(self, response, **kwargs): print(LLM 调用结束响应:, response)这种钩子在不改动 LangChain 核心代码的情况下实现了可观测性日志、监控。四、如何实现一个简单的 Hook代码示例假设你有一个函数greet你想在不修改其源码的情况下在它执行前后加日志。python# 原始代码 def greet(name): return fHello, {name}! # 编写一个 Hook 函数 def hook_before(func): def wrapper(*args, **kwargs): print(f调用 {func.__name__} 前参数: {args}) result func(*args, **kwargs) print(f调用 {func.__name__} 后结果: {result}) return result return wrapper # 挂载 Hook装饰器方式 greet_with_hook hook_before(greet) greet_with_hook(World)输出text调用 greet 前参数: (World,) 调用 greet 后结果: Hello, World!更先进的实现方式包括事件监听器注册表、切面编程 (AOP)、中间件栈等。五、Hook 与其他相似概念的区别概念特点例子Hook预留的扩展点可插入自定义逻辑Git 钩子、React 生命周期Callback作为参数传入的函数由框架在特定时机调用setTimeout(callback, 1000)Middleware按顺序执行的钩子链通常用于 HTTP 请求处理Express 中间件Event Listener监听某个事件事件触发时执行DOM 的click监听器Hook 可以看作是更通用的概念回调、中间件等可以视为其特例。六、在 MCP / Agent 开发中的具体用处1.MCP 协议中可以在 MCP Client 发送请求前注入认证 tokenbefore_request钩子。在收到服务器响应后自动脱敏敏感数据after_response钩子。2.Agent 推理循环工具调用前校验参数合法性若不合法可重写或拒绝。工具调用后将结果格式化、截断、记录到记忆。3.上下文工程在构建 Prompt 前通过钩子从记忆或 RAG 动态加载上下文。在模型输出后通过钩子检测是否存在幻觉触发重新生成。七、面试中可能被追问的问题QHook 和 微调 的区别是什么AHook 是在不改变模型参数的前提下运行时插入逻辑微调是改变模型内部权重属于训练阶段。Hook 更轻量、灵活适合动态策略但无法改变模型的基础能力。QHook 会导致性能下降吗A会。每次执行钩子函数都有额外开销。但合理的钩子设计如异步执行、条件触发可以将影响降到最低通常远小于模型推理本身。Q常见的 Hook 滥用场景有哪些A过多层钩子嵌套导致调用栈混乱在钩子中执行耗时操作如同步网络请求拖慢核心流程钩子之间的顺序依赖难以调试。总结一句话Hook 是系统预留的“插头”让你可以在不拆改内部电路的情况下外挂自己的逻辑从而实现可观测、可扩展、可定制的系统行为。在 AI Agent、框架开发、运维自动化中无处不在。碎碎念后续会更新每天学习的八股和算法 题开始准备秋招的第25天。努力连续更新100天以后每天就按秋招项目【javaagent】科研必做项目算法八股锻炼身体来总结。总结项目的代码起码还是要逻辑理清楚看懂不然一点下问题都要搞半天1.算法要系统过一遍【灵神】14/27【早上】大概1.5h【要再整理一遍感觉还是没有理解透彻】2.秋招项目【java】开始实际看业务2.9/10无【昨天睡前半小时算是啊啊啊啊快看】【agent】还在学learncc大体看来一遍打算整理一下感觉看cc的性价比高很多2h3.科研要跑一下无【这个重要需要提高】4.检测项目也得总结文档5h【这个和其他并行的】5.训练项目无6.背八股看看睡前能不能半小时吧7.锻炼身体1h反思今天和ai聊天写代码写到生气啊啊啊啊时间不够用好多事情没干坚持坚持规划好时间少玩手机。第25天了感觉还是啥也没干每周周五下午就开始放纵自己啊啊啊啊啊啊啊啊啊学习呀学习。