基于langchain和文心一言的检索增强生成(RAG)初级实验
一、什么是RAG?
RAG的架构如图中所示,简单来讲,RAG就是通过检索获取相关的知识并将其融入Prompt,让大模型能够参考相应的知识从而给出合理回答。因此,可以将RAG的核心理解为“检索+生成”,前者主要是利用向量数据库的高效存储和检索能力,召回目标知识;后者则是利用大模型和Prompt工程,将召回的知识合理利用,生成目标答案。
RAG架构
完整的RAG应用流程主要包含两个阶段:
- 数据准备阶段:数据提取——>文本分割——>向量化(embedding)——>数据入库
- 应用阶段:用户提问——>数据检索(召回)——>注入Prompt——>LLM生成答案
技术细节和注意事项:
1、数据准备阶段:
数据准备一般是一个离线的过程,主要是将私域数据向量化后构建索引并存入数据库的过程。主要包括:数据提取、文本分割、向量化、数据入库等环节。
数据准备
- 数据提取
- 数据加载:包括多格式数据加载、不同数据源获取等,根据数据自身情况,将数据处理为同一个范式。
- 数据处理:包括数据过滤、压缩、格式化等。
- 元数据获取:提取数据中关键信息,例如文件名、Title、时间等 。
- 文本分割:
文本分割主要考虑两个因素:1)embedding模型的Tokens限制情况;2)语义完整性对整体的检索效果的影响。一些常见的文本分割方式如下:- 句分割:以”句”的粒度进行切分,保留一个句子的完整语义。常见切分符包括:句号、感叹号、问号、换行符等。
- 固定长度分割:根据embedding模型的token长度限制,将文本分割为固定长度(例如256/512个tokens),这种切分方式会损失很多语义信息,一般通过在头尾增加一定冗余量来缓解。
- 向量化(embedding):
向量化是一个将文本数据转化为向量矩阵的过程,该过程会直接影响到后续检索的效果。目前常见的embedding模型如表中所示,这些embedding模型基本能满足大部分需求,但对于特殊场景(例如涉及一些罕见专有词或字等)或者想进一步优化效果,则可以选择开源Embedding模型微调或直接训练适合自己场景的Embedding模型。
模型名称
描述
获取地址
ChatGPT-Embedding
ChatGPT-Embedding由OpenAI公司提供,以接口形式调用。
https://platform.openai.com/docs/guides/embeddings/what-are-embeddings
ERNIE-Embedding V1
ERNIE-Embedding V1由百度公司提供,依赖于文心大模型能力,以接口形式调用。
https://cloud.baidu.com/doc/WENXINWORKSHOP/s/alj562vvu
M3E
M3E是一款功能强大的开源Embedding模型,包含m3e-small、m3e-base、m3e-large等多个版本,支持微调和本地部署。
https://huggingface.co/moka-ai/m3e-base
BGE
BGE由北京智源人工智能研究院发布,同样是一款功能强大的开源Embedding模型,包含了支持中文和英文的多个版本,同样支持微调和本地部署。
https://huggingface.co/BAAI/bge-base-en-v1.5
- 数据入库:
数据向量化后构建索引,并写入数据库的过程可以概述为数据入库过程,适用于RAG场景的数据库包括:FAISS、Chromadb、ES、milvus等。一般可以根据业务场景、硬件、性能需求等多因素综合考虑,选择合适的数据库。
2、应用阶段:
在应用阶段,我们根据用户的提问,通过高效的检索方法,召回与提问最相关的知识,并融入Prompt;大模型参考当前提问和相关知识,生成相应的答案。关键环节包括:数据检索、注入Prompt等。
数据检索
- 数据检索
常见的数据检索方法包括:相似性检索、全文检索等,根据检索效果,一般可以选择多种检索方式融合,提升召回率。
- 相似性检索:即计算查询向量与所有存储向量的相似性得分,返回得分高的记录。常见的相似性计算方法包括:余弦相似性、欧氏距离、曼哈顿距离等。
- 全文检索:全文检索是一种比较经典的检索方式,在数据存入时,通过关键词构建倒排索引;在检索时,通过关键词进行全文检索,找到对应的记录。
- 注入Prompt
LLM生成
Prompt作为大模型的直接输入,是影响模型输出准确率的关键因素之一。在RAG场景中,Prompt一般包括任务描述、背景知识(检索得到)、任务指令(一般是用户提问)等,根据任务场景和大模型性能,也可以在Prompt中适当加入其他指令优化大模型的输出。一个简单知识问答场景的Prompt如下所示:
【任务描述】
假如你是一个专业的客服机器人,请参考【背景知识】,回
【背景知识】
{content} // 数据检索得到的相关文本
【问题】
石头扫地机器人P10的续航时间是多久?
Prompt的设计只有方法、没有语法,比较依赖于个人经验,在实际应用过程中,往往需要根据大模型的实际输出进行针对性的Prompt调优。
二、跑通一个例子
在环境已经安装、embeded模型和文心正确部署的情况下,编写代码。这里待处理的对象是小学课文《观潮》
钱塘江大潮,自古以来被称为天下奇观。
农历八月十八是一年中传统的观潮日。这一天早上,我们来到了海宁市的盐官镇,据说这里是观潮最好的地方。我们随着观潮的人群,登上了海塘大堤。宽阔的钱塘江横卧在眼前。江面很平静,越往东越宽,在雨后的阳光下,笼罩着一层蒙蒙的薄雾。镇海古塔、中山亭和观潮台屹立在江边。远处,几座小山在云雾中若隐若现。江潮还没有来,海塘大堤上早已人山人海。大家昂首东望,等着,盼着。
午后一点左右,从远处传来隆隆的响声,好像闷雷滚动。顿时人声鼎沸,有人告诉我们,潮来了!我们踮着脚往东望去,江面还是风平浪静,看不出有什么变化。过了一会儿,响声越来越大,只见东边水天相接的地方出现了一条白线,人群又沸腾起来。
那条白线很快地向我们移来,逐渐拉长,变粗,横贯江面。再近些,只见白浪翻滚,形成一堵两丈多高的水墙。浪潮越来越近,犹如千万匹白色战马齐头并进,浩浩荡荡地飞奔而来;那声音如同山崩地裂,好像大地都被震得颤动起来。
霎时,潮头奔腾西去,可是余波还在漫天卷地般涌来,江面上依旧风号浪吼。过了好久,钱塘江才恢复了平静。看看堤下,江水已经涨了两丈来高了。
from langchain import LLMChain from langchain.document_loaders import TextLoader from langchain.text_splitter import CharacterTextSplitter from langchain.embeddings import HuggingFaceBgeEmbeddings from langchain.vectorstores import Chroma from langchain.prompts import PromptTemplate from langchain.memory import ConversationBufferMemory from langchain.chains import ConversationalRetrievalChain from langchain.prompts.chat import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate from langchain_wenxin.llms import Wenxin #读取领域信息 loader = TextLoader("./knowledge/观潮.txt") documents = loader.load() #文本分割 text_splitter = CharacterTextSplitter(chunk_size=128, chunk_overlap=0) documents = text_splitter.split_documents(documents) # 向量化 embedding model: m3e-base model_name = "./m3e-base" model_kwargs = {'device': 'cpu'} encode_kwargs = {'normalize_embeddings': True} embedding = HuggingFaceBgeEmbeddings( model_name=model_name, model_kwargs=model_kwargs, encode_kwargs=encode_kwargs, query_instruction="为文本生成向量表示用于文本检索" ) # load data to Chroma db db = Chroma.from_documents(documents, embedding) # LLM选型 llm = Wenxin(model="ernie-bot", baidu_api_key= baidu_api_key, baidu_secret_key= baidu_secret_key) retriever = db.as_retriever() memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True) qa = ConversationalRetrievalChain.from_llm(llm, retriever, memory=memory) ret = qa({"question": "什么时间去哪里观潮比较合适?"}) print(ret)
返回结果:
{'question': '什么时间去哪里观潮比较合适?',
'chat_history': [HumanMessage(content='什么时间去哪里观潮比较合适?'), AIMessage(content='根据原文信息得出,农历八月十八去海宁市的盐官镇观潮比较合适。')], 'answer': '根据原文信息得出,农历八月十八去海宁市的盐官镇观潮比较合适。'}
再提问:
{'question': '钱塘江大潮分为那几个过程?',
'chat_history': [HumanMessage(content='钱塘江大潮分为那几个过程?'), AIMessage(content='钱塘江大潮分为三个过程:
1. 远处传来闷雷滚动的声音,人群沸腾,但江面依旧平静;
2. 东边水天相接的地方出现一条白线,白线逐渐拉长、变粗、横贯江面,形成一堵两丈多高的水墙,浩浩荡荡地飞奔而来;
3. 潮头奔腾西去,余波涌来,江面上依旧风号浪吼,过了好久,钱塘江才恢复了平静。')],
'answer': '钱塘江大潮分为三个过程:\n1. 远处传来闷雷滚动的声音,人群沸腾,但江面依旧平静;\n2. 东边水天相接的地方出现一条白线,白线逐渐拉长、变粗、横贯江面,形成一堵两丈多高的水墙,浩浩荡荡地飞奔而来;\n3. 潮头奔腾西去,余波涌来,江面上依旧风号浪吼,过了好久,钱塘江才恢复了平静。'}
这里的对原文的理解和解析,还是很到位的。
三、对民法典进行分析
法典是规范性文件,但是一千多条很专业,如果能够使用RAG来加快理解,应该是有帮助的:
'question': '处理民事纠纷应该注意哪些要点?'
'answer': '处理民事纠纷应该注意以下要点:
1. 当事人的姓名或者名称和住所。
2. 标的。
3. 数量。
4. 质量。
5. 价款或者报酬。
6. 履行期限、地点和方式。
7. 违约责任。
8. 解决争议的方法。
9. 内容与公序良俗的关联性。
10. 受害人名誉受贬损的可能性。
11. 核实能力和核实成本。
'question': '签订合同的时候需要注意哪些?'
AIMessage(content='在签订合同时,应该注意以下方面:
1. 确保合同内容具体确定,包括双方的权利和义务、交易条件等。
2. 如果采用格式条款订立合同,提供格式条款的一方应当遵循公平原则确定当事人之间的权利和义务,并采取合理的方式提示对方注意免除或者减轻其责任等与对方有重大利害关系的条款,按照对方的要求,对该条款予以说明。如果提供格式条款的一方未履行提示或者说明义务,致使对方没有注意或者理解与其有重大利害关系的条款的,对方可以主张该条款不成为合同的内容。
3. 合同成立地点:采用数据电文形式订立合同的,收件人的主营业地为合同成立的地点;没有主营业地的,其住所地为合同成立的地点。当事人另有约定的,按照其约定。
4. 合同组成部分:与履行合同有关的技术背景资料、可行性论证和技术评价报告、项目任务书和计划书、技术标准、技术规范、原始设计和工艺文件,以及其他技术文档,按照当事人的约定可以作为合同的组成部分。')],
question='法人和自然人有哪些区别?'),
AIMessage(content='法人和自然人的区别主要有以下几点:
1. 法律地位:法人是一个组织,具有法律上的独立人格,可以独立享有权利和承担义务。而自然人是指有生命的个人,是权利和义务的主体。
2. 成立条件和程序:法人必须依法成立,具备相应的条件和程序,如名称、组织机构、住所、财产或经费等。而自然人无需经过任何法定程序即可成为权利主体。
3. 民事权利和义务:法人和自然人在民事权利和义务方面有所不同。法人主要享有名称权、名誉权和荣誉权等权利,而自然人则享有生命权、身体权、健康权、姓名权、肖像权、名誉权、荣誉权、隐私权、婚姻自主权等权利。此外,法人的民事权利能力和民事行为能力从法人成立时产生,到法人终止时消灭,而自然人的民事权利能力和民事行为能力则伴随其一生。\n4. 财产责任:法人以其全部财产独立承担民事责任,而自然人的财产责任则根据具体情况而定。')],
四、对新闻进行分析
鉴于大模型的知识库中可能存在历史的信息,所以用最新的新闻来进行以上流程。这里我特地在提问的时候写了错别字,应该被文心看出来了。
{'question': '如何评价此次恐怖注意袭击?', 'chat_history': [HumanMessage(content='如何评价此次恐怖注意袭击?'),
AIMessage(content='此次恐怖袭击是一起非常严重的暴力事件,造成了大量的人员伤亡和财产损失。这种行为违反了国际法和人道主义原则,是绝对不能容忍的。
从国际社会的反应来看,欧盟、土耳其等国家和地区都对这次袭击表示强烈谴责,并表达了对遇难者家属的慰问和对伤者的祝福。这种团结和同情的态度表明,国际社会对于恐怖主义行为的零容忍态度是十分明确的。
对于此次袭击的影响,它不仅给伊朗人民带来了巨大的悲痛和损失,也给中东地区的局势带来了更多的不稳定和不确定性。这样的暴力事件往往会引发更多的仇恨和报复,进一步加剧地区的紧张局势。
因此,对于国际社会来说,需要加强反恐合作,采取更加有效的措施来打击恐怖主义势力,维护世界和平与安全。同时,也需要加强对于恐怖主义根源的研究和分析,从源头上解决恐怖主义问题。'
另可以看原文:
中国专家分析伊朗爆炸袭击:手法“十分特殊”
【环球时报-环球网报道 记者 白云怡 赵觉珵】当地时间1月3日,伊朗东南部城市克尔曼接连发生两起爆炸事件。据伊朗媒体援引伊朗克尔曼省省长最新消息,爆炸造成84人遇难、284人受伤。值得注意的是,此次爆炸不仅正值巴以冲突为地区动荡带来升级风险,发生日期也是美国空袭伊拉克造成伊朗高级指挥官苏莱曼尼身亡四周年,爆炸就发生在通往苏莱曼尼墓地的路上。谁可能是这场发生在特殊时间与背景下的血腥事件的“制造者”?该事件又会为本就不平静的中东局势造成哪些影响?
《环球时报》记者4日就此采访多名中东事务专家。他们分析认为,此次爆炸的实施手法相当特殊,既不是以色列、美国的惯常手法,也不像“伊斯兰国”等极端势力所为,判断谁是幕后肇事者为时尚早。但在中东动荡局势已有蔓延之势的大背景下,类似此次连环爆炸案一样的“黑天鹅”事件接二连三发生,势必将刺激有关国家的安全神经,并将对地区局势产生较大冲击,使本已陷入紧张的地区关系更加脆弱敏感。
据伊朗国家电视台报道,两起爆炸发生时,位于克尔曼的苏莱曼尼墓地附近正在举行纪念活动,因此爆炸导致大量参与纪念活动的民众伤亡。现场画面显示,惊慌的民众慌忙逃离爆炸现场,鲜血淋漓的受害者躺在地上。据法新社报道,3日的爆炸事件是伊朗自1979年后发生的遇难人数最多的事件。
伊朗内政部长瓦希迪称,3日的第一次爆炸发生在当地时间下午3时。20分钟后,发生了第二次更致命的爆炸。本次连环爆炸中的死伤者大部分是在第二次爆炸中死亡或受伤。“两个装有炸弹的袋子在现场爆炸。”伊朗塔斯尼姆通讯社援引知情人士的话报道称,“这次事件的肇事者……显然是用遥控器引爆了炸弹。”
截至目前,尚未有组织或个人宣布对此次事件负责。最初,伊朗媒体援引负责政治和安全的当地官员的话称,目前尚不清楚“事件是由瓦斯爆炸还是恐怖袭击引起的”。但伊朗媒体随后称,克尔曼省副省长拉赫曼·贾拉利认为,“这是一起恐怖袭击事件。”
上海外国语大学中东研究所助理研究员文少彪4日对《环球时报》分析认为,此次爆炸时间和地点明显系刻意挑选。2020年1月3日,苏莱曼尼在伊拉克巴格达遭美军空袭身亡,其墓地被认为已成为当地反美情绪的象征性地点之一。在此地发动袭击,会产生较大的政治联想。
美联社分析称,伊朗有多个敌人可能是这次袭击的幕后黑手,包括流亡组织、激进组织和国家行为者。也有舆论分析认为,从这次袭击所表现出来的渗透能力上看,有可能是以色列所为。
兰州大学“一带一路”研究中心特聘教授王晋4日对《环球时报》记者分析认为,从目前披露的信息来看,此次爆炸事件的手法“十分特殊”。他解释称,从以往事件来看,以色列的袭击目标通常针对伊朗的高层,尤其是与核设施相关的科研人员或行政人员,手法以暗杀为主;而美国袭击的目标主要是伊朗的政治军事高层,行动主要发生在伊朗境外,避免和伊朗发生直接冲突;以“伊斯兰国”为代表的极端组织常发动针对平民的恐怖袭击,但手段以自杀式袭击为主。此次克尔曼市的连环爆炸采用的是“远程炸弹”,也不像是“伊斯兰国”的手法。
“此次爆炸事件与几个主要相关势力的手法都不太像”,他分析认为,这意味着判断谁是幕后肇事者为时尚早,是否和巴以局势相关也尚难给出定论。但从结果来看,此次事件将必然使包括巴以局势在内的地区局势更加复杂、紧张。
爆炸案发生后,伊朗总统易卜拉欣·莱希发表声明,他没有明确指出谁是爆炸事件的责任方,但表示“参与这一恐怖罪行的肇事者和罪犯将很快被查明,并会为他们的行为受到惩罚” 。不过,一些伊朗官员已经将此次爆炸事件归咎于美国和以色列,如伊朗“圣城旅”指挥官卡尼发表讲话,指责以色列和美国代理人在克尔曼制造了恐怖爆炸事件。
据美国《国会山报》报道,美国国务院发言人马修·米勒3日在新闻发布会上称,美国没有参与伊朗爆炸事件,也“没有理由相信以色列参与其中”。
克尔曼市的爆炸事件正值巴以冲突影响不断外溢、中东地区多处陷入动荡之际,突发事件层出不穷:2023年12月25日,在叙利亚担任军事顾问的一名伊朗高级指挥官在以色列对叙首都大马士革的空袭中丧生;今年1月2日,哈马斯高级领导人萨利赫·阿鲁里在黎巴嫩首都贝鲁特郊区的一次爆炸中丧生。美国有线电视新闻网(CNN)援引一名美国官员的话称,这是由以色列实施的袭击。
另一方面,红海危机也在持续发酵,美国海军近日击沉3艘也门胡塞武装小艇,而声援巴勒斯坦的胡塞武装则对以色列发起跨境袭击,并利用临近红海水域的便利袭击“关联以色列”的商船,红海局势与巴以冲突形成联动。
王晋对《环球时报》记者表示,克尔曼爆炸势必将对伊朗的安全神经产生较大刺激,也将为本就紧张的地区局势带来进一步冲击。他分析称,巴以冲突已有蔓延升级趋势,更多方面的力量正在下场,此时“黑天鹅”事件的发生,将使地区国家之间、不同阵营之间的关系更加敏感、脆弱。
“克尔曼爆炸加上哈马斯高级领导人的身亡,可能对地区局势造成叠加效应。”法新社分析认为,在以色列北部靠近黎巴嫩边境的地区,很多居民都担心未来将有更大规模的冲突,以至于许多人都随身带着武器。
文少彪分析认为,此次事件后,伊朗或将加强其内部政治动员,有可能利用其地区代理人发动矛头指向美国和以色列的“报复性行动”,包括支持黎巴嫩真主党、也门胡塞武装、伊拉克“人民动员力量”等组织继续袭扰以色列。在此背景下,美国在叙利亚的军事基地可能受到打击,红海危机也可能继续加剧。
“和平解决本轮巴以冲突的希望看起来更加渺茫。”英国《卫报》评论称,无论谁是此次爆炸事件的幕后黑手,显然他都甘冒引发地区战争的风险。在当前的局势下,各方发生误判的可能性不断升高。其中一个高度不稳定的因素是以色列内政:美国正在为本轮巴以冲突寻找可能的外交解决方案,但当方案涉及加沙和黎巴嫩,以色列总理内塔尼亚胡就难以克服以国内政治压力。
“这起连环爆炸,将进一步加剧更广泛地区的紧张局势。”卡塔尔半岛电视台称,在中东地区紧张局势升级几天之后,“战争的乌云”正在积聚。“现在这个地区什么都可能发生。”
据媒体报道,爆炸发生后,联合国、欧盟、俄罗斯、土耳其,以及阿拉伯国家纷纷对肇事者予以谴责。联合国秘书长古特雷斯通过发言人发表声明,强烈谴责3日发生在伊朗克尔曼市的袭击事件。他向遇难者家属,以及伊朗人民和政府表示深切慰问,并祝愿伤者能够早日康复。古特雷斯呼吁,必须追究袭击实施者的责任。
欧盟则在一份官方声明中表示:“欧盟以最强烈的措辞谴责今天的爆炸事件……这一恐怖行为造成了令人震惊的平民伤亡。肇事者必须承担责任。”欧盟外交与安全政策高级代表博雷利在与伊朗外长的通话中强烈谴责了这一恐怖主义行为,并向遇难者家属以及伊朗政府和人民表示哀悼,祝愿伤者早日康复。
土耳其总统埃尔多安在X社交平台表示,他就这一“令人发指的恐怖袭击”向“友好和兄弟”的伊朗人民表示哀悼。他写道:“我们对伊朗克尔曼发生的令人发指的恐怖袭击深感悲痛。”
五、表格数据分析
随机生成100条简化版的列车时刻表信息。每条信息包括车次和出发时间。使用RAG进行问询:
1. 车次:G12345,出发时间:08:00
2. 车次:D67890,出发时间:09:30
3. 车次:K12345,出发时间:10:00
4. 车次:T67890,出发时间:11:30
5. 车次:G12346,出发时间:12:00
6. 车次:D67891,出发时间:13:30
7. 车次:K12346,出发时间:14:00
8. 车次:T67891,出发时间:15:30
9. 车次:G12347,出发时间:16:00
10. 车次:D67892,出发时间:17:30
11. 车次:K12347,出发时间:18:00
12. 车次:T67892,出发时间:19:30
13. 车次:G12348,出发时间:20:00
14. 车次:D67893,出发时间:21:30
15. 车次:K12348,出发时间:22:00
16. 车次:T67893,出发时间:23:30
17. 车次:G12349,出发时间:00:00
18. 车次:D67894,出发时间:01:30
19. 车次:K12349,出发时间:02:00
20. 车次:T67894,出发时间:03:30
21. 车次:G12350,出发时间:04:00
22. 车次:D67895,出发时间:05:30
23. 车次:K12350,出发时间:06:00
24. 车次:T67895,出发时间:07:30
25. 车次:G12351,出发时间:08:00
26. 车次:D67896,出发时间:09:30
27. 车次:K12351,出发时间:10:00
28. 车次:T67896,出发时间:11:30
29. 车次:G12352,出发时间:12:00
30. 车次:D67897,出发时间:13:30
31. 车次:K12352,出发时间:14:00
32. 车次:T67897,出发时间:15:30
33. 车次:G12353,出发时间:16:00
34. 车次:D67898,出发时间:17:30
35. 车次:K12353,出发时间:18:00
36. 车次:T67898,出发时间:19:30
37. 车次:G12354,出发时间:20:00
38. 车次:D67899,出发时间:21:30
39. 车次:K12354,出发时间:22:00
40. 车次:T67899,出发时间:23:30
41. 车次:G12355,出发时间:00:00
42. 车次:D67900,出发时间:01:30
43. 车次:K12355,出发时间:02:00
44. 车次:T67900,出发时间:03:30
45. 车次:G12356,出发时间:04:00
46. 车次:D67901,出发时间:05:30
47. 车次:K12356,出发时间:06:00
48. 车次:T67901,出发时间:07:30
49. 车次:G12357,出发时间:08:00
50. 车次:D67902,出发时间:09:30
51. 车次:K12357,出发时间:10:00
52. 车次:T67902,出发时间:11:30
53. 车次:G12358,出发时间:12:00
54. 车次:D67903,出发时间:13:30
55. 车次:K12358,出发时间:14:00
56. 车次:T67903,出发时间:15:30
57. 车次:G12359,出发时间:16:00
58. 车次:D67904,出发时间:17:30
59. 车次:K12359,出发时间:18:00
60. 车次:T67904,出发时间:19:30
61. 车次:G12360,出发时间:20:00
62. 车次:D67905,出发时间:21:30
63. 车次:K12360,出发时间:22:00
64. 车次:T67905,出发时间:23:30
65. 车次:G12361,出发时间:00:00
66. 车次:D67906,出发时间:01:30
67. 车次:K12361,出发时间:
提问:出发最早的车是哪个?
RAG回答: 出发最早的车是车次G12345,出发时间是08:00。【这个答案是错误的】
提问:我下午2点出发,最好坐那班车?
RAG回答: 要确定下午2点哪个车次最适合你乘坐,需要找到所有出发时间在2点之后的车次,然后从中选择最适合的车次。以下是符合要求的所有车次:
1. 车次:G12351,出发时间:14:00
2. 车次:K12352,出发时间:14:00
3. 车次:T67897,出发时间:15:30
4. 车次:G12353,出发时间:16:00
5. 车次:K12353,出发时间:16:00
6. 车次:T67898,出发时间:17:30
7. 车次:G12354,出发时间:18:00
8. 车次:K12354,出发时间:18:00
9. 车次:T67899,出发时间:19:30
10. 车次:G12355,出发时间:20:00
11. 车次:D67903,出发时间:20:00
12. 车次:K12355,出发时间:20:00
13. 车次:T67903,出发时间:21:30
14. 车次:G12356,出发时间:22:00
15. 车次:D67904,出发时间:22:00
16. 车次:K12356,出发时间:22:00
17. 车次:T67904,出发时间:23:30
在这些车次中,你可以选择出发时间最早的车次G12351。
提问:有哪些以K开头的车?
RAG回答: 以K开头的车次有:
1. K12345,出发时间:10:00
2. K12346,出发时间:14:00
3. K12347,出发时间:18:00
4. K12348,出发时间:22:00
5. K12349,出发时间:02:00
6. K12350,出发时间:06:00
7. K12351,出发时间:10:00
8. K12352,出发时间:14:00
9. K12353,出发时间:18:00
10. K12354,出发时间:22:00
11. K12355,出发时间:02:00
12. K12356,出发时间:06:00
13. K12357,出发时间:10:00
14. K12358,出发时间:14:00
15. K12359,出发时间:18:00
16. K12360,出发时间:22:00
17. K12361,出发时间: