news 2026/7/26 23:52:01

LLM数值提取-计算场景示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LLM数值提取-计算场景示例

之前探索了LLM长上下文和数值类有效输出的关系

https://blog.csdn.net/liliang199/article/details/159175752

这里选用 苹果公司 2023 财年 10-K 年报(约 90 页,约 70K tokens)作为测试文本。

任务包括:

1)直接数值提取:从文本中找出指定财务数据(如总营收、净利润)。

2)基于提取值的计算:如计算“研发费用占总营收的比例”。

3)结构化输出:要求模型以 JSON 格式返回结果,便于程序解析。

将通过两种方式处理长文本:

1)一次性传入,如果模型上下文窗口足够,如 128K context的模型。

2)分块 + 摘要/检索,模拟超长文本无法一次性处理的情况。

1 环境准备

假设openai、faiss等相关工具已按照,这里示例环境设置和数据获取过程。

1.1 设置环境

模拟分块检索需要向量模型,可能存在hf访问问题,所以这里先设置hf国内镜像。

同时设置api key、user_url、model_name等。

import os os.environ['HF_ENDPOINT'] = "https://hf-mirror.com" os.environ['OPENAI_API_KEY'] = "sk-xxxxxx" os.environ['OPENAI_BASE_URL'] = "https://llm_provider.com/v1" model_name = "qwen3-xxxx"

这里实际运行采用qwen3.5系列模型。

1.2 数据获取

从 SEC EDGAR 下载苹果 2023 年 10-K 文本,链接如下所示

https://www.sec.gov/Archives/edgar/data/320193/000032019323000106/aapl-20230930.htm

由于是网页数据,这里采用选中所有内容后复制,然后在本地粘贴的方式,在本地构建aapl-20230930.txt文件。

1.3 预估token量

使用 tiktoken 预估 token 数,确保不超过模型限制。

import tiktoken def num_tokens_from_string(string: str, encoding_name: str = "cl100k_base") -> int: encoding = tiktoken.get_encoding(encoding_name) num_tokens = len(encoding.encode(string)) return num_tokens with open("./aapl-20230930.txt") as f: full_text = f.read() tokens = num_tokens_from_string(full_text) print(f"文档 token 数: {tokens}") if tokens < 128000: print("可直接使用128K上下文的模型") else: print("需采用分块策略")

输出示例如下

文档 token 数: 45185
可直接使用128K上下文的模型

2 基于单轮对话的提取计算

这里构造提示并调用 API,示例单轮对话的提取计算过程。

2.1 设计示例

首先设计提示词,要求模型提取指定数据并以 JSON 返回,细节如下。

