parrot.nvim

GitHub
784 51 简单 1 次阅读 4天前NOASSERTION语言模型Agent插件开发框架
AI 解读 由 AI 自动生成,仅供参考

parrot.nvim 是一款专为 Neovim 打造的 AI 辅助插件,旨在将大语言模型(LLM)的能力无缝融入你的文本编辑工作流。它专注于提供按需的代码补全、内容生成及对话式交互,所有操作均在原生的 Neovim 缓冲区中进行,让开发者能像在聊天一样与 AI 协作编写和修改代码。

这款工具主要解决了传统 AI 编程助手“黑盒化”的问题。不同于自动扫描文件或在后台静默发送数据的智能体,parrot.nvim 坚持透明与隐私优先的原则:用户完全掌控发送给 API 的内容,绝无隐藏的后台请求或自动分析行为。这意味着你可以放心地在敏感项目中使用它,而无需担心代码泄露风险。

parrot.nvim 特别适合注重隐私、希望精确控制 AI 交互过程的资深开发者和技术研究人员。其技术亮点在于强大的兼容性,支持 OpenAI、Anthropic、Google Gemini 等主流云服务,也能通过 Ollama 连接本地离线模型;同时提供灵活的凭证管理和基于 Markdown 的持久化对话存储。无论是修复 Bug、重写代码段还是添加注释,它都能让你在不离开编辑器的情况下,安全高效地完成各类编程任务。

使用场景

资深后端工程师小李正在 Neovim 中重构一个遗留的 Python 微服务模块,需要快速修复逻辑漏洞并补充缺失的文档注释。

没有 parrot.nvim 时

  • 上下文切换频繁:遇到复杂 Bug 或需要生成代码时,必须手动复制代码片段切换到浏览器,在多个 AI 网页标签页间粘贴提问,打断心流。
  • 隐私与安全顾虑:担心将包含内部业务逻辑或敏感配置的代码上传至不明云端服务,缺乏对发送内容的完全掌控权。
  • 工作流割裂:AI 返回的代码需要手动复制回编辑器,再仔细对齐缩进和格式,容易引入人为错误且效率低下。
  • 模型切换困难:想对比不同大模型(如本地 Ollama 与云端 Claude)的输出效果时,需重新配置环境变量或更换工具,过程繁琐。

使用 parrot.nvim 后

  • 原生无缝交互:直接在 Neovim 缓冲区选中代码,通过 PrtRewritePrtAppend 命令即可让 AI 基于当前上下文修复 Bug 或续写代码,无需离开编辑器。
  • 数据主权在握:所有发送给 LLM 的内容均由用户显式触发且完全可控,支持调用本地部署的 Ollama 模型,确保敏感代码绝不流出内网。
  • 指令驱动编辑:利用预定义的系统提示词(System Prompts),一键为函数添加符合团队规范的文档注释(PrtPrepend),自动保持代码风格一致。
  • 灵活模型调度:通过统一接口随时在 OpenAI、Anthropic 或本地模型间切换,针对不同任务选择最优模型,且凭据管理安全透明。

parrot.nvim 通过将大模型能力深度融入 Neovim 原生工作流,让开发者在享有极致编码效率的同时,牢牢掌握数据隐私与操作控制权。

运行环境要求

操作系统
  • Linux
  • macOS
  • Windows
GPU

未说明 (插件本身无 GPU 需求,若使用本地 Ollama 部署模型则取决于所选模型)

内存

未说明

依赖
notes该工具是 Neovim 插件而非独立 Python 应用,因此无 Python 或特定 GPU 驱动要求。核心依赖为 Neovim 0.10+ 版本。支持通过 Ollama 进行本地离线部署,此时硬件需求取决于用户选择运行的具体大语言模型。需配置 API Key(支持环境变量、Bash 命令或密码管理器)才能使用云端服务。
python未说明
Neovim 0.10+
plenary.nvim
fzf-lua (可选)
ripgrep (可选)
telescope.nvim (可选)
parrot.nvim hero image

快速开始

parrot.nvim 🦜

这是 parrot.nvim,一款终极的 随机鹦鹉模型,旨在支持你在 Neovim 中进行文本编辑。

功能演示快速入门命令配置路线图常见问题

parrot.nvim logo

功能

parrot.nvim 提供开箱即用的无缝体验,将当前的 LLM API 与你的 Neovim 工作流紧密集成,专注于文本生成。精选的核心功能包括 按需文本补全和编辑,以及在原生 Neovim 缓冲区 中进行的 类聊天会话

此插件面向那些真正知道自己在做什么,并且重视 隐私与透明度 的用户。用户始终完全掌控将发送到 LLM API 端点的内容,因此该插件完全 排除 了诸如 codexclaude-codegemini-cli 等工具所提供的代理概念。

代码的很大一部分基于 Tibor Schmidt 华丽作品 gp.nvim 的早期分支。

  • 持久对话以 Markdown 文件形式存储在 Neovim 的标准路径或用户定义的位置
  • 基于用户指令和带有预设系统提示的聊天的自定义钩子,用于内联文本编辑
  • 统一的提供者系统,支持任何兼容 OpenAI 的 API:
  • 来自不同来源的灵活 API 凭证管理:
    • 环境变量
    • Bash 命令
    • 密码管理器 CLI(惰性求值)
  • 使用 .parrot.md 文件通过 PrtContext 命令实现仓库特定的指令
  • 自动补全, 在后台隐藏请求来分析你的文件

演示

