## 1. Project Overview & Quickstart (Tencent/WeKnora)

## File: README.md

# 💡 WeKnora — Turn Documents into Living Knowledge with RAG, Agents and Auto-Wiki

## 📌 Overview

[**WeKnora**](https://weknora.weixin.qq.com) is an open-source, LLM-powered knowledge framework built for enterprise-grade document understanding, semantic retrieval, and autonomous reasoning.

It is organized around three core capabilities: **RAG-based Quick Q&A** for everyday lookups, a **ReAct Agent** that autonomously orchestrates retrieval, MCP tools and web search to handle complex multi-step tasks, and a brand-new **Wiki Mode** in which agents distill raw documents into a self-maintaining, interlinked markdown knowledge base with an interactive knowledge graph, complete with manual editing, revision history and one-click rollback. Knowledge curation is equally hands-on: a **tree-structured folder view** preserves the directory layout of uploads, and **chunk editing with revision history** lets retrieval chunks be edited, diffed and reverted like documents. Combined with multi-source ingestion (Feishu wiki / Feishu Drive / Notion / Yuque / RSS, and growing), **website embed widgets** for publishing agents to external sites, **scoped API keys with a principal model** for programmatic integrations, **multi-instance storage backends** per workspace for flexible data placement, 20+ LLM provider integrations, full Langfuse observability plus a **runtime task-queue dashboard with worker-pool governance**, **enterprise-ready multi-workspace RBAC** (4-tier role matrix + per-resource ownership + per-workspace audit log), and a fully self-hostable modular architecture, WeKnora turns scattered documents into a queryable, reasoning-capable, continuously evolving knowledge asset.

The framework supports auto-syncing knowledge from Feishu, Notion, and Yuque (more data sources coming soon), handles 10+ document formats including PDF, Word, images, and Excel, and can serve Q&A directly through IM channels like WeCom, Feishu, Slack, and Telegram. It is compatible with major LLM providers including OpenAI, DeepSeek, Qwen (Alibaba Cloud), Zhipu, Hunyuan, Gemini, MiniMax, NVIDIA, and Ollama. Its fully modular design allows swapping LLMs, vector databases, and storage backends, with support for local and private cloud deployment ensuring complete data sovereignty. WeKnora also integrates with **Langfuse** for comprehensive observability into agent reasoning, token usage, and pipeline tracing.


## ✨ Latest Updates

- **v0.7.2** — Launched the **official product documentation site** (VitePress; six sections, ~50 pages covering ~360 API endpoints and ~150 environment variables, with standalone Docker/Nginx deployment, quickstart sample data and a local MCP demo); **knowledge base folder tree** (upload paths stored as first-class data, browse/rename/re-file documents like a file manager); **chunk editing with revision history** (edit retrieval chunks in the UI, per-version diff and rollback, automatic reindexing, plus custom document metadata); **Wiki page revision history** (snapshots + line-level diff + one-click rollback + in-browser manual editing); **directly loadable file URLs** via `resource_urls=public` / `RESOURCE_URL_MODE` (third-party apps render images and files without a second authenticated proxy call); **Feishu Drive data source** and docx sync through the blocks API; batch document tagging; **MCP Server 1.1.x** (migrated to the mcp 2.x high-level API, official PyPI package `tencent-weknora-mcp`, new `create_knowledge_from_text` and `list_shared_knowledge_bases` for 29 tools total); AWS S3 default credential chain (IAM Role / IRSA); local HTML upload parsing; QQBot markdown replies; new PR CI checks for app / frontend / docreader / mcp-server. Plus large-scale router and `modelcontext` refactors, rerank and chunking quality work, and broad stability fixes. See [`CHANGELOG.md`](./CHANGELOG.md).
- **v0.7.1** — New **Yunzhijia (云之家) IM integration** (WebSocket + image messages + markdown replies); **Volcengine rerank** provider (with request batching) and **Zhipu AI web search** provider; **platform-scoped API keys** for control-plane automation (tenant management, system settings, runtime queues, audit logs); **per-KB activity audit trail**; FAQ management enhancements (filtering, tagging, export, import tracking); **Langfuse OTLP/OTel tracing** migration with W3C traceparent propagation; chat header actions with one-click **Markdown export** and wiki tool results in the references drawer; prompt-cache observability; session channel governance (admin-scoped IM/embed/API sessions); resilient Feishu large-wiki sync; and removal of the legacy Neo4j conversation-memory dependency. Plus broad slug-integrity, SSRF-transport, and state-sync hardening. See [`CHANGELOG.md`](./CHANGELOG.md).
- **v0.7.0** — Fine-grained **scoped API keys & principal model** (capability-level grants + per-KB restriction + API integration playground); **runtime task-queue observability dashboard & worker-pool governance** (per-stage pools + per-model concurrency governors + failed-task inspection/retry); **multi-instance storage backends** (multiple storage instances per workspace, per-KB binding, default instance); **session-scoped temporary attachments** (async image/doc parsing + combined limits); question & follow-up suggestions; stable resource registry with LLM-context alias compaction; `@Skill / @MCP` mentions with scoped agent runtime; mid-conversation MCP OAuth; QQBot & Lark (Feishu International) IM integration; Redis TLS; Requesty model provider + Keenable web search; tenantless provisioning & gated self-service workspaces; admin password reset; knowledge base duplicate flow; `weknora` CLI v0.10. Plus broad security hardening (SSRF, secret redaction, SQL validation, IDOR). See [`CHANGELOG.md`](./CHANGELOG.md).
- **v0.6.3** — Website embed widget & Integrations Center (secure-mode token exchange + rate limits); chat experience overhaul (citation popovers, RAG pipeline progress, streaming markdown); document multi-tag & batch reparse; Wiki folders & hierarchy navigation; RSS data source; MCP OAuth2; EPUB / MHTML parsing; agent model-readiness checks; model test debugger; session source filter; workspace deletion UI. See [`CHANGELOG.md`](./CHANGELOG.md).
- **v0.6.2** — Per-upload process configuration with upload-confirm dialog; document reparse with `process_config`; `weknora` CLI v0.9 (bundled Agent Skills, `session stop`, auth/profile harmonization); KB marquee multi-select; HNSW index for 1024-dim pgvector embeddings; chat resources store refactor; Langfuse-only tracing (Jaeger removed). See [`CHANGELOG.md`](./CHANGELOG.md).
- **v0.6.1** — Document parsing trace timeline (Langfuse-style span tree with stage-by-stage progress + stop-parse); OpenSearch vector store driver; declarative built-in models via YAML; system admin & consolidated platform settings + audit log; new-user onboarding guide; settings UI redesign; `weknora` CLI v0.7 / v0.8 (agent-first wire contract, NDJSON, `--dry-run`); OpenDataLoader + PaddleOCR-VL parsers; MCP server multi-transport (stdio / SSE / HTTP); per-model thinking-mode config; Tencent LKEAP rerank + native Gemini embeddings + MiniMax-M3. See [`CHANGELOG.md`](./CHANGELOG.md).
- **v0.6.0** — Workspace RBAC (4-tier role matrix `Owner` / `Admin` / `Contributor` / `Viewer` + per-KB ownership + per-workspace audit log), workspace member management & multi-workspace UX, self-service workspaces; `weknora` CLI v0.4 GA with `mcp serve`; KB retrieval fan-out across vector stores; AES-256-GCM credential encryption + docreader gRPC TLS + Token; Zhipu embedder + Huawei OBS; server-side user preferences; Go 1.26.0. See [`docs/RBAC说明.md`](./docs/RBAC说明.md) and [`CHANGELOG.md`](./CHANGELOG.md).
- **v0.5.2** — Wiki ingest scales to 40k-document KBs (task queue + DLQ); MCP human-in-the-loop tool approval; Anthropic / Apache Doris / Tencent VectorDB / KS3 / SearXNG backends; adaptive 3-tier chunking with live preview; global ⌘K command palette; Yuque connector + WeChat Mini Program; `weknora` CLI preview.
- **v0.5.1** — Knowledge-base batch management; workspace-wide IM channels overview; session search + user-scoped pinning; unified Model / Web Search / MCP settings cards; per-agent LLM timeout; desktop workspace switching.
- **v0.5.0** — Wiki Mode GA — agents auto-generate structured, interlinked Markdown wiki pages with a knowledge graph; wiki browser + visual graph in the UI.
- **v0.4.0** — WeKnora Cloud (hosted LLM + parsing); Chrome Extension; ClawHub Skill; WeChat IM; attachment processing; Azure OpenAI / Alibaba OSS; Notion connector; Baidu + Ollama web search; VectorStore management.
- **v0.3.6** — ASR (audio); Feishu data-source auto-sync; OIDC; IM quote-reply context + thread-based sessions; document summarization; Tavily search; parallel tool calling; agent @mention scope restriction.
- **v0.3.5** — Telegram / DingTalk / Mattermost IM; IM slash commands + QA queue; suggested questions; VLM auto-describe MCP tool images; Novita AI; channel tracking.
- **v0.3.4** — WeCom / Feishu / Slack IM; multimodal image support; NVIDIA model API; Weaviate; AWS S3; AES-256-GCM API-key encryption; built-in MCP service; hybrid-search optimization; `final_answer` tool.
- **v0.3.3** — Parent-child chunking; KB pinning; fallback response; passage cleaning for rerank; storage auto-creation; Milvus.
- **v0.3.2** — Knowledge Search entry; per-source parser & storage engine config; image rendering in local storage; document preview; Volcengine TOS; Mermaid rendering; batch session management; memory graph preview.
- **v0.3.0** — Shared Space; Agent Skills + sandboxed execution; custom agents; Data Analyst agent; thinking mode; Bing / Google web search; API Key auth; Helm chart; Korean i18n; Qdrant.
- **v0.2.0** — Agent Mode (ReACT); multi-type knowledge bases (FAQ + document); conversation strategy config; DuckDuckGo web search; MCP tool integration; new UI with agent mode switching; MQ async task management.


## 📱 Interface Showcase



| **💬 Intelligent Q&A Conversation** |  |
| --- | --- |
| **📖 Wiki Browser** | **🕸️ Wiki Knowledge Graph** |
| **🕘 Wiki Page Revision History & Rollback** | **✂️ Chunk Editing & Revision History** |
| **📁 Folder Tree & Batch Operations** | **🤖 Agent Mode · Tool Call Process** |
| **🔭 Observability · Langfuse Tracing** |  |



## 🏗️ Architecture



Fully modular pipeline from document parsing, vectorization, and retrieval to LLM inference — every component is swappable and extensible. Supports local / private cloud deployment with full data sovereignty and a zero-barrier Web UI for quick onboarding.

## 🧩 Feature Overview

**Intelligent Conversation**

| Capability | Details |
|------------|---------|
| Intelligent Reasoning | ReACT progressive multi-step reasoning, autonomously orchestrating knowledge retrieval, MCP tools, and web search |
| Quick Q&A | RAG-based Q&A over knowledge bases for fast and accurate answers |
| Wiki Mode | Agent-driven auto-generation of structured, interlinked markdown Wiki pages from raw documents; in-browser manual editing, page revision history, line-level diff and one-click rollback |
| Tool Calling | Built-in tools, MCP tools (incl. OAuth2 remote services, mid-conversation OAuth), web search; `@Skill / @MCP` mentions to scope the agent runtime per turn |
| Conversation Strategy | Online Prompt editing, retrieval threshold tuning, multi-turn context awareness, per-agent citation output toggle |
| Suggested Questions | Auto-generated question suggestions and after-answer follow-ups based on knowledge base content |
| Temporary Attachments | Session-scoped image / document uploads with async parsing for one-off Q&A, with a combined image + attachment limit |
| Citations & RAG Progress | Inline citation popovers and a references drawer (web / KB source distinction), shared markdown rendering, and stage-by-stage RAG pipeline progress in chat |
| Session Management | Filter and group sidebar sessions by source (Web / IM / Embed), with inline session-title rename |

**Knowledge Management**

| Capability | Details |
|------------|---------|
| Knowledge Base Types | FAQ / Document / Wiki with folder import, URL import, multi-tag management, and online entry |
| Folder Tree | Folder uploads keep their original directory structure, with a sidebar tree for browsing, folder rename, and re-filing documents into another folder |
| Chunk Editing & Revisions | Edit retrieval chunks directly in the UI with per-version snapshots, diff and one-click rollback, and automatic reindexing after an edit; generated questions can be added, edited, deleted and regenerated; custom document metadata supported |
| Per-Upload Process Config | Override parser, chunking, multimodal (VLM / ASR), graph extraction, and question generation per upload batch via upload-confirm dialog or `process_config` API; reparse with new settings |
| Batch Reparse | Re-queue parsing for multiple documents at once with optional per-batch `process_config` |
| Data Source Import | Auto-sync from Feishu wiki / Feishu Drive / Lark / Notion / Yuque / RSS feeds (more data sources coming soon); incremental and full sync |
| Document Formats | PDF / Word / Txt / Markdown / HTML / EPUB / MHTML / Images / CSV / Excel / PPT / JSON |
| Retrieval Strategies | BM25 sparse / Dense retrieval / GraphRAG / parent-child chunking / HNSW-accelerated pgvector (1024-dim) / multi-dimensional indexing |
| Batch Selection & Tagging | Marquee drag-select multiple documents in the KB list for batch reparse and batch tagging (common tags pre-selected) |
| E2E Testing | Full-pipeline visualization with recall hit rate, BLEU / ROUGE metric evaluation |

**Integrations & Extensions**

| Capability | Details |
|------------|---------|
| LLMs | OpenAI / Azure OpenAI / Anthropic (Claude) / DeepSeek / Qwen (Alibaba Cloud) / Zhipu / Hunyuan / Doubao (Volcengine) / Gemini / MiniMax / NVIDIA / Novita AI / SiliconFlow / OpenRouter / Requesty / Ollama |
| Embeddings | Ollama / BGE / GTE / Zhipu / OpenAI-compatible APIs |
| Vector DBs | PostgreSQL (pgvector) / Elasticsearch / OpenSearch / Milvus / Weaviate / Qdrant / Apache Doris / Tencent VectorDB |
| Object Storage | Local / MinIO / AWS S3 (IAM Role / IRSA default credential chain) / Volcengine TOS / Alibaba Cloud OSS / Kingsoft Cloud KS3 / Huawei Cloud OBS; **multiple storage instances per workspace** with per-KB binding and a default instance |
| IM Channels | WeCom / Feishu / Lark (Feishu International) / QQBot / Slack / Telegram / DingTalk / Mattermost / WeChat / Yunzhijia |
| Website Embed | Publish agents via embed widget with domain allowlists, rate limits, and secure-mode token exchange |
| Web Search | DuckDuckGo / Bing / Google / Tavily / Baidu / Ollama / SearXNG / Keenable / Zhipu AI |
| API Integration | Scoped API keys (capability-level grants + per-KB restriction + throttled last-used tracking) with an API integration playground; MCP OAuth and embed sessions isolated per principal; `resource_urls=public` returns directly loadable file/image URLs, removing the second authenticated proxy call |
| MCP Server | Official PyPI package `tencent-weknora-mcp` with 29 tools over stdio / SSE / HTTP transports |

**Platform**

| Capability | Details |
|------------|---------|
| Deployment | Local / Docker / Kubernetes (Helm) with private and offline support |
| UI | Web UI / RESTful API / CLI (`weknora`) / Chrome Extension / Website Embed Widget / WeChat Mini Program |
| Access Control | Workspace RBAC with 4-tier role matrix (Owner / Admin / Contributor / Viewer), per-KB resource ownership, per-workspace audit log, invite-only workspaces, tenantless provisioning & gated self-service workspace creation, admin password reset (session revocation), cross-workspace superuser, scoped API keys |
| Security | AES-256-GCM at-rest encryption for API keys and MCP / data-source credentials with graceful key rotation; gRPC TLS + Token between app and docreader; Redis TLS; SSRF-safe HTTP client (data sources, URL import, redirect chains); secret redaction in responses; sandbox isolation for agent skills |
| Observability | Integrated Langfuse (sole tracing backend) for ReAct loops, token tracking, tool calls, and pipeline tracing; built-in Langfuse-style document parsing trace timeline with stage-by-stage progress; system-admin runtime task-queue dashboard (queue depth, per-model concurrency, failed-task inspection & manual retry) |
| Task Management | MQ async tasks with per-stage worker-pool governance (core / post-process / enrichment / maintenance + elastic shared pool, plus an independent Wiki pool) and per-model background concurrency governors; automatic database migration on version upgrade |
| Model Management | Centralized config, declarative built-in models via YAML, per-knowledge-base model selection, per-model thinking-mode and embedding-dimension overrides, interactive model test debugger, multi-workspace built-in model sharing, WeKnora Cloud hosted models and parsing |

## 🧩 Chrome Extension

[**WeKnora Chrome Extension**](https://chromewebstore.google.com/detail/jpemjbopikggjlmikmclgbmkhhopjdgd) lets you capture web content directly into your WeKnora knowledge base. Select text, images, or entire pages in the browser and save them as knowledge entries with one click — no copy-paste or file upload needed.


## 📱 WeChat Mini Program

The [WeKnora Mini Program](./miniprogram/README.md) provides a lightweight mobile client for configuring WeKnora API access, selecting knowledge bases, importing URLs, and asking knowledge chat from WeChat.


## 🦞 ClawHub Skill

[**WeKnora ClawHub Skill**](https://clawhub.ai/lyingbug/weknora) is a WeKnora skill published on the ClawHub platform. Once installed, it enables document import (file / URL / Markdown), hybrid search (vector + keyword) across knowledge bases, and knowledge entry management — all through the WeKnora REST API.

- **Document Import** — Upload files, import web pages, or write Markdown knowledge via the agent
- **Hybrid Search** — Search within or across knowledge bases with vector + keyword retrieval
- **Knowledge Management** — List, browse, edit, and delete knowledge entries programmatically

## ⌨️ Command-Line Interface

`weknora` is the official CLI for driving the API from a terminal or an AI
agent. It is **agent-first**: every command emits a stable JSON envelope by
default (with typed error codes mapped to exit codes), and `--format text`
renders for humans. It also serves a curated MCP tool surface
(`weknora mcp serve`) and ships bundled Agent Skills.

```bash
weknora profile add prod --host https://kb.example.com --use
weknora auth login
weknora kb list
weknora link --kb my-knowledge-base    # bind the current directory
weknora doc upload notes.md
weknora chat "summarise the design doc"
```

For headless / CI use, set `WEKNORA_API_KEY` + `WEKNORA_HOST` and skip
`auth login` entirely — no credentials written to disk.

See [`cli/README.md`](./cli/README.md) for install + 5-minute quickstart and
[`cli/AGENTS.md`](./cli/AGENTS.md) for the operational contract AI agents rely on.

## 🚀 Getting Started

### 🛠 Prerequisites

- [Docker](https://www.docker.com/) & [Docker Compose](https://docs.docker.com/compose/)
- [Git](https://git-scm.com/)

### 📦 Installation & Launch

```bash
git clone https://github.com/Tencent/WeKnora.git
cd WeKnora
cp .env.example .env   # Edit .env as needed, see comments in the file
docker compose pull     # Pull the latest images
docker compose up -d    # Start core services
```

Once started, visit **http://localhost** to get started.

> To use a local Ollama model, run `ollama serve > /dev/null 2>&1 &` first.

### 🔄 Upgrading

If you already have WeKnora running and downloaded a newer release:

```bash
# Set WEKNORA_VERSION in .env to the target release (e.g. 0.7.0), or keep latest
docker compose pull     # Pull images matching WEKNORA_VERSION
docker compose up -d    # Recreate containers with new images
```

> `docker compose up -d` alone reuses locally cached images and may leave the UI version out of sync with the release you downloaded.

### 🔧 Optional Services (Docker Compose Profiles)

Add `--profile` flags to enable additional components. Multiple profiles can be combined:

| Profile | Description | Command |
|---------|-------------|---------|
| _(default)_ | Core services | `docker compose pull && docker compose up -d` |
| `full` | All features | `docker compose --profile full pull && docker compose --profile full up -d` |
| `neo4j` | Knowledge Graph (Neo4j) | `docker compose --profile neo4j pull && docker compose --profile neo4j up -d` |
| `minio` | Object Storage (MinIO) | `docker compose --profile minio pull && docker compose --profile minio up -d` |
| `langfuse` | Tracing (Langfuse) | `docker compose --profile langfuse pull && docker compose --profile langfuse up -d` |

Combine profiles: `docker compose --profile neo4j --profile minio pull && docker compose --profile neo4j --profile minio up -d`

Stop services: `docker compose down`

### 🌐 Service URLs

| Service | URL |
|---------|-----|
| Web UI | `http://localhost` |
| Backend API | `http://localhost:8080` |
| Langfuse Tracing | `http://localhost:3000` |

## 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:

- **Zero-code Deployment**: Simply upload knowledge to quickly deploy intelligent Q&A services within the WeChat ecosystem, achieving an "ask and answer" experience
- **Efficient Question Management**: Support for categorized management of high-frequency questions, with rich data tools to ensure accurate, reliable, and easily maintainable answers
- **WeChat Ecosystem Integration**: Through the WeChat Dialog Open Platform, WeKnora's intelligent Q&A capabilities can be seamlessly integrated into WeChat Official Accounts, Mini Programs, and other WeChat scenarios, enhancing user interaction experiences



## 📘 API Reference

**Official product documentation**: [`website-docs/`](./website-docs/README.md) — the complete documentation set organized as Getting Started → Architecture → Features → API → Clients → Development, covering ~360 API endpoints, ~150 environment variables, and 9 extension points. The directory is also a VitePress site: run `cd website-docs && npm install && npm run dev` to preview locally, or deploy it standalone with the `Dockerfile` inside.

Troubleshooting FAQ: [Troubleshooting FAQ](./docs/QA.md)

Detailed API documentation is available at: [API Docs](./docs/api/README.md)

Product plans and upcoming features: [Roadmap](./docs/ROADMAP.md)

## 🧭 Developer Guide

### ⚡ Fast Development Mode (Recommended)

If you need to frequently modify code, **you don't need to rebuild Docker images every time**! Use fast development mode:

```bash
# Start infrastructure
make dev-start

# Start backend (new terminal)
make dev-app

# Start frontend (new terminal)
make dev-frontend
```

**Development Advantages:**
- ✅ Frontend modifications auto hot-reload (no restart needed)
- ✅ Backend modifications quick restart (5-10 seconds, supports Air hot-reload)
- ✅ No need to rebuild Docker images
- ✅ Support IDE breakpoint debugging

**Detailed Documentation:** [Development Environment Quick Start](./docs/开发指南.md)


## 🤝 Contributing

Welcome to submit [Issues](https://github.com/Tencent/WeKnora/issues) or Pull Requests.

**Process:** Fork → Create branch → Commit changes → Open PR

**Standards:** Format code with `gofmt`, follow [Conventional Commits](https://www.conventionalcommits.org/) (`feat:` / `fix:` / `docs:` / `test:` / `refactor:`)

### Validation

For a focused PR, validate the changed scope first:

```bash
git fetch origin main
git diff --check origin/main...HEAD
golangci-lint run --new-from-rev=origin/main ./...
go test ./path/to/changed/package -count=1
```

Run `gofmt` on changed Go files before committing. For frontend changes, run the relevant tests from `frontend/` and use `npm run type-check` when the change affects TypeScript or Vue components.

The full maintainer gate remains:

```bash
make fmt
make lint
make test
```

`make fmt` formats the entire Go repository, so run it only with a clean worktree and review the resulting diff. Some full-suite tests require local infrastructure or service configuration. If a full check fails for an unrelated baseline or environment reason, include the exact command and failure in the PR while still providing passing targeted tests for your change.

## 🔒 Security Notice

**Important:** Starting from v0.1.3, WeKnora includes login authentication functionality to enhance system security. For production deployments, we strongly recommend:

- Deploy WeKnora services in internal/private network environments rather than public internet
- Avoid exposing the service directly to public networks to prevent potential information leakage
- Configure proper firewall rules and access controls for your deployment environment
- Regularly update to the latest version for security patches and improvements

## 👥 Contributors

Thanks to these excellent contributors:

[](https://github.com/Tencent/WeKnora/graphs/contributors)

## 📄 License

This project is licensed under the [MIT License](./LICENSE).
You are free to use, modify, and distribute the code with proper attribution.

## 📈 Project Statistics

<a href="https://www.star-history.com/#Tencent/WeKnora&type=date&legend=top-left">
 
   
   
   
 
</a>

---

## File: cli/README.md

# weknora — WeKnora CLI

A command-line interface for the WeKnora RAG knowledge-base server. Lets you
authenticate, manage knowledge bases and documents, run hybrid search, and
ask streaming RAG questions from your terminal or from an AI agent.

```bash
$ weknora --help
Command-line client for the WeKnora RAG server. Manage knowledge bases
and documents, run hybrid search, chat with grounded answers, or expose
a curated read-only MCP tool surface for AI agents.

Available Commands:
  agent       Manage custom agents (CRUD + status/check)
  api         Make a raw API request to the WeKnora server
  auth        Manage authentication credentials and profiles
  chat        Ask a streaming RAG question against a knowledge base
  chunk       Manage document chunks (RAG retrieval debug)
  completion  Generate the autocompletion script for the specified shell
  config      Inspect the CLI's resolved configuration
  doc         Manage documents in a knowledge base
  doctor      Run 4 self-checks: base URL, auth, server version, credential storage
  exit-codes  Exit code matrix and the agent action for each
  help        Help about any command
  kb          Manage knowledge bases
  link        Bind the current directory to a knowledge base
  mcp         Run weknora as a Model Context Protocol server
  message     Inspect and manage messages inside chat sessions
  model       Manage models (list / view / create / update / delete)
  profile     Manage CLI profiles (named connection targets)
  schema      Machine-readable contract for a command (or the whole surface)
  search      Search across chunks, knowledge bases, documents, or sessions
  session     Manage chat sessions
  skills      List and install the bundled Agent Skills
  unlink      Remove the directory's knowledge-base binding
  version     Show CLI build metadata
```

The wire contract for AI agents is documented [below](#wire-contract).
For contributing to the CLI source, see [AGENTS.md](AGENTS.md).

---

## Install

### From source

Requires Go 1.26+.

```bash
git clone https://github.com/Tencent/WeKnora.git
cd WeKnora/cli
go build -o weknora .
sudo mv weknora /usr/local/bin/   # or anywhere on $PATH
```

### Pre-built binaries

Building from source is the supported install method today. Pre-built binaries,
`go install`, and a Homebrew formula are planned to accompany a tagged release;
until then, use the from-source build above.

---

## 5-minute quickstart

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Agent quick start

For AI agents (any MCP-capable host) integrating WeKnora:

1. Install: build from source (see [Install](#install))
2. Authenticate. In a sandbox / CI, the **stateless** path needs no `auth login`
   and writes nothing to disk — just set two env vars:
   ```bash
   export WEKNORA_API_KEY="sk-…"   # or WEKNORA_TOKEN for a bearer JWT
   export WEKNORA_HOST="https://kb.example.com"
   weknora kb list                 # already authenticated
   ```
   Or, for a persisted local profile:
   ```bash
   weknora profile add prod --host <server-url> --use
   weknora auth login
   ```
3. Register MCP in the host's MCP config:
   ```json
   {"mcpServers": {"weknora": {"command": "weknora", "args": ["mcp", "serve"]}}}
   ```
4. Read the [wire contract](AGENTS.md#wire-contract-for-ai-agents) before
   parsing `--format json` output.
5. Read the [exit-10 anti-patterns](AGENTS.md#exit-10-anti-patterns) before
   any destructive call.

**Bundled Agent Skills.** This CLI ships [Agent Skills](https://agentskills.io/specification)
under [`skills/`](skills/) that teach an agent to drive WeKnora without trial and error:

- [`weknora-shared`](skills/weknora-shared/SKILL.md) — **read first**: auth/profile
  sequence, `--kb` resolution, the JSON-envelope + exit-code contract, the exit-10
  protocol, `--dry-run`, and CLI-vs-MCP selection.
- [`weknora-rag-search`](skills/weknora-rag-search/SKILL.md) — when to use `chat`
  vs `session ask` vs `search chunks`, plus retrieval gotchas.

Install them with the CLI (the skills are embedded in the binary, no checkout
needed):

```bash
weknora skills install                      # writes to ~/.claude/skills
weknora skills install --dir <agent-skills-dir> --force   # other agents / overwrite
weknora skills list --format json           # what would be installed
```

Existing files are left untouched without `--force`; `--dry-run` previews the
file list. Each skill's frontmatter records the CLI version it was
`tested_against`; a CI parity test (`internal/skillparity`) fails if a skill
ever references a command, flag, or MCP tool the CLI no longer has.

---

## Multi-profile

`profile.*` manages profile *records* (positional `<name>`); `auth.*` operates
on the *active* profile (override per-invocation with the global `--profile`
flag). Create a profile first, then authenticate it:

```bash
weknora profile add prod    --host https://prod.example.com --use     # add + switch
weknora auth login                                                    # authenticate active (prod)

weknora profile add staging --host https://staging.example.com        # add (stays inactive)
echo "sk-..." | weknora --profile staging auth login --with-token     # authenticate staging

weknora auth list
weknora profile use prod                                              # switch back
```

Credentials are persisted to your OS keyring (Keychain on macOS, libsecret on
Linux, Wincred on Windows) when available, otherwise to a 0600-mode file
under `$XDG_CONFIG_HOME/weknora/secrets/`. The active profile lives in
`~/.config/weknora/config.yaml`.

To remove a profile's stored credentials:

```bash
weknora auth logout                       # active profile
weknora --profile staging auth logout     # specific profile
weknora auth logout --all
```

---

## Wire contract

Designed to be AI-agent-first. Stable across minor releases; breaking
changes announced in the changelog and the corresponding
`weknora --version` bump. This section is the human overview; the complete,
authoritative contract (envelope field stability, error taxonomy, streaming,
confirmation and dry-run protocols) lives in **[AGENTS.md](AGENTS.md)**.

### Streams

- **stdout** is the data channel: bare JSON with `--format json`, or
  human-formatted output. Never carries error text.
- **stderr** is logs, progress, warnings, and errors. A non-empty
  stderr does **not** mean failure — read the exit code.

### JSON output

Every command supports `--format json`, wrapping the resource in the
symmetric envelope `{ok, data, meta?}` — `data` is an array for `list` /
`search`, a single object for `view` and write outcomes. `--jq` runs
against the whole envelope, so reach into list items with `.data[]`:

```bash
weknora kb list --format json                              # {"ok":true,"data":[{"id":"kb_x",…}],"meta":{"count":1,…}}
weknora kb view kb_x --format json                         # {"ok":true,"data":{"id":"kb_x","name":"Eng",…}}
weknora kb list --format json --jq '.data[] | {id, name}'  # project listed fields out of each item
weknora kb list --format json --jq '.data[].id'            # ids only
weknora kb list --format json --jq '.meta.count'           # number returned
```

`--format ndjson` is also accepted for streaming list commands; each
element is emitted as its own JSON line. `--format json` is the default
regardless of TTY — running `weknora kb list | jq` works without an
explicit flag. Use `--format text` for human-readable output.

### Errors

On failure, stdout stays empty and the typed error goes to stderr in
this format:

```
<code.namespace>: <message>[: <wrapped cause>]
hint: <actionable next-step>
```

Example:

```
auth.unauthenticated: fetch current user: HTTP error 401: ...
hint: run `weknora auth login`
```

Under `--format json` the same failure is the typed error envelope on stderr
(`{ok:false, error:{type, exit_code, hint?, retry_argv?, …}}`) — see
[AGENTS.md §1.4](AGENTS.md) for the field-by-field contract and the full code
taxonomy.

### Exit codes

| Code | Meaning | Agent action |
|---|---|---|
| `0`   | success                                                | continue |
| `1`   | typed `local.*` / `operation.failed` / unclassified    | read stderr, decide retry/abort |
| `2`   | flag / argument validation error                       | re-check `weknora <cmd> --help` |
| `3`   | `auth.*` (token missing / expired / forbidden)         | re-auth, then retry |
| `4`   | `resource.not_found`                                   | verify the resource id |
| `5`   | `input.*` (other than `confirmation_required`)         | adjust args, retry |
| `6`   | `server.rate_limited`                                  | back off, retry |
| `7`   | `server.*` / `network.*`                               | transient — retry with backoff |
| `10`  | **`input.confirmation_required`** (high-risk write)    | ask the human, retry with `-y` only after explicit approval |
| `124` | `operation.timeout` (e.g. `doc wait --timeout` reached) | raise `--timeout` or check the underlying job |
| `130` | cancelled by signal — typed `operation.cancelled` errors exit 1; `main.go` promotes the process exit to 130 when the root context was signal-cancelled (SIGINT / SIGTERM) | stop, do not retry |

Run `weknora exit-codes` for the machine-readable matrix (JSON); `weknora help exit-codes` for the human-readable table.

**Exit 10** is the wire-level signal for "destructive write needs
explicit confirmation". Pass `-y/--yes` on `kb delete` /
`doc delete` (including `--all --kb=`) / `session delete` /
`profile remove` (on the current profile) / `agent delete` /
`chunk delete` when running headless.
**Never auto-add `-y` without the user's explicit go-ahead** — exit 10
is the guard against unintended writes.

### Other AI-agent ergonomics

- For chat / session ask in AI-agent contexts, pass `--format json` for a
  bounded answer-event envelope. Add `--reference` for indexed citations,
  `--verbose` for reasoning/tools/lifecycle events, or `--format ndjson` for
  the unmodified raw stream.
- `--format json` composes with the global `--profile <name>` for
  single-shot profile overrides without disk writes.
- `weknora mcp serve` exposes a curated read-only tool surface over
  stdio MCP for any MCP-compatible client.
- `weknora schema` enumerates every command with its `used_for`, and
  `weknora schema <cmd path>` (e.g. `weknora schema doc update`) prints that
  command's full contract — `used_for`, `required_flags`, `examples`,
  `output`, `warnings`, `risk`, and local `flags` — as the standard envelope,
  so an agent can discover the surface without scraping `--help` prose.

---

## Advanced operations not exposed as flags

WeKnora CLI exposes top use cases as polished commands; deep
configuration goes through the raw HTTP passthrough. CLI flag coverage
targets common workflows, not 1:1 API parity. Examples of deep
operations that intentionally go through `weknora api`:

- **Tuning a KB's nested config** — chunking strategy, summary model,
  multimodal extraction defaults, FAQ thresholds, VLM model. Use
  `weknora api PUT /api/v1/knowledge-bases/ --input -` with a JSON
  body matching the server's `UpdateKnowledgeBaseRequest`. (Note: the
  storage provider is set once at create time via
  `kb create --storage-provider <name>` and is not updatable.)
- **Per-request `chat` parameters** — multi-KB scope, summary model
  override, image attachments, web search toggle. Use `weknora api POST
  /api/v1/knowledge-chat/<session-id> --input -`.
- **Per-request `session ask --agent` overrides** — same shape via
  `weknora api POST /api/v1/agent-chat/<session-id> --input -`.
- **Operations without a CLI verb** — register / change-password /
  OIDC flows, organization / sharing endpoints, tenant management.

`weknora api --help` documents the raw passthrough. Run
`weknora doctor` first to verify auth and base URL.

---

## Dry-run preview

Add `--dry-run` to any mutation command to preview the would-be action without executing it. Useful for verifying flag/arg parsing before committing to a destructive operation, or for agent-side action planning.

```bash
# Preview a kb create without actually creating
weknora kb create --name "test-kb" --description "for review" --dry-run

# Output (single line; pretty-printed here for readability):
# {
#   "ok": true,
#   "meta": {
#     "dry_run": true,
#     "plan": {
#       "action": "kb.create",
#       "args": {"name": "test-kb", "description": "for review"}
#     }
#   }
# }
# Exit code: 0
```

dry-run is **offline**: no network calls, no file IO, no credential touches. Works without an active profile.

For destructive commands, dry-run does NOT trigger the exit-10 confirmation flow:

```bash
weknora kb delete kb_xxxx --dry-run   # exit 0, no prompt
weknora kb delete kb_xxxx             # exit 10, prompts for -y
```

For the `api` command, dry-run requires explicit write method (POST/PUT/PATCH/DELETE); GET returns FlagError:

```bash
echo '{"name":"foo"}' | weknora api -X POST /api/v1/knowledge-bases --input - --dry-run   # OK
weknora api /api/v1/knowledge-bases --dry-run                                              # exit 2: requires explicit -X
```

---

## Resuming streams

The `weknora session resume` command resumes an SSE event stream for an existing assistant message. Useful for network-blip recovery or polling long-running agent invocations:

```bash
# Original streaming call captures session_id + message_id from init event:
weknora session ask "..." --agent ag_xxxx --format ndjson | tee /tmp/stream.ndjson
# {"type":"init","session_id":"sess_abc","message_id":"msg_xyz"}
# ... events flow ...
# [network blip]

# Resume the same stream:
weknora session resume sess_abc --message msg_xyz
# Server REPLAYS all stored events from the start, then tails new ones.
# Agent must dedupe (by message_id or event hash) to avoid double-processing.
```

### Tool-approval unlock chain

An agent run may pause the stream on a tool-approval event until a human approves or rejects the pending tool call. The unlock sequence:

```bash
# 1. Stream pauses with a tool-approval event carrying a pending_id.
# 2. Surface the pending tool call to the user; get explicit go-ahead.
weknora session tool-approval resolve pend_xxx -y                      # approve
# weknora session tool-approval resolve pend_xxx --reject --reason "..." -y  # reject
# 3. Resume the stream — server replays + tails from where the run was blocked.
weknora session resume sess_abc --message msg_xyz
```

Pass `--modified-args '{"key":"value"}'` to replace tool arguments on approve (must be a non-empty JSON object). Never auto-pass `-y` — the approval is the exit-10 human-in-the-loop gate.

Server-side buffer TTL: 1 hour for redis mode; process lifetime for memory mode (default). After TTL, expect `local.sse_stream_aborted` typed error.

See `cli/AGENTS.md` "Stream recovery" section for the full agent contract.

---

## Health check

Run `weknora doctor` for a 4-status diagnostic (OK / warn / fail /
skip) covering base URL reachability, authentication, server-CLI
version skew, and credential storage backend. Add `--format json` for
machine-readable output, `--offline` to skip network checks.

For per-resource verification, the `status` / `check` verb pair gives
a fast vs deep choice:

| Verb | Cost | Use |
|---|---|---|
| `weknora kb status <kb-id>`     | 1 HTTP    | live counts / processing flag |
| `weknora kb check <kb-id>`      | 1+N HTTP  | adds `failed_count` via doc-list page-walk |
| `weknora agent status <agent-id>` | 1 HTTP  | reachable / model_id |
| `weknora agent check <agent-id>`  | 1+N HTTP | also probes every KB in the agent's scope |

`weknora doc wait <doc-id> [<doc-id>...]` blocks until each document
reaches a terminal `parse_status` (completed or failed). Exit codes:
0 (all completed), 1 (any failed), 124 (`--timeout` reached), 130
(Ctrl-C / SIGTERM). Multi-target is polled concurrently (max 5 in
flight; pipe through `xargs -P` for more).

---

## Development

```bash
# Run unit + contract tests
go test ./...

# Run the real-server e2e suite (requires WEKNORA_E2E_HOST + token env vars)
go test -tags acceptance_e2e ./acceptance/e2e/...

# Static analysis
go vet ./...
```

CI (`.github/workflows/cli.yml`) runs build + unit + contract tests on Linux /
macOS / Windows × Go 1.26, path-filtered to changes under `cli/`.

---

## Contributing / Reporting issues

- **Bugs and feature requests**: file an issue at
  [github.com/Tencent/WeKnora/issues](https://github.com/Tencent/WeKnora/issues).
- **Security disclosures**: see the repository-level
  [SECURITY.md](../SECURITY.md). Do not file public issues for
  security findings.
- **Pull requests**: the developer guide for editing the CLI lives in
  [AGENTS.md](AGENTS.md) (build / test / command-surface design SOP /
  CRUD flag conventions). Run `go test ./... -race -count=1` and `go vet ./...`
  before submitting.

---

## License

MIT — see the repository [LICENSE](../LICENSE).

---

## File: client/README.md

# WeKnora HTTP 客户端

这个包提供了与WeKnora服务进行交互的客户端库，支持所有基于HTTP的接口调用，使其他模块更方便地集成WeKnora服务，无需直接编写HTTP请求代码。

## 主要功能

该客户端包含以下主要功能模块：

1. **会话管理**：创建、获取、更新和删除会话
2. **知识库管理**：创建、获取、更新和删除知识库
3. **知识管理**：添加、获取和删除知识内容
4. **空间管理**：空间的CRUD操作
5. **知识问答**：支持普通问答和流式问答
6. **Agent问答**：支持基于Agent的智能问答，包含思考过程、工具调用和反思
7. **分块管理**：查询、更新和删除知识分块
8. **消息管理**：获取和删除会话消息
9. **模型管理**：创建、获取、更新和删除模型

## 使用方法

### 创建客户端实例

```go
import (
    "context"
    "github.com/Tencent/WeKnora/client"
    "time"
)

// 创建客户端实例
apiClient := client.NewClient(
    "http://api.example.com", 
    client.WithToken("your-auth-token"),
    client.WithTimeout(30*time.Second),
)
```

### 空间配置

客户端支持通过 `WithTenantID` 设置默认空间，请求时会自动携带 `X-Tenant-ID` 请求头：

```go
tenantID := uint64(10000)
apiClient := client.NewClient(
    "http://api.example.com",
    client.WithToken("your-auth-token"),
    client.WithTenantID(tenantID),
)
```

如果某个请求需要临时切换空间，可以在 `context` 中设置 `TenantID`，值可以是 `uint64`、`*uint64` 或字符串形式的数字，客户端会优先使用该值：

```go
ctx := context.WithValue(context.Background(), "TenantID", uint64(10000))
// 调用任意客户端方法时传入 ctx，即可切换到空间 10000
```

### 示例：创建知识库并上传文件

```go
// 创建知识库
kb := &client.KnowledgeBase{
    Name:        "测试知识库",
    ChunkingConfig: client.ChunkingConfig{
        ChunkSize:    500,
        ChunkOverlap: 50,
        Separators:   []string{"\n\n", "\n", ". ", "? ", "! "},
    },
    ImageProcessingConfig: client.ImageProcessingConfig{
        ModelID: "image_model_id",
    },
    EmbeddingModelID: "embedding_model_id",
    SummaryModelID:   "summary_model_id",
}

kb, err := apiClient.CreateKnowledgeBase(context.Background(), kb)
if err != nil {
    // 处理错误
}

// 上传知识文件并添加元数据
metadata := map[string]string{
    "source": "local",
    "type":   "document",
}
knowledge, err := apiClient.CreateKnowledgeFromFile(context.Background(), kb.ID, "path/to/file.pdf", metadata)
if err != nil {
    // 处理错误
}
```

### 示例：创建会话并进行问答

```go
// 创建会话
sessionRequest := &client.CreateSessionRequest{
    KnowledgeBaseID: knowledgeBaseID,
    SessionStrategy: &client.SessionStrategy{
        MaxRounds:        10,
        EnableRewrite:    true,
        FallbackStrategy: "fixed_answer",
        FallbackResponse: "抱歉，我无法回答这个问题",
        EmbeddingTopK:    5,
        KeywordThreshold: 0.5,
        VectorThreshold:  0.7,
        RerankModelID:    "rerank_model_id",
        RerankTopK:       3,
        RerankThreshold:  0.8,
        SummaryModelID:   "summary_model_id",
    },
}

session, err := apiClient.CreateSession(context.Background(), sessionRequest)
if err != nil {
    // 处理错误
}

// 普通问答
answer, err := apiClient.KnowledgeQA(context.Background(), session.ID, &client.KnowledgeQARequest{
    Query: "什么是人工智能?",
})
if err != nil {
    // 处理错误
}

// 流式问答
err = apiClient.KnowledgeQAStream(context.Background(), session.ID, &client.KnowledgeQARequest{
    Query:            "什么是机器学习?",
    KnowledgeBaseIDs: []string{knowledgeBaseID}, // 可选：指定知识库
    WebSearchEnabled: false,                      // 可选：是否启用网络搜索
}, func(response *client.StreamResponse) error {
    // 处理每个响应片段
    fmt.Print(response.Content)
    return nil
})
if err != nil {
    // 处理错误
}
```

### 示例：Agent智能问答

Agent问答提供更强大的智能对话能力，支持工具调用、思考过程展示和自我反思。

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Agent事件类型说明

| 事件类型 | 说明 | 何时触发 |
|---------|------|---------|
| `AgentResponseTypeThinking` | Agent思考过程 | Agent分析问题和制定计划时 |
| `AgentResponseTypeToolCall` | 工具调用 | Agent决定使用某个工具时 |
| `AgentResponseTypeToolResult` | 工具执行结果 | 工具执行完成后 |
| `AgentResponseTypeReferences` | 知识引用 | 检索到相关知识时 |
| `AgentResponseTypeAnswer` | 最终答案 | Agent生成回答时（流式） |
| `AgentResponseTypeReflection` | 自我反思 | Agent评估自己的回答时 |
| `AgentResponseTypeError` | 错误 | 发生错误时 |

### Agent问答测试工具

我们提供了一个交互式命令行工具用于测试Agent功能：

```bash
cd client/cmd/agent_test
go build -o agent_test
./agent_test -url http://localhost:8080 -kb <knowledge_base_id>
```

该工具支持：
- 创建和管理会话
- 交互式Agent问答
- 实时显示所有Agent事件
- 性能统计和调试信息

详细使用说明请参考 `client/cmd/agent_test/README.md`。

### Agent问答的高级用法

更多高级用法示例，请参考 `agent_example.go` 文件，包括：
- 基础Agent问答
- 工具调用跟踪
- 知识引用捕获
- 完整事件跟踪
- 自定义错误处理
- 流取消控制
- 多会话管理

```

### 示例：管理模型

```go
// 创建模型
modelRequest := &client.CreateModelRequest{
    Name:        "测试模型",
    Type:        client.ModelTypeChat,
    Source:      client.ModelSourceInternal,
    Parameters: client.ModelParameters{
        "temperature": 0.7,
        "top_p":       0.9,
    },
    IsDefault: true,
}
model, err := apiClient.CreateModel(context.Background(), modelRequest)
if err != nil {
    // 处理错误
}

// 列出所有模型
models, err := apiClient.ListModels(context.Background())
if err != nil {
    // 处理错误
}
```

### 示例：管理知识分块

```go
// 列出知识分块
chunks, total, err := apiClient.ListKnowledgeChunks(context.Background(), knowledgeID, 1, 10)
if err != nil {
    // 处理错误
}

// 更新分块
updateRequest := &client.UpdateChunkRequest{
    Content:   "更新后的分块内容",
    IsEnabled: true,
}
updatedChunk, err := apiClient.UpdateChunk(context.Background(), knowledgeID, chunkID, updateRequest)
if err != nil {
    // 处理错误
}
```

### 示例：重新解析知识

```go
// 重新解析知识（删除现有内容并重新解析）
// 适用场景：
// 1. 原始解析失败，需要重试
// 2. 更新了解析配置（如分块策略、多模态设置等），需要重新解析
// 3. 知识内容已更新，需要刷新解析结果

knowledge, err := apiClient.ReparseKnowledge(context.Background(), knowledgeID)
if err != nil {
    // 处理错误
}

// 知识将进入 "pending" 状态，异步重新解析
fmt.Printf("Knowledge ID: %s\n", knowledge.ID)
fmt.Printf("Parse Status: %s\n", knowledge.ParseStatus)      // "pending"
fmt.Printf("Enable Status: %s\n", knowledge.EnableStatus)    // "disabled"

// 可以轮询检查解析状态
for {
    time.Sleep(5 * time.Second)
    knowledge, err := apiClient.GetKnowledge(context.Background(), knowledgeID)
    if err != nil {
        // 处理错误
    }
    
    if knowledge.ParseStatus == "completed" {
        fmt.Println("Knowledge re-parsing completed!")
        break
    } else if knowledge.ParseStatus == "failed" {
        fmt.Printf("Knowledge re-parsing failed: %s\n", knowledge.ErrorMessage)
        break
    }
}
```

### 示例：取消解析

```go
// 取消正在进行的解析任务（资源紧张 / 上传错误文件时使用）
// - 已经 completed / failed 的知识不能取消
// - 已写入的分块/索引会保留，可后续调用 ReparseKnowledge 重新解析

knowledge, err := apiClient.CancelKnowledgeParse(context.Background(), knowledgeID)
if err != nil {
    // 处理错误
}
fmt.Printf("Parse Status: %s\n", knowledge.ParseStatus) // "cancelled"
```

### 示例：查看文档解析追踪（Span 树）

```go
// 获取文档解析流水线的 Span 树（root → stage → subspan）
// - attempt 传 0 表示获取最新一次解析尝试
// - 始终返回 5 个标准阶段：docreader / chunking / embedding / multimodal / postprocess
trace, err := apiClient.GetKnowledgeProcessingSpans(context.Background(), knowledgeID, 0)
if err != nil {
    // 处理错误
}
fmt.Printf("ParseStatus=%s CurrentStage=%s\n", trace.ParseStatus, trace.CurrentStage)
for _, stage := range trace.Trace.Children {
    fmt.Printf("- %s: %s (%dms)\n", stage.Name, stage.Status, stage.DurationMs)
}
```

### 示例：获取会话消息

```go
// 获取最近消息
messages, err := apiClient.GetRecentMessages(context.Background(), sessionID, 10)
if err != nil {
    // 处理错误
}

// 获取指定时间之前的消息
beforeTime := time.Now().Add(-24 * time.Hour)
olderMessages, err := apiClient.GetMessagesBefore(context.Background(), sessionID, beforeTime, 10)
if err != nil {
    // 处理错误
}
```

## 完整示例

请参考 `example.go` 文件中的 `ExampleUsage` 函数，其中展示了客户端的完整使用流程。

---

## File: client/README_EN.md

# WeKnora HTTP Client

This package provides a client library for interacting with WeKnora services, supporting all HTTP-based interface calls, making it easier for other modules to integrate with WeKnora services without having to write HTTP request code directly.

## Main Features

The client includes the following main functional modules:

1. **Session Management**: Create, retrieve, update, and delete sessions
2. **Knowledge Base Management**: Create, retrieve, update, and delete knowledge bases
3. **Knowledge Management**: Add, retrieve, and delete knowledge content
4. **Workspace Management**: CRUD operations for workspaces
5. **Knowledge Q&A**: Supports regular Q&A and streaming Q&A
6. **Chunk Management**: Query, update, and delete knowledge chunks
7. **Message Management**: Retrieve and delete session messages
8. **Model Management**: Create, retrieve, update, and delete models
9. **Evaluation Function**: Start evaluation tasks and get evaluation results

## Usage

### Creating Client Instance

```go
import (
    "context"
    "github.com/Tencent/WeKnora/client"
    "time"
)

// Create client instance
apiClient := client.NewClient(
    "http://api.example.com", 
    client.WithToken("your-auth-token"),
    client.WithTimeout(30*time.Second),
)
```

### Workspace Configuration

You can set a default workspace with `WithTenantID`; the client will automatically send the `X-Tenant-ID` header:

```go
tenantID := uint64(10000)
apiClient := client.NewClient(
    "http://api.example.com",
    client.WithToken("your-auth-token"),
    client.WithTenantID(tenantID),
)
```

If a single request needs a different workspace, set `TenantID` in the request context. The value can be a `uint64`, `*uint64`, or a numeric string, and it will take precedence over the client default:

```go
ctx := context.WithValue(context.Background(), "TenantID", uint64(10000))
// Pass ctx into any client method to switch to workspace 10000 for that request
```

### Example: Create Knowledge Base and Upload File

```go
// Create knowledge base
kb := &client.KnowledgeBase{
    Name:        "Test Knowledge Base",
    ChunkingConfig: client.ChunkingConfig{
        ChunkSize:    500,
        ChunkOverlap: 50,
        Separators:   []string{"\n\n", "\n", ". ", "? ", "! "},
    },
    ImageProcessingConfig: client.ImageProcessingConfig{
        ModelID: "image_model_id",
    },
    EmbeddingModelID: "embedding_model_id",
    SummaryModelID:   "summary_model_id",
}

kb, err := apiClient.CreateKnowledgeBase(context.Background(), kb)
if err != nil {
    // Handle error
}

// Upload knowledge file with metadata
metadata := map[string]string{
    "source": "local",
    "type":   "document",
}
knowledge, err := apiClient.CreateKnowledgeFromFile(context.Background(), kb.ID, "path/to/file.pdf", metadata)
if err != nil {
    // Handle error
}
```

### Example: Create Session and Chat

```go
// Create session
sessionRequest := &client.CreateSessionRequest{
    KnowledgeBaseID: knowledgeBaseID,
    SessionStrategy: &client.SessionStrategy{
        MaxRounds:        10,
        EnableRewrite:    true,
        FallbackStrategy: "fixed_answer",
        FallbackResponse: "Sorry, I cannot answer this question",
        EmbeddingTopK:    5,
        KeywordThreshold: 0.5,
        VectorThreshold:  0.7,
        RerankModelID:    "rerank_model_id",
        RerankTopK:       3,
        RerankThreshold:  0.8,
        SummaryModelID:   "summary_model_id",
    },
}

session, err := apiClient.CreateSession(context.Background(), sessionRequest)
if err != nil {
    // Handle error
}

// Regular Q&A
answer, err := apiClient.KnowledgeQA(context.Background(), session.ID, &client.KnowledgeQARequest{
    Query: "What is artificial intelligence?",
})
if err != nil {
    // Handle error
}

// Streaming Q&A
err = apiClient.KnowledgeQAStream(context.Background(), session.ID, "What is machine learning?", func(response *client.StreamResponse) error {
    // Handle each response chunk
    fmt.Print(response.Content)
    return nil
})
if err != nil {
    // Handle error
}
```

### Example: Managing Models

```go
// Create model
modelRequest := &client.CreateModelRequest{
    Name:        "Test Model",
    Type:        client.ModelTypeChat,
    Source:      client.ModelSourceInternal,
    Parameters: client.ModelParameters{
        "temperature": 0.7,
        "top_p":       0.9,
    },
    IsDefault: true,
}
model, err := apiClient.CreateModel(context.Background(), modelRequest)
if err != nil {
    // Handle error
}

// List all models
models, err := apiClient.ListModels(context.Background())
if err != nil {
    // Handle error
}
```

### Example: Managing Knowledge Chunks

```go
// List knowledge chunks
chunks, total, err := apiClient.ListKnowledgeChunks(context.Background(), knowledgeID, 1, 10)
if err != nil {
    // Handle error
}

// Update chunk
updateRequest := &client.UpdateChunkRequest{
    Content:   "Updated chunk content",
    IsEnabled: true,
}
updatedChunk, err := apiClient.UpdateChunk(context.Background(), knowledgeID, chunkID, updateRequest)
if err != nil {
    // Handle error
}
```

### Example: Getting Session Messages

```go
// Get recent messages
messages, err := apiClient.GetRecentMessages(context.Background(), sessionID, 10)
if err != nil {
    // Handle error
}

// Get messages before a specific time
beforeTime := time.Now().Add(-24 * time.Hour)
olderMessages, err := apiClient.GetMessagesBefore(context.Background(), sessionID, beforeTime, 10)
if err != nil {
    // Handle error
}
```

## Complete Example

Please refer to the `ExampleUsage` function in the `example.go` file, which demonstrates the complete usage flow of the client.

---

## File: dataset/README_zh.md

# QA数据集采样工具

一个全面的QA数据集采样工具，使用OpenAI的GPT模型生成答案。该工具帮助您从大规模数据集（如MS MARCO）创建高质量的问答数据集。

## 功能特性

- **智能采样**：智能地从大型数据集中采样查询、文档和相关性判断
- **答案生成**：使用OpenAI的GPT模型自动生成高质量答案
- **断点续传**：支持中断后继续生成，从上次位置开始
- **进度跟踪**：实时进度更新和统计信息
- **结果可视化**：易于阅读的问答对展示，包含完整上下文

## 安装指南

### 系统要求

- Python 3.7+
- OpenAI API密钥

### 安装依赖

```bash
pip install pandas pyarrow openai
```

### 设置环境变量

```bash
export OPENAI_API_KEY="你的openai-api-key"
# 可选：使用自定义OpenAI端点
export OPENAI_BASE_URL="https://api.openai.com/v1"
```

### 准备数据集

您可以使用任何符合格式要求的QA数据集，或下载预处理好的样本：

**使用HuggingFace/ModelScope样本**
我们提供了来自流行QA数据集的预处理样本：
- MarkrAI/eli5_sample_autorag
- MarkrAI/msmarco_sample_autorag
- MarkrAI/triviaqa_sample_autorag
- gnekt/hotpotqa_small_sample_autorag

**使用您自己的数据集**
确保您的数据集包含以下文件：
- `queries.parquet`（列：id, text）
- `corpus.parquet`（列：id, text）
- `qrels.parquet`（列：qid, pid）

## 快速开始

### 1. 从大型数据集采样

首先，从完整数据集中采样查询、文档和相关性判断的子集：

```bash
python dataset/qa_dataset.py sample \
  --queries ~/dataset/mmarco-queries.parquet \
  --corpus ~/dataset/mmarco-corpus.parquet \
  --qrels ~/dataset/mmarco-qrels.parquet \
  --nq 100 \
  --output_dir ./dataset/samples
```

### 2. 生成答案

使用OpenAI的GPT模型为采样的问答生成答案：

```bash
python dataset/qa_dataset.py generate \
  --input_dir ./dataset/samples \
  --output_dir ./dataset/samples
```

### 3. 查看结果

展示生成的问答对及其上下文：

```bash
python dataset/qa_dataset.py show \
  --input_dir ./dataset/samples \
  -n 5
```

## 详细使用说明

### 采样命令

从完整数据集中创建代表性样本。

```bash
python dataset/qa_dataset.py sample [选项]
```

**必需参数：**
- `--queries`：查询parquet文件路径（列：`id`, `text`）
- `--corpus`：语料库parquet文件路径（列：`id`, `text`）
- `--qrels`：相关性判断parquet文件路径（列：`qid`, `pid`）

**可选参数：**
- `--nq`：要采样的查询数量（默认：1000）
- `--output_dir`：采样数据输出目录（默认：./save）

**示例：**
```bash
python dataset/qa_dataset.py sample \
  --queries data/queries.parquet \
  --corpus data/corpus.parquet \
  --qrels data/qrels.parquet \
  --nq 500 \
  --output_dir ./my_sample
```

### 生成命令

使用OpenAI API为采样问题生成答案。

```bash
python dataset/qa_dataset.py generate [选项]
```

**必需参数：**
- `--input_dir`：包含采样数据的目录（queries.parquet, corpus.parquet, qrels.parquet）

**可选参数：**
- `--output_dir`：生成答案的输出目录（默认：./save）

**特性：**
- **断点续传**：中断后自动从上次位置继续
- **错误处理**：API调用失败自动重试3次
- **进度保存**：每成功生成一个答案就保存进度

**示例：**
```bash
python dataset/qa_dataset.py generate \
  --input_dir ./my_sample \
  --output_dir ./my_sample
```

### 展示命令

展示生成的问答对及完整上下文。

```bash
python dataset/qa_dataset.py show [选项]
```

**必需参数：**
- `--input_dir`：包含QA数据的目录（queries.parquet, corpus.parquet, qrels.parquet, qas.parquet, answers.parquet）

**可选参数：**
- `-n`：要展示的结果数量（默认：5）

**示例：**
```bash
python dataset/qa_dataset.py show \
  --input_dir ./my_sample \
  -n 3
```

## 输入数据格式

### 查询文件 (queries.parquet)
| 列名 | 类型 | 描述 |
|------|------|------|
| id | string | 唯一查询标识符 |
| text | string | 实际的问题文本 |

### 语料库文件 (corpus.parquet)
| 列名 | 类型 | 描述 |
|------|------|------|
| id | string | 唯一段落/文档标识符 |
| text | string | 段落/文档内容 |

### 相关性判断文件 (qrels.parquet)
| 列名 | 类型 | 描述 |
|------|------|------|
| qid | string | 查询ID（匹配queries.id） |
| pid | string | 段落ID（匹配corpus.id） |

## 输出文件

运行所有命令后，输出目录将包含：

### 采样数据
- `queries.parquet`：采样的查询子集
- `corpus.parquet`：采样的文档子集
- `qrels.parquet`：采样的相关性判断

### 生成的答案
- `answers.parquet`：生成的答案（含唯一ID）
- `qas.parquet`：问答映射（qid → aid）

## 高级用法

### 自定义OpenAI配置

您可以使用不同的OpenAI模型或端点：

```bash
# 使用GPT-4 Turbo
export OPENAI_API_KEY="你的密钥"
python dataset/qa_dataset.py generate --input_dir ./samples

# 使用Azure OpenAI
export OPENAI_API_KEY="azure密钥"
export OPENAI_BASE_URL="https://你的资源.openai.azure.com/openai/deployments/gpt-4"
python dataset/qa_dataset.py generate --input_dir ./samples
```

### 大型数据集采样

对于非常大的数据集，建议分批采样：

```bash
# 第一批
python dataset/qa_dataset.py sample --nq 1000 --output_dir ./batch1
python dataset/qa_dataset.py generate --input_dir ./batch1

# 第二批
python dataset/qa_dataset.py sample --nq 1000 --output_dir ./batch2
python dataset/qa_dataset.py generate --input_dir ./batch2
```

## 故障排除

### 常见问题

**1. OpenAI API错误**
- 确保API密钥设置正确：`echo $OPENAI_API_KEY`
- 检查API配额和账单状态
- 验证与OpenAI的网络连接

**2. 大数据集内存问题**
- 减小`--nq`参数以获得更小的样本
- 确保pandas操作有足够的RAM
- 考虑使用更小的parquet文件

**3. 文件未找到错误**
- 验证所有输入文件路径是否正确
- 确保parquet文件有正确的列名
- 检查文件权限

### 调试模式

通过添加打印语句或使用Python调试器启用详细输出：

```bash
python -m pdb dataset/qa_dataset.py sample --queries ...
```

## 示例工作流

```bash
# 1. 设置环境
export OPENAI_API_KEY="sk-..."

# 2. 从MS MARCO采样200个查询
python dataset/qa_dataset.py sample \
  --queries ~/mmarco/queries.parquet \
  --corpus ~/mmarco/corpus.parquet \
  --qrels ~/mmarco/qrels.parquet \
  --nq 200 \
  --output_dir ./marco_sample

# 3. 生成答案（根据API速率限制可能需要一些时间）
python dataset/qa_dataset.py generate \
  --input_dir ./marco_sample \
  --output_dir ./marco_sample

# 4. 查看结果
python dataset/qa_dataset.py show \
  --input_dir ./marco_sample \
  -n 10
```

## 贡献

欢迎提交问题和功能增强请求！

## 许可证

MIT许可证 - 可自由用于研究和项目。

---

## File: docreader/README.md

# DocReader Service

DocReader 是 WeKnora 项目中负责文档解析和处理的 gRPC 服务。它支持多种文档格式的读取、OCR 识别、多模态处理等功能。

## Docker Compose 环境变量配置

在 `docker-compose.yml` 文件中，docreader 服务配置了以下环境变量：

```yaml
docreader:
  image: wechatopenai/weknora-docreader:${WEKNORA_VERSION:-latest}
  environment:
    - MINIO_ENDPOINT=minio:9000
    - MINIO_PUBLIC_ENDPOINT=http://localhost:${MINIO_PORT:-9000}
    - MINERU_ENDPOINT=${MINERU_ENDPOINT:-}
    - MAX_FILE_SIZE_MB=${MAX_FILE_SIZE_MB:-}
```

### 环境变量说明

#### 1. MINIO_ENDPOINT

- **说明**: MinIO 服务的内部访问地址（容器间通信）
- **默认值**: `minio:9000`
- **用途**: DocReader 服务使用此地址连接到 MinIO 对象存储服务，用于读取和存储文档处理过程中的文件
- **配置示例**:
  ```yaml
  - MINIO_ENDPOINT=minio:9000  # Docker 网络内部地址
  ```

#### 2. MINIO_PUBLIC_ENDPOINT

- **说明**: MinIO 服务的公开访问地址（外部访问）
- **默认值**: `http://localhost:9000`
- **用途**: 用于生成可从外部访问的文件 URL，例如在文档解析后返回图片链接时使用
- **重要提示**: 
  - 如果需要从其他设备或容器访问，需要将 `localhost` 替换为实际的 IP 地址
  - 可以在 `.env` 文件中配置 `MINIO_PORT` 来自定义端口
- **配置示例**:
  ```bash
  # .env 文件
  MINIO_PORT=9000
  ```
  或直接在 docker-compose.yml 中修改：
  ```yaml
  - MINIO_PUBLIC_ENDPOINT=http://192.168.1.100:9000  # 使用实际 IP
  ```

#### 3. MINERU_ENDPOINT

- **说明**: MinerU 服务的访问地址（可选）
- **默认值**: 空（不使用 MinerU）
- **用途**: MinerU 是一个高级文档解析服务，支持更复杂的文档结构识别和处理。配置此变量后，DocReader 可以调用 MinerU 进行文档解析
- **配置示例**:
  ```bash
  # .env 文件
  MINERU_ENDPOINT=http://mineru-service:8080
  ```

#### 4. MAX_FILE_SIZE_MB

- **说明**: 允许上传的最大文件大小（单位：MB）
- **默认值**: `50` MB
- **用途**: 限制 gRPC 服务接收的文件大小，防止过大的文件导致服务崩溃或性能问题
- **配置示例**:
  ```bash
  # .env 文件
  MAX_FILE_SIZE_MB=100  # 允许最大 100MB 的文件
  ```

## 其他可配置的环境变量

除了 docker-compose.yml 中已配置的变量外，DocReader 还支持以下环境变量（可根据需要添加）：

### gRPC 配置

- `DOCREADER_GRPC_MAX_WORKERS`: gRPC 服务的最大工作线程数（默认：4）
- `DOCREADER_GRPC_PORT`: gRPC 服务监听端口（默认：50051）

### 解析器资源控制

- `DOCREADER_MARKITDOWN_MAX_WORKERS`: MarkItDown 解析的最大并发数（默认：1，设为 0 可关闭限流）
- `DOCREADER_PDF_RENDER_MAX_WORKERS`: 扫描 PDF 渲染为图片的最大并发数（默认：1，设为 0 可关闭限流）
- `DOCREADER_PDF_RENDER_DPI`: 扫描 PDF 渲染 DPI（默认：200）
- `DOCREADER_PDF_JPEG_QUALITY`: 扫描 PDF 输出 JPEG 质量（默认：85，范围会自动限制在 1-95）

### OCR / VLM

DocReader 自身不再内置 OCR 与 VLM 后端。扫描 PDF 会被渲染为 JPEG 图片后交由 Go App 侧调用 OCR/VLM 服务处理，相关配置请参考主项目文档。

### 存储配置

DocReader 支持多种存储后端：

#### MinIO/S3 存储（推荐）

- `STORAGE_TYPE`: 设置为 `minio`
- `MINIO_ACCESS_KEY_ID`: MinIO 访问密钥 ID（默认：minioadmin）
- `MINIO_SECRET_ACCESS_KEY`: MinIO 访问密钥（默认：minioadmin）
- `MINIO_BUCKET_NAME`: MinIO 存储桶名称（默认：WeKnora）
- `MINIO_PATH_PREFIX`: 文件路径前缀
- `MINIO_USE_SSL`: 是否使用 SSL（默认：false）

#### 腾讯云 COS 存储

- `STORAGE_TYPE`: 设置为 `cos`
- `COS_SECRET_ID`: COS 访问密钥 ID
- `COS_SECRET_KEY`: COS 访问密钥
- `COS_REGION`: COS 区域
- `COS_BUCKET_NAME`: COS 存储桶名称
- `COS_APP_ID`: COS 应用 ID
- `COS_PATH_PREFIX`: 文件路径前缀
- `COS_ENABLE_OLD_DOMAIN`: 是否使用旧域名（默认：true）

#### 阿里云 OSS 存储

- `STORAGE_TYPE`: 设置为 `oss`
- `OSS_ACCESS_KEY_ID`: OSS 访问密钥 ID
- `OSS_ACCESS_KEY_SECRET`: OSS 访问密钥
- `OSS_ENDPOINT`: OSS 端点（如 `oss-cn-hangzhou.aliyuncs.com`）
- `OSS_BUCKET_NAME`: OSS 存储桶名称
- `OSS_REGION`: OSS 区域（如 `cn-hangzhou`）
- `OSS_PATH_PREFIX`: 文件路径前缀

### 代理配置

如果需要通过代理访问外部服务：

- `EXTERNAL_HTTP_PROXY`: HTTP 代理地址
- `EXTERNAL_HTTPS_PROXY`: HTTPS 代理地址

### 图像处理配置

扫描 PDF 会被渲染为 JPEG 图片后交给 Go App 侧 OCR 处理。如果在导入多个大 PDF 时出现资源占用过高，
可以优先调低 `DOCREADER_PDF_RENDER_MAX_WORKERS` 或 `DOCREADER_MARKITDOWN_MAX_WORKERS`。

## 配置示例

### 基础配置（使用 MinIO）

```yaml
docreader:
  environment:
    - MINIO_ENDPOINT=minio:9000
    - MINIO_PUBLIC_ENDPOINT=http://localhost:9000
    - MAX_FILE_SIZE_MB=50
```

### 高级配置（启用 MinerU）

```yaml
docreader:
  environment:
    - MINIO_ENDPOINT=minio:9000
    - MINIO_PUBLIC_ENDPOINT=http://192.168.1.100:9000
    - MINERU_ENDPOINT=http://mineru:8080
    - MAX_FILE_SIZE_MB=100
```

### 使用腾讯云 COS

```yaml
docreader:
  environment:
    - STORAGE_TYPE=cos
    - COS_SECRET_ID=your_secret_id
    - COS_SECRET_KEY=your_secret_key
    - COS_REGION=ap-guangzhou
    - COS_BUCKET_NAME=your-bucket
    - COS_APP_ID=your_app_id
    - MAX_FILE_SIZE_MB=50
```

### 使用阿里云 OSS

```yaml
docreader:
  environment:
    - STORAGE_TYPE=oss
    - OSS_ACCESS_KEY_ID=your_access_key_id
    - OSS_ACCESS_KEY_SECRET=your_access_key_secret
    - OSS_ENDPOINT=oss-cn-hangzhou.aliyuncs.com
    - OSS_BUCKET_NAME=your-bucket
    - OSS_REGION=cn-hangzhou
    - MAX_FILE_SIZE_MB=50
```

## 常见问题

### 1. DocReader 服务无法启动？

检查容器日志中是否存在依赖缺失或权限相关错误，必要时确认 `MINIO_ENDPOINT` / 存储相关环境变量是否正确配置。

### 2. 图片无法显示？

检查 `MINIO_PUBLIC_ENDPOINT` 配置：
- 确保使用的是可从浏览器访问的地址
- 如果从其他设备访问，不要使用 `localhost`，应使用实际 IP 地址

### 3. 文件上传失败？

检查 `MAX_FILE_SIZE_MB` 配置，确保限制足够大。同时需要确保前端和后端服务的文件大小限制保持一致。

## 服务健康检查

DocReader 服务配置了健康检查：

```yaml
healthcheck:
  test: ["CMD", "grpc_health_probe", "-addr=localhost:50051"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 60s
```

可以通过以下命令检查服务状态：

```bash
docker ps | grep docreader
docker logs WeKnora-docreader
```

## 更多信息

- 服务端口：50051（gRPC）
- 容器名称：WeKnora-docreader
- 网络：WeKnora-network
- 重启策略：unless-stopped

---

## File: docs/api/agent.md

# 智能体（Agent）管理 API

[返回目录](./README.md)

## 概述

智能体 API 用于管理自定义智能体（Custom Agent）。系统提供了内置智能体，同时支持用户创建自定义智能体来满足不同的业务场景需求。

> 智能体的共享与跨组织分发（`/agents/:id/shares` 等）属于组织协作能力，文档见 [组织管理 API](./organization.md)。本文件只覆盖智能体自身的 CRUD、复制、占位符、类型预设以及推荐问题接口。

### 内置智能体

系统默认提供以下内置智能体：

| ID | 名称 | 描述 | 模式 |
|----|------|------|------|
| `builtin-quick-answer` | 快速问答 | 基于知识库的 RAG 问答，快速准确地回答问题 | quick-answer |
| `builtin-smart-reasoning` | 智能推理 | ReAct 推理框架，支持多步思考和工具调用 | smart-reasoning |
| `builtin-data-analyst` | 数据分析师 | 专业数据分析智能体，支持 CSV/Excel 文件的 SQL 查询与统计分析 | smart-reasoning |

### 智能体模式

| 模式 | 说明 |
|------|------|
| `quick-answer` | RAG 模式，快速问答，直接基于知识库检索结果生成回答 |
| `smart-reasoning` | ReAct 模式，支持多步推理和工具调用 |

## API 列表

| 方法   | 路径                       | 描述                       |
| ------ | -------------------------- | -------------------------- |
| POST   | `/agents`                  | 创建智能体                 |
| GET    | `/agents`                  | 获取智能体列表             |
| GET    | `/agents/:id`              | 获取智能体详情             |
| PUT    | `/agents/:id`              | 更新智能体                 |
| DELETE | `/agents/:id`              | 删除智能体                 |
| POST   | `/agents/:id/copy`         | 复制智能体                 |
| GET    | `/agents/placeholders`     | 获取占位符定义             |

---

## POST `/agents` - 创建智能体

创建新的自定义智能体。成功返回 HTTP 201。

**请求体参数**:

| 参数          | 类型   | 必填 | 说明                                              |
| ------------- | ------ | ---- | ------------------------------------------------- |
| `name`        | string | 是   | 智能体名称                                        |
| `description` | string | 否   | 智能体描述                                        |
| `avatar`      | string | 否   | 头像（emoji 或图标名称）                          |
| `config`      | object | 否   | 智能体配置，详见 [配置参数](#配置参数)            |

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/agents' \
--header 'X-API-Key: sk-xxxxx' \
--header 'Content-Type: application/json' \
--data '{
    "name": "我的智能体",
    "description": "自定义智能体描述",
    "avatar": "🤖",
    "config": {
        "agent_mode": "smart-reasoning",
        "system_prompt": "你是一个专业的助手...",
        "temperature": 0.7,
        "max_iterations": 10,
        "kb_selection_mode": "all",
        "web_search_enabled": true,
        "multi_turn_enabled": true,
        "history_turns": 5
    }
}'
```

**响应**:

```json
{
    "success": true,
    "data": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "我的智能体",
        "description": "自定义智能体描述",
        "avatar": "🤖",
        "is_builtin": false,
        "tenant_id": 1,
        "created_by": "user-123",
        "config": {
            "agent_mode": "smart-reasoning",
            "system_prompt": "你是一个专业的助手...",
            "temperature": 0.7,
            "max_iterations": 10
        },
        "created_at": "2025-01-19T10:00:00Z",
        "updated_at": "2025-01-19T10:00:00Z"
    }
}
```

**错误响应**:

| 状态码 | 错误码 | 错误                  | 说明                              |
| ------ | ------ | --------------------- | --------------------------------- |
| 400    | 1000   | Bad Request           | 请求参数错误或智能体名称为空      |
| 500    | 1007   | Internal Server Error | 服务器内部错误                    |

---

## GET `/agents` - 获取智能体列表

获取当前空间的所有智能体，包括内置智能体和自定义智能体。响应中额外返回 `disabled_own_agent_ids`，指示当前空间在前端对话下拉框中主动隐藏的本空间自有智能体 ID 列表（不影响其他空间）。

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/agents' \
--header 'X-API-Key: sk-xxxxx'
```

**响应**:

```json
{
    "success": true,
    "data": [
        {
            "id": "builtin-quick-answer",
            "name": "快速问答",
            "description": "基于知识库的 RAG 问答，快速准确地回答问题",
            "avatar": "💬",
            "is_builtin": true,
            "tenant_id": 10000,
            "created_by": "",
            "config": {
                "agent_mode": "quick-answer",
                "temperature": 0.3,
                "max_completion_tokens": 2048,
                "kb_selection_mode": "all",
                "web_search_enabled": false,
                "multi_turn_enabled": true,
                "history_turns": 5
            },
            "created_at": "2025-12-29T20:06:01.696308+08:00",
            "updated_at": "2025-12-29T20:06:01.696308+08:00",
            "deleted_at": null
        },
        {
            "id": "550e8400-e29b-41d4-a716-446655440000",
            "name": "我的智能体",
            "is_builtin": false,
            "config": {
                "agent_mode": "smart-reasoning"
            }
        }
    ],
    "disabled_own_agent_ids": []
}
```

**错误响应**:

| 状态码 | 错误码 | 错误                  | 说明               |
| ------ | ------ | --------------------- | ------------------ |
| 401    | 1001   | Unauthorized          | 缺少空间上下文     |
| 500    | 1007   | Internal Server Error | 服务器内部错误     |

---

## GET `/agents/:id` - 获取智能体详情

根据 ID 获取智能体的详细信息。

**路径参数**:

| 参数 | 类型   | 说明     |
| ---- | ------ | -------- |
| `id` | string | 智能体 ID |

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/agents/builtin-quick-answer' \
--header 'X-API-Key: sk-xxxxx'
```

**响应**:

```json
{
    "success": true,
    "data": {
        "id": "builtin-quick-answer",
        "name": "快速问答",
        "description": "基于知识库的 RAG 问答，快速准确地回答问题",
        "is_builtin": true,
        "tenant_id": 1,
        "config": {
            "agent_mode": "quick-answer",
            "system_prompt": "",
            "context_template": "请根据以下参考资料回答用户问题...",
            "temperature": 0.7,
            "max_completion_tokens": 2048,
            "kb_selection_mode": "all",
            "web_search_enabled": true,
            "multi_turn_enabled": true,
            "history_turns": 5
        },
        "created_at": "2025-01-01T00:00:00Z",
        "updated_at": "2025-01-01T00:00:00Z"
    }
}
```

**错误响应**:

| 状态码 | 错误码 | 错误                  | 说明               |
| ------ | ------ | --------------------- | ------------------ |
| 400    | 1000   | Bad Request           | 智能体 ID 为空     |
| 404    | 1003   | Not Found             | 智能体不存在       |
| 500    | 1007   | Internal Server Error | 服务器内部错误     |

---

## PUT `/agents/:id` - 更新智能体

更新智能体的名称、描述、头像和配置。内置智能体不可修改。

**路径参数**:

| 参数 | 类型   | 说明     |
| ---- | ------ | -------- |
| `id` | string | 智能体 ID |

**请求体参数**:

| 参数          | 类型   | 必填 | 说明         |
| ------------- | ------ | ---- | ------------ |
| `name`        | string | 否   | 智能体名称   |
| `description` | string | 否   | 智能体描述   |
| `avatar`      | string | 否   | 智能体头像   |
| `config`      | object | 否   | 智能体配置   |

**请求**:

```curl
curl --location --request PUT 'http://localhost:8080/api/v1/agents/550e8400-e29b-41d4-a716-446655440000' \
--header 'X-API-Key: sk-xxxxx' \
--header 'Content-Type: application/json' \
--data '{
    "name": "更新后的智能体",
    "description": "更新后的描述",
    "config": {
        "agent_mode": "smart-reasoning",
        "temperature": 0.8,
        "max_iterations": 20
    }
}'
```

**响应**:

```json
{
    "success": true,
    "data": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "更新后的智能体",
        "description": "更新后的描述",
        "config": {
            "agent_mode": "smart-reasoning",
            "temperature": 0.8,
            "max_iterations": 20
        },
        "updated_at": "2025-01-19T11:00:00Z"
    }
}
```

**错误响应**:

| 状态码 | 错误码 | 错误                  | 说明                              |
| ------ | ------ | --------------------- | --------------------------------- |
| 400    | 1000   | Bad Request           | 请求参数错误或智能体名称为空      |
| 403    | 1002   | Forbidden             | 无法修改内置智能体                |
| 404    | 1003   | Not Found             | 智能体不存在                      |
| 500    | 1007   | Internal Server Error | 服务器内部错误                    |

---

## DELETE `/agents/:id` - 删除智能体

删除指定的自定义智能体。内置智能体不可删除。

**路径参数**:

| 参数 | 类型   | 说明     |
| ---- | ------ | -------- |
| `id` | string | 智能体 ID |

**请求**:

```curl
curl --location --request DELETE 'http://localhost:8080/api/v1/agents/550e8400-e29b-41d4-a716-446655440000' \
--header 'X-API-Key: sk-xxxxx'
```

**响应**:

```json
{
    "success": true,
    "message": "Agent deleted successfully"
}
```

**错误响应**:

| 状态码 | 错误码 | 错误                  | 说明                  |
| ------ | ------ | --------------------- | --------------------- |
| 400    | 1000   | Bad Request           | 智能体 ID 为空        |
| 403    | 1002   | Forbidden             | 无法删除内置智能体    |
| 404    | 1003   | Not Found             | 智能体不存在          |
| 500    | 1007   | Internal Server Error | 服务器内部错误        |

---

## POST `/agents/:id/copy` - 复制智能体

复制指定的智能体，创建一个新的副本，副本始终为自定义智能体。支持复制内置智能体。成功返回 HTTP 201。

**路径参数**:

| 参数 | 类型   | 说明           |
| ---- | ------ | -------------- |
| `id` | string | 源智能体 ID    |

**请求**:

```curl
curl --location --request POST 'http://localhost:8080/api/v1/agents/builtin-smart-reasoning/copy' \
--header 'X-API-Key: sk-xxxxx'
```

**响应**:

```json
{
    "success": true,
    "data": {
        "id": "660e8400-e29b-41d4-a716-446655440001",
        "name": "智能推理 (副本)",
        "description": "ReAct 推理框架，支持多步思考和工具调用",
        "is_builtin": false,
        "config": {
            "agent_mode": "smart-reasoning",
            "max_iterations": 50
        },
        "created_at": "2025-01-19T12:00:00Z",
        "updated_at": "2025-01-19T12:00:00Z"
    }
}
```

**错误响应**:

| 状态码 | 错误码 | 错误                  | 说明               |
| ------ | ------ | --------------------- | ------------------ |
| 400    | 1000   | Bad Request           | 智能体 ID 为空     |
| 404    | 1003   | Not Found             | 智能体不存在       |
| 500    | 1007   | Internal Server Error | 服务器内部错误     |

---

## GET `/agents/placeholders` - 获取占位符定义

获取所有可用的提示词占位符定义，按字段类型分组。这些占位符可用于系统提示词和上下文模板中。

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/agents/placeholders' \
--header 'X-API-Key: your_api_key'
```

**响应**:

```json
{
    "success": true,
    "data": {
        "all": [...],
        "system_prompt": [...],
        "agent_system_prompt": [...],
        "context_template": [...],
        "rewrite_system_prompt": [...],
        "rewrite_prompt": [...],
        "fallback_prompt": [...]
    }
}
```

---

## 配置参数

智能体的 `config` 对象支持以下配置项：

### 基础设置

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `agent_mode` | string | - | 智能体模式：`quick-answer`（RAG）或 `smart-reasoning`（ReAct） |
| `system_prompt` | string | - | 系统提示词，支持使用占位符 |
| `system_prompt_id` | string | - | 系统提示词模板 ID（引用 `prompt_templates/` YAML 文件中的模板） |
| `context_template` | string | - | 上下文模板（仅 quick-answer 模式使用） |
| `context_template_id` | string | - | 上下文模板 ID（引用 `prompt_templates/` YAML 文件中的模板） |

### 模型设置

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `model_id` | string | - | 对话模型 ID |
| `rerank_model_id` | string | - | 重排序模型 ID |
| `temperature` | float | 0.7 | 温度参数（0-1） |
| `max_completion_tokens` | int | 2048 | 最大生成 token 数 |
| `thinking` | *bool | nil | 是否启用思考模式（适用于支持扩展思考的模型） |

### Agent 模式设置

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `max_iterations` | int | 10 | ReAct 最大迭代次数 |
| `allowed_tools` | []string | - | 允许使用的工具列表 |
| `mcp_selection_mode` | string | - | MCP 服务选择模式：`all`/`selected`/`none` |
| `mcp_services` | []string | - | 选中的 MCP 服务 ID 列表 |
| `skills_selection_mode` | string | - | Skills 选择模式：`all`/`selected`/`none` |
| `selected_skills` | []string | - | 选中的 Skill 名称列表（mode 为 `selected` 时） |

### 知识库设置

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `kb_selection_mode` | string | - | 知识库选择模式：`all`/`selected`/`none` |
| `knowledge_bases` | []string | - | 关联的知识库 ID 列表 |
| `retrieve_kb_only_when_mentioned` | bool | false | 仅在用户通过 @ 显式提及时才检索知识库 |
| `supported_file_types` | []string | - | 支持的文件类型（如 `["csv", "xlsx"]`） |

### 图片上传 / 多模态设置

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `image_upload_enabled` | bool | false | 是否允许上传图片 |
| `vlm_model_id` | string | - | 图片分析所用的 VLM 模型 ID |
| `image_storage_provider` | string | - | 图片存储提供者：`local`/`minio`/`cos`/`tos`/`oss`，为空使用全局默认 |

### FAQ 策略设置

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `faq_priority_enabled` | bool | true | FAQ 优先策略开关 |
| `faq_direct_answer_threshold` | float | 0.9 | FAQ 直接回答阈值 |
| `faq_score_boost` | float | 1.2 | FAQ 分数加成系数 |

### 网络搜索设置

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `web_search_enabled` | bool | true | 是否启用网络搜索 |
| `web_search_max_results` | int | 5 | 网络搜索最大结果数 |
| `web_search_provider_id` | string | - | 网络搜索提供者 ID，为空使用空间默认提供者 |
| `web_fetch_enabled` | bool | false | 是否自动获取重排后的搜索结果页面全文 |
| `web_fetch_top_n` | int | 3 | 重排后获取全文的最大页面数 |

### 多轮对话设置

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `multi_turn_enabled` | bool | true | 是否启用多轮对话 |
| `history_turns` | int | 5 | 保留的历史轮次数 |

### 检索策略设置

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `embedding_top_k` | int | 10 | 向量检索 TopK |
| `keyword_threshold` | float | 0.3 | 关键词检索阈值 |
| `vector_threshold` | float | 0.5 | 向量检索阈值 |
| `rerank_top_k` | int | 5 | 重排序 TopK |
| `rerank_threshold` | float | 0.5 | 重排序阈值 |

### 推荐问题设置

`question_suggestions` 是智能体拥有的统一策略。网页嵌入等渠道只能关闭展示，不能覆盖内容或生成规则。

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `question_suggestions.starters.enabled` | bool | true | 是否在首次提问前展示开场问题 |
| `question_suggestions.starters.mode` | string | `hybrid` | `curated`、`knowledge` 或 `hybrid` |
| `question_suggestions.starters.items` | []string | `[]` | 运营配置的开场问题 |
| `question_suggestions.starters.count` | int | 6 | 展示数量，范围 1-8 |
| `question_suggestions.follow_ups.enabled` | bool | false | 是否在每次完整回答后异步生成追问 |
| `question_suggestions.follow_ups.mode` | string | `hybrid` | `generated`、`knowledge` 或 `hybrid` |
| `question_suggestions.follow_ups.count` | int | 3 | 生成数量，范围 1-5 |
| `question_suggestions.follow_ups.model_id` | string | - | 独立生成模型；为空使用本轮对话模型 |
| `question_suggestions.follow_ups.categories` | []string | `clarify,deepen,action` | 允许的问题类型 |
| `question_suggestions.follow_ups.max_context_turns` | int | 2 | 生成时使用的最近对话轮数，范围 1-5 |
| `question_suggestions.follow_ups.additional_instruction` | string | - | 智能体作者的附加生成要求 |
| `question_suggestions.follow_ups.suppress_on_fallback` | bool | true | 兜底回答后不展示 |
| `question_suggestions.follow_ups.suppress_when_answer_asks_question` | bool | true | 回答本身以问题结尾时不展示 |
| `question_suggestions.follow_ups.knowledge_fallback` | bool | true | 模型失败时使用知识库候选补位 |
| `question_suggestions.follow_ups.allow_regenerate` | bool | false | 是否允许用户换一批 |

旧 `suggested_prompts` 会在数据库迁移时一次性写入 `starters.items`，API 不再接受该字段。

### 高级设置

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `enable_query_expansion` | bool | true | 是否启用查询扩展 |
| `enable_rewrite` | bool | true | 是否启用多轮对话查询改写 |
| `rewrite_prompt_system` | string | - | 改写系统提示词 |
| `rewrite_prompt_user` | string | - | 改写用户提示词模板 |
| `fallback_strategy` | string | `model` | 回退策略：`fixed`（固定回复）或 `model`（模型生成）；未设置时在服务端默认为 `model` |
| `fallback_response` | string | - | 固定回退回复（`fallback_strategy` 为 `fixed` 时使用） |
| `fallback_prompt` | string | - | 回退提示词（`fallback_strategy` 为 `model` 时使用） |

---

## 使用 Agent 进行问答

创建或获取智能体后，可以通过 `/agent-chat/:session_id` 接口使用智能体进行问答。详情请参考 [聊天功能 API](./chat.md)。

在问答请求中使用 `agent_id` 参数指定要使用的智能体：

```curl
curl --location 'http://localhost:8080/api/v1/agent-chat/session-123' \
--header 'X-API-Key: sk-xxxxx' \
--header 'Content-Type: application/json' \
--data '{
    "query": "帮我分析一下这份数据",
    "agent_enabled": true,
    "agent_id": "builtin-data-analyst"
}'
```

## 相关文档

- 智能体的组织共享、跨空间分发与禁用（`/agents/:id/shares`、`/shared-agents` 等）：见 [组织管理 API](./organization.md)
- 智能体绑定 IM 渠道（`/agents/:id/im-channels`）：见组织/IM 渠道相关文档
- 网络搜索提供者配置（被 `web_search_provider_id` 引用）：见 [Web Search API](./web-search.md)

---

## File: docs/api/auth.md

# 认证管理 API

[返回目录](./README.md)

OIDC 完整调用流程见 [../OIDC认证调用流程.md](../OIDC认证调用流程.md)。本文档作为端点参考。

## 说明

WeKnora 的 `/auth/*` 端点本身**不需要 X-API-Key**，但部分端点需要在 `Authorization: Bearer <token>` 头中携带由 `/auth/login` 或 `/auth/oidc/callback` 返回的 JWT：

| 端点 | 鉴权方式 |
| --- | --- |
| `/auth/register` `/auth/login` | 无 |
| `/auth/oidc/config` `/auth/oidc/url` `/auth/oidc/callback` | 无 |
| `/auth/refresh` | refresh_token（请求体携带） |
| `/auth/validate` `/auth/me` `/auth/logout` `/auth/change-password` | Bearer JWT |

注册接口可通过环境变量 `DISABLE_REGISTRATION=true` 关闭。

## 端点一览

| 方法 | 路径                       | 描述                                       |
| ---- | -------------------------- | ------------------------------------------ |
| POST | `/auth/register`           | 用户注册                                   |
| POST | `/auth/login`              | 用户登录                                   |
| GET  | `/auth/oidc/config`        | 获取 OIDC 配置元数据                       |
| GET  | `/auth/oidc/url`           | 获取 OIDC 授权链接                         |
| GET  | `/auth/oidc/callback`      | OIDC 授权回调（由 IdP 重定向触发）         |
| POST | `/auth/refresh`            | 用 refresh_token 换新的 access_token       |
| GET  | `/auth/validate`           | 验证 JWT 有效性                            |
| POST | `/auth/logout`             | 退出登录                                   |
| GET  | `/auth/me`                 | 获取当前用户信息                           |
| POST | `/auth/change-password`    | 修改密码                                   |

---

## POST `/auth/register` - 用户注册

**参数说明（请求体）**:

| 字段     | 类型   | 必填 | 校验                       | 说明      |
| -------- | ------ | ---- | -------------------------- | --------- |
| username | string | 是   | 长度 2-50                   | 用户名    |
| email    | string | 是   | 邮箱格式                   | 邮箱      |
| password | string | 是   | 最少 6 位                   | 密码      |

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/auth/register' \
--header 'Content-Type: application/json' \
--data '{
    "username": "alice",
    "email": "alice@example.com",
    "password": "secret123"
}'
```

**响应**（201 Created）:

```json
{
    "success": true,
    "message": "Registration successful",
    "user": {
        "id": "usr-...",
        "username": "alice",
        "email": "alice@example.com",
        "tenant_id": 1,
        "is_active": true,
        "created_at": "2026-05-11T10:00:00+08:00",
        "updated_at": "2026-05-11T10:00:00+08:00"
    },
    "tenant": {
        "id": 1,
        "name": "alice's workspace",
        "api_key": "sk-..."
    }
}
```

**错误**: 注册被禁用 → 403；参数校验失败 → 400。

---

## POST `/auth/login` - 用户登录

**参数说明（请求体）**:

| 字段     | 类型   | 必填 | 说明          |
| -------- | ------ | ---- | ------------- |
| email    | string | 是   | 注册邮箱      |
| password | string | 是   | 密码          |

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/auth/login' \
--header 'Content-Type: application/json' \
--data '{
    "email": "alice@example.com",
    "password": "secret123"
}'
```

**响应**:

```json
{
    "success": true,
    "message": "Login successful",
    "user": { "id": "usr-...", "username": "alice", "email": "alice@example.com" },
    "tenant": { "id": 1, "name": "alice's workspace", "api_key": "sk-..." },
    "token": "eyJhbGciOi...",
    "refresh_token": "eyJhbGciOi..."
}
```

**错误**: 邮箱或密码错误 → 401；账号被禁用 → 403。

---

## GET `/auth/oidc/config` - 获取 OIDC 配置元数据

返回 OIDC 是否启用以及 Provider 显示名，前端登录页据此决定是否展示 OIDC 登录按钮。

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/auth/oidc/config'
```

**响应**:

```json
{
    "success": true,
    "enabled": true,
    "provider_display_name": "WeKnora SSO"
}
```

---

## GET `/auth/oidc/url` - 获取 OIDC 授权链接

返回前端应跳转的 OIDC IdP 授权页 URL 与状态码。

**查询参数**:

| 字段       | 类型   | 必填 | 说明                                                    |
| ---------- | ------ | ---- | ------------------------------------------------------- |
| redirect   | string | 否   | 登录成功后前端期望落地的路径（如 `/dashboard`），透传到 state |

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/auth/oidc/url?redirect=%2Fdashboard'
```

**响应**:

```json
{
    "success": true,
    "provider_display_name": "WeKnora SSO",
    "authorization_url": "https://idp.example.com/oauth/authorize?client_id=...&state=...",
    "state": "abcdef..."
}
```

---

## GET `/auth/oidc/callback` - OIDC 授权回调

由 IdP 在用户授权后重定向到此端点。一般不需要客户端代码直接调用——它的作用是把登录结果通过浏览器 hash 传回前端首页。

**查询参数**:

| 字段              | 类型   | 必填 | 说明                          |
| ----------------- | ------ | ---- | ----------------------------- |
| code              | string | 是   | IdP 颁发的 authorization code |
| state             | string | 是   | 与 `/auth/oidc/url` 返回值一致 |
| error             | string | 否   | IdP 返回的错误标识            |
| error_description | string | 否   | IdP 返回的错误详情            |

**响应**：始终返回 `302 Found`，跳转到 `/`，并把结果编码进 URL hash：

- 成功：`/#oidc_result=`，其中 payload 包含 `success` / `user` / `tenant` / `token` / `refresh_token` / `is_new_user`，与登录响应一致。
- 失败：`/#oidc_error=<reason>[&oidc_error_description=<message>]`，常见 reason 包括 `invalid_state`、`missing_code`、`login_failed`、`payload_encode_failed`。

---

## GET `/auth/oidc/start` - 发起 OIDC 登录（直接 302）

与 `/auth/oidc/url` 不同，此端点**直接 302 重定向**到 OIDC Provider 的授权页，不返回 JSON，因此无需前端 JS 介入。适用于外部平台（如企业门户 / Nexus）直接给出一个链接即可触发 OIDC 授权码流程，借助 IdP 的 SSO session 实现免再次输密码。

回调地址由后端根据请求自身的 origin（`<scheme>://<host>/api/v1/auth/oidc/callback`）自动构造，无需调用方提供。

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/auth/oidc/start'
```

**响应**：`302 Found`，`Location` 指向 IdP 授权页（含 `client_id` / `state` / `redirect_uri` / `scope`）。

> 登录成功后的回调行为与 `/auth/oidc/callback` 一致：302 回前端首页并把登录结果编码进 URL hash。当前登录后固定落到默认首页 `/platform/knowledge-bases`（直达指定业务页的 `next` 参数为未来扩展）。

---

## POST `/auth/refresh` - 刷新令牌

**参数说明（请求体）**:

| 字段          | 类型   | 必填 | 说明              |
| ------------- | ------ | ---- | ----------------- |
| refreshToken  | string | 是   | 登录时颁发的 refresh_token |

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/auth/refresh' \
--header 'Content-Type: application/json' \
--data '{
    "refreshToken": "eyJhbGciOi..."
}'
```

**响应**:

```json
{
    "success": true,
    "message": "Token refreshed successfully",
    "access_token": "eyJhbGciOi...",
    "refresh_token": "eyJhbGciOi..."
}
```

**错误**: refresh_token 无效或过期 → 401。

---

## GET `/auth/validate` - 验证 JWT

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/auth/validate' \
--header 'Authorization: Bearer eyJhbGciOi...'
```

**响应**:

```json
{
    "success": true,
    "valid": true,
    "user_id": "usr-...",
    "tenant_id": 1
}
```

无效 token 返回 401。

---

## POST `/auth/logout` - 退出登录

**请求**:

```curl
curl --location --request POST 'http://localhost:8080/api/v1/auth/logout' \
--header 'Authorization: Bearer eyJhbGciOi...'
```

**响应**: `{ "success": true, "message": "Logged out successfully" }`

---

## GET `/auth/me` - 获取当前用户信息

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/auth/me' \
--header 'Authorization: Bearer eyJhbGciOi...'
```

**响应**:

```json
{
    "success": true,
    "data": {
        "user": {
            "id": "usr-...",
            "username": "alice",
            "email": "alice@example.com",
            "avatar": "",
            "tenant_id": 1,
            "is_active": true,
            "can_access_all_tenants": false,
            "created_at": "2026-05-11T10:00:00+08:00",
            "updated_at": "2026-05-11T10:00:00+08:00"
        },
        "tenant": {
            "id": 1,
            "name": "My Workspace"
        },
        "memberships": [
            {
                "tenant_id": 1,
                "tenant_name": "My Workspace",
                "role": "owner"
            }
        ],
        "tenant_required": false,
        "capabilities": {
            "can_create_tenant": false,
            "auto_accept_invitation": false
        }
    }
}
```

`capabilities` 供 SPA 读取部署级开关，无需调用超管设置 API：

| 字段 | 说明 |
| ---- | ---- |
| `can_create_tenant` | 当前用户是否可自助创建空间 |
| `auto_accept_invitation` | 全局 `tenant.auto_accept_invitation`：邮箱邀请已注册用户时是否直接加入（无需收件箱确认） |

---

## POST `/auth/change-password` - 修改密码

修改当前用户的登录密码。新密码须满足 **8–32 位**且**同时包含字母与数字**；不能与当前密码相同。成功后**所有会话被撤销**，需使用新密码重新登录。

**参数说明（请求体）**:

| 字段          | 类型   | 必填 | 校验    | 说明      |
| ------------- | ------ | ---- | ------- | --------- |
| old_password  | string | 是   |          | 当前密码  |
| new_password  | string | 是   | 8–32 位，须含字母与数字，且不同于旧密码 | 新密码    |

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/auth/change-password' \
--header 'Authorization: Bearer eyJhbGciOi...' \
--header 'Content-Type: application/json' \
--data '{
    "old_password": "secret123",
    "new_password": "newsecret456"
}'
```

**响应**: `{ "success": true, "message": "Password changed successfully" }`

**错误**（400）:

| `error.details`       | 含义                         |
| --------------------- | ---------------------------- |
| `invalid_old_password` | 当前密码不正确               |
| `password_policy`      | 新密码不满足长度/复杂度要求  |
| `same_password`        | 新密码与当前密码相同         |

---

## File: docs/api/chat.md

# 聊天功能 API

[返回目录](./README.md)

| 方法 | 路径                          | 描述                     |
| ---- | ----------------------------- | ------------------------ |
| POST | `/knowledge-chat/:session_id` | 基于知识库的问答         |
| POST | `/agent-chat/:session_id`     | 基于 Agent 的智能问答    |
| POST | `/knowledge-search`           | 基于知识库的搜索知识     |
| GET  | `/sessions/:session_id/messages/:message_id/suggestions` | 获取已生成的回答后推荐 |
| POST | `/sessions/:session_id/messages/:message_id/suggestions` | 确保生成或换一批推荐 |
| POST | `/sessions/:session_id/suggestion-events` | 上报曝光、点击、关闭事件 |

## POST `/knowledge-chat/:session_id` - 基于知识库的问答

基于知识库的 RAG 问答，支持 SSE 流式响应。

**查询参数**：

| 参数 | 取值 | 说明 |
|------|------|------|
| `resource_urls` | `handle`（默认）/ `public` | `public` 让答案与引用里的图片直接返回可加载的 http(s) 链接，省去逐个调用 `/files` 代理。详见[文件与图片引用](./README.md#文件与图片引用resource-与直链) |

同样适用于下面的 `/agent-chat/:session_id`、`/knowledge-search` 与 `/sessions/continue-stream/:session_id`。

**请求参数**：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `query` | string | 是 | 查询文本 |
| `knowledge_base_ids` | string[] | 否 | 知识库 ID 列表 |
| `knowledge_ids` | string[] | 否 | 知识文件 ID 列表，指定具体文件进行检索 |
| `agent_id` | string | 否 | 自定义 Agent ID，指定使用的智能体 |
| `summary_model_id` | string | 否 | 覆盖默认的摘要模型 ID |
| `mentioned_items` | object[] | 否 | @提及的知识库和文件列表 |
| `disable_title` | bool | 否 | 是否禁用自动标题生成（默认 false） |
| `images` | object[] | 否 | 附带的图片（base64 格式），需要 Agent 启用图片上传 |
| `channel` | string | 否 | 来源渠道标识：`web`、`api`、`im`、`browser_extension` |
| `suggestion_attribution` | object | 否 | 用户从推荐问题发起本轮时传入 `{suggestion_set_id, question_id}`；服务端会校验归属 |

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/knowledge-chat/ceb9babb-1e30-41d7-817d-fd584954304b' \
--header 'X-API-Key: sk-xxxxx' \
--header 'Content-Type: application/json' \
--data '{
    "query": "彗尾的形状",
    "knowledge_base_ids": ["kb-00000001"],
    "agent_id": "builtin-quick-answer"
}'
```

**响应格式**:
服务器端事件流（Server-Sent Events，Content-Type: text/event-stream）

**响应**:

```
event: message
data: {"id":"3475c004-0ada-4306-9d30-d7f5efce50d2","response_type":"references","content":"","done":false,"knowledge_references":[{"id":"c8347bef-...","content":"彗星xxx。","knowledge_id":"a6790b93-...","chunk_index":0,"knowledge_title":"彗星.txt","score":4.04,"match_type":3,"chunk_type":"text","knowledge_filename":"彗星.txt"}]}

event: message
data: {"id":"3475c004-0ada-4306-9d30-d7f5efce50d2","response_type":"answer","content":"彗尾的形状主要表现为...","done":false,"knowledge_references":null}

event: message
data: {"id":"3475c004-0ada-4306-9d30-d7f5efce50d2","response_type":"answer","content":"","done":true,"knowledge_references":null}
```

## POST `/agent-chat/:session_id` - 基于 Agent 的智能问答

Agent 模式支持更智能的问答，包括工具调用、网络搜索、多知识库检索等能力。

**请求参数**：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `query` | string | 是 | 查询文本 |
| `knowledge_base_ids` | string[] | 否 | 知识库 ID 列表，可动态指定本次查询使用的知识库 |
| `knowledge_ids` | string[] | 否 | 知识文件 ID 列表，可动态指定本次查询使用的具体文件 |
| `agent_enabled` | bool | 否 | 是否启用 Agent 模式（默认 false，优先使用 Agent 配置） |
| `agent_id` | string | 否 | 自定义 Agent ID，指定使用的智能体（支持共享 Agent） |
| `web_search_enabled` | bool | 否 | 是否启用网络搜索（默认 false） |
| `summary_model_id` | string | 否 | 覆盖默认的摘要模型 ID |
| `mentioned_items` | object[] | 否 | @提及的知识库和文件列表 |
| `disable_title` | bool | 否 | 是否禁用自动标题生成（默认 false） |
| `images` | object[] | 否 | 附带的图片（base64 格式），需要 Agent 启用图片上传 |
| `channel` | string | 否 | 来源渠道标识：`web`、`api`、`im`、`browser_extension` |
| `suggestion_attribution` | object | 否 | 用户从推荐问题发起本轮时传入 `{suggestion_set_id, question_id}`；服务端会校验归属 |

## 回答后推荐问题

回答主消息完成后，服务端会异步生成推荐问题，不阻塞 SSE 的 `complete`/`done` 事件。生成结果按“空间、助手消息、位置、配置快照、语言”持久化并去重。

```http
POST /api/v1/sessions/{session_id}/messages/{message_id}/suggestions
Content-Type: application/json

{"regenerate": false}
```

状态包括 `generating`、`ready`、`suppressed`、`failed`。`ready` 时的每个问题都有稳定 `id`，点击后应先上报事件，并在下一次聊天请求中携带 `suggestion_attribution`。

```http
POST /api/v1/sessions/{session_id}/suggestion-events
Content-Type: application/json

{
  "suggestion_set_id": "...",
  "question_id": "...",
  "event_type": "click"
}
```

网页嵌入提供同构接口：`/api/v1/embed/{channel_id}/sessions/{session_id}/...`，继续使用嵌入令牌和 `X-Embed-Session`。

**mentioned_items 结构**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | string | 知识库或文件 ID |
| `name` | string | 显示名称 |
| `type` | string | 类型：`kb`（知识库）或 `file`（文件） |
| `kb_type` | string | 知识库类型：`document` 或 `faq`（仅 `type=kb` 时） |

**images 结构**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `data` | string | base64 编码的图片数据（`data:image/png;base64,...`） |

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/agent-chat/ceb9babb-1e30-41d7-817d-fd584954304b' \
--header 'X-API-Key: sk-xxxxx' \
--header 'Content-Type: application/json' \
--data '{
    "query": "帮我查询今天的天气",
    "agent_enabled": true,
    "web_search_enabled": true,
    "knowledge_base_ids": ["kb-00000001"],
    "agent_id": "builtin-smart-reasoning",
    "mentioned_items": [
        {
            "id": "kb-00000001",
            "name": "天气知识库",
            "type": "kb",
            "kb_type": "document"
        }
    ]
}'
```

**响应格式**:
服务器端事件流（Server-Sent Events，Content-Type: text/event-stream）

**响应类型说明**：

| response_type | 描述 |
|---------------|------|
| `agent_query` | Agent 开始处理查询 |
| `thinking` | Agent 思考过程 |
| `tool_call` | 工具调用信息 |
| `tool_result` | 工具调用结果 |
| `references` | 知识库检索引用 |
| `answer` | 最终回答内容 |
| `reflection` | Agent 反思内容 |
| `session_title` | 自动生成的会话标题 |
| `error` | 错误信息 |

**响应示例**:

```
event: message
data: {"id":"req-001","response_type":"thinking","content":"用户想查询天气，我需要使用网络搜索工具...","done":false}

event: message
data: {"id":"req-001","response_type":"tool_call","content":"","done":false,"data":{"tool_name":"web_search","arguments":{"query":"今天天气"}}}

event: message
data: {"id":"req-001","response_type":"tool_result","content":"搜索结果：今天晴，气温25°C...","done":false}

event: message
data: {"id":"req-001","response_type":"answer","content":"根据查询结果，今天天气晴朗，气温约25°C。","done":false}

event: message
data: {"id":"req-001","response_type":"answer","content":"","done":true}
```

---

## File: docs/api/chunk.md

# 分块管理 API

[返回目录](./README.md)

| 方法   | 路径                              | 描述                       |
| ------ | --------------------------------- | -------------------------- |
| GET    | `/chunks/:knowledge_id`           | 获取知识的分块列表         |
| PUT    | `/chunks/:knowledge_id/:id`       | 更新分块                   |
| DELETE | `/chunks/:knowledge_id/:id`       | 删除单个分块               |
| DELETE | `/chunks/:knowledge_id`           | 删除知识下的所有分块       |
| GET    | `/chunks/by-id/:id`               | 根据分块 ID 直接获取分块    |
| DELETE | `/chunks/by-id/:id/questions`     | 删除分块下的某个生成问题   |

## GET `/chunks/:knowledge_id` - 获取知识的分块列表

**路径参数**:

| 字段          | 类型   | 说明        |
| ------------- | ------ | ----------- |
| knowledge_id  | string | 知识 ID     |

**查询参数**:

| 字段       | 类型 | 默认 | 说明       |
| ---------- | ---- | ---- | ---------- |
| page       | int  | 1    | 页码       |
| page_size  | int  | 20   | 每页条数   |

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/chunks/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5?page=1&page_size=1' \
--header 'X-API-Key: sk-xxxxx' \
--header 'Content-Type: application/json'
```

**响应**:

```json
{
    "data": [
        {
            "id": "df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7",
            "tenant_id": 1,
            "knowledge_id": "4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5",
            "knowledge_base_id": "kb-00000001",
            "tag_id": "",
            "content": "彗星xxxx",
            "chunk_index": 0,
            "is_enabled": true,
            "status": 2,
            "start_at": 0,
            "end_at": 964,
            "pre_chunk_id": "",
            "next_chunk_id": "",
            "chunk_type": "text",
            "parent_chunk_id": "",
            "relation_chunks": null,
            "indirect_relation_chunks": null,
            "metadata": null,
            "content_hash": "",
            "image_info": "",
            "created_at": "2025-08-12T11:52:36.168632+08:00",
            "updated_at": "2025-08-12T11:52:53.376871+08:00",
            "deleted_at": null
        }
    ],
    "page": 1,
    "page_size": 1,
    "success": true,
    "total": 5
}
```

## PUT `/chunks/:knowledge_id/:id` - 更新分块

更新指定分块的内容和属性。所有字段均可选，未传则保留原值。

**路径参数**:

| 字段          | 类型   | 说明        |
| ------------- | ------ | ----------- |
| knowledge_id  | string | 知识 ID     |
| id            | string | 分块 ID     |

**参数说明（请求体）**:

| 字段         | 类型    | 必填 | 说明                  |
| ------------ | ------- | ---- | --------------------- |
| content      | string  | 否   | 分块内容               |
| chunk_index  | int     | 否   | 分块在知识中的序号     |
| is_enabled   | boolean | 否   | 是否启用               |
| start_at     | int     | 否   | 起始位置（字符偏移）   |
| end_at       | int     | 否   | 结束位置（字符偏移）   |
| image_info   | string  | 否   | 图像分块的元信息（JSON 字符串） |

**请求**:

```curl
curl --location --request PUT 'http://localhost:8080/api/v1/chunks/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7' \
--header 'X-API-Key: sk-xxxxx' \
--header 'Content-Type: application/json' \
--data '{
    "content": "更新后的分块内容",
    "is_enabled": true
}'
```

**响应**:

```json
{
    "data": {
        "id": "df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7",
        "content": "更新后的分块内容",
        "is_enabled": true,
        "...": "其他字段同 GET 响应"
    },
    "success": true
}
```

## DELETE `/chunks/:knowledge_id/:id` - 删除单个分块

**路径参数**: 同 PUT。

**请求**:

```curl
curl --location --request DELETE 'http://localhost:8080/api/v1/chunks/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5/df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7' \
--header 'X-API-Key: sk-xxxxx'
```

**响应**:

```json
{
    "message": "Chunk deleted",
    "success": true
}
```

## DELETE `/chunks/:knowledge_id` - 删除知识下的所有分块

**路径参数**:

| 字段          | 类型   | 说明        |
| ------------- | ------ | ----------- |
| knowledge_id  | string | 知识 ID     |

**请求**:

```curl
curl --location --request DELETE 'http://localhost:8080/api/v1/chunks/4c4e7c1a-09cf-485b-a7b5-24b8cdc5acf5' \
--header 'X-API-Key: sk-xxxxx'
```

**响应**:

```json
{
    "message": "All chunks under knowledge deleted",
    "success": true
}
```

## GET `/chunks/by-id/:id` - 根据 ID 直接获取分块

无需提供 `knowledge_id` 即可获取分块。常用于跨知识库的引用展示。

**路径参数**:

| 字段 | 类型   | 说明    |
| ---- | ------ | ------- |
| id   | string | 分块 ID |

**请求**:

```curl
curl --location 'http://localhost:8080/api/v1/chunks/by-id/df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7' \
--header 'X-API-Key: sk-xxxxx'
```

**响应**: 同 `GET /chunks/:knowledge_id` 列表中的单条 data。

## DELETE `/chunks/by-id/:id/questions` - 删除分块下的某个生成问题

删除指定分块关联的某条生成问题。

**路径参数**:

| 字段 | 类型   | 说明    |
| ---- | ------ | ------- |
| id   | string | 分块 ID |

**参数说明（请求体）**:

| 字段        | 类型   | 必填 | 说明        |
| ----------- | ------ | ---- | ----------- |
| question_id | string | 是   | 问题 ID     |

**请求**:

```curl
curl --location --request DELETE 'http://localhost:8080/api/v1/chunks/by-id/df10b37d-cd05-4b14-ba8a-e1bd0eb3bbd7/questions' \
--header 'X-API-Key: sk-xxxxx' \
--header 'Content-Type: application/json' \
--data '{
    "question_id": "q-00000001"
}'
```

**响应**:

```json
{
    "message": "Question deleted successfully",
    "success": true
}
```

## 2. Official Technical Reference & Guides (Tencent/Tencent.github.io)

## File: konajdk/sm/README.md

# Tencent Kona JDK ShangMi Reference Guide

## Overview of ShangMi

ShangMi is a set of cryptographic techniques and tools in commercial or business applications, including the encryption of sensitive business data, secure communication channels, digital signatures for document verification, and even more.

Generally, the below ShangMi cryptographic algorithms and secure communication protocols are widely applied.

### ShangMi cryptographic algorithms

- SM2, which is a public key algorithm based on Elliptic Curve Cryptography (ECC). It complies with China's specifications:
  - GB/T 32918.1-2016 Part 1：General
  - GB/T 32918.2-2016 Part 2：Digital signature algorithm
  - GB/T 32918.3-2016 Part 3：Key exchange protocol
  - GB/T 32918.4-2016 Part 4：Public key encryption algorithm
  - GB/T 32918.5-2017 Part 5：Parameter definition
- SM3, which is a cryptographic hash algorithm. It complies with China's specification:
  - GB/T 32905-2016 SM3 cryptographic hash algorithm
- SM4, which is a block encryption algorithm. It complies with China's specification:
  - GB/T 32907-2016 SM4 block cipher algorithm

### ShangMi secure communication protocols

- Transport Layer Cryptography Protocol (TLCP), which is a TLS liked protocol. This protocol requires an endpoint must have two certificates, one for signature and the other for encryption. It complies China's specification:
  - GB/T 38636-2020 Information security technology-Transport layer cryptography protocol (TLCP)
- ShangMi (SM) Cipher Suites for TLS 1.3, which defines elements for applying ShangMi algorithms over TLS 1.3. It complies IETF's Informational specification:
  - RFC 8998 ShangMi (SM) Cipher Suites for TLS 1.3

## Providers and implementations

Tencent Kona JDK extends Java Cryptography Architecture (JCA) and Java Cryptography Extension (JCE) providers, exactly SUN, SunEC and SunJCE, to support ShangMi cryptographic algorithms, and extends Java Secure Socket Extension (JSSE) provider, exactly SunJSSE, to support ShangMi secure communication protocols.

The algorithms are implemented by the different security providers in the JDK. The followings list the security providers and the corresponding supported ShangMi algorithms and protocols.

- SUN
  - MessageDigest
    - SM3
- SunEC
  - KeyAgreement
    - SM2
  - KeyFactory
    - SM2
  - Signature
    - SM3withSM2
- SunJCE
  - Cipher
    - SM4
  - KeyGenerator
    - HmacSM3
    - SM4
  - Mac
    - HmacSM3
- SunJSSE
  - SSLContext
    - TLCP 1.1
      - ECC_SM4_GCM_SM3
      - ECC_SM4_CBC_SM3
      - ECDHE_SM4_GCM_SM3
      - ECDHE_SM4_CBC_SM3
    - TLS 1.3/RFC 8998
      - TLS_SM4_GCM_SM3

Since Tencent Kona JDK just uses the existing OpenJDK security providers to accommodate the ShangMi stuff, and these providers are already installed with the highest priorities, so the application developers can use these out-of-the-box features as those JDK built-in algorithms (e.g. EC and AES) and protocols (e.g. TLS 1.3).

The following sections will unveil the details on using the above algorithms and protocols.

## SM2

### Key pair

Generating SM2 key pair is the same as generating the key pairs on other EC curves. It just needs to invoke the standard JDK APIs. The JDK builtin ECKeyPairGenerator. In the key pair generated by this implementation, the format of private key is PKCS#8 and the format of public key is X.509.

Create KeyPairGenerator implementation for EC algorithm.

```
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC);
keyPairGenerator.initialize(spec);
```

`spec` can be `SM2ParameterSpec` (use `SM2ParameterSpec.instance()` to create the instance) or `ECGenParameterSpec` (use `new ECGenParameterSpec("curveSM2")` to create the instance).

Generate key pair.

```
KeyPair keyPair = keyPairGenerator.generateKeyPair();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
```

SM2 key pair is also EC key pair, so the public key and private key are also ECPublicKey and ECPrivateKey respectively.

For more information on key pair generation, please refer to the official [KeyPairGenerator](https://docs.oracle.com/en/java/javase/21/security/java-cryptography-architecture-jca-reference-guide.html#GUID-7EA29AC2-28B5-405D-BD2F-7055EC9E1EDD) docs.

Generally, in the signing and encrypting operations, the key pairs are already generated. They are not generated on the runtime. It just reads the existing public key and private key to create PublicKey and PrivateKey instances respectively.

```
byte[] encodedPublicKey = ;
byte[] encodedPrivateKey = ;

KeyFactory keyFactory = KeyFactory.getInstance("SM2");

SM2PublicKeySpec publicKeySpec = new SM2PublicKeySpec(encodedPublicKey);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);

SM2PrivateKeySpec privateKeySpec = new SM2PrivateKeySpec(encodedPrivateKey);
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
```

### Signature
Using SM2 signature is the same as using other existing signatures, like ECDSA, though the parameters are different.

Create Signature instance.

```
Signature signature = Signature.getInstance("SM3withSM2);
```

Initialize the Signature instance with the private key for signing.

```
signature.initSign(privateKey);
```

Behind the above initialization, it uses the default SM2 ID, exactly `1234567812345678`. It also derives the public key from the private key. According to the associated specification, public key must be used for generating SM2 signature data. This is a significant difference between the international algorithms, like ECDSA, and SM2 signature algorithm. Note that calculating the public key leads to some overhead. That is harmful for the performance.

If not using the default ID or expect to eliminate the cost on calculating public key, it would provide an AlgorithmParameterSpec, exactly SM2SignatureParameterSpec, instance.

```
byte[] altID = <custom ID>;
ECPublicKey publicKey = ;
SM2SignatureParameterSpec paramSpec = new SM2SignatureParameterSpec(altID, publicKey);
signature.setParameter(paramSpec);
signature.initSign(privateKey);
```

After parameter setup and initialization, it is time to pass the message data in.

```
byte[] message = ;
signature.update(message);
```

Generate signature data.

```
byte[] sign = signature.sign();
```

Initialize the Signature instance with public key for verifying.

```
signature.initVerify(publicKey);
```

Pass the message in.

```
byte[] message = ;
signature.update(message);
```

Pass the signature data in.

```
boolean verified = signature.verify(sign);
```

If the verification is successful, the result is true, otherwise false.

It has to know that the private key is used for signing, however the public key is used for verifying. For detailed information about Signature APIs, please refer to the official [Signature](https://docs.oracle.com/en/java/javase/21/security/java-cryptography-architecture-jca-reference-guide.html#GUID-9CF09CE2-9443-4F4E-8095-5CBFC7B697CF) docs.

### Encryption
Because of the performance concern, public key encryption is only used for encrypting short but critical data. The same to SM2 encryption.

Create Cipher instance.

```
Cipher cipher = Cipher.getInstance("SM2");
```

Initialize the Cipher instance with public key and set the mode to encryption.

```
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
```

Pass the message in and generate the ciphertext.

```
byte[] message = ;
byte[] ciphertext = cipher.doFinal(message);
```

Initialize the Cipher instance with private key and set the mode to decryption.

```
cipher.init(Cipher.DECRYPT_MODE, privateKey);
```

Pass the ciphertext in and generate the plaintext.

```
byte[] cleartext = cipher.doFinal(ciphertext);
```

It has to know that public key is used for encryption, however the private key is used for decryption. For detailed information on Cipher APIs, please refer to the official [Cipher](https://docs.oracle.com/en/java/javase/21/security/java-cryptography-architecture-jca-reference-guide.html#GUID-94225C88-F2F1-44D1-A781-1DD9D5094566) docs.

## SM3

Using SM3 hash algorithm is the same as using other existing hash algorithm, like SHA-256. It only invokes JDK APIs to generate the message digest (hash).

Create MessageDigest instance.

```
MessageDigest md = MessageDigest.getInstance("SM3");
```

It can pass all the message in and generate the digest.

```
byte[] message = ;
byte[] digest = md.digest(message);
```

It also can pass the message segments, and finally generate the digest.

```
byte[] messageSegment1 = ;
byte[] messageSegment2 = ;

// Pass the message segments in
md.update(messageSegment1);
md.update(messageSegment2);

// Finally generate the digest
byte[] digest = md.digest();
```

For detailed information about MessageDigest APIs, please refer to the official [MessageDigest](https://docs.oracle.com/en/java/javase/21/security/java-cryptography-architecture-jca-reference-guide.html#GUID-FB0090CA-2BCC-4D2C-BD2F-6F0A97197BD7) docs.

## HmacSM3

Using HmacSM3 algorithm is the same as using other existing MAC algorithm, like HmacSHA256. It only invokes JDK APIs to generate the message authentication code.

Prepare a 16-bytes key.

```
byte[] key = ;
SecretKey secretKey = new SecretKeySpec(key, "SM4");
```

Create Mac instance.

```
Mac hmac = Mac.getInstance("HmacSM3");
```

Initialize the Mac instance with the key.

```
hmac.init(secretKey);
```

Pass all the message in and generate message authentication code. The code is 32-bytes length.

```
byte[] message = ;
byte[] mac = hmac.doFinal(message);
```

It also can pass the message segments, and finally generate the message authentication code.

```
byte[] messageSegment1 = ;
byte[] messageSegment2 = ;

// Pass the message segments in
hmac.update(messageSegment1);
hmac.update(messageSegment2);

// Finally generate the message authentication code
byte[] mac = hmac.doFinal();
```

For the detailed information about Mac APIs, please refer to the official [Mac](https://docs.oracle.com/en/java/javase/21/security/java-cryptography-architecture-jca-reference-guide.html#GUID-8E014689-EBBB-4DE1-B6E0-24CE59AD8B9A) docs.

## SM4

Using SM4 cipher algorithm is the same as using other existing block cipher algorithm, like AES. It only invokes JDK APIs to do encryption and decryption. Tencent Kona JDK supports four operation modes, including CBC, CTR, ECB and GCM. It also supports PKCS#7 padding specification.

Prepare a 16-bytes key.

```
byte[] key = ;
SecretKey secretKey = new SecretKeySpec(key, "SM4");
```

Create Cipher instance.

```
Cipher cipher = Cipher.getInstance(transformation);
```

The transformation consists of algorithm name, operation mode and padding type. A symbol `/` is used as separator between the parts.

The following transformations are supported:
- SM4/CBC/NoPadding: The operation mode is CBC. No padding is needed. It requires the input must be 16x bytes.
- SM4/CBC/PKCS7Padding: The operation mode is CBC. PKCS#7 padding is used. It doesn't require that the input must be 16x bytes.
- SM4/CTR/NoPadding: The operation mode is CBC. No padding is needed. It doesn't require that the input must be 16x bytes.
- SM4/ECB/NoPadding: The operation mode is ECB. No padding is needed. It requires the input must be 16x bytes.
- SM4/ECB/PKCS7Padding: The operation mode is ECB. PKCS#7 padding is used. It doesn't require that the input must be 16x bytes.
- SM4/GCM/NoPadding: The operation mode is GCM. No padding is needed. It doesn't require that the input must be 16x bytes.

Create the algorithm parameters.

```
AlgorithmParameterSpec paramSpec = ;
```

Different operation modes may need different AlgorithmParameterSpec types,

- CBC and CTR modes use [IvParameterSpec](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/javax/crypto/spec/IvParameterSpec.html). This parameter type needs a 16-bytes as Initialization Vector (IV).
- GCM mode uses [GCMParameterSpec](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/javax/crypto/spec/GCMParameterSpec.html). This parameter type needs a 12-bytes as Initialization Vector (IV) and specifies the tag length to 12-bytes.

Initialize the Cipher instance with the key and parameter type and set the mode to encryption.

```
cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
```

Pass all the message in and generate the ciphertext.

```
byte[] ciphertext = cipher.doFinal(message);
```

Initialize the Cipher instance with the key and parameter type and set the mode to decryption.

```
cipher.init(Cipher.DECRYPT_MODE, secretKey);
```

Pass all the ciphertext in and generate the plaintext.

```
byte[] cleartext = cipher.doFinal(ciphertext);
```

It also can receive the plaintext/ciphertext segments in, and then generate the ciphertext/plaintext segments.

```
byte[] input1 = <segment1>;
byte[] input2 = <segment2>;

// Pass the plaintext/ciphertext segments in
byte[] output1 = cipher.update(input1);
byte[] output2 = cipher.update(input2);

// Generate the ciphertext/plaintext segments
byte[] outputFinal = cipher.doFinal();
```

For the detailed information about Cipher APIs, please refer to the official [Cipher] docs.

## Certificate

The so-called ShangMi certificate also complies to X.509 specification, though the public key algorithm is EC with SM2 curve and the signature algorithm is SM3withSM2.

```
String certPEM = <certificate in PEM format>;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) cf.generateCertificate(
        new ByteArrayInputStream(certPEM.getBytes(StandardCharsets.UTF_8));
```

For more information on certificate parsing, please read the official reference on [CertificateFactory](https://docs.oracle.com/en/java/javase/21/security/java-pki-programmers-guide.html#GUID-BCABADD4-C0DC-4987-B187-F086B4BCE195).

It can parse public key from the ShangMi certificate.

```
PublicKey publicKey = certificate.getPublicKey();
```

publicKey can be cast to ECPublicKey.

It can parse private key from PKCS#8 format.

```
String peyPEM = ;
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(
        Base64.getMimeDecoder().decode(keyPem));
KeyFactory keyFactory = KeyFactory.getInstance("EC");
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
```

privateKey can be cast to ECPrivateKey.

It can sign data with PKCS#8 private key and verify the signature with X.509 certificate.

Create Signature instance and use SM3withSM2 signature algorithm.

```
Signature signature = Signature.getInstance("SM3withSM2");
```

Sign the message with the private key and generate the signature data.

```
signature.initSign(privateKey);
signature.update(message);
byte[] sign = signature.sign();
```

Verify the signature data with the certificate and determine whether the verification passes or fails.

```
signature.initVerify(certificate);
signature.update(message);
boolean verified = signature.verify(sign);
```

The certificate has to contain digitalSignature key usage for verifying signature data. But it also can verify the signature with the public key directly.

```
signature.initVerify(publicKey);
signature.update(message);
boolean verified = signature.verify(sign);
```

## TLCP 1.1 and TLS 1.3/RFC 8998

In order to take advantage of the implementations on TLCP 1.1 and TLS 1.3/RFC 8998, the most important point is taking SSLSocket or SSLEngine to apply the [SSLContext](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/javax/net/ssl/SSLContext.html) implementation that supports the protocols.

```
KeyStore trustStore = <a trust store carring the CAs>;
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(trustStore);

KeyStore keyStore = <a key store carring the end entity certificates>;
KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509");
kmf.init(keyStore, keyStorePassword);

SSLContext context = SSLContext.getInstance("TLCPv1.1");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
```

When create SSLContext instances, it allows to specify the following context protocols:

- TLCPv1.1, which indicates only TLCP 1.1 is supported.
- TLSv1.3, which indicates only TLS 1.3 is supported.
- TLCP, which indicates TLCP 1.1, TLS 1.3 and TLS 1.2 are supported.

Please read the official [JSSE](https://docs.oracle.com/en/java/javase/21/security/java-secure-socket-extension-jsse-reference-guide.html#GUID-93DEEE16-0B70-40E5-BBE7-55C3FD432345) reference for understanding it deeply.

---

## File: konajdk/README.md

## Tencent Kona
Tencent Kona is a no-cost, production-ready distribution of the Open Java Development Kit (OpenJDK), Long-Term Support (LTS) with quarterly updates.

Kona serves as the default JDK at Tencent internally, optimized for extreme-scale of Big Data, Machine Learning and Cloud Computing workloads. It is also used to build and maintain a Tencent supported version of OpenJDK for Tencent customers and partners who wish to use OpenJDK to run their applications.

Kona also implements China's cryptographic algorithms, including SM2, SM3 and SM4, and secure communication protocol TLCP (or SMSSL). For more details on these features, please refer to [the reference guide](https://tencent.github.io/konajdk/sm/).

## Supported JDK Releases

Tencent Kona currently supports the JDK Long-Term Support (LTS) releases 8, 11, 17, 21 and 25.
For more details, please go through the individual repository for each of these releases:

- Tencent Kona 8: [https://github.com/Tencent/TencentKona-8](https://github.com/Tencent/TencentKona-8)
- Tencent Kona 11: [https://github.com/Tencent/TencentKona-11](https://github.com/Tencent/TencentKona-11)
- Tencent Kona 17: [https://github.com/Tencent/TencentKona-17](https://github.com/Tencent/TencentKona-17)
- Tencent Kona 21: [https://github.com/Tencent/TencentKona-21](https://github.com/Tencent/TencentKona-21)
- Tencent Kona 25: [https://github.com/Tencent/TencentKona-25](https://github.com/Tencent/TencentKona-25)

## Supported System Platforms

Tencent Kona currently supports Linux/x86_64, Linux/aarch64, Windows/x86_64, macOS/x86_64 and macOS/aarch64.

## License

Tencent Kona is under the same licensing terms as the upstream OpenJDK project. It is clearly a "friendly fork". Please find the license [text](https://tencent.github.io/konajdk/LICENSE.txt) for more details.