Files
llm-in-text/AGENTS.md
T
2026-06-27 22:22:42 +08:00

15 KiB
Raw Blame History

LLM in Text 仓库指引 (v0.2.0)

本文件适用于整个仓库。进入更深层目录后,子目录中的 AGENTS.md 优先于本文件。

项目定位

  • 这是一个智能 Markdown 编辑器,前端负责编辑器 UI、上传导出、补全交互和设置状态,后端负责 LLM、OCR、文件转换和 TTS 接口。
  • 前端技术栈:Vue 3 + Vite + Milkdown/Crepe + Pinia + Vue Router。
  • 后端技术栈:FastAPI + Python + OpenAI-compatible LLM endpoint + Redis Streams。
  • 项目版本:v0.2.0(自 b82c6d3 之后的全栈架构升级版本)。

功能块系统(核心概念)

  • 统一命名:文档块、PRO 块、上传块统称为"功能块"。
  • 禁止嵌套:所有功能块的 schema 均设 atom: true, isolating: trueProseMirror 层面强制禁止互相嵌套。DocBlockCrepe.vue 的嵌套 Crepe 编辑器不注册任何功能块插件,从架构上杜绝深层嵌套。
  • 无数量限制:一篇文档可包含任意数量的功能块,彼此独立存在。
  • 导入自动解析:从 Markdown 文件导入后,各功能块语法必须通过对应的 Remark/parseMarkdown 解析器自动识别并还原为交互卡片。
  • 导出可复原:通过 toMarkdown/leafText 序列化器将功能块还原为 Markdown 语法,确保导出后再次导入能完整复原。doc_block toMarkdown 输出 legacy HTML taggetExportMarkdown() 中通过 transformLegacyDocBlocksForExport() 转换为 fenced code block。
类型 Node Type Markdown 语法 Plugin 文件 Utility 文件
文档块 doc_block ```llm-file fenced code / <doc_type=...> legacy HTML tag plugins/docBlockPlugin.ts utils/docBlock.js
PRO 块 pro_block [PRO] / [PRO]{指令} plugins/proBlockPlugin.ts utils/proBlock.js
上传块 upload_block {{{}}} / {{{upload file type:...}}} plugins/uploadBlockPlugin.ts utils/uploadBlock.js
  • 已验证:当前代码完全符合"禁止嵌套、自动解析复原"的要求。修改功能块相关逻辑时需验证:1) schema 的 atom/isolating 属性不被移除;2) Remark/parseMarkdown/toMarkdown 解析链路完整。
  • 当前代码中可以确认的主功能是:AI 补全、OCR、文档转 Markdown、TTS/ASR、Markdown/DOCX/PDF 导入导出。
  • 历史文档中有一部分 TTS/ASR、Apple Silicon、Whisper、离线模式说明已经落后于当前代码;出现冲突时以实际代码和测试为准。

先看哪里

  • 项目概览和运行说明:README.md
  • 前端入口:src/main.js
  • 路由:src/router/index.js
  • 编辑器主组件:src/components/MilkdownEditor.vue
  • AI 补全核心:src/plugins/copilotPlugin.ts、src/plugins/copilotTypes.ts(类型定义)
  • 前端请求层:src/utils/api.js、src/utils/fetch.js(安全 fetch wrapper
  • 前端配置:src/utils/config.js
  • 设置状态:src/stores/settings.js
  • 后端入口和主路由:backend/main.py
  • LLM 和 OCR 调用:backend/llm.py
  • Prompt 组装:backend/prompt.py(含 PRO 模式模板)
  • TTS 路由:backend/tts_asr.py
  • 任务队列系统backend/job_system.pyRedis Streams 异步任务管理)
  • Worker 进程入口backend/worker.py(消费 Redis Streams 的独立 worker
  • 任务处理器注册表backend/job_handlers.pycompletion/PRO/web_search/compress/OCR/convert/TTS/ASR 处理器)
  • 会话管理backend/session_store.py(内存 + PostgreSQL 双后端)
  • API/LLM 审计日志backend/audit_store.pyPostgreSQL 持久化)
  • 风控配置backend/risk_config.py(环境变量驱动的配置数据类)
  • 风控引擎backend/risk_control.py(速率限制、并发控制、熔断器、预算追踪)
  • 验证码路由backend/captcha_api.pyFastAPI router
  • 文档存储backend/docs_store.pyMIME 类型检测、文本/二进制分类)
  • LLM 策略解析backend/llm_policy.py(按 job_type 解析模型配置)
  • 网页搜索块组件src/components/WebSearchBlockCrepe.vue(可折叠/压缩/删除的搜索结果卡片)
  • 网页搜索块插件src/plugins/webSearchBlockPlugin.ts```llm-websearch fenced code 语法)
  • OCR 图片包装器src/components/OCRImageWrapper.vue(加载/成功/失败状态覆盖层)
  • 验证码组件src/components/CaptchaComponent.vuevue3-captcha 封装)
  • SSE 流式解析src/utils/sse.tsServer-Sent Events 事件解析)
  • 文档管理 APIsrc/utils/docsApi.js(上传/预览/删除接口客户端)
  • PRO 功能接受追踪src/utils/proAccept.js(使用分析和统计)
  • Cookie 策略管理src/utils/cookie_policy.jsSameSite/Secure 标志处理)
  • 字符串工具src/utils/string.ts(长度计算、编码检测)
  • 网页搜索上下文提取src/utils/webSearch.js(搜索结果 Markdown 构建与解析)
  • 测试配置和入口pytest.ini、backend/tests/run_tests.py

