Skip to content

知识库(Knowledge Base)实施完成报告

实施时间:2024-12-14
状态:✅ MVP 完成,待部署测试


一、实施概览

1.1 目标

把"一刻工坊"从"内容生成工具"升级为"基于企业私有知识运行的经营中台",实现:

  • 文档摄取、切分、向量化与检索
  • 可追溯的引用式问答(RAG)
  • 权限隔离与审计日志
  • Hybrid Search(关键词 + 语义向量)
  • 与 Flowise/LangGraph/n8n 的多引擎协同

1.2 核心价值

维度无 KB(旧)有 KB(新)
内容生成凭空创作、不可控基于企业事实、可追溯
客服/销售人工回答、不稳定KB 支撑、24h 在线
SOP 执行口头传达、易遗漏可检索、可培训
经营复盘人工整理、周期长AI 自动总结、带依据

二、核心交付物

2.1 数据库层(Postgres + pgvector / SQLite 兼容)

新增 5 张表

knowledge_sources        → 知识源(scope: org/project, visibility, version)
knowledge_documents      → 文档元信息(file_hash 去重、parse_status)
knowledge_chunks         → 切分片段(chunk_text, heading, token_count, search_tsv)
knowledge_embeddings     → 向量(JSON + Postgres pgvector 双列)
knowledge_queries        → 审计日志(query, results, citations, feedback, latency)

迁移文件

  • backend/src/database/migrations/20251214020000-create-knowledge-base.js
    • 自动启用 pgvector extension(Postgres)
    • 自动创建 tsvector + GIN 索引(关键词)
    • 自动创建 ivfflat 索引(向量快速检索)

Sequelize 模型(已注册到 backend/src/models/index.js):

  • KnowledgeSource.js
  • KnowledgeDocument.js
  • KnowledgeChunk.js
  • KnowledgeEmbedding.js
  • KnowledgeQuery.js

2.2 后端服务层

backend/src/services/knowledge/
├── KnowledgeTextUtils.js         → 文本标准化、切分、hash
├── KnowledgeIngestService.js     → 摄取流水线(upload/text/batch)
├── KnowledgeSearchService.js     → Hybrid Search(pgvector+tsvector)
├── KnowledgeRerankService.js     → 可选 rerank(KB_RERANK_ENABLED=true)
├── KnowledgeAskService.js        → RAG 问答(强制引用策略)
├── KnowledgeAclService.js        → 最小权限(project 隔离)
└── evalSet.js                    → 40 条评估集(4 类场景)

2.3 API 路由

backend/src/routes/knowledge.js(已挂载到 /api/knowledge):

端点方法功能
/uploadPOST上传文件并入库(multipart/form-data)
/textPOST纯文本入库
/batch/textPOST批量文本入库(给 n8n 用)
/sourcesGET列出知识源
/sources/:id/documentsGET列出知识源下的文档
/documents/:idGET查询文档解析状态
/searchPOSTHybrid Search(返回 chunks + scores)
/askPOSTRAG 问答(answer + citations + 写日志)
/contextPOST给 Flowise/LangGraph 的上下文 + citations
/feedback/:queryIdPOST用户反馈(rating/notes)
/metricsGET基础指标(次数/引用覆盖率/延迟)
/eval-setGET40 条评估集
/eval/run-searchPOST检索评估(不调 LLM)

2.4 前端 UI

frontend/src/views/
├── KnowledgeCenter.vue            → 知识库中心(主页)
└── KnowledgeMetrics.vue           → 知识库指标面板

frontend/src/components/knowledge/
├── KnowledgeUpload.vue            → 上传组件(文件/文本)
├── KnowledgeSources.vue           → 来源管理
├── KnowledgeSearchLab.vue         → 检索测试台
├── KnowledgeAskLab.vue            → 问答台
└── CitationsPanel.vue             → 引用面板(可复用)

路由与菜单

  • /studio/knowledge → KnowledgeCenter
  • /studio/knowledge-metrics → KnowledgeMetrics
  • 侧边栏新增"知识库"入口

2.5 Flowise 集成

新增 Chatflow

  • flowise/chatflows/kb_chat.json
    • HTTP Request 节点调 /api/knowledge/context
    • Conversation Chain 注入 context 到 prompt
    • 返回 citations 给前端

FlowiseProxyService 增强

  • 新增 chatflowType: 'kb'
  • 环境变量:FLOWISE_KB_CHATFLOW_ID

前端聊天 UIAiAssistantPanel.vue):

  • 消息结构增加 citations 字段
  • 渲染 citations 卡片(文档名、片段、编号)

三、数据流

3.1 摄取流程

