ydy0615 e28125079c feat(api): add system prompt support for LLM completion
Separate prompt generation into system and user prompts for better LLM instruction following. Backend now builds a detailed system prompt with constraints for math formatting, code block handling, boundary newlines, and OCR safety, while user prompt contains context and completion state flags. Added corresponding tests for both modules.
2026-02-23 15:17:36 +08:00
2026-01-11 14:11:14 +00:00

LLM in Text - 智能写作助手

基于 Vue3 和 FastAPI 的智能 Markdown 编辑器集成大语言模型LLM实时补全建议功能提供类似 GitHub Copilot 的 Ghost Text 体验。

功能特性

Markdown 编辑器

  • 基于 Milkdown Crepe 的所见即所得编辑体验
  • 支持完整 Markdown 语法和 LaTeX 公式
  • 导入/导出 Markdown 文件

AI 智能补全

  • 实时生成文本补全建议(灰色显示)
  • 流式响应,低延迟体验
  • 多种交互方式:
    • Tab 键:接受建议
    • Esc 键:拒绝建议
    • 点击灰色文本:接受建议
    • 继续输入:自动拒绝建议

AI 开关控制

  • 右下角 AI 开关按钮
  • 白色 = AI 启用,黑色 = AI 禁用
  • 禁用时自动清除灰色文本并停止 API 调用

技术架构

flowchart TB
    subgraph Frontend["前端 (Vue3 + Vite)"]
        A[App.vue] --> B[MilkdownEditor.vue]
        B --> C[Crepe Editor]
        C --> D[ProseMirror]
        D --> E[copilotPlugin.ts]
        E --> F[copilotGhostMark]
        E --> G[api.js]
    end
    
    subgraph Backend["后端 (FastAPI + Python)"]
        H[main.py<br/>FastAPI Server] --> I[prompt.py<br/>Prompt 构建]
        H --> J[llm.py<br/>Ollama 调用]
        J --> K[Ollama API]
    end
    
    G -->|POST /v1/completions<br/>SSE 流式响应| H
    K -->|LLM 响应| J

项目结构

llm-in-text/
├── src/
│   ├── components/
│   │   └── MilkdownEditor.vue    # 主编辑器组件
│   ├── plugins/
│   │   ├── copilotPlugin.ts      # ProseMirror AI 补全插件
│   │   ├── types.ts              # 类型定义
│   │   └── index.ts              # 插件导出
│   ├── utils/
│   │   ├── api.js                # API 调用封装
│   │   ├── config.js             # 配置文件
│   │   └── ocrCache.js           # OCR 缓存管理
│   ├── App.vue
│   └── main.js
├── backend/
│   ├── main.py                   # FastAPI 服务器
│   ├── llm.py                    # LLM API 调用
│   ├── prompt.py                 # Prompt 构建
│   └── requirements.txt
└── README.md

快速开始

环境要求

  • Node.js 18+
  • Python 3.8+
  • Ollama 服务(或其他兼容 OpenAI API 的服务)

安装

# 前端
npm install

# 后端
cd backend
pip install -r requirements.txt

配置

backend/.env 中配置:

OLLAMA_MODEL=gpt-oss:20b
OLLAMA_HOST=http://localhost:11434

启动

# 后端(端口 8000
cd backend
python main.py

# 前端(端口 5173
npm run dev

访问 http://localhost:5173

API 接口

POST /v1/completions

流式获取补全建议

请求:

{
  "prefix": "# Title\n\nContent ",
  "suffix": "",
  "languageId": "markdown"
}

响应SSE

data: {"content": "here"}
data: {"content": "here is"}
data: {"done": true}

核心实现

后端设计

main.py - FastAPI 服务器

  • 定义 /v1/completions 端点
  • 使用 StreamingResponse 返回 SSE 流式响应
  • CORS 配置允许跨域请求

