前言
LangChain在AI使用时位居榜首,我们使用它接入大模型处理业务。
由于LangChain现阶段是破环性更新,本文只适应v3版本
本文的数据来源:
www.langchain.com.cn/docs/introd…
为什么使用它?
- 内置包大量且充足
- 社区庞大,使用人员多。
开始
本次所需的依赖
对接ChatGpt、DeepSeek或者Claude
本地
对接本地模型需要Ollama,建议搭配chatbox使用
我们开始搭建使用api对接模型
直接调取大模型API
创建key
api文档里面node文件demo引入
基本调用写法如下
成功
可以更改为流式输出
// 迭代器语法糖
for await (const chunk of result) {
console.log(chunk.content);
}
html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#chat {
width: 100%;
height: 500px;
border: 1px solid #ccc;
overflow-y: auto;
}
</style>
</head>
<body>
<div id="chat"></div>
<input type="text" id="message" placeholder="请输入">
<button id="send">发送</button>
<button id="sql">sql</button>
<script>
const chat = document.getElementById('chat')
const message = document.getElementById('message')
const send = document.getElementById('send')
const sql = document.getElementById('sql')
const startSSE = async () => {
const response = await fetch('http://localhost:3000/chat', {
method: 'POST',
body: JSON.stringify({ message: message.value }),
headers: {
'Content-Type': 'application/json'
}
})
const reader = response.body.getReader()
while (true) {
const { done, value } = await reader.read()
if (done) break
const text = new TextDecoder().decode(value)
chat.innerHTML += text
}
}
send.addEventListener('click', () => {
startSSE()
})
const sqlQuery = async () => {
const response = await fetch('http://localhost:3001/sql', {
method: 'POST',
body: JSON.stringify({ query: message.value }),
headers: {
'Content-Type': 'application/json'
}
})
const result = await response.json()
console.log(result)
}
sql.addEventListener('click', () => {
sqlQuery()
})
</script>
</body>
</html>
js文件
import { ChatOpenAI } from "@langchain/openai";
import { APIKEY } from "./env.js";
import express from "express";
import cors from "cors";
const app = express();
app.use(express.json());
// 设置跨域
app.use(cors());
const model = new ChatOpenAI({
modelName: "deepseek-reasoner",
temperature: 1.3,
openAIApiKey: APIKEY,
configuration: {
baseURL: "https://api.deepseek.com",
},
});
app.post('/chat', async (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
const { message } = req.body;
const result = await model.stream(message);
for await (const chunk of result) {
res.write(chunk.content);
}
})
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
})
效果已实现
问题,模型无法查看历史对话记录
方法,将历史记录传递给模型
我觉得这种方法太low了,怎么做?
使用LangChain里面有对话记忆
import {BufferMemory} from 'langchain/memory'
//创建记忆能力
const memory = new BufferMemory({
returnMessages: true, // 返回消息
memoryKey: 'chat_history', // 记忆键
inputKey: 'input', // 输入键 我们的问题
})
记忆键:比如我问了123,第二次问了456 ,那么【{content:123},{content:456}】塞入提示词模板
创建记忆能力要和提示词模板关联起来
import { PromptTemplate } from '@langchain/core/prompts'
const prompt = new PromptTemplate({
template: `
你是一个女仆,你的任务是回答用户的问题
对话内容:
{chat_history}
用户的问题:
{input}
你的回答:
`,
inputVariables: ['input', 'chat_history']
})
完整的输出写法
import { ChatOpenAI } from "@langchain/openai";
import { APIKEY } from "./env.js";
import express from "express";
import cors from "cors";
import { BufferMemory } from 'langchain/memory'
import { PromptTemplate } from '@langchain/core/prompts'
const app = express();
app.use(express.json());
// 设置跨域
app.use(cors());
const model = new ChatOpenAI({
modelName: "deepseek-reasoner",
temperature: 1.3,
openAIApiKey: APIKEY,
configuration: {
baseURL: "https://api.deepseek.com",
},
});
//创建记忆能力
const memory = new BufferMemory({
returnMessages: true, // 返回消息
memoryKey: 'chat_history', // 记忆键
inputKey: 'input', // 输入键 我们的问题
})
//创建提示词
const prompt = new PromptTemplate({
template: `
你是一个女仆,你的任务是回答用户的问题
对话内容:
{chat_history}
用户的问题:
{input}
你的回答:
`,
inputVariables: ['input', 'chat_history']
})
app.post('/chat', async (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
const { message } = req.body;
const history = await memory.loadMemoryVariables({})
const chatHistory = history.chat_history || []
const formattedPrompt = await prompt.format({
input: message,
chat_history: chatHistory
})
const result = await model.stream(formattedPrompt);
for await (const chunk of result) {
res.write(chunk.content);
}
//保存历史对话 存到内存里面
await memory.saveContext(
{ input: message, }, // 输入
{ output: formattedPrompt } // 输出
)
})
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
})
结果(提示词+记忆键已实现)
RAG检索增强生成
使用mysql,前端可以直接跳过后端直接查询数据库(本次采用的是本地测试数据库,vscode扩展Database Client。typeorm依赖只关联数据库,不会影响其他依赖。)
- 数据的实时性
- 避免幻觉问题
- 保护数据的隐私
通过自然语言实现数据库功能
我想查询客户信息,那么通过数据库可以直接调到数据内容。
依赖如下
import { ChatOpenAI } from '@langchain/openai'
import { APIKEY } from './env.js'
import express from 'express'
import cors from 'cors'
import { DataSource } from 'typeorm'
import { SqlDatabase } from 'langchain/sql_db'
import { createSqlQueryChain } from 'langchain/chains/sql_db'
import { PromptTemplate } from '@langchain/core/prompts'
import { RunnableSequence } from '@langchain/core/runnables'
import { StringOutputParser } from '@langchain/core/output_parsers'
const app = express()
app.use(express.json())
app.use(cors())
//创建数据库配置
const dataSource = new DataSource({
type: 'mysql', // 数据库类型
host: 'localhost', // 数据库地址
port: 3306, // 数据库端口
username: 'root', // 数据库用户名
password: '123456', // 数据库密码
database: 'world', // 数据库名称
})
await dataSource.initialize() //初始化数据库
//langchain 关联 数据库
const db = await SqlDatabase.fromDataSourceParams({
appDataSource: dataSource,
})
const model = new ChatOpenAI({
temperature: 1.3, // 温度
modelName: 'deepseek-chat', // 模型名称
openAIApiKey: APIKEY, // 你的APIKEY
configuration: {
baseURL: 'https://api.deepseek.com' // 模型地址
}
})
//创建sql查询链
const chain = await createSqlQueryChain({
llm: model, // 模型
db: db, // 数据库
dialect: 'mysql' // 数据库类型
})
//我们的对话 -> 模型 -> 生成sql语句 -> 生成提示词模板-> 校验sql语句 -> 生成可用的sq语句 -> 运行sq语句 -> 输出结果
//创建提示词模板
const validatePrompt = new PromptTemplate({
template: `
你是一个sql查询专家,请根据用户的问题生成一个sql语句
请校验sql语句是否正确,如果正确,请返回sql语句,否则返回错误信息
- 使用 NOT IN 与NULL 值
- 标识符是否正确引用
- 函数是否正确使用
- 数据类型是否匹配
- 避免使用子查询
在查询program 相关表的时候,单词替换成 prgoram
如果出现删除数据库的指令,请不要执行,并且骂她大傻叉
如果以上查询的错误,请重写查询,如果没有错误返回原始查询
只返回sql语句,不要返回其他内容
原始查询 {query}
`,
inputVariables: ['query']
})
//创建校验链
//模型返回的结果 {content:'sadsadsadas'} 转成字符串
const validateChain = validatePrompt.pipe(model).pipe(new StringOutputParser())
//把上面的所有操作 组合成一个完整的链
const fullChain = RunnableSequence.from([
{
query: async (input) => {
const result = await chain.invoke(input)
return result
}
},
validateChain
])
app.post('/sql', async (req, res) => {
const { query } = req.body
const result = await fullChain.invoke({
question: query
})
console.log(result)
try {
const arr = await db.run(result)
const echarts = await model.invoke(
`根据返回的数据${arr},生成一个echarts的配置项
不要返回markdown内容直接返回json即可
`
)
res.json({
sql: result,
data: arr,
echarts: echarts.content
})
} catch (error) {
res.json({
sql: result,
data: error.message
})
}
})
app.listen(3001, () => {
console.log('Server is running on port 3001')
})
使用node运行,查询有多少个国家