无缝切换提供商和模型。

https://github.com/user-attachments/assets/0df0348f-85c0-4a2d-ba1f-ede2738c6d02


根据注释触发代码补全。

https://github.com/user-attachments/assets/197f99ac-9854-4fe9-bddb-394c1b64f6b6


让鹦鹉帮你修复 bug。

https://github.com/user-attachments/assets/d3a0b261-a9dd-45e6-b508-dc5280594b06


使用 `PrtRewrite` 重写可视选区。

https://github.com/user-attachments/assets/c3d38702-7558-4e9e-96a3-c43312a543d0


使用 `PrtAppend` 将可视选区作为上下文追加到代码中。

https://github.com/user-attachments/assets/80af02fa-cd88-4023-8a55-f2d3c0a2f28e


使用 `PrtPrepend` 向函数添加注释。

https://github.com/user-attachments/assets/9a6bfe66-4bc7-4b63-8694-67bf9c23c064


使用 `PrtRetry` 重试你最近的一次重写、追加或插入操作。

https://github.com/user-attachments/assets/03442f34-687b-482e-b7f1-7812f70739cc

快速入门

依赖项

此插件需要最新版本的 Neovim,并依赖于一组精心挑选的成熟插件。

安装

lazy.nvim
{
  "frankroeder/parrot.nvim",
  dependencies = { "ibhagwan/fzf-lua", "nvim-lua/plenary.nvim" },
  opts = {}
}
Packer
require("packer").startup(function()
  use({
    "frankroeder/parrot.nvim",
    requires = { 'ibhagwan/fzf-lua', 'nvim-lua/plenary.nvim'},
    config = function()
      require("parrot").setup()
    end,
  })
end)
Neovim 原生包
git clone --depth=1 https://github.com/frankroeder/parrot.nvim.git \
  "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/pack/parrot/start/parrot.nvim

设置

最低要求是至少设置一个提供者,例如下面提供的那个,或者从 提供者配置示例 中选择一个。

{
  "frankroeder/parrot.nvim",
  dependencies = { 'ibhagwan/fzf-lua', 'nvim-lua/plenary.nvim' },
  -- 可选地包含 "folke/noice.nvim" 或 "rcarriga/nvim-notify" 以获得精美的通知
  config = function()
    require("parrot").setup {
      -- 必须显式设置提供者才能使其可用。
      providers = {
        openai = {
          name = "openai",
          api_key = os.getenv "OPENAI_API_KEY",
          endpoint = "https://api.openai.com/v1/chat/completions",
          params = {
            chat = { temperature = 1.1, top_p = 1 },
            command = { temperature = 1.1, top_p = 1 },
          },
          topic = {
            model = "gpt-4.1-nano",
            params = { max_completion_tokens = 64 },
          },
          models ={
            "gpt-4o",
            "o4-mini",
            "gpt-4.1-nano",
          }
        },
      },
    }
  end,
}

使用

聊天基础

parrot.nvim 中的聊天本质上是标准的 Markdown 缓冲区。

工作原理:

  1. 打开聊天:使用 :PrtChatNew 打开一个新的聊天缓冲区(或使用 :PrtChatToggle 切换最近的一个)。
  2. 输入提示:在用户前缀 🗨: 后直接在缓冲区中写下你的问题或指令。
  3. 触发 LLM:按下触发键映射(默认为插入模式下的 <C-g><C-g>)或使用 :PrtChatRespond 命令。
  4. 接收响应:LLM 会将响应流式传输到光标所在位置。
  5. 停止生成:随时按下 <C-g>s 可以停止生成。

关键概念:

  • 上下文:整个缓冲区内容都会作为上下文发送(除非使用了隐藏注释)。
  • 系统提示:你可以为每个聊天或全局设置独特的系统提示。
  • 持久化:聊天会以 .md 文件的形式保存在你配置的目录中。

命令模式(交互式命令)

命令模式允许你在不离开当前缓冲区的情况下,直接对代码与 LLM 进行交互。

可用命令:

  • :PrtRewrite – 根据你的提示重写可视选区。
  • :PrtAppend – 在选区后追加生成的文本。
  • :PrtPrepend – 在选区前插入生成的文本。
  • :PrtRetry – 重试上次的重写/追加/插入操作。
  • :PrtEdit – 编辑并重新运行上一条命令,同时修改提示。

工作流程:

  1. 使用可视化模式选择需要修改的代码。
  2. 运行上述命令之一(例如 :PrtRewrite fix the bug)。
  3. LLM 处理你的选区,并将结果流式传输过来,或者显示一个差异视图。

独立的模型选择: parrot.nvim 维持 两套独立的模型选择

  • 聊天模型:用于聊天缓冲区。可在聊天缓冲区内部使用 :PrtModel 进行切换。
  • 命令模型:用于交互式命令(如 PrtRewrite 等)。可在任何非聊天缓冲区中使用 :PrtModel 进行切换。

这使得你可以为快速的内联编辑使用速度快、成本低的模型,而为深入的对话交流则使用功能更强大的模型。

命令

以下是可配置为快捷键的可用命令。这些命令已包含在默认配置中。此外,还有一些有用的命令通过钩子实现(见下文)。

通用

