architecture AI architecture - WangYajun369/ai-writing-platform GitHub Wiki
éçšçæ¬ïŒ
1.7.0ã|ãæåæ žå¯¹ïŒ2026-09-05æ¶µç AI æµåŒå¯¹è¯ïŒå·¥å ·ç®±ïŒãAI äŸ§é¢æ¿å¯¹è¯ïŒç» Agent åŒæïŒãRAG åéæ£çŽ¢ïŒé¢çïŒãå 容æ»ç»ãè¿æ¥æµè¯ã Agent èªåšååŒæïŒRust åçïŒåŠè§ Agent åŒææ¶æã
- æ¶ææ»è§
- æå¡åé 眮
- Rust å端暡å
- æµåŒå¯¹è¯
- RAG æ£çŽ¢äž Embedding
- å 容æ»ç»
- è¿æ¥æµè¯
- å端ç»ä»¶
- ç¶æäžç±»å
- AI å·¥å ·ç®±
- å ³é®åžžé
- 讟计è¯ä»·äžäŒåæ¹å
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â å端 (React/TypeScript) â
â AiSidePanel.tsx AiToolboxPanel.tsx â
â (AI 对è¯é¢æ¿Â·4 æèœ) (äžæ å·¥å
·ç®±Â·é¢è®Ÿ Prompt) â
â âââââââââââââââââ¬ââââââââââââââââ â
â useAiChat.ts â
â ïŒæµåŒäºä»¶çå¬ / å岿»ç» / é误æ¢å€ïŒ â
â â â
â Zustand aiSlice + Jotai aiPanelOpenAtom â
â tauri-bridge.tsïŒaiApi + ç»ä»¶çŽè° Agent åœä»€ïŒ â
âââââââââââââââââââââââââŒâââââââââââââââââââââââââââââââââââ€
â Tauri IPC 蟹ç â
âââââââââââââââââââââââââŒâââââââââââââââââââââââââââââââââââ€
â Rust 对è¯äžæ»ç»å± commands/ai/ + commands/agent/ â
â ââââââââââââââââââ¬âââââââââââââââââ¬âââââââââââââââ â
â â agent/skills.rsâ chat.rs â embedding.rs â â
â â engine.rs â å·¥å
·ç®±æµåŒå¯¹è¯ â RAG/玢åŒ(é¢ç) â â
â â AI 颿¿å¯¹è¯ â OpenAI SSE â â â
â â (ReAct+å·¥å
·) â â summarize.rs â â
â â â â ç« è/å¯¹è¯æ»ç» â â
â ââââââââ¬ââââââââââŽââââââââ¬âââââââââŽâââââââââ¬âââââââ â
â ⌠⌠⌠â
â emit('agent- emit('ai-stream- emit('chapter-/ â
â stream-chunk') chunk') conversation- â
â (chunk/done/ (thinking/ summary-done') â
â error/cancelled) answering/retrying) â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
å ³é®è®Ÿè®¡å³çïŒ
- æµåŒè¯·æ±å®å
šåš Rust 端éè¿
reqwestå€çïŒå端åªéè¿ Tauri äºä»¶æ¥æ¶å¢éææ¬ïŒè§é¿æµè§åš CORS / æµåŒè§£æé®é¢ - æ¯æ Ollama åçåè®®ïŒNDJSONïŒäž OpenAI å Œå®¹åè®®ïŒSSEïŒäž€æ¡è·¯åŸïŒå·¥å ·ç®±/æ»ç»çŽè¿åºæ¯ïŒ
-
v1.2 èµ· AI äŸ§é¢æ¿å¯¹è¯ç»äžç± Agent åŒæé©±åšïŒ
useAiChat.handleSend()âexecute_agent_skillïŒé»è®€writingæèœïŒïŒPrompt æå»ºäžäžäžææ£çŽ¢ç±åŒæå éšå®æïŒå·¥å ·ç®±ä¿çstream_ai_chatçŽè¿ -
RAG/Embedding 䞺é¢çèœåïŒå端
rag_search/trigger_embedding/check_embedding_statusåœä»€å·²å®ç°å¹¶æ³šåïŒäœè®Ÿçœ®é¡µæ è®°ãé¢çãïŒåœå对è¯äžäžæç± Agent åŒæå çœ®å·¥å ·ïŒç« è读å/æçŽ¢ïŒæäŸïŒtriggerEmbeddingæ å端 UI æ¥çº¿ - 对è¯é 眮ïŒAiChatConfigïŒäž RAG/Embedding é 眮ïŒRagConfigïŒå®å šè§£èŠïŒåèªç¬ç«ç®¡ç API Keyã端ç¹ãæš¡å
- åæå¡å API Key ç¬ç«ååšïŒ
bigmodelApiKey/deepseekApiKeyïŒ - AI é çœ®ç» localStorage æä¹ åïŒèªåšå Œå®¹æ§çæå¹³æ ŒåŒè¿ç§»
- æµåŒäºä»¶äžé¶æ®µéç¥ïŒ
thinkingâansweringâdone
v1.0.0 åæŽïŒå
commands/ai.rsïŒçºŠ 1265 è¡ïŒå·²æå䞺ai/{chat,embedding,summarize,test}.rsåäžªåæš¡åã v1.2.0 åæŽïŒAgent è¿ç§»äžº Rust åçåïŒAI 颿¿å¯¹è¯æ¥å ¥ Agent åŒæïŒè§ Agent åŒææ¶æïŒã
| æå¡å | æ è¯ | é»è®€ç«¯ç¹ | é»è®€æš¡å | å¯éæš¡å |
|---|---|---|---|---|
| æºè°± BigModel | bigmodel |
https://open.bigmodel.cn/api/paas/v4 |
glm-5.1 |
glm-5.1 |
| DeepSeek | deepseek |
https://api.deepseek.com |
deepseek-v4-flash |
deepseek-v4-flashãdeepseek-v4-pro
|
| Ollama | ollama |
http://127.0.0.1:11434 |
qwen2.5:7b |
ä»»ææ¬å°æš¡å |
| èªå®ä¹ | custom |
çšæ·å¡«å | çšæ·å¡«å | â |
RAG ä» æ¯ææºè°± BigModelïŒDeepSeek äžæäŸ Embeddings APIïŒïŒ
| æå¡å | é»è®€ç«¯ç¹ | Embedding æš¡å |
|---|---|---|
| æºè°± BigModel | https://open.bigmodel.cn/api/paas/v4 |
embedding-3 |
| èœå | æºè°± BigModel | DeepSeek | Ollama |
|---|---|---|---|
| æµåŒå¯¹è¯ | â | â | â |
| 深床æè / æšç | â | â | â |
| Embedding åé | â
(embedding-3) |
â | â |
| RAG è¯ä¹æ£çŽ¢ | â | â | â |
å®ä¹äº src/components/settings/constants.tsïŒ
PROVIDER_DEFAULTS = {
bigmodel: { endpoint: 'https://open.bigmodel.cn/api/paas/v4', model: 'glm-5.1' },
deepseek: { endpoint: 'https://api.deepseek.com', model: 'deepseek-v4-flash' },
ollama: { endpoint: 'http://127.0.0.1:11434', model: 'qwen2.5:7b' },
custom: { endpoint: '', model: '' },
}
RAG_PROVIDER_DEFAULTS = {
bigmodel: { endpoint: 'https://open.bigmodel.cn/api/paas/v4', embeddingModel: 'embedding-3' },
}| æä»¶ | åœä»€ | è莣 |
|---|---|---|
ai/chat.rs |
stream_ai_chat |
SSE/NDJSON æµåŒå¯¹è¯ãéè¯ãè¶ æ¶ãbuffer å·æ° |
ai/embedding.rs |
rag_searchãtrigger_embeddingãcheck_embedding_statusãtest_rag_connection
|
åéæ£çŽ¢ãæ¹é玢åŒãç¶ææ£æ¥ |
ai/summarize.rs |
summarize_chapterãsummarize_conversation
|
ç« èæ»ç»ïŒéæµåŒïŒã对è¯åçŒ©ïŒæ»åšçªå£ïŒ |
ai/test.rs |
test_ai_connectionãtest_rag_connection
|
è¿éæ§æµè¯ |
/// RAG æ£çŽ¢ç»æ
#[derive(Serialize)]
pub struct RagResult {
pub snippet: String,
#[serde(rename = "sourceId")] pub source_id: String,
#[serde(rename = "sourceTitle")] pub source_title: String,
pub distance: f64,
}
/// æµåŒå¯¹è¯è¯·æ±åæ°
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct StreamChatArgs {
pub provider: String, // "ollama" | "openai_compatible"
pub endpoint: String,
pub model: String,
pub temperature: f64,
pub max_tokens: Option<u32>,
pub api_key: Option<String>,
pub messages: Vec<ChatMessage>,
pub thinking_enabled: bool, // æšçæš¡åæèæš¡åŒ
}
/// æµåŒäºä»¶èŽèœœïŒæšéå°å端çå¢éïŒ
#[derive(Debug, Clone, Serialize)]
pub struct StreamEvent {
pub content: String, // çŽ¯ç§¯çæ£åŒèŸåº
pub thinking: String, // 环积çæèè¿çš
pub phase: String, // "thinking" | "answering" | "retrying" | "done"
pub done: bool,
pub error: Option<String>,
pub usage: Option<UsageInfo>, // Token/åæ°çšéïŒä»
doneïŒ
}
/// Embedding 玢åŒç¶æ
pub struct EmbeddingStatus {
pub total_chapters: usize,
pub total_world_cards: usize,
pub indexed_chapters: usize,
pub indexed_world_cards: usize,
pub stale: bool, // total > indexed äž > 0 æ¶äžº true
}v1.2 èµ·
useAiChatå·²æ¹äžºç» Agent åŒææ§è¡ïŒexecute_agent_skillïŒïŒchat.rsçŽè¿éŸè·¯ä¿çç» AI å·¥å ·ç®±ïŒAiToolboxPanelâaiApi.streamChatïŒçåºæ¯ã䞀æ¡éŸè·¯åŠäžïŒ
éŸè·¯ AïŒAI äŸ§é¢æ¿å¯¹è¯ïŒåœåé»è®€ïŒèµ° Agent åŒæïŒ
çšæ·èŸå
¥ïŒAI äŸ§é¢æ¿ïŒâ useAiChat.handleSend()
â
1. åå²å猩ïŒè¿é¿æ¶å
summarize_conversation æ»åšçªå£å猩ïŒå¯éïŒ
â
2. 泚å listen('agent-stream-chunk')ïŒæ requestId è¿æ»€ïŒ
â
3. invoke('execute_agent_skill', { skill:'writing', bookId, message, aiConfig, requestId, ... })
â [Rust commands/agent/]
4. engine.rs ç»è£
PromptïŒåºç¡ + åºæ¯æç€º + è®°å¿æ®µïŒâ reqwest POST /chat/completions (stream: true)
5. éèŠäžäžææ¶è°çšå·¥å
·ïŒç» repository å±çŽè¯» SQLite
â
6. emit('agent-stream-chunk', { event: chunk/done/error/cancelled }) â å端 RAF çŒå²åå¹¶æž²æ
â
7. persistAiConversation() â localStorageïŒç»æäºä»¶åïŒ
éŸè·¯ BïŒå·¥å ·ç®± / çŽè¿åºæ¯ïŒèµ° stream_ai_chatïŒ
AiToolboxPanel â aiApi.streamChat() â invoke('stream_ai_chat')
â [Rust commands/ai/chat.rs]
reqwest POST {endpoint}/chat/completions (stream: true)
â emit('ai-stream-chunk', StreamEvent) â åç«¯å®æ¶æž²æ
â [DONE] â æŽæ°æ¶æ¯ïŒå« usage / æèè¿çšïŒ
æ¬ç« 4.2ïœ4.6 ç客æ·ç«¯é 眮ãé¶æ®µç®¡çã容éãå议对æ¯äžæèæš¡åŒåé对éŸè·¯ BïŒ
chat.rsïŒã
Client::builder()
.connect_timeout(30s) // è¿æ¥è¶
æ¶
.http1_only() // ä»
HTTP/1.1ïŒSSE å
Œå®¹ïŒ
.no_gzip().no_brotli() // çŠçšèªåšè§£å
.tcp_keepalive(120s) // é²é¿æèæéŽæè¿| é¶æ®µ | phase |
诎æ |
|---|---|---|
| æ»ç»äž | summarizing |
åçœ®ç« èæ»ç»ïŒåæ > 300 åïŒ |
| æèäž | thinking |
å€ç reasoning_contentïŒæªæ¶å° content
|
| èŸåºäž | answering |
æ¶å°ç¬¬äžäžª content å¢éå忢 |
| éè¯äž | retrying |
çœç»æ³¢åšïŒèªåšéè¯ |
| 宿 | done |
æ¶å° [DONE] ææµèªç¶ç»æ |
èªåšéè¯
- æå€éè¯ 2 æ¬¡ïŒææ°éé¿ïŒ1s â 2sïŒ
- å¯éè¯ïŒtimeoutãconnection resetã5xxã429ã空å 容
- äžå¯éè¯ïŒ401ã403ã404ïŒè®€è¯/æéé®é¢çŽæ¥è¿åïŒ
åå±è¶ æ¶
- 10 åéå šå±è¶ æ¶
- 60 ç§å chunk 读åè¶
æ¶ïŒ
tokio::time::timeoutïŒïŒå€å®äžºååŒè¿æ¥
ææµä¿åº
-
flush_sse_buffer()仿®ç buffer æåæåçå 容/æè/token çšé - å·²æå
容æ¶ä»¥
doneäºä»¶æ¶å°ŸïŒé垊é误æç€ºïŒïŒä¿çå·²çæå 容èéæŽäœæ¥é
| ç¹æ§ | OllamaïŒNDJSONïŒ | OpenAI å Œå®¹ïŒSSEïŒ |
|---|---|---|
| ç«¯ç¹ | /api/chat |
/chat/completions |
| å¢éæå | message.content |
choices[0].delta.content |
| æèè¿çš | äžæ¯æ | choices[0].delta.reasoning_content |
| ç»æä¿¡å· | æµå ³é + æç»äºä»¶ |
[DONE] ææµå
³é |
| Token éå¶ |
num_predictïŒé»è®€ -1 äžéå¶ïŒ |
max_tokens |
| è®€è¯ | æ é | Authorization: Bearer {key} |
thinking_enabled = true æ¶ïŒDeepSeek R1 / æºè°±æšçæš¡åïŒïŒ
- è§£æ SSE æµäžç
reasoning_contentåæ®µäœäžºæèè¿çš - éè¿
phase: "thinking"æšéïŒå端枲æäžºå¯æå åºå - æ£åŒåçéè¿
phase: "answering"æšédelta.content - DeepSeek é¢å€ç»è®¡ KV Cache åœäžïŒ
prompt_cache_hit_tokensïŒ
ââââââââââââââââââââ
â çšæ·æ¥è¯¢ â
ââââââââââ¬ââââââââââ
â
ââââââââââââââââââââââââââââ
â embeddings è¡šææ°æ®? â
ââââââââ¬âââââââââââââ¬âââââââ
YESâ âNO
â â
ââââââââââââââââââââ ââââââââââââââââââââ
â åéè¯ä¹æçŽ¢ â â FTS5 / LIKE é级 â
â sqlite-vec KNN â â å
³é®è¯å¹é
â
ââââââââââ¬ââââââââââ âââââââââââ¬âââââââââ
ââââââââââââ¬ââââââââââââ
â
è¿å Top N RagResult[]
-
æ£çŽ¢èåŽïŒ
chaptersè¡šïŒæé€èœ¯å é€ïŒ+world_cards衚ïŒåæbook_idé犻 -
åéæçŽ¢ïŒæ¥è¯¢åéç»
/embeddingsAPI è·åïŒå¯¹chunks_vecèæè¡šæ§è¡ sqlite-vec KNNïŒäœåŒŠè·çŠ»ïŒæ£çŽ¢ïŒååºå Top NïŒv1.6.0 èµ·äžåå šéå åäœåŒŠïŒ -
é级çç¥ïŒæ¥è¯¢è¯å 20 å笊æé
LIKE '%keyword%'ïŒå æç« èãäžè¶³æ¶è¡¥æäžçè§å¡çïŒéçº§ç»ædistanceåºå® 0.5
v1.6.0 èµ·è¯ä¹æ£çŽ¢æ¹äžº sqlite-vec KNNïŒçŽ¢åŒåéåå ¥ SQLite
chunks_vecïŒvec0 èæè¡šïŒïŒæ¥è¯¢æ§è¡ SQLknnè·çŠ»æ£çŽ¢ïŒäžåå šéå 蜜 embeddings å°å åéæ¡äœåŒŠè®¡ç®ïŒrepository/embedding_repo.rs绎æ€ïŒã
1. æ¶éæºæ°æ®
âââ ç« èïŒSELECT id, content_html FROM chapters WHERE book_id=? AND deleted_at IS NULL
âââ äžçè§å¡çïŒSELECT id, content_html FROM world_cards WHERE book_id=?
2. ææ¬é¢å€ç
âââ strip_html() â å» HTML æ çŸ
âââ truncate_for_embedding() â æªæå° 1800 å笊
âââ è¿æ»€ç©ºææ¬
3. æ¹éè°çš Embedding APIïŒæ¯æ¹ 20 æ¡ïŒ
4. åå
¥æ°æ®åº
âââ INSERT OR REPLACE INTO embeddings (source_type, source_id, embedding, model)
âââ UPDATE world_cards SET vectorized = 1
å ³é®åæ°
| åæ° | åŒ | 诎æ |
|---|---|---|
EMBEDDING_MAX_CHARS |
1800 | åæ¡ææ¬æªæé¿åºŠ |
BATCH_SIZE |
20 | æ¯æ¹ API è°ç𿡿° |
| ååšæ ŒåŒ | f32 â LE bytes BLOB |
å°ç«¯åèåº |
å æ¶éå šéšæ°æ®ïŒéæŸ statement éïŒïŒåæ§è¡åŒæ¥ API è°çšã
CREATE TABLE IF NOT EXISTS embeddings (
source_type TEXT NOT NULL, -- 'chapter' | 'world_card'
source_id TEXT NOT NULL,
embedding BLOB NOT NULL, -- f32[] â LE bytes
model TEXT NOT NULL,
PRIMARY KEY (source_type, source_id)
);-
éæµåŒè¯·æ±ïŒ
stream: falseïŒïŒè¿å宿޿»ç» - é»è®€ System PromptïŒäžäžå°è¯Žå©æïŒ300 åå æ»ç»äž»èŠæ è/äºä»¶/人ç©ïŒæ¯æèªå®ä¹
- æ¯æ DeepSeek æèæš¡åŒ
- è¿å
ChapterSummary { summary, originalChars, summaryChars, thinking } - è°çšåºæ¯ïŒAI äŸ§é¢æ¿å¯¹è¯ïŒåæ > 300 åèªåšè§ŠåïŒãç« èæ»ç»ç¬ç«çªå£
- ç« èè¿çïŒ< 50 åïŒèªåšè·³è¿ïŒå 容æªåå 8000 å笊
æ»åšçªå£ + æèŠå猩çåå± context 管çïŒ
ââââââââââââââââââââââââââââââââââââââââââ
â System Prompt â
â â è§è²æä»€ + å·/ç« èäžäžæ + RAG èæ¯ â
â â [åå²å¯¹è¯æèŠ]ïŒåçŒ©çæ§å¯¹è¯ïŒ â
ââââââââââââââââââââââââââââââââââââââââââ€
â æ»åšçªå£ïŒæè¿ N èœ®ïŒ â
â [user] ... [assistant] ... â
â [user] (åœåæé®) â
ââââââââââââââââââââââââââââââââââââââââââ
-
buildMessages()ä» åæè¿windowSize * 2æ¡æ¶æ¯ - æ¯æ¬¡
doneäºä»¶åè§ŠåsummarizeOverflowMessages()ïŒè¶ åºçªå£æ¶åå°å猩ïŒäžé»å¡ UIïŒ - æèŠç»æ
{ summary, coveredUpToId, summaryChars, updatedAt }ïŒæ bookId åç»æä¹ åïŒcoveredUpToIdä¿è¯å¹ç -
contextWindowSizeé 眮项ïŒé»è®€ 10ïŒèåŽ 1-50ïŒïŒæ§é 眮èªåšè¿ç§»è¡¥äžº 10
| åœä»€ | è¯·æ± | æåè¿å | è¶ æ¶ |
|---|---|---|---|
test_ai_connection |
GET {endpoint}/modelsïŒåžŠ Bearer KeyïŒ |
å¯çšæš¡ååè¡šïŒæå€ 10 äžªïŒ | 15s |
test_rag_connection |
POST {endpoint}/embeddingsïŒæµè¯ææ¬ïŒ |
åéç»ŽåºŠä¿¡æ¯ | 15s |
é误åºåïŒ401 â "讀è¯å€±èŽ¥"ïŒå ¶ä» â åå§é误诊æ ã
| ç»ä»¶ | è·¯åŸ | è莣 |
|---|---|---|
AiSidePanel |
components/ai/AiSidePanel.tsx |
对è¯é¢æ¿ïŒHeaderïŒè¿æ¥ç¶ææç€ºåšïŒ+ MessageList + QuickHints + InputArea |
useAiChat |
components/ai/useAiChat.tsïŒ425 è¡ïŒ |
æ žå¿ hookïŒåéïŒç» Agent åŒæ execute_agent_skillïŒãæµåŒäºä»¶ïŒagent-stream-chunkïŒãå岿»ç»/å猩ãé误æ¢å€ |
MessageBubble |
components/ai/MessageBubble.tsx |
æ¶æ¯æ°æ³¡ïŒMarkdown æž²æ + æèè¿çšæå + æäœæé® |
RequestDetailModal |
components/ai/RequestDetailModal.tsx |
请æ±è¯Šæ ïŒåæ° / System Prompt / ç« èæ»ç» / RAG äžäžæ / æ¶æ¯å衚 |
AiToolboxPanel |
components/ai/AiToolboxPanel.tsx |
AI å·¥å ·ç®±ïŒäžæ åžå±ïŒç¬ç«çªå£ïŒ |
ChapterSummaryHeader |
components/editor/ChapterSummaryHeader.tsx |
çŒèŸåšé¡¶éšå èæ»ç»æ¡ |
const statusConfig = {
idle: { icon: CircleIcon, color: 'text-muted-foreground/50' },
testing: { icon: Loader2Icon, color: 'text-blue-500 animate-spin' },
connected: { icon: CircleCheckIcon, color: 'text-green-500' },
error: { icon: CircleAlertIcon, color: 'text-red-500' },
}| æäœ | æ¯åŠåç |
|---|---|
addAiMessage |
â ç«å³ |
updateAiMessageïŒæµåŒé«é¢ïŒ |
â ä» å å |
persistAiConversationïŒæµç»æïŒ |
â äžæ¬¡åå ¥ |
| å é€ / æž ç©º | â ç«å³ |
interface AiChatConfig {
provider: 'bigmodel' | 'deepseek' | 'ollama' | 'custom'
endpoint: string
model: string
temperature: number // 0â1ïŒé»è®€ 0.7
maxTokens: number // é»è®€ 131072
bigmodelApiKey?: string
deepseekApiKey?: string
thinkingEnabled: boolean // é»è®€ true
contextWindowSize: number // é»è®€ 10ïŒèåŽ 1â50
}
interface RagConfig {
// v1.2 èµ·æ enabled åæ®µïŒé¢çèœåïŒäžåœ±ååœå对è¯ïŒ
provider: 'bigmodel' // åœåä»
æºè°±æäŸ Embeddings API
endpoint: string
embeddingModel: string // é»è®€ embedding-3
bigmodelApiKey?: string
}
interface AiConfig { chat: AiChatConfig; rag: RagConfig }
interface AiMessage {
id: string
role: 'user' | 'assistant'
content: string
thinking: string
phase: 'thinking' | 'answering' | 'done' | 'summarizing' | 'retrying'
isSummarizing?: boolean
loading?: boolean
usage?: { inputTokens; outputTokens; inputChars; outputChars } | null
requestPayload?: ChatRequestPayload
}localStorage é®
| é® | å 容 |
|---|---|
time-write-ai-config |
AiConfig 对象 |
time-write-ai-conversations |
Record<bookId, AiMessage[]> |
time-write-ai-tool-categories |
AiToolCategory[] |
å
Œå®¹æ§è¿ç§»ïŒæ§çæå¹³ AiConfig â chat/rag åçŠ»ïŒæ§ç apiKey â åæå¡å KeyïŒæ§ç aiToolPrompts â aiToolCategoriesã
äžæ åžå±ïŒå·ŠäŸ§å·¥å ·å衚ïŒ192pxïŒâ äžéŽèŸå ¥åºïŒ288pxïŒâ å³äŸ§èŸåºé¢æ¿ïŒåŒ¹æ§ïŒã
| åç±» | å·¥å ·æ° | å å«å·¥å · |
|---|---|---|
| åžžçšå·¥å · | 7 | ç« èæ»ç»ãå°è¯Žå€§çº²çæãç« èæ·±åºŠæ¶Šè²ãå°è¯Žæ©åãç»åãæ¶Šè²ãæ¹å |
| å§æ 讟计 | 6 | äž»çº¿å§æ 讟å®ãæ¯çº¿åè§£ãå§æ åèœ¬ãæ žå¿å²çªçæåšãç« èç»çº²ãç³»ç»è®Ÿå®çæåš |
| æåèŸ å© | 6 | æææåãç»èæåãæå®æåãå€è²æåãæ ææåãç¯å¢/åºæ¯æå |
| äžçè®Ÿå® | 5 | äžçæ¶æè®Ÿå®ã人ç©è®Ÿå®ãå¿åç»ç»æ¶æãå¢ç/åæ³ç级ãç©åè®Ÿå® |
| ååç¥åš | 5 | 人ç©ååå®å¶ãå°è¯Žä¹Šåãå€é£å§åãéšæŽŸå¿ååç§°ãå°ç¹åºæ¯åå |
- æ¯äžªå·¥å ·ç System Prompt å¯ç¬ç«çŒèŸïŒç空䜿çšé»è®€æç€ºè¯
- ç« èæ»ç»å·¥å ·åšå·¥å ·ç®±äžèªåšè¿æ»€ïŒæç¬ç«çªå£ïŒ
- æ¯æ
initialToolIdåæ°ä»å€éšæå®é»è®€éäžå·¥å · - ææå·¥å ·äžåç±»æä¹ åå° localStorage
| åžžé | åŒ | 诎æ |
|---|---|---|
EMBEDDING_MAX_CHARS |
1800 | åæ¡ææ¬æªæé¿åºŠ |
BATCH_SIZE |
20 | Embedding æ¹éçææ¹å€§å° |
MAX_RETRIES |
2 | æµåŒå¯¹è¯æå€§éè¯æ¬¡æ° |
SSE_READ_TIMEOUT_SECS |
60 | SSE 读åè¶ æ¶ïŒç§ïŒ |
GLOBAL_TIMEOUT |
600 | å šå±è¶ æ¶ïŒç§ïŒ |
CHAPTER_SUMMARY_THRESHOLD |
300 | ç« èèªåšæ»ç»åæ°éåŒ |
RAG_TOP_N |
3 | RAG æ£çŽ¢è¿åçæ®µæ° |
DEFAULT_CONTEXT_WINDOW_SIZE |
10 | é»è®€æ»åšçªå£èœ®æ° |
DEFAULT_MAX_TOKENS |
131072 | é»è®€æå€§èŸåº Token |
| æ¹é¢ | è¯ä»· |
|---|---|
| æ¶æå犻 | Rust å€ç HTTP/SSEïŒåç«¯ä» å€ç UIïŒè§é¿ CORS äžæµåŒè§£æé®é¢ |
| æµäžæä¿æ€ | 60s è¶ æ¶ + buffer å·æ° + ä¿çéšåå 容ïŒçšæ·äœéªå奜 |
| èªåšéè¯ | åºåå¯éè¯/äžå¯éè¯éè¯¯ïŒææ°éé¿ïŒåå°çšæ·å¹²é¢ |
| åæ£çŽ¢æš¡åŒïŒv1.6.0ïŒ | å端 sqlite-vec KNN è¯ä¹ â FTS5/LIKE é级ïŒå¯¹è¯äžäžæç± Agent åŒæå·¥å ·æ£çŽ¢æäŸ |
| ç« èæºèœæ»ç» | åæ > 300 åèªåšæ»ç»ïŒèç context token |
| å€ç»ŽåºŠé 眮 | 对è¯/RAG è§£èŠïŒæå¡åç¬ç« API KeyïŒå·¥å ·ç®±å¯æ©å± |
| æä¹ åå¯é | AI å¯¹è¯ 800ms 鲿åå¹¶åç + çªå£åžèœœ / 页é¢éè flush å åºïŒv1.6.0ïŒ |
| è¿ç§»å Œå®¹ | èªå𿣿µæ§çé çœ®æ ŒåŒå¹¶è¿ç§»ïŒçšæ·æ æç¥å级 |
| 请æ±éæ | RequestDetailModal å±ç€ºå®æŽè¯·æ±èœœè·ïŒäŸ¿äºè°è¯ |
| æ¹å | ç°ç¶ | 建议 |
|---|---|---|
| Provider æ©å±æ§ | æå¡å硬çŒç åšé 眮垞éäž | æä»¶åæé 眮驱åšç Provider æ³šåæºå¶ |
| Embedding æå¡å | ä»
æºè°±ïŒåæ» bigmodel
|
æ¯æ OpenAI embeddingsãæ¬å°æš¡å |
| RAG æ£çŽ¢æç | â å·²èœå° sqlite-vec KNNïŒv1.6.0ïŒ | å¯ç»§ç»è¯äŒ°åç / éåå猩çè¿é¶äŒå |
| æµåŒæç¹ç»äŒ | æè¿åä» ä¿çå·²çæå 容 | é¿æèåºæ¯å¯èèçæ£çæç¹ç»äŒ |
| API Key èœ®æ¢ | æ¯äžªæå¡åå Key | æ¯æå€ Key èŽèœœåè¡¡ / æ é蜬移 |
| 对è¯å¯Œåº | â å·²æ¯æ Markdown / JSONïŒv1.6.0ïŒAI 䟧蟹æ ã富åºå¯¹è¯ãïŒ | å¯å¢å å¯Œåºæ ŒåŒïŒTXT / HTMLïŒäžæ¹éå¯Œåº |
| ç« èæ»ç»çŒå | æ¯æ¬¡å¯¹è¯éœéæ°æ»ç» | summary æææå å€çšçŒå |
| ç³»ç»é»è®€å·¥å · | é¢è®Ÿ 29 äžªå·¥å ·äžå¯å é€/é眮 | å¢å ãæ¢å€é»è®€ãåèœ |
| 请æ±åæ°å¯é çœ®æ§ | Temperature çäžºå šå±è®Ÿçœ® | æ¯äžªå·¥å ·å¯ç¬ç«èŠçåæ° |
useAiChat äœç§¯ |
425 è¡ïŒv1.2 ç§»é€ Embedding é»èŸïŒïŒä»è莣èŸå€ | æå䞺 useChapterValidation / useChapterSummary / useStreamChat / useConversationCompression
|
| RAG æ¥çº¿ | å端åœä»€å·²å®ç°ãå端æ è®°é¢ç | æç¡® RAG å®äœïŒæ¥å ¥è®Ÿçœ®é¡µæå¹¶å ¥ Agent å·¥å ·ïŒïŒæç§»é€åäœåœä»€ |
- API Key ååšäº localStorage ææïŒæªå å¯
- ææ API éä¿¡åš Rust 䟧è¿è¡ïŒåç«¯æ æ³æªè·
- è¿æ¥æµè¯äžæ³é² API KeyïŒä» éªè¯è®€è¯ç¶æïŒ
-
capabilities/default.jsonäžhttp:allow-fetchæŸåŒä»»æ HTTP/HTTPS 端ç¹ïŒAI 请æ±å¿ éïŒäœä¹æ¯ CSP æ¶çާçæè¡¡ç¹ïŒè§ äŒåæ¥å é®é¢ 8ïŒ