Skip to content

07 改进RAG应用:诊断调试和支持Java、.NET的MIS系统

你好,我是叶伟民。

上一节课我们完成了实战案例1的代码。然而这只是个demo,如果要支持更多的用例,支持更多的模块,我们需要添加更多选项序号和提示。

但是这又会引起一个新问题,用户在用我们系统的时候,你并没有在旁边看着,你怎么知道如何添加这些内容呢?你又怎么知道如何改进呢?

这就是我们今天要讨论的主题——诊断与调试。我们先开始第一步,查看用户的提问。

查看用户的提问

前面我们已经把对话记录保存在数据库里面了。现在,我们只需要在管理员界面添加相关模块就可以查看它们了。

启用管理员界面

首先,我们需要启用管理员界面。

我们打开 实战案例1\改造前\mysite\urls.py 文件。然后把第7行的注释取消掉。

from django.contrib import admin
from django.urls import include, path

app_name = "home"
urlpatterns = [
    path('', include('home.urls')),
    path('admin/', admin.site.urls),
]

然后重新运行,打开浏览器,导航到 http://127.0.0.1:8000/admin。将会出现以下界面。

然后我们还需要添加管理员,设置管理员密码,才能登录管理员界面。

添加管理员

我们回到 Anaconda Powershell Prompt。按ctrl+c停止运行,然后输入以下命令。

python manage.py createsuperuser

这时将会出现以下提示。

我们输入admin。然后在接下来的Email Address输入 “admin@admin.com”。

Password和Password(again)都输入admin。如果需要跳过密码验证这一步,这里输入y。

如果一切顺利,将会提示超级用户创建成功。

图3

然后我们重新运行,打开浏览器,导航到 http://127.0.0.1:8000/admin。用户名输入admin,密码输入admin,点击登录。

然后我们将会看到如下界面。

在管理员界面添加对话记录模块

不过,现在这个管理员界面并没有地方可以查看对话记录。我们还需要在管理员界面添加对话记录模块。

我们打开实战案例1\改造前\home\admin.py文件。在文件尾部添加以下代码。

from .models import 对话记录

class 对话记录Admin(admin.ModelAdmin):
    ordering = ["created_time"]
    list_display = ['已结束','created_time','不带入大模型对话中','role', 'content', '处理后content','提交给大模型的payload']
    search_fields = ['已结束']
    list_filter = ('已结束',)

admin.site.register(对话记录, 对话记录Admin)

这段代码很好理解,其中第1行导入对话记录模型。第3行到第7行声明了对话记录模块在管理员界面里面的表现方式。第9行将对话记录模块注册到了管理员界面。

添加完刚才的代码,我们重新运行,打开浏览器登录管理员界面。这时将会看到以下界面。

点击“对话记录”来链接之后,我们可以看到后面这几项内容。

首先是用户的对话记录。

然后是大模型返回的结构化结果和AI处理后的结果。

另外还有提交给大模型的payload。

细心的同学可能会注意到,提交给大模型的payload里面的文字是乱码,这样是否会影响到调试和诊断呢?

实际上,我们并不需要查看这些文字,我一般只关心payload里面有多少条messages,前面的内容在提交时有没有遗漏。如果你一定要查看,网上有很多unicode转中文的小工具,自行搜索一下就会发现很多。

现在我们可以根据前面这些信息来改进我们的RAG质量。和我们上节课讲的一样,如果示例不够多,那就加示例;如果对大模型结果做的进一步处理不到位,那就加上对应代码;如果大模型还是出现其他意外,那就参考之前 第五节课“ 让大模型不要那么啰嗦”一节中所说的,添加更多指令。

讲到这里,我们实战案例1的相关代码都讲解完了。但是目前的代码还很乱。因此,我们需要整理一下代码。

整理一下代码

我们可以使用python的region关键词将同一类型的函数归类到一起。我们用VSCode打开项目以后,就可以使用ctrl+shift+a快捷键把

rag.py文件 的代码按region折叠。这时代码将会变成后面这样。

你可以在 这里 查看完整的rag.py代码。另外,我们会在这门专栏的最后一节课使用AI帮助我们认真的重构代码。

如何将本实战案例代码重用到你的MIS系统?

这里还有一个问题,有很多MIS系统并非使用Python编写,而是使用Java或.NET编写。那么如何将本实战案例的代码重用到你的MIS系统呢?

其中一个方案是将这个实战案例的代码从Python改成Java或.NET。然而AI基本是Python的世界,要想在AI的大道上走得更远,我并不建议你采用这个方案。

还有一个方案就是将现有的MIS系统从Java或.NET全部改成Python。这个方案工程量实在是太大,风险实在是太高。我也不建议你采用这个方案。

另一个方案是将这个实战案例的功能开放成API接口,提供给现有的MIS系统调用。这个方案是比较可行的,我们现在就来动手做做!

新增接口

我们在 实战案例1\改造后\home 目录下新增一个文件,命名为 views_api.py。然后添加后面的代码。

from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.core import serializers

from .rag import *
def 开始新的对话api(request):
    开始新的对话()
    return JsonResponse({"code":200,"message":"已经成功开始新的对话"})

@csrf_exempt
def 获取结构化数据查询参数api(request):
    用户输入 = request.POST['question']

    查询参数 = 获取结构化数据查询参数(用户输入)
    return JsonResponse({"querydata":查询参数})