命令 描述
PrtChatNew <target> 打开一个新的聊天
PrtChatToggle <target> 切换聊天(打开最近一次聊天或新建一个)
PrtChatPaste <target> 将可视选区粘贴到最近的聊天中
PrtInfo 打印插件配置
PrtContext <target> 编辑本地上下文文件
PrtChatFinder 使用 fzf 模糊搜索聊天文件
PrtChatDelete 删除当前的聊天文件
PrtChatRespond 触发聊天响应(在聊天文件中)
PrtStop 中断正在进行的 Parrot 生成任务(在任何地方都有效)
PrtProvider <provider> 切换提供商(参数为空时会触发 fzf)
PrtModel <model> 切换交互式命令使用的模型(参数为空时会触发 fzf)。注意:聊天模型必须在聊天缓冲区内部更改。
PrtStatus 打印当前的提供商和模型选择
PrtReloadCache <optional provider> 为所有或特定提供商重新加载缓存的模型
PrtCmd <optional prompt> 直接生成可执行的 Neovim 命令(需明确确认后执行)
交互式
PrtRewrite <optional prompt> 根据提供的提示重写可视选区(直接输入、输入对话框或从集合中选择)
PrtEdit 类似于 PrtRewrite,但可以修改上次的提示
PrtAppend <optional prompt> 根据提供的提示将文本追加到可视选区(直接输入、输入对话框或从集合中选择)
PrtPrepend <optional prompt> 根据提供的提示将文本插入到可视选区之前(直接输入、输入对话框或从集合中选择)
PrtRetry 重复上次的重写/追加/插入操作
示例钩子
PrtImplement 将可视选区作为提示来生成代码
PrtAsk 向模型提问

其中 <target> 表示在以下目标位置之一打开聊天的命令(默认为 toggle_target):

  • popup:打开一个弹出窗口,可通过下方提供的选项进行配置。
  • split:在水平分割窗口中打开聊天。
  • vsplit:在垂直分割窗口中打开聊天。
  • tabnew:在新标签页中打开聊天。

所有聊天相关命令(PrtChatNew, PrtChatToggle)以及自定义钩子都支持在触发时将可视选区显示在聊天中。而交互式命令则要求用户利用 模板占位符,以便在 API 请求中考虑可视选区。

配置

选项

{
    -- 提供商定义包括端点、API密钥、默认参数,
    -- 以及用于聊天摘要的主题模型参数。你可以为你的提供商使用任意名称,
    -- 并通过自定义函数进行配置。
    providers = {
      openai = {
        name = "openai",
        endpoint = "https://api.openai.com/v1/chat/completions",
        -- 用于在线查询可用模型的端点
        model_endpoint = "https://api.openai.com/v1/models",
        api_key = os.getenv("OPENAI_API_KEY"),
        -- 可选:获取API密钥的替代方法
        -- 使用GPG解密:
        -- api_key = { "gpg", "--decrypt", vim.fn.expand("$HOME") .. "/my_api_key.txt.gpg" },
        -- 使用macOS Keychain:
        -- api_key = { "/usr/bin/security", "find-generic-password", "-s my-api-key", "-w" },
        --- 用于聊天和交互式命令的默认模型参数
        params = {
          chat = { temperature = 1.1, top_p = 1 },
          command = { temperature = 1.1, top_p = 1 },
        },
        -- 用于总结聊天的主题模型参数
        topic = {
          model = "gpt-4.1-nano",
          params = { max_completion_tokens = 64 },
        },
        -- Parrot可以在会话间记住的一组模型
        -- 注意:在未来的版本中,这将得到更智能的处理
        models = {
          "gpt-4.1",
          "o4-mini",
          "gpt-4.1-mini",
          "gpt-4.1-nano",
        },
      },
      ...
    }

    -- 聊天会话和命令流程中使用的默认系统提示
    system_prompt = {
      chat = ...,
      command = ...
    },

    -- 所有命令使用的前缀
    cmd_prefix = "Prt",

    -- curl 的可选参数
    curl_params = {},

    -- 存储持久化状态信息的目录,例如
    -- 当前提供商和所选模型
    state_dir = vim.fn.stdpath("data"):gsub("/$", "") .. "/parrot/persisted",

    -- 存储聊天记录的目录(可通过 PrtChatFinder 搜索)
    chat_dir = vim.fn.stdpath("data"):gsub("/$", "") .. "/parrot/chats",

    -- 聊天用户提示前缀
    chat_user_prefix = "🗨:",

    -- LLM 提示前缀
    llm_prefix = "🦜:",

    -- 明确确认删除聊天文件
    chat_confirm_delete = true,

    -- 本地聊天缓冲区快捷键
    chat_shortcut_respond = { modes = { "n", "i", "v", "x" }, shortcut = "<C-g><C-g>" },
    chat_shortcut_delete = { modes = { "n", "i", "v", "x" }, shortcut = "<C-g>d" },
    chat_shortcut_stop = { modes = { "n", "i", "v", "x" }, shortcut = "<C-g>s" },
    chat_shortcut_new = { modes = { "n", "i", "v", "x" }, shortcut = "<C-g>c" },

    -- 完成回复后将光标移动到文件末尾的选项
    chat_free_cursor = false,

    -- PrtChatToggle、PrtChatNew、PrtContext 以及从 ChatFinder 打开的聊天的默认目标
    -- 取值:popup / split / vsplit / tabnew
    toggle_target = "vsplit",

    -- 交互式用户输入的方式可以是“原生”的
    -- 对于 vim.ui.input,也可以是“缓冲区”,即在原生 nvim 缓冲区中查询输入
    -- (见下方视频演示)
    user_input_ui = "native",

    -- 弹出窗口布局
    -- 边框样式:single、double、rounded、solid、shadow、none
    style_popup_border = "single",

    -- 边距以字符或行数表示
    style_popup_margin_bottom = 8,
    style_popup_margin_left = 1,
    style_popup_margin_right = 2,
    style_popup_margin_top = 2,
    style_popup_max_width = 160

    -- 用于交互式 LLM 调用的提示模板,例如 PrtRewrite,其中 {{llm}} 是
    -- LLM 名称的占位符
    command_prompt_prefix_template = "🤖 {{llm}} ~ ",

    -- 自动选择命令响应(便于命令链式调用)
    -- 如果设置为 false,则会释放缓冲区光标以便在其他地方继续编辑
    command_auto_select_response = true,

    -- 模型缓存刷新的时间间隔(小时)
    -- 设置为 0 可禁用模型缓存
    model_cache_expiry_hours = 48,

    -- 安装插件时,PrtModel 和 PrtChatFinder 使用的 fzf_lua 选项
    fzf_lua_opts = {
        ["--ansi"] = true,
        ["--sort"] = "",
        ["--info"] = "inline",
        ["--layout"] = "reverse",
        ["--preview-window"] = "nohidden:right:75%",
    },

    -- 启用查询加载动画
    enable_spinner = true,
    -- 加载时显示的动画类型
    -- 可选:dots、line、star、bouncing_bar、bouncing_ball
    spinner_type = "star",
    -- 显示通过 @file、@buffer 或 @directory 补全添加的上下文提示
    show_context_hints = true

    -- 在应用重写/追加/前置更改之前显示差异预览
    enable_preview_mode = true,
    preview_auto_apply = false, -- 如果为真,预览超时后会自动应用更改
    preview_timeout = 10000, -- 自动应用前的等待时间(毫秒)
    preview_border = "rounded",
    preview_max_width = 120,
    preview_max_height = 30,
}

