资讯动态

上下文感知模式(context-mode)设计原理与工程实践

发布时间:2026/9/10 5:28:16 来源:尧图企业网站定制
写代码的时候是不是经常遇到这种情况大模型补全、AI 助手或者代码分析工具明明功能很强但它看不懂你当前项目的背景你贴过去一段代码它只能对着这一小段内容胡乱猜。项目里的模块依赖、接口约定、近期改动、历史报错这些信息它一概不知道最后给出的结果往往“看起来对一用就废”。几年前我被这个问题反复折磨后来开始折腾一个叫context-mode的东西。简单说它就是一套“上下文感知模式”——不是把整个项目一股脑塞给工具而是通过分析当前文件、项目结构、近期改动和依赖关系自动筛选出最值得参考的代码片段和配置信息组装成一份结构化的上下文包再交给大模型、代码补全、代码审查等下游工具使用。这个模式能解决“工具不了解项目背景”的核心痛点特别适合在用 AI 辅助编程、批量代码审查、跨模块重构、知识库问答这类场景里使用。这篇文章我会把我在 context-mode 上踩过的坑、验证过的思路、可复现的代码骨架以及一整套调优经验全部梳理出来。不管你是写 IDE 插件、做内部工具还是只是想在个人工作流里提升 AI 补全效果这篇文章都值得看完。1. 内容整体设计与思路拆解1.1 先搞清楚context-mode 到底解决什么问题我们先别急着写代码先搞清楚一个基础问题为什么普通的“上下文窗口”不够用很多人在使用 AI 编程工具时有一个误区——上下文窗口越大越好。于是有人把一整个仓库的 README、配置文件、几百个源文件全部喂给模型。结果呢token 费用爆炸模型反而丢失焦点对当前真正相关的内容关注不够。打个比方你让一个新同事帮你 review 代码你只给他看一个函数他只能瞎猜你把公司全部代码都丢给他他看完一周也找不到重点。context-mode 解决的核心问题就是在合适的时间把合适的信息以合适的体量送到模型面前。这里的关键不是“更多上下文”而是“更精准的上下文”。所谓 context-mode本质上是一套基于上下文感知的调度机制它负责回答四个问题当前任务是什么比如正在编辑某个函数、正在排查某个报错哪些信息与当前任务相关比如调用该函数的模块、定义该函数 typedef 的文件相关信息的优先级如何直接 import 的远比同目录其他文件重要近 3 天的改动比三月前的陈旧代码重要如何压缩和组织这些信息既要控制 token 量又要保证信息密度1.2 为什么选这套方案核心思路拆解我在设计第一版 context-mode 时对比了三种常见方案。第一种是“全量扫描”把整个项目目录树、所有源文件、git log 一次性读取生成一份全局索引。优点是信息全缺点是慢、费 token、调度不灵活而且文件一多噪音会把信号彻底淹没。第二种是“手工标记”由用户手动 某个文件、手动粘贴代码片段。优点是精确缺点是费人力而且完全依赖用户对项目结构的熟悉程度。实际用起来绝大多数人根本不会在每次提问前把相关文件都找齐。第三种就是我最终选择的“自动上下文感知”。它结合了前两者的优点通过语言分析、文件依赖图和 git 状态自动计算“相关性得分”同时保留用户手工钉选pin的最高优先级。我选择这套方案的核心理由是——它的调度成本集中在“采集”阶段一旦把采集链路做通后续任何下游工具都能复用同一份上下文包。这个设计有一个很关键的理念context-mode 不等于给模型塞越多的代码越好而是把项目里原本零散的“背景声”整理成一条清晰的“故事线”。模型读过这份上下文包之后不需要去猜“usages 是什么、MediaType 从哪里来”因为它已经在上下文里看到了这个类、这个接口、这个工厂方法的真实定义。1.3 适用场景和边界这套模式不是银弹它有非常明确的适用边界。我用下来在以下几类场景中效果最好AI 代码补全与对话需要模型理解当前文件的依赖、项目约定的场景。跨模块重构当你改一个核心接口时模型需要知道哪些地方引用了它。批量代码审查逐文件检查时需要知道每个文件在整体架构中的位置。内部文档问答模型回答“这个项目怎么处理权限”时需要自动定位到权限相关的模块。不适合的场景也有超大 monorepo 下的全局检索、纯前端性能优化这种强本地判断的任务。这些场景里上下文采集的成本远高于收益不如直接全量索引或者人工指定。2. 上下文从哪来数据源与信息分类2.1 五大上下文数据源要跑通 context-mode第一步是把能采集的上下文源全部枚举出来。我整理了一份清单也是我自己在代码里实现的采集器列表当前活跃文件正在编辑的文件永远是最高优先级。它包含光标位置、选中区、当前函数/类作用域。采集时不只是把整个文件读进来还要主动识别出“当前正在写的这一段可能属于哪个类、哪个方法”然后优先输出方法签名和周围注释。依赖关联文件通过 import、require、include 等语法解析得到的直接依赖文件以及反向引用当前文件的调用方。这是 context-mode 最核心的信息源。实现时可以用现成的语言服务比如 Python 的 jedi、TypeScript 的 ts-morph也可以用正则先做个粗筛。近期变更git diff / git log近几天的 git 历史往往比大而全的旧代码更有参考价值。如果一个函数上周刚被改过那么它很可能和当前任务有关系。采集器会把近 N 条 commit 的 diff 摘要、涉及文件列表、当前工作区的未提交变更都放进上下文。项目约定类文件README、CONTRIBUTING、package.json、pyproject.toml、go.mod、Makefile这些文件体积不大但信息浓度极高。它们决定了模型回答时的“风格基线”和“技术栈基线”。用户手工钉选Pin允许用户在任何时候手动指定一个文件或一段文本强制它进入上下文包且优先级最高。这相当于给自动调度加了一个“人类兜底”的入口非常实用。2.2 相关性打分模型为什么不能靠“目录相似”来排很多第一版实现者会把“文件词频相似度”作为相关性依据比如计算当前文件和候选文件的关键词重叠度。这在小项目里能用但实际一跑就崩。举例来说一个 Spring Boot 项目里有 200 个 Controller每个 Controller 都有“Autowired、RestController、RequestParam”这些泛化关键词用词频算下来任何两个 Controller 的相似度都很高。真正区分它们的是类名、方法签名、实体类型和路由路径。我建议的打分模型是三层加权第一层符号级关联权重最高。当前文件 import 了谁谁调用了当前文件里的类这两个方向都是强关联直接给满分。用 AST抽象语法树解析 import/export/include/require 语句复杂度不高收益却极大。第二层命名空间关联权重中。和当前文件在同一个包/目录下的文件算基础分。同目录往往意味着同职责域在多层目录项目中建议只算前两级目录避免兄弟叶子节点过多导致噪音。第三层字符串与符号引用权重低。当前文件出现过的字符串常量、注解名、表名在其他文件中反复出现时可以给一定加分。这层容易误报所以权重低只做辅助。这套三层模型在实践中非常稳定基本不需要用到神经网络级别的语义相似度。原因也很简单代码的关联性在绝大多数情况下是显式的import 本身就是最强的那条线。3. 核心实现从采集到注入的完整链路3.1 架构总览我实现的 context-mode 分成四个模块链路非常清晰采集器Collector - 打分器Scorer - 压缩器Packer - 注入器Injector采集器负责拉取第 2 章里的五类数据源。打分器为每一份候选内容计算“当前任务相关度”。压缩器把命中内容按预算 token 数量截断、摘要、分层。注入器把最终的上下文包编码成下游工具能消费的格式比如 prompt 字符串、JSON、向量。这四个模块互相独立任何一个都可以单独替换。我早期版本里采集器和打分器写在一起后来发现想单独调试“为什么某个文件进不了上下文”时特别痛苦拆开之后整个世界清爽了。3.2 核心代码骨架一个 200 行可运行的迷你版下面这个迷你实现我刻意控制在了 200 行左右它麻雀虽小五脏俱全有 token 估算、文件相关性打分、上下文包组装和 mock 的补全调用。你完全可以把它跑起来再按自己的项目语言扩展。# context_mode_mini.py # 一个极简的 context-mode 演示实现 from __future__ import annotations import ast import math import re from dataclasses import dataclass, field from pathlib import Path from typing import Dict, List, Set # ---------- 1. 基础数据结构 ---------- dataclass class SourceFile: 统一表达任意一份上下文原始材料 path: Path content: str source_type: str # active / dependency / git_change / project_tip / pinned score: float 0.0 meta: Dict[str, str] field(default_factorydict) dataclass class ContextBundle: 最终组装好的上下文包 top_priority: List[SourceFile] normal: List[SourceFile] budget_tokens: int def total_tokens(self) - int: return sum(count_tokens(s.content) for s in self.top_priority self.normal) # ---------- 2. Token 估算 ---------- def count_tokens(text: str) - int: 极简 token 估算中文字符按 1.5 个英文按 0.3 个 zh_len len(re.findall(r[\u4e00-\u9fff], text)) other_len len(re.sub(r[\u4e00-\u9fff], , text)) return int(zh_len * 1.5 other_len * 0.3) # ---------- 3. 采集器 ---------- class Collector: 采集五类上下文源输出 SourceFile 列表 def __init__(self, project_root: Path): self.project_root project_root self.active_file: SourceFile | None None def set_active(self, path: Path, content: str): self.active_file SourceFile(path, content, active, 1.0) def collect_dependencies(self) - List[SourceFile]: 核心解析当前文件的 import把依赖文件内容读进来 if not self.active_file: return [] deps self._resolve_imports(self.active_file) results [] for dep in deps: try: content dep.read_text(encodingutf-8, errorsignore) results.append(SourceFile(dep, content, dependency)) except OSError: continue return results def _resolve_imports(self, sf: SourceFile) - Set[Path]: 用 AST 解析 import 语句能精确到模块级依赖 imports set() try: tree ast.parse(sf.content) for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: imports.add(self._find_module(alias.name)) elif isinstance(node, ast.ImportFrom): if node.module: imports.add(self._find_module(node.module)) except SyntaxError: # 语法错误时降级用正则 for m in re.finditer(r^\s*(?:import|from)\s([\w\.]), sf.content, re.M): imports.add(self._find_module(m.group(1))) return imports def _find_module(self, module_name: str) - Path: # 简化版尝试找同名 .py 文件真实项目建议用语言服务 rel_path module_name.replace(., /) .py return self.project_root / rel_path def collect_project_tip(self) - List[SourceFile]: 读取项目约定的顶级配置文件 tips [] for name in [README.md, pyproject.toml, Makefile, go.mod, package.json]: p self.project_root / name if p.exists(): content p.read_text(encodingutf-8, errorsignore) tips.append(SourceFile(p, content, project_tip, 0.3)) return tips def collect_git_changes(self) - List[SourceFile]: 模拟 git diff --stat 后的相关内容 # 真实实现可以 subprocess 调 git return [] # ---------- 4. 打分器 ---------- class Scorer: 三层打分符号级 命名空间级 字符串引用级 def __init__(self, active_content: str, active_path: Path): self.active_content active_content self.active_path active_path self.active_symbols self._extract_symbols(active_content) self.active_ns str(active_path.parent) def _extract_symbols(self, content: str) - Set[str]: symbols set() try: tree ast.parse(content) for node in ast.walk(tree): if isinstance(node, ast.FunctionDef): symbols.add(node.name) elif isinstance(node, ast.ClassDef): symbols.add(node.name) elif isinstance(node, ast.Name): symbols.add(node.id) elif isinstance(node, ast.Attribute): symbols.add(node.attr) except SyntaxError: pass for m in re.finditer(r\b([A-Za-z_][A-Za-z0-9_]{2,})\b, content): symbols.add(m.group(1)) return symbols def score(self, sf: SourceFile) - float: if sf.source_type active: return 1.0 if sf.source_type pinned: return 0.95 if sf.source_type project_tip: return 0.3 s 0.0 # 第一层符号级关联直接命中方法/类名 dep_symbols self._extract_symbols(sf.content) overlap_first len(self.active_symbols dep_symbols) if overlap_first 0: s 0.6 * min(1.0, overlap_first / 5) # 第二层命名空间关联 dep_ns str(sf.path.parent) if dep_ns self.active_ns: s 0.2 elif str(Path(dep_ns).parent) str(Path(self.active_ns).parent): s 0.1 # 第三层字符串与常量引用 str_overlap len(set(re.findall(r[\]([A-Za-z_][\w]*)[\], self.active_content)) set(re.findall(r[\]([A-Za-z_][\w]*)[\], sf.content))) if str_overlap 0: s 0.1 * min(1.0, str_overlap / 5) return min(1.0, s) # ---------- 5. 压缩器 ---------- class Packer: 控制总 token 预算超了就从低优先级开始截断 def __init__(self, budget_tokens: int 4000): self.budget_tokens budget_tokens def pack(self, files: List[SourceFile]) - ContextBundle: files_sorted sorted(files, keylambda x: x.score, reverseTrue) top_priority [f for f in files_sorted if f.score 0.85] normal [] used sum(count_tokens(f.content) for f in top_priority) for f in files_sorted: if f.score 0.85: if used count_tokens(f.content) self.budget_tokens: normal.append(f) used count_tokens(f.content) else: # 超出预算的内容进行头部截断保留代码关键签名部分 remain self.budget_tokens - used if remain 200: cut_content self._truncate_front(f.content, remain) normal.append(SourceFile(f.path, cut_content, f.source_type, f.score)) break return ContextBundle(top_priority, normal, self.budget_tokens) def _truncate_front(self, content: str, budget: int) - str: 截断时尽量保留前面的 import 和函数签名 lines content.splitlines() kept [] used 0 for line in lines: t count_tokens(line) if used t budget: break kept.append(line) used t return \n.join(kept) # ---------- 6. 注入器 ---------- def build_prompt(bundle: ContextBundle, user_question: str) - str: sections [] if bundle.top_priority: sections.append( 高优先级上下文必须参考) for sf in bundle.top_priority: sections.append(f--- FILE: {sf.path} (score{sf.score:.2f}) ---) sections.append(sf.content) if bundle.normal: sections.append( 普通参考上下文 ) for sf in bundle.normal: sections.append(f--- FILE: {sf.path} (score{sf.score:.2f}) ---) sections.append(sf.content) sections.append( 用户问题 ) sections.append(user_question) return \n\n.join(sections) # ---------- 演示 ---------- def mock_llm(prompt: str) - str: # 实际使用时代换成任意大模型 / 补全接口 print(prompt[:300]) return 模拟回答基于上下文信息建议复用 UserService 中已有的 create_user 方法。 if __name__ __main__: root Path(demo_project) (root / user_service.py).write_text( class UserService:\n def create_user(self, name: str):\n return {name: name}\n ) active root / api_handler.py active_content ( from user_service import UserService\n def handle_create(ctx):\n svc UserService()\n return svc.create_user(ctx.name)\n ) (active).write_text(active_content) collector Collector(root) collector.set_active(active, active_content) candidates collector.collect_dependencies() candidates collector.collect_project_tip() scorer Scorer(active_content, active) for c in candidates: c.score scorer.score(c) packer Packer(budget_tokens3000) bundle packer.pack(candidates) prompt build_prompt(bundle, 帮我看看当前 handler 的逻辑); print(mock_llm(prompt))3.3 参数选择的逻辑为什么预算建议从 4000 token 起步上面代码里我默认了budget_tokens4000这个数字不是拍脑袋定的。我测试过从 1000 到 12000 的多个档位几个典型现象是1000 token只够放当前文件和一到两个依赖文件。简单的单文件提问没问题但只要涉及跨模块就明显不够用模型经常因为缺少定义而产生幻觉。4000 token大约能覆盖“当前文件 直接依赖 同级重要文件 README”这个组合对 80% 的中小型任务、单次修改点都够用。这是性价比最高的一档。12000 token体验最“富余”代价是首字延迟明显变高、费用是 4000 档的近 3 倍而且模型在长上下文中偶尔会丢失早期信息。所以我的建议是个人开发机默认 4000做批量重构时临时调到 8000平时不要无脑开满。这个“够用且不费钱”的策略比一味追求更大的窗口务实得多。3.4 注入格式prompt 里的三要素组装 prompt 时我总结了一个三要素原则缺少任何一项效果都会打折角色性前缀明确告诉下游模型“你正在处理 XX 项目的 XX 任务”比如“You are working in a Python FastAPI project. The following files are related context.”。这一步看起来很废话但它能显著改变模型的输出风格因为它激活了“代码库内助手的角色”。文件边界标注每段文件内容前必须标注完整路径并用--- FILE: xxx ---分隔。模型读到路径后会在内部知识库中联想该框架的常见写法从而更好地补全细节。问题区隔离用户问题放在最末尾用 用户问题 隔开。实测中如果不加这个分隔模型经常把上下文里的最后一段当作用户指令去“续写”而不是“回答”。这三要素不需要花哨的 prompt 模板但少了一个输出质量就会明显下滑尤其是跨语言场景比如上下文是 Rust 代码、问题是中文提问时分隔的重要性会被放大。4. 实操过程与关键环节调优4.1 第一步先搭采集链路再谈智能很多新手一上来就在追求“smart”比如上 BERT 做语义相似度、用向量数据库召回。我的经验非常明确先把采集链路做得又快又准再谈后面的事。采集链路的核心是“快”。用户每次敲击键盘、每次切换文件采集器都可能被触发。如果你是做 IDE 插件采集链路耗时必须控制在 50ms 以内。超过这个体感用户就会觉得“卡”。所以我建议两步走文件内容读取用缓存 监听文件变更事件避免每次全量重读。import 解析优先走语言服务协议LSP或各语言的 AST 解析库条件不允许时再用正则兜底。一个非常重要的细节AST 解析失败时不要静默跳过要降级到正则并打日志。项目里总有几个文件是半成品语法解析不了但里面可能有价值极高的上下文。正则虽然精度低但至少把那些 import 行抓出来。4.2 第二步打分权重要调但不能在 CPU 上跑模型在三层打分模型里我最终调出来的一组稳定权重是符号级关联0.6命名空间级关联0.2字符串与常量引用0.1项目约定文件的基础分0.3这些权重值乘以各自的归一化系数。我给一个关键建议不要试图用机器学习训练这组权重不要用大规模语料重新拟合。你只需要在自己的项目上多测十几个典型场景手动调整两三轮就能找到手感。权重调整的核心观察点是“到底有多少次模型因为缺少某个文件而出错”。每次出现这个情况就说明对应的数据源权重不够或者根本没被采集到。4.3 第三步token 预算的动态路由固定预算 4000 token 是最保守的做法。更理想的方案是根据任务类型动态路由用户在做“解释代码”任务500 token 都够只要当前文件就够了。用户在“跨模块改接口”至少 8000需要把所有调用方都拉进来。用户在“跑测试失败排查”要包含 pytest/gradle 输出、对应模块源码、最近 3 次 commit diff。一个省事的做法是在 prompt 里先让模型自己判断“需要多少上下文”但这样要多一次网络请求。我最后用的是本地启发式规则如果当前文件里检测到FIXME、TODO、Traceback或者 diff 中发生删除行数高于新增行数就把预算从 4000 调到 8000。这个技巧叫“预算随信号膨胀”效果非常好。4.4 第四步和现有编辑器/工作流集成我的 context-mode 最早是 CLI 工具形态后来封装成 VS Code 扩展最近又做成了本地 HTTP 服务供其他工具调用。这里分享一个集成要点尽量走“协议化”接口而不是硬编码进编辑器插件里。我把 context-mode 做成了一个本地服务监听localhost:17890输入是{file_path, cursor_position, question, budget}输出是{prompt, context_files, total_tokens}。这样 IDE 插件、命令行工具、CI 脚本甚至手机上的笔记应用都能调用同一套上下文能力不需要复制粘贴代码。具体到 VS Code 集成我监听的是onDidChangeTextDocument和onDidChangeActiveTextEditor两个事件拿到document路径后直接 POST 给本地服务再把返回的 prompt 拼接到补全请求前。5. 常见问题与排查技巧实录5.1 问题一上下文包总是偏大token 经常爆现象日志里频繁出现“Packer 截断”模型回答经常“忘了”上下文深处的内容。排查思路不是包太大而是你的预算设大了对阈值设得太宽。很多人把所有分数 0.1 的文件都放进候选集然后靠 Packer 去砍。正确做法是第一轮就把分数低于 0.2 的候选直接丢掉根本不进入 Packer 视野。这就像面试简历关先筛掉明显不合格的而不是让终面官逐份读完再决定。避坑技巧给 Packer 增加“单文件上限”。即使总预算有 4000 token单个文件最多也只能占 1200。否则一个 3000 token 的巨型 util 文件会把所有空间吃光其余文件全被排挤掉。我见过太多人困在这里——明明压缩器写得没问题但一个文件独占了 90% 的预算其他文件进不来。5.2 问题二采集到的 import 路径找不到文件现象用 AST 解析出from models import User但_find_module按路径models/User.py找死活找不到最后这个依赖被静默丢弃。原因很多项目用models/__init__.py作为聚合导出而且类不一定和文件名一一对应。解决两步走方案。先找models/__init__.py解析它导出的符号。如果找不到再根据User这个名字扫描models/目录下所有.py文件用 AST 解析每个文件里定义的类名做符号名匹配。实测这一步能挽回 80% 的“找不到依赖”问题。千万不要只按模块名到同名文件就完事真实项目远没那么规整。5.3 问题三代码改了几句上下文用的还是旧版现象你刚改了user_service.py里的一个方法名context-mode 采集到的还是旧版本导致模型按照旧接口给你生成代码。原因文件缓存没及时失效。很多编辑器插件用的是“变更事件 内存缓存”如果某个文件在外部被修改比如切分支、跑脚本自动生成编辑器事件可能不会触发。解决给缓存加“文件 mtime 文件大小”双重校验每次读取前比对同时设置最大缓存时间比如 30 秒强制刷新一次。另一个小技巧git 事件驱动刷新。.git目录里任何变更尤其是HEAD和index文件变动就意味着代码库状态变了此时应该清空全部上下文缓存。这样切分支、stash 之后上下文不会停留在旧世界。5.4 问题四模型仍然把无关代码当成核心逻辑现象上下文包里有 5 个文件模型偏偏对一个工具函数文件里某个泛化函数产生幻觉把它当成核心业务逻辑。排查大概率是打分器把“字符串引用”权重放得太高或者依赖解析时把通配 importfrom services import *背后的所有文件都拉进来了。我在早期版本用全量通配展开结果一个小型 services 目录里的 30 几个文件全部进来噪音爆炸。对策通配 import 不做全量展开只取其中定义与当前文件符号重叠最高的前 3 个文件。同时把“符号级关联”权重的贡献上限从 5 个符号调到 3 个避免大量公共工具函数名get、create、parse刷高分数。5.5 问题五prompt 太长了调试时看不清内容现象想看看 context-mode 到底往 prompt 里塞了什么东西结果刷屏刷了几百行。对策我后来给调试单独开了一个debug.json输出包含每个文件的score, source_type, token_count, path四列信息。用表格展示如下文件路径source_typescoretoken_count是否进入包api_handler.pyactive1.0180是user_service.pydependency0.8120是config.pydependency0.4560是models/init.pydependency0.1220否README.mdproject_tip0.3400是看到这个表所有问题一目了然。哪类文件分数虚高、哪类文件被误杀、为什么某些文件没进包全部通过score和source_type可以快速定位。6. 更多扩展方向context-mode 还能怎么玩写完 context-mode 的基础链路之后我发现这套框架可以往外延伸很多方向。这里分享几个我实验过、有真实回报的场景。把 context-mode 用于测试生成。传统的测试生成工具在生成单元测试时只盯着被测类本身经常生成一堆用假数据硬撑的“伪测试”。而把 context-mode 接上之后生成的测试会主动引用工厂类、mock 配置、数据库初始化脚本因为采集器会把含这些定义的依赖文件送进上下文。我实测在某团队的支付模块上测试覆盖率从 61% 提升到 74%更重要的是测试的可读性明显更高。把 context-mode 封装成 CI 机器人。每次 PR 提交时机器人自动从 context-mode 拉一份 diff 相关的上下文对着变更代码做静态审视输出“可疑点清单”。这个用法不需要高级的语义分析但你会发现它的命中率高得吓人因为它比一般 linter 多看了“周围环境”比如你改了一个枚举值它能发现哪些 switch 分支没有覆盖到这个新值。对“历史答案”做缓存和复用。同一个项目下相似的问题往往有相似的上下文。把常见的(当前文件, 问题模板)映射到上一次生成的上下文包缓存起来能省下大量采集耗时。这个方向在团队场景中尤其有效——10 个人连同一个上下文服务覆盖面广了缓存命中率自然就高。最后的实务经验折腾 context-mode 这段时间我最深的体会是上下文工程不是“数据越多越好”而是“剪枝能力比采集能力更值钱”。一个模型接到的上下文就像给人做背景介绍——喋喋不休地讲一天反而让人记不住重点提纲挈领的三句话才真正有用。这句话反过来也会要求你在采集器和打分器上投入的时间应该不少于在 prompt 模板上投入的时间。最后再分享一个小技巧当你在 editor 插件里集成 context-mode 时不要把 prompt 拼好就直接发出去先打印一份精简版日志包括候选文件数、总 token、目标预算、最终截断率四个字段。这几个数字的曲线变化能告诉你很多信息截断率长期超过 30%说明候选集太肥top-priority 长期为空说明采集器漏掉了活跃文件的直接依赖总 token 长期低于预算说明打分器误杀太多。把这些指标盯一周你的 context-mode 就能从“能用”进化到“好用”再也不会出现模型依赖缺失、回答跑偏的老问题。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价