在构建RAG(检索增强生成)和Agent(智能体)系统时,数据结构的标准化处理是确保系统可靠性的关键环节。JSON作为轻量级数据交换格式,在这类系统中扮演着数据传输"通用语言"的角色。想象一下,当大模型需要将生成的商品列表传递给前端展示,或者Agent需要将执行状态保存到数据库时,JSON就是确保双方能够准确理解彼此意图的"翻译官"。
我曾在开发一个电商推荐系统时深刻体会到JSON的重要性。当时系统需要处理来自不同渠道的商品数据,有的来自爬虫,有的来自合作伙伴API,还有的来自用户上传。正是通过JSON标准化,我们才能将这些异构数据统一处理。特别是在与大模型交互时,强制要求返回JSON格式的结果,使得后续的自动化处理成为可能,避免了传统文本解析的诸多不确定性。
JSON(JavaScript Object Notation)虽然名字中带有JavaScript,但它已经完全超越了最初的设计场景,成为跨语言数据交换的事实标准。其核心特点可以概括为:
重要提示:JSON字符串必须使用UTF-8编码,这是现代系统的默认选择,也是避免字符乱码的关键
理解类型映射关系是避免转换错误的基础。下表展示了完整映射关系:
| JSON类型 | Python类型 | 特殊说明 |
|---|---|---|
| object | dict | 键必须是字符串类型 |
| array | list | 有序可变序列 |
| string | str | Unicode字符序列 |
| number | int/float | 不区分整数和浮点数 |
| true/false | bool | 必须小写 |
| null | None | 表示空值 |
在实际项目中,我经常遇到数字类型处理的坑。比如JSON中的数字123.0在Python中可能被转换为123(int),而当这个值代表价格时,类型变化可能导致后续计算出错。因此我养成了在关键字段上显式类型标注的习惯:
python复制{
"price": {"value": 99.9, "type": "float"},
"quantity": {"value": 5, "type": "int"}
}
json.dumps()是将Python对象转换为JSON字符串的核心方法。除了基本的转换功能,它提供了一系列控制参数:
python复制import json
data = {
"name": "张三",
"age": 30,
"married": True,
"children": None,
"skills": ["Python", "SQL", "Docker"]
}
# 基础转换
basic_json = json.dumps(data)
print(basic_json) # 中文会被转义
# 美化输出
pretty_json = json.dumps(data,
ensure_ascii=False, # 保留中文
indent=2, # 缩进
sort_keys=True) # 键排序
print(pretty_json)
关键参数解析:
ensure_ascii=False:禁用ASCII转义,保留原始字符indent=2:使用2空格缩进,提升可读性separators=(',', ':'):控制分隔符样式default=str:处理无法序列化的对象json.loads()的看似简单,但在实际应用中需要注意几个关键点:
python复制json_str = '''
{
"name": "李四",
"active": false,
"projects": [
{"id": 1, "name": "AI平台"},
{"id": 2, "name": "数据管道"}
]
}
'''
try:
data = json.loads(json_str)
print(data['projects'][0]['name']) # 输出: AI平台
except json.JSONDecodeError as e:
print(f"JSON解析失败: {e.msg} (行{e.lineno}, 列{e.colno})")
异常处理要点:
让大模型输出规范的JSON需要精心设计的Prompt。以下是我在多个项目中验证有效的模板:
python复制prompt_template = """
请严格按照JSON格式返回数据,遵循以下要求:
1. 使用双引号包裹所有键和字符串值
2. 不使用任何注释
3. 不在末尾添加逗号
4. 对于空值使用null
输入文本:{user_input}
提取以下字段:
- name (字符串)
- age (整数)
- hobbies (数组)
- address (对象,包含city和street)
返回示例:
{{
"name": "示例",
"age": 20,
"hobbies": ["运动", "音乐"],
"address": {{
"city": "北京",
"street": "长安街"
}}
}}
"""
user_input = "王五,25岁,住在上海南京路,喜欢游泳和读书"
response = ask_llm(prompt_template.format(user_input=user_input))
# 清理可能存在的代码块标记
cleaned = response.strip().removeprefix('```json').removesuffix('```')
data = json.loads(cleaned)
在Agent系统中,JSON是保存和恢复状态的理想格式。这是我常用的状态管理方案:
python复制class AgentState:
def __init__(self):
self.current = "idle"
self.memory = []
self.context = {}
def to_json(self):
return json.dumps({
"current": self.current,
"memory": self.memory,
"context": self.context,
"_meta": {
"version": "1.0",
"timestamp": datetime.now().isoformat()
}
}, default=str)
@classmethod
def from_json(cls, json_str):
data = json.loads(json_str)
obj = cls()
obj.current = data["current"]
obj.memory = data["memory"]
obj.context = data["context"]
return obj
处理Python特殊对象时,可以通过default参数实现自定义序列化:
python复制def custom_serializer(obj):
if isinstance(obj, datetime):
return obj.isoformat()
elif isinstance(obj, set):
return list(obj)
raise TypeError(f"无法序列化类型: {type(obj)}")
data = {
"event": "会议",
"time": datetime.now(),
"participants": {"Alice", "Bob"}
}
json_str = json.dumps(data, default=custom_serializer)
当处理大型JSON文件时,应该使用增量式处理:
python复制# 写入大型JSON文件
with open('large.json', 'w') as f:
for chunk in generate_large_data():
json.dump(chunk, f)
f.write('\n') # 每行一个JSON对象
# 读取大型JSON文件
def stream_json(file_path):
with open(file_path) as f:
for line in f:
yield json.loads(line)
中文字符乱码是常见问题,解决方案包括:
当处理大量小JSON文档时,可以考虑:
解析不可信JSON时需注意:
在最近的一个知识图谱项目中,我们需要处理超过10GB的JSON数据。通过以下优化手段将处理时间从8小时缩短到30分钟:
python复制import ijson
def extract_entities(json_file):
with open(json_file, 'rb') as f:
# 流式提取特定路径下的数据
entities = ijson.items(f, 'item.entities.item')
for entity in entities:
process_entity(entity)
这个案例让我深刻认识到,即使是简单的JSON处理,在面对海量数据时也需要精心设计处理流程。