e28125079cca410653b7c538bbc0fb5a753e2458
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.
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
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'思考模式 - 返回
content和thinking字段
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
设计亮点
- 前后端分离:前端只负责渲染和数据回传,后端负责 LLM 调用、Prompt 构建和数据解析
- 低延迟优化:防抖机制 (1000ms) + SSE 流式响应 + AbortController 取消过期请求
- ProseMirror Mark 系统:与编辑器状态完美集成,支持 Undo/Redo
- 多种交互方式:Tab/Esc/点击/输入,用户体验友好
- 智能大小限制:文档超过 32KB 自动禁用 AI 功能
许可证
MIT License
Languages
Vue
37.9%
Python
26.6%
JavaScript
16.7%
TypeScript
14.4%
CSS
2.7%
Other
1.7%