若想向 GPT-4o 传递 HTML 数据,建议移除所有 HTML 标签,仅传递纯文本内容。这样做成本更低,且对性能的影响微乎其微。
我原本以为,文档结构在提取结构化数据时起着关键作用,并且我将观察到成本与准确性之间“不可调和的矛盾”:我本来假设去掉 HTML 文档的结构会导致准确性的大幅下降,但实验结果证明这一假设是错误的 —— GPT-4o 无需 HTML 结构即可准确提取数据。
我选取了维基百科上的“水星音乐奖”(Mercury Prize)页面作为输入数据;该页面大小适中,拥有一个包含多项内容(years, artists, albums, nominees)的表格,而且这是一个颇为有趣的数据集。
01 Experimental setup: questions
在探索 HTML 结构对数据提取质量的影响时,我设计了两种类型的问题来测试 GPT-4o:
非结构化问题:正确答案包含在文档内容中,答案形式为字符串。
结构化问题:正确答案包含在表格内容中,答案形式为结构化数据(字符串列表)。
我总共提出了 20 个问题,其中 10 个是非结构化的,另外 10 个是结构化的。
在问题的复杂度上,我进行了调整。对于非结构化问题,由于可操作的空间有限,我尽量保持了问题简单,并且不会涉及数学问题(因为评估数学能力并非本次实验的目的)。
不过,结构化的问题给了我更多的实验空间。以下是一些示例问题:
Give me the years for the 1st, 4th and 8th editions (in order)
答案涉及理解表格结构和顺序。
Extract the shortlisted nominees (include the winner) for the 25th edition, only the artist names (they appear first, followed by the album)”
这个问题也涉及表格结构的理解:首先需要找到第 25 届“水星音乐奖”这一行,然后从两列表格中提取数据(获奖者和提名者在两个不同的表格列中),接着分割同一列中的数据(艺术家 - 专辑名)。
You can see all the questions in the source code🙂.
02 Experimental setup: pre-processing
随后,我设计了一套文本预处理流程来转换 HTML 文档,目的是减少 tokens 数量,以此来降低成本(因为 OpenAI 是按照 tokens 数目来收费的)。我尝试了以下预处理流程:
不进行任何处理:HTML 文档直接交给模型处理(这无疑是成本最高的方式!)
清除部分 HTML 标签:清除 <body></body> 标签以外的所有内容,删除 HTML 标签中的所有属性(class、id 和 data-testid 除外),用递增数字(1、2、3 等)代替 class 和 id,清除空白,用 TEXT 代替 <a>TEXT</a>
清除所有 HTML 标签:彻底清除文档中的所有 HTML 标签,仅保留纯文本内容
将 HTML 转换成 Markdown 格式(我采纳了这个建议,因为有人在 X/Twitter 上提到过 —— 由于大语言模型(LLMs)在训练过程中大量接触 Markdown 格式,它们理应能够掌握 Markdown 的结构特点)
03 Experimental setup: prompts
以下是我用来调用 GPT-4o 和 GPT-4o mini 的函数。
非结构化问题
def answer_question(*, html_content: str, model: str, query: str) -> str: SYSTEM_PROMPT = """ You're an expert question-answering system. You're given a snippet of HTML content and a question. You need to answer the question based on the HTML content. Your response should be a plain text answer to the question based on the HTML content. Your answer should be concise and to the point. """ completion = client.chat.completions.create( model=model, messages=[ { "role": "system", "content": SYSTEM_PROMPT.strip(), }, { "role": "user", "content": f"HTML Content: {html_content}\n\nQuestion: {query}", }, ], ) return completion.choices[0].message.content
结构化问题
class ParsedColumn(BaseModel): name: str values: List[str] def parse_column(*, html_content: str, model: str, query: str) -> dict: SYSTEM_PROMPT = """ You're an expert web scraper. You're given the HTML contents of a table, a user query and you have to extract a column from it that is related to the user query. The name of the column should be the header of the column. The values should be the text content of the cells in the column. """ completion = client.beta.chat.completions.parse( model=model, messages=[ {"role": "system", "content": SYSTEM_PROMPT}, { "role": "user", "content": f"HTML Content: {html_content}", }, { "role": "user", "content": f"User Query: {query}", }, ], response_format=ParsedColumn, ) event = completion.choices[0].message.parsed return event.model_dump()
04 模型评估流程
对于非结构化问题的回答,只要回答中包含了正确信息即可视为正确。以下是一个示例问题:
哪位艺术家获得 “水星音乐奖” 提名次数最多却未获奖?
以下任一回答均可视为正确:
Radiohead
Radiohead 是获得提名最多但未曾赢取奖项的乐队
答案是 Radiohead
在结构化问题中,答案的排序要求不一。有些问题对答案顺序没有要求,而有些则需按特定顺序回答。
以下是一个不需要排序答案的示例问题:
请列出 2015 年水星音乐奖的入围艺术家(不包括获奖者)。只需提供艺术家姓名(艺术家姓名在前,其后为专辑名称)。
answer = { "Aphex Twin", "Gaz Coombes", "C Duncan", "Eska", "Florence and the Machine", "Ghostpoet", "Róisín Murphy", "Slaves", "Soak", "Wolf Alice", "Jamie xx", } assert set(answer_gpt4) == answer
以下是一个需要按顺序回答的问题示例:
按顺序提取 1992 年至 1995 年的水星音乐奖获奖者
预期答案:
answer = [ "Primal Scream – Screamadelica", "Suede – Suede", "M People – Elegant Slumming", "Portishead – Dummy", ] assert answer_gpt4 == answer
05 Results
针对非结构化问题的提问,GPT-4o 及其 mini 版的表现几乎一致,而且预处理步骤对结果影响不大。考虑到两者之间的成本差异,建议在处理非结构化问题时选择 GPT-4o mini 版,并完全移除 HTML 内容,以最大限度地节省成本。
对于结构化问题,情况则有所不同:GPT-4o 的表现显著优于其 mini 版。尽管如此,预处理步骤对准确性的影响甚微。考虑到模型间的价格差异,建议您用样本数据进行测试,判断为了提升准确性是否愿意付出更高的费用支出。无论是哪种情况,移除所有 HTML 标签都是一个降低成本的好方法。
Raw results
P.S. 原文作者没有审校过本译文,且译者在翻译本内容时带有个人对原文的理解。可能理解有误,麻烦请在评论区指出!
Minifying HTML for GPT-4o: Remove all the HTML Tags
译者:codeUpHub