稳定事实

  • 功能块禁止嵌套doc_blockpro_blockupload_block 的 schema 均设 atom: true, isolating: trueProseMirror 层面强制禁止互相嵌套。DocBlockCrepe.vue 的嵌套 Crepe 编辑器仅注册 copilotPlugin + hiddenText*,不注册任何功能块插件。修改时不得移除 atom/isolating 属性或在嵌套编辑器中引入功能块插件。
  • 功能块导入解析:各功能块的 Remark 插件(docBlockRemark, proBlockRemark, uploadBlockRemark)负责从 Markdown AST 识别对应语法并转换为节点。doc_block Remark 同时支持 fenced code (llm-file) 和 legacy HTML tag (<doc_type=...>)。解析链路必须保持完整,否则导入后无法自动复原。
  • 功能块导出序列化doc_block 的 toMarkdown runner 输出 legacy HTML tag,但 getExportMarkdown() 中通过 transformLegacyDocBlocksForExport() 转换为 fenced code block。pro_block/upload_block 的 toMarkdown/leafText 直接输出标准语法。修改时需验证导出后再次导入能完整复原。
  • Web Search 块语法web_search_block 使用 fenced code ```llm-websearch date=... 语法,与 doc_block(llm-file)、pro_block([PRO])、upload_block({{{}}}) 并列。触发语法为 [WEBSEARCH](不区分大小写),由 webSearchBlockPlugin.ts 处理。
  • Store 同步格式scheduleMarkdownSync() emit markdown 不做转换(doc_block 为 legacy HTML tag),store 中始终是 legacy format。syncInitialMarkdown() / getExportMarkdown() 负责格式转换。
  • 上传块生命周期upload_block 是临时占位符,文件上传后被替换为 image node(图片)或 doc_block(文档),不会与 pro_block 共存。
  • PRO block escape/unescapeescapeProBlockContent() / unescapeProBlockSyntax() 处理 \, ], }, newline,确保指令 round-trip 正确。
  • 补全接口当前不是 SSE;前端用普通 POST 请求拿 JSON 响应。
  • 任务队列架构(v0.2.0 新增):后端从同步端点转向 Redis Streams 异步队列。job_system.py 定义 JOB_TYPEScompletion/pro_completion/web_search/compress/ocr/convert/tts/asr),worker.py 消费队列,job_handlers.py 注册各类型处理器。所有任务支持并发控制、速率限制和熔断器模式。
  • 会话追踪session_store.py 提供 InMemorySessionStore(开发)和 PostgresSessionStore(生产),通过 session_hash + ip_hash 追踪请求身份。
  • 风控系统risk_config.py + risk_control.py 实现速率限制(滑动窗口)、并发控制、熔断器模式和预算追踪,所有阈值通过环境变量配置。
  • 前端会生成 X-Request-Id,并在请求被中止时额外调用 /v1/completions/cancel。
  • 文档超过 32 KB 时,AI 补全会在前端和插件层被禁用。
  • OCR 文本和文档块内容会被注入补全上下文,但这些内容属于隐藏上下文,不应被直接当作用户可见文本重复输出。
  • /v1/convert 当前支持 txt、docx、pptx、pdf,非 txt 文件通过 MarkItDown 转成 Markdown,之后会清理图片标记。
  • AI 开关是全局广播状态MilkdownEditor.vue 通过 llm-in-text:copilot-toggle 同步主编辑器、文档块嵌套编辑器、网页搜索块嵌套编辑器;修 ghost text 时要同时检查这三处。
  • 设置项统一使用 country:前后端请求、prompt、store、设置面板只使用 country
  • DOCX/PDF 导出改为纯前端:不再依赖 /v1/export/pdf。当前策略是先展开所有功能块,再从编辑器 HTML 构建导出内容;src/utils/richExport.js 负责 HTML -> PDF / DOCX。
  • 上传单文件限制统一为 100MB:前端校验和后端 OCR 风控上限都按 100MB 处理。
  • 视频解析策略:上传视频时,后端 /v1/ocr 接收 media_type=video,视频画面走 OCR 模型,音轨通过 ffmpeg 抽取后走 ASR 模型,最终合并为“视频画面 OCR + 视频音频 ASR”文本。
  • OCR 明确关闭思考backend/llm.py 的 OCR payload 显式下发 options.think = Falsetemperature = 0
  • TTS/ASR 当前真实实现backend/tts_asr.py 统一通过 LLM_BASE_URL + LLM_API_KEY 调用 OpenAI-compatible Speech API,默认模型为 Qwen3-TTS-12Hz-1.7B-VoiceDesign-8bitQwen3-ASR-0.6B-8bit

常用命令

  • 前端安装:npm install
  • 前端开发:npm run dev
  • 前端构建:npm run build
  • 后端安装:pip install -r backend/requirements.txt
  • 后端启动:python backend/main.py
  • 可选启动方式:uvicorn backend.main:app --reload --port 8001
  • 全量测试:pytest
  • 常用窄测试:
    • pytest backend/tests/test_main_endpoints.py -v
    • pytest backend/tests/test_main_cancel.py -v
    • pytest backend/tests/test_prompt.py -v
    • pytest backend/tests/test_llm.py -v

Docker 部署约定

  • 本机部署目录固定在 /Users/allenyuan/lit/ 下,不在仓库外再散落数据库或 Docker 持久化目录。
  • 当前推荐的部署工作目录是 /Users/allenyuan/lit/llm-in-text/;把仓库同步到该目录后,从该目录执行 docker compose up -d --build
  • 不再使用 /Volumes/New Volume/lit/ 部署本项目;迁移时可放弃旧 PostgreSQL 数据,从新部署目录初始化空数据库。
  • docker-compose.yml 定义的服务apiFastAPI)、worker(任务消费者)、frontendNginx)、postgres(审计+会话存储)、redis(任务队列)、searxng(网页搜索后端)、firecrawl(网页内容抓取)。每次 docker compose up --build 后,必须验证所有目标服务(api/worker/frontend/postgres/redis)是否处于 Up 状态。
  • 前端网络硬约定:前端 API 必须调用 https://api.imageteach.tech:8002/ 反向代理,不要让前端调用 Docker 内 api 服务、本机 localhost:8001localhost:8081 或同源 /v1 代理。网络和反向代理由外部配置处理,除非用户明确要求,不要新增或恢复前端到 Docker 后端的代理。
  • 每次修改会影响 Docker 运行效果的代码后,不能只停留在本地测试;必须同步更新当前 Docker 环境中的代码,并验证容器内代码已经变化。
  • 首选更新方式:
    1. 在仓库根目录执行 docker compose up -d --build
    2. 执行 docker compose ps 确认 apiworkerfrontend 等目标服务已重新创建并处于 Up。
    3. 对关键修复点执行容器内验证,例如 docker compose exec -T worker sh -lc "python - <<'PY'\nfrom pathlib import Path\nprint('_normalize_preferences' in Path('/app/backend/prompt.py').read_text())\nPY"
  • 如果 docker compose build 因 Docker Hub、镜像源、网络 token 超时等外部原因无法拉基础镜像,仍然必须更新正在运行的 Docker 环境。可用应急方式:
    1. 先执行 npm run build 生成最新前端产物。
    2. docker cp 将改动后的后端文件复制到 apiworker 容器的 /app/backend/,必要时将 dist/ 复制到 frontend 容器的 /usr/share/nginx/html/
    3. 执行 docker compose restart api worker frontend 重启受影响服务。
    4. 执行 docker commit llm-in-text-api-1 llm-in-text-api:latestdocker commit llm-in-text-worker-1 llm-in-text-worker:latest;如果更新了前端,也执行 docker commit llm-in-text-frontend-1 llm-in-text-frontend:latest
    5. 执行 docker compose up -d --no-build --force-recreate api worker frontend,确保新容器来自已更新镜像。
    6. 再次用 docker compose exec -T ... 验证容器内文件和行为,不能只看本地文件。
  • Docker 持久化数据统一落在部署目录内的 docker-data/,包括 PostgreSQL、Redis 和任务共享临时目录。
  • 容器内访问宿主机模型服务时,不要继续使用 localhost;应改成 host.docker.internal 之类的容器可达地址。
  • 当前 Docker 部署的 backend/requirements.docker.txt 已包含 OCR、转换、队列和基础 API 依赖;backend/Dockerfile 额外安装 ffmpeg 以支持视频拆音轨。
  • Worker 容器:worker.py 作为独立服务运行,通过 Redis Streams 消费任务队列。修改 job_handlers.py 或 worker.py 后需要验证 worker 容器内的代码已更新,可通过 docker compose exec -T worker sh -lc "python -c 'from backend.job_handlers import get_handler; print(get_handler(\"completion\").__name__)'" 验证。
  • Redis Streams 架构:任务队列使用 Redis Streams,支持并发控制、速率限制和熔断器。job_system.py 定义 JOB_TYPES 和队列配置,worker.py 注册处理器并运行事件循环。
  • 修改 Docker 相关文件时,除了代码本身,还要同步检查:
    • docker-compose.yml
    • backend/Dockerfile
    • backend/requirements.docker.txt
    • Dockerfile.frontend
    • docker/nginx.conf
    • backend/.env.example 与实际部署用 backend/.env

代码约定

  • 不要把整个仓库当成“全小写+短横线命名”项目。当前实际情况是:
    • Vue 组件和视图多为 PascalCase
    • 前端工具模块多为小写 .js
    • 插件层使用 TypeScript
    • Python 使用 snake_case
  • 以就地风格为准,不要顺手做全仓格式统一。
  • UI 文案和代理回复默认使用中文。
  • 不要修改 milkdown-docs/,它是只读参考资料。
  • 不要新增硬编码密钥、空 catch/except、as any、@ts-ignore 之类的扩散式技术债。
  • 代理在这个仓库里应优先做局部、可验证的修改,不要做无关重构。

调试路径

  • 补全问题: src/components/MilkdownEditor.vue -> src/plugins/copilotPlugin.ts -> src/utils/api.js -> backend/main.py -> backend/prompt.py / backend/llm.py

  • OCR 问题: src/components/MilkdownEditor.vue -> backend/main.py -> backend/llm.py

  • 文档转换问题: src/utils/convert.js -> backend/main.py

  • TTS 问题: src/components/TTSMenu.vue / src/components/TTSPlayer.vue / src/components/MilkdownEditor.vue -> src/utils/api.js -> backend/tts_asr.py

测试和产物

  • pytest.ini 对 backend.main、backend.llm、backend.prompt、backend.geoip、backend.prompts、backend.tts_asr 设了覆盖率门槛,低于 90% 会失败。
  • 默认测试目录是 backend/tests。
  • 常见生成产物包括 dist、htmlcov、.pytest_cache、api_performance_report.md;它们不是源代码。

文档注意事项

  • README.md 对产品功能有参考价值,但其中补全、TTS/ASR 和部分接口说明已经比代码旧。
  • backend/TTS_ASR_MACOS_FIX.md 和 backend/tests/TESTING_GUIDE.md 更适合作为历史背景,不应在与代码冲突时被当成事实来源。
  • 修改行为时,优先参考实现代码和对应测试,再决定是否同步普通文档。