#
摘要
本文围绕标题所述主题,结合本仓库当前源码行进行说明。仅供技术理解与内部培训,不构成定密、法务或密码测评结论。文中代码块均摘自本地仓库对应路径与行号。
正文
0. 结论先行
结论先行:保密检查由内置助手触发大模型按模板输出审查意见,程序再从摘要中抽取命中片段并尝试挂批注。长文档依赖分块与结构化 JSON 提示。下文每节先说明要点,紧接着给出仓库中的对应源码片段。
本篇标题:结构化分批(ChunkMeta)如何约束模型只依据 ChunkText 定位
1. 助手标识与默认写回方式
保密检查对应内置助手 id 为 analysis.security-check。下列片段展示其在注册表中的默认动作与输入来源等字段,便于与界面行为对照。
// src/utils/assistantRegistry.js 第653-667行
{
id: 'analysis.security-check',
label: '保密检查',
shortLabel: '保密检查',
group: 'analysis',
modelType: 'chat',
defaultModelCategory: 'chat',
supportsRibbon: true,
defaultDisplayLocations: ['ribbon-main'],
allowedActions: ['comment', 'link-comment', 'insert', 'append', 'none'],
defaultAction: 'link-comment',
defaultOutputFormat: 'markdown',
defaultInputSource: INPUT_SOURCE_DOCUMENT,
description: '基于关键词和上下文检查文档中的涉密、涉军、单位名称、密级标识和敏感业务信息风险。',
2. 研判原则写入提示词模板
研判原则与风险级别枚举写在 userPromptTemplate 的长模板中,模型据此输出 Markdown 小节。下列片段为模板中研判原则与风险级别相关行。
// src/utils/assistantRegistry.js 第704-713行
研判原则:
1. 关键词命中不等于泄密,必须结合上下文判断,不要机械命中即报高风险
2. 对常见公开词、泛化称谓、新闻公开语境、教材示例语境,要谨慎降噪,避免误报
3. 仅依据原文内容作出审慎判断,不要臆测背景、来源、真实单位或法律结论
4. 对每项风险必须说明:命中片段、风险类别、风险级别、判断依据、建议处理方式
5. 风险级别统一使用:
- 高风险:直接出现密级标识、具体部队/单位身份、具体部署计划、未公开内部编号、明确敏感联系人信息等
- 中风险:出现敏感关键词且上下文指向内部事项,但是否涉密仍需人工确认
- 低风险或待人工复核:存在可疑词或敏感线索,但公开性、敏感性、上下文不足,暂不能直接判定
6. 如果文本未发现明显保密风险,明确写“未发现明显保密风险”
3. Ribbon 按钮如何启动该助手
功能区按钮在 ribbon 的 OnAction 分支中调用 executeAssistantFromRibbon,并传入 taskTitle。
// src/components/ribbon.js 第3364-3370行
// 脱密分组
case 'btnDocumentDeclassifyCheck':
executeAssistantFromRibbon('analysis.security-check', {
taskTitle: '保密检查' }).catch((e) => {
console.error('保密检查失败:', e)
alert('保密检查失败: ' + (e?.message || e))
})
break
4. 任务运行器中的专项提示
对保密检查助手,任务运行器追加一条最佳实践说明,强调关键词命中须结合上下文分级。
// src/utils/assistantTaskRunner.js 第310-312行
if (assistantId === 'analysis.security-check') {
return '保密检查时,关键词命中必须结合上下文分级判断;避免武断下结论,并明确哪些内容需要人工复核。'
}
5. 结构化批次系统提示骨架
长文档走结构化 JSON 批次时,buildStructuredBatchInstruction 拼接 schemaVersion、模式说明与 ChunkText 约束。
// src/utils/assistantStructuredPipeline.js 第90-104行
export function buildStructuredBatchInstruction(assistantId, options = {
}) {
const mode = getStructuredAssistantMode(assistantId)
return [
`你必须只输出一个合法 JSON 对象,schemaVersion 固定为 "${
STRUCTURED_PIPELINE_SCHEMA_VERSION}"。`,
'不要输出 Markdown,不要输出 JSON 之外的解释。',
getModeSpecificInstruction(mode, options),
getStructuredJsonAnchorExtraRules(assistantId, options.documentAction),
'你将收到一个带 ChunkMeta / LineMap / ChunkText 的结构化原文块,所有定位都必须以 ChunkText 为唯一依据。',
String(options.documentAction || '').trim() === 'replace'
? '当文档动作是 replace 时,只有在 originalText 可以从 ChunkText 中精确截取时才输出 replace;否则改为 comment。'
: '',
'统一 JSON 结构如下:',
'{"schemaVersion":"","mode":"","summary":"","content":"","operations":[{"type":"replace|comment|insert-after|prepend|append|none","target":"absolute-range|chunk-relative-range|text-anchor|paragraph-range","start":0,"end":0,"originalText":"","replacementText":"","commentText":"","reason":"","suggestion":"","confidence":"high|medium|low","paragraphIndex":0,"prefix":"","suffix":"","sentence":""}]}',
'若没有可执行操作,operations 返回 [];若没有正文结果,content 返回空字符串。'
].filter(Boolean).join('\n')
6. 从 Markdown 摘要抽取命中片段
程序不直接信任自由格式,而用 extractHitFragmentsFromSecurityCheckMarkdown 做多模式正则抽取,供后续批注锚点。
// src/utils/structuredCommentPolicy.js 第22-52行
export function extractHitFragmentsFromSecurityCheckMarkdown(markdown) {
const text = String(markdown || '')
const out = []
const seen = new Set()
const add = (raw) => {
let t = String(raw || '').trim()
t = t.replace(/^[`「'"“]+|[`」'"”]+$/g, '').replace(/\*\*/g, '').trim()
if (t.length < 2 || seen.has(t)) return
if (/^(无|暂无|未发现|没有|不适用)\s*$/u.test(t)) return
seen.add(t)
out.push(t)
}
let m
// 模型常见变体:- **命中片段:** `原文`(加粗标签 + 中英文冒号 + 反引号)
const reBacktickPatterns = [
/\*\*命中片段[::]\*\*\s*`([^`\n]+)`/g,
/[-*]\s*\*\*命中片段[::]\*\*\s*`([^`\n]+)`/g,
/[-*]\s*\*{
1,2}\s*命中片段\s*\*{
1,2}\s*[::]\s*`([^`\n]+)`/g,
/命中片段\s*\*{0,2}\s*[::]\s*\*{0,2}\s*`([^`\n]+)`/g
]
for (const reBacktick of reBacktickPatterns) {
while ((m = reBacktick.exec(text)) !== null) add(m[1])
}
const reLinePatterns = [
/[-*]\s*\*\*命中片段[::]\*\*\s*(?!`)([^\n]+)/g,
/[-*]\s*命中片段[::]\s*(?!`)([^\n]+)/g
]
for (const reLine of reLinePatterns) {
while ((m = reLine.exec(text)) !== null) add(m[1])
}
return out
7. 保密检查在结构化 JSON 下的额外锚点规则
getStructuredJsonAnchorExtraRules 为保密检查追加「命中片段须逐字来自 ChunkText」等说明,与批注类文档动作组合使用。
// src/utils/structuredCommentPolicy.js 第119-127行
export function getStructuredJsonAnchorExtraRules(assistantId, documentAction = '') {
const id = String(assistantId || '').trim()
const act = String(documentAction || '').trim()
const parts = []
if (id === ANALYSIS_SECURITY_CHECK_ID) {
parts.push([
'保密检查:须将完整审查结论写入 JSON 的 summary 字段;summary 内仍使用模板要求的 Markdown 小节(## 高风险项 等)。',
'每条风险项必须包含「命中片段」且反引号内为 ChunkText 中的连续原文(逐字照抄);推荐格式:- **命中片段:** `原文片段`(加粗与否均可,反引号内必须与正文一致);operations 可留空 []。'
].join('\n'))
8. 与定密流程的边界
README 已说明检查结论仅为辅助参考。实现上判断来自大模型与提示词,不包含法定密级鉴定接口。组织对外处置仍须走正式流程。