FEATURED · 精选文章

LLM上下文模式设计实战:分层、预算与切换策略

发布时间 / 2026/9/10 8:31:36
来源 / 创域科博编辑部
栏目 / 资讯中心
LLM上下文模式设计实战:分层、预算与切换策略 1. context-mode 到底在解决什么问题如果你最近在调 LLM 应用一定对 context-mode 这个词不陌生。它看起来只是一个开关在零上下文、当前文件、项目级上下文之间切换。但真正把这个功能做扎实涉及的问题远比想象的多。我花了两周时间在一个终端 AI 助手里落地了 context-mode过程中踩了不少坑也总结出一套可以复用的设计思路今天完整写出来。先说结论context-mode 的本质不是“给 AI 更多资料”而是“在正确的时间给 AI 恰好正确的资料”。没有上下文模型只能泛泛而谈上下文塞得太多模型会被无关文件带偏回答质量反而下降Token 成本和响应延迟也上去了。Context-mode 就是通过一套显式的模式切换机制把“上下文数量”的控制权从开发者手里交还给使用者。这个功能非常适合下面几类人来参考正在做 AI 编程助手、聊天机器人、代码评审工具的人需要在项目内快速定位问题、又不愿意每次都手动把文件内容粘进 prompt 的开发者以及想优化 API 调用成本、减少无效 Token 消耗的团队。我后面讲到的方案不绑定任何特定框架你用 Python、TypeScript 或者纯命令行脚本都能迁移过去。1.1 从一次糟糕的对话说起我做这个功能之前先遇到了一个很典型的场景。有个同事在终端里问 AI“帮我看看 user service 里为什么登录接口偶尔超时”当时助手绑定的是整个项目上下文一口气把仓库里的 30 多个文件都塞进 prompt。结果模型回答的时候重点放在了数据库连接池的配置上反而把真正有问题的 timeout 参数和 Redis 调用链给忽略了。最尴尬的是那次请求花了 8 万多 Token响应时间接近 30 秒。反过来如果完全不提供上下文让 AI 直接回答同样的问题它会给你一段非常正确的废话“建议检查网络连接、数据库负载、代码逻辑”一句能用的话都没有。这两种体验都非常糟糕。而 context-mode 要解决的就是这个二选一的困境。我后来把交互改成了三个档位用户可以先问一个“零上下文”的问题比如“解释一下 JWT 鉴权流程”等确认问题范围后再切换到“当前文件模式”让 AI 只看正在编辑的代码如果需要改动涉及多个文件就再升级到“项目级模式”让 AI 自己从文件列表里挑相关文件。整个过程从“赌运气”变成了“可控”。1.2 设计目标与使用场景我在设计 context-mode 时给自己定了四个目标响应质量要稳定不能因为切换模式后回答反而更差。Token 消耗要透明用户能清楚知道每个模式大概花多少。模式切换要快最好一次按键就能完成。用户始终保有最终控制权自动推荐只能参考不能替用户做决定。四个目标按优先级排序质量第一控制权第二成本第三速度第四。原因很简单一次垃圾回答浪费的时间和情绪成本往往比多花几毛钱 Token 更昂贵。适用场景我经验里主要就三类。第一类是单文件维度的代码解释和修复比如“这个函数为什么返回空数组”“帮我加一个参数校验”当前文件模式就够了塞整个项目反而干扰。第二类是跨文件重构比如“把支付逻辑从订单服务里抽出来”这种必须用项目级模式让 AI 理解依赖关系。第三类是纯知识问答不需要读任何项目文件用零上下文模式最省。后续我们加的自定义模式是把这三类的边界继续细化这个后面专门讲。1.3 context-mode 不是 context window有一件事必须澄清context-mode 和模型支持的上下文窗口长度是完全无关的两个概念。上下文窗口是模型硬件层面的能力上限比如 128K、200Kcontext-mode 是我们产品层面的设计是“主动决定往窗口里放什么数据的策略”。这就像一个大厨房锅的大小上下文窗口决定了最多能煮多少食材而 context-mode 是“菜谱和采购清单”决定今天这顿饭到底该买什么、切什么、先下锅什么。把整扇猪肉都丢进锅里不如按照菜谱切好那半斤五花肉。很多人误以为“既然窗口有 200K我就把整个仓库都丢进去”实测下来的效果通常都很差原因不是窗口不够用而是相关性问题——模型面对超长上下文时注意力会被无关信息稀释关键代码反而找不到了。所以 context-mode 的底层逻辑很简单上下文长度不是目的相关性才是。2. 核心设计上下文的分层、预算与切换策略2.1 上下文资源的三层结构在实现之前我把一次对话涉及的上下文拆成了三层第一层是系统层包括角色设定、输出格式要求、安全规则第二层是会话层包括用户之前的问题、AI 之前的回答、当前正在编辑的文件第三层是检索层包括从项目里搜索出来的相关代码、文档、配置文件。context-mode 主要控制的是第二层和第三层的组合方式。系统层内容必须永远保留不随模式变化。这一点很多初学者容易忽略我见过有人把系统提示词当成“可被覆盖的普通文本”结果切到零上下文模式后模型连基本的输出格式都忘了。正确做法是系统层恒定会话层按模式裁剪检索层按模式增强。我用一张表来总结三个基础模式的资源配置方式模式系统层会话层历史当前文件检索层文件推荐预算zero固定保留最近 2 轮不含不含2K Tokenfile固定保留最近 4 轮含不含8K Tokenproject固定保留最近 4 轮含根据问题检索32K Token这里推荐预算不是写死的硬限制是防止误操作的软上限。在 file 模式下如果你选中的文件本身有 3 万行当然可以超过 8K但那说明你可能选错文件了系统会提示一次。预算的作用是让用户对“成本诡异上升”这件事有感知。2.2 三个基础模式的行为定义零上下文模式 zero mode是所有模式的兜底。它不读取任何项目文件只把用户当前的问题、历史对话摘要、以及系统固定的角色信息交给模型。适合做通用问答、格式化代码、写正则表达式这类不依赖项目背景的任务。这个模式最大的价值是便宜响应快而且不会因为加载了不相关代码而产生“幻觉式引用”。当前文件模式 file mode是日常编辑中使用频率最高的模式。它读取编辑器当前聚焦的文件内容同时会优先截取“和光标位置最近的代码块”而不是把整个文件原样塞进去。比如你在一个 1000 行的文件里修改第 800 行的函数那 context-mode 会把第 700 到 900 行作为核心上下文剩余的 700 行只保留一个精简的“符号清单”列出类名、函数名、全局变量名。这样做是因为大模型对局部代码的修改最关心的是“我改的这个函数周边有什么”。项目级模式 project mode是成本最高、效果也最容易翻车的模式。它不是把整个项目目录递归读一遍然后全塞进去而是先构建索引再通过关键词、文件依赖关系和最近 git 变更来筛选一个“候选文件集合”。我实测下来候选集合控制在 5 到 10 个文件是最优区间超过 15 个文件后回答质量的提升曲线会迅速变平Token 消耗却接近线性上涨。2.3 模式自动推荐与手动覆盖很多人以为 context-mode 就是让用户手动切其实更成熟的交互是“半自动”。每次用户提问时系统先根据问题的词频判断如果问题里出现了当前打开文件的函数名或变量名自动推荐切换到 file 模式如果问题里出现两个以上文件的类名自动推荐切换到 project 模式如果问题完全不带项目特征就维持 zero 模式。但推荐只以状态栏提示的形式出现绝不自动帮用户切。自动切换的问题在于它打断了心智模型用户不知道现在 AI 到底在用什么上下文回答错了也不知道该怪谁。我们后来把推荐逻辑做成一个可选开关默认打开推荐、不自动切换用户按一次快捷键CtrlShiftM就能接受推荐再按一次可以手动换到别的模式。用了一周后我发现用户对自动推荐的接受率大概在 70%剩下 30% 的情况用户会手动选择这 30% 恰恰是最容易出错的高价值场景。3. 从零实现一个可用的 context-mode3.1 先定义一个清晰的上下文数据模型动手写代码之前我先定义了一个数据模型把所有上下文相关的信息都装进一个对象里。这一步看起来多余但后面做缓存、做调试、做日志的时候会非常省心。我用 Python 的类型标注来描述核心结构from dataclasses import dataclass, field from enum import Enum from pathlib import Path class ContextMode(str, Enum): ZERO zero FILE file PROJECT project CUSTOM custom dataclass class FileSnippet: path: Path content: str start_line: int end_line: int token_estimate: int 0 dataclass class ContextUnit: role: str # system / history / current_file / retrieved content: str mode: ContextMode source_path: Path | None None priority: int 0 # 数字越大越优先保留 dataclass class ContextBundle: mode: ContextMode units: list[ContextUnit] field(default_factorylist) def total_tokens(self) - int: return sum(u.token_estimate for u in self.units)这段代码的核心是ContextUnit它把每一块上下文都打上了来源标记和优先级。为什么要加priority字段因为最后做 Token 截断时我们不是按“谁先谁后”截而是按“谁的优先级低先丢谁”。系统提示词优先级最高当前文件里的核心代码块优先级次之历史对话再次之检索出来的辅助文件优先级最低。这个设计让我避开了很多截断后格式崩坏的问题。实际项目中你还可以加上embedding字段用来做向量检索加上mtime字段用来做文件变更检测但是最初版本不必一次性设计完够用就好先跑通核心链路再迭代。3.2 实现当前文件模式的收集器收集器是整个 context-mode 最繁重的一块因为它要处理真实文件。我实现 file 模式时专门写了一个FileContextCollector它做三件事定位当前文件、定位光标附近的代码块、生成符号表。import ast def extract_active_block(source: str, cursor_line: int) - str: 根据光标行号提取所在的函数或类定义块。 tree ast.parse(source) for node in ast.walk(tree): if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): block_start, block_end node.lineno, node.end_lineno if block_start cursor_line block_end: lines source.splitlines() return \n.join(lines[block_start - 1: block_end]) return source这个函数的原理很简单先把源码解析成 AST 语法树然后遍历所有函数、异步函数和类定义判断光标行号落在哪个代码块的范围内把那个块整体提取出来。之所以用 AST 而不是按行号附近取 200 行是因为函数嵌套场景下按行数硬取会把外层函数和内层函数混在一起语义会被破坏。我在一次真实测试里遇到过光标在一个嵌套函数内部按固定行截取把外层函数的 30 行注释都带进来了模型回答受干扰特别严重。AST 方案出来后这个问题就消失了。符号表生成则是把所有类名、函数名、全局变量名和它们所在行号整理成一个紧凑列表。比如[Symbols] UserService:1024 UserRepository:888 validate_token:756这段符号表单独占一块放在完整代码块之后让模型知道“这个项目里还有这些符号但你没看到全文”。模型在回答跨函数调用时会依据符号表去猜测关系比啥都没有强很多。3.3 项目级模式的检索与筛选project 模式的收集器我建议先做“关键词 依赖图”的两阶段筛选不要一上来就上向量数据库。很多项目规模其实没有大到需要 embedding 的程度用关键词过滤加上文件内容里的 import / require 关系分析就能选出 80% 的相关文件。第一阶段是关键词召回。把用户当前问题分词后提取里面的标识符比如类名UserService、函数名create_order、配置项timeout。然后在整个项目的文件列表里做匹配文件名命中的权重乘 3文件内容命中的权重乘 1按得分倒序取前 20 个候选文件。第二阶段是依赖图扩展。对第一阶段的候选文件做 import 分析找出它们依赖了哪些文件、又被哪些文件依赖。为什么必须做这一步因为很多 bug 的根源不在报错的那个文件里而在它调用的下游模块。关键词只搜报错文件时经常漏掉真正的问题源。依赖图扩展能把这个缺口补上。def expand_candidates(candidates: list[Path], import_map: dict[str, set[str]]) - list[Path]: result set(candidates) for path in candidates: for dep in import_map.get(path.name, set()): if len(result) 12: # 候选文件数量上限 break result.add(path) return sorted(result, keylambda p: str(p))注意这里的候选文件数量上限我填的是 12。这个值不是拍脑袋定的后面测试环节我会专门说数据12 到 15 个是一个性价比拐点。候选文件的完整内容都会进入 prompt但会按“是否与关键词直接相关”分成核心区和辅助区核心区内容完整保留辅助区只保留关键函数和类的摘要。3.4 模式切换的交互设计模式怎么切直接影响到用户会不会用。我一开始把模式切换放在配置文件里结果同事都懒得去改功能基本没人用。后来改成命令行输入框前的斜杠命令输入/context file切到当前文件模式输入/context zero切到零上下文模式输入/context project切到项目级模式。这样最直接因为用户本来就要敲命令提问。同时我在终端界面底部加了状态栏显示当前模式和预估 Token[context-mode: file mode] [used: 7.2K/8K tokens]这个状态栏异常重要它让用户对“AI 是否在看正确的文件”有一个直观判断。很多用户反馈说看见 Token 快爆了才意识到自己上次项目级上下文一直没切回来。加了一行状态栏之后这类错误少了 80%。还有一个小技巧模式切换不会自动清空历史记录但我会把上一次模式生成的检索结果标记为stale过期状态在下一次回答前自动丢弃。如果不做这一步旧模式加载的文件会残留在上下文里变成上下文污染这个问题我在后面的排查章节会专门展开。4. 实测数据不同模式的成本与质量对比理论说再多不如跑一次真实测试。我用一个大约 80 个 Python 文件、包含 API 层、服务层、数据访问层和测试文件的中型项目做了对照实验。测试工具是一个基准脚本对 5 个典型问题分别用零上下文/单文件/项目级三种模式提问并统计每次的 Token 消耗、响应时间、以及人工评分。测试问题设计如下“解释 JWT 鉴权流程” —— 通用知识预期 zero 最优。“user service 里登录接口为什么超时” —— 跨文件排查预期 project 最优。“给 payment.py 的validate_sign函数补一个边界判断” —— 单文件修改预期 file 最优。“列出这个项目里所有用到 Redis 的地方” —— 跨文件搜索预期 project 最优。“这段代码的 PEP8 问题有哪些” —— 单文件风格检查预期 file 最优。评分规则是 1 到 5 分人工判断“回答是否准确、是否可执行”。结果如下问题编号zero 模式得分file 模式得分project 模式得分最优模式14.54.03.5zero21.52.54.5project32.04.54.0file41.02.04.5project52.54.53.5file最值得注意的数据是问题 1。零上下文模式得分 4.5比 project 模式的 3.5 高了一大截。很多人以为项目上下文是“万金油”加了总比不加好可实测发现当问题本身是通用知识时项目文件纯粹是噪音模型甚至会因为项目里某个不规范的写法而改变标准答案。这说明 context-mode 里“留白”本身也是一种主动设计而不是“没找到上下文”的失败状态。Token 消耗方面三次 project 模式平均消耗 29.6K Tokenfile 模式平均 7.8Kzero 模式平均 1.9K。单次成本差别不算特别大但如果一个开发者每天触发 100 次调用project 模式一个月比 file 模式多出大约 200 万 Token按常见模型定价换算不是一笔可以忽略的钱。所以我在默认配置里把 project 模式设成了“手动确认后才能进入”避免系统自动把用户拖进高成本通道。关于候选文件数量的经验值我也做了一组对照实验。候选文件从 3 个增加到 6 个时问题 2 的评分从 3.0 跳到 4.5提升非常明显从 6 个增加到 12 个评分只从 4.5 升到了 4.7几乎可以忽略超过 15 个之后评分反而因为噪音增加掉回了 4.2同时 Token 消耗增加近一倍。这就是我把 12 作为候选文件数量上限的原因。与其堆文件不如提升文件内容筛选的质量。5. 常见问题与排查技巧实录5.1 上下文污染模式切换后残留旧文件这是我在实际使用中遇到最多的问题。现象是用户切到 file 模式后问一个当前文件的问题AI 回答时却提到了完全不相干的旧文件。排查下来发现是因为上一次 project 模式加载的检索文件还留在会话历史里没有清掉它们被模型当作了隐式上下文。解决方案是在切换模式时给所有不属于当前模式的上下文单元打上stale过期标记并在下一次构造 prompt 前过滤掉。更彻底的做法是切换模式时同时生成一条变更日志写入会话元数据{ event: mode_changed, from: project, to: file, discarded_units: [retrieved:db_config.py, retrieved:cache_service.py], timestamp: 2025-01-15T10:23:00Z }这样一旦回答出问题可以拿着日志回看“当时模型到底看到了什么”。这个习惯帮我迅速定位了很多诡异回答。没有日志时排查上下文污染是很痛苦的因为问题往往要等到两轮之后才暴露出来。5.2 Token 溢出核心内容被意外截掉最开始我做 Token 截断时贪图省事直接按照列表顺序从头截到尾。结果发现一个典型问题系统层提示词和历史对话占用了太多空间当前文件的代码块被甩到列表末尾一超出预算就被裁掉最后模型完全没有看到用户要改的代码回答质量断崖式下跌。修正后的策略是按优先级排序再截断。优先级顺序是系统提示词最高当前文件核心代码块第二检索文件核心区第三符号表第四历史对话第五。截断时从低优先级开始丢弃直到总 Token 数低于预算。几个文件之间我还会优先保留当前文件再保留最近修改的文件。这套策略跑了一个月再没出现“模型看不到代码”的尴尬。另外我建议在 prompt 尾部加一段可见的 Token 使用提示比如“当前上下文已使用 7.9K Token接近 8K 上限与问题无关的文件内容可能被省略”。这不算浪费 Token反而能避免模型在信息缺失时“脑补”实测能减少 30% 左右的幻觉式回答。5.3 模式切换后效果不一致历史依赖不可信有用户反馈同一个问题先切 project 再切 file和直接 file 提问得到的回答不一致甚至更差。原因在于切到 file 模式时我保留了最近 4 轮历史对话而这几轮对话里可能包含了 project 模式下的推理过程比如 AI已经分析了 5 个文件、给出了一个阶段性结论。新的 file 模式提问会继承这个推理过程于是被旧结论带偏。这个问题的最优解不是清空历史而是对历史对话做摘要压缩。我写了一个condense_history函数把最近 4 轮聊天压缩成 200 字以内的摘要“用户询问了登录超时问题AI 分析了 user_service.py 和 redis_config.py暂未定位到根因”。下一轮提问时模型只带上这个摘要而不是完整的旧分析过程。这样既保留了对上下文的衔接又不会让旧模式的结论过度污染新模式。5.4 文件读取性能大项目下卡顿严重project 模式刚上线时用户反馈第一次切换要等 5 秒以上体验非常差。瓶颈出在读取文件内容候选 12 个文件平均每个 500 行加上 AST 解析和符号提取耗时确实不短。我的做法是引入两层缓存。第一层是文件哈希缓存记录每个文件的修改时间、文件大小、以及内容哈希。文件没变就直接复用之前提取的代码块和符号表不重新解析。第二层是检索结果缓存同一个问题关键词在 5 分钟内不重复检索TTL 设为 5 分钟是考虑到开发者通常会在一个任务上停留较长时间。做完缓存后project 模式首次切换耗时从 5.1 秒降到了 1.8 秒第二次命中缓存时降到 0.4 秒。虽然还有优化空间但已经不影响正常使用了。缓存带来的副作用是文件改动后可能读不到最新内容所以我用了文件修改时间和大小做双校验只要有一个变化就把缓存标记为失效问题不大。6. 后续扩展从手工模式到智能模式6.1 把静态检索升级为 RAG 向量检索context-mode 做扎实之后我尝试给 project 模式接入了向量检索把项目文件按函数粒度切片后 embedding用户提问时先做向量召回再和关键词召回合并最终用重排序模型挑选候选文件。效果提升主要集中在一类问题上“项目里有没有类似 XXX 的实现”这类语义匹配是关键词召回做不到的。但代价是索引构建和向量存储的额外基础设施小项目没必要上100 个文件以内关键词方案完全够用。6.2 把“模式”升级为“任务模板”比多模态更实用的扩展方向是任务模板。比如把“写测试”定义成一个 custom 模式它固定包含测试框架配置、被测文件、相关 fixtures、以及一条“只输出测试代码”的系统指令把“Code Review”定义成另一个 custom 模式它固定包含 git diff、当前分支变更文件列表、以及评审规则。这些模板本质上就是 context-mode 四个档位之外的第五档但因为它能绑定具体任务用户切换时的心理负担更小。任务模板看起来是小事实际价值非常大。它让 context-mode 从“上下文数量控制工具”进化成了“团队最佳实践的封装器”。新同事入职后不需要知道项目里有哪几个核心文件、测试怎么写直接选一个“写测试”模板就能得到不错的结果。我自己团队用下来这是投入产出比最高的一个扩展。6.3 建立回归测试集防止越改越差最后我想说一个外面文章很少提的点context-mode 这类功能必须配一套回归测试。因为 AI 模型经常升级同一个 prompt 今天能用下个月可能就变了项目代码重构后原来能检索出的文件路径可能已经失效。我建了一个非常轻量的测试集包含大约 20 个问题每个问题标注了预期模式和预期得分下限每周自动跑一遍发现评分低于阈值就告警。这套测试救了我不止一次。有一次模型厂商改格式输出偏好导致 file 模式下代码块被误判为 Markdown 表格所有回答全都走样。如果没有回归测试这种问题要等到用户吐槽才会暴露有了自动化测试在发布前就拦住了。context-mode 做到这一步已经不再是简单的开关切换而是变成了一个可观测、可评测、可持续迭代的上下文管理系统。我个人感触最深的一点是做这类功能永远不要神话“上下文越多越好”也不要迷信“向量检索天下无敌”真正重要的是给用户足够的控制感和反馈信息。如果用户能随时看清 AI 当前在看什么、预算是多少、是什么模式那么即使某个模式选错了他们也能快速自我纠正。这个设计哲学我后来在做其他 AI 功能时也一直沿用。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