From 6f48e356b9720f905b13625ad28d92fd7220af19 Mon Sep 17 00:00:00 2001
From: wizardchen
Date: Mon, 30 Mar 2026 21:20:14 +0800
Subject: [PATCH] feat: update readme & docs
---
README.md | 113 +-
README_CN.md | 168 +--
README_JA.md | 136 +--
README_KO.md | 136 +--
docs/docs.go | 2145 ++++++++++++++++++++++++++++++++++++--
docs/images/agent-qa.png | Bin 964324 -> 559962 bytes
docs/images/qa.png | Bin 87783 -> 248094 bytes
docs/images/settings.png | Bin 264438 -> 167277 bytes
docs/swagger.json | 2145 ++++++++++++++++++++++++++++++++++++--
docs/swagger.yaml | 1491 ++++++++++++++++++++++++--
go.mod | 4 +-
11 files changed, 5820 insertions(+), 518 deletions(-)
diff --git a/README.md b/README.md
index 95aaea3e..b265ae3f 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@
-| English | 简体中文 | 日本語 |
+| English | 简体中文 | 日本語 | 한국어 |
@@ -223,21 +223,17 @@ cp .env.example .env
# All variables are documented in the .env.example comments
```
-#### ③ Start the services (include Ollama)
+#### ③ Start the core services
-Check the images that need to be started in the .env file.
+Check which images need to be started in the `.env` file, then start the WeKnora core services with Docker Compose.
```bash
-./scripts/start_all.sh
+docker compose up -d
```
-or
+#### ③.0 Start Ollama separately (Optional)
-```bash
-make start-all
-```
-
-#### ③.0 Start ollama services (Optional)
+If you configured a local Ollama model in `.env`, start the Ollama service separately:
```bash
ollama serve > /dev/null 2>&1 &
@@ -252,35 +248,33 @@ docker compose up -d
- All features enabled
```bash
-docker-compose --profile full up -d
+docker compose --profile full up -d
```
- Tracing logs required
```bash
-docker-compose --profile jaeger up -d
+docker compose --profile jaeger up -d
```
- Neo4j knowledge graph required
```bash
-docker-compose --profile neo4j up -d
+docker compose --profile neo4j up -d
```
- Minio file storage service required
```bash
-docker-compose --profile minio up -d
+docker compose --profile minio up -d
```
- Multiple options combination
```bash
-docker-compose --profile neo4j --profile minio up -d
+docker compose --profile neo4j --profile minio up -d
```
#### ④ Stop the services
```bash
-./scripts/start_all.sh --stop
-# Or
-make stop-all
+docker compose down
```
### 🌐 Access Services
@@ -291,6 +285,39 @@ Once started, services will be available at:
* Backend API: `http://localhost:8080`
* Jaeger Tracing: `http://localhost:16686`
+## 📱 Interface Showcase
+
+### Web UI Interface
+
+
+
+ Knowledge Base Management
+ Conversation Settings
+
+
+ Intelligent Q&A Conversation
+
+
+ Agent Mode Tool Call Process
+
+
+
+**Knowledge Base Management:** Support for creating FAQ and document knowledge base types, with multiple import methods including drag-and-drop, folder import, and URL import. Automatically identifies document structures and extracts core knowledge to establish indexes. Supports tag management and online entry. The system clearly displays processing progress and document status, achieving efficient knowledge base management.
+
+**Agent Mode:** Support for ReACT Agent mode that can use built-in tools to retrieve knowledge bases, call user-configured MCP tools and web search tools to access external services, providing comprehensive summary reports through multiple iterations and reflection. Supports cross-knowledge base retrieval, allowing selection of multiple knowledge bases for simultaneous retrieval.
+
+**Conversation Strategy:** Support for configuring Agent models, normal mode models, retrieval thresholds, and online Prompt configuration, with precise control over multi-turn conversation behavior and retrieval execution methods. The conversation input box supports Agent mode/normal mode switching, enabling/disabling web search, and selecting conversation models.
+
+### Document Knowledge Graph
+
+WeKnora supports transforming documents into knowledge graphs, displaying the relationships between different sections of the documents. Once the knowledge graph feature is enabled, the system analyzes and constructs an internal semantic association network that not only helps users understand document content but also provides structured support for indexing and retrieval, enhancing the relevance and breadth of search results.
+
+For detailed configuration, please refer to the [Knowledge Graph Configuration Guide](./docs/KnowledgeGraph.md).
+
+### MCP Server
+
+Please refer to the [MCP Configuration Guide](./mcp-server/MCP_CONFIG.md) for the necessary setup.
+
### 🔌 Using WeChat Dialog Open Platform
WeKnora serves as the core technology framework for the [WeChat Dialog Open Platform](https://chatbot.weixin.qq.com), providing a more convenient usage approach:
@@ -341,7 +368,7 @@ If this is your first time using this project, you can skip steps ①② and go
### ① Stop the services
```bash
-./scripts/start_all.sh --stop
+docker compose down
```
### ② Clear existing data tables (recommended when no important data exists)
@@ -353,7 +380,7 @@ make clean-db
### ③ Compile and start services
```bash
-./scripts/start_all.sh
+docker compose up -d --build
```
### ④ Access Web UI
@@ -362,36 +389,6 @@ http://localhost
On your first visit, you will be automatically redirected to the registration/login page. After completing registration, please create a new knowledge base and finish the relevant settings on its configuration page.
-## 📱 Interface Showcase
-
-### Web UI Interface
-
-
-
- Knowledge Base Management
- Conversation Settings
-
-
- Agent Mode Tool Call Process
-
-
-
-**Knowledge Base Management:** Support for creating FAQ and document knowledge base types, with multiple import methods including drag-and-drop, folder import, and URL import. Automatically identifies document structures and extracts core knowledge to establish indexes. Supports tag management and online entry. The system clearly displays processing progress and document status, achieving efficient knowledge base management.
-
-**Agent Mode:** Support for ReACT Agent mode that can use built-in tools to retrieve knowledge bases, call user-configured MCP tools and web search tools to access external services, providing comprehensive summary reports through multiple iterations and reflection. Supports cross-knowledge base retrieval, allowing selection of multiple knowledge bases for simultaneous retrieval.
-
-**Conversation Strategy:** Support for configuring Agent models, normal mode models, retrieval thresholds, and online Prompt configuration, with precise control over multi-turn conversation behavior and retrieval execution methods. The conversation input box supports Agent mode/normal mode switching, enabling/disabling web search, and selecting conversation models.
-
-### Document Knowledge Graph
-
-WeKnora supports transforming documents into knowledge graphs, displaying the relationships between different sections of the documents. Once the knowledge graph feature is enabled, the system analyzes and constructs an internal semantic association network that not only helps users understand document content but also provides structured support for indexing and retrieval, enhancing the relevance and breadth of search results.
-
-For detailed configuration, please refer to the [Knowledge Graph Configuration Guide](./docs/KnowledgeGraph.md).
-
-### MCP Server
-
-Please refer to the [MCP Configuration Guide](./mcp-server/MCP_CONFIG.md) for the necessary setup.
-
## 📘 API Reference
Troubleshooting FAQ: [Troubleshooting FAQ](./docs/QA.md)
@@ -407,18 +404,14 @@ Product plans and upcoming features: [Roadmap](./docs/ROADMAP.md)
If you need to frequently modify code, **you don't need to rebuild Docker images every time**! Use fast development mode:
```bash
-# Method 1: Using Make commands (Recommended)
-make dev-start # Start infrastructure
-make dev-app # Start backend (new terminal)
-make dev-frontend # Start frontend (new terminal)
+# Start infrastructure
+make dev-start
-# Method 2: One-click start
-./scripts/quick-dev.sh
+# Start backend (new terminal)
+make dev-app
-# Method 3: Using scripts
-./scripts/dev.sh start # Start infrastructure
-./scripts/dev.sh app # Start backend (new terminal)
-./scripts/dev.sh frontend # Start frontend (new terminal)
+# Start frontend (new terminal)
+make dev-frontend
```
**Development Advantages:**
diff --git a/README_CN.md b/README_CN.md
index 47a7a961..d1a8556d 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -1,18 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+| English | 简体中文 | 日本語 | 한국어 |
+
-
-
-
-| **[English](./README.md)** | **简体中文** | **[日本語](./README_JA.md)** |
-
-
-
-
+
+
[项目介绍](#-项目介绍) • [架构设计](#-架构设计) • [核心特性](#-核心特性) • [快速开始](#-快速开始) • [文档](#-文档) • [开发指南](#-开发指南)
-
+
+
# 💡 WeKnora - 基于大模型的文档理解检索框架
@@ -50,7 +74,8 @@
- **混合检索优化**:按目标分组并复用查询向量,提升检索性能
- **Final Answer工具**:新增final_answer工具及Agent耗时跟踪,优化Agent工作流
-**更早版本**
+
+更早版本
**v0.3.3 版本亮点:**
@@ -102,7 +127,7 @@
- 🎨 **全新UI**:优化对话界面,支持Agent模式/普通模式切换,展示工具调用过程,知识库管理界面全面升级
- ⚡ **底层升级**:引入MQ异步任务管理,支持数据库自动迁移,提供快速开发模式
-
+
## 🔒 安全声明
@@ -115,7 +140,7 @@
## 🏗️ 架构设计
-weknora-pipelone.png
+
WeKnora 采用现代化模块化设计,构建了一条完整的文档理解与检索流水线。系统主要包括文档解析、向量化处理、检索引擎和大模型推理等核心模块,每个组件均可灵活配置与扩展。
@@ -202,21 +227,17 @@ cp .env.example .env
# 所有变量说明详见 .env.example 注释
```
-#### ③ 启动服务 (含 Ollama)
+#### ③ 启动主服务
-检查 .env 文件中需要启动的镜像。
+检查 `.env` 文件中需要启动的镜像,然后使用 Docker Compose 启动 WeKnora 主服务。
```bash
-./scripts/start_all.sh
+docker compose up -d
```
-或者
+#### ③.0 单独启动 Ollama(可选)
-```bash
-make start-all
-```
-
-#### ③.0 启动Ollama (可选)
+如果你在 `.env` 中配置了本地 Ollama 模型,还需要额外启动 Ollama 服务:
```bash
ollama serve > /dev/null 2>&1 &
@@ -233,39 +254,37 @@ docker compose up -d
- 启动全部功能
```bash
-docker-compose --profile full up -d
+docker compose --profile full up -d
```
- 需要 tracing 日志
```bash
-docker-compose --profile jaeger up -d
+docker compose --profile jaeger up -d
```
- 需要 neo4j 知识图谱
```bash
-docker-compose --profile neo4j up -d
+docker compose --profile neo4j up -d
```
- 需要 minio 文件存储服务
```bash
-docker-compose --profile minio up -d
+docker compose --profile minio up -d
```
- 多选项组合
```bash
-docker-compose --profile neo4j --profile minio up -d
+docker compose --profile neo4j --profile minio up -d
```
#### ④ 停止服务
```bash
-./scripts/start_all.sh --stop
-# 或
-make stop-all
+docker compose down
```
### 🌐 服务访问地址
@@ -276,6 +295,40 @@ make stop-all
- 后端 API:`http://localhost:8080`
- 链路追踪(Jaeger):`http://localhost:16686`
+## 📱 功能展示
+
+### Web UI 界面
+
+
+
+ 知识库管理
+ 对话设置
+
+
+ 智能问答对话
+
+
+ Agent模式工具调用过程
+
+
+
+
+**知识库管理:** 支持创建FAQ和文档两种类型知识库,支持拖拽上传、文件夹导入、URL导入等多种方式,自动识别文档结构并提取核心知识,建立索引。支持标签管理和在线录入,系统清晰展示处理进度和文档状态,实现高效的知识库管理。
+
+**Agent模式:** 支持开启ReACT Agent模式,可使用内置工具检索知识库,调用用户配置的MCP工具和网络搜索工具访问外部服务,通过多次迭代和反思,最终给出全面的总结报告。支持跨知识库检索,可以选择多个知识库同时检索。
+
+**对话策略:** 支持配置Agent模型、普通模式所需的模型、检索阈值,支持在线配置Prompt,精确控制多轮对话行为和检索召回执行方式。对话输入框支持Agent模式/普通模式切换,支持开启和关闭网络搜索,支持选择对话模型。
+
+### 文档知识图谱
+
+WeKnora 支持将文档转化为知识图谱,展示文档中不同段落之间的关联关系。开启知识图谱功能后,系统会分析并构建文档内部的语义关联网络,不仅帮助用户理解文档内容,还为索引和检索提供结构化支撑,提升检索结果的相关性和广度。
+
+具体配置请参考 [知识图谱配置说明](./docs/KnowledgeGraph.md) 进行相关配置。
+
+### 配套MCP服务器
+
+请参考 [MCP配置说明](./mcp-server/MCP_CONFIG.md) 进行相关配置。
+
### 🔌 使用微信对话开放平台
WeKnora 作为[微信对话开放平台](https://chatbot.weixin.qq.com)的核心技术框架,提供更简便的使用方式:
@@ -330,7 +383,7 @@ python -m weknora-mcp-server
### ① 关闭服务
```bash
-./scripts/start_all.sh --stop
+docker compose down
```
### ② 清空原有数据表(建议在没有重要数据的情况下使用)
@@ -342,7 +395,7 @@ make clean-db
### ③ 编译并启动服务
```bash
-./scripts/start_all.sh
+docker compose up -d --build
```
### ④ 访问Web UI
@@ -351,33 +404,6 @@ make clean-db
首次访问会自动跳转到注册登录页面,完成注册后,请创建一个新的知识库,并在该知识库的设置页面完成相关设置。
-## 📱 功能展示
-
-### Web UI 界面
-
-
-| | |
-| ----------------- | -------- |
-| **知识库管理** | **对话设置** |
-| **Agent模式工具调用过程** | |
-
-
-**知识库管理:** 支持创建FAQ和文档两种类型知识库,支持拖拽上传、文件夹导入、URL导入等多种方式,自动识别文档结构并提取核心知识,建立索引。支持标签管理和在线录入,系统清晰展示处理进度和文档状态,实现高效的知识库管理。
-
-**Agent模式:** 支持开启ReACT Agent模式,可使用内置工具检索知识库,调用用户配置的MCP工具和网络搜索工具访问外部服务,通过多次迭代和反思,最终给出全面的总结报告。支持跨知识库检索,可以选择多个知识库同时检索。
-
-**对话策略:** 支持配置Agent模型、普通模式所需的模型、检索阈值,支持在线配置Prompt,精确控制多轮对话行为和检索召回执行方式。对话输入框支持Agent模式/普通模式切换,支持开启和关闭网络搜索,支持选择对话模型。
-
-### 文档知识图谱
-
-WeKnora 支持将文档转化为知识图谱,展示文档中不同段落之间的关联关系。开启知识图谱功能后,系统会分析并构建文档内部的语义关联网络,不仅帮助用户理解文档内容,还为索引和检索提供结构化支撑,提升检索结果的相关性和广度。
-
-具体配置请参考 [知识图谱配置说明](./docs/KnowledgeGraph.md) 进行相关配置。
-
-### 配套MCP服务器
-
-请参考 [MCP配置说明](./mcp-server/MCP_CONFIG.md) 进行相关配置。
-
## 📘 文档
常见问题排查:[常见问题排查](./docs/QA.md)
@@ -393,18 +419,14 @@ WeKnora 支持将文档转化为知识图谱,展示文档中不同段落之间
如果你需要频繁修改代码,**不需要每次重新构建 Docker 镜像**!使用快速开发模式:
```bash
-# 方式 1:使用 Make 命令(推荐)
-make dev-start # 启动基础设施
-make dev-app # 启动后端(新终端)
-make dev-frontend # 启动前端(新终端)
+# 启动基础设施
+make dev-start
-# 方式 2:一键启动
-./scripts/quick-dev.sh
+# 启动后端(新终端)
+make dev-app
-# 方式 3:使用脚本
-./scripts/dev.sh start # 启动基础设施
-./scripts/dev.sh app # 启动后端(新终端)
-./scripts/dev.sh frontend # 启动前端(新终端)
+# 启动前端(新终端)
+make dev-frontend
```
**开发优势:**
@@ -476,7 +498,7 @@ refactor: 重构文档解析模块
感谢以下优秀的贡献者们:
-[Contributors](https://github.com/Tencent/WeKnora/graphs/contributors)
+[](https://github.com/Tencent/WeKnora/graphs/contributors)
## 📄 许可证
@@ -485,3 +507,11 @@ refactor: 重构文档解析模块
## 📈 项目统计
+
+
+
+
+
+
+
+
diff --git a/README_JA.md b/README_JA.md
index a906c4a0..5c4412b6 100644
--- a/README_JA.md
+++ b/README_JA.md
@@ -27,7 +27,7 @@
-| English | 简体中文 | 日本語 |
+| English | 简体中文 | 日本語 | 한국어 |
@@ -86,6 +86,21 @@
- **バケット自動作成**:ストレージエンジン接続チェックの強化、バケットの自動作成をサポート
- **Milvusベクトルデータベース**:ナレッジ検索用にMilvusベクトルデータベースバックエンドを追加
+**v0.3.2 バージョンのハイライト:**
+
+- 🔍 **ナレッジ検索**:新しい「ナレッジ検索」エントリポイント、セマンティック検索をサポートし、検索結果を直接会話ウィンドウに持ち込み可能
+- ⚙️ **パーサーとストレージエンジンの設定**:設定画面でソースごとのドキュメントパーサーとストレージエンジンを設定可能、ナレッジベースでファイルタイプ別のパーサー選択をサポート
+- 🖼️ **ローカルストレージ画像レンダリング**:ローカルストレージモードで会話中の画像レンダリングをサポート、ストリーミング中の画像プレースホルダーを最適化
+- 📄 **ドキュメントプレビュー**:ユーザーがアップロードした元のファイルをプレビューする組み込みドキュメントプレビューコンポーネント
+- 🎨 **UI最適化**:ナレッジベース、エージェント、共有スペースリストページのインタラクションを再設計
+- 🗄️ **Milvusサポート**:ナレッジ検索用にMilvusベクトルデータベースバックエンドを追加
+- 🌋 **Volcengine TOS**:Volcengine TOSオブジェクトストレージサポートを追加
+- 📊 **Mermaidレンダリング**:チャットでMermaidダイアグラムのレンダリングをサポート、フルスクリーンビューアー、ズーム、パン、ツールバー、エクスポート機能付き
+- 💬 **バッチ会話管理**:バッチ管理と全セッション一括削除機能
+- 🔗 **リモートURLナレッジ**:リモートファイルURLからナレッジエントリの作成をサポート
+- 🧠 **メモリグラフプレビュー**:ユーザーレベルのメモリグラフ可視化プレビュー
+- 🔄 **非同期再解析**:既存のナレッジドキュメントの非同期再処理API
+
**v0.3.0 バージョンのハイライト:**
- 🏢 **共有スペース**:共有スペース管理、メンバー招待、メンバー間でのナレッジベースとAgentの共有、テナント分離検索
@@ -125,7 +140,7 @@
## 🏗️ アーキテクチャ設計
-
+
WeKnoraは現代的なモジュラー設計を採用し、完全な文書理解と検索パイプラインを構築しています。システムには主に文書解析、ベクトル化処理、検索エンジン、大規模モデル推論などのコアモジュールが含まれ、各コンポーネントは柔軟に設定および拡張できます。
@@ -208,21 +223,17 @@ cp .env.example .env
# すべての変数の説明は.env.exampleのコメントを参照
```
-#### ③ サービスを起動します(Ollama を含む)
+#### ③ メインサービスを起動します
-.env ファイルで、起動する必要があるイメージを確認します。
+`.env` ファイルで起動する必要があるイメージを確認し、Docker Compose で WeKnora のメインサービスを起動します。
```bash
-./scripts/start_all.sh
+docker compose up -d
```
-または
+#### ③.0 Ollama を個別に起動する (オプション)
-```bash
-make start-all
-```
-
-#### ③.0 ollama サービスを起動する (オプション)
+`.env` でローカル Ollama モデルを設定している場合は、追加で Ollama サービスを起動してください。
```bash
ollama serve > /dev/null 2>&1 &
@@ -237,35 +248,33 @@ docker compose up -d
- すべての機能を有効にする
```bash
-docker-compose --profile full up -d
+docker compose --profile full up -d
```
- トレースログが必要
```bash
-docker-compose --profile jaeger up -d
+docker compose --profile jaeger up -d
```
- Neo4j ナレッジグラフが必要
```bash
-docker-compose --profile neo4j up -d
+docker compose --profile neo4j up -d
```
- Minio ファイルストレージサービスが必要
```bash
-docker-compose --profile minio up -d
+docker compose --profile minio up -d
```
- 複数のオプションの組み合わせ
```bash
-docker-compose --profile neo4j --profile minio up -d
+docker compose --profile neo4j --profile minio up -d
```
#### ④ サービスの停止
```bash
-./scripts/start_all.sh --stop
-# または
-make stop-all
+docker compose down
```
### 🌐 サービスアクセスアドレス
@@ -276,6 +285,40 @@ make stop-all
* バックエンドAPI:`http://localhost:8080`
* リンクトレース(Jaeger):`http://localhost:16686`
+## 📱 機能デモ
+
+### Web UIインターフェース
+
+
+
+ ナレッジベース管理
+ 対話設定
+
+
+ インテリジェントQ&A対話
+
+
+ Agentモードツール呼び出しプロセス
+
+
+
+**ナレッジベース管理:** FAQとドキュメントの2種類のナレッジベースの作成をサポート、ドラッグ&ドロップアップロード、フォルダーインポート、URLインポートなど複数の方法をサポート、文書構造を自動認識してコア知識を抽出し、インデックスを構築します。タグ管理とオンライン入力をサポート、システムは処理の進行状況と文書のステータスを明確に表示し、効率的なナレッジベース管理を実現します。
+
+**Agentモード:** ReACT Agentモードの有効化をサポート、組み込みツールでナレッジベースを検索、ユーザーが設定したMCPツールとWeb検索ツールを呼び出して外部サービスにアクセス、複数回の反復とリフレクションを通じて、最終的に包括的なサマリーレポートを提供します。クロスナレッジベース検索をサポート、複数のナレッジベースを同時に検索できます。
+
+**対話戦略:** Agentモデル、通常モードに必要なモデル、検索閾値の設定をサポート、オンラインPrompt設定をサポート、マルチターン対話の動作と検索リコールの実行方法を精密に制御します。対話入力ボックスはAgentモード/通常モードの切り替えをサポート、Web検索の有効化/無効化をサポート、対話モデルの選択をサポートします。
+
+### 文書ナレッジグラフ
+
+WeKnoraは文書をナレッジグラフに変換し、文書内の異なる段落間の関連関係を表示することをサポートします。ナレッジグラフ機能を有効にすると、システムは文書内部の意味関連ネットワークを分析・構築し、ユーザーが文書内容を理解するのを助けるだけでなく、インデックスと検索に構造化サポートを提供し、検索結果の関連性と幅を向上させます。
+
+詳細な設定については、[ナレッジグラフ設定ガイド](./docs/KnowledgeGraph.md)をご参照ください。
+
+### 対応するMCPサーバー
+
+[MCP設定ガイド](./mcp-server/MCP_CONFIG.md) をご参照のうえ、必要な設定を行ってください。
+
+
### 🔌 WeChat対話オープンプラットフォームの使用
WeKnoraは[WeChat対話オープンプラットフォーム](https://chatbot.weixin.qq.com)のコア技術フレームワークとして、より簡単な使用方法を提供します:
@@ -327,7 +370,7 @@ python -m weknora-mcp-server
### ① サービスの停止
```bash
-./scripts/start_all.sh --stop
+docker compose down
```
### ② 既存のデータテーブルをクリア(重要なデータがない場合の推奨)
@@ -339,7 +382,7 @@ make clean-db
### ③ コンパイルしてサービスを起動
```bash
-./scripts/start_all.sh
+docker compose up -d --build
```
### ④ Web UIにアクセス
@@ -348,43 +391,14 @@ http://localhost
初回アクセス時は自動的に登録・ログインページに遷移します。登録完了後、新規にナレッジベースを作成し、その設定画面で必要な項目を構成してください。
-## 📱 機能デモ
-
-### Web UIインターフェース
-
-
-
- ナレッジベース管理
- 対話設定
-
-
- Agentモードツール呼び出しプロセス
-
-
-
-**ナレッジベース管理:** FAQとドキュメントの2種類のナレッジベースの作成をサポート、ドラッグ&ドロップアップロード、フォルダーインポート、URLインポートなど複数の方法をサポート、文書構造を自動認識してコア知識を抽出し、インデックスを構築します。タグ管理とオンライン入力をサポート、システムは処理の進行状況と文書のステータスを明確に表示し、効率的なナレッジベース管理を実現します。
-
-**Agentモード:** ReACT Agentモードの有効化をサポート、組み込みツールでナレッジベースを検索、ユーザーが設定したMCPツールとWeb検索ツールを呼び出して外部サービスにアクセス、複数回の反復とリフレクションを通じて、最終的に包括的なサマリーレポートを提供します。クロスナレッジベース検索をサポート、複数のナレッジベースを同時に検索できます。
-
-**対話戦略:** Agentモデル、通常モードに必要なモデル、検索閾値の設定をサポート、オンラインPrompt設定をサポート、マルチターン対話の動作と検索リコールの実行方法を精密に制御します。対話入力ボックスはAgentモード/通常モードの切り替えをサポート、Web検索の有効化/無効化をサポート、対話モデルの選択をサポートします。
-
-### 文書ナレッジグラフ
-
-WeKnoraは文書をナレッジグラフに変換し、文書内の異なる段落間の関連関係を表示することをサポートします。ナレッジグラフ機能を有効にすると、システムは文書内部の意味関連ネットワークを分析・構築し、ユーザーが文書内容を理解するのを助けるだけでなく、インデックスと検索に構造化サポートを提供し、検索結果の関連性と幅を向上させます。
-
-詳細な設定については、[ナレッジグラフ設定ガイド](./docs/KnowledgeGraph.md)をご参照ください。
-
-### 対応するMCPサーバー
-
-[MCP設定ガイド](./mcp-server/MCP_CONFIG.md) をご参照のうえ、必要な設定を行ってください。
-
-
## 📘 ドキュメント
よくある問題の解決:[よくある問題](./docs/QA.md)
詳細なAPIドキュメントは:[APIドキュメント](./docs/api/README.md)を参照してください
+製品計画と今後の機能:[Roadmap](./docs/ROADMAP.md)
+
## 🧭 開発ガイド
### ⚡ 高速開発モード(推奨)
@@ -392,18 +406,14 @@ WeKnoraは文書をナレッジグラフに変換し、文書内の異なる段
コードを頻繁に変更する必要がある場合、**Dockerイメージを毎回再構築する必要はありません**!高速開発モードを使用してください:
```bash
-# 方法1:Makeコマンドを使用(推奨)
-make dev-start # インフラストラクチャを起動
-make dev-app # バックエンドを起動(新しいターミナル)
-make dev-frontend # フロントエンドを起動(新しいターミナル)
+# インフラストラクチャを起動
+make dev-start
-# 方法2:ワンクリック起動
-./scripts/quick-dev.sh
+# バックエンドを起動(新しいターミナル)
+make dev-app
-# 方法3:スクリプトを使用
-./scripts/dev.sh start # インフラストラクチャを起動
-./scripts/dev.sh app # バックエンドを起動(新しいターミナル)
-./scripts/dev.sh frontend # フロントエンドを起動(新しいターミナル)
+# フロントエンドを起動(新しいターミナル)
+make dev-frontend
```
**開発の利点:**
@@ -474,7 +484,7 @@ refactor: 文書解析モジュールをリファクタリング
素晴らしいコントリビューターに感謝します:
-[](https://github.com/Tencent/WeKnora/graphs/contributors )
+[](https://github.com/Tencent/WeKnora/graphs/contributors)
## 📄 ライセンス
diff --git a/README_KO.md b/README_KO.md
index 483429c4..ec0e0a47 100644
--- a/README_KO.md
+++ b/README_KO.md
@@ -27,7 +27,7 @@
-| English | 简体中文 | 한국어 |
+| English | 简体中文 | 日本語 | 한국어 |
@@ -86,15 +86,36 @@
- **버킷 자동 생성**: 스토리지 엔진 연결 확인 강화, 버킷 자동 생성 지원
- **Milvus 벡터 데이터베이스**: 지식 검색을 위한 Milvus 벡터 데이터베이스 백엔드 추가
+**v0.3.2 하이라이트:**
+
+- 🔍 **지식 검색**: 시맨틱 검색을 지원하는 새로운 "지식 검색" 진입점, 검색 결과를 대화 창으로 바로 가져오기 지원
+- ⚙️ **파서 및 스토리지 엔진 설정**: 설정에서 소스별 문서 파서 엔진과 스토리지 엔진 구성 가능, 지식베이스에서 파일 타입별 파서 선택 지원
+- 🖼️ **로컬 스토리지 이미지 렌더링**: 로컬 스토리지 모드에서 대화 중 이미지 렌더링 지원, 스트리밍 이미지 플레이스홀더 최적화
+- 📄 **문서 미리보기**: 사용자가 업로드한 원본 파일을 미리 볼 수 있는 내장 문서 미리보기 컴포넌트
+- 🎨 **UI 최적화**: 지식베이스, 에이전트, 공유 공간 목록 페이지 인터랙션 개편
+- 🗄️ **Milvus 지원**: 지식 검색을 위한 Milvus 벡터 데이터베이스 백엔드 추가
+- 🌋 **Volcengine TOS**: Volcengine TOS 오브젝트 스토리지 지원 추가
+- 📊 **Mermaid 렌더링**: 채팅에서 Mermaid 다이어그램 렌더링 지원, 전체 화면 뷰어/줌/내비게이션/내보내기 기능 포함
+- 💬 **대화 일괄 관리**: 일괄 관리 및 전체 세션 삭제 기능
+- 🔗 **원격 URL 지식**: 원격 파일 URL로 지식 항목 생성 지원
+- 🧠 **메모리 그래프 미리보기**: 사용자 레벨 메모리 그래프 시각화 미리보기
+- 🔄 **비동기 재파싱**: 기존 지식 문서를 비동기로 재처리하는 API
+
**v0.3.0 하이라이트:**
- 🏢 **공유 공간**: 멤버 초대, 멤버 간 지식베이스/에이전트 공유, 테넌트 격리 검색을 지원하는 공유 공간
- 🧩 **Agent Skills**: 스마트 추론 에이전트를 위한 사전 로드 스킬과 샌드박스 기반 보안 격리 실행 환경 제공
- 🤖 **커스텀 에이전트**: 지식베이스 선택 모드(전체/지정/비활성화)와 함께 커스텀 에이전트 생성, 설정, 선택 지원
+- 📊 **데이터 분석 에이전트**: 내장 데이터 분석 에이전트, CSV/Excel 분석용 DataSchema 도구
- 🧠 **사고 모드**: LLM과 에이전트의 사고 모드 지원 및 사고 내용 지능형 필터링
- 🔍 **웹 검색 제공자**: DuckDuckGo 외에 Bing, Google 검색 제공자 추가
+- 📋 **FAQ 강화**: 일괄 임포트 드라이런, 유사 질문, 검색 결과 매칭 질문 필드, 대량 임포트 오브젝트 스토리지 오프로드
+- 🔑 **API Key 인증**: API Key 인증 메커니즘, Swagger 문서 보안 설정
+- 📎 **입력창 내 선택**: 입력창에서 지식베이스와 파일을 직접 선택, @멘션 표시
- ☸️ **Helm Chart**: Neo4j GraphRAG 지원을 포함한 Kubernetes 배포용 완전한 Helm Chart 제공
-- 🔒 **보안 강화**: SSRF 안전 HTTP 클라이언트, 향상된 SQL 검증, MCP stdio 전송 보안
+- 🌍 **국제화**: 한국어(한국어) 지원 추가
+- 🔒 **보안 강화**: SSRF 안전 HTTP 클라이언트, 향상된 SQL 검증, MCP stdio 전송 보안, 샌드박스 기반 실행
+- ⚡ **인프라**: Qdrant 벡터 데이터베이스 지원, Redis ACL, 로그 레벨 설정, Ollama 임베딩 최적화, `DISABLE_REGISTRATION` 제어
**v0.2.0 하이라이트:**
@@ -202,21 +223,17 @@ cp .env.example .env
# 모든 변수는 .env.example 주석에 설명되어 있습니다
```
-#### ③ 서비스 시작(Ollama 포함)
+#### ③ 메인 서비스 시작
-.env 파일에서 시작해야 하는 이미지를 확인하세요.
+`.env` 파일에서 시작할 이미지를 확인한 뒤 Docker Compose로 WeKnora 메인 서비스를 시작하세요.
```bash
-./scripts/start_all.sh
+docker compose up -d
```
-또는
+#### ③.0 Ollama 별도 시작(선택)
-```bash
-make start-all
-```
-
-#### ③.0 ollama 서비스 시작(선택)
+`.env` 에 로컬 Ollama 모델을 설정했다면 Ollama 서비스도 별도로 시작해야 합니다.
```bash
ollama serve > /dev/null 2>&1 &
@@ -231,35 +248,33 @@ docker compose up -d
- 전체 기능 활성화
```bash
-docker-compose --profile full up -d
+docker compose --profile full up -d
```
- 트레이싱 로그 필요 시
```bash
-docker-compose --profile jaeger up -d
+docker compose --profile jaeger up -d
```
- Neo4j 지식 그래프 필요 시
```bash
-docker-compose --profile neo4j up -d
+docker compose --profile neo4j up -d
```
- Minio 파일 스토리지 필요 시
```bash
-docker-compose --profile minio up -d
+docker compose --profile minio up -d
```
- 여러 옵션 조합
```bash
-docker-compose --profile neo4j --profile minio up -d
+docker compose --profile neo4j --profile minio up -d
```
#### ④ 서비스 중지
```bash
-./scripts/start_all.sh --stop
-# 또는
-make stop-all
+docker compose down
```
### 🌐 서비스 접속 주소
@@ -270,6 +285,39 @@ make stop-all
* 백엔드 API: `http://localhost:8080`
* Jaeger 트레이싱: `http://localhost:16686`
+## 📱 인터페이스 소개
+
+### Web UI 인터페이스
+
+
+
+ 지식베이스 관리
+ 대화 설정
+
+
+ 지능형 Q&A 대화
+
+
+ Agent 모드 도구 호출 과정
+
+
+
+**지식베이스 관리:** FAQ/문서 지식베이스 타입 생성 지원, 드래그 앤 드롭/폴더/URL 임포트 등 다양한 방식 지원. 문서 구조를 자동 식별하고 핵심 지식을 추출해 인덱스를 구축합니다. 태그 관리와 온라인 입력을 지원하며, 처리 진행 상황과 문서 상태를 명확히 표시해 효율적인 지식베이스 운영을 돕습니다.
+
+**Agent 모드:** ReACT Agent 모드를 지원하며, 내장 도구로 지식베이스 검색, 사용자 설정 MCP 도구 및 웹 검색 도구 호출을 통해 외부 서비스 접근이 가능합니다. 다중 반복과 리플렉션을 통해 종합 요약 리포트를 제공합니다. 교차 지식베이스 검색도 지원하여 여러 지식베이스를 동시에 검색할 수 있습니다.
+
+**대화 전략:** Agent 모델, 일반 모드 모델, 검색 임계값, 온라인 프롬프트 설정을 지원하여 멀티턴 대화 동작과 검색 실행 방식을 정밀하게 제어할 수 있습니다. 입력창에서 Agent/일반 모드 전환, 웹 검색 활성화/비활성화, 대화 모델 선택을 지원합니다.
+
+### 문서 지식 그래프
+
+WeKnora는 문서를 지식 그래프로 변환해 문서 내 서로 다른 섹션 간 관계를 시각화할 수 있습니다. 지식 그래프 기능을 활성화하면 문서 내부의 시맨틱 연관 네트워크를 분석/구성하여 문서 이해를 돕고, 인덱싱과 검색에 구조화된 지원을 제공해 검색 결과의 관련성과 폭을 향상시킵니다.
+
+자세한 설정은 [지식 그래프 설정 가이드](./docs/KnowledgeGraph.md)를 참고하세요.
+
+### MCP 서버
+
+필요한 설정은 [MCP 설정 가이드](./mcp-server/MCP_CONFIG.md)를 참고하세요.
+
### 🔌 WeChat 대화 오픈 플랫폼 사용
WeKnora는 [WeChat 대화 오픈 플랫폼](https://chatbot.weixin.qq.com)의 핵심 기술 프레임워크로 사용되며, 보다 간편한 사용 방식을 제공합니다:
@@ -320,7 +368,7 @@ python -m weknora-mcp-server
### ① 서비스 중지
```bash
-./scripts/start_all.sh --stop
+docker compose down
```
### ② 기존 데이터 테이블 정리(중요 데이터가 없을 때 권장)
@@ -332,7 +380,7 @@ make clean-db
### ③ 컴파일 및 서비스 시작
```bash
-./scripts/start_all.sh
+docker compose up -d --build
```
### ④ Web UI 접속
@@ -341,36 +389,6 @@ http://localhost
처음 접속하면 자동으로 회원가입/로그인 페이지로 이동합니다. 가입 완료 후 새 지식베이스를 생성하고 설정 페이지에서 필요한 항목을 구성하세요.
-## 📱 인터페이스 소개
-
-### Web UI 인터페이스
-
-
-
- 지식베이스 관리
- 대화 설정
-
-
- Agent 모드 도구 호출 과정
-
-
-
-**지식베이스 관리:** FAQ/문서 지식베이스 타입 생성 지원, 드래그 앤 드롭/폴더/URL 임포트 등 다양한 방식 지원. 문서 구조를 자동 식별하고 핵심 지식을 추출해 인덱스를 구축합니다. 태그 관리와 온라인 입력을 지원하며, 처리 진행 상황과 문서 상태를 명확히 표시해 효율적인 지식베이스 운영을 돕습니다.
-
-**Agent 모드:** ReACT Agent 모드를 지원하며, 내장 도구로 지식베이스 검색, 사용자 설정 MCP 도구 및 웹 검색 도구 호출을 통해 외부 서비스 접근이 가능합니다. 다중 반복과 리플렉션을 통해 종합 요약 리포트를 제공합니다. 교차 지식베이스 검색도 지원하여 여러 지식베이스를 동시에 검색할 수 있습니다.
-
-**대화 전략:** Agent 모델, 일반 모드 모델, 검색 임계값, 온라인 프롬프트 설정을 지원하여 멀티턴 대화 동작과 검색 실행 방식을 정밀하게 제어할 수 있습니다. 입력창에서 Agent/일반 모드 전환, 웹 검색 활성화/비활성화, 대화 모델 선택을 지원합니다.
-
-### 문서 지식 그래프
-
-WeKnora는 문서를 지식 그래프로 변환해 문서 내 서로 다른 섹션 간 관계를 시각화할 수 있습니다. 지식 그래프 기능을 활성화하면 문서 내부의 시맨틱 연관 네트워크를 분석/구성하여 문서 이해를 돕고, 인덱싱과 검색에 구조화된 지원을 제공해 검색 결과의 관련성과 폭을 향상시킵니다.
-
-자세한 설정은 [지식 그래프 설정 가이드](./docs/KnowledgeGraph.md)를 참고하세요.
-
-### MCP 서버
-
-필요한 설정은 [MCP 설정 가이드](./mcp-server/MCP_CONFIG.md)를 참고하세요.
-
## 📘 API 레퍼런스
문제 해결 FAQ: [문제 해결 FAQ](./docs/QA.md)
@@ -386,18 +404,14 @@ WeKnora는 문서를 지식 그래프로 변환해 문서 내 서로 다른 섹
코드를 자주 수정해야 한다면 **매번 Docker 이미지를 다시 빌드할 필요가 없습니다**. 고속 개발 모드를 사용하세요.
```bash
-# 방법 1: Make 명령 사용 (권장)
-make dev-start # 인프라 시작
-make dev-app # 백엔드 시작 (새 터미널)
-make dev-frontend # 프론트엔드 시작 (새 터미널)
+# 인프라 시작
+make dev-start
-# 방법 2: 원클릭 시작
-./scripts/quick-dev.sh
+# 백엔드 시작 (새 터미널)
+make dev-app
-# 방법 3: 스크립트 사용
-./scripts/dev.sh start # 인프라 시작
-./scripts/dev.sh app # 백엔드 시작 (새 터미널)
-./scripts/dev.sh frontend # 프론트엔드 시작 (새 터미널)
+# 프론트엔드 시작 (새 터미널)
+make dev-frontend
```
**개발 장점:**
diff --git a/docs/docs.go b/docs/docs.go
index fbcbdfb9..601fe390 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -359,6 +359,625 @@ const docTemplate = `{
}
}
},
+ "/agents/{id}/suggested-questions": {
+ "get": {
+ "security": [
+ {
+ "Bearer": []
+ },
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "基于智能体关联的知识库,返回推荐问题供用户快捷提问",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "智能体"
+ ],
+ "summary": "获取推荐问题",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "智能体ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "知识库ID列表(逗号分隔),覆盖智能体默认配置",
+ "name": "knowledge_base_ids",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "知识ID列表(逗号分隔),限定到具体文档",
+ "name": "knowledge_ids",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "返回数量上限(默认6)",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "推荐问题列表",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "400": {
+ "description": "请求参数错误",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
+ }
+ },
+ "404": {
+ "description": "智能体不存在",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/datasource": {
+ "get": {
+ "description": "List all data sources for a specific knowledge base",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "List data sources for a knowledge base",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Knowledge base ID",
+ "name": "kb_id",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.DataSource"
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "description": "Create a new data source configuration for a knowledge base",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "Create a new data source",
+ "parameters": [
+ {
+ "description": "Data source configuration",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.DataSource"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "Created",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.DataSource"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/datasource/logs/{log_id}": {
+ "get": {
+ "description": "Retrieve a specific sync log entry",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "Get specific sync log",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Sync log ID",
+ "name": "log_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.SyncLog"
+ }
+ },
+ "404": {
+ "description": "Not Found",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/datasource/types": {
+ "get": {
+ "description": "Get list of available data source connectors",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "Get available connectors",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_datasource.ConnectorMetadata"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/datasource/validate-credentials": {
+ "post": {
+ "description": "Validate connectivity to an external data source using type + credentials",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "Test connection with raw credentials (no persistence)",
+ "parameters": [
+ {
+ "description": "type and credentials",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "type": "object"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/datasource/{id}": {
+ "get": {
+ "description": "Retrieve a data source configuration by ID",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "Get a data source by ID",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Data source ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.DataSource"
+ }
+ },
+ "404": {
+ "description": "Not Found",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "description": "Update an existing data source configuration",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "Update a data source",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Data source ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "Updated configuration",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.DataSource"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.DataSource"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "description": "Delete a data source (soft delete)",
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "Delete a data source",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Data source ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content"
+ },
+ "404": {
+ "description": "Not Found",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/datasource/{id}/logs": {
+ "get": {
+ "description": "Retrieve sync history for a data source",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "Get sync logs",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Data source ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "description": "Limit (default: 10)",
+ "name": "limit",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "Offset (default: 0)",
+ "name": "offset",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.SyncLog"
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/datasource/{id}/pause": {
+ "post": {
+ "description": "Pause a data source's scheduled syncs",
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "Pause data source",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Data source ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/datasource/{id}/resources": {
+ "get": {
+ "description": "List resources available for sync in the external system",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "List available resources in data source",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Data source ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.Resource"
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/datasource/{id}/resume": {
+ "post": {
+ "description": "Resume a paused data source",
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "Resume data source",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Data source ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/datasource/{id}/sync": {
+ "post": {
+ "description": "Trigger an immediate sync for a data source",
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "Trigger immediate sync",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Data source ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.SyncLog"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/datasource/{id}/validate": {
+ "post": {
+ "description": "Validate the connection to an external data source",
+ "tags": [
+ "DataSource"
+ ],
+ "summary": "Test data source connection",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Data source ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
"/auth/change-password": {
"post": {
"security": [
@@ -523,6 +1142,113 @@ const docTemplate = `{
}
}
},
+ "/auth/oidc/callback": {
+ "get": {
+ "description": "接收OIDC provider回调并由后端完成code交换,随后重定向回前端登录页",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "认证"
+ ],
+ "summary": "OIDC登录重定向回调",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "OIDC授权码",
+ "name": "code",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "OIDC状态",
+ "name": "state",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "OIDC错误码",
+ "name": "error",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "302": {
+ "description": "Found"
+ }
+ }
+ }
+ },
+ "/auth/oidc/config": {
+ "get": {
+ "description": "返回OIDC是否启用以及provider展示名称,供前端决定是否展示OIDC登录入口",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "认证"
+ ],
+ "summary": "获取OIDC登录配置",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.OIDCConfigResponse"
+ }
+ }
+ }
+ }
+ },
+ "/auth/oidc/url": {
+ "get": {
+ "description": "根据后端OIDC配置生成第三方登录跳转地址",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "认证"
+ ],
+ "summary": "获取OIDC授权地址",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "OIDC回调地址",
+ "name": "redirect_uri",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.OIDCAuthURLResponse"
+ }
+ },
+ "400": {
+ "description": "请求参数错误",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
+ }
+ },
+ "403": {
+ "description": "OIDC未启用",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
+ }
+ }
+ }
+ }
+ },
"/auth/refresh": {
"post": {
"description": "使用刷新令牌获取新的访问令牌",
@@ -3107,6 +3833,57 @@ const docTemplate = `{
}
}
}
+ },
+ "delete": {
+ "security": [
+ {
+ "Bearer": []
+ },
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "删除知识库下的所有知识条目(异步任务)。知识库本身保留,仅清空其中的内容",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "知识管理"
+ ],
+ "summary": "清空知识库内容",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "知识库ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "清空任务已提交",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "400": {
+ "description": "请求参数错误",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
+ }
+ },
+ "403": {
+ "description": "权限不足",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
+ }
+ }
+ }
}
},
"/knowledge-bases/{id}/knowledge/file": {
@@ -3327,6 +4104,53 @@ const docTemplate = `{
}
}
},
+ "/knowledge-bases/{id}/pin": {
+ "put": {
+ "security": [
+ {
+ "Bearer": []
+ },
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "切换知识库的置顶状态",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "知识库"
+ ],
+ "summary": "置顶/取消置顶知识库",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "知识库ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "更新后的知识库",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "404": {
+ "description": "知识库不存在",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
+ }
+ }
+ }
+ }
+ },
"/knowledge-bases/{id}/shares": {
"get": {
"security": [
@@ -4284,6 +5108,55 @@ const docTemplate = `{
}
}
},
+ "/knowledge/{id}/preview": {
+ "get": {
+ "security": [
+ {
+ "Bearer": []
+ },
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "返回知识条目关联的原始文件,Content-Type 根据文件类型设置,用于浏览器内嵌预览",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/pdf",
+ "image/jpeg",
+ "image/png",
+ "text/plain"
+ ],
+ "tags": [
+ "知识管理"
+ ],
+ "summary": "预览知识文件",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "知识ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "文件内容",
+ "schema": {
+ "type": "file"
+ }
+ },
+ "400": {
+ "description": "请求参数错误",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
+ }
+ }
+ }
+ }
+ },
"/knowledge/{id}/reparse": {
"post": {
"security": [
@@ -4709,6 +5582,87 @@ const docTemplate = `{
}
}
},
+ "/messages/chat-history-stats": {
+ "get": {
+ "security": [
+ {
+ "Bearer": []
+ },
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "获取聊天历史知识库的统计信息(已索引消息数、知识库大小等)",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "消息"
+ ],
+ "summary": "获取聊天历史知识库统计",
+ "responses": {
+ "200": {
+ "description": "统计信息",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ }
+ }
+ }
+ },
+ "/messages/search": {
+ "post": {
+ "security": [
+ {
+ "Bearer": []
+ },
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "通过关键词和/或向量相似度搜索历史对话记录,支持关键词、向量、混合三种模式",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "消息"
+ ],
+ "summary": "搜索历史对话",
+ "parameters": [
+ {
+ "description": "搜索请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/internal_handler.SearchMessagesRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "搜索结果",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "400": {
+ "description": "请求参数错误",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
+ }
+ }
+ }
+ }
+ },
"/messages/{session_id}/load": {
"get": {
"security": [
@@ -6208,7 +7162,7 @@ const docTemplate = `{
"ApiKeyAuth": []
}
],
- "description": "根据ID列表批量删除对话会话",
+ "description": "根据ID列表批量删除对话会话,或设置 delete_all=true 删除当前租户的所有会话",
"consumes": [
"application/json"
],
@@ -6442,6 +7396,59 @@ const docTemplate = `{
}
}
},
+ "/sessions/{id}/messages": {
+ "delete": {
+ "security": [
+ {
+ "Bearer": []
+ },
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "删除会话中的所有消息,同时清除 LLM 上下文和聊天历史知识库条目。会话本身保留。",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "会话"
+ ],
+ "summary": "清空会话消息",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "会话ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "清空成功",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ },
+ "400": {
+ "description": "请求参数错误",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
+ }
+ },
+ "404": {
+ "description": "会话不存在",
+ "schema": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
+ }
+ }
+ }
+ }
+ },
"/sessions/{session_id}/agent-qa": {
"post": {
"security": [
@@ -6784,6 +7791,36 @@ const docTemplate = `{
}
}
},
+ "/system/docreader/reconnect": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "系统"
+ ],
+ "summary": "重连文档解析服务",
+ "parameters": [
+ {
+ "description": "DocReader 地址",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "type": "object"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
"/system/info": {
"get": {
"description": "获取系统版本、构建信息和引擎配置",
@@ -6844,6 +7881,110 @@ const docTemplate = `{
}
}
},
+ "/system/parser-engines": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "系统"
+ ],
+ "summary": "列出可用的文档解析引擎",
+ "responses": {
+ "200": {
+ "description": "解析引擎列表",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ }
+ }
+ }
+ },
+ "/system/parser-engines/check": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "系统"
+ ],
+ "summary": "使用当前参数检测解析引擎可用性",
+ "parameters": [
+ {
+ "description": "解析引擎配置(与保存接口同结构)",
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "type": "object"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/system/storage-engine-check": {
+ "post": {
+ "description": "使用当前填写的参数测试 MinIO/COS 连通性,不保存配置",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "系统"
+ ],
+ "summary": "测试存储引擎连通性",
+ "parameters": [
+ {
+ "description": "存储引擎配置",
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/internal_handler.StorageCheckRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/internal_handler.StorageCheckResponse"
+ }
+ }
+ }
+ }
+ },
+ "/system/storage-engine-status": {
+ "get": {
+ "description": "返回 Local、MinIO、COS 各存储引擎的可用状态及说明,供全局设置与知识库选择使用",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "系统"
+ ],
+ "summary": "获取存储引擎状态",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/internal_handler.GetStorageEngineStatusResponse"
+ }
+ }
+ }
+ }
+ },
"/tenants": {
"get": {
"security": [
@@ -7457,6 +8598,38 @@ const docTemplate = `{
}
},
"definitions": {
+ "github_com_Tencent_WeKnora_internal_datasource.ConnectorMetadata": {
+ "type": "object",
+ "properties": {
+ "auth_type": {
+ "description": "\"oauth2\", \"api_key\", \"token\", etc.",
+ "type": "string"
+ },
+ "capabilities": {
+ "description": "\"incremental\", \"webhook\", \"deletion_sync\", etc.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": {
+ "type": "string"
+ },
+ "icon": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "priority": {
+ "description": "Priority order for UI display (lower = higher priority)",
+ "type": "integer"
+ },
+ "type": {
+ "type": "string"
+ }
+ }
+ },
"github_com_Tencent_WeKnora_internal_errors.AppError": {
"type": "object",
"properties": {
@@ -7551,10 +8724,22 @@ const docTemplate = `{
"type": "string"
}
},
+ "llm_call_timeout": {
+ "description": "LLM call timeout in seconds (default: 120). Controls the maximum time for a single LLM call.",
+ "type": "integer"
+ },
+ "max_context_tokens": {
+ "description": "Maximum context window tokens for the agent (default: 200000).\nThe agent compresses older messages to stay within this limit,\npreserving tool_call/tool_result pairs.",
+ "type": "integer"
+ },
"max_iterations": {
"description": "Maximum number of ReAct iterations",
"type": "integer"
},
+ "max_tool_output_chars": {
+ "description": "Maximum character length for tool output (default: 16000).\nOutputs exceeding this limit are truncated with head + tail preservation.",
+ "type": "integer"
+ },
"mcp_selection_mode": {
"description": "MCP service selection",
"type": "string"
@@ -7570,8 +8755,8 @@ const docTemplate = `{
"description": "Whether multi-turn conversation is enabled",
"type": "boolean"
},
- "reflection_enabled": {
- "description": "Whether to enable reflection",
+ "parallel_tool_calls": {
+ "description": "Whether to execute independent tool calls in parallel (default: false).\nWhen enabled and the LLM returns multiple tool calls, they run concurrently via errgroup.",
"type": "boolean"
},
"retrieve_kb_only_when_mentioned": {
@@ -7658,9 +8843,53 @@ const docTemplate = `{
"AnswerStrategyRandom"
]
},
+ "github_com_Tencent_WeKnora_internal_types.COSEngineConfig": {
+ "type": "object",
+ "properties": {
+ "app_id": {
+ "type": "string"
+ },
+ "bucket_name": {
+ "type": "string"
+ },
+ "path_prefix": {
+ "type": "string"
+ },
+ "region": {
+ "type": "string"
+ },
+ "secret_id": {
+ "type": "string"
+ },
+ "secret_key": {
+ "type": "string"
+ }
+ }
+ },
+ "github_com_Tencent_WeKnora_internal_types.ChatHistoryConfig": {
+ "type": "object",
+ "properties": {
+ "embedding_model_id": {
+ "description": "EmbeddingModelID is the ID of the embedding model used for vectorizing chat messages.\nOnce messages have been indexed, the model cannot be changed (requires re-indexing).",
+ "type": "string"
+ },
+ "enabled": {
+ "description": "Enabled controls whether chat history indexing is active",
+ "type": "boolean"
+ },
+ "knowledge_base_id": {
+ "description": "KnowledgeBaseID is the auto-managed hidden knowledge base for chat history.\nThis is set internally when the feature is first enabled; users should not set this directly.",
+ "type": "string"
+ }
+ }
+ },
"github_com_Tencent_WeKnora_internal_types.ChunkingConfig": {
"type": "object",
"properties": {
+ "child_chunk_size": {
+ "description": "ChildChunkSize is the size of child chunks used for embedding (default: 384).\nOnly used when EnableParentChild is true.",
+ "type": "integer"
+ },
"chunk_overlap": {
"description": "Chunk overlap",
"type": "integer"
@@ -7673,6 +8902,21 @@ const docTemplate = `{
"description": "EnableMultimodal (deprecated, kept for backward compatibility with old data)",
"type": "boolean"
},
+ "enable_parent_child": {
+ "description": "EnableParentChild enables two-level parent-child chunking strategy.\nWhen enabled, large parent chunks provide context while small child chunks\nare used for vector matching. Retrieval matches on child but returns parent content.",
+ "type": "boolean"
+ },
+ "parent_chunk_size": {
+ "description": "ParentChunkSize is the size of parent chunks (default: 4096).\nOnly used when EnableParentChild is true.",
+ "type": "integer"
+ },
+ "parser_engine_rules": {
+ "description": "ParserEngineRules configures which parser engine to use for each file type.\nWhen empty, the builtin engine is used for all types.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.ParserEngineRule"
+ }
+ },
"separators": {
"description": "Separators",
"type": "array",
@@ -7836,6 +9080,10 @@ const docTemplate = `{
"description": "Context template for normal mode (how to format retrieved chunks)",
"type": "string"
},
+ "context_template_id": {
+ "description": "ContextTemplateID references a template ID in prompt_templates/ YAML files.\nIf set and ContextTemplate is empty, the template content will be resolved at startup.",
+ "type": "string"
+ },
"embedding_top_k": {
"description": "===== Retrieval Strategy Settings (for both modes) =====\nEmbedding/Vector retrieval top K",
"type": "integer"
@@ -7876,6 +9124,14 @@ const docTemplate = `{
"description": "Number of history turns to keep in context",
"type": "integer"
},
+ "image_storage_provider": {
+ "description": "Storage provider for image uploads: \"local\", \"minio\", \"cos\", \"tos\"\nEmpty means use the global/tenant default provider.",
+ "type": "string"
+ },
+ "image_upload_enabled": {
+ "description": "===== Image Upload / Multimodal Settings =====\nWhether image upload is enabled for this agent (default: false)",
+ "type": "boolean"
+ },
"kb_selection_mode": {
"description": "===== Knowledge Base Settings =====\nKnowledge base selection mode: \"all\" = all KBs, \"selected\" = specific KBs, \"none\" = no KB",
"type": "string"
@@ -7918,10 +9174,6 @@ const docTemplate = `{
"description": "===== Multi-turn Conversation Settings =====\nWhether multi-turn conversation is enabled",
"type": "boolean"
},
- "reflection_enabled": {
- "description": "Whether reflection is enabled (only for agent type)",
- "type": "boolean"
- },
"rerank_model_id": {
"description": "ReRank model ID for retrieval",
"type": "string"
@@ -7957,6 +9209,13 @@ const docTemplate = `{
"description": "===== Skills Settings (only for smart-reasoning mode) =====\nSkills selection mode: \"all\" = all preloaded skills, \"selected\" = specific skills, \"none\" = no skills",
"type": "string"
},
+ "suggested_prompts": {
+ "description": "===== Suggested Prompts =====\n推荐问题列表,用于在前端对话面板展示快捷提问",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
"supported_file_types": {
"description": "===== File Type Restriction Settings =====\nSupported file types for this agent (e.g., [\"csv\", \"xlsx\", \"xls\"])\nEmpty means all file types are supported\nWhen set, only files with matching extensions can be used with this agent",
"type": "array",
@@ -7968,6 +9227,10 @@ const docTemplate = `{
"description": "System prompt for the agent (unified prompt, uses web_search_status placeholder for dynamic behavior)",
"type": "string"
},
+ "system_prompt_id": {
+ "description": "SystemPromptID references a template ID in prompt_templates/ YAML files.\nIf set and SystemPrompt is empty, the template content will be resolved at startup.",
+ "type": "string"
+ },
"temperature": {
"description": "Temperature for LLM (0-1)",
"type": "number"
@@ -7980,6 +9243,10 @@ const docTemplate = `{
"description": "Vector retrieval threshold",
"type": "number"
},
+ "vlm_model_id": {
+ "description": "VLM model ID for image analysis (optional, falls back to tenant-level VLM)",
+ "type": "string"
+ },
"web_search_enabled": {
"description": "===== Web Search Settings =====\nWhether web search is enabled",
"type": "boolean"
@@ -7990,6 +9257,112 @@ const docTemplate = `{
}
}
},
+ "github_com_Tencent_WeKnora_internal_types.DataSource": {
+ "type": "object",
+ "properties": {
+ "config": {
+ "description": "Encrypted configuration (API credentials, tokens, etc.)\nStored as JSON with AES-256-GCM encryption",
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "conflict_strategy": {
+ "description": "Conflict resolution strategy: overwrite or skip",
+ "type": "string"
+ },
+ "created_at": {
+ "description": "Creation timestamp",
+ "type": "string"
+ },
+ "deleted_at": {
+ "description": "Soft delete timestamp",
+ "allOf": [
+ {
+ "$ref": "#/definitions/gorm.DeletedAt"
+ }
+ ]
+ },
+ "error_message": {
+ "description": "Error message if status is \"error\"",
+ "type": "string"
+ },
+ "id": {
+ "description": "Unique identifier",
+ "type": "string"
+ },
+ "knowledge_base_id": {
+ "description": "Target knowledge base ID",
+ "type": "string"
+ },
+ "last_sync_at": {
+ "description": "Last successful sync timestamp",
+ "type": "string"
+ },
+ "last_sync_cursor": {
+ "description": "Cursor or state for incremental sync (connector-specific)",
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "last_sync_result": {
+ "description": "Summary of last sync result",
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "latest_sync_log": {
+ "description": "Latest sync log (not stored in DB, populated on query)",
+ "allOf": [
+ {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.SyncLog"
+ }
+ ]
+ },
+ "name": {
+ "description": "User-friendly name",
+ "type": "string"
+ },
+ "status": {
+ "description": "Current status: active, paused, error",
+ "type": "string"
+ },
+ "sync_deletions": {
+ "description": "Whether to sync deletions from source",
+ "type": "boolean"
+ },
+ "sync_log_retention_days": {
+ "description": "Number of days to keep sync logs (default: 30)",
+ "type": "integer"
+ },
+ "sync_mode": {
+ "description": "Sync mode: \"incremental\" (recommended) or \"full\"",
+ "type": "string"
+ },
+ "sync_schedule": {
+ "description": "Cron expression for scheduled syncs (e.g., \"0 */6 * * *\" = every 6 hours)",
+ "type": "string"
+ },
+ "tenant_id": {
+ "description": "Tenant ID for multi-tenancy",
+ "type": "integer"
+ },
+ "total_items_synced": {
+ "description": "Total items synced (not stored in DB, calculated on query)",
+ "type": "integer"
+ },
+ "type": {
+ "description": "Connector type (feishu, notion, confluence, etc.)",
+ "type": "string"
+ },
+ "updated_at": {
+ "description": "Last update timestamp",
+ "type": "string"
+ }
+ }
+ },
"github_com_Tencent_WeKnora_internal_types.EmbeddingParameters": {
"type": "object",
"properties": {
@@ -8116,7 +9489,6 @@ const docTemplate = `{
"github_com_Tencent_WeKnora_internal_types.FAQEntryPayload": {
"type": "object",
"required": [
- "answers",
"standard_question"
],
"properties": {
@@ -8323,6 +9695,10 @@ const docTemplate = `{
"github_com_Tencent_WeKnora_internal_types.Knowledge": {
"type": "object",
"properties": {
+ "channel": {
+ "description": "Channel indicates through which channel the knowledge was ingested (web, api, browser_extension, wechat, etc.)",
+ "type": "string"
+ },
"created_at": {
"description": "Creation time of the knowledge",
"type": "string"
@@ -8406,7 +9782,7 @@ const docTemplate = `{
"type": "string"
},
"source": {
- "description": "Source of the knowledge",
+ "description": "Source of the knowledge (e.g. URL address for url type, \"manual\" for manual type)",
"type": "string"
},
"storage_size": {
@@ -8454,14 +9830,6 @@ const docTemplate = `{
}
]
},
- "cos_config": {
- "description": "Storage config",
- "allOf": [
- {
- "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.StorageConfig"
- }
- ]
- },
"created_at": {
"description": "Creation time of the knowledge base",
"type": "string"
@@ -8510,6 +9878,10 @@ const docTemplate = `{
}
]
},
+ "is_pinned": {
+ "description": "Whether this knowledge base is pinned to the top of the list",
+ "type": "boolean"
+ },
"is_processing": {
"description": "IsProcessing indicates if there is a processing import task (for FAQ type knowledge bases)",
"type": "boolean"
@@ -8526,6 +9898,10 @@ const docTemplate = `{
"description": "Name of the knowledge base",
"type": "string"
},
+ "pinned_at": {
+ "description": "Time when the knowledge base was pinned (nil if not pinned)",
+ "type": "string"
+ },
"processing_count": {
"description": "ProcessingCount indicates the number of knowledge items being processed (for document type knowledge bases)",
"type": "integer"
@@ -8542,6 +9918,22 @@ const docTemplate = `{
"description": "ShareCount indicates the number of organizations this knowledge base is shared with (not stored in database)",
"type": "integer"
},
+ "storage_config": {
+ "description": "Deprecated: legacy COS config column. Kept for backward compatibility with old data.",
+ "allOf": [
+ {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.StorageConfig"
+ }
+ ]
+ },
+ "storage_provider_config": {
+ "description": "Storage provider config (new): only stores provider selection; credentials from tenant StorageEngineConfig",
+ "allOf": [
+ {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.StorageProviderConfig"
+ }
+ ]
+ },
"summary_model_id": {
"description": "Summary model ID",
"type": "string"
@@ -8703,6 +10095,14 @@ const docTemplate = `{
}
}
},
+ "github_com_Tencent_WeKnora_internal_types.LocalEngineConfig": {
+ "type": "object",
+ "properties": {
+ "path_prefix": {
+ "type": "string"
+ }
+ }
+ },
"github_com_Tencent_WeKnora_internal_types.LoginRequest": {
"type": "object",
"required": [
@@ -8823,6 +10223,10 @@ const docTemplate = `{
"id": {
"type": "string"
},
+ "is_builtin": {
+ "description": "Whether this is a builtin MCP service (visible to all tenants)",
+ "type": "boolean"
+ },
"name": {
"type": "string"
},
@@ -8891,6 +10295,9 @@ const docTemplate = `{
"github_com_Tencent_WeKnora_internal_types.ManualKnowledgePayload": {
"type": "object",
"properties": {
+ "channel": {
+ "type": "string"
+ },
"content": {
"type": "string"
},
@@ -8973,6 +10380,10 @@ const docTemplate = `{
"github_com_Tencent_WeKnora_internal_types.Message": {
"type": "object",
"properties": {
+ "agent_duration_ms": {
+ "description": "Agent total execution duration in milliseconds (from query start to answer start)",
+ "type": "integer"
+ },
"agent_steps": {
"description": "Agent execution steps (only for assistant messages generated by agent)\nThis contains the detailed reasoning process and tool calls made by the agent\nStored for user history display, but NOT included in LLM context to avoid redundancy",
"type": "array",
@@ -8980,6 +10391,10 @@ const docTemplate = `{
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.AgentStep"
}
},
+ "channel": {
+ "description": "Channel indicates the source channel of this message (e.g., \"web\", \"api\", \"im\")",
+ "type": "string"
+ },
"content": {
"description": "Message text content",
"type": "string"
@@ -9000,10 +10415,25 @@ const docTemplate = `{
"description": "Unique identifier for the message",
"type": "string"
},
+ "images": {
+ "description": "Attached images with OCR/Caption text (for user messages)",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.MessageImage"
+ }
+ },
"is_completed": {
"description": "Whether message generation is complete",
"type": "boolean"
},
+ "is_fallback": {
+ "description": "Whether this response is a fallback (no knowledge base match found)",
+ "type": "boolean"
+ },
+ "knowledge_id": {
+ "description": "KnowledgeID links this message to a Knowledge entry in the chat history knowledge base\nUsed for vector search indexing: when set, the message content has been indexed as a Knowledge passage",
+ "type": "string"
+ },
"knowledge_references": {
"description": "References to knowledge chunks used in the response",
"type": "array",
@@ -9036,6 +10466,44 @@ const docTemplate = `{
}
}
},
+ "github_com_Tencent_WeKnora_internal_types.MessageImage": {
+ "type": "object",
+ "properties": {
+ "caption": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ }
+ }
+ },
+ "github_com_Tencent_WeKnora_internal_types.MinIOEngineConfig": {
+ "type": "object",
+ "properties": {
+ "access_key_id": {
+ "type": "string"
+ },
+ "bucket_name": {
+ "type": "string"
+ },
+ "endpoint": {
+ "type": "string"
+ },
+ "mode": {
+ "description": "\"docker\" or \"remote\"",
+ "type": "string"
+ },
+ "path_prefix": {
+ "type": "string"
+ },
+ "secret_access_key": {
+ "type": "string"
+ },
+ "use_ssl": {
+ "type": "boolean"
+ }
+ }
+ },
"github_com_Tencent_WeKnora_internal_types.ModelParameters": {
"type": "object",
"properties": {
@@ -9065,6 +10533,10 @@ const docTemplate = `{
"provider": {
"description": "Provider identifier: openai, aliyun, zhipu, generic",
"type": "string"
+ },
+ "supports_vision": {
+ "description": "Whether the model accepts image/multimodal input",
+ "type": "boolean"
}
}
},
@@ -9084,7 +10556,9 @@ const docTemplate = `{
"mimo",
"siliconflow",
"jina",
- "openrouter"
+ "openrouter",
+ "nvidia",
+ "novita"
],
"x-enum-comments": {
"ModelSourceAliyun": "Aliyun DashScope model",
@@ -9095,6 +10569,8 @@ const docTemplate = `{
"ModelSourceLocal": "Local model",
"ModelSourceMimo": "Mimo model",
"ModelSourceMinimax": "Minimax mode",
+ "ModelSourceNovita": "Novita AI model",
+ "ModelSourceNvidia": "NVIDIA model",
"ModelSourceOpenAI": "OpenAI model",
"ModelSourceOpenRouter": "OpenRouter model",
"ModelSourceRemote": "Remote model",
@@ -9116,7 +10592,9 @@ const docTemplate = `{
"Mimo model",
"SiliconFlow model",
"Jina AI model",
- "OpenRouter model"
+ "OpenRouter model",
+ "NVIDIA model",
+ "Novita AI model"
],
"x-enum-varnames": [
"ModelSourceLocal",
@@ -9132,7 +10610,9 @@ const docTemplate = `{
"ModelSourceMimo",
"ModelSourceSiliconFlow",
"ModelSourceJina",
- "ModelSourceOpenRouter"
+ "ModelSourceOpenRouter",
+ "ModelSourceNvidia",
+ "ModelSourceNovita"
]
},
"github_com_Tencent_WeKnora_internal_types.ModelType": {
@@ -9162,6 +10642,37 @@ const docTemplate = `{
"ModelTypeVLLM"
]
},
+ "github_com_Tencent_WeKnora_internal_types.OIDCAuthURLResponse": {
+ "type": "object",
+ "properties": {
+ "authorization_url": {
+ "type": "string"
+ },
+ "provider_display_name": {
+ "type": "string"
+ },
+ "state": {
+ "type": "string"
+ },
+ "success": {
+ "type": "boolean"
+ }
+ }
+ },
+ "github_com_Tencent_WeKnora_internal_types.OIDCConfigResponse": {
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean"
+ },
+ "provider_display_name": {
+ "type": "string"
+ },
+ "success": {
+ "type": "boolean"
+ }
+ }
+ },
"github_com_Tencent_WeKnora_internal_types.OrgMemberRole": {
"type": "string",
"enum": [
@@ -9274,6 +10785,69 @@ const docTemplate = `{
}
}
},
+ "github_com_Tencent_WeKnora_internal_types.ParserEngineConfig": {
+ "type": "object",
+ "properties": {
+ "docreader_addr": {
+ "description": "文档解析服务地址",
+ "type": "string"
+ },
+ "mineru_api_key": {
+ "description": "MinerU 云 API Key",
+ "type": "string"
+ },
+ "mineru_cloud_enable_formula": {
+ "type": "boolean"
+ },
+ "mineru_cloud_enable_ocr": {
+ "type": "boolean"
+ },
+ "mineru_cloud_enable_table": {
+ "type": "boolean"
+ },
+ "mineru_cloud_language": {
+ "type": "string"
+ },
+ "mineru_cloud_model": {
+ "description": "MinerU 云 API 解析参数",
+ "type": "string"
+ },
+ "mineru_enable_formula": {
+ "type": "boolean"
+ },
+ "mineru_enable_ocr": {
+ "type": "boolean"
+ },
+ "mineru_enable_table": {
+ "type": "boolean"
+ },
+ "mineru_endpoint": {
+ "description": "MinerU 自建服务端点",
+ "type": "string"
+ },
+ "mineru_language": {
+ "type": "string"
+ },
+ "mineru_model": {
+ "description": "MinerU 自建解析参数",
+ "type": "string"
+ }
+ }
+ },
+ "github_com_Tencent_WeKnora_internal_types.ParserEngineRule": {
+ "type": "object",
+ "properties": {
+ "engine": {
+ "type": "string"
+ },
+ "file_types": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
"github_com_Tencent_WeKnora_internal_types.QuestionGenerationConfig": {
"type": "object",
"properties": {
@@ -9304,7 +10878,7 @@ const docTemplate = `{
"username": {
"type": "string",
"maxLength": 50,
- "minLength": 3
+ "minLength": 2
}
}
},
@@ -9346,6 +10920,44 @@ const docTemplate = `{
}
}
},
+ "github_com_Tencent_WeKnora_internal_types.Resource": {
+ "type": "object",
+ "properties": {
+ "description": {
+ "description": "Optional description",
+ "type": "string"
+ },
+ "external_id": {
+ "description": "Unique identifier in the external system",
+ "type": "string"
+ },
+ "metadata": {
+ "description": "Additional metadata",
+ "type": "object",
+ "additionalProperties": true
+ },
+ "modified_at": {
+ "description": "Last modified time in external system",
+ "type": "string"
+ },
+ "name": {
+ "description": "Display name",
+ "type": "string"
+ },
+ "parent_id": {
+ "description": "For hierarchical resources (parent ID if applicable)",
+ "type": "string"
+ },
+ "type": {
+ "description": "Resource type (document, folder, space, page, etc.)",
+ "type": "string"
+ },
+ "url": {
+ "description": "URL to access in external system",
+ "type": "string"
+ }
+ }
+ },
"github_com_Tencent_WeKnora_internal_types.ResourceCountsByOrgResponse": {
"type": "object",
"properties": {
@@ -9373,6 +10985,35 @@ const docTemplate = `{
}
}
},
+ "github_com_Tencent_WeKnora_internal_types.RetrievalConfig": {
+ "type": "object",
+ "properties": {
+ "embedding_top_k": {
+ "description": "EmbeddingTopK is the maximum number of chunks returned by vector search (default: 50)",
+ "type": "integer"
+ },
+ "keyword_threshold": {
+ "description": "KeywordThreshold is the minimum keyword match score (0-1, default: 0.3)",
+ "type": "number"
+ },
+ "rerank_model_id": {
+ "description": "RerankModelID is the ID of the rerank model to use (required for search)",
+ "type": "string"
+ },
+ "rerank_threshold": {
+ "description": "RerankThreshold is the minimum rerank score (-10 to 10, default: 0.2)",
+ "type": "number"
+ },
+ "rerank_top_k": {
+ "description": "RerankTopK is the maximum number of results after reranking (default: 10)",
+ "type": "integer"
+ },
+ "vector_threshold": {
+ "description": "VectorThreshold is the minimum vector similarity score (0-1, default: 0.15)",
+ "type": "number"
+ }
+ }
+ },
"github_com_Tencent_WeKnora_internal_types.RetrieverEngineParams": {
"type": "object",
"properties": {
@@ -9401,14 +11042,20 @@ const docTemplate = `{
"elasticsearch",
"infinity",
"elasticfaiss",
- "qdrant"
+ "qdrant",
+ "milvus",
+ "weaviate",
+ "sqlite"
],
"x-enum-varnames": [
"PostgresRetrieverEngineType",
"ElasticsearchRetrieverEngineType",
"InfinityRetrieverEngineType",
"ElasticFaissRetrieverEngineType",
- "QdrantRetrieverEngineType"
+ "QdrantRetrieverEngineType",
+ "MilvusRetrieverEngineType",
+ "WeaviateRetrieverEngineType",
+ "SQLiteRetrieverEngineType"
]
},
"github_com_Tencent_WeKnora_internal_types.RetrieverEngines": {
@@ -9465,6 +11112,29 @@ const docTemplate = `{
}
}
},
+ "github_com_Tencent_WeKnora_internal_types.S3EngineConfig": {
+ "type": "object",
+ "properties": {
+ "access_key": {
+ "type": "string"
+ },
+ "bucket_name": {
+ "type": "string"
+ },
+ "endpoint": {
+ "type": "string"
+ },
+ "path_prefix": {
+ "type": "string"
+ },
+ "region": {
+ "type": "string"
+ },
+ "secret_key": {
+ "type": "string"
+ }
+ }
+ },
"github_com_Tencent_WeKnora_internal_types.SearchParams": {
"type": "object",
"properties": {
@@ -9477,6 +11147,13 @@ const docTemplate = `{
"keyword_threshold": {
"type": "number"
},
+ "knowledge_base_ids": {
+ "description": "KnowledgeBaseIDs overrides the single KB ID passed to HybridSearch,\nallowing a single retrieval call to span multiple KBs that share the\nsame embedding model. When empty, HybridSearch uses its own id parameter.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
"knowledge_ids": {
"type": "array",
"items": {
@@ -9489,9 +11166,19 @@ const docTemplate = `{
"only_recommended": {
"type": "boolean"
},
+ "query_embedding": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ }
+ },
"query_text": {
"type": "string"
},
+ "skip_context_enrichment": {
+ "description": "SkipContextEnrichment skips fetching parent, nearby, and relation chunks\nin processSearchResults. Used by the chat pipeline where context assembly\nis handled separately in the merge stage.",
+ "type": "boolean"
+ },
"tag_ids": {
"description": "Tag IDs for filtering (used for FAQ priority filtering)",
"type": "array",
@@ -9538,6 +11225,18 @@ const docTemplate = `{
"description": "图片信息 (JSON 格式)",
"type": "string"
},
+ "knowledge_base_id": {
+ "description": "KnowledgeBaseID is the ID of the knowledge base this result belongs to",
+ "type": "string"
+ },
+ "knowledge_channel": {
+ "description": "KnowledgeChannel indicates through which channel the knowledge was ingested (web, api, wechat, etc.)",
+ "type": "string"
+ },
+ "knowledge_description": {
+ "description": "KnowledgeDescription is the description of the knowledge document",
+ "type": "string"
+ },
"knowledge_filename": {
"description": "Knowledge file name\nUsed for file type knowledge, contains the original file name",
"type": "string"
@@ -9647,31 +11346,57 @@ const docTemplate = `{
"type": "object",
"properties": {
"app_id": {
- "description": "App ID",
"type": "string"
},
"bucket_name": {
- "description": "Bucket Name",
"type": "string"
},
"path_prefix": {
- "description": "Path Prefix",
"type": "string"
},
"provider": {
- "description": "Provider",
"type": "string"
},
"region": {
- "description": "Region",
"type": "string"
},
"secret_id": {
- "description": "Secret ID",
"type": "string"
},
"secret_key": {
- "description": "Secret Key",
+ "type": "string"
+ }
+ }
+ },
+ "github_com_Tencent_WeKnora_internal_types.StorageEngineConfig": {
+ "type": "object",
+ "properties": {
+ "cos": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.COSEngineConfig"
+ },
+ "default_provider": {
+ "description": "\"local\", \"minio\", \"cos\", \"tos\", \"s3\"",
+ "type": "string"
+ },
+ "local": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.LocalEngineConfig"
+ },
+ "minio": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.MinIOEngineConfig"
+ },
+ "s3": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.S3EngineConfig"
+ },
+ "tos": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.TOSEngineConfig"
+ }
+ }
+ },
+ "github_com_Tencent_WeKnora_internal_types.StorageProviderConfig": {
+ "type": "object",
+ "properties": {
+ "provider": {
+ "description": "\"local\", \"minio\", \"cos\", \"tos\"",
"type": "string"
}
}
@@ -9701,6 +11426,101 @@ const docTemplate = `{
}
}
},
+ "github_com_Tencent_WeKnora_internal_types.SyncLog": {
+ "type": "object",
+ "properties": {
+ "created_at": {
+ "description": "Creation timestamp (usually same as StartedAt)",
+ "type": "string"
+ },
+ "data_source_id": {
+ "description": "Reference to the data source",
+ "type": "string"
+ },
+ "error_message": {
+ "description": "Error details if status is \"failed\"",
+ "type": "string"
+ },
+ "finished_at": {
+ "description": "Sync completion time",
+ "type": "string"
+ },
+ "id": {
+ "description": "Unique identifier",
+ "type": "string"
+ },
+ "items_created": {
+ "description": "New items created in knowledge base",
+ "type": "integer"
+ },
+ "items_deleted": {
+ "description": "Items deleted from knowledge base",
+ "type": "integer"
+ },
+ "items_failed": {
+ "description": "Items that failed to sync",
+ "type": "integer"
+ },
+ "items_skipped": {
+ "description": "Items skipped (no changes detected)",
+ "type": "integer"
+ },
+ "items_total": {
+ "description": "Total items fetched from source",
+ "type": "integer"
+ },
+ "items_updated": {
+ "description": "Existing items updated",
+ "type": "integer"
+ },
+ "result": {
+ "description": "Detailed sync result (JSON-encoded)",
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "started_at": {
+ "description": "Sync start time",
+ "type": "string"
+ },
+ "status": {
+ "description": "Sync status: running, success, partial, failed, canceled",
+ "type": "string"
+ },
+ "tenant_id": {
+ "description": "Tenant ID",
+ "type": "integer"
+ },
+ "updated_at": {
+ "description": "Last update timestamp",
+ "type": "string"
+ }
+ }
+ },
+ "github_com_Tencent_WeKnora_internal_types.TOSEngineConfig": {
+ "type": "object",
+ "properties": {
+ "access_key": {
+ "type": "string"
+ },
+ "bucket_name": {
+ "type": "string"
+ },
+ "endpoint": {
+ "type": "string"
+ },
+ "path_prefix": {
+ "type": "string"
+ },
+ "region": {
+ "type": "string"
+ },
+ "secret_key": {
+ "type": "string"
+ }
+ }
+ },
"github_com_Tencent_WeKnora_internal_types.Tenant": {
"type": "object",
"properties": {
@@ -9720,6 +11540,14 @@ const docTemplate = `{
"description": "Business",
"type": "string"
},
+ "chat_history_config": {
+ "description": "Chat history config: knowledge base configuration for indexing and searching chat messages via vector search",
+ "allOf": [
+ {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.ChatHistoryConfig"
+ }
+ ]
+ },
"context_config": {
"description": "Global Context configuration for this tenant (default for all sessions)",
"allOf": [
@@ -9760,6 +11588,22 @@ const docTemplate = `{
"description": "Name",
"type": "string"
},
+ "parser_engine_config": {
+ "description": "Parser engine config overrides (MinerU endpoint, API key, etc.). Used when parsing documents; overrides env.",
+ "allOf": [
+ {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.ParserEngineConfig"
+ }
+ ]
+ },
+ "retrieval_config": {
+ "description": "Retrieval config: global search/retrieval parameters shared by knowledge search and message search",
+ "allOf": [
+ {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.RetrievalConfig"
+ }
+ ]
+ },
"retriever_engines": {
"description": "Retriever engines",
"allOf": [
@@ -9772,6 +11616,14 @@ const docTemplate = `{
"description": "Status",
"type": "string"
},
+ "storage_engine_config": {
+ "description": "Storage engine config: parameters for Local, MinIO, COS. Used for document/file storage and docreader.",
+ "allOf": [
+ {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.StorageEngineConfig"
+ }
+ ]
+ },
"storage_quota": {
"description": "Storage quota (Bytes), default is 10GB, including vector, original file, text, index, etc.",
"type": "integer"
@@ -9840,6 +11692,13 @@ const docTemplate = `{
"description": "Error message if execution failed",
"type": "string"
},
+ "images": {
+ "description": "Base64 data URIs from tool (e.g. MCP image content)",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
"output": {
"description": "Human-readable output",
"type": "string"
@@ -10148,9 +12007,12 @@ const docTemplate = `{
},
"internal_handler.FabriTextRequest": {
"type": "object",
+ "required": [
+ "model_id"
+ ],
"properties": {
- "llm_config": {
- "$ref": "#/definitions/internal_handler.LLMConfig"
+ "model_id": {
+ "type": "string"
},
"tags": {
"type": "array",
@@ -10160,6 +12022,20 @@ const docTemplate = `{
}
}
},
+ "internal_handler.GetStorageEngineStatusResponse": {
+ "type": "object",
+ "properties": {
+ "engines": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/internal_handler.StorageEngineStatusItem"
+ }
+ },
+ "minio_env_available": {
+ "type": "boolean"
+ }
+ }
+ },
"internal_handler.GetSystemInfoResponse": {
"type": "object",
"properties": {
@@ -10169,6 +12045,12 @@ const docTemplate = `{
"commit_id": {
"type": "string"
},
+ "db_version": {
+ "type": "string"
+ },
+ "edition": {
+ "type": "string"
+ },
"go_version": {
"type": "string"
},
@@ -10200,12 +12082,27 @@ const docTemplate = `{
"description": "文档分块配置",
"type": "object",
"properties": {
+ "childChunkSize": {
+ "type": "integer"
+ },
"chunkOverlap": {
"type": "integer"
},
"chunkSize": {
"type": "integer"
},
+ "enableParentChild": {
+ "type": "boolean"
+ },
+ "parentChunkSize": {
+ "type": "integer"
+ },
+ "parserEngineRules": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.ParserEngineRule"
+ }
+ },
"separators": {
"type": "array",
"items": {
@@ -10221,52 +12118,11 @@ const docTemplate = `{
"type": "string"
},
"multimodal": {
- "description": "多模态配置",
+ "description": "多模态配置(仅模型相关;存储引擎在 storageProvider 中配置)",
"type": "object",
"properties": {
- "cos": {
- "type": "object",
- "properties": {
- "appId": {
- "type": "string"
- },
- "bucketName": {
- "type": "string"
- },
- "pathPrefix": {
- "type": "string"
- },
- "region": {
- "type": "string"
- },
- "secretId": {
- "type": "string"
- },
- "secretKey": {
- "type": "string"
- }
- }
- },
"enabled": {
"type": "boolean"
- },
- "minio": {
- "type": "object",
- "properties": {
- "bucketName": {
- "type": "string"
- },
- "pathPrefix": {
- "type": "string"
- },
- "useSSL": {
- "type": "boolean"
- }
- }
- },
- "storageType": {
- "description": "\"cos\" or \"minio\"",
- "type": "string"
}
}
},
@@ -10312,28 +12168,15 @@ const docTemplate = `{
}
}
},
+ "storageProvider": {
+ "description": "存储引擎选择(\"local\" | \"minio\" | \"cos\"),影响文档上传与文档内图片存储,参数从全局设置读取",
+ "type": "string"
+ },
"vlm_config": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.VLMConfig"
}
}
},
- "internal_handler.LLMConfig": {
- "type": "object",
- "properties": {
- "api_key": {
- "type": "string"
- },
- "base_url": {
- "type": "string"
- },
- "model_name": {
- "type": "string"
- },
- "source": {
- "type": "string"
- }
- }
- },
"internal_handler.ListMinioBucketsResponse": {
"type": "object",
"properties": {
@@ -10378,15 +12221,95 @@ const docTemplate = `{
}
}
},
+ "internal_handler.SearchMessagesRequest": {
+ "type": "object",
+ "required": [
+ "query"
+ ],
+ "properties": {
+ "limit": {
+ "description": "Maximum number of results to return (default: 20)",
+ "type": "integer"
+ },
+ "mode": {
+ "description": "Search mode: \"keyword\", \"vector\", \"hybrid\" (default: \"hybrid\")",
+ "type": "string"
+ },
+ "query": {
+ "description": "Query text for search",
+ "type": "string"
+ },
+ "session_ids": {
+ "description": "Filter by specific session IDs (optional)",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "internal_handler.StorageCheckRequest": {
+ "type": "object",
+ "properties": {
+ "cos": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.COSEngineConfig"
+ },
+ "minio": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.MinIOEngineConfig"
+ },
+ "provider": {
+ "description": "\"minio\", \"cos\", \"tos\", or \"s3\"",
+ "type": "string"
+ },
+ "s3": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.S3EngineConfig"
+ },
+ "tos": {
+ "$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.TOSEngineConfig"
+ }
+ }
+ },
+ "internal_handler.StorageCheckResponse": {
+ "type": "object",
+ "properties": {
+ "bucket_created": {
+ "type": "boolean"
+ },
+ "message": {
+ "type": "string"
+ },
+ "ok": {
+ "type": "boolean"
+ }
+ }
+ },
+ "internal_handler.StorageEngineStatusItem": {
+ "type": "object",
+ "properties": {
+ "available": {
+ "description": "whether the engine can be used",
+ "type": "boolean"
+ },
+ "description": {
+ "description": "short description for UI",
+ "type": "string"
+ },
+ "name": {
+ "description": "\"local\", \"minio\", \"cos\", \"tos\"",
+ "type": "string"
+ }
+ }
+ },
"internal_handler.TextRelationExtractionRequest": {
"type": "object",
"required": [
+ "model_id",
"tags",
"text"
],
"properties": {
- "llm_config": {
- "$ref": "#/definitions/internal_handler.LLMConfig"
+ "model_id": {
+ "type": "string"
},
"tags": {
"type": "array",
@@ -10448,7 +12371,6 @@ const docTemplate = `{
"internal_handler.UpdateKnowledgeBaseRequest": {
"type": "object",
"required": [
- "config",
"name"
],
"properties": {
@@ -10527,6 +12449,10 @@ const docTemplate = `{
"description": "Selected custom agent ID (backend resolves shared agent and its tenant from share relation)",
"type": "string"
},
+ "channel": {
+ "description": "Source channel: \"web\", \"api\", \"im\", etc.",
+ "type": "string"
+ },
"disable_title": {
"description": "Whether to disable auto title generation",
"type": "boolean"
@@ -10535,6 +12461,13 @@ const docTemplate = `{
"description": "Whether memory feature is enabled for this request",
"type": "boolean"
},
+ "images": {
+ "description": "Attached images for multimodal chat",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/internal_handler_session.ImageAttachment"
+ }
+ },
"knowledge_base_ids": {
"description": "Selected knowledge base ID for this request",
"type": "array",
@@ -10598,6 +12531,23 @@ const docTemplate = `{
}
}
},
+ "internal_handler_session.ImageAttachment": {
+ "type": "object",
+ "properties": {
+ "caption": {
+ "description": "VLM analysis result (context-aware, single call)",
+ "type": "string"
+ },
+ "data": {
+ "description": "base64 data URI from frontend (data:image/png;base64,...)",
+ "type": "string"
+ },
+ "url": {
+ "description": "serving URL after saving to storage",
+ "type": "string"
+ }
+ }
+ },
"internal_handler_session.MentionedItemRequest": {
"type": "object",
"properties": {
@@ -10660,13 +12610,12 @@ const docTemplate = `{
},
"internal_handler_session.batchDeleteRequest": {
"type": "object",
- "required": [
- "ids"
- ],
"properties": {
+ "delete_all": {
+ "type": "boolean"
+ },
"ids": {
"type": "array",
- "minItems": 1,
"items": {
"type": "string"
}
diff --git a/docs/images/agent-qa.png b/docs/images/agent-qa.png
index 5f3b1adf362d4e98a9da54e0f32d9fbf8c9621e1..18e318ebe63c60b8681a922967ff0c0c88cfbf86 100644
GIT binary patch
literal 559962
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 i`98#q|L
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