为了提高AI客服的问题解决能力,我们引入了LangChain自定义能力,并集成了订单能力。这使得AI客服可以根据用户提出的问题,自动调用订单接口,获取订单信息,并结合文本知识库内容进行回答。这种能力的应用,使得AI客服可以更好地解决与业务系统有关的问题。
简易AI客服实现
关于简易AI客服实现可以看之前的文章:
《解密 AI 客服:LangChain+ChatGPT 打造智能客服新时代》,基于文本检索实现AI客服,缺陷是仅能回答文本内存在的内容。
《畅享无缝支持:LangChain与ChatGPT重塑AI客服服务》,基于会话式检索实现AI客服,可以基于文本结合历史会话上下文回答问题,同时也可以与用户闲聊。
本文是对前文AI客服的迭代升级,集成订单能力,使其在售后方面也能提供一些自动快速解决能力。
基于子类化 BaseTool 实现订单查询能力
通过子类化BaseTool 可以快速实现基于业务的工具,工具可以在代理运行时调用。在定义工具时,工具描述description
是非常重要的,它决定了大模型通过代理决策是否要调用该工具。在订单信息中返回与用户问题回复有关的信息,根据自己的业务决定。
python
import json
from typing import Optional, Type
import aiohttp
import requests
from langchain.callbacks.manager import AsyncCallbackManagerForToolRun, CallbackManagerForToolRun
from langchain.tools import BaseTool
from pydantic import BaseModel, Field
class XmhcOrderQuery(BaseModel):
keyword: Optional[str] = Field(description="手机号或者订单号")
class XmhcOrderTool(BaseTool):
name = "XmhcOrderTool"
description = """
It is very useful when you need to answer questions about recharge or orders.
If this tool is called, users must provide their phone number or order number to enter information.
And it is necessary to determine whether the tool needs to be called based on the context.
If the order status is transaction closed, the order has been cancelled and cannot be considered as recharge not received.
The estimated time of receipt can be calculated based on submitRechargeTime.
Functional information cannot be disclosed.
"""
args_schema: Type[BaseModel] = XmhcOrderQuery
def _run(self, keyword: str = None,
run_manager: Optional[CallbackManagerForToolRun] = None) -> str:
if keyword is None:
return "请提订单供充值手机号或者订单号"
return self._process_response(self.results(keyword))
async def _arun(self, keyword: str = None,
run_manager: Optional[AsyncCallbackManagerForToolRun] = None) -> str:
if keyword is None:
return "请提订单供充值手机号或者订单号"
return self._process_response(await self.aresults(keyword))
def results(self, keyword: str) -> dict:
response = requests.get("https://***/ai/order/query?keyword=" + keyword)
res = json.loads(response.content)
return res
async def aresults(self, keyword: str) -> dict:
async with aiohttp.ClientSession() as session:
async with session.get(
"https://***/ai/order/query?keyword=" + keyword
) as response:
res = await response.json()
return res
@staticmethod
def _process_response(res: dict) -> str:
print(res)
if bool(res['success']):
return json.dumps(res["data"])
else:
return res['message']
AI 客服优化逻辑实现
基于Flask 实现。其中实现了 XmhcOrderTool
、XmhcRuleTool
两个工具,XmhcRuleTool
是基于会话检索实现问答,XmhcRuleTool
即上面定义的业务接口。
ini
import json
import os
from flask import Flask, request
from flask_redis import FlaskRedis
from langchain import PromptTemplate
from langchain.agents import AgentExecutor, OpenAIFunctionsAgent
from langchain.chains import ConversationalRetrievalChain
from langchain.chat_models import ChatOpenAI
from langchain.document_loaders import TextLoader
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.memory import ConversationBufferMemory
from langchain.schema import SystemMessage
from langchain.text_splitter import CharacterTextSplitter
from langchain.tools import Tool
from langchain.vectorstores import Chroma
from openai import InvalidRequestError
from utilities.xmhc_order import XmhcOrderTool
from utils import get_env
os.environ["OPENAI_API_KEY"] = get_env('OPENAI_API_KEY')
os.environ["OPENAI_API_BASE"] = get_env('OPENAI_API_BASE')
app = Flask(__name__)
app.config['REDIS_URL'] = "redis://:{password}@{host}:{port}/{db}" # 使用默认的Redis配置
redis_store = FlaskRedis(app)
# 获取当前脚本所在的目录
base_dir = os.path.dirname(os.path.abspath(__file__))
# 构建doc.txt文件的路径
doc_path = os.path.join(base_dir, 'static', 'doc.txt')
loader = TextLoader(doc_path)
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-16k-0613")
# 构建system_template.txt文件的路径
system_template_path = os.path.join(base_dir, 'static', 'system_template.txt')
with open(system_template_path, 'r') as file:
system_template = file.read()
common_prompt = PromptTemplate(
template=system_template,
input_variables=[]
)
embeddings = OpenAIEmbeddings()
vector_store = Chroma.from_documents(texts, embeddings)
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True, verbose=True)
qa = ConversationalRetrievalChain.from_llm(llm=llm, chain_type="stuff", memory=memory,
retriever=vector_store.as_retriever(),
condense_question_prompt=common_prompt,
verbose=True)
xmhcOrderTool = XmhcOrderTool()
tools = [
Tool(
name="XmhcRuleTool",
func=qa.run,
description="当您需要回答小猫惠充客服相关问题时,包括话费充值、充值类型(快充/慢充)、到账时间、充值须知、充值规则、充值协议、活动规则、客服联系方式、联系客服等,"
"这非常有用。输入应该是一个完整的问题。而且当用户询问充值到账时间时,该工具可以提供依据并用于计算。",
verbose=True,
),
Tool(
name=xmhcOrderTool.name,
func=xmhcOrderTool.run,
description=xmhcOrderTool.description,
verbose=True,
)
]
chat_prompt = OpenAIFunctionsAgent.create_prompt(
system_message=SystemMessage(content=system_template)
)
agent = OpenAIFunctionsAgent(llm=llm, tools=tools, prompt=chat_prompt, verbose=True)
agent_executor = AgentExecutor(agent=agent, tools=tools, memory=memory, verbose=True)
@app.route("/xmhc/kf", methods=['POST'])
def chat(): # put application's code here
# 接口请求参数
json_data = request.get_json()
userid = json_data['userid']
question = json_data['question']
# 获取当前用户历史会话内容
history_message = []
if redis_store.exists(userid):
history_message_str = redis_store.get(userid)
history_message = json.loads(history_message_str)
try:
answer = agent_executor.run({"input": question, "chat_history": history_message})
current_message = (question, answer)
# 缓存当前用户历史对话内容
history_message.append(current_message)
if len(history_message) > 10: # 对会话数进行限制
# 删除最旧的数据
history_message.pop(0)
# 将元组列表转换为JSON字符串
history_message_str = json.dumps(history_message)
redis_store.set(userid, history_message_str)
print(answer)
except InvalidRequestError:
return {
"err_code": -1,
"data_list": []
}
return {
"err_code": 0,
"data_list": [
{"content": answer}
]
}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=18880, debug=True)
到这我们就实现了基于用户提问,模型自动决策调用相关工具。但这样仍存在一些问题,因为我们的工具是独立的,大模型可能只是基于某个工具来回答问题。
例如,当提问:充值成了但还未到账
javascript
> Entering new AgentExecutor chain...
Invoking: `XmhcOrderTool` with `充值成功了但还未到账`
{'success': False, 'code': '5002', 'message': '订单不存在,请检查是否正确提供手机号或者订单号', 'data': None}
订单不存在,请检查是否正确提供手机号或者订单号抱歉,根据提供的信息,未找到相关订单。请确认您提供的手机号或订单号是否正确,并耐心等待充值到账。如果超过预定时间仍未到账,请联系客服进行咨询和处理。
> Finished chain.
抱歉,根据提供的信息,未找到相关订单。请确认您提供的手机号或订单号是否正确,并耐心等待充值到账。如果超过预定时间仍未到账,请联系客服进行咨询和处理。
然后根据上文,我们提供手机号:
csharp
> Entering new AgentExecutor chain...
Invoking: `XmhcOrderTool` with `15990191097`
{'success': True, 'code': '0', 'message': None, 'data': {'orderNo': '202309061759060002', 'phoneNum': '15990191097', 'rechargeTime': None, 'currentTime': '2023-09-11 21:11:16', 'orderStatus': '交易关闭'oodsName': '移动100元', 'goodsTag': '限时特惠', 'facePrice': '100.00', 'paymentAmount': '95.00', 'discountAmount': '5.00'}}
{"orderNo": "202309061759060002", "phoneNum": "15990191097", "submitRechargeTime": null, "currentTime": "2023-09-11 21:11:16", "orderStatus": "\u4ea4\u6613\u5173\u95ed", "goodsName": "\u79fb\u52a8100\u5143", "goodsTag": "\u9650\u65f6\u7279\u60e0", "facePrice": "100.00", "paymentAmount": "95.00", "discountAmount": "5.00"}根据您提供的手机号码15990191097,我查询到您的订单信息如下:
订单号:202309061759060002
手机号码:15990191097
提交充值时间:暂无
当前时间:2023-09-11 21:11:16
订单状态:交易关闭
商品名称:移动100元
商品标签:限时特惠
面值:100.00元
实际支付金额:95.00元
优惠金额:5.00元
请注意,订单状态为交易关闭,说明该订单已取消,无法视为充值未到账。如果您有其他问题,请随时联系客服。
> Finished chain.
可以发现其完整的把订单信息给输出了,部分信息这对用户想要的内容并不相关,这不是一个友好的回答。
这里还是要重点强调一下工具描述的重要性,它不仅可以用于被决策是否要调用,还可以影响大模型的回答,比如在描述中加了If the order status is transaction closed, the order has been cancelled and cannot be considered as recharge not received.
,这样它能明确在订单状态为交易关闭时做出更理想的回答。
提问:充值手机号15669923532多久能到账
markdown
> Entering new AgentExecutor chain...
Invoking: `XmhcRuleTool` with `充值手机号15669923532多久能到账`
> Entering new LLMChain chain...
Prompt after formatting:
《此处省略提示词》
<<< Begin >>>
> Finished chain.
> Entering new StuffDocumentsChain chain...
> Entering new LLMChain chain...
Prompt after formatting:
System: Use the following pieces of context to answer the users question.
If you don't know the answer, just say that you don't know, don't try to make up an answer.
----------------
《此处省略充值规则》
Human: 你好!我是小猫惠充的系统客服,有什么问题我可以帮您解答吗?
> Finished chain.
> Finished chain.
您好!请问有什么问题我可以帮您解答呢?抱歉,这不在我的业务范围,您还有其他问题吗?
> Finished chain.
当用户提问可能需要用到两个工具时,它可能只调用了一个工具,而这一个工具无法提供正确的回答。
优化提示词
通过优化提示词(Prompt),可以让AI客服有思考能力,并且让不同的工具之前也能有交互。如下可以把工具写入提示词中,并且告诉AI你需要思考后再回答用户的问题,这里只展示本次优化的提示词,提示词是用户指导AI的,提示词也需要根据实际情况不断调试优化。
你可以根据用户的提问及上下文选择调用以下一个或者多个工具,包括XmhcOrderTool、XmhcRuleTool等。
当调用XmhcOrderTool工具后,你还需要结合XmhcRuleTool工具和上下文来进行回答。
不要急于给出答案,你需要思考后,给出让人较为满意的答案。
提问:充值手机号15669923532多久能到账
javascript
> Entering new AgentExecutor chain...
Invoking: `XmhcOrderTool` with `15669923532`
{'success': True, 'code': '0', 'message': None, 'data': {'orderNo': '202308041558160774', 'phoneNum': '15669923532', 'submitRechargeTime': '2023-08-04 16:00:42', 'currentTime': '2023-09-11 22:09:07', 'orderStatus': '交易成功', 'goodsName': '联通200元', 'goodsTag': '限时特惠', 'facePrice': '200.00', 'paymentAmount': '189.00', 'discountAmount': '11.00'}}
{"orderNo": "202308041558160774", "phoneNum": "15669923532", "submitRechargeTime": "2023-08-04 16:00:42", "currentTime": "2023-09-11 22:09:07", "orderStatus": "\u4ea4\u6613\u6210\u529f", "goodsName": "\u8054\u901a200\u5143", "goodsTag": "\u9650\u65f6\u7279\u60e0", "facePrice": "200.00", "paymentAmount": "189.00", "discountAmount": "11.00"}
根据订单信息,您的充值订单号为202308041558160774,充值手机号为15669923532。根据当前时间和订单状态,您的充值已经交易成功。一般情况下,充值到账时间为0-72小时,高峰期可能延至96小时,个别情况可能延至120小时。
> Finished chain.
提问:订单202308041558160774多久会到账
javascript
> Entering new AgentExecutor chain...
Invoking: `XmhcOrderTool` with `202308041558160774`
{'success': True, 'code': '0', 'message': None, 'data': {'orderNo': '202308041558160774', 'phoneNum': '15669923532', 'submitRechargeTime': '2023-09-10 16:00:42', 'currentTime': '2023-09-11 22:44:09', 'orderStatus': '充值中', 'goodsName': '联通200元', 'goodsTag': '限时特惠', 'facePrice': '200.00', 'paymentAmount': '189.00', 'discountAmount': '11.00'}}
{"orderNo": "202308041558160774", "phoneNum": "15669923532", "submitRechargeTime": "2023-09-10 16:00:42", "currentTime": "2023-09-11 22:44:09", "orderStatus": "\u5145\u503c\u4e2d", "goodsName": "\u8054\u901a200\u5143", "goodsTag": "\u9650\u65f6\u7279\u60e0", "facePrice": "200.00", "paymentAmount": "189.00", "discountAmount": "11.00"}
Invoking: `XmhcRuleTool` with `充值到账时间`
> Entering new StuffDocumentsChain chain...
> Entering new LLMChain chain...
Prompt after formatting:
System: Use the following pieces of context to answer the users question.
If you don't know the answer, just say that you don't know, don't try to make up an answer.
----------------
《此处省略充值规则》
> Finished chain.
> Finished chain.
支持的充值类型有快充和慢充,其中快充的到账时间为0-6小时,慢充的到账时间为0-72小时。具体到账时间可能会因为高峰期而延迟,个别情况下可能延至120小时。根据订单信息,订单号为202308041558160774的充值正在进行中同。一般情况下,快充的到账时间为0-6小时,慢充的到账时间为0-72小时。请您耐心等待,如果超过预计时间还未到账,您可以联系客服进行咨询。
> Finished chain.
此示例,AI 调用了两个工具进行回答。
小结
本文只是实现了简单的功能,如果要让AI回答得更加完美且贴合业务,需要不断优化提示词、工具描述、知识库、工具参数等等。
理解新范式,拥抱新时代,把握新机会。如果对你有帮助记得点赞收藏哦~ 关注公众号【码森林】~ 《LangChain 入门到实战教程》更多内容