# knowledge **Repository Path**: web/knowledge ## Basic Information - **Project Name**: knowledge - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-06 - **Last Updated**: 2026-08-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 企业 AI 知识库客服 一个基于 PHP 原生实现的企业知识库客服 Demo。前端提供聊天界面,后端调用 OpenAI-compatible Responses API,并通过 Qdrant 向量检索企业知识片段,支持会话历史记录。 ## 功能 - 流式输出 AI 回复 - 基于 Qdrant 的知识库向量检索 - MySQL 保存会话和消息历史 - 支持 OpenAI-compatible 聊天模型和 Embedding 模型 - 前端通过统一 `/api/v1/*` API 请求后端 - 提供知识库管理页、同步脚本和 Qdrant 集合初始化脚本 - 支持知识文档聚合、版本替换、历史版本查看和回滚 ## 运行环境 - PHP 8.0+ - PHP 扩展:`curl`、`pdo_mysql`、`mbstring` - MySQL 8.0+ - Qdrant - OpenAI-compatible API Key ## 配置 复制环境变量模板并填写真实配置: ```bash cp .env.example .env ``` 主要配置项: ```ini OPENAI_BASE_URL=https://api.openai.com OPENAI_API_KEY=your-openai-api-key OPENAI_MODEL=your-chat-model OPENAI_MAX_OUTPUT_TOKENS=4096 OPENAI_STREAM_TIMEOUT_SECONDS=0 DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=ai_business_demo DB_USERNAME=your-db-user DB_PASSWORD=your-db-password QDRANT_URL=http://127.0.0.1:6333 QDRANT_COLLECTION=knowledge_chunks QDRANT_SCORE_THRESHOLD=0.45 QDRANT_STRICT_TERM_SCORE_THRESHOLD=0.30 QDRANT_VECTOR_SIZE=4096 QDRANT_UPSERT_BATCH_SIZE=64 EMBEDDING_BASE_URL=https://api.openai.com EMBEDDING_API_KEY=your-embedding-api-key EMBEDDING_MODEL=your-embedding-model EMBEDDING_TIMEOUT_SECONDS=8 EMBEDDING_MAX_RETRIES=5 EMBEDDING_RETRY_BASE_SECONDS=2 EMBEDDING_SYNC_SLEEP_MS=0 KNOWLEDGE_SEARCH_LIMIT=3 KNOWLEDGE_SEARCH_CANDIDATE_LIMIT=8 KNOWLEDGE_CHUNK_MAX_LENGTH=800 KNOWLEDGE_CHUNK_OVERLAP=100 KNOWLEDGE_UPLOAD_MAX_BYTES=2097152 CHAT_HISTORY_LIMIT=50 ``` 如果 `OPENAI_BASE_URL` 或 `EMBEDDING_BASE_URL` 已经包含 `/v1`,代码会自动复用该路径;否则会自动拼接 `/v1`。 可按需要调整: - `OPENAI_INSTRUCTIONS`:客服助手系统提示词 - `OPENAI_MAX_OUTPUT_TOKENS`:单次回复最大输出 token - `OPENAI_STREAM_TIMEOUT_SECONDS`:流式回复读取超时,`0` 表示不限制 - `KNOWLEDGE_SEARCH_LIMIT`:每次问答最终使用的知识片段数量 - `KNOWLEDGE_SEARCH_CANDIDATE_LIMIT`:向量召回候选数量,用于再筛选后取最终片段 - `KNOWLEDGE_CHUNK_MAX_LENGTH` / `KNOWLEDGE_CHUNK_OVERLAP`:后台录入知识时的切片大小和重叠长度 - `KNOWLEDGE_UPLOAD_MAX_BYTES`:后台文件上传大小上限 - `QDRANT_VECTOR_SIZE` / `QDRANT_DISTANCE`:Qdrant 集合向量维度和距离算法 - `QDRANT_STRICT_TERM_SCORE_THRESHOLD`:命中关键术语时使用的最低相似度阈值 - `EMBEDDING_TIMEOUT_SECONDS` / `QDRANT_TIMEOUT_SECONDS`:问答链路的知识库检索超时;超时后会降级为无参考资料回答,不阻断聊天 - `EMBEDDING_MAX_RETRIES` / `EMBEDDING_RETRY_BASE_SECONDS`:Embedding 临时失败时的重试次数和线性退避基准秒数 - `EMBEDDING_SYNC_SLEEP_MS`:批量同步 Qdrant 时每次生成向量后的停顿毫秒数,用于降低限流风险 - `QDRANT_UPSERT_TIMEOUT_SECONDS`:后台录入或批量同步知识库时的向量写入超时 - `QDRANT_UPSERT_BATCH_SIZE`:命令行批量同步 Qdrant 时每批写入的 point 数量 - `CHAT_HISTORY_LIMIT` / `SESSION_LIST_LIMIT`:会话上下文和会话列表数量 ## 数据库表 创建业务数据库后,可执行 `database/schema.sql`。完整 SQL 如下: ```sql CREATE TABLE knowledge_chunks ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, document_id VARCHAR(64) NOT NULL DEFAULT '', title VARCHAR(255) NOT NULL, content TEXT NOT NULL, source VARCHAR(255) DEFAULT '', document_type VARCHAR(32) NOT NULL DEFAULT 'note', priority TINYINT UNSIGNED NOT NULL DEFAULT 50, version INT UNSIGNED NOT NULL DEFAULT 1, status VARCHAR(16) NOT NULL DEFAULT 'active', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_knowledge_chunks_document_id (document_id), INDEX idx_knowledge_chunks_document_status (document_id, status), INDEX idx_knowledge_chunks_document_version (document_id, version) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE chat_logs ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, session_id VARCHAR(64) NOT NULL, user_message TEXT NOT NULL, assistant_answer MEDIUMTEXT NOT NULL, model VARCHAR(128) NOT NULL, request_time_ms INT UNSIGNED DEFAULT 0, sources_json JSON NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_chat_logs_session_id (session_id), INDEX idx_chat_logs_created_at (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` 已有旧表可执行以下 SQL 手动补充参考资料快照列;应用保存或读取历史消息时也会自动检测并补列: ```sql ALTER TABLE chat_logs ADD COLUMN sources_json JSON NULL AFTER request_time_ms; ``` 已有旧知识表如果还没有文档 ID、资料类型和优先级字段,可执行以下 SQL,并为常见资料来源设置初始权重: ```sql ALTER TABLE knowledge_chunks ADD COLUMN document_id VARCHAR(64) NOT NULL DEFAULT '' AFTER id; CREATE INDEX idx_knowledge_chunks_document_id ON knowledge_chunks (document_id); UPDATE knowledge_chunks SET document_id = CONCAT('doc_', id) WHERE document_id = ''; ALTER TABLE knowledge_chunks ADD COLUMN document_type VARCHAR(32) NOT NULL DEFAULT 'note' AFTER source; ALTER TABLE knowledge_chunks ADD COLUMN priority TINYINT UNSIGNED NOT NULL DEFAULT 50 AFTER document_type; UPDATE knowledge_chunks SET document_type = 'policy' WHERE title LIKE '%退款%' OR source LIKE '%售后%'; UPDATE knowledge_chunks SET priority = 90 WHERE title LIKE '%退款%' OR source LIKE '%售后%'; UPDATE knowledge_chunks SET document_type = 'manual' WHERE source LIKE '%产品说明%'; UPDATE knowledge_chunks SET priority = 70 WHERE source LIKE '%产品说明%'; UPDATE knowledge_chunks SET document_type = 'test' WHERE source LIKE '%test%' OR title LIKE '%test%'; UPDATE knowledge_chunks SET priority = 30 WHERE source LIKE '%test%' OR title LIKE '%test%'; ``` 如果旧知识表已经有 `document_id`、`document_type`、`priority`,但还没有文档版本字段,可执行 `database/migrations/20260707_add_knowledge_document_versions.sql`: ```sql ALTER TABLE knowledge_chunks ADD COLUMN version INT UNSIGNED NOT NULL DEFAULT 1 AFTER priority; ALTER TABLE knowledge_chunks ADD COLUMN status VARCHAR(16) NOT NULL DEFAULT 'active' AFTER version; CREATE INDEX idx_knowledge_chunks_document_status ON knowledge_chunks (document_id, status); CREATE INDEX idx_knowledge_chunks_document_version ON knowledge_chunks (document_id, version); ``` `version` 用于记录同一 `document_id` 的历史版本;`status='active'` 的片段是当前检索和命令行同步到 Qdrant 的有效版本,替换或回滚文档时会把旧版本标记为 `archived`。 向 `knowledge_chunks` 写入企业资料后,可同步到 Qdrant。 也可以打开后台录入页直接写入 MySQL 并同步到 Qdrant: ```text http://127.0.0.1:8000/knowledge_admin.html ``` ## 初始化 Qdrant 启动 Qdrant 后,创建集合: ```bash php bin/qdrant_cli.php init ``` 默认 `QDRANT_VECTOR_SIZE=4096`。如果你的 Embedding 模型输出维度不是 4096,请先在 `.env` 中修改该值,再初始化集合。 同步 MySQL 中当前 `active` 的知识片段到 Qdrant: ```bash php bin/qdrant_cli.php sync ``` ## 本地启动 使用 PHP 内置服务器: ```bash php -S 127.0.0.1:8000 router.php ``` 然后打开: ```text http://127.0.0.1:8000 ``` ## API 路由 前端统一通过 API 路由访问后端,不直接请求具体 PHP 脚本: - `POST /api/v1/chat/stream`:流式问答 - `POST /api/v1/chat`:非流式问答 - `GET /api/v1/sessions`:会话列表 - `GET /api/v1/sessions/{session_id}/messages`:会话消息 - `POST /api/v1/knowledge-chunks`:保存知识并同步到 Qdrant - `GET /api/v1/knowledge-chunks`:最近知识片段列表 - `GET /api/v1/knowledge-chunks/{id}`:获取单条知识片段 - `PUT /api/v1/knowledge-chunks/{id}`:更新单条知识片段并同步 Qdrant - `DELETE /api/v1/knowledge-chunks/{id}`:删除单条知识片段并删除 Qdrant point - `POST /api/v1/knowledge-chunks/sync`:将 MySQL 知识片段重新同步到 Qdrant - `POST /api/v1/knowledge-files`:上传 `txt` 或 `md` 知识文件 - `GET /api/v1/knowledge-documents`:按 `document_id` 聚合的文档列表,返回当前版本、active 片段数和总片段数 - `DELETE /api/v1/knowledge-documents/{document_id}`:删除整篇文档及其所有版本片段 - `POST /api/v1/knowledge-documents/{document_id}/replace`:把当前 active 版本归档,并保存新版本片段 - `GET /api/v1/knowledge-documents/{document_id}/versions`:查看文档历史版本 - `POST /api/v1/knowledge-documents/{document_id}/versions/{version}/rollback`:把指定历史版本恢复为 active,并重建对应 Qdrant points ## 文件说明 - `router.php`:PHP 内置服务器路由脚本 - `database/schema.sql`:新数据库初始化 SQL - `database/migrations/20260707_add_knowledge_document_versions.sql`:旧知识表补充文档版本字段的迁移 SQL - `public/`:前端页面和静态资源 - `public/index.html`:聊天界面 - `public/knowledge_admin.html`:知识库录入和同步页面 - `public/assets/css/`:基础、聊天页、后台页样式 - `public/assets/js/api.js`:前端 API 客户端 - `public/assets/js/chat-renderer.js`:聊天消息和参考资料渲染 - `public/assets/js/chat.js`:聊天页交互入口 - `public/assets/js/knowledge-admin.js`:知识库管理页交互入口 - `src/`:后端 API 和公共方法 - `src/api.php`:统一 API 路由入口 - `src/api_handlers.php`:API handler 实现 - `src/app_helper.php`:环境读取、JSON 响应、PDO、OpenAI 请求等公共方法 - `src/knowledge_helper.php`:知识切片、Embedding、Qdrant、知识上下文构造等公共方法 - `bin/qdrant_cli.php`:Qdrant 集合初始化和知识库同步脚本 ## 安全说明 `.env`、日志、依赖目录、本地缓存和数据库文件已通过 `.gitignore` 排除。不要将真实 API Key、数据库密码或生产数据提交到 Git。