演示

user_input_ui = "native" 时,使用 vim.ui.input 作为简洁的输入界面。

https://github.com/user-attachments/assets/c2fe3bde-a35a-4f2a-957b-687e4f6f2e5c

user_input_ui = "buffer" 时,你的输入就是一个普通的缓冲区。关闭时,所有内容都会传递给 API。

https://github.com/user-attachments/assets/63e6e1c4-a2ab-4c60-9b43-332e4b581360

加载动画对于响应时间较长的提供商来说是一个有用的指示器。

https://github.com/user-attachments/assets/ebcd27cb-da00-4150-a0f8-1d2e1afa0acb

键位绑定

该插件提供了以下默认键映射:

键位 描述
<C-g>c 通过 PrtChatNew 打开一个新的聊天
<C-g><C-g> 通过 PrtChatRespond 触发 API 生成回复
<C-g>s 通过 PrtStop 停止任何正在进行的 Parrot 生成过程
<C-g>d 通过 PrtChatDelete 删除当前的聊天文件

提供商配置示例

统一的提供商系统允许你配置任何兼容 OpenAI 的 API 提供商。以下是几个流行提供商的示例:

Anthropic Claude
providers = {
  perplexity = {
    name = "perplexity",
    endpoint = "https://api.perplexity.ai/chat/completions",
    model_endpoint = "https://api.perplexity.ai/models",
    api_key = os.getenv "PERPLEXITY_API_KEY",
    params = {
      chat = { temperature = 1.0, top_p = 1 },
      command = { temperature = 1.0, top_p = 1 },
    },
    topic = {
      model = "pplx-70b-chat",
      params = { max_tokens = 32 },
    },
    headers = function(self)
      return {
        ["Content-Type"] = "application/json",
        ["Authorization"] = "Bearer " .. self.api_key,
      }
    end,
    models = {
      "pplx-70b-chat",
      "pplx-70b-instruct",
      "pplx-40b-chat",
      "pplx-40b-instruct",
      "pplx-22b-chat",
      "pplx-22b-instruct",
    },
    preprocess_payload = function(payload)
      for _, message in ipairs(payload.messages) do
        message.content = message.content:gsub("^%s*(.-)%s*$", "%1")
      end
      if payload.messages[1] and payload.messages[1].role == "system" then
        -- 移除作为系统提示的第一条消息,因为 Perplexity 要求系统提示应包含在 API 请求体中,而非消息列表中
        payload.system = payload.messages[1].content
        table.remove(payload.messages, 1)
      end
      return payload
    end,
  },
}
Meta Llama
providers = {
  llama = {
    name = "llama",
    endpoint = "https://api.llama-api.com/v1/chat/completions",
    model_endpoint = "https://api.llama-api.com/v1/models",
    api_key = os.getenv "LLAMA_API_KEY",
    params = {
      chat = { temperature = 1.0, top_p = 1 },
      command = { temperature = 1.0, top_p = 1 },
    },
    topic = {
      model = "Llama-3.1-8B-Instruct",
      params = { max_tokens = 32 },
    },
    headers = function(self)
      return {
        ["Content-Type"] = "application/json",
        ["Authorization"] = "Bearer " .. self.api_key,
      }
    end,
    models = {
      "Llama-3.1-8B-Instruct",
      "Llama-3.1-70B-Instruct",
      "Llama-3.1-405B-Instruct",
    },
    preprocess_payload = function(payload)
      for _, message in ipairs(payload.messages) do
        message.content = message.content:gsub("^%s*(.-)%s*$", "%1")
      end
      if payload.messages[1] and payload.messages[1].role == "system" then
        -- 移除作为系统提示的第一条消息,因为 Llama API 要求系统提示应包含在请求体中
        payload.system = payload.messages[1].content
        table.remove(payload.messages, 1)
      end
      return payload
    end,
  },
}
Qwen
providers = {
  qwen = {
    name = "qwen",
    endpoint = "https://dashscope.aliyuncs.com/compatible/oss/api/v1/chat/completions",
    model_endpoint = "https://dashscope.aliyuncs.com/compatible/oss/api/v1/models",
    api_key = os.getenv "QWEN_API_KEY",
    params = {
      chat = { temperature = 1.0, top_p = 1 },
      command = { temperature = 1.0, top_p = 1 },
    },
    topic = {
      model = "Qwen-Max",
      params = { max_tokens = 32 },
    },
    headers = function(self)
      return {
        ["Content-Type"] = "application/json",
        ["Authorization"] = "Bearer " .. self.api_key,
      }
    end,
    models = {
      "Qwen-Max",
      "Qwen-Turbo",
      "Qwen-Long",
      "Qwen-Plus",
    },
    preprocess_payload = function(payload)
      for _, message in ipairs(payload.messages) do
        message.content = message.content:gsub("^%s*(.-)%s*$", "%1")
      end
      if payload.messages[1] and payload.messages[1].role == "system" then
        -- 移除作为系统提示的第一条消息,因为 Qwen API 要求系统提示应包含在请求体中
        payload.system = payload.messages[1].content
        table.remove(payload.messages, 1)
      end
      return payload
    end,
  },
}
DeepSeek
providers = {
  deepseek = {
    name = "deepseek",
    endpoint = "https://api.deepseek.com/v1/chat/completions",
    model_endpoint = "https://api.deepseek.com/v1/models",
    api_key = os.getenv "DEEPSEEK_API_KEY",
    params = {
      chat = { temperature = 1.0, top_p = 1 },
      command = { temperature = 1.0, top_p = 1 },
    },
    topic = {
      model = "DeepSeek-V2-Lite",
      params = { max_tokens = 32 },
    },
    headers = function(self)
      return {
        ["Content-Type"] = "application/json",
        ["Authorization"] = "Bearer " .. self.api_key,
      }
    end,
    models = {
      "DeepSeek-V2-Lite",
      "DeepSeek-V2-Pro",
      "DeepSeek-V2.5-Lite",
      "DeepSeek-V2.5-Pro",
    },
    preprocess_payload = function(payload)
      for _, message in ipairs(payload.messages) do
        message.content = message.content:gsub("^%s*(.-)%s*$", "%1")
      end
      if payload.messages[1] and payload.messages[1].role == "system" then
        -- 移除作为系统提示的第一条消息,因为 DeepSeek API 要求系统提示应包含在请求体中
        payload.system = payload.messages[1].content
        table.remove(payload.messages, 1)
      end
      return payload
    end,
  },
}
Yandex GigaChat
providers = {
  gigachat = {
    name = "gigachat",
    endpoint = "https://gigachat.app/api/v1/chat/completions",
    model_endpoint = "https://gigachat.app/api/v1/models",
    api_key = os.getenv "GIGACHAT_API_KEY",
    params = {
      chat = { temperature = 1.0, top_p = 1 },
      command = { temperature = 1.0, top_p = 1 },
    },
    topic = {
      model = "GigaChat Pro",
      params = { max_tokens = 32 },
    },
    headers = function(self)
      return {
        ["Content-Type"] = "application/json",
        ["Authorization"] = "Bearer " .. self.api_key,
      }
    end,
    models = {
      "GigaChat Pro",
      "GigaChat Lite",
      "GigaChat Turbo",
    },
    preprocess_payload = function(payload)
      for _, message in ipairs(payload.messages) do
        message.content = message.content:gsub("^%s*(.-)%s*$", "%1")
      end
      if payload.messages[1] and payload.messages[1].role == "system" then
        -- 移除作为系统提示的第一条消息,因为 Yandex GigaChat 要求系统提示应包含在请求体中
        payload.system = payload.messages[1].content
        table.remove(payload.messages, 1)
      end
      return payload
    end,
  },
}
providers = {
  perplexity = {
    name = "perplexity",
    api_key = os.getenv("PERPLEXITY_API_KEY"),
    endpoint = "https://api.perplexity.ai/chat/completions",
    headers = function(self)
      return {
        ["Content-Type"] = "application/json",
        ["Accept"] = "application/json",
        ["Authorization"] = "Bearer " .. self.api_key,
      }
    end,
    topic = {
      model = "r1-1776",
      params = {
        max_tokens = 64,
      },
    },
    models = {
      "sonar",
      "sonar-pro",
      "sonar-deep-research",
      "sonar-reasoning",
      "sonar-reasoning-pro",
      "r1-1776",
    },
  }
}