def 从数据库查不到相关数据时的操作api(request):
    从数据库查不到相关数据时的操作()
    return JsonResponse({"code":200,"message":"已经成功执行从数据库查不到相关数据时的操作"})

@csrf_exempt
def 根据查询结果回答用户输入api(request):
    用户输入 = request.POST['question']
    查询结果 = request.POST['query-result']

    根据查询结果回答用户输入(查询结果,用户输入)
    return JsonResponse({"code":200,"message":"已经成功根据查询结果回答用户输入"})

def 获取对话记录api(request):
    conversation_list = 获取当前对话记录()
    conversation_list_json = serializers.serialize("json", list(conversation_list))
    return JsonResponse({"conversationlist":conversation_list_json})

以上代码总共有5个接口,接口的功能都是我们之前接触过的。

除了新增接口,我们还需要修改rag.py和views.py文件。

第一处要修改的是 开始新的对话 函数。原因是views_api.py和views.py都重用了这个函数。首先我们打开views.py文件,将newtalk函数修改成以下模样。

def newtalk(request):
    开始新的对话()
    return redirect(reverse('home:index'))

现在我们看到,第2行代码已经和接口文件里面的代码一样了。

然后打开rag.py文件,在文件尾部新增这个函数。

def 开始新的对话():
  未结束的对话 = 对话记录.objects.filter(已结束=False)
  for current in 未结束的对话:
      current.已结束 = True
  对话记录.objects.bulk_update(未结束的对话, ['已结束'])

其实就是将这段原来在views.py文件的代码移到了rag.py文件,从而让views_api.py和views.py能够共用它。

第二处是将获取当前对话记录的代码从views.py文件移到了rag.py文件,从而让views_api.py和views.py共用它。也就是说,我们首先需要在rag.py文件尾部新增这个函数。

def 获取当前对话记录():
  return 对话记录.objects.filter(已结束=False).order_by('created_time')

然后将views.py文件的index函数改成如下模样。

def index(request):
    if request.method == 'POST':
        用户输入 = request.POST['question']

        查询参数 = 获取结构化数据查询参数(用户输入)
        查询结果 = None
        if 查询参数 is not None:
            查询结果 = 查询(查询参数)

        if 查询结果 is None:
            从数据库查不到相关数据时的操作()
        else:
            查询结果json格式 = serializers.serialize("json", list(查询结果))
            根据查询结果回答用户输入(查询结果json格式,用户输入)

    conversation_list = 获取当前对话记录()
    return render(request, "home/index.html",{"object_list":conversation_list})

第16行就是我们修改的地方。

然而细心的同学会发现,第13行也有变化。是的,这就是我们第三处需要修改的地方,把我们这个Python MIS系统才支持的功能从rag.py文件移出来。

我们需要打开rag.py文件,把将查询结果转为字符串函数修改成以下模样。

def 将查询结果转为字符串(查询结果):
  return_str = ""
  data = json.loads(查询结果)
  for current in data:
    if 'fields' in current:
      for key, value in current['fields'].items():
        return_str += f"{key}{value}\n"
    else:
      for key, value in current.items():
        return_str += f"{key}{value}\n"
  return return_str

我们可以看到,其实就是把原来的第2行代码从rag.py文件移到了views.py文件的index函数。

现在我们已经编写完API接口了,我们还需要注册这些接口,才能让外部调用它们。

注册接口

我们打开 home\urls.py 文件,在第11行之前插入以下代码。

path("api/new-talk", views_api.开始新的对话api, name="api-new-talk"),
path("api/get-query-paras", views_api.获取结构化数据查询参数api, name="api-get-query-paras"),
path("api/answer-without-data", views_api.从数据库查不到相关数据时的操作api, name="api-answer-without-data"),
path("api/answer-with-data", views_api.根据查询结果回答用户输入api, name="api-answer-with-data"),
path("api/get-conversation-list", views_api.获取对话记录api, name="api-get-conversation-list"),

测试接口

现在我们已经注册完接口了,我们重新运行,然后测试接口。

这里需要我们打开postman,输入以下接口url、提交方法和提交数据。然后进行测试。

需要注意的是, 在测试第2和第5个接口时,我们的key和value是需要添加body里面的,并且数据提交类型下拉列表要选form-data。

完成这些操作后,你的Java或者.NET MIS系统就能通过以上接口调用我们的实战案例了。

至此,我们的实战案例1就结束了。完整的代码可以在 这里 下载。

希望你通过这个实战案例能够说服你的利益相关者,说服他支持改造现有的MIS系统,从而真正进入生成式AI这条热门赛道。

小结

好了,今天这一讲到这里就结束了,最后我们来回顾一下。这一讲我们学会了两件事情。

第一件事情是通过查看用户的提问来改进我们的RAG应用。我们可以在管理员界面查看用户的提问,借此改进我们的RAG应用。

第二件事情是如何将实战案例1的代码重用到你的MIS系统。我们可以把rag.py文件里面的函数包装成http接口对外开放,这样你的MIS系统可以通过调用这些http接口来实现RAG功能了。

思考题

为了教学方便,代码中的数据库字段都是使用中文,但是实际工作中基本是英文,所以传给大模型的都会是英文而不是中文,如何处理这个问题?

欢迎你在留言区和我交流互动,如果这节课对你有启发,也推荐分享给身边更多朋友。