llm.py - LLM 调用封装

  • 使用 ollama.AsyncClient 异步调用
  • 支持 think='high' 思考模式
  • 返回 contentthinking 字段

prompt.py - Prompt 工程

精心设计的 Prompt 模板,包含 7 条核心规则:

规则 说明
RULE #1 无缝连接 - 不重复 suffix 内容,避免"复读机"错误
RULE #2 空白处理 - 避免双空格,正确对接标点
RULE #3 缩进对齐 - 匹配当前缩进级别和类型
RULE #4 列表维护 - 识别并继续任务列表、有序列表、无序列表
RULE #5 语法闭合 - 自动闭合未完成的 Markdown 语法
RULE #6 输出格式 - 仅输出续写文本,无解释无注释
RULE #7 必须输出 - 始终提供有用的续写建议

前端设计

ProseMirror Mark 系统

使用 ProseMirror 的 Mark 系统实现灰色建议文本:

// 定义 ghost mark
export const copilotGhostMark = $markSchema('copilot_ghost', () => ({
  excludes: '_',
  inclusive: true,
  toDOM: () => ['span', { 
    'data-copilot-ghost': '', 
    class: 'copilot-ghost-text' 
  }, 0]
}))

// CSS 样式
.copilot-ghost-text {
  color: #999;
  opacity: 0.6;
}

copilotPlugin 核心逻辑

flowchart LR
    A[用户输入] --> B{文档变化?}
    B -->|是| C[清除旧建议]
    C --> D[防抖 1000ms]
    D --> E[发送 API 请求]
    E --> F[收到建议]
    F --> G[插入 Ghost Text]
    
    G --> H{用户操作}
    H -->|Tab| I[接受建议<br/>移除 mark]
    H -->|Esc| J[拒绝建议<br/>删除文本]
    H -->|点击 Ghost| I
    H -->|继续输入| J

关键函数

函数 作用
scheduleFetch 防抖调度 API 请求
insertGhostText 插入带 mark 的建议文本
acceptSuggestion Tab 接受建议
rejectSuggestion Esc 拒绝建议
clearGhostText 清除当前建议

数据流

sequenceDiagram
    participant U as 用户
    participant E as Editor (ProseMirror)
    participant P as copilotPlugin
    participant A as api.js
    participant B as Backend
    participant L as LLM
    
    U->>E: 输入文本
    E->>P: view.update()
    P->>P: 清除旧建议
    P->>P: 防抖 1000ms
    P->>A: fetchSuggestion(prefix, suffix)
    A->>B: POST /v1/completions
    B->>B: build_prompt()
    B->>L: ollama.chat()
    L-->>B: {content, thinking}
    B-->>A: SSE stream
    A-->>P: suggestion text
    P->>E: insertGhostText()
    E-->>U: 显示灰色建议
    
    alt Tab 键
        U->>P: Tab
        P->>E: acceptSuggestion()
        E-->>U: 建议变为正常文本
    else Esc 键
        U->>P: Esc
        P->>E: rejectSuggestion()
        E-->>U: 建议消失
    else 继续输入
        U->>E: 输入其他字符
        E->>P: handleKeyDown()
        P->>E: clearGhostText()
    end

设计亮点

  1. 前后端分离:前端只负责渲染和数据回传,后端负责 LLM 调用、Prompt 构建和数据解析
  2. 低延迟优化:防抖机制 (1000ms) + SSE 流式响应 + AbortController 取消过期请求
  3. ProseMirror Mark 系统:与编辑器状态完美集成,支持 Undo/Redo
  4. 多种交互方式Tab/Esc/点击/输入,用户体验友好
  5. 智能大小限制:文档超过 32KB 自动禁用 AI 功能

许可证

MIT License

Description
No description provided
Readme MIT 33 MiB
Languages
Vue 37.9%
Python 26.6%
JavaScript 16.7%
TypeScript 14.4%
CSS 2.7%
Other 1.7%