添加新命令

提问并以弹出窗口形式接收答案

require("parrot").setup {
    -- ...
    hooks = {
      Ask = function(parrot, params)
        local template = [[
          根据你现有的知识库,请生成一个简洁明了、直接回答问题的回复。在回答中优先考虑准确性和相关性,尽量利用你所掌握的最新信息。力求用简练的语言,抓住问题的核心要点。
          问题: {{command}}
        ]]
        local model_obj = parrot.get_model("command")
        parrot.logger.info("正在调用模型: " .. model_obj.name)
        parrot.Prompt(params, parrot.ui.Target.popup, model_obj, "🤖 询问 ~ ", template)
      end,
    }
    -- ...
}

使用预设聊天提示开始对话,检查拼写

require("parrot").setup {
    -- ...
    hooks = {
      SpellCheck = function(prt, params)
        local chat_prompt = [[
          你的任务是将提供的文本改写成清晰、语法正确的版本,同时尽可能保留原文的意思。请纠正其中的拼写错误、标点符号错误、动词时态问题、用词不当以及其他语法错误。
        ]]
        prt.ChatNew(params, chat_prompt)
      end,
    }
    -- ...
}

更多钩子和键绑定可以参考我的 个人 lazy.nvim 配置 或者 其他用户的配置

提示语集合

如果你在使用 PrtRewritePrtAppendPrtPrepend 时,反复输入相同的提示语,那么可以考虑一种更轻量级的方式——直接定义提示语,而不是通过用户命令(即钩子)来实现:

require("parrot").setup {
    -- ...
    prompts = {
        ["Spell"] = "我希望你校对所提供的文本,并修正其中的错误。" -- 例如::'<,'>PrtRewrite Spell
        ["Comment"] = "请添加注释,解释这段代码的作用。" -- 例如::'<,'>PrtPrepend Comment
        ["Complete"] = "请继续完成文件 {{filename}} 中提供的代码片段。" -- 例如::'<,'>PrtAppend Complete
    }
    -- ...
}

这些提示语可以直接作为上述交互式命令的参数使用,也可以与 模板占位符 结合使用。

模板占位符

用户可以在钩子和系统模板中使用以下占位符,以注入额外的上下文信息:

占位符 含义
{{selection}} 当前可视选区
{{filetype}} 当前缓冲区的文件类型
{{filepath}} 当前文件的完整路径
{{filecontent}} 当前缓冲区的全部内容
{{multifilecontent}} 所有打开缓冲区的全部内容

下面是一个如何在补全钩子中使用这些占位符的示例,该钩子会接收整个文件上下文和选中的代码片段作为输入。

require("parrot").setup {
    -- ...
    hooks = {
      CompleteFullContext = function(prt, params)
        local template = [[
        我有来自 {{filename}} 的如下代码:

        ```{{filetype}}
        {{filecontent}}
        ```

        请特别关注以下部分:
        ```{{filetype}}
        {{selection}}
        ```

        请仔细且逻辑严谨地完成上述代码。请仅回复应插入的代码片段。
        ]]
        local model_obj = prt.get_model("command")
        prt.Prompt(params, prt.ui.Target.append, model_obj, nil, template)
      end,
    }
    -- ...
}

此外,{{filetype}}{{filecontent}} 占位符也可以用于自定义钩子中调用 prt.ChatNew(params, chat_prompt) 时,直接注入整个文件的内容。

require("parrot").setup {
    -- ...
      CodeConsultant = function(prt, params)
        local chat_prompt = [[
          你的任务是分析提供的 {{filetype}} 代码,并提出优化建议以提升其性能。找出代码中可以变得更高效、更快或更节省资源的地方。提供具体的优化方案,并说明这些改动将如何提升代码性能。优化后的代码应保持与原代码相同的功能,同时展现出更高的效率。

          这里是代码:
          ```{{filetype}}
          {{filecontent}}
          ```
        ]]
        prt.ChatNew(params, chat_prompt)
      end,
    }
    -- ...
}

补全功能

除了使用 模板占位符 外,parrot.nvim 还支持通过 nvim-cmpblink.cmp 实现内联补全,从而提供更多上下文信息:

  • @buffer:foo.txt - 包含名为 foo.txt 的打开缓冲区的内容
  • @file:test.lua - 包含名为 test.lua 的文件内容
  • @directory:src/ - 包含目录 src/ 下的所有文件内容

提示:启用 show_context_hints 选项后,你可以直观地看到请求所考虑的实际文件内容提示。补全文本(如 @file)需要放在 新行 上!

配置 nvim-cmp

要启用 parrot.nvim 的补全功能,只需将该源添加到你的 nvim-cmp 配置中:

...
sources = cmp.config.sources({
  { name = "parrot" },
}),
...

配置 blink.cmp

对于 blink.cmp,你需要将 "parrot" 添加到默认的源列表中,并按以下方式配置提供者:

...
parrot = {
    module = "parrot.completion.blink",
    name = "parrot",
    score_offset = 20,
    opts = {
        show_hidden_files = false,
        max_items = 50,
    }
},
...

状态栏支持

