from elasticsearch import Elasticsearch, helpers
from nltk.stem import PorterStemmer
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
import nltk
import re
import warnings
# 屏蔽 ES 的一些 Warnings
warnings.simplefilter("ignore")
# 英文切词、词根、切句等方法
nltk.download('punkt')
nltk.download('stopwords')
defto_keywords(input_string):
'''(英文)文本只保留关键字'''
no_symbols = re.sub(, , input_string)
word_tokens = word_tokenize(no_symbols)
stop_words = (stopwords.words())
ps = PorterStemmer()
filtered_sentence = [ps.stem(w) w word_tokens w.lower() stop_words]
.join(filtered_sentence)
# 使用正则表达式替换所有非字母数字的字符为空格
r'[^a-zA-Z0-9\s]'
' '
# 加载停用词表
set
'english'
# 去停用词,取词根
for
in
if
not
in
return
' '
针对中文文本的处理实现
中文处理主要依赖分词工具(如 jieba),NLTK 对中文支持有限,建议结合 jieba 使用。
import re
import jieba
# 定义简单的中文停用词表,实际项目中可加载更完整的列表
STOP_WORDS_CN = {'的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '一个', '上', '也', '很', '到', '说', '要', '去', '你', '会', '着', '没有', '看', '好', '自己', '这'}
defto_keywords(input_string):
"""将句子转成检索关键词序列"""# 按搜索引擎模式分词
word_tokens = list(jieba.cut_for_search(input_string))
# 去除停用词
filtered_sentence = [w for w in word_tokens if w notin STOP_WORDS_CN andlen(w) > 1]
return' '.join(filtered_sentence)
defsent_tokenize(input_string):
"""按标点断句"""# 按标点切分
sentences = re.split(r'(?<=[。!?;?!])', input_string)
# 去掉空字符串return [sentence for sentence in sentences if sentence.strip()]
将文本灌入检索引擎
连接 Elasticsearch 并创建索引,将处理后的文本写入。
import os, time
# 引入配置文件
ELASTICSEARCH_BASE_URL = os.getenv('ELASTICSEARCH_BASE_URL', 'http://localhost:9200')
ELASTICSEARCH_PASSWORD = os.getenv('ELASTICSEARCH_PASSWORD', '')
ELASTICSEARCH_NAME= os.getenv('ELASTICSEARCH_NAME', 'elastic')
# 1. 创建 Elasticsearch 连接
es = Elasticsearch(
hosts=[ELASTICSEARCH_BASE_URL], # 服务地址与端口
http_auth=(ELASTICSEARCH_NAME, ELASTICSEARCH_PASSWORD), # 用户名,密码
verify_certs=False# 开发环境可关闭证书验证
)
# 2. 定义索引名称
index_name = "teacher_demo_index"# 3. 如果索引已存在,删除它(仅供演示,实际应用时不需要这步)if es.indices.exists(index=index_name):
es.indices.delete(index=index_name)
# 4. 创建索引(包含自定义映射以优化搜索)
es.indices.create(
index=index_name,
body={
"mappings": {
"properties": {
"keywords": {"type": "text", "analyzer": "ik_max_word"},
"text": {"type": "text"}
}
}
}
)
# 模拟段落数据
paragraphs = [
"Llama 2 is a collection of pretrained and fine-tuned large language models.",
"It ranges in scale from 7 billion to 70 billion parameters.",
"The model supports grouped-query attention and has increased context length."
]
# 5. 灌库指令
actions = [
{
"_index": index_name,
"_source": {
"keywords": to_keywords(para),
"text": para
}
}
for para in paragraphs
]
# 6. 文本灌库
helpers.bulk(es, actions)
# 灌库是异步的,等待刷新
time.sleep(2)
es.indices.refresh(index=index_name)
实现关键字检索
使用 ES 的 match 查询语言进行搜索。
defsearch(query_string, top_n=3):
# ES 的查询语言
search_query = {
"query": {
"multi_match": {
"query": to_keywords(query_string),
"fields": ["keywords"]
}
},
"size": top_n
}
res = es.search(index=index_name, query=search_query)
return [hit["_source"]["text"] for hit in res["hits"]["hits"]]
results = search("how many parameters does llama 2 have?", 2)
for r in results:
print(r + "\n")
===Prompt===
你是一个问答机器人。
你的任务是根据下述给定的已知信息回答用户问题。
已知信息:
1. Llama 2, an updated version of Llama 1, trained on a new mix of publicly available data...
2. In this work, we develop and release Llama 2, a collection of pretrained and fine-tuned large language models (LLMs) ranging in scale from 7 billion to 70 billion parameters...
用户问:
how many parameters does llama 2 have?
如果已知信息不包含用户问题的答案,或者已知信息不足以回答用户的问题,请直接回复"我无法回答您的问题"。
请不要输出已知信息中不包含的信息或答案。
请用中文回答用户问题。
===回复===
Llama 2 有 7B, 13B 和 70B 参数。
高级配置与优化建议
为了提升关键字检索的效果,除了基础的分词和索引外,还可以考虑以下优化策略:
1. 自定义 Analyzer
Elasticsearch 默认的分词器可能不适合特定业务场景。可以配置自定义分析器,例如针对中文使用 ik_max_word 或 smartcn,针对英文使用 standard 配合同义词过滤器。