from openai import OpenAI client = OpenAI() def ask_model(document, questions): prompt = f""" 你是一个财务分析专家。以下是苹果公司2023财年10-K年报的部分文本。 请根据文本回答以下问题,并以JSON格式返回结果。JSON键为问题编号,值为对应的答案(数值或字符串)。 文本内容: {document} 问题: {questions} 请直接输出JSON,不要包含其他文字。 """ response = client.chat.completions.create( model=model_name, # 支持128K上下文的模型 messages=[{"role": "user", "content": prompt}], temperature=0, # 降低随机性,提高数值准确性 max_tokens=500 ) return response.choices[0].message.content # 截取前60000 token(若文档超长可截断,此处假设一次性传入) if tokens > 120000: # 简单截断(更优做法是按段落截取,保证完整性) encoding = tiktoken.get_encoding("cl100k_base") encoded = encoding.encode(full_text) truncated = encoding.decode(encoded[:120000]) document = truncated else: document = full_text question_list = [ "1. 2023财年总营收(Total net sales)是多少?请以百万美元为单位,只输出数字。", "2. 2023财年研发费用(Research and Development)是多少?请以百万美元为单位,只输出数字。", "3. 研发费用占总营收的比例是多少?请以百分比形式输出,保留两位小数,如\"15.23%\"。", ] questions = "\n".join(question_list) result = ask_model(document, questions) print(result)

返回如下所示

{
"1": 383285,
"2": 29915,
"3": "7.80%"
}

2.2 解析 JSON 并验证

在获取LLM返回后,解析json并进行验证,代码细节如下。

import json import re def extract_json(text): # 从模型输出中提取JSON部分(有时会夹杂额外文本) json_pattern = r'\{.*\}' # 简单匹配,实际可用json.loads尝试 match = re.search(json_pattern, text, re.DOTALL) if match: return json.loads(match.group()) else: return json.loads(text) # 如果整个输出就是JSON try: data = extract_json(result) print("解析结果:", data) # 真实值(根据实际年报) ground_truth = { "1": 383285, # 百万美元,约3833亿美元 "2": 29915, # 约299亿美元 "3": 7.804 # 比例 = 29915/383285 ≈ 7.81% } print("真实值:", ground_truth) # 简单比较 for q, pred in data.items(): gt = ground_truth[q] if q == "3": # 百分比字符串转浮点比较 pred_val = float(pred.strip('%')) print(f"Q{q}: 预测 {pred_val}% vs 真实 {gt}%,误差 {abs(pred_val-gt):.2f}%") else: print(f"Q{q}: 预测 {pred} vs 真实 {gt},误差 {abs(int(pred)-gt)}") except Exception as e: print("解析失败:", e) print("原始输出:", result)

输出示例如下

解析结果: {'1': 383285, '2': 29915, '3': '7.80%'}
真实值: {'1': 383285, '2': 29915, '3': 7.804}
Q1: 预测 383285 vs 真实 383285,误差 0
Q2: 预测 29915 vs 真实 29915,误差 0
Q3: 预测 7.8% vs 真实 7.804%,误差 0.00%

可以在上下文长度允许的情况下,单次传入可以获得精确结果。

3 基于分块+检索的提取计算

这里假设文本远超模型上下文时,这时可采用检索增强生成RAG的思路。

将文档分块,用向量检索相关块,再将相关块送入模型。

3.1 分块与向量化

这里将文本划分为1000大小块,使用all-MiniLM-L6-v2将分块文本转向量,使用faiss管理向量。

from sentence_transformers import SentenceTransformer import numpy as np import faiss # 分块函数(按段落或固定长度) def chunk_text(text, chunk_size=500, overlap=50): words = text.split() chunks = [] for i in range(0, len(words), chunk_size - overlap): chunk = ' '.join(words[i:i+chunk_size]) chunks.append(chunk) return chunks chunks = chunk_text(full_text, chunk_size=1000) # 每个块约1000词 print(f"分块数量: {len(chunks)}") # 生成向量(使用轻量模型) model = SentenceTransformer('all-MiniLM-L6-v2') chunk_embeddings = model.encode(chunks, show_progress_bar=True) # 构建FAISS索引 dimension = chunk_embeddings.shape[1] index = faiss.IndexFlatL2(dimension) index.add(np.array(chunk_embeddings))

示例如下

分块数量: 32
Batches: 100%|██████████| 1/1 [00:00<00:00, 1.38it/s]

3.2 检索与问答

在将full_text文本向量化后,这里进行分块检索和LLM回答示例,代码如下所示。

def retrieve_relevant_chunks(query, k=3): query_emb = model.encode([query]) distances, indices = index.search(query_emb, k) return [chunks[i] for i in indices[0]] def ask_with_rag(questions): # 将多个问题合并成一个查询,检索相关块 combined_query = " ".join(questions.values() if isinstance(questions, dict) else questions) relevant = retrieve_relevant_chunks(combined_query, k=5) context = "\n\n---\n\n".join(relevant) prompt = f""" 根据以下从苹果10-K年报中提取的相关段落,回答问题。以JSON格式返回。 相关段落: {context} 问题: 1. 2023财年总营收(Total net sales)是多少?以百万美元为单位。 2. 2023财年研发费用(Research and Development)是多少?以百万美元为单位。 3. 研发费用占总营收的比例是多少?以百分比形式。 JSON输出示例: {{"1": 383285, "2": 29915, "3": "7.81%"}} """ response = client.chat.completions.create( model=model_name, # 可使用较小模型 messages=[{"role": "user", "content": prompt}], temperature=0, max_tokens=300 ) return response.choices[0].message.content questions_dict = { "1": "2023财年总营收", "2": "2023财年研发费用", "3": "研发费用占总营收的比例" } rag_result = ask_with_rag(questions_dict) print(rag_result)

输出如下

{
"1": 383285,
"2": 29915,
"3": "7.80%"
}

4 数值可靠性增强技巧

以上示例了基于LLM以及RAG,对苹果财报的总营收、研发投入,以及研发占比进行提取和计算。

实现逻辑简单清洗,然而在实际案例中,可能需要提取更复杂的数据。

数据可能不是显式分布,可能更隐蔽,更难发现,这时可能要采用更可靠的增强技巧。

比如CoT、取均值、以及应用外部计算工具。

1)思维链多步推理

