04-llm-prompts.md 6.1 KB

LLM Prompt 设计

本系统调用 LLM 完成三类任务,所有 prompt 要求 LLM 返回严格 JSON


1. 公共约束

  • 模型:用户在前端配置(base_url + api_key + model),如 gpt-4o-minideepseek-chatqwen-plus
  • temperature:默认 0.3(归类需要稳定,不要太高)
  • max_tokens:4000(聚合生成内容可能较长)
  • 响应格式:所有 prompt 都要求输出 JSON,并在系统消息中强约束
  • 错误处理:解析失败时记录 error_msg,前端可重试

2. Prompt A:文章归类

触发:录入新文章后或用户点击"重新归类"按钮

System

你是一个知识分类助手。我会给你一篇文章和当前已有的标签体系(扁平列表,含完整路径)。
你的任务:
1. 在已有标签中找出与文章主题相关的标签(0~5 个)
2. 若有重要主题在已有标签中找不到,可以建议新增(最多 2 个),需指定父节点路径
3. 归纳文章的主题名(用于后续聚合,5~15 字,简洁明确)

返回 JSON,格式严格如下:
{
  "candidateTagIds": [数字数组,已有标签的 ID],
  "newTags": [
    {"parentPath": "父节点完整路径如 计算机/人工智能", "name": "新标签名"}
  ],
  "topic": "主题名"
}
不要返回任何 JSON 之外的内容。

User

【文章标题】{title}

【文章正文】
{raw_text 截断到 6000 字符以内}

【当前已有标签体系】(格式:ID | 完整路径)
1 | 计算机
2 | 计算机/人工智能
3 | 计算机/人工智能/LLM
4 | 计算机/运维
5 | 文学
...

解析

  • 对每个 newTags[].parentPath 在表中按 / 分段查找;找不到父节点则忽略该项(前端展示警告)
  • 创建新 tag,加入 candidateTagIds
  • 调用 setEntityTags("article", articleId, allTagIds)

3. Prompt B:聚合判定 + 内容生成

触发:classify 完成后,自动或手动调用 aggregate

Step 1:聚合判定(轻量)

System

你是一个知识聚合助手。我会给你一篇新文章的标题、主题和摘要,以及现有知识库中的所有知识标题。
你的任务:判断这篇文章是否应该并入某条已有知识。

返回 JSON:
{
  "action": "merge" | "create",
  "targetKnowledgeId": 数字,   // action=merge 时必填
  "reason": "判断理由简述"
}
判断原则:
- 主题明确指向同一对象(如"大模型部署方法"与"LLM 部署实践"应合并)
- 主题相关但角度不同(如"大模型部署"与"大模型推理优化")应分开
- 若不确定,倾向 create(创建新知识)
不要返回 JSON 之外的内容。

User

【新文章】
标题:{title}
主题:{llm_topic}
摘要:{raw_text 前 500 字}

【已有知识列表】(ID | 标题 | 文章数)
1 | 大模型部署方法 | 3
2 | RAG 实践 | 2
3 | Prompt Engineering | 5

Step 2:内容生成

根据 action 分两种情况:

3a. action = create(新建知识)

System

你是一个知识整理专家。基于下面这篇文章,生成一篇结构化 Markdown 知识文档。
要求:
- 包含清晰的章节标题(##)
- 内容必须基于给定文章,不要臆造
- 保留原文的关键技术细节、代码、命令、参数
- 在文档末尾用 `> 来源: {文章标题}` 标注来源

返回 JSON:
{
  "title": "知识标题(5~20字,简洁通用的主题表述)",
  "content": "完整 Markdown 内容",
  "changeDesc": "本次变更描述,如「基于文章《xxx》创建初版知识」"
}
不要返回 JSON 之外的内容。

User

【文章标题】{title}
【文章正文】
{raw_text}

写入:knowledge(新建)+ knowledge_version(type=create, version_no=1, content_snapshot=content)

3b. action = merge(并入已有)

System

你是一个知识整理专家。我会给你当前已有的知识文档全文,以及一篇新文章。
你的任务:将新文章的内容整合进现有知识,并在合适位置增补或修订。

要求:
- 保持原文档的整体结构和章节顺序
- 新内容应合并到对应章节;若现有章节不匹配,可在末尾新增 ##
- 不要删除原有内容,除非明确冲突(此时在 changeDesc 中说明)
- 增补段落以保留原文关键细节为主,不要扩写臆造
- 在文档末尾用 `> 来源: {文章标题}` 在原来源行下追加新来源

返回 JSON:
{
  "content": "整合后的完整 Markdown",
  "changeDesc": "本次变更描述,如「根据文章《xxx》补充了关于 vLLM 部署的章节」"
}
不要返回 JSON 之外的内容。

User

【当前知识全文】
{knowledge.current_content}

【新文章标题】{title}
【新文章正文】
{raw_text}

写入:knowledge(更新 current_content, article_count+1)+ knowledge_version(type=append, version_no=last+1, content_snapshot=新 content)


4. Diff 计算

聚合完成后,后端计算 diff(不依赖 LLM):

// 伪代码
String oldContent = previousVersion != null ? previousVersion.getContentSnapshot() : "";
String newContent = currentContent;
DiffRowResult diff = diffMatchPatch.diffLineMode(oldContent, newContent);
int added = countAddedLines(diff);
int removed = countRemovedLines(diff);
String patchText = diffMatchPatch.patchToText(diffMatchPatch.patchMake(oldContent, newContent));

version.setDiffAdded(added);
version.setDiffRemoved(removed);
version.setDiffPatch(patchText);

前端 DiffViewer.vue

  • 接收 oldContentnewContent,前端用 diff-match-patch 重新渲染(避免传输大 patch)
  • 增行绿色背景,删行红色背景,未变行灰色

5. 错误与重试

场景 处理
LLM 返回非 JSON 后端正则提取 {...} 段重试一次;失败则记录 error_msg
LLM 调用网络异常 重试 2 次(指数退避),仍失败则 article.status=error
LLM 建议新增标签但父路径不存在 跳过该项,记录 warning,其他流程正常进行
用户配置错误(base_url/api_key) /api/llm/config/test 接口先测试连通性