Python RAG 开发:两类特殊字符串解析实战
前言
在做 Langchain RAG 项目开发的时候,经常会遇到工具返回、日志打印产生的字符串,这些字符串看着像列表、对象,但本质只是文本,无法直接读取。下面两个是实操高频遇到的问题。
问题 1:字符串格式的列表,转为 Python 列表对象
场景
拿到字符串:
python
运行
raw_str ='["qwen-turbo","qwen-plus","deepseek"]'目标:把这个字符串变成真正的 Python 列表对象。
错误做法
直接使用eval()。eval会执行字符串内任意代码,如果字符串来源是用户输入,会造成代码注入安全漏洞,生产环境禁止使用。
方案 1:ast.literal_eval【推荐】
ast.literal_eval只解析 Python 字面量,不会执行逻辑代码,安全性高,同时兼容单引号、双引号。
python
运行
import ast raw_str ='["qwen-turbo","qwen-plus","deepseek"]' result = ast.literal_eval(raw_str) print(result) print(type(result)) #输出 #['qwen-turbo', 'qwen-plus', 'deepseek'] #<class 'list'>方案 2:json.loads
当字符串严格遵循 JSON 标准,全部使用双引号时可以使用。
python
运行
import json raw_str ='["qwen-turbo","qwen-plus","deepseek"]' result = json.loads(raw_str)缺点:如果字符串内部出现单引号,json 直接抛出解析异常,RAG 项目中经常遇到单引号,因此优先选择
ast.literal_eval。
问题 2:字符串化的 Document 对象,提取 page 属性
场景描述
Tool 打印输出得到下面一长串字符串,它不是标准 JSON,只是对象print()之后的文本。里面包含多个Document实例,需要拿到每一个的page页码。
python
运行
s = """[Document(metadata={'pk': 12, 'page': 2}, page_content='2. 费⽤报销: 业务活动中产⽣的费⽤,须凭真 实、合规的发票,并写明事由,经审批后⽅可报销。严禁虚报、\\n谎报费⽤。\\n第五章 奖惩制度 \\n第13条 奖励\\n对于符合下列条件的员⼯,公司将视情况给予通报表扬、奖⾦、晋升等奖励:\\n1. 超额完成销售指标,业绩突出者。\\n2. 提出合理化建议,经采纳后为公司带来显著效益者。\\n3. 在维护公司利益和声誉⽅⾯有重 ⼤贡献者。'), Document(metadata={'pk': 27, 'page': 2}, page_content='2. 费⽤报销: 业务活动中产⽣的费⽤,须凭真实、合规的发票,并写明事由,经审批后⽅可报销。严禁虚报、\\n谎报费⽤。\\n第五章 奖惩制度 \\n第13条 奖励\\n对于符合下列条件的员⼯,公司将视情况给予通报表扬、奖⾦、晋升等奖励:\\n1. 超额完成销售指标,业绩突出者。\\n2. 提出合理化建议,经采纳后为公司带来显著效益者。\\n3. 在维护公司利益和 声誉⽅⾯有重⼤贡献者。'), Document(metadata={'pk': 14, 'page': 3}, page_content='3. 最后警告: 情节严重,留司察看。\\n4. ⽴即解雇: 对于严重违纪者,公司有权单⽅⾯⽆条件解除劳动合同,⽆需⽀付经济补偿⾦。严重违纪包括但\\n不限于:\\n贪污、受贿、泄露核⼼商业秘密。\\n虚报业绩、伪造销售记录或报销凭证。\\n⻓期旷⼯(连续三天或⽉累计五天)。\\n严重失职,给公司造成重⼤经济损失或声誉损害。\\n违反国家法律法规,被依法追究刑事责任。\\n第六章 附则 \\n第15条 守则的修订与解释')]"""方案 A:正则表达式提取(只需要 page,推荐)
不需要还原完整 Document 对象,直接正则匹配捕获page后的数字,速度快,不需要导入类。
python
运行
import re s = """[Document(metadata={'pk': 12, 'page': 2}, page_content='xxx'),Document(metadata={'pk': 14, 'page': 3}, page_content='yyy')]""" pattern = re.compile(r"metadata=\{.*?'page':\s*(\d+)") match_list = pattern.findall(s) page_list = [int(item) for item in match_list] print(page_list) #输出 [2,3]方案 B:eval 完整还原对象(仅限内部可控日志)
⚠️风险:只允许处理程序内部产生字符串,禁止接收外部用户输入,存在安全风险。 必须提前导入
Document类,否则会报Document未定义。
python
运行
from langchain_core.documents import Document s = """[Document(metadata={'pk': 12, 'page': 2}, page_content='测试'),Document(metadata={'pk': 14, 'page': 3}, page_content='测试2')]""" doc_list = eval(s) page_list = [doc.metadata["page"] for doc in doc_list] print(page_list) #输出 [2,3]✅工程最佳实践
不要把 Document 对象直接序列化为字符串传递。 写 Tool 的时候,不要直接返回list[Document],提前提取字段返回字典列表,后续就不需要做复杂解析。
python
运行
# 不推荐 return docs #推荐 return [{"page":d.metadata["page"], "content":d.page_content} for d in docs]总结
- 普通字符串列表解析:优先
ast.literal_eval,安全,兼容单引号; - 打印出来的
Document(...)字符串:只取页码优先正则;完整还原使用eval,仅限内部可控数据; - RAG 开发中尽量避免传递对象字符串,Tool 直接返回字典,减少解析麻烦。
