技术知识点
本文介绍了在线C语言编译器的核心实现技术,重点解析了Monaco Editor的集成、智能缩进算法与浏览器持久化策略。读者可学习如何通过CDN直接引入编辑器、实现自适应布局与主题切换,掌握基于状态机的代码格式化方法,以及利用localStorage进行数据存储的技巧。
目录
技术知识点
在线 C 语言编译器
- Monaco Editor 集成
- 项目未使用打包工具,直接通过 CDN 提供的 AMD loader 引入 Monaco:
require.config({ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.33.0/min/vs' } });
require(['vs/editor/editor.main'], () => {
window.editor = monaco.editor.create(document.getElementById('editor'), {
value: DEFAULT_CODE,
language: 'c',
theme: localStorage.getItem('editorTheme') === 'light' ? 'vs-light' : 'vs-dark',
automaticLayout: true,
minimap: { enabled: false },
fontSize: 13
});
});
-
重点:使用
automaticLayout保障窗口缩放时不需要手动layout;使用window.editor.trigger绑定撤销/重做、注释切换等命令;通过 CSS 控制整体暗/明主题。 -
面试补充:相比
textarea,Monaco 自带语法折叠、多光标、命令面板,提升在线编译器体验;项目中测试过 Chrome/Safari/Edge 的中文输入法,未出现光标漂移。 -
智能缩进 / 格式化
- 采用自定义的
smartCIndent函数,对每行代码建立状态机,核心规则:- 维护块栈统计
{}层级; - 为
case/default、do...while设置专属分支避免误缩; - 遍历时跳过字符串、注释,保证内部花括号不干扰。
- 维护块栈统计
- 采用自定义的
function smartCIndent(code, tabSize = 4) {
const lines = code.split(/\r?\n/);
let indent = 0;
let inBlockComment = false, inString = false, stringChar = '';
return lines.map((line) => {
const trimmed = line.trim();
if (trimmed.startsWith('}')) indent = Math.max(0, indent - 1);
const formatted = ' '.repeat(tabSize * indent) + trimmed;
// ...扫描字符更新 indent / 状态...
if (trimmed.endsWith('{')) indent += 1;
return formatted;
}).join('\n');
}
-
面试补充:若老师问为何不用 AST,可以说明 AST 解析器体积大且需要完整 C 标准库;纯栈 + 状态机已经覆盖主流语法结构,成本更低。
-
浏览器持久化策略
- 使用多个 localStorage Key 分别记录代码、主题、测试用例:
localStorage.setItem('deepseek_saved_c', window.editor.getValue());
localStorage.setItem('editorTheme', isDark ? 'dark' : 'light');
localStorage.setItem('test_cases_v1', JSON.stringify(cases));
-
页面加载时先读取存档,fallback 到默认模板;在
beforeunload事件中追加自动保存,减少意外丢失。 -
面试补充:Safari 隐私模式会抛出
QuotaExceededError,因此所有写操作都包裹try...catch并在 UI 提醒用户。 -
Wandbox API 调用
- 运行按钮触发
tryWandboxCompile,内部实现:
- 运行按钮触发
const wandboxUrl = 'https://wandbox.org/api/compile.json';
const resp = await fetch(wandboxUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, compiler: 'gcc-head', stdin })
});
const json = await resp.json();
const out = (json.program_output || '') + (json.program_error ? `\n[stderr]\n${json.program_error}` : '');
stdoutEl.textContent = `${out}\n[exit code] ${json.status ?? 0}`;
-
若请求失败(超时 / 网络 / HTTP 错误),直接在输出面板显示真实错误信息并写入控制台,无本地 WASM 回退,用真实情况教育用户。
-
面试补充:通过
Promise.race+ 手动超时避免长时间等待;在测试用例批量运行里沿用同一 API,统一错误处理经验。 -
URL hash 分享
- 分享弹窗会把数据打包成 JSON 再 Base64:
function buildShareUrl(opts) {
const payload = {
code: window.editor.getValue(),
stdin: opts.stdin ? stdinEl.value : undefined,
cases: opts.cases ? cases : undefined,
name: currentFilename
};
const b64 = btoa(unescape(encodeURIComponent(JSON.stringify(payload))));
return `${location.origin}${location.pathname}#share=${b64}`;
}
-
页面加载检查
location.hash,若识别到#share=前缀就解码恢复编辑器、stdin 和测试用例。 -
面试补充:Base64 长度比原文短一截,但仍受浏览器 URL 长度限制;所以 UI 中提示“过长建议下载文件”。
-
Service Worker / PWA
- 手写
sw.js维护固定版本号,核心策略是 precache + cache-first:
- 手写
const CACHE_NAME = `c-compiler-${VERSION}`;
self.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET') return;
event.respondWith(
caches.match(event.request).then((cached) => cached || fetch(event.request).then((resp) => {
if (resp.status === 200) caches.open(CACHE_NAME).then((cache) => cache.put(event.request, resp.clone()));
return resp;
}).catch(() => caches.match(event.request)))
);
});
-
安装阶段缓存首页、Manifest、图片;激活时清理旧版本缓存,并在离线时返回友好文本提示。
-
面试补充:没有借助 Workbox,便于完全掌控缓存逻辑;通过
scripts/version.js的版本号手动触发缓存刷新。 -
GitHub Pages 发布
- 项目无构建链路,直接把静态资源放在主分支根目录,GitHub Pages 自动部署。若要更新,只需
git push主分支。 - 面试补充:若老师问自动化,可说明未来计划增加轻量工作流生成压缩版资源;目前重点是快速迭代前端功能。
- 项目无构建链路,直接把静态资源放在主分支根目录,GitHub Pages 自动部署。若要更新,只需
AI 医学辅助诊断系统
- 多 Agent 协同流程
SymptomAnalyzer将一次分析拆成搜索计划、证据检索、证据排序、临床规划、追问、中文翻译六个阶段,每个阶段都用 DeepSeek Agent + JSON Schema 约束输出结构。
result, raw = await self._run_agent(
agent_name="ClinicalPlannerAgent",
system_prompt="You design safe, evidence-aware triage plans for telemedicine workflows.",
instructions=instructions,
schema=schema,
context=context,
temperature=0.2,
)
-
每个 Agent 的原始输出与结构化结果都会记录在
agent_traces,方便面试时解释“如何保证可追溯性”。当 LLM 返回空值或证据不足时,代码回退到预置安全方案,确保 API 不会直接返回空响应。 -
PubMed/NICE 抓取与过滤
knowledge.py直接调用 NCBI E-utilities(esearch/esummary/efetch),并对返回文献做人口、物种和研究类型过滤:- 年龄段:根据患者年龄组构建包含/排除词,例如成人病例会自动排除
neonatal、pediatric。 - 物种:串联
HUMAN_ONLY_FILTER、VETERINARY_FILTER和关键词黑名单(canine、vet journal等)。 - 研究类型:优先保留指南、系统综述等高质量文献,检索不到时再回退到病例报告。
- 年龄段:根据患者年龄组构建包含/排除词,例如成人病例会自动排除
if item.category and "guideline" in item.category.lower():
category_bonus = 0.25
guideline_priority = True
-
同时抓取 NICE 官方指南 HTML,提取标题、摘要和 PDF 链接,与 PubMed 结果合并后统一排序。
-
证据打分与可视化
_rank_evidence使用词覆盖度、证据新鲜度、研究类型、实验室指标匹配等特征打分,并给指南附加优先级;最终分数写回EvidenceItem.score。_build_visualizations将文献按年份聚合生成ChartDatum列表,前端据此渲染“年份柱状图 + 来源占比圆图”。这部分是手写 CSS 图表,面试时可强调“无第三方图表库,也能快速实现轻量可视化”。
-
前端状态编排
- 主页面
page.tsx使用 React hooks 管理病例列表、追问草稿、分析结果、折叠状态,并通过localStorage在刷新后恢复:
- 主页面
const payload = { cases, caseCounter, activeCaseIndex, analyses: caseAnalyses, followupHistory, collapsedCases };
localStorage.setItem(CASE_STORAGE_KEY, JSON.stringify(payload));
-
一旦收到
analysis.requires_followup=true,界面会锁定结果区,展示追问列表并记录历史追问轮次;补充回答后再调用/api/v1/analyze生成最终方案。 -
进度追踪与下载接口
- 每次请求在后台注册进度令牌,前端通过
setInterval调用/api/v1/progress/:token显示实时进度条。 download_pubmed_asset根据 PMID 调用 PubMed API 获取摘要或全文 PDF URL,封装成二进制响应返回前端,支持一键下载文献。
- 每次请求在后台注册进度令牌,前端通过
-
部署与调试
- 项目提供
deploy.sh,根据 git diff 判断本次需要同步的模块,并通过 systemd 重启medagt-api(FastAPI)和medagt-web(Next.js)。 - 日志分散在前端浏览器控制台与后端
logging输出,调试 Agent 行为时可以直接查看agent_traces,定位是检索、排序还是规划阶段出现偏差。 说明:这种结构化格式方便后端做校验,例如检查severity是否在mild/moderate/severe范围内。老师如果问到多语言输入(中文症状如何转换成英文),可以说会结合自建词典或调用医学术语标准(如 SNOMED CT)将自然语言对应到统一编码。
- 项目提供
-
大模型安全策略
- 提示模板中要求“必须引用检索证据编号”,并在返回后校验。如果模型输出未引用证据或涉及药物剂量等高风险内容,规则引擎会重写为“建议线下就医”。
if not has_reference(advice):
advice = "当前证据不足,请建议患者尽快线下就医。"
说明:这个检查函数会解析模型输出中是否包含像 [1] 或 (PMID:xxxxxx) 之类的引文标注。老师若问到如何防止模型推荐违规药品,可以说会在规则引擎里增加黑名单词汇,发现敏感词就自动改写成“需要医生评估”。
- 人工校准闭环
- 审核界面展示模型建议、引用文献、置信度。医生点击“通过/修改/驳回”后,将最终意见写入审核日志表,为后续模型调优提供数据。
INSERT INTO review_logs(case_id, reviewer, final_decision, confidence)
VALUES (:case_id, :reviewer, :decision, :confidence);
说明:人工校准记录可以追踪每条建议的责任人,符合医疗合规要求。老师如果问“人手不够怎么办”,可以说会根据置信度分类,只有低置信度结果才必须人工审核,高置信度结果标注“未经人工复核”让医生快速浏览。
- API 设计范例
- 前端提交病例数据到
/api/cases, 后端返回 triage 信息和建议。接口遵循 REST 风格,返回的 JSON 包含决策解释和证据链。
- 前端提交病例数据到
POST /api/cases
{
"case_id": "CASE-001",
"symptoms": [ ... ]
}
HTTP/1.1 200 OK
{
"triage": "Level 3",
"recommendation": "建议 24 小时内就诊",
"references": [ { "id": "PMID:123456" } ]
}
说明:接口返回的 Level 3 对应院内急诊分级标准,老师可能会问“这个等级从哪里来”,可以回答参考了国际常用的 ESI (Emergency Severity Index),并请医学顾问调整到本土标准。如果需要图像输入,也可以在 symptoms 中增加附件 URL,后端再连接影像识别服务。
- 部署与安全要点
- 后端部署在阿里云 ECS,预计接入 HTTPS 证书。为演示模式提供“本地存储”选项,把病例数据仅保留在浏览器,降低隐私风险。
sudo certbot --nginx -d medagt.example.com
说明:医疗项目对安全要求高,老师若问还有哪些安全措施,可以补充:部署时会打开阿里云 WAF,并将敏感日志透过 KMS 加密后储存。对于演示账号,会限制只能使用匿名病例,并在登入页加上告警提示。
- 向量数据库维护
- Milvus 定期导入最新文献摘要,使用定时任务重建索引。对低置信度检索结果进行人工标注并回填,提高后续检索准确率。
python scripts/update_embeddings.py --source pubmed.csv --collection med_papers
说明:这个脚本会重新计算向量并写入 Milvus。老师如果问到“如何确保同步过程不影响线上服务”,可以回答会使用灰度方案:先写入新集合,待检查完毕后再切换索引别名。若源数据格式变动,会先在测试环境演练。
- DeepSeek LLM 集成
- 通过 HTTP API 发送用户请求,设置温度较低(如 0.2)保证回答稳定可控。预留本地部署接口,以便未来在院内环境运行。
response = requests.post(
'https://api.deepseek.com/v1/chat/completions',
headers={'Authorization': f'Bearer {DEEPSEEK_KEY}'},
json={
'model': 'deepseek-medical',
'messages': [
{ 'role': 'system', 'content': '你是医学问诊助手,必须引用证据。' },
{ 'role': 'user', 'content': prompt }
],
'temperature': 0.2
}
)
说明:温度设为 0.2 可以让回答更稳定。若老师问到“如果 API 失效怎么办”,可以说会设定重试机制与后备模型(例如本地部署版),并在监控面板透过 Prometheus + Grafana 跟踪请求成功率。一旦失败率升高,会自动切换到备援模式。