系列篇章💥
No. | 文章 |
---|---|
1 | AI大模型探索之路-实战篇:智能化IT领域搜索引擎的构建与初步实践 |
2 | AI大模型探索之路-实战篇:智能化IT领域搜索引擎之GLM-4大模型技术的实践探索 |
3 | AI大模型探索之路-实战篇:智能化IT领域搜索引擎之知乎网站数据获取(初步实践) |
4 | AI大模型探索之路-实战篇:智能化IT领域搜索引擎之知乎网站数据获取(函数封装) |
5 | AI大模型探索之路-实战篇:智能化IT领域搜索引擎之知乎网站数据获取(流程优化) |
6 | AI大模型探索之路-实战篇:智能化IT领域搜索引擎之github网站在线搜索 |
7 | AI大模型探索之路-实战篇:智能化IT领域搜索引擎之HuggingFace网站在线搜索 |
目录
- 系列篇章💥
- 一、前言
- 二、总体概览
- 三、流程优化
- 1、文件名优化
- 2、重新改造搜索服务
- 3、构建判别模型
- 4、搜索词优化
- 5、外部函数流程优化
- 四、全流程代码总结
- 1、大模型客户端创建
- 2、google搜索函数
- 3、创建文件名称函数
- 4、知乎内容查询函数
- 5、大模型判断函数
- 6、关键词获取函数
- 7、问题答案获取函数
- 8、funcation calling函数信息生成器
- 9、大模型调用函数
- 10、最外层的结果查询函数
- 结语
一、前言
在上一篇文章中,我们对智能搜索中涉及的核心函数进行了精心的封装,为智能化IT领域搜索引擎的实现奠定了坚实基础。本文将致力于智能搜索流程中相关函数和工具的进一步优化与整理,旨在提升整个搜索引擎的效率和用户体验。
二、总体概览
本章将重点关注智能搜索流程的优化,通过简化函数调用流程、消除冗余代码、优化算法效率等手段,进一步提升搜索引擎的性能。同时,我们将对使用的工具方法进行改造升级,确保它们能够更好地支持当前的搜索功能。
三、流程优化
在流程优化环节,我们将对数据获取、处理、索引和搜索等关键环节进行细致入微的调整。通过采用更高效的数据结构、更先进的算法和更合理的系统架构,我们希望能够在保证搜索准确性的前提下,大幅度提高搜索速度,降低系统资源消耗。
1、文件名优化
文件名里有特殊字符的,在创建文件的时候会创建失败
def windows_create_name(s, max_length=255):
"""
将字符串转化为符合Windows文件/文件夹命名规范的名称。
参数:
- s (str): 输入的字符串。
- max_length (int): 输出字符串的最大长度,默认为255。
返回:
- str: 一个可以安全用作Windows文件/文件夹名称的字符串。
"""
# Windows文件/文件夹名称中不允许的字符列表
forbidden_chars = ['<', '>', ':', '"', '/', '\\', '|', '?', '*']
# 使用下划线替换不允许的字符
for char in forbidden_chars:
s = s.replace(char, '_')
# 删除尾部的空格或点
s = s.rstrip(' .')
# 检查是否存在以下不允许被用于文档名称的关键词,如果有的话则替换为下划线
reserved_names = ["CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"]
if s.upper() in reserved_names:
s += '_'
# 如果字符串过长,进行截断
if len(s) > max_length:
s = s[:max_length]
return s
文件名测试
# 测试
input_str = "这是一个包含不允许的字符,如 <, >, :, \, /, |, ?, * 等的很长的字符串..."
print(windows_create_name(input_str))
输出:
这是一个包含不允许的字符,如 _, _, _, _, _, _, _, _ 等的很长的字符串
2、重新改造搜索服务
def get_search_text(q, url):
cookie = "q_c1=3c10baf5bd084b3cbfe7eece648ba243|1704976541000|1704976541000; _zap=086350b3-1588-49c4-9d6d-de88f3faae03; d_c0=AGCYfYvO_RePTvjfB1kZwPLeke_N5AM6nwo=|1704949678; _xsrf=qR1FJHlZ9dvYhhoj4SUj43SAIBUwPOqm; __snaker__id=wNWnamiJKBI0kzkI; q_c1=d44e397edb6740859a7d2a0d4155bfab|1706509753000|1706509753000; Hm_lvt_98beee57fd2ef70ccdd5ca52b9740c49=1706509695,1706589529,1706765465,1708650963; z_c0=2|1:0|10:1713167971|4:z_c0|80:MS4xOGRHQVNnQUFBQUFtQUFBQVlBSlZUYjZqOTJaLXVDSjdRSmJKMHgyVEhxTE13UGN1TUJBdHZnPT0=|15b2c2ece393ac4ea374d9b36cde5af7304f8ee7632e060fe6835bfadb5e4132; KLBRSID=9d75f80756f65c61b0a50d80b4ca9b13|1713170212|1713167958"
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
title = None
code_ = False
headers = {
'authority': 'www.zhihu.com',
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
'cache-control': 'max-age=0',
'cookie': cookie,
'upgrade-insecure-requests': '1',
'user-agent':user_agent,
}
# 普通问答地址
if 'zhihu.com/question' in url:
res = requests.get(url, headers=headers).text
res_xpath = etree.HTML(res)
title = res_xpath.xpath('//div/div[1]/div/h1/text()')[0]
text_d = res_xpath.xpath('//div/div/div/div[2]/div/div[2]/div/div/div[2]/span[1]/div/div/span/p/text()')
# 专栏地址
elif 'zhuanlan' in url:
headers['authority'] = 'zhaunlan.zhihu.com'
res = requests.get(url, headers=headers).text
res_xpath = etree.HTML(res)
title = res_xpath.xpath('//div[1]/div/main/div/article/header/h1/text()')[0]
text_d = res_xpath.xpath('//div/main/div/article/div[1]/div/div/div/p/text()')
code_ = res_xpath.xpath('//div/main/div/article/div[1]/div/div/div//pre/code/text()')
# 特定回答的问答网址
elif 'answer' in url:
res = requests.get(url, headers=headers).text
res_xpath = etree.HTML(res)
title = res_xpath.xpath('//div/div[1]/div/h1/text()')[0]
text_d = res_xpath.xpath('//div[1]/div/div[3]/div/div/div/div[2]/span[1]/div/div/span/p/text()')
if title == None:
return None
else:
title = windows_create_name(title)
# 创建问题答案正文
text = ''
for t in text_d:
txt = str(t).replace('\n', ' ')
text += txt
# 如果有code,则将code追加到正文的追后面
if code_:
for c in code_:
co = str(c).replace('\n', ' ')
text += co
encoding = tiktoken.encoding_for_model("gpt-3.5-turbo")
json_data = [
{
"link": url,
"title": title,
"content": text,
"tokens": len(encoding.encode(text))
}
]
with open('./auto_search/%s/%s.json' % (q, title), 'w') as f:
json.dump(json_data, f)
return title
3、构建判别模型
为了让大模型调用流程更稳定,创建一个判别模型
import random
import string
def generate_random_key(length=30):
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(length))
定义模型判断函数
def identify_model(q):
# 创建密钥
sk = generate_random_key()
# 调用模型进行判别
response = client.chat.completions.create(
model="glm-4",
messages=[
{"role": "system", "content": "你是一个用户问题判断器,专门用于判别你是否知道用户当前问题的答案。\
如果不知道,请回答“%s”,若知道,请正常回答" % sk},
{"role": "user", "content": "请问,GPT-3.5微调总共分为几步?"},
{"role": "assistant", "content": "%s" % sk},
{"role": "user", "content": q}
]
)
res = response.choices[0].message.content
if sk in res or '对不起' in res or '抱歉' in res or '超出知识库' in res:
return(True)
else:
return(res)
判断函数测试(询问大模型知识库知道的问题)
q = '请问什么是机器学习?'
print(identify_model(q))
输出:
机器学习是人工智能(AI)的一个分支,它赋予计算机从数据中学习的能力,使其能够通过经验改进处理任务的能力。机器学习算法利用统计学方法,让计算机系统在不需要进行显式编程的情况下,通过识别数据中的模式和特征来自动改进其性能。它广泛应用于各种领域,包括语音识别、图像处理、医学诊断、推荐系统等。机器学习主要包括监督学习、非监督学习、半监督学习和强化学习等几种方法。
判断函数测试(询问大模型知识库不知道的问题)
q = '介绍一下北京今天的天气'
print(identify_model(q))
输出:
True
判断函数测试
q = '介绍一下DB-GPT项目?'
print(identify_model(q))
输出:
DB-GPT是一个开源项目,旨在通过结合大型语言模型(LLMs)和数据库技术,实现数据处理的自动化和优化。这个项目的核心是利用本地部署的大模型来与数据和环境进行交互,以此保护数据隐私和安全。DB-GPT支持在独立私有环境中部署,可以根据业务需求进行模块化部署,确保大模型的能力在私域内是绝对私有、安全和可控的。
该项目主要具有以下特点:
数据安全和隐私保护:通过本地化部署,避免了数据泄露的风险,确保用户数据的安全和隐私。
全流程自动化:整合了数据摄取、结构化和访问等多个流程,通过增强型大型语言模型来优化这些流程。
多模型框架:基于RAG框架设计,采用了多源RAG、自适应上下文学习等技术,提供了面向服务的多模型框架。
知识库构建与处理:原生支持知识库的构建和处理,通过langchain提供私域知识库问答能力。
插件模式:支持Auto-GPT等插件,提高了与数据交互的效率。
文本转SQL细化调整:包含文本转SQL任务的语言模型,能够更好地理解和处理自然语言查询。
多源知识整合:能够整合多源知识库,支持双语查询和生成数据分析。
DB-GPT的架构包括多个关键模块,例如用于知识构建、知识检索、自适应交互式编程和响应生成的模块,这些模块协同工作,以提高性能和效率。
项目的代码在GitHub上开源,地址是:https://github.com/eosphoros-ai/DB-GPT。该项目鼓励社区参与和贡献,共同推动数据库与大型语言模型结合的技术发展。
4、搜索词优化
用户提出的问题并不一定是最适合在知乎上进行搜索的关键词,例如用户提问“我想要知道“GPT-3.5微调总共需要几步”,此时应该将其转化为更加准确凝练的关键词并输入到知乎上进行搜索,方可提高搜索结果准确性。这里我们也完全可以让大模型自主将用户提的问题转化为适合在知乎上进行搜索的搜索关键词。当然,为达此目的,我们需要首先测试大模型是否知道知乎,以及大模型是否知道如何设计搜索关键词:
1)测试大模型是否知道知乎
response = client.chat.completions.create(
model="glm-4",
messages=[
{"role": "user", "content": "你知道什么是知乎么?"}
]
)
response.choices[0].message.content
输出:
'当然知道。知乎是中国领先的知识分享和社交平台,以问答的形式构建了一个信息分享、经验交流、意见互动的在线社区。它由北京智者天下科技有限公司运营,成立于2011年1月26日,其名称寓意在文言文中“知道吗”。\n\n知乎的目标是构建一个高质量的问答社区,让用户可以找到解决问题的答案,同时也能分享自己的知识与见解。随着时间的推移,知乎逐渐发展成为一个综合性内容平台,不仅提供问答服务,还涵盖了图文、音频、视频等多种内容形式。\n\n用户可以在知乎上提出问题,其他用户则可以回答问题或者对已有的回答进行评论。知乎的社区氛围以认真、专业和友善著称,吸引了大量专业人士和领域专家参与讨论。\n\n根据提供的信息,知乎在2019年完成了F轮融资,并推出了包括“盐选会员”在内的多项服务。到了2024年,知乎进一步拓展了其业务范围,不仅在社区建设上持续发力,还推出了结合AI技术的功能,如“发现·AI搜索”,以及“海盐计划6.0:航海家”和“灯塔计划”等项目,旨在通过技术赋能,提升用户体验,并推动平台持续进化。\n\n截至2020年12月,知乎拥有超过4400万条问题和2.4亿条回答,月活跃付费用户数超过250万,显示了其强大的用户基础和商业潜力。知乎不仅是一个问答社区,更是一个内容创作和知识分享的重要平台。'
2)咨询大模型设计关键词有什么方法
response = client.chat.completions.create(
model="glm-4",
messages=[
{"role": "user", "content": "我如果想在知乎上搜索一些问题的答案,在设计搜索关键词方面,有什么技巧么?"}
]
)
response.choices[0].message.content
输出:
'在设计搜索关键词以便在知乎上找到满意的问题答案时,以下是一些有用的技巧:\n\n1. **明确搜索意图**:\n - 在开始搜索前,明确你想要解决的问题或者你感兴趣的话题。\n - 确定你的搜索目标是寻找具体信息、教程、观点对比还是案例分析。\n\n2. **选择准确的关键词**:\n - 选择与问题主题密切相关的关键词。\n - 使用专业术语或行话,如果适用,以获取更精准的结果。\n - 考虑使用同义词或近义词,以扩大搜索范围。\n\n3. **组合关键词**:\n - 使用组合关键词来提高搜索的针对性。例如,如果你的问题是关于“数据分析工具”,可以组合成“数据分析工具比较”、“数据分析软件推荐”等。\n - 尝试不同的组合方式,如“最佳”、“如何”、“教程”、“区别”等,以找到不同角度的答案。\n\n4. **利用长尾关键词**:\n - 长尾关键词通常更具体,竞争也较小,可以帮助你找到更精确的信息。例如,“Python数据分析入门教程”就比单独的“Python”更具体。\n\n5. **避免过度堆砌关键词**:\n - 过度堆砌关键词可能导致搜索结果不准确,降低搜索效率。\n\n6. **利用知乎的搜索提示**:\n - 知乎的搜索栏通常会有相关搜索提示,可以参考这些提示来调整和优化你的关键词。\n\n7. **注意关键词的顺序**:\n - 有时关键词的顺序也会影响搜索结果,尝试不同的顺序来获得更准确的搜索结果。\n\n8. **检查拼写和语法**:\n - 确保关键词的拼写和语法正确,以免影响搜索效果。\n\n9. **使用高级搜索选项**:\n - 如果知乎支持高级搜索,利用这些功能,如时间筛选、用户筛选等,来进一步优化搜索结果。\n\n通过上述技巧,你可以设计出更有效的搜索关键词,提高在知乎上找到高质量答案的几率。'
3)咨询大模型怎么设计关键词
response = client.chat.completions.create(
model="glm-4",
messages=[
{"role": "user", "content": "例如,我想要知道“GPT-3.5微调总共需要几步”,\
现在围绕这个问题在知乎上进行搜索,应该怎样设计这个问题的搜索关键词呢?"}
]
)
response.choices[0].message.content
输出:
'若要在知乎上搜索关于“GPT-3.5微调总共需要几步”的信息,可以设计以下关键词进行搜索:\n\n1. GPT-3.5 微调流程\n2. GPT-3.5定制步骤\n3. GPT-3.5模型训练几步走\n4. GPT-3.5 Turbo 微调指南\n5. GPT-3.5自定义模型步骤\n6. GPT-3.5训练数据生成与微调流程\n7. GPT-3.5 RAG混合方法步骤\n\n以下是一些可能的搜索短语:\n\n- “GPT-3.5微调需要哪些步骤?”\n- “如何对GPT-3.5进行定制训练?”\n- “GPT-3.5从数据生成到微调的流程介绍”\n- “OpenAI GPT-3.5微调流程详解”\n- “GPT-3.5 Turbo训练与微调步骤”\n\n组合这些关键词和短语,可以更准确地找到关于GPT-3.5微调步骤的相关讨论和答案。在搜索时,也可以根据需要添加一些限定词,比如“2023”,“最新指南”等,以获取最新的信息。'
4)让大模型帮忙提取问题关键词
response = client.chat.completions.create(
model="glm-4",
messages=[
{"role": "system", "content": "你专门负责将用户的问题转化为知乎网站搜索关键词,只返回一个你认为最合适的搜索关键词即可"},
{"role": "user", "content": "请问,GPT-3.5微调总共分为几步?"},
{"role": "assistant", "content": "GPT-3.5微调流程"},
{"role": "user", "content": "请问ChatGPT企业版都有哪些功能?"}
]
)
response.choices[0].message.content
5)封装关键词设计的函数
def convert_keyword(q):
"""
将用户输入的问题转化为适合在知乎上进行搜索的关键词
"""
response = client.chat.completions.create(
model="glm-4",
messages=[
{"role": "system", "content": "你专门负责将用户的问题转化为知乎网站搜索关键词,只返回一个你认为最合适的搜索关键词即可"},
{"role": "user", "content": "请问,GPT-3.5微调总共分为几步?"},
{"role": "assistant", "content": "GPT-3.5微调流程"},
{"role": "user", "content": q}
]
)
q = response.choices[0].message.content
return q
6)基于关键词设计函数,智能助手函数
def get_answer(q):
"""
智能助手函数,当你无法回答某个问题时,调用该函数,能够获得答案
:param q: 必选参数,询问的问题,字符串类型对象
:return:某问题的答案,以字符串形式呈现
"""
# 调用转化函数,将用户的问题转化为更适合在知乎上进行搜索的关键词
q = convert_keyword(q)
# 默认搜索返回10个答案
print('正在接入谷歌搜索,查找和问题相关的答案...')
results = google_search(query=q, num_results=2, site_url='https://zhihu.com/')
# 创建对应问题的子文件夹
folder_path = './auto_search/%s' % q
if not os.path.exists(folder_path):
os.makedirs(folder_path)
# 单独提取links放在一个list中
print('正在读取搜索的到的相关答案...')
num_tokens = 0
content = ''
for item in results:
url = item['link']
title = get_search_text(q, url)
with open('./auto_search/%s/%s.json' % (q, title), 'r') as f:
jd = json.load(f)
num_tokens += jd[0]['tokens']
if num_tokens <= 12000:
content += jd[0]['content']
else:
break
print('正在进行最后的整理...')
return(content)
5、外部函数流程优化
而既然是依据“密钥”来判断是否调用搜索功能,那么该外部函数的调用就不能再使用此前定义的auto function calling功能了,而是需要我们手动来调用这个外部函数(即接收到密钥时一定要调用外部函数)
functions_list=[get_answer]
tools = auto_functions(functions_list)
tools
1)外部函数调用测试
response = client.chat.completions.create(
model="glm-4",
messages=[
{"role": "user", "content": "今天的北京天气如何?"},
],
tools=tools,
tool_choice={"type": "function", "function": {"name": "get_answer"}},
)
response.choices[0].message
2)优化前的大模型调用
def run_conversation(messages, functions_list=None, model="glm-4", function_call="auto"):
"""
能够自动执行外部函数调用的对话模型
:param messages: 必要参数,字典类型,输入到Chat模型的messages参数对象
:param functions_list: 可选参数,默认为None,可以设置为包含全部外部函数的列表对象
:param model: Chat模型,可选参数,默认模型为glm-4
:return:Chat模型输出结果
"""
# 如果没有外部函数库,则执行普通的对话任务
if functions_list == None:
response = client.chat.completions.create(
model=model,
messages=messages,
)
response_message = response.choices[0].message
final_response = response_message.content
# 若存在外部函数库,则需要灵活选取外部函数并进行回答
else:
# 创建functions对象
tools = auto_functions(functions_list)
# 创建外部函数库字典
available_functions = {func.__name__: func for func in functions_list}
# 第一次调用大模型
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
tool_choice=function_call, )
response_message = response.choices[0].message
tool_calls = response_message.tool_calls
if tool_calls:
#messages.append(response_message)
messages.append(response.choices[0].message.model_dump())
for tool_call in tool_calls:
function_name = tool_call.function.name
function_to_call = available_functions[function_name]
function_args = json.loads(tool_call.function.arguments)
## 真正执行外部函数的就是这儿的代码
function_response = function_to_call(**function_args)
messages.append(
{
"role": "tool",
"content": function_response,
"tool_call_id": tool_call.id,
}
)
## 第二次调用模型
second_response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools
)
# 获取最终结果
print(second_response.choices[0].message)
final_response = second_response.choices[0].message.content
else:
final_response = response_message.content
return final_response
3)优化后的智能机器人逻辑编写
def auto_search_answer(q):
# 调用判别模型
res = identify_model(q)
if res == True:
messages = [{"role": "user", "content": q}]
res =run_conversation(messages=messages,
functions_list=[get_answer],
model="glm-4",
function_call={"type": "function", "function": {"name": "get_answer"}})
return(res)
auto_search_answer("北京2024年5月1日的天气如何?")
auto_search_answer("什么是大模型?")
输出:
'大模型指的是参数规模极大、计算能力极强、能够处理非常复杂任务的人工智能模型。通常这些模型的参数量达到百亿、千亿甚至更多,例如GPT-3、天工3.0等。大模型具有卓越的理解能力、生成能力和泛化能力,能够应用于多种场景,如图像识别、自然语言处理、音乐生成等,为人工智能在各个领域的应用提供了强大的支持。随着算力的提升和算法的发展,大模型在推动人工智能技术进步方面发挥着越来越重要的作用。'
四、全流程代码总结
对整个智能搜索的代码进行全面的梳理和总结,为后续的其他技术平台的搜索提供清晰的指导。
1、大模型客户端创建
import os
import openai
from openai import OpenAI
import glob
import shutil
import numpy as np
import pandas as pd
import json
import io
import inspect
import requests
import re
import random
import string
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
import base64
from bs4 import BeautifulSoup
import dateutil.parser as parser
import tiktoken
from lxml import etree
## 初始化客户端
api_key = os.getenv("ZHIPUAI_API_KEY")
from zhipuai import ZhipuAI
client = ZhipuAI(api_key=api_key)
2、google搜索函数
def google_search(query, num_results=10, site_url=None):
api_key = os.getenv("GOOGLE_SEARCH_API_KEY")
cse_id = os.getenv("CSE_ID")
url = "https://www.googleapis.com/customsearch/v1"
# API 请求参数
if site_url == None:
params = {
'q': query,
'key': api_key,
'cx': cse_id,
'num': num_results
}
else:
params = {
'q': query,
'key': api_key,
'cx': cse_id,
'num': num_results,
'siteSearch': site_url
}
# 发送请求
response = requests.get(url, params=params)
response.raise_for_status()
# 解析响应
search_results = response.json().get('items', [])
# 提取所需信息
results = [{
'title': item['title'],
'link': item['link'],
'snippet': item['snippet']
} for item in search_results]
return results
3、创建文件名称函数
def windows_create_name(s, max_length=255):
"""
将字符串转化为符合Windows文件/文件夹命名规范的名称。
参数:
- s (str): 输入的字符串。
- max_length (int): 输出字符串的最大长度,默认为255。
返回:
- str: 一个可以安全用作Windows文件/文件夹名称的字符串。
"""
# Windows文件/文件夹名称中不允许的字符列表
forbidden_chars = ['<', '>', ':', '"', '/', '\\', '|', '?', '*']
# 使用下划线替换不允许的字符
for char in forbidden_chars:
s = s.replace(char, '_')
# 删除尾部的空格或点
s = s.rstrip(' .')
# 检查是否存在以下不允许被用于文档名称的关键词,如果有的话则替换为下划线
reserved_names = ["CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"]
if s.upper() in reserved_names:
s += '_'
# 如果字符串过长,进行截断
if len(s) > max_length:
s = s[:max_length]
return s
4、知乎内容查询函数
def get_search_text(q, url):
cookie = "q_c1=3c10baf5bd084b3cbfe7eece648ba243|1704976541000|1704976541000; _zap=086350b3-1588-49c4-9d6d-de88f3faae03; d_c0=AGCYfYvO_RePTvjfB1kZwPLeke_N5AM6nwo=|1704949678; _xsrf=qR1FJHlZ9dvYhhoj4SUj43SAIBUwPOqm; __snaker__id=wNWnamiJKBI0kzkI; q_c1=d44e397edb6740859a7d2a0d4155bfab|1706509753000|1706509753000; Hm_lvt_98beee57fd2ef70ccdd5ca52b9740c49=1706509695,1706589529,1706765465,1708650963; z_c0=2|1:0|10:1713167971|4:z_c0|80:MS4xOGRHQVNnQUFBQUFtQUFBQVlBSlZUYjZqOTJaLXVDSjdRSmJKMHgyVEhxTE13UGN1TUJBdHZnPT0=|15b2c2ece393ac4ea374d9b36cde5af7304f8ee7632e060fe6835bfadb5e4132; KLBRSID=9d75f80756f65c61b0a50d80b4ca9b13|1713170212|1713167958"
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
code_ = False
headers = {
'authority': 'www.zhihu.com',
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
'cache-control': 'max-age=0',
'cookie': cookie,
'upgrade-insecure-requests': '1',
'user-agent':user_agent,
}
# 普通问答地址
if 'zhihu.com/question' in url:
res = requests.get(url, headers=headers).text
res_xpath = etree.HTML(res)
title = res_xpath.xpath('//div/div[1]/div/h1/text()')[0]
text_d = res_xpath.xpath('//*[@id="root"]/div/main/div/div/div[3]/div[1]/div/div[2]/div/div/div/div[2]/span[1]/div/div/span/p/text()')
# 专栏地址
elif 'zhuanlan' in url:
headers['authority'] = 'zhaunlan.zhihu.com'
res = requests.get(url, headers=headers).text
res_xpath = etree.HTML(res)
title = res_xpath.xpath('//div[1]/div/main/div/article/header/h1/text()')[0]
text_d = res_xpath.xpath('//div/main/div/article/div[1]/div/div/div/p/text()')
code_ = res_xpath.xpath('//div/main/div/article/div[1]/div/div/div//pre/code/text()')
# 特定回答的问答网址
elif 'answer' in url:
res = requests.get(url, headers=headers).text
res_xpath = etree.HTML(res)
title = res_xpath.xpath('//div/div[1]/div/h1/text()')[0]
text_d = res_xpath.xpath('//div[1]/div/div[3]/div/div/div/div[2]/span[1]/div/div/span/p/text()')
# 创建问题答案正文
text = ''
for t in text_d:
txt = str(t).replace('\n', ' ')
text += txt
# 如果有code,则将code追加到正文的追后面
if code_:
for c in code_:
co = str(c).replace('\n', ' ')
text += co
encoding = tiktoken.encoding_for_model("gpt-3.5-turbo")
json_data = [
{
"link": url,
"title": title,
"content": text,
"tokens": len(encoding.encode(text))
}
]
with open('./auto_search/%s/%s.json' % (q, title), 'w') as f:
json.dump(json_data, f)
return title
5、大模型判断函数
import random
import string
def generate_random_key(length=30):
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(length))
def identify_model(q):
# 创建密钥
sk = generate_random_key()
# 调用模型进行判别
response = client.chat.completions.create(
model="glm-4",
messages=[
{"role": "system", "content": "你是一个用户问题判断器,专门用于判别你是否知道用户当前问题的答案。\
如果不知道,请回答“%s”,若知道,请正常回答" % sk},
{"role": "user", "content": "请问,GPT-3.5微调总共分为几步?"},
{"role": "assistant", "content": "%s" % sk},
{"role": "user", "content": q}
]
)
res = response.choices[0].message.content
if sk in res or '对不起' in res or '抱歉' in res or '超出知识库' in res:
return(True)
else:
return(res)
6、关键词获取函数
def convert_keyword(q):
"""
将用户输入的问题转化为适合在知乎上进行搜索的关键词
"""
response = client.chat.completions.create(
model="glm-4",
messages=[
{"role": "system", "content": "你专门负责将用户的问题转化为知乎网站搜索关键词,只返回一个你认为最合适的搜索关键词即可"},
{"role": "user", "content": "请问,GPT-3.5微调总共分为几步?"},
{"role": "assistant", "content": "GPT-3.5微调流程"},
{"role": "user", "content": q}
]
)
q = response.choices[0].message.content
return q
7、问题答案获取函数
def get_answer(q):
"""
智能助手函数,当你无法回答某个问题时,调用该函数,能够获得答案
:param q: 必选参数,询问的问题,字符串类型对象
:return:某问题的答案,以字符串形式呈现
"""
# 调用转化函数,将用户的问题转化为更适合在知乎上进行搜索的关键词
q = convert_keyword(q)
# 默认搜索返回10个答案
print('正在接入谷歌搜索,查找和问题相关的答案...')
results = google_search(query=q, num_results=2, site_url='https://zhihu.com/')
# 创建对应问题的子文件夹
folder_path = './auto_search/%s' % q
if not os.path.exists(folder_path):
os.makedirs(folder_path)
# 单独提取links放在一个list中
print('正在读取搜索的到的相关答案...')
num_tokens = 0
content = ''
for item in results:
url = item['link']
title = get_search_text(q, url)
with open('./auto_search/%s/%s.json' % (q, title), 'r') as f:
jd = json.load(f)
num_tokens += jd[0]['tokens']
if num_tokens <= 12000:
content += jd[0]['content']
else:
break
print('正在进行最后的整理...')
return(content)
8、funcation calling函数信息生成器
def auto_functions(functions_list):
"""
Chat模型的functions参数编写函数
:param functions_list: 包含一个或者多个函数对象的列表;
:return:满足Chat模型functions参数要求的functions对象
"""
def functions_generate(functions_list):
# 创建空列表,用于保存每个函数的描述字典
functions = []
# 对每个外部函数进行循环
for function in functions_list:
# 读取函数对象的函数说明
function_description = inspect.getdoc(function)
# 读取函数的函数名字符串
function_name = function.__name__
system_prompt = '以下是某的函数说明:%s,输出结果必须是一个JSON格式的字典,只输出这个字典即可,前后不需要任何前后修饰或说明的语句' % function_description
user_prompt = '根据这个函数的函数说明,请帮我创建一个JSON格式的字典,这个字典有如下5点要求:\
1.字典总共有三个键值对;\
2.第一个键值对的Key是字符串name,value是该函数的名字:%s,也是字符串;\
3.第二个键值对的Key是字符串description,value是该函数的函数的功能说明,也是字符串;\
4.第三个键值对的Key是字符串parameters,value是一个JSON Schema对象,用于说明该函数的参数输入规范。\
5.输出结果必须是一个JSON格式的字典,只输出这个字典即可,前后不需要任何前后修饰或说明的语句' % function_name
response = client.chat.completions.create(
model="glm-4",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
]
)
json_str=response.choices[0].message.content.replace("```json","").replace("```","")
json_function_description=json.loads(json_str)
json_str={"type": "function","function":json_function_description}
functions.append(json_str)
return functions
## 最大可以尝试4次
max_attempts = 4
attempts = 0
while attempts < max_attempts:
try:
functions = functions_generate(functions_list)
break # 如果代码成功执行,跳出循环
except Exception as e:
attempts += 1 # 增加尝试次数
print("发生错误:", e)
if attempts == max_attempts:
print("已达到最大尝试次数,程序终止。")
raise # 重新引发最后一个异常
else:
print("正在重新运行...")
return functions
9、大模型调用函数
def run_conversation(messages, functions_list=None, model="glm-4", function_call="auto"):
"""
能够自动执行外部函数调用的对话模型
:param messages: 必要参数,字典类型,输入到Chat模型的messages参数对象
:param functions_list: 可选参数,默认为None,可以设置为包含全部外部函数的列表对象
:param model: Chat模型,可选参数,默认模型为glm-4
:return:Chat模型输出结果
"""
# 如果没有外部函数库,则执行普通的对话任务
if functions_list == None:
response = client.chat.completions.create(
model=model,
messages=messages,
)
response_message = response.choices[0].message
final_response = response_message.content
# 若存在外部函数库,则需要灵活选取外部函数并进行回答
else:
# 创建functions对象
tools = auto_functions(functions_list)
# 创建外部函数库字典
available_functions = {func.__name__: func for func in functions_list}
# 第一次调用大模型
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
tool_choice=function_call, )
response_message = response.choices[0].message
tool_calls = response_message.tool_calls
if tool_calls:
#messages.append(response_message)
messages.append(response.choices[0].message.model_dump())
for tool_call in tool_calls:
function_name = tool_call.function.name
function_to_call = available_functions[function_name]
function_args = json.loads(tool_call.function.arguments)
## 真正执行外部函数的就是这儿的代码
function_response = function_to_call(**function_args)
messages.append(
{
"role": "tool",
"content": function_response,
"tool_call_id": tool_call.id,
}
)
## 第二次调用模型
second_response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools
)
# 获取最终结果
print(second_response.choices[0].message)
final_response = second_response.choices[0].message.content
else:
final_response = response_message.content
return final_response
10、最外层的结果查询函数
def auto_search_answer(q):
# 调用判别模型
res = identify_model(q)
if res == True:
messages = [{"role": "user", "content": q}]
res =run_conversation(messages=messages,
functions_list=[get_answer],
model="glm-4",
function_call={"type": "function", "function": {"name": "get_answer"}})
return(res)
结语
随着本章的结束,我们已经成功实现了知乎网站数据的智能搜索,并将其整合到了我们的智能化IT领域搜索引擎中。这一成果不仅提升了搜索引擎的数据覆盖范围和准确性,也为后续的开发工作奠定了坚实的基础。然而,我们仍需认识到,搜索引擎的优化和升级是一个持续不断的过程。因此,在接下来的工作中,下一篇章我们将完成Github网站搜索的接入。
🎯🔖更多专栏系列文章:AIGC-AI大模型探索之路
😎 作者介绍:我是寻道AI小兵,资深程序老猿,从业10年+、互联网系统架构师,目前专注于AIGC的探索。
📖 技术交流:建立有技术交流群,可以扫码👇 加入社群,500本各类编程书籍、AI教程、AI工具等你领取!
如果文章内容对您有所触动,别忘了点赞、⭐关注,收藏!加入我,让我们携手同行AI的探索之旅,一起开启智能时代的大门!