个人博客8:解释层接入 LLM、JavaScript 文件内上下文补全、补全结果展示统一
报告日期: 2026-06-01
一、变更总览
| 序号 | 能力 | 修改前 | 修改后 |
|---|---|---|---|
| 1 | 「网上资源」解释 | 曾接入 Tavily/Serper 搜索 API(后移除) | 仅 LLM(OpenAI 兼容,默认 DeepSeek) |
| 2 | JS 选中分析 + 无漏洞 | 无文件内补全提示 | 与 Python 对齐:短选区前置提示 + 无漏洞后置提示 |
| 3 | 跨文件补全展示 | 独立 Webview + 另一套输出格式 | 与主分析共用 Problems / 高亮 / 解释面板 / 输出日志 |
二、后端:解释层改为 LLM(DeepSeek 兼容)
2.1 设计目标
- 用户选择 web_only 或 materials_and_web 时,用大模型生成中文「漏洞说明 / 如何解决」,不再依赖外网搜索 API。
- 未配置 API Key 或调用失败时,回退内置 WEB_KNOWLEDGE(OWASP/CWE 风格摘要),保证教学场景可用。
- 接口契约不变:仍为 POST /enrich-explanations,explanation_source 枚举保持 materials_only | materials_and_web | web_only。
2.2 新增 llm_service.py
职责: 读取环境变量 → 调用 Chat Completions → 解析 JSON → 返回 web_block 结构(与旧逻辑字段一致:title / explanation / fix / refs)。
默认对接 DeepSeek:
_DEFAULT_BASE_URL = "https://api.deepseek.com"
_DEFAULT_MODEL = "deepseek-chat"
环境变量(优先级从高到低):
| 变量 | 说明 |
|---|---|
| CODEGUARD_LLM_API_KEY | 通用密钥 |
| DEEPSEEK_API_KEY / OPENAI_API_KEY | 简写别名 |
| CODEGUARD_LLM_BASE_URL / DEEPSEEK_BASE_URL | API 根地址 |
| CODEGUARD_LLM_MODEL / DEEPSEEK_MODEL | 模型名 |
核心调用逻辑:
def generate_knowledge_for_risk(
risk: RiskItem,
*,
source_code: str = "",
material_excerpt: str = "",
timeout_sec: float = _DEFAULT_TIMEOUT_SEC,
) -> dict[str, str | list[str]] | None:
"""调用 OpenAI 兼容 Chat Completions API 生成教学向说明;失败返回 None。"""
api_key = _api_key()
if not api_key:
return None
user_prompt = _build_user_prompt(risk, source_code=source_code, material_excerpt=material_excerpt)
try:
content = _chat_completion(
api_key=api_key,
model=_model(),
base_url=_base_url(),
messages=[
{"role": "system", "content": _SYSTEM_PROMPT},
{"role": "user", "content": user_prompt},
],
timeout_sec=timeout_sec,
)
parsed = parse_knowledge_json(content)
...
Prompt 约定:
- System:要求输出单行 JSON:{“explanation”,“fix”,“refs”},中文、可提 OWASP/CWE,不编造 URL。
- User:拼装漏洞类型、行号、reason、attack_path、引擎 fix 建议、带行号的代码片段(选区上下各约 6 行)、可选「上传资料摘录」。
parse_knowledge_json: 支持纯 JSON、markdown 代码块包裹、或文本中提取首个 {…},提高模型格式漂移时的容错。
2.3 修改 explanation_service.py
删除: 对 **web_search_service **的依赖(该文件已移除)。
主流程 enrich_explanations:
mode = req.explanation_source
use_materials = mode in ("materials_only", "materials_and_web")
use_llm = mode in ("web_only", "materials_and_web")
if mode == "materials_only" and not req.reference_materials:
use_llm = True # 无资料时降级,避免空解释
...
llm_cache: dict[str, dict[str, str | list[str]] | None] = {}
for risk in req.risks:
...
if use_llm:
cache_key = _llm_cache_key(
risk, material_excerpt if mode == "materials_and_web" else ""
)
if cache_key not in llm_cache:
llm_cache[cache_key] = _knowledge_block_for(
risk,
source_code=req.source_code,
material_excerpt=material_excerpt if mode == "materials_and_web" else "",
)
web_block = llm_cache[cache_key]
缓存键: 类型:起始行:结束行:reason前60字:资料摘录前80字,避免同请求内重复调 LLM。
_knowledge_block_for:
- is_llm_configured()
→generate_knowledge_for_risk(…) - 失败 → WEB_KNOWLEDGE[risk.type] 或 _DEFAULT_WEB
文案调整(面向用户):
- web_only:大模型生成的安全说明
- materials_and_web:大模型补充的公开安全实践说明
2.4 测试
| 文件 | 内容 |
|---|---|
| backend/tests/test_llm_service.py | JSON 解析(纯文本 / markdown fence) |
| backend/tests/test_explanation_service.py | 离线 OWASP 兜底;mock LLM 时断言 enrich 结果 |
2.5 DeepSeek 产品建议(运维)
| 产品 | 场景 |
|---|---|
| deepseek-chat | 默认,漏洞说明 + 修复步骤,性价比高 |
| deepseek-reasoner | 复杂攻击链,可选,更慢更贵 |
| 硅基流动等兼容中转 | 改 BASE_URL + MODEL 即可 |
配置说明已写入 插件启动方式.txt 第 15–31 行。
三、插件:JavaScript 文件内上下文补全
3.1 问题
原先仅 language === "python" 时,选中过短片段会提示「补全上下文」;JavaScript 选中分析无漏洞时,也不会询问是否在同一文件内扩窗重分析。
3.2 语言判定扩展
/** 支持选中分析时的文件内上下文逐步扩窗(与后端已接入的语言一致) */
export function supportsInFileContextExpansion(language: string): boolean {
return language === "python" || language === "javascript";
}
normalizeLanguage 已将 javascript / typescript 归为 javascript 送审后端。
3.3 两条触发路径(extension.ts)
路径 A — 分析前(选区过短,与 Python 相同):
if (
mode === "selection" &&
supportsInFileContextExpansion(language) &&
expansionSettings.enabled &&
document.lineCount > 0
) {
...
if (isWindowTooShort(...)) {
const choice = await vscode.window.showWarningMessage(
`...是否自动扩大选区并补全上下文后再分析?`,
confirmExpand, cancel
);
if (choice !== confirmExpand) { return; }
useExpandingContext = true;
}
}
路径 B — 分析后(仅 JavaScript,首次分析无漏洞):
if (
mode === "selection" &&
language === "javascript" &&
expansionSettings.enabled &&
!useExpandingContext &&
!responseHasRisks(responseJson) &&
document.lineCount > 0
) {
const choice = await vscode.window.showInformationMessage(
`当前选中范围内未发现漏洞。...是否在文件内自动扩大分析窗口后再分析?`,
{ modal: true },
confirmExpand, keep
);
if (choice === confirmExpand) {
useExpandingContext = true;
responseJson = await analyzeSelectionWithExpandingContext({...});
}
}
扩窗算法(共用 analyzeSelectionWithExpandingContext):
- 以选区行为 [winStart, winEnd],每轮 expandWindow 向上下各扩 stepLines 行;
- 每轮
POST /analyze,mode: "selection",带range; - 停止条件:risks_found | 覆盖全文 | 窗口不再增长 | 达到 maxIterations;
- 结束时写入 _codeguard_context_expansion、_codeguard_completion_kind: “in_file”。
配置项:codeGuardTutor.expandContextForShortSelections 等(package.json 描述已改为「Python / JavaScript」)。
四、插件:文件内 / 跨文件补全 — 展示统一
4.1 问题
| 路径 | 修改前 |
|---|---|
| 主分析 / 文件内补全 | ResultPresenter + ExplanationPanel + 统一 logResponse |
| 跨文件补全 | 多个 codeguardCrossFileRisk Webview + 手写 appendLine 分段 |
用户体验割裂:跨文件无 Problems 高亮、解释面板不联动。
4.2 新增 analysisPresentation.ts
类型:
export type CompletionKind = "standard" | "in_file" | "cross_file";
跨文件合并响应 buildCrossFileMergedResponse:
- 遍历 riskHits[],将各文件 risks 合并为单一 risks[];
- 强制 file_path 为命中文件路径,id 缺省为 risk-cross-{n};
- 附加元数据:
_codeguard_completion_kind: "cross_file",
_codeguard_cross_file_meta: {
files_checked, files_with_risks, risk_count
}
统一出口 presentUnifiedAnalysisOutcome:
logAnalysisResponse— 输出通道([补全类型]、[跨文件]统计、风险条目含文件名)presentAnalysisResult→ResultPresenter(Diagnostics、行高亮)ExplanationPanel(右侧 Webview)- 中文 Toast(摘要、风险等级、解释来源、条数提示)
主分析与跨文件均调用:
// 主流程(extension.ts)
presentUnifiedAnalysisOutcome({
completionKind: useExpandingContext ? "in_file" : "standard",
...
});
// 跨文件(runCrossFileAnalysis)
const merged = buildCrossFileMergedResponse(riskHits, args.relatedFiles.length);
presentUnifiedAnalysisOutcome({
completionKind: "cross_file",
responseJson: merged,
...
});
删除: showCrossFileRiskWebviewPanels、buildCrossFileRiskWebviewHtml、flattenCrossFileRisks 的独立展示分支。
4.3 解释面板 explanationPanel.ts 统一
- 标题:CodeGuard 导师 · 漏洞说明(中文)
- 风险列表:显示 文件名 · Lx–Ly、严重程度中文
- 详情:文件 / 漏洞说明 / 攻击路径 / 如何解决(优先 how_to_fix)/ 最小改动 / 推荐做法 / 修改前后代码
与输出面板 formatRiskLineForOutput / formatRiskDetailForOutput 字段对齐。
4.4 端到端展示流程(示意)
五、插件:解释来源文案(LLM)
先解释一下为什么没有连接到搜索引擎的API:
首先搜索引擎功能比较局限,返回结果后需要用户自己查看网页内容。
其次功能中包含同时使用用户自身上传文档与线上资源,搜索引擎没有合并功能,比较局限
再次我们使用的平台没有提供主流搜索引擎API
最后综合考虑后,我认为将功能变更为大模型回答更符合当下的发展趋势,也更适合我们这个项目。
userMaterials.ts 模态框按钮更新:
| 原文案(概念) | 现文案 |
|---|---|
| 资料 + 网上资源 | ② 参考我的资料与大模型 |
| 仅网上资源 | ③ 仅参考大模型(LLM) |
extension.ts 中 explanationSourceLabelZh 同步为「上传资料与大模型」「仅大模型」。
六、涉及文件清单
6.1 后端
| 文件 | 操作 |
|---|---|
backend/core/context/llm_service.py | 新增 |
backend/core/context/explanation_service.py | 修改(LLM + 缓存 + 文案) |
backend/core/context/web_search_service.py | 删除 |
backend/tests/test_llm_service.py | 新增 |
backend/tests/test_explanation_service.py | 修改(mock LLM) |
backend/tests/test_web_search_service.py | 删除 |
6.2 插件
| 文件 | 操作 |
|---|---|
vscode-extension/src/analysisPresentation.ts | 新增 |
vscode-extension/src/extension.ts | 修改(JS 补全、统一展示、传 resultPresenter) |
vscode-extension/src/contextExpansion.ts | 修改(supportsInFileContextExpansion) |
vscode-extension/src/explanationPanel.ts | 修改(中文 UI、文件路径、如何解决) |
vscode-extension/src/userMaterials.ts | 修改(LLM 文案) |
vscode-extension/package.json | 配置项描述更新 |
6.3 文档与启动说明
| 文件 | 操作 |
|---|---|
插件启动方式.txt | DeepSeek / LLM 环境变量说明 |
docs/项目开发报告-2026-05-31.md | 本报告 |
七、配置与验证
7.1 后端 LLM
在这里加了配置大模型的方法,具体模型和密钥使用自己账号上的就行
注:大家跑测试之前记得找我要APIKey这些参数
cd backend
#在这里加了配置大模型的方法,具体模型和密钥使用自己账号上的就行,
#依旧只需要配置一次
$env:CODEGUARD_LLM_API_KEY = "sk-...(APIkey)"
$env:CODEGUARD_LLM_BASE_URL = "https://api...(URL)"
$env:CODEGUARD_LLM_MODEL = "...(模型名称)"
python -m uvicorn app:app --host 127.0.0.1 --port 8000 --reload
7.2 插件
cd vscode-extension
npm run compile
# F5 启动扩展开发窗口
7.3 测试用例
在test文件夹里面我更新了相关被测文件,选择相应语言的文件夹的子文件夹打开就可以进行测试。
code-guard-tutor\tests> tree
└─cases
├─javascript
│ └─javascript
└─python
└─vulnerability_samples
八、已知限制与后续
| 项目 | 说明 |
|---|---|
| LLM 非实时联网 | 说明来自模型知识 + 引擎上下文,非爬取网页 |
| 跨文件语言 | 仍仅 Python import 推断关联 .py |
| JS 后置扩窗 | 仅 javascript;typescript 归并后同等对待 |
| 多文件高亮 | 需打开对应文件编辑器才显示行装饰;Diagnostics 已按路径分组 |
| 接口字段名 | 仍为 web_only / web_references_used,与历史插件兼容 |
后续可能会扩展:
- 将 risk_level / summary 在解释面板改为中文映射(与输出通道一致)
- 跨文件支持 JavaScript import / require
- LLM 流式输出或按文件批量合并 prompt 以降低调用次数
九、小结
本次完成三件事:(1) C 层解释由搜索 API 转为可配置的 LLM(默认 DeepSeek),保留离线兜底;(2) JavaScript 与 Python 在选中分析上对齐文件内上下文补全交互;(3) 通过 analysisPresentation.ts 将标准分析、文件内补全、跨文件补全的结果收束到同一套输出日志、Problems、解释面板与 Toast,降低学习与演示成本。
到这里这个项目的主要开发工作就结束了,后面是测试优化与材料整理。
更多推荐




所有评论(0)