一、搭建自己的大模型助手
大型模型的出現(xiàn)為許多領(lǐng)域帶來(lái)了革命性的變化,從自然語(yǔ)言處理到計(jì)算機(jī)視覺(jué),甚至是醫(yī)學(xué)和金融領(lǐng)域。然而,對(duì)于許多開發(fā)者來(lái)說(shuō),使用一些開源的模型進(jìn)行實(shí)驗(yàn)和應(yīng)用卻是一個(gè)挑戰(zhàn),因?yàn)樗鼈兺ǔP枰嘿F的硬件資源來(lái)運(yùn)行。大多數(shù)情況下,使用這些模型需要擁有一臺(tái)配備高性能GPU
的服務(wù)器,而這往往是一項(xiàng)昂貴的投資。而 OpenRouter
為使用者提供了部分開源模型的實(shí)現(xiàn),可以通過(guò)API
免費(fèi)使用,主要聚焦在7B
規(guī)模大小的模型,比如谷歌的 gemma-7b
,Mistral AI
的 mistral-7b-instruct
,一定程度避免了自己去部署大模型的成本。
本文就基于 OpenRouter
中免費(fèi)模型接口的能力,使用谷歌的 gemma-7b
模型,搭建自己的大模型助手,實(shí)現(xiàn)效果如下:
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-841236.html
二、OpenRouter 使用
在實(shí)驗(yàn)前首先了解下 OpenRouter
是什么。OpenRouter
是一款整合了各類大模型的中間代理商,而且在國(guó)內(nèi)無(wú)需梯子即可訪問(wèn),通過(guò) OpenRouter
可以調(diào)用超 100
種優(yōu)秀的大模型,其中包括比較流行的 OpenAI
的 ChatGPT
系列(包括GPT4V
),Anthropic
的 Claude
系列,谷歌的 PaLM
和 Gemini
系列等,而且更換模型僅需修改模型的名字即可,無(wú)需修改調(diào)用代碼得邏輯:
官方地址如下:
https://openrouter.ai/
OpenRouter
沒(méi)有對(duì)QQ
郵箱做限制,支持 QQ
郵箱登錄注冊(cè),一定程度上給國(guó)內(nèi)的一些用戶提供了便利,并且還免費(fèi)提供了一批7B
的模型,包括 nous-capybara-7b、mistral-7b-instruct、mythomist-7b、toppy-m-7b、cinematika-7b、gemma-7b-it
:
因此,當(dāng)我們沒(méi)有 GPU
服務(wù)器的時(shí)候,又想借助開源模型搭建一套自己的大模型助手時(shí),就可以考慮使用 OpenRouter
了,注意使用前需要先注冊(cè)賬號(hào),并生成 Api key
:
OpenRouter
主要以 http
的交互方式,因此幾乎可以使用任何支持 http
的語(yǔ)言和框架去調(diào)用 ,同時(shí)也支持通過(guò) OpenAI
的 client.chat.completions.create
方式調(diào)用:
例如:使用 Python
語(yǔ)言 http
的方式,調(diào)用 gemma-7b
模型:
import requests
import json
url = "https://openrouter.ai/api/v1/chat/completions"
model = "google/gemma-7b-it:free"
request_headers = {
"Authorization": "Bearer 你的api_key",
"HTTP-Referer": "http://localhost:8088",
"X-Title": "test"
}
default_prompt = "You are an AI assistant that helps people find information."
def llm(user_prompt,system_prompt=default_prompt):
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
]
request_json = {
"model": model,
"messages": messages,
"max_tokens": 2048
}
respose = requests.request(
url=url,
method="POST",
json=request_json,
headers=request_headers
)
return json.loads(respose.content.decode('utf-8'))['choices'][0]['message']['content']
if __name__ == '__main__':
print(llm("你好,介紹一下你自己"))
運(yùn)行輸出:
使用 OpenAI
的 client.chat.completions.create
方式,調(diào)用 gemma-7b
模型:
from openai import OpenAI
model = "google/gemma-7b-it:free"
default_prompt = "You are an AI assistant that helps people find information."
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key="你的api_key",
)
def llm(user_prompt, system_prompt=default_prompt):
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
]
completion = client.chat.completions.create(
extra_headers={
"HTTP-Referer": "http://localhost:8088",
"X-Title": "test",
},
model=model,
messages=messages,
max_tokens = 2048
)
return completion.choices[0].message.content
if __name__ == '__main__':
print(llm("你好,介紹一下你自己"))
運(yùn)行輸出:
流式輸出示例:
from openai import OpenAI
model = "google/gemma-7b-it:free"
default_prompt = "You are an AI assistant that helps people find information."
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key="你的api_key",
)
def llm(user_prompt, system_prompt=default_prompt):
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
]
completion = client.chat.completions.create(
extra_headers={
"HTTP-Referer": "http://localhost:8088",
"X-Title": "test",
},
model=model,
messages=messages,
max_tokens = 2048,
stream=True
)
for respose in completion:
if respose and respose.choices and len(respose.choices) > 0:
msg = respose.choices[0].delta.content
print(msg, end='', flush=True)
if __name__ == '__main__':
llm("你好,介紹一下你自己")
運(yùn)行輸出:
三、搭建大模型助手
上面簡(jiǎn)單認(rèn)識(shí)了 OpenRouter
的能力,下面基于 OpenRouter
上谷歌的 gemma-7b
模型搭建一個(gè)自己的大模型助手,簡(jiǎn)單的執(zhí)行過(guò)程如下。
其中后端服務(wù)使用 Python + tornado
實(shí)現(xiàn) Web
服務(wù),前端使用基礎(chǔ)的 Html + Jquery
的方式。
3.1 服務(wù)端搭建
所屬依賴版本如下:
openai==0.27.8
tornado==6.3.2
構(gòu)建問(wèn)答助手接口 server.py
:
接口我們接收兩個(gè)參數(shù) questions
和 history
,其中 history
由后端維護(hù)并追加聊天記錄,前端只負(fù)責(zé)臨時(shí)存儲(chǔ),每次請(qǐng)求攜帶上一次請(qǐng)求返回的 history
即可,調(diào)用 OpenRouter
使用 OpenAI
庫(kù)的方式。
整體實(shí)現(xiàn)邏輯如下:
from tornado.concurrent import run_on_executor
from tornado.web import RequestHandler
import tornado.gen
from openai import OpenAI
import json
class Assistant(RequestHandler):
model = "google/gemma-7b-it:free"
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key="你的api_key",
)
default_prompt = "You are an AI assistant that helps people find information."
def prepare(self):
self.executor = self.application.pool
def set_default_headers(self):
self.set_header('Access-Control-Allow-Origin', "*")
self.set_header('Access-Control-Allow-Headers', "Origin, X-Requested-With, Content-Type, Accept")
self.set_header('Access-Control-Allow-Methods', "GET, POST, PUT, DELETE, OPTIONS")
@tornado.gen.coroutine
def post(self):
json_data = json.loads(self.request.body)
if 'questions' not in json_data or 'history' not in json_data:
self.write({
"code": 400,
"message": "缺少必填參數(shù)"
})
return
questions = json_data['questions']
history = json_data['history']
result = yield self.do_handler(questions, history)
self.write(result)
@run_on_executor
def do_handler(self, questions, history):
try:
answer, history = self.llm(questions, history)
return {
"code": 200,
"message": "success",
"answer": answer,
"history": history
}
except Exception as e:
return {
"code": 400,
"message": str(e)
}
def llm(self, user_prompt, messages, system_prompt=default_prompt):
if not messages:
messages = []
messages.append({"role": "user", "content": user_prompt})
completion = self.client.chat.completions.create(
extra_headers={
"HTTP-Referer": "http://localhost:8088",
"X-Title": "test",
},
model=self.model,
messages=messages,
max_tokens=2048
)
answer = completion.choices[0].message.content
messages.append({"role": "assistant", "content": answer})
return answer, messages
路由配置,并啟動(dòng)服務(wù) app.py
:
import tornado.web
import tornado.ioloop
import tornado.httpserver
import os
from concurrent.futures.thread import ThreadPoolExecutor
from server import Assistant
## 配置
class Config():
port = 8081
base_path = os.path.dirname(__file__)
settings = {
# "debug":True,
# "autore load":True,
"static_path": os.path.join(base_path, "resources/static"),
"template_path": os.path.join(base_path, "resources/templates"),
"autoescape": None
}
# 路由
class Application(tornado.web.Application):
def __init__(self):
handlers = [
("/assistant", Assistant),
("/(.*)$", tornado.web.StaticFileHandler, {
"path": os.path.join(Config.base_path, "resources/static"),
"default_filename": "index.html"
})
]
super(Application, self).__init__(handlers, **Config.settings)
self.pool = ThreadPoolExecutor(10)
if __name__ == '__main__':
app = Application()
httpserver = tornado.httpserver.HTTPServer(app)
httpserver.listen(Config.port)
print("start success", "prot = ", Config.port)
print("http://localhost:" + str(Config.port) + "/")
tornado.ioloop.IOLoop.current().start()
下面可以使用 Postman
進(jìn)行測(cè)試:
請(qǐng)求內(nèi)容:
{
"questions":"你好,介紹下你自己",
"history":[]
}
輸出示例:
從結(jié)果看接口訪問(wèn)正常,下面開始前端的搭建。
3.2 前端搭建
前端需要構(gòu)建一個(gè)問(wèn)答聊天界面,需要注意的是,模型返回的內(nèi)容可能是 MD
格式,前端需要解析成html
格式展示,整體實(shí)現(xiàn)過(guò)程如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>AI 聊天對(duì)話</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.container {
display: flex;
height: 100vh;
}
.left-panel {
flex: 15%;
background-color: #f2f2f2;
padding: 10px;
}
.right-panel {
flex: 85%;
background-color: #ffffff;
display: flex;
flex-direction: column;
}
.chat-log {
flex: 1;
overflow-y: auto;
padding: 20px;
}
.chat-bubble {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.user-bubble {
justify-content: flex-end;
}
.bubble-content {
padding: 10px 15px;
border-radius: 20px;
}
.user-bubble .bubble-content {
background-color: #d6eaff;
color: #000000;
}
.ai-bubble .bubble-content {
background-color: #e5ece7;
color: #000;
}
.input-area {
display: flex;
align-items: center;
padding: 20px;
}
.input-text {
flex: 1;
padding: 10px;
margin-right: 10px;
}
.submit-button {
padding: 10px 20px;
background-color: #2196f3;
color: #ffffff;
border: none;
cursor: pointer;
}
li {
margin-top: 10px;
}
a {
text-decoration: none;
}
table {
border: 1px solid #000;
border-collapse: collapse;
}
table td, table th {
border: 1px solid #000;
}
table td, table th {
padding: 10px;
}
.language-sql {
width: 95%;
background-color: #F6F6F6;
padding: 10px;
font-weight: bold;
border-radius: 5px;
word-wrap: break-word;
white-space: pre-line;
/* overflow-wrap: break-word; */
display: block;
}
select {
width: 100%;
height: 30px;
border: 2px solid #6089a4;
font-size: 15px;
margin-top: 5px;
}
.recommendation{
color: #1c4cf3;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="container">
<div class="left-panel">
<h2>智能問(wèn)答助手</h2>
<h3>常用問(wèn)題</h3>
<div class="recommendation">幫我寫一個(gè)Java快速排序</div>
<div class="recommendation">Java 8有什么新特性</div>
<div class="recommendation">JVM優(yōu)化建議</div>
<div class="recommendation">內(nèi)存占用高,如何優(yōu)化</div>
<div class="recommendation">MySQL優(yōu)化建議</div>
<div class="recommendation">MySQL如何查看執(zhí)行計(jì)劃</div>
</div>
<div class="right-panel">
<div class="chat-log" id="chat-log">
</div>
<div class="input-area">
<input type="text" id="user-input" class="input-text" placeholder="請(qǐng)輸入您的問(wèn)題,回車或點(diǎn)擊發(fā)送確定。">
<button id="submit" style="margin-left: 10px;width: 100px" onclick="sendMessage()" class="submit-button">
發(fā)送
</button>
<button style="margin-left: 20px;width: 100px;background-color: red" onclick="clearChat()"
class="submit-button">清除記錄
</button>
</div>
</div>
</div>
<script type="text/javascript" src="http://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script>
// 聊天歷史記錄
var messageHistory = [];
// 添加AI信息
function addAIMessage(message) {
$("#chat-log").append(
"<div class=\"chat-bubble ai-bubble\">\n" +
" <div class=\"bubble-content\">" + message + "</div>\n" +
"</div>"
)
}
// 添加人類信息
function addUserMessage(message) {
$("#chat-log").append(
"<div class=\"chat-bubble user-bubble\">\n" +
" <div class=\"bubble-content\">" + message + "</div>\n" +
"</div>"
)
}
// 滑動(dòng)到底部
function slideBottom() {
let chatlog = document.getElementById("chat-log");
chatlog.scrollTop = chatlog.scrollHeight;
}
// 調(diào)用api
function chatApi(message) {
slideBottom();
data = {
questions: message,
history: messageHistory
};
$.ajax({
url: "http://127.0.0.1:8081/assistant",
type: "POST",
contentType: "application/json",
dataType: "json",
data: JSON.stringify(data),
success: function (res) {
if (res.code === 200) {
let answer = res.answer;
answer = marked.parse(answer);
addAIMessage(answer);
messageHistory = res.history;
} else {
addAIMessage("服務(wù)接口調(diào)用錯(cuò)誤。");
}
},
error: function (e) {
addAIMessage("服務(wù)接口調(diào)用異常。");
}
});
}
// 發(fā)送消息
function sendMessage() {
let userInput = $('#user-input');
let userMessage = userInput.val();
if (userMessage.trim() === '') {
return;
}
userInput.val("");
addUserMessage(userMessage);
chatApi(userMessage);
}
// 清空聊天記錄
function clearChat() {
$("#chat-log").empty();
messageHistory = [];
addAIMessage("你好,請(qǐng)輸入你想問(wèn)的問(wèn)題。");
}
// 初始化
function init() {
addAIMessage("你好,請(qǐng)輸入你想問(wèn)的問(wèn)題。");
var submit = $("#submit");
var userInput = $("#user-input");
var focus = false;
// 監(jiān)聽輸入框焦點(diǎn)
userInput.focus(function () {
focus = true;
}).blur(function () {
focus = false;
});
// 回車監(jiān)聽事件
document.addEventListener("keydown", function (event) {
if (event.keyCode === 13) {
console.log(focus);
if (focus) {
submit.click();
}
}
});
}
init();
</script>
</body>
</html>
運(yùn)行效果:
到此,我們自己的大模型助手就基本做好了!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-841236.html
到了這里,關(guān)于無(wú)需 GPU 服務(wù)器,借助 OpenRouter 零成本搭建自己的大模型助手的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!