
備考軟考這幾年我最煩兩件事一是知識點散落在各種文檔、云筆記、截圖里換臺電腦就找不到二是下午論文摘要卡 300 字、正文卡 20002500 字Word 里數(shù)格子既不準又折磨人。寫完還得自己對照評分標準摳「切題、實踐、結構、表達」。所以我做了軟考通一款本地優(yōu)先的軟考備考筆記桌面端。它不只是記筆記論文練習時實時按軟考習慣統(tǒng)計字數(shù)寫完還可以交給 AI 評分、潤色并給出修改意見。技術棧很克制桌面端Vue 3 TypeScript ElectronAI 后端Spring Boot AgentScope 2.xJava數(shù)據(jù)筆記全部落本地 Markdown不登錄、不上傳數(shù)據(jù)無縫遷移需要源碼的同學可以私信我。客戶端界面一、它到底能干什么能力說明分學科架構、高項、軟設、網工、庫工等常見科目一鍵切換本地倉庫自己選一個文件夾當筆記根目錄應用按科目自動建目錄知識點筆記富文本工具欄標題 / 字號 / 對齊 / 下劃線 / 列表保存為 Markdown截圖粘貼粘貼或拖入圖片存到assets/Markdown 里引用相對路徑論文練習題目、摘要、正文分塊編輯摘要 ≤300 字正文建議 20002500 字字數(shù)統(tǒng)計漢字 1 字、英文/數(shù)字整詞 1 字、標點 1 字貼近考場習慣AI 潤色可單獨潤色摘要、正文或一次潤色全部AI 評分按 75 分制打分≥45 合格給維度分、優(yōu)點和改進建議拖動排序筆記列表原生拖拽順序寫入.order.json不啟動 AI 后端時記筆記、寫論文、打包安裝包照樣能用。AI 是可選插件不是強綁定。二、為什么做成桌面端而不是又一個 Web 筆記備考資料往往帶項目經歷、架構圖、論文底稿我不希望默認就上云。所以產品原則是本地優(yōu)先選一個文件夾筆記就是普通.mdVS Code、Typora、Obsidian 都能打開。無登錄打開就能寫配置只記「根目錄 上次科目」。AI 解耦桌面端只負責編輯和展示評分 / 潤色走本機 HTTP默認127.0.0.1:9001。目錄約定很簡單你的目錄/ architect/ # 系統(tǒng)架構設計師 notes/ # 知識點 .md notes/assets/ # 截圖 notes/.order.json essays/ # 論文練習 .md essays/assets/ essays/.order.json pm/ # 高項 notes/ essays/ ...論文落盤時按固定結構組裝方便以后用別的編輯器繼續(xù)改--- title: 論大模型智能運維技術及應用 type: essay updatedAt: 2026-08-15T12:00:00.000Z --- # 論大模型智能運維技術及應用 ## 題目描述 首行題目名 下方題目背景與寫作要求 ## 摘要 …… ## 正文 ……三、整體架構┌─────────────────────────────────────────────┐ │ 軟考通 桌面端 │ │ Vue 3 Pinia Vue Router渲染進程 │ │ ? preload contextBridge │ │ Electron 主進程fs / dialog / IPC │ │ 讀寫本地 Markdown / 圖片 │ └──────────────────────┬──────────────────────┘ │ HTTP JSON ▼ ┌─────────────────────────────────────────────┐ │ Spring Boot AgentScope 2.x │ │ POST /api/ai/essay/polish 論文潤色 │ │ POST /api/ai/essay/score 論文評分 │ │ ReActAgent無記憶、無工具一次一評 │ └─────────────────────────────────────────────┘進程劃分層技術職責渲染進程Vue 3、Pinia、marked / turndownUI、字數(shù)、論文結構、調 AI預加載contextBridge只暴露受控window.api不把 Node 直接塞給頁面主進程Electron Nodefs選目錄、讀寫筆記、存圖、排序AI 服務Java AgentScope 2.x按軟考論文標準潤色、打分工程用electron-vite把主進程、preload、渲染進程打在一起開發(fā)時熱更新發(fā)布用electron-builder打 Mac DMG / Windows 安裝包。四、Electron 安全橋頁面不直接碰文件系統(tǒng)渲染進程不能require(fs)。所有本地 IO 都走 IPC。preload 里只暴露白名單方法import{contextBridge,ipcRenderer}fromelectronconstapi{getConfig:()ipcRenderer.invoke(config:get),saveConfig:(config)ipcRenderer.invoke(config:save,config),selectDirectory:()ipcRenderer.invoke(dialog:selectDirectory),ensureSubject:(rootPath,subjectId)ipcRenderer.invoke(notes:ensureSubject,rootPath,subjectId),listNotes:(rootPath,subjectId,kind)ipcRenderer.invoke(notes:list,rootPath,subjectId,kind),readNote:(rootPath,subjectId,kind,fileName)ipcRenderer.invoke(notes:read,rootPath,subjectId,kind,fileName),writeNote:(payload)ipcRenderer.invoke(notes:write,payload),saveNotesOrder:(payload)ipcRenderer.invoke(notes:saveOrder,payload),saveImage:(payload)ipcRenderer.invoke(notes:saveImage,payload),}contextBridge.exposeInMainWorld(api,api)窗口創(chuàng)建時打開隔離webPreferences:{preload:join(__dirname,../preload/index.js),contextIsolation:true,nodeIntegration:false,}主進程這邊選目錄用系統(tǒng)對話框科目目錄按需創(chuàng)建ipcMain.handle(dialog:selectDirectory,async(){constresultawaitdialog.showOpenDialog({title:選擇筆記保存目錄,properties:[openDirectory,createDirectory],})if(result.canceled||!result.filePaths[0])returnnullreturnresult.filePaths[0]})ipcMain.handle(notes:ensureSubject,async(_e,rootPath,subjectId){ensureSubjectDirs(rootPath,subjectId)})exportfunctionensureSubjectDirs(rootPath:string,subjectId:string):void{ensureDir(join(rootPath,subjectId,notes))ensureDir(join(rootPath,subjectId,essays))ensureDir(join(rootPath,subjectId,notes,assets))ensureDir(join(rootPath,subjectId,essays,assets))}讀圖還有一層路徑約束只允許assets/下的相對路徑拒絕..避免任意讀盤。五、論文練習結構拆開字數(shù)按考場習慣算軟考論文不是「一個大文本框」。客戶端拆成三塊題目首行當題目名稱下面貼題目描述和要求摘要硬限制 300 字正文建議 20002500 字底部實時變色提示字數(shù)規(guī)則刻意貼近考場而不是text.length中文每個漢字計1英文 / 數(shù)字連續(xù)串整個單詞計1中間加空格就變成兩個詞標點計1空白不計exportfunctioncountExamWords(text:string):number{if(!text)return0constmatchestext.match(/[\u4e00-\u9fff]|[A-Za-z0-9]|[^\s\u4e00-\u9fffA-Za-z0-9]/g,)returnmatches?matches.length:0}界面上摘要超 300 會標紅禁止保存正文落在 20002500 顯示為合適區(qū)間。打開 / 保存 / AI 潤色之后還會做一次「章節(jié)消毒」摘要框里如果誤帶了## 正文會截斷避免三個框互相污染。這對從 Word 粘貼、或模型偶爾把結構標題一起吐出來特別有用。論文 Markdown 組裝大致如下exportfunctionbuildEssayMarkdown(parts:EssayParts):string{constcleanedsanitizeEssayParts(parts)consttitlecleaned.title||未命名論文consttopicBlockcleaned.prompt.trim()||titlereturn--- title: ${title.replace(//g,\)} type: essay updatedAt: ${newDate().toISOString()} --- #${title}## 題目描述${topicBlock}## 摘要${cleaned.abstract}## 正文${cleaned.body}}知識點筆記走另一條路contenteditable編輯保存時turndown轉 Markdown打開時marked轉回 HTML。標題只寫 YAML frontmatter正文不再重復插# 標題避免每次保存疊一層標題。六、AI 評分與潤色桌面端只負責「問」和「展示」前端請求很薄默認打本機9001constAI_BASEimport.meta.env.VITE_AI_BASE_URL||http://127.0.0.1:9001exportfunctionpolishEssay(payload:EssayPolishRequest){returnpostJson(/api/ai/essay/polish,payload)}exportfunctionscoreEssay(payload:{subject:stringtopic:stringabstractText:stringbodyText:string}){returnpostJson(/api/ai/essay/score,payload)}潤色可以指定partabstract/body/all。寫回編輯器前同樣走sanitizeEssayParts防止模型把「## 摘要」這類結構標題帶回來。評分彈窗直接消費后端 JSONtotalScore總分滿分 75level合格 / 不及格≥45 合格dimensions切題、實踐、結構、表達等維度分 評語strengths/improvements優(yōu)點和改進建議asyncfunctionrunScore(){constresawaitscoreEssay({subject:store.subject.name,topic:topic.value.trim(),abstractText:abstractText.value,bodyText:body.value,})scoreResult.valueres scoreOpen.valuetruestatus.valueAI 評分完成${res.totalScore}分${res.level||-}}開發(fā)環(huán)境在.env.development里配VITE_AI_BASE_URLhttp://127.0.0.1:9001七、后端AgentScope 2.x Java 實現(xiàn)論文智能體論文潤色、評分都屬于「一次任務、不要記憶」的場景。我沒有用帶 Redis 會話的長對話 Agent而是每次請求新建一個ReActAgent無 Toolkit不需要搜網頁、不需要調工具無會話記憶、無壓縮系統(tǒng)提示詞里寫死軟考論文規(guī)則強制模型只吐 JSON方便桌面端直接渲染依賴AgentScope Java 2.xdependencygroupIdio.agentscope/groupIdartifactIdagentscope-core/artifactIdversion2.0.0/version/dependencydependencygroupIdio.agentscope/groupIdartifactIdagentscope-extensions-model-openai/artifactIdversion2.0.0/version/dependency模型走 OpenAI 兼容協(xié)議本地 Ollama、DeepSeek、通義等都可以換baseUrlConfigurationpublicclassEssayAgentConfig{BeanpublicModelchatModel(Value(${essay.llm.base-url})StringbaseUrl,Value(${essay.llm.api-key})StringapiKey,Value(${essay.llm.model})StringmodelName){returnOpenAIChatModel.builder().baseUrl(baseUrl).apiKey(apiKey).modelName(modelName).stream(false).build();}}評分 Agent 的核心注入「軟考下午論文」身份要求按 75 分制輸出結構化結果。ServicepublicclassEssayAiService{privatefinalModelchatModel;privatefinalObjectMapperobjectMappernewObjectMapper();publicEssayAiService(ModelchatModel){this.chatModelchatModel;}publicEssayScoreResponsescore(EssayScoreRequestreq){ReActAgentagentReActAgent.builder().name(ruankao-essay-score).sysPrompt(SCORE_PROMPT).model(chatModel).toolkit(newToolkit()).build();Msgreplyagent.call(newUserMessage(buildScoreUserText(req))).block();returnparseScore(reply.getTextContent());}publicEssayPolishResponsepolish(EssayPolishRequestreq){ReActAgentagentReActAgent.builder().name(ruankao-essay-polish).sysPrompt(POLISH_PROMPT).model(chatModel).toolkit(newToolkit()).build();Msgreplyagent.call(newUserMessage(buildPolishUserText(req))).block();returnparsePolish(req.getPart(),reply.getTextContent());}}系統(tǒng)提示詞是效果的關鍵。評分側我會明確告訴模型你不是通用作文老師而是軟考閱卷老師。privatestaticfinalStringSCORE_PROMPT 你是軟考高級/中級下午論文閱卷老師。滿分 75 分45 分及格。 結合科目、題目要求、摘要和正文從下面維度打分 1. 切合題意20 2. 應用深度與技術方案20 3. 實踐性與角色貢獻15 4. 結構完整性摘要/背景/問題/對策/效果10 5. 表達與條理性10 要求 - 只輸出 JSON不要 Markdown 代碼塊 - 分數(shù)必須是整數(shù)總分等于各維度之和 - 摘要超過 300 字、正文明顯不足 2000 或嚴重超 2500要在 improvements 里指出 JSON 字段 { totalScore: 0, level: 合格|不及格, summary: 總評, dimensions: [{name:,score:0,max:0,comment:}], strengths: [], improvements: [] } ;潤色側則強調「像考生自己改稿」不要寫成 AI 腔也不要發(fā)明沒寫過的項目privatestaticfinalStringPOLISH_PROMPT 你是軟考論文寫作教練。請潤色考生文稿保留其項目事實不要編造經歷。 摘要控制在 300 字以內正文盡量落在 20002500 字。 按 part 只改對應部分abstract / body / all。 只輸出 JSON {part:abstract|body|all,abstractText:,bodyText:} 未修改的字段也要原樣帶回。 ;Controller 對上桌面端路徑RestControllerRequestMapping(/api/ai/essay)publicclassEssayAiController{privatefinalEssayAiServiceessayAiService;publicEssayAiController(EssayAiServiceessayAiService){this.essayAiServiceessayAiService;}PostMapping(/polish)publicEssayPolishResponsepolish(RequestBodyEssayPolishRequestreq){returnessayAiService.polish(req);}PostMapping(/score)publicEssayScoreResponsescore(RequestBodyEssayScoreRequestreq){returnessayAiService.score(req);}}為什么這里用獨立 ReActAgent而不是帶記憶的 HarnessAgent論文評分如果把上一篇稿子的上下文帶進來分數(shù)會被污染。每次新建 Agent等價于「換一份卷子、換一個閱卷老師」結果更穩(wěn)也更省 token。模型返回偶爾會包一層 json解析前要剝一下privateStringunwrapJson(Stringraw){Stringtextrawnull?:raw.trim();if(text.startsWith()){texttext.replaceAll(^(?:json)?\s*,).replaceAll(\s*$,);}intstarttext.indexOf({);intendtext.lastIndexOf(});if(start0endstart){returntext.substring(start,end1);}thrownewIllegalStateException(模型未返回 JSON);}八、列表拖動排序不引入第三方拖拽庫中間欄用原生draggable。拖動過程中 Pinia 即時換位松手后把文件名數(shù)組寫進.order.json。functiononDragOver(e:DragEvent,fileName:string):void{e.preventDefault()if(!dragFrom.value||dragFrom.valuefileName)returnconstfromIndexstore.notes.findIndex((n)n.fileNamedragFrom.value)consttoIndexstore.notes.findIndex((n)n.fileNamefileName)if(fromIndex0||toIndex0)returnstore.moveNote(fromIndex,toIndex)}asyncfunctiononDrop(e:DragEvent):Promisevoid{e.preventDefault()awaitstore.persistNotesOrder()}列表讀取時排序表里有的按表排沒有出現(xiàn)過的文件按修改時間補在后面。新建默認插到最前刪除 / 重命名會同步維護這份順序。九、本地跑起來桌面端Node.js 18cdruankaotongnpminstallnpmrun dev需要 AI 時先啟動 Java 后端默認 9001再確認VITE_AI_BASE_URLhttp://127.0.0.1:9001打包npmrun dist:mac# macOS DMGnpmrun dist:win# Windows NSIS 便攜包未配置蘋果 / 微軟代碼簽名時別人第一次打開可能被系統(tǒng)攔截需要在「隱私與安全性」里允許或右鍵打開。正式分發(fā)再補簽名即可。十、幾個做下來比較有用的點筆記是 Markdown不是私有庫。卸載應用不會帶走倉庫換編輯器也不怕被鎖死。字數(shù)規(guī)則要單獨寫。length對英文單詞和標點都不符合軟考直覺正則按「漢字 / 整詞 / 標點」切一次就夠用。AI 不要塞進 Electron 主進程。模型、提示詞、JSON 解析都放 Java 服務里桌面端保持可獨立分發(fā)。評分 Agent 不要記憶。論文是試卷不是聊天。AgentScope 的 ReActAgent 很適合這種一次性任務。模型輸出要契約化。彈窗、維度分、優(yōu)缺點列表全靠 JSON 字段提示詞里把 schema 寫死解析失敗再兜底。寫在最后軟考通解決的是我自己備考時的真實摩擦知識點要本地可檢索論文要能看見字數(shù)寫完還想有個「閱卷老師」給修改意見。它不是又一個 ChatGPT 套殼而是本地 Markdown 倉庫 軟考論文編輯器 AgentScope 2.x 評分潤色智能體。前端 Vue 3 / Electron后端 Java AgentScope 2.x兩邊通過兩個 HTTP 接口對接結構很干凈。如果這篇文章對你有幫助歡迎點贊收藏。需要完整源碼桌面端 AI 后端的同學私信我即可。