来源:互联网 更新时间:2026-05-30 21:06
在做RAG知识增强或者微调模型的时候,很多团队都会碰到一个共同的问题:直接拿大段文本往里灌,效果往往不尽如人意。相比之下,结构化的问答对要高效得多——无论是存入向量数据库,还是作为微调的训练语料。
那么问题来了,怎么从杂乱的长文本里,批量地、自动化地生成这些问答对?尤其麻烦的是,这些问答对必须是结构化的,能存进一个list里,每个元素都是格式统一的dict。这篇文章就基于Agently框架,给出了一条非常直接的实现路径。
![[Agently Show Case]长文本生成问答对](http://www.haoling.cc/uploadfile/2026/0530/a5bd092f9451d7246d31e7a2700dffec.webp)
先看整体流程。数据流大致是:把一份文档(本地或线上)切分成若干文本块,然后对每一块调用大模型生成问答对,最后把结果拼接起来。整个过程的控制精度很高——文本块大小可以自定义,请求间隔可控,输出的数据结构也是预先定义好的。
下面是完整的代码样例。注意,这里的模型建议使用有16k或更大上下文窗口的版本,这样在处理长段落时不容易丢信息。
import Agently
import requests
import time
# Model Settings
agent_factory = Agently.AgentFactory()
.set_settings("model.OpenAI.auth", { "api_key": "" })
.set_settings("model.OpenAI.options", { "model": "gpt-3.5-turbo-16k" })
# recommend using 16k or larger context model for this kind of tasks
# Download document
document_link = "https://raw.githubusercontent.com/Maplemx/Agently/main/README.md"
document_content = ""
response = requests.get(document_link)
if response.status_code == 200:
document_content = response.content.decode("utf-8")
# Work Settings
piece_length_control = 1000
sleep_time = 5 # sleep for a while in case of reaching API request limit
# Chop document
chunks = document_content.split("
")
paragraphs = []
paragraph_num = -1
for chunk in chunks:
if chunk.startswith("#"):
paragraphs.append(chunk + "
")
paragraph_num += 1
else:
paragraphs[paragraph_num] += chunk + "
"
text_pieces = []
text_piece_num = 0
for paragraph in paragraphs:
if len(text_pieces) == 0:
text_pieces.append(paragraph)
else:
if len(text_pieces[text_piece_num] + paragraph) > piece_length_control:
text_pieces.append(paragraph)
text_piece_num += 1
else:
text_pieces[text_piece_num] += paragraph
# Generate QA Pairs
qa_pairs = []
agent = agent_factory.create_agent()
for text_piece in text_pieces:
print("[Working on]: ", text_piece.split("
")[0])
result = agent
.input({"text": text_piece })
.instruct("Generate at least 5 question and answer pairs about {text}")
.output([{
"question": ("String", "Question you may ask about {text}"),
"answer": ("String", "Your answer to {question} according {text}"),
}])
.start()
qa_pairs.append({
"origin_piece": text_piece,
"qa_pairs": result,
})
print("[Done] Start next work in " + str(sleep_time) + " seconds.")
time.sleep(sleep_time)
print("[All Works Done]
")
# Print QA Paris
for item in qa_pairs:
print("[Origin Text Piece]:
", item["origin_piece"], end="
")
for qa in item["qa_pairs"]:
print("Question: ", qa["question"])
print("Answer: ", qa["answer"], end="
")
print("------")
代码一次性跑完,输出的是结构化数据,每个问答对都绑定了原始文本块。后面无论是存数据库还是做进一步分析,都非常方便。
文本来源无非两种:本地文档和在线文档。
本地文档最简单,Python自带的open()函数直接搞定。如果是PDF格式,用PyPDF2库先提取文本,再走同样流程:
import PyPDF2
document_piece = []
with open("./README.pdf", "rb") as file:
reader = PyPDF2.PdfReader(file)
for page in reader.pages:
document_piece.append(page.extract_text())
在线文档则需要用requests库下载。如果你是从GitHub上取文件,注意URL格式:https://raw.githubusercontent.com/<账户名>/<项目名>/<分支名>/<文件名>。直接用这个地址请求,就能拿到raw内容。
import requests
document_link = "https://raw.githubusercontent.com/Maplemx/Agently/main/README.md"
document_content = ""
response = requests.get(document_link)
if response.status_code == 200:
document_content = response.content.decode("utf-8")
切块是整条流水线的核心技术点。切得太碎,句子可能断裂,语义不完整;切得太大,又容易超出模型上下文窗口。
本例处理的是markdown文档,所以巧用了markdown的标题结构。具体分两步:
第一步,用
做初始拆行,然后逐行遍历。每遇到以#开头的行,就新建一个段落;否则,把当前行追加到上一个段落末尾。这样能保证每一段都是一个相对完整的语义单元。
chunks = document_content.split("
")
paragraphs = []
paragraph_num = -1
for chunk in chunks:
if chunk.startswith("#"):
paragraphs.append(chunk + "
")
paragraph_num += 1
else:
paragraphs[paragraph_num] += chunk + "
"
第二步,把这些段落再尽量拼装起来,只要拼装后的总长度不超过piece_length_control(本例设为1000字符)就继续拼。这样既能减少对大模型的调用次数,又能保证每个文本块的完整性。
text_pieces = []
text_piece_num = 0
for paragraph in paragraphs:
if len(text_pieces) == 0:
text_pieces.append(paragraph)
else:
if len(text_pieces[text_piece_num] + paragraph) > piece_length_control:
text_pieces.append(paragraph)
text_piece_num += 1
else:
text_pieces[text_piece_num] += paragraph
这个策略的核心在于:让切分方法匹配文档的结构。如果是HTML文档,可以用
这部分正是Agently框架的强项——用工程化的语言,把复杂的大模型调用包装成直观的Python操作。
.input()中传入{ "text": text_piece },就像给函数传参一样自然。.output()中声明想要的JSON结构。比如本例中,每个问答对就是一个dict,包含question和answer两字段。每个字段还可以通过("String", "描述")的方式,进一步告诉模型这个字段的用途和格式要求。.instruct()中加入“对每一段文本至少生成5个问答对”这样的指导。你甚至可以传入一个list,里面写上“output language: Chinese”,看看效果有什么不同。qa_pairs = []
agent = agent_factory.create_agent()
for text_piece in text_pieces:
print("[Working on]: ", text_piece.split("
")[0])
result = agent
.input({"text": text_piece })
.instruct("Generate at least 5 question and answer pairs about {text}")
.output([{
"question": ("String", "Question you may ask about {text}"),
"answer": ("String", "Your answer to {question} according {text}"),
}])
.start()
qa_pairs.append({
"origin_piece": text_piece,
"qa_pairs": result,
})
print("[Done] Start next work in " + str(sleep_time) + " seconds.")
time.sleep(sleep_time)
print("[All Works Done]
")
最后还有一点细节:批量调用时注意接口频率限制。通过sleep_time变量控制请求间隔,避免被限流。整个流程跑完后,每个文本块对应的问答对和原始文本块一一映射,后续存储和检索都一目了然。
简单总结一句:这套方案的核心价值不在于代码有多复杂,而在于把“从长文本到结构化问答”这条路径,用最少的样板代码实现了闭环。剩下的,就是根据你自己的文档场景,调整切块策略和输出格式即可。
下饭影视APP下载安装指南
灵宝派对手游下载安装地址推荐
和平精英如何做到压枪稳-和平精英怎样才能压枪稳
下载浏览器app下载安装选择推荐
初中英语同步课文跟读APP推荐|免费下载高口碑跟读软件排行榜
BuuPo官网在哪下载 最新官方下载安装地址
4D采矿者官网在哪下载 最新官方下载安装地址
阅读app安卓版下载推荐
碎片人偶Vragmeet官网在哪下载 最新官方下载安装地址
Elysium Above 履云录官网在哪下载 最新官方下载安装地址
无尽花界时装合辑
免费影视剧APP推荐
喧哗番长乙女 2nd Rumble !!官网在哪下载 最新官方下载安装地址
纸嫁衣9官网在哪下载 最新官方下载安装地址
萌神契约手游下载安装
好用的手环阅读app下载安装
儿子穿新中式现身大会堂 马斯克罕见用中文回应:他正在学习普通话
人声接近真人!OpenAI一口气更新三款超强语音AI
名单曝光!库克、马斯克等将随团到访中国 黄仁勋不在其中
短视频软件推荐
手机号码测吉凶
本站所有软件,都由网友上传,如有侵犯你的版权,请发邮件haolingcc@hotmail.com 联系删除。 版权所有 Copyright@2012-2013 haoling.cc