背景
NeoAI(https://github.com/Bryley/neoai.nvim)是一个Neovim插件,它将OpenAI的GPT-4的强大功能直接引入Neovim编辑器。它可以帮助你生成代码、重写文本,甚至在与你的代码上下文相符的情况下提供建议。该插件采用了用户友好的界面,使你可以轻松地与人工智能进行交互,获取所需的帮助。该插件的主要动机是在你的Neovim编程工作流中提供无缝集成的AI聊天助手,如ChatGPT。而作者目前的实现也仅支持ChatGPT。对于中国用户来说,多少有点不便。
在一段时间前,我向作者提了一个issue(https://github.com/Bryley/neoai.nvim/issues/50),并且实现了基于科大讯飞Spark的集成实现(https://github.com/Bryley/neoai.nvim/pull/52)。不过仓库的作者截至目前也没有评审代码和回复(可能比较忙),因此,我fork了一份到自己的仓库(https://github.com/skyfireitdiy/neoai.nvim),并将其他的一些pr合入。
现在既然千帆大模型开放了,那么,今天就将千帆大模型接入NeoAI。
测试
创建应用
千帆大模型的配置参数主要有API Key/Secret Key,由此通过调用远端API获取access_token,后续根据access_token调用服务。
接下来获取API Key/Secret Key。
在百度云控制台“应用接入”创建应用,即可拿到API Key/Secret Key。
开通付费
在“计费管理”页面开通想要使用的模型的付费,才能从API调用。
测试接口
在“人工智能 AI”可以直接进行测试,并生成对应的接口代码。
编写一个命令行工具
接下来,将接口代码下载下来,加以改造,使其接受命令行参数。
代码如下:
 #!/usr/bin/env python3import requestsimport jsonimport argparse
urls = {    "ErnieBot": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant",    "ErnieBot-turbo": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant",}
def chat(api_key, secret_key, messages, m):    url = urls[m] + "?access_token=" + get_access_token(api_key, secret_key)
    payload = json.dumps({        "messages": messages    })    headers = {        'Content-Type': 'application/json'    }    response = requests.request("POST", url, headers=headers, data=payload)    print(response.text)
def get_access_token(api_key, secret_key):    url = "https://aip.baidubce.com/oauth/2.0/token"    params = {"grant_type": "client_credentials",              "client_id": api_key, "client_secret": secret_key}    return str(requests.post(url, params=params).json().get("access_token"))
if __name__ == '__main__':    parser = argparse.ArgumentParser()    parser.add_argument('api_key')    parser.add_argument('secret_key')    parser.add_argument('messages')    parser.add_argument('model')    args = parser.parse_args()    chat(args.api_key, args.secret_key, args.messages, args.model)
       复制代码
 
测试如下:
集成进 NeoAI
之前集成过讯飞Spark,因此本次集成千帆就比较简单了。
集成配置
AI配置位于:neoai.nvim/lua/neoai/config.lua
在模型配置中增加千帆配置:
 models = {    {        name = "openai",        model = "gpt-3.5-turbo",        params = nil,    },    {        name = "spark",        model = "v1",        params = nil,    },    {        name = "qianfan",        model = "ErnieBot-turbo",        params = nil,    }},
       复制代码
 
增加千帆认证信息配置
 qianfan = {    api_key = {        secret_env = "QIANFAN_SECRET",        apikey_env = "QIANFAN_APIKEY",        secret = nil,        apikey = nil,        get = function()            if not M.options.qianfan.api_key.secret then                M.options.qianfan.api_key.secret = os.getenv(M.options.qianfan.api_key.secret_env)            end            if not M.options.qianfan.api_key.apikey then                M.options.qianfan.api_key.apikey = os.getenv(M.options.qianfan.api_key.apikey_env)            end            if M.options.qianfan.api_key.secret and M.options.qianfan.api_key.apikey then                return M.options.qianfan.api_key.secret, M.options.qianfan.api_key.apikey            end            local msg =  M.options.qianfan.api_key.secret_env .. "/"            .. M.options.qianfan.api_key.apikey_env .. " environment variable is not set"            logger.error(msg)            error(msg)        end    },},
       复制代码
 
本质上是从环境变量 QIANFAN_SECRET和QIANFAN_APIKEY获取认证信息。
使用认证信息,调用脚本
在neoai.nvim/lua/neoai/chat/models/中创建qianfan.lua:
 local utils = require("neoai.utils")local config = require("neoai.config")
---@type ModelModulelocal M = {}
M.name = "Qianfan"
M._chunks = {}local raw_chunks = {}
M.get_current_output = function()    return table.concat(M._chunks, "")end
---@param chunk string---@param on_stdout_chunk fun(chunk: string) Function to call whenever a stdout chunk occursM._recieve_chunk = function(chunk, on_stdout_chunk)    local raw_json = chunk
    table.insert(raw_chunks, raw_json)
    local ok, path = pcall(vim.json.decode, raw_json)    if not ok then        on_stdout_chunk("111")        return    end
    path = path.result    if path == nil then        on_stdout_chunk("222")        return    end
    on_stdout_chunk(path)    -- append_to_output(path, 0)    table.insert(M._chunks, path)end
---@param chat_history ChatHistory---@param on_stdout_chunk fun(chunk: string) Function to call whenever a stdout chunk occurs---@param on_complete fun(err?: string, output?: string) Function to call when model has finishedM.send_to_model = function(chat_history, on_stdout_chunk, on_complete)    local secret, apikey  = config.options.qianfan.api_key.get()    local model = config.options.models[config.options.selected_model_index+1].model
    local get_script_dir = function()        local info = debug.getinfo(1, "S")        local script_path  = info.source:sub(2)        return script_path:match("(.*/)")    end
    local py_script_path = get_script_dir() .. "qianfan.py"
    os.execute("chmod +x "..py_script_path)
    chunks = {}    raw_chunks = {}    on_stdout_chunk(apikey)    on_stdout_chunk(secret)    on_stdout_chunk(vim.json.encode(chat_history.messages))    on_stdout_chunk(model)    utils.exec(py_script_path, {        apikey,        secret,        vim.json.encode(chat_history.messages),        model,    }, function(chunk)        M._recieve_chunk(chunk, on_stdout_chunk)    end, function(err, _)    local total_message = table.concat(raw_chunks, "")    local ok, json = pcall(vim.json.decode, total_message)    if ok then        if json.error ~= nil then            on_complete(json.error.message, nil)            return        end    end    on_complete(err, M.get_current_output())end)end
return M
       复制代码
 
此lua脚本会调用相同目录下的qianfan.py脚本,与千帆对话交互。
在初始化中配置使用千帆大模型
 let $QIANFAN_APIKEY = "xxxxxxxxxx"let $QIANFAN_SECRET = "yyyyyyyyyyyyyyyyyyyyyyy"
lua<<EOFrequire("neoai").setup({    selected_model_index = 2,})EOF
       复制代码
 
上述配置在插件配置中配置。
效果
整体还是很不错的。
欢迎大家试用:https://github.com/skyfireitdiy/neoai.nvim
评论