知识库(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- 自动启用
pgvectorextension(Postgres) - 自动创建
tsvector+ GIN 索引(关键词) - 自动创建
ivfflat索引(向量快速检索)
- 自动启用
Sequelize 模型(已注册到 backend/src/models/index.js):
KnowledgeSource.jsKnowledgeDocument.jsKnowledgeChunk.jsKnowledgeEmbedding.jsKnowledgeQuery.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):
| 端点 | 方法 | 功能 |
|---|---|---|
/upload | POST | 上传文件并入库(multipart/form-data) |
/text | POST | 纯文本入库 |
/batch/text | POST | 批量文本入库(给 n8n 用) |
/sources | GET | 列出知识源 |
/sources/:id/documents | GET | 列出知识源下的文档 |
/documents/:id | GET | 查询文档解析状态 |
/search | POST | Hybrid Search(返回 chunks + scores) |
/ask | POST | RAG 问答(answer + citations + 写日志) |
/context | POST | 给 Flowise/LangGraph 的上下文 + citations |
/feedback/:queryId | POST | 用户反馈(rating/notes) |
/metrics | GET | 基础指标(次数/引用覆盖率/延迟) |
/eval-set | GET | 40 条评估集 |
/eval/run-search | POST | 检索评估(不调 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 给前端
- HTTP Request 节点调
FlowiseProxyService 增强:
- 新增
chatflowType: 'kb' - 环境变量:
FLOWISE_KB_CHATFLOW_ID
前端聊天 UI(AiAssistantPanel.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 mammoth4.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-id4.5 导入 Flowise KB Chatflow
- 打开 Flowise UI:
http://localhost:3000 - Import
flowise/chatflows/kb_chat.json - 配置 HTTP Request 节点:
- URL:
http://localhost:3001/api/knowledge/context - Headers:
{ "Authorization": "Bearer " } - Body:
{ "query": "", "project_id": "", "top_k": 5 }
- URL:
- 保存并获取 Chatflow ID
- 更新
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.7bash
# 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 前端验收流程
- 登录后进入:
http://localhost:3000/studio/knowledge - 切换到"上传文档" Tab
- 粘贴一段 SOP 文本(如上述客服话术)→ 点击"导入文本"
- 切换到"来源管理" Tab,查看知识源与文档列表
- 切换到"检索测试" Tab,输入"营业时间" → 点击"检索",应返回相关片段
- 切换到"问答台" Tab,输入"你们什么时候营业?" → 点击"提问",应返回 answer + citations
- 对回答点赞/踩 → 反馈已记录
- 切换到"指标" Tab,查看 Ask 次数、引用覆盖率等
5.3 Flowise 对话验收
- 确保 Flowise 运行中:
http://localhost:3000 - 导入
flowise/chatflows/kb_chat.json - 配置 HTTP Request 节点中的 token(实际场景会从 overrideConfig 传递)
- 在前端"AI 助手对话" Tab 中,选择
chatflowType: 'kb' - 输入问题:"营业时间是几点?"
- 预期回答带 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_key7.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/MB | 1MB 文档 < 5s 完成 |
| 检索延迟 | < 800ms | Hybrid Search P95 < 800ms |
| Ask 延迟 | < 3s | RAG 问答 P95 < 3s |
| 引用覆盖率 | > 90% | Ask 回答带 citations >= 90% |
| 检索命中率 | > 70% | eval-set 40 题命中率 >= 70% |
九、后续优化(非 MVP)
短期(1-2 周)
- LangGraph 工作流接入:在
project_diagnosis_workflow、content_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: 检索结果不相关?
排查:
- 检查语料质量(是否太碎片、无标题层级)
- 调整 chunk_size(建议 800-1200)
- 尝试启用 rerank(
KB_RERANK_ENABLED=true) - 查看
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
十一、相关文档
- KKMUSIC_ARCHITECTURE_BLUEPRINT.md - 整体架构
- P3_FLOWISE_INSPIRATION_COMPLETE.md - Flowise 集成
- flowise/README.md - Flowise 使用指南
- flowise/vector-store-setup.md - Vector Store 配置
维护者:一刻工坊技术团队
最后更新:2024-12-14
状态:✅ MVP 完成,已验证摄取/检索/问答/引用/权限/审计闭环