使用您最喜欢的状态栏插件,可以显示当前的聊天或命令模式。下面我们提供一个针对 lualine 的示例:

  -- 定义函数及信息格式化
  local function parrot_status()
    local status_info = require("parrot.config").get_status_info()
    local status = ""
    if status_info.is_chat then
      status = status_info.prov.chat.name
    else
      status = status_info.prov.command.name
    end
    return string.format("%s(%s)", status, status_info.model)
  end

  -- 添加到 lueline 区域
  require('lualine').setup {
    sections = {
      lualine_a = { parrot_status }
  }

添加自定义提供者

如果默认提供者不可用,您可以根据需要定义任意数量的自定义提供者。这使您可以自定义各种方面,例如端点、可用模型、默认参数、头部以及处理 LLM 响应的函数。 请注意,以这种方式配置提供者是为高级用户设计的。如果您需要帮助或对改进提供者支持有任何建议,请随时提交问题或讨论。

  providers = {
    my_custom_provider = {
      name = "my_custom_provider",
      api_key = os.getenv("MY_API_KEY"),
      endpoint = "https://api.example.com/v1/chat/completions",
      model = { "model-1", "model-2" },
      -- 提供者特定的 curl 参数(可选)
      curl_params = { "--insecure", "--max-time", "30", "--proxy", "http://proxy:8080" },
      -- 自定义头部函数
      headers = function(api_key)
        return {
          ["Content-Type"] = "application/json",
          ["Authorization"] = "Bearer " .. api_key,
          ["X-Custom-Header"] = "custom-value",
        }
      end,
      -- 自定义负载预处理
      preprocess_payload = function(payload)
        -- 根据您的 API 格式修改负载
        return payload
      end,
      -- 自定义响应处理
      process_stdout = function(response)
        -- 解析来自您的 API 的流式响应
        local success, decoded = pcall(vim.json.decode, response)
        if success and decoded.content then
          return decoded.content
        end
      end,
    },
  }

取消操作

您可以随时使用多种方法停止正在进行的 Parrot 生成:

方法

  1. 快捷键<C-g>s(可通过 chat_shortcut_stop 配置)
  2. 命令:PrtStop(在任何地方都有效)

行为

当您取消生成时:

  • 立即终止:API 请求会立即停止
  • 保留已生成文本:目前为止生成的文本仍保留在缓冲区中
  • 视觉反馈:您会收到确认取消的通知
  • 预览模式:如果在流式传输过程中取消,则不会显示预览
  • 多任务处理:如果有多个生成任务正在运行,所有任务都会被停止

自动命令事件

当生成被取消时,会触发 User PrtCancelled 事件,允许您创建自定义钩子:

vim.api.nvim_create_autocmd("User", {
  pattern = "PrtCancelled",
  callback = function()
    -- 您的自定义逻辑在此处
    print("Parrot 生成已被取消")
  end,
})

高级用法

对于自定义代码中的特定缓冲区取消操作:

-- 仅停止当前缓冲区的任务
local chat_handler = require("parrot").chat_handler
chat_handler:stop({ buffer = vim.api.nvim_get_current_buf() })

-- 不发送通知地停止
chat_handler:stop({ notify = false })

附赠

可以直接从终端访问 parrot.nvim:

command nvim -c "PrtChatNew"

也可以将内容直接通过管道输入到聊天中:

ls -l | command nvim - -c "normal ggVGy" -c ":PrtChatNew" -c "normal p"

路线图

  • 添加状态栏集成/通知功能,用于总结使用的 token 数量或花费金额
  • 改进文档
  • 制作教程视频
  • 降低整体代码复杂度并提高健壮性

常见问题解答

  • 我遇到了与状态相关的错误。

    如果状态损坏,只需删除文件 ~/.local/share/nvim/parrot/persisted/state.json 即可。

  • 补全功能无法正常工作,并且出现了错误。

    请确保您有足够的 API 余额,并检查日志文件 ~/.local/state/nvim/parrot.nvim.log 中是否有任何错误。

  • 聊天和交互式命令的模型选择是如何工作的?

    聊天和交互式命令的模型选择是分开的。要更改聊天模型,您必须位于使用 PrtChatNew 启动的聊天窗口中。在聊天窗口之外切换模型只会影响交互式命令的模型(例如 PrtRewritePrtAppend)。一旦设置,这些选择就会保持不变。

  • 我发现了一个 bug,有一个功能建议,或者有一个总体的想法来改进这个项目。

    欢迎所有人参与该项目!如果您有任何建议、想法或 bug 报告,请随时提交问题。

相关项目

  • parrot.nvim 是早期版本 robitx/gp.nvim 的分支,于 2024 年 1 月从提交 607f94d361f36b8eabb148d95993604fdd74d901 分支出来。此后,原始代码的很大一部分已被移除或重写,这一努力将持续进行,直到 parrot.nvim 发展成为其独立的版本。原始的 MIT 许可证已被保留并将继续维持。
  • huynle/ogpt.nvim
  • PrtCmd 的想法受到 exit.nvim 的启发。

星标历史

星标历史图表

版本历史

v2.5.12025/11/17
v2.5.02025/10/05
v2.4.02025/09/30
v2.3.02025/07/15
v2.2.02025/07/11
v2.1.02025/05/31
v2.0.02025/05/29
v1.8.02025/05/29
v1.7.02025/04/10
v1.6.02025/03/31
v1.5.02025/03/15
v1.4.02025/03/11
v1.3.02025/03/06
v1.2.22025/01/26
v1.2.12024/11/21
v1.2.02024/10/21
v1.1.02024/10/17
v1.0.02024/10/15
v0.7.02024/09/13
v0.6.02024/08/22

常见问题

相似工具推荐

stable-diffusion-webui

stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面,旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点,将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。 无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师,还是想要深入探索模型潜力的开发者与研究人员,都能从中获益。其核心亮点在于极高的功能丰富度:不仅支持文生图、图生图、局部重绘(Inpainting)和外绘(Outpainting)等基础模式,还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外,它内置了 GFPGAN 和 CodeFormer 等人脸修复工具,支持多种神经网络放大算法,并允许用户通过插件系统无限扩展能力。即使是显存有限的设备,stable-diffusion-webui 也提供了相应的优化选项,让高质量的 AI 艺术创作变得触手可及。

162.1k|★★★☆☆|今天
开发框架图像Agent

everything-claude-code

everything-claude-code 是一套专为 AI 编程助手(如 Claude Code、Codex、Cursor 等)打造的高性能优化系统。它不仅仅是一组配置文件,而是一个经过长期实战打磨的完整框架,旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。 通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能,everything-claude-code 能显著提升 AI 在复杂任务中的表现,帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略,使得模型响应更快、成本更低,同时有效防御潜在的攻击向量。 这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库,还是需要 AI 协助进行安全审计与自动化测试,everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目,它融合了多语言支持与丰富的实战钩子(hooks),让 AI 真正成长为懂上

140.4k|★★☆☆☆|今天
开发框架Agent语言模型

ComfyUI

ComfyUI 是一款功能强大且高度模块化的视觉 AI 引擎,专为设计和执行复杂的 Stable Diffusion 图像生成流程而打造。它摒弃了传统的代码编写模式,采用直观的节点式流程图界面,让用户通过连接不同的功能模块即可构建个性化的生成管线。 这一设计巧妙解决了高级 AI 绘图工作流配置复杂、灵活性不足的痛点。用户无需具备编程背景,也能自由组合模型、调整参数并实时预览效果,轻松实现从基础文生图到多步骤高清修复等各类复杂任务。ComfyUI 拥有极佳的兼容性,不仅支持 Windows、macOS 和 Linux 全平台,还广泛适配 NVIDIA、AMD、Intel 及苹果 Silicon 等多种硬件架构,并率先支持 SDXL、Flux、SD3 等前沿模型。 无论是希望深入探索算法潜力的研究人员和开发者,还是追求极致创作自由度的设计师与资深 AI 绘画爱好者,ComfyUI 都能提供强大的支持。其独特的模块化架构允许社区不断扩展新功能,使其成为当前最灵活、生态最丰富的开源扩散模型工具之一,帮助用户将创意高效转化为现实。

107.7k|★★☆☆☆|2天前
开发框架图像Agent

NextChat

NextChat 是一款轻量且极速的 AI 助手,旨在为用户提供流畅、跨平台的大模型交互体验。它完美解决了用户在多设备间切换时难以保持对话连续性,以及面对众多 AI 模型不知如何统一管理的痛点。无论是日常办公、学习辅助还是创意激发,NextChat 都能让用户随时随地通过网页、iOS、Android、Windows、MacOS 或 Linux 端无缝接入智能服务。 这款工具非常适合普通用户、学生、职场人士以及需要私有化部署的企业团队使用。对于开发者而言,它也提供了便捷的自托管方案,支持一键部署到 Vercel 或 Zeabur 等平台。 NextChat 的核心亮点在于其广泛的模型兼容性,原生支持 Claude、DeepSeek、GPT-4 及 Gemini Pro 等主流大模型,让用户在一个界面即可自由切换不同 AI 能力。此外,它还率先支持 MCP(Model Context Protocol)协议,增强了上下文处理能力。针对企业用户,NextChat 提供专业版解决方案,具备品牌定制、细粒度权限控制、内部知识库整合及安全审计等功能,满足公司对数据隐私和个性化管理的高标准要求。

87.6k|★★☆☆☆|昨天
开发框架语言模型

ML-For-Beginners

ML-For-Beginners 是由微软推出的一套系统化机器学习入门课程,旨在帮助零基础用户轻松掌握经典机器学习知识。这套课程将学习路径规划为 12 周,包含 26 节精炼课程和 52 道配套测验,内容涵盖从基础概念到实际应用的完整流程,有效解决了初学者面对庞大知识体系时无从下手、缺乏结构化指导的痛点。 无论是希望转型的开发者、需要补充算法背景的研究人员,还是对人工智能充满好奇的普通爱好者,都能从中受益。课程不仅提供了清晰的理论讲解,还强调动手实践,让用户在循序渐进中建立扎实的技能基础。其独特的亮点在于强大的多语言支持,通过自动化机制提供了包括简体中文在内的 50 多种语言版本,极大地降低了全球不同背景用户的学习门槛。此外,项目采用开源协作模式,社区活跃且内容持续更新,确保学习者能获取前沿且准确的技术资讯。如果你正寻找一条清晰、友好且专业的机器学习入门之路,ML-For-Beginners 将是理想的起点。

85k|★★☆☆☆|今天
图像数据工具视频

ragflow

RAGFlow 是一款领先的开源检索增强生成(RAG)引擎,旨在为大语言模型构建更精准、可靠的上下文层。它巧妙地将前沿的 RAG 技术与智能体(Agent)能力相结合,不仅支持从各类文档中高效提取知识,还能让模型基于这些知识进行逻辑推理和任务执行。 在大模型应用中,幻觉问题和知识滞后是常见痛点。RAGFlow 通过深度解析复杂文档结构(如表格、图表及混合排版),显著提升了信息检索的准确度,从而有效减少模型“胡编乱造”的现象,确保回答既有据可依又具备时效性。其内置的智能体机制更进一步,使系统不仅能回答问题,还能自主规划步骤解决复杂问题。 这款工具特别适合开发者、企业技术团队以及 AI 研究人员使用。无论是希望快速搭建私有知识库问答系统,还是致力于探索大模型在垂直领域落地的创新者,都能从中受益。RAGFlow 提供了可视化的工作流编排界面和灵活的 API 接口,既降低了非算法背景用户的上手门槛,也满足了专业开发者对系统深度定制的需求。作为基于 Apache 2.0 协议开源的项目,它正成为连接通用大模型与行业专有知识之间的重要桥梁。

77.1k|★★★☆☆|2天前
Agent图像开发框架