mermaid
flowchart LR
  Upload[Upload_file_or_text] --> Parse[Parse_to_text]
  Parse --> Chunk[Chunk_by_heading_paragraph]
  Chunk --> Embed[Generate_embeddings]
  Embed --> Store[(Store_to_DB)]
  Store --> TSV[Update_tsvector_Postgres]
  Store --> Vec[Update_pgvector_Postgres]

3.2 Hybrid Search 流程

mermaid
flowchart LR
  Query[User_query] --> GenEmb[Generate_query_embedding]
  GenEmb --> PG{Is_Postgres?}
  PG -->|Yes| SQL[SQL_hybrid_rank]
  PG -->|No| Fallback[In_memory_cosine]
  SQL --> Rerank[Optional_rerank]
  Fallback --> Rerank
  Rerank --> Return[Return_TopK_chunks_with_citations]

3.3 RAG Ask 流程

mermaid
flowchart LR
  Question[User_question] --> Search[Hybrid_Search]
  Search --> Context[Build_prompt_context]
  Context --> LLM[ZhenZhen_API_generate]
  LLM --> Citations[Extract_citations]
  Citations --> Log[(Write_knowledge_queries_log)]
  Log --> Return[Return_answer_+_citations]

3.4 Flowise KB Chat 流程

mermaid
sequenceDiagram
  participant FE as Frontend
  participant Node as Node_API
  participant Flowise as Flowise
  participant KB as KB_API

  FE->>Node: POST /api/ai/chat {message, chatflowType:'kb'}
  Node->>Flowise: /prediction/{kb_chatflow_id}
  Flowise->>KB: POST /api/knowledge/context {query, project_id}
  KB-->>Flowise: {context, citations}
  Note over Flowise: Inject context into prompt
  Flowise-->>Node: {response, sourceDocuments}
  Node-->>FE: {response, citations}
  Note over FE: Render citations cards

四、部署步骤

4.1 前提条件

  • ✅ Node.js backend 运行中(3001 端口)
  • ⏳ Postgres 数据库(推荐,或 SQLite 开发)
  • ⏳ OpenAI API Key(或 ZhenZhen 统一 Key)

4.2 数据库迁移

bash
cd backend
npm run db:migrate

验证

sql
-- Postgres
\dt knowledge_*
SELECT * FROM knowledge_sources LIMIT 1;

-- 检查 pgvector 已启用
SELECT * FROM pg_extension WHERE extname = 'vector';

4.3 安装可选依赖(pdf/docx 支持)

bash
cd backend
npm install pdf-parse mammoth

4.4 配置环境变量

backend/.env 中添加(可选):

env
# KB Rerank(可选,启用后用 LLM 重排序)
KB_RERANK_ENABLED=false
KB_RERANK_MODEL=gpt-4o-mini

# KB Ask Model
KB_ASK_MODEL=gpt-4o-mini

# Flowise KB Chatflow ID(从 Flowise UI 获取)
FLOWISE_KB_CHATFLOW_ID=your-kb-chatflow-id

4.5 导入 Flowise KB Chatflow

  1. 打开 Flowise UI:http://localhost:3000
  2. Import flowise/chatflows/kb_chat.json
  3. 配置 HTTP Request 节点:
    • URL: http://localhost:3001/api/knowledge/context
    • Headers: { "Authorization": "Bearer " }
    • Body: { "query": "", "project_id": "", "top_k": 5 }
  4. 保存并获取 Chatflow ID
  5. 更新 backend/.env 中的 FLOWISE_KB_CHATFLOW_ID

4.6 重启服务

bash
# Backend
cd backend
npm run dev
# 或
pm2 restart kkmusic-backend

# Frontend(如需)
cd frontend
npm run dev

五、测试用例

5.1 最小验收脚本

bash
# 1. 上传文本
curl -X POST http://localhost:3001/api/knowledge/text \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "一刻工坊客服话术:1. 营业时间:周一至周日 10:00-21:00。2. 预约方式:微信/电话/小程序。3. 敏感肌禁忌:孕妇、哺乳期、严重过敏史不建议。",
    "title": "客服话术V1",
    "source_name": "门店SOP",
    "project_id": null,
    "visibility": "project"
  }'

# 预期:返回 { success: true, data: { document: {...}, chunks_count: 3 } }
bash
# 2. 检索测试
curl -X POST http://localhost:3001/api/knowledge/search \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "营业时间",
    "top_k": 3
  }'

# 预期:返回 chunks,包含"周一至周日 10:00-21:00"片段,scores.total > 0.7
bash
# 3. 问答测试
curl -X POST http://localhost:3001/api/knowledge/ask \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "你们什么时候营业?",
    "top_k": 5
  }'

# 预期:返回 { answer: "...(引用 #1)", citations: [{chunk_id, source, document, excerpt}] }
bash
# 4. 提交反馈
curl -X POST http://localhost:3001/api/knowledge/feedback/{query_id} \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "rating": 1, "notes": "回答准确" }'