要求模型先解释推理过程,再给出数值,可减少计算错误。

cot_prompt = """ 请逐步推理并计算: 1. 从文本中找到总营收数字。 2. 找到研发费用数字。 3. 计算比例。 最后以JSON输出结果。 """

2)多次运行取均值

temperature能调节模型的灵敏度,多次采用不同的temperature采样,取多数答案或平均值。

另外,采用幂采样的方式优化模型输出,也能获得更好的结果,缺点是会拖慢处理速度。

参考链接如下

https://blog.csdn.net/liliang199/article/details/154833697

3)应用外部计算工具

LLM运行计算并不是总是可靠的,有可能出现幻觉,导致计算错误。

让模型输出表达式,用 Pythoneval()执行计算,可以避免模型内部计算错误,示例如下。

calc_prompt = """ 请输出一个Python表达式来计算研发费用占比,例如 "x / y * 100",其中x和y是提取的数字。 """

该方法的前提是数据已就绪,否值还需要补充数据提取和清洗过程。

reference

---

LLM长上下文和数值类有效输出的关系探索

https://blog.csdn.net/liliang199/article/details/159175752

如何基于幂采样优化LLM推理-原理&代码&示例

https://blog.csdn.net/liliang199/article/details/154833697

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 14:36:12

基于Docker容器化部署的ROS2 Gazebo导航仿真环境搭建

1. 为什么选择Docker部署ROS2导航仿真环境 第一次接触机器人导航仿真时&#xff0c;我花了整整三天时间在Ubuntu系统上折腾各种依赖库。ROS2的版本冲突、Gazebo的插件缺失、Nav2的编译错误...这些坑让我深刻体会到环境配置的痛苦。直到尝试用Docker容器化方案&#xff0c;才发…

作者头像 李华
网站建设 2026/7/14 14:36:13

ofa_image-caption一文详解:OFA-COCO蒸馏模型本地推理原理与限制说明

OFA 图像描述生成工具一文详解&#xff1a;OFA-COCO蒸馏模型本地推理原理与限制说明 1. 工具概述与核心价值 OFA图像描述生成工具是一个基于先进多模态模型的本地化应用&#xff0c;专门用于为图片自动生成英文描述。这个工具最大的特点是完全在本地运行&#xff0c;不需要联…

作者头像 李华
网站建设 2026/7/14 14:36:12

不只是画点:在ArcMap中创建点数据集的完整工作流与数据管理心得

不只是画点&#xff1a;在ArcMap中创建点数据集的完整工作流与数据管理心得 在GIS项目中&#xff0c;点数据集的创建往往被视为基础操作&#xff0c;但真正高效、规范的生产流程远不止于"画几个点"。许多从业者在项目后期常遇到数据混乱、坐标系不匹配、属性表缺失等…

作者头像 李华
网站建设 2026/7/14 14:36:11

Java开发避坑:从实际项目出发,搞定ArrayList线程安全问题

做Java开发的同学&#xff0c;几乎每天都要和集合打交道&#xff0c;而ArrayList更是家常便饭——简单、高效、易用&#xff0c;大部分业务场景下用它都没毛病。但最近做项目迭代时&#xff0c;我踩了个大坑&#xff1a;线上环境突然报出ConcurrentModificationException&#…

作者头像 李华