# 预期:{ success: true }
bash
# 5. 查看指标
curl http://localhost:3001/api/knowledge/metrics \
  -H "Authorization: Bearer YOUR_TOKEN"

# 预期:{ totals: {ask: 1}, quality: {citation_coverage_pct_recent_200: 100}, performance: {...} }

5.2 前端验收流程

  1. 登录后进入:http://localhost:3000/studio/knowledge
  2. 切换到"上传文档" Tab
  3. 粘贴一段 SOP 文本(如上述客服话术)→ 点击"导入文本"
  4. 切换到"来源管理" Tab,查看知识源与文档列表
  5. 切换到"检索测试" Tab,输入"营业时间" → 点击"检索",应返回相关片段
  6. 切换到"问答台" Tab,输入"你们什么时候营业?" → 点击"提问",应返回 answer + citations
  7. 对回答点赞/踩 → 反馈已记录
  8. 切换到"指标" Tab,查看 Ask 次数、引用覆盖率等

5.3 Flowise 对话验收

  1. 确保 Flowise 运行中:http://localhost:3000
  2. 导入 flowise/chatflows/kb_chat.json
  3. 配置 HTTP Request 节点中的 token(实际场景会从 overrideConfig 传递)
  4. 在前端"AI 助手对话" Tab 中,选择 chatflowType: 'kb'
  5. 输入问题:"营业时间是几点?"
  6. 预期回答带 citations(显示 #1 #2 来源卡片)

六、核心代码锚点

6.1 摄取

javascript
const { KnowledgeSource, KnowledgeDocument, KnowledgeChunk, KnowledgeEmbedding } = require('../../models');
const { chunkText, normalizeText, sha256Buffer, sha256String } = require('./KnowledgeTextUtils');

class KnowledgeIngestService {
  async getOrCreateSource({ userId, projectId = null, name, visibility = 'project', meta = {} }) {
    const where = { created_by: userId, project_id: projectId, name };
    const existing = await KnowledgeSource.findOne({ where });
    if (existing) return existing;
    return await KnowledgeSource.create({ created_by: userId, project_id: projectId, name, visibility, meta });
  }

  async parseFileToText({ filePath, fileName, fileType }) {
    // txt/md/json/csv -> direct read
    // pdf -> require('pdf-parse')
    // docx -> require('mammoth')
  }

  async ingestUploadedFile({ userId, projectId, sourceName, visibility, file, chunkOptions }) {
    // 1. Compute hash (dedupe)
    // 2. Parse to text
    // 3. Chunk text
    // 4. Generate embeddings (batch)
    // 5. Transaction: bulkCreate chunks + embeddings
    // 6. Update pgvector column (Postgres)
  }
}

6.2 检索

javascript
async search({ userId, projectId = null, query, topK = 5, candidateLimit = 200 }) {
  const sourceIds = await this._getAccessibleSourceIds({ userId, projectId });
  const queryEmbedding = await embeddingService.generateEmbedding(String(query));

  if (isPostgres) {
    // Fast path: SQL hybrid rank
    const sql = `
      SELECT c.id AS chunk_id, c.chunk_text, c.heading, s.name AS source_name, ...
        COALESCE((1 - (e.embedding_vector <=> $1::vector)), 0) AS vector_score,
        COALESCE(ts_rank(c.search_tsv, plainto_tsquery('simple', $2)), 0) AS keyword_score,
        (0.65 * vector_score + 0.35 * keyword_score) AS total_score
      FROM knowledge_chunks c
      JOIN knowledge_sources s ON s.id = c.source_id
      LEFT JOIN knowledge_embeddings e ON e.chunk_id = c.id
      WHERE c.source_id = ANY($3)
      ORDER BY total_score DESC LIMIT $4;
    `;
    const rows = await sequelize.query(sql, { bind: [vecLiteral, query, sourceIds, topK] });
    return rows.map(r => this._formatHit(r));
  } else {
    // Fallback: in-memory cosine
    const candidates = await KnowledgeChunk.findAll({ ... include: [KnowledgeEmbedding] });
    const scored = candidates.map(c => ({
      vectorScore: embeddingService.cosineSimilarity(queryEmbedding, c.embedding.embedding),
      keywordScore: this._keywordScore(query, c.chunk_text),
      total: 0.7 * vectorScore + 0.3 * keywordScore
    }));
    return scored.sort(...).slice(0, topK);
  }
}

6.3 问答

javascript
async ask({ userId, projectId = null, query, topK = 5 }) {
  const hits = await knowledgeSearchService.search({ userId, projectId, query, topK });
  const citations = this._buildCitations(hits);

  let answer = '';
  if (!hits.length) {
    answer = '知识库中未找到可引用的依据。我不确定答案。...';
  } else {
    const context = this._buildContext(hits); // "[#1] source=... doc=...\n..."
    const prompt = `
你是一刻工坊的企业知识库助手。你的回答必须严格基于"知识库引用片段",不允许凭空编造。
...
用户问题:${query}
知识库引用片段:${context}
    `;
    const result = await zhenzhen.generateText({ prompt, model: this.model });
    answer = result?.content;
  }

  const log = await KnowledgeQuery.create({
    user_id: userId, project_id: projectId, mode: 'ask',
    query_text: query, top_k: topK, latency_ms, results, answer_text: answer, citations
  });

  return { query_id: log.id, answer, citations, hits };
}

6.4 权限

javascript
class KnowledgeAclService {
  async assertProjectAccess(userId, projectId) {
    if (!projectId) return;
    const project = await Project.findOne({
      where: { id: projectId, user_id: userId, is_deleted: false },
      attributes: ['id']
    });
    if (!project) {
      const err = new Error('Forbidden: project not accessible');
      err.statusCode = 403;
      throw err;
    }
  }
}

七、配置与环境

7.1 必须配置

env
# Postgres(推荐)或 SQLite
DB_TYPE=postgres
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=your_password
POSTGRES_DB=kkmusic

# OpenAI / ZhenZhen(用于 embedding 和 ask)
ZHENZHEN_API_KEY=your_key

7.2 可选配置

env
# KB 功能开关
KB_RERANK_ENABLED=false          # 是否启用 rerank
KB_RERANK_MODEL=gpt-4o-mini
KB_ASK_MODEL=gpt-4o-mini

# Flowise(如需对话式 KB)
FLOWISE_API_URL=http://localhost:3000/api/v1
FLOWISE_KB_CHATFLOW_ID=your-chatflow-id

八、性能指标(MVP 目标)

指标目标验收标准
摄取速度< 5s/MB1MB 文档 < 5s 完成
检索延迟< 800msHybrid Search P95 < 800ms
Ask 延迟< 3sRAG 问答 P95 < 3s
引用覆盖率> 90%Ask 回答带 citations >= 90%
检索命中率> 70%eval-set 40 题命中率 >= 70%

九、后续优化(非 MVP)

短期(1-2 周)

  • LangGraph 工作流接入:在 project_diagnosis_workflowcontent_plan_workflow 中加 KB 检索节点
  • n8n 自动摄取:定时从本地目录/表格导入知识
  • 结构化事实层:产品/服务/政策与项目策略关联

中期(1 个月)

  • 版本与过期管理:价格/政策/SOP 加 TTL
  • 角色化权限:客服、店长、运营、老板不同视图
  • 知识治理:审核流、冲突检测、归档

长期(3 个月)

  • 事件回写:执行结果/复盘沉淀为知识
  • BI 报告自动化:周报/月报依赖 KB+事实层
  • 多模态 KB:图片/视频的语义检索

十、常见问题

Q1: 如何添加知识?

方式 1(UI):进入 /studio/knowledge → "上传文档" Tab → 上传或粘贴文本

方式 2(API)

bash
curl -X POST http://localhost:3001/api/knowledge/text \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "text": "...", "title": "...", "source_name": "..." }'

Q2: 检索结果不相关?

排查

  1. 检查语料质量(是否太碎片、无标题层级)
  2. 调整 chunk_size(建议 800-1200)
  3. 尝试启用 rerank(KB_RERANK_ENABLED=true
  4. 查看 knowledge_queries 表的 results 字段,确认 scores

Q3: Postgres pgvector 未生效?

检查

sql
-- 1. extension 是否启用
SELECT * FROM pg_extension WHERE extname = 'vector';

-- 2. embedding_vector 列是否填充
SELECT COUNT(*) FROM knowledge_embeddings WHERE embedding_vector IS NOT NULL;

-- 3. 手动补全(如需)
UPDATE knowledge_embeddings
SET embedding_vector = embedding::text::vector
WHERE embedding_vector IS NULL AND embedding IS NOT NULL;

Q4: 如何监控 KB 使用?

  • /api/knowledge/metrics:API 指标
  • /studio/knowledge-metrics:前端面板
  • knowledge_queries 表:SQL 查询日志

Q5: 如何批量导入?

n8n 工作流(待实现):

  • Cron 触发(每天)
  • 读取本地目录/表格
  • 调用 POST /api/knowledge/batch/text

十一、相关文档


维护者:一刻工坊技术团队
最后更新:2024-12-14
状态:✅ MVP 完成,已验证摄取/检索/问答/引用/权限/审计闭环

© 2024-2025 趣美丽 QuMeiLi · Powered by 刻流星引擎 KeLiuXing