
做Agent开发很多人一上来就研究框架、调API、写Prompt结果真到了要把Agent接到实际业务里才发现最先卡住的居然是数据解析。Agent往外吐的内容是JSON格式但另一套老系统只接受CSV用户输入的是结构化文本Agent内部却需要组织成特定的数据结构才能继续运算更别提日志里躺着的各种“Unexpected token”“Expected , or ]”报错。这些数据处理基本功才是Agent开发里最容易被低估的门槛。这篇就来聊清楚Agent输入输出处理最核心的三个东西JSON解析、CSV解析、以及简单数据结构。不扯虚的全部围绕Agent开发的实际场景展开代码可直接抄踩过的坑也一并说清楚。1. 为什么Agent开发绕不开数据解析1.1 Agent的本质是数据进、数据出很多人对Agent有一个浪漫化的误解觉得Agent是“会思考的程序”。但从工程视角看Agent就是一个输入输出处理单元接收任务描述文本/结构化数据经过LLM推理、工具调用、内部逻辑处理后产出结果文本/JSON/文件等。整个链条里数据格式的转换贯穿始终。举个最常见的场景Agent接了一个任务需要从用户提供的CSV文件里筛选数据再用JSON格式返回结果。如果你连CSV都读不进来、JSON都解析不了后面Prompt写得再漂亮都是白搭。这也是为什么几乎所有Agent开发框架无论是LangChain、AutoGPT还是各类Harness底层都离不开对结构化数据的处理能力。从工作流的角度看Agent的数据处理有三个关键节点输入侧把用户的自由输入、文件上传、API回调等不同来源的数据统一解析成Agent能理解的结构化格式。内部流转Agent在调用工具、存储记忆、做逻辑判断时需要维护自己的数据结构。输出侧把Agent生成的最终结果转换成用户或下游系统需要的格式JSON、CSV、文本等。任何一个节点出问题整个Agent就像人失去味觉一样吃什么都没味道做什么都别扭。1.2 结构化数据是Agent的“通用语言”为什么偏偏是JSON和CSV原因很务实JSON是LLM最擅长的结构化输出格式现在主流Agent框架都默认要求模型返回JSON因为它嵌套灵活、类型丰富、可读性好。CSV则是数据交换的“最小公分母”数据库导出、Excel表格、老旧系统的数据交换全用它。这两者组合起来正好覆盖了Agent开发中最常见的两种数据形态JSON处理单条复杂信息CSV处理批量简单记录。而把它们串联起来的就是数据结构。数据结构这个词听着像是科班课程里的抽象概念但在Agent开发里它就是你的“组织信息的方式”用字典存一条订单信息用列表存多轮对话记录用集合去重用元组传不可变参数。数据结构选得对代码写起来顺手、调试起来轻松选不对后面全是补丁。2. JSON解析Agent最常用的数据交换格式2.1 从“字符串”到“结构体”——JSON解析的本质JSON全称是JavaScript Object Notation但现在已经成了编程界的通用语言。对Agent开发来说JSON的威力在于它能表达嵌套的复杂结构而且几乎是所有LLM API默认的输入输出格式。看一个典型的Agent工具返回{ status: success, data: { order_id: A12345, items: [ {name: 蓝牙耳机, price: 299.0, quantity: 1}, {name: 数据线, price: 39.9, quantity: 2} ], total: 378.8 }, ts: 1735000000 }这串文本到了Python里核心操作只有一行import json result json.loads(response_text) order_id result[data][order_id] total result[data][total]就这么简单对就这么简单。但坑往往不在解析本身而在数据取出来之后。2.2 高频操作与隐蔽的坑在实际Agent项目中我常用的JSON处理套路是这几个第一防御性取数。Agent返回的JSON结构可能因为模型输出不稳定而变化直接result[data][items]很容易炸。我习惯用.get()加默认值data result.get(data, {}) items data.get(items, []) if not items: print(没有商品数据可能是解析或工具调用异常)第二处理嵌套结构。嵌套是JSON的核心能力但也是出错的重灾区。多层嵌套的JSON取数路径一旦写错就是KeyError。我常用的技巧是写一个小函数来做安全取值def safe_get(data, path, defaultNone): current data for key in path: if isinstance(current, dict): current current.get(key) if current is None: return default else: return default return current # 用法取 items 里的第一个商品名称 name safe_get(result, [data, items, 0, name], 未知商品)第三JSON序列化的反向操作。Agent需要把内部数据结构返回给下游系统或者写进文件时用json.dumps()output { order_id: order_id, items_count: len(items), total: total } json_str json.dumps(output, ensure_asciiFalse, indent2)这里有个小细节ensure_asciiFalse一定要加否则中文会变成\u59dc\u6c70那一串转义字符调试的时候看着头大用户看到更是一脸懵。2.3 Agent场景下的JSON特殊注意事项Agent开发和普通后端开发的JSON处理区别主要在两个方面一是模型输出的JSON可能不合法。LLM生成JSON时偶尔会多一个逗号、漏一个引号、甚至混进自然语言的解释文字。针对这个问题业界有几个常见的兜底方案import json import re def parse_llm_json(text): # 尝试直接解析 try: return json.loads(text) except json.JSONDecodeError: pass # 尝试提取大括号代码块 match re.search(r\{.*\}, text, re.DOTALL) if match: try: return json.loads(match.group()) except json.JSONDecodeError: pass # 最后兜底修正常见错误 text re.sub(r,\s*([}\]]), r\1, text) # 去掉多余的逗号 return json.loads(text)这段代码不一定能解决所有问题但能覆盖80%的模型输出异常实测下来比直接裸解析稳定得多。二是JSON Schema校验。Agent开发里强烈建议给关键输出做结构校验先校验再使用不要默认模型一定按你给的格式输出def validate_agent_output(data): required_keys [status, data] if not all(k in data for k in required_keys): return False, f缺少关键字段: {required_keys} if not isinstance(data.get(data), dict): return False, data字段格式错误 return True, ok另外一个很多新手忽略的点JSON的“值类型”是敏感且容易翻车的。比如价格字段有时候模型返回的是字符串299.0有时候是数字299.0。建议在解析后统一做一次类型转换避免后续四则运算报错。3. CSV解析批量数据处理的实用方案3.1 什么时候Agent需要处理CSVCSVComma-Separated Values看起来简单实际上它是Agent在处理批量数据时最常打交道的格式。典型的场景包括Agent读一张用户上传的表格筛选、汇总、分析。Agent把处理结果批量导出成CSV方便用户下载到Excel里看。Agent对接旧系统或第三方平台数据交换格式就是CSV。CSV之所以在Agent开发里重要是因为它天然适合“批量”。比如你做一个客服Agent用户上传一份订单CSVAgent要按状态列分组统计——这个任务的本质就是把CSV读进来变成内存里的数据结构然后做处理。3.2 用Python标准库读CSV——基础但不寒碜很多人一上来就用pandas但pandas虽然强在Agent场景里有时候是杀鸡用牛刀。如果你的CSV文件不大几千行以内Python内置的csv模块完全够用而且不用额外装依赖部署更轻import csv def read_csv_to_dicts(file_path): data [] with open(file_path, moder, encodingutf-8-sig) as f: reader csv.DictReader(f) for row in reader: data.append(row) return data rows read_csv_to_dicts(orders.csv) print(rows[0]) # {订单号: A12345, 商品: 蓝牙耳机, 金额: 299.0, 状态: 已付款}注意这里编码用了utf-8-sig而不是utf-8。因为有些CSV文件是从Excel导出的带BOM头字节顺序标记不用sig方式读取的话第一列列名会出现一个乱码前缀比如\ufeff订单号非常恶心。3.3 用pandas处理CSV——Agent数据分析的主力方案如果你需要在Agent里做稍微复杂一点的筛选、汇总、排序那pandas就派上用场了。最常见的读取方式import pandas as pd df pd.read_csv(orders.csv) print(df.head()) print(df[状态].value_counts()) # 按状态分组统计 paid_orders df[df[状态] 已付款] # 筛选 total_amount paid_orders[金额].astype(float).sum() # 求和为什么这里要.astype(float)因为pandas读CSV时纯数字列有时候会被识别成字符串特别是列里有空值或特殊字符时。不转换直接sum()会报错或者把字符串拼起来而不是数值相加。这是新手最容易踩的坑之一。pandas读CSV还有几个我觉得很实用的参数df pd.read_csv( orders.csv, dtype{金额: float}, # 强制指定列类型 parse_dates[下单时间], # 自动解析日期 thousands,, # 千位分隔符处理 na_values[, NULL] # 指定空值标记 )3.4 写CSV的常见姿势Agent生成结果之后要导出CSV同样是两个方案。标准库版本import csv def write_csv(filename, headers, rows): with open(filename, modew, encodingutf-8-sig, newline) as f: writer csv.writer(f) writer.writerow(headers) writer.writerows(rows)pandas版本更简单df.to_csv(summary.csv, indexFalse, encodingutf-8-sig)这里有两个细节要强调newline必须加否则在Windows系统下CSV里会出现多余的空行。写文件时用utf-8-sig编码这样用户在Excel里直接打开才不会有中文乱码。这不是“兼容性妥协”而是对用户负责。说到乱码很多人反馈“CSV豆包乱码”本质上就是编码不匹配代码用utf-8写入但Excel默认用ANSIGBK打开自然乱。解决办法就是写utf-8-sig或者干脆在导出时提示用户用WPS/Excel的“导入CSV”功能并选择UTF-8编码。3.5 CSV处理的进阶用SQL过滤CSV数据热搜词里有个“sql语句过滤csv数据”这个用法在Agent开发里也很实用。你不用把CSV导入数据库直接用pandas的query方法就能模拟SQL过滤df pd.read_csv(orders.csv) # 等价于 SELECT * FROM orders WHERE 金额 100 AND 状态 已付款 result df.query(金额 100 and 状态 已付款)如果你真的需要跑完整的SQL可以用duckdb这个库直接对DataFrame执行SQLimport duckdb df pd.read_csv(orders.csv) result duckdb.query(SELECT 状态, SUM(金额) FROM df GROUP BY 状态).df()这个组合拳在构建数据分析类Agent时特别能打。4. 简单数据结构Agent内部的数据组织方式4.1 为什么数据结构不是考试概念很多人在学习“数据结构”这门课时觉得它就是链表、二叉树、图这些抽象东西。但在Agent开发里数据结构是真正帮你把代码写优雅的工具。你不需要实现红黑树但你需要知道什么时候用列表、什么时候用字典、什么时候用集合。简单说列表有序、可重复适合存储“一批同类型的实体”。比如多轮对话记录conversation_history []每轮是一个dict。字典键值映射适合存储“一个实体的多属性”。比如单条订单信息order {id: A123, amount: 299.0}。元组不可变序列适合存储“不应该被修改的组合”。比如坐标(纬度, 经度)、或者工具调用的固定参数组合。集合无序、不重复适合去重和成员判断。比如检查一个用户ID是否已经在处理队列中。4.2 Agent开发里的数据结构实战组合Agent内部最经典的数据结构组合就是“字典套列表”。比如一个Agent要维护一个工具调用记录tool_calls [ {tool: search, query: Python JSON, ts: 1735000000}, {tool: calculator, expression: 299*2, ts: 1735000010} ]这种结构的最大好处是既方便单独取某条记录又能整体遍历。按时间筛选、按工具名分组都很好写search_calls [call for call in tool_calls if call[tool] search]另一个常见场景是去重。比如Agent从多个渠道收集数据可能有重复项。用集合一步搞定all_ids [order[order_id] for order in orders] unique_ids set(all_ids)4.3 数据结构选型的三条经验法则我踩过不少数据结构的坑总结出三条特别实在的经验第一条存“一组东西”用列表存“内容相关的一组东西”用字典。这句话听起来像废话但很多人写代码时没有刻意去想。比如你要给一个Agent传入“任务执行参数”正确做法是字典{max_iterations: 5, temperature: 0.7}而不是列表[max_iterations, 5, temperature, 0.7]。后者取参数的时候全靠记忆下标代码写多了根本维护不了。第二条数据量大、查询频繁就考虑转成字典索引。比如你要在10000条订单里反复查找某个订单号每次都遍历列表是O(n)转成字典的键查找是O(1)order_map {order[order_id]: order for order in orders} # 之后每次查找 order order_map.get(A12345)第三条临时去重用集合但你得承受它的“无序”代价。如果你需要去重之后还保持原来的顺序集合就不行了得用一个小技巧seen set() deduped [] for order in orders: if order[order_id] not in seen: seen.add(order[order_id]) deduped.append(order)这套写法既去重又保序在Agent处理用户上传的重复数据时非常常用。5. Agent输入输出实战从CSV数据到结构化JSON5.1 一个完整的数据分析Agent流程现在把上面的内容串起来做一个完整的数据分析Agent流程示例。场景是这样用户上传一个订单CSVAgent读取后按状态统计金额最后以JSON格式返回结果。import json import pandas as pd def process_orders(csv_path): # Step 1: 读取CSV忽略脏数据 df pd.read_csv(csv_path, encodingutf-8-sig) # Step 2: 类型清洗 df[金额] df[金额].astype(float) # Step 3: 按状态聚合统计 summary df.groupby(状态).agg( 订单数(订单号, count), 总金额(金额, sum) ).reset_index() # Step 4: 转换成Agent友好的JSON结构 result { status: success, total_orders: len(df), summary: summary.to_dict(orientrecords) } return json.dumps(result, ensure_asciiFalse, indent2) # 使用 output process_orders(orders.csv) print(output)这个流程虽然短但涵盖了Agent数据处理的所有核心环节读入、清洗、聚合、结构化输出。5.2 输入侧来自不同类型来源的解析策略Agent的输入来源五花八门比如用户可能直接贴一段JSON文本也可能上传一个文件还可能直接说一句话让Agent自己去拿数据。作为开发者你的任务是把这个输入变成统一的内部表示。针对“用户输入可能是JSON字符串或纯文本”的常见场景我写过一个统一的解析函数def parse_input(raw_input): # 尝试JSON解析 if isinstance(raw_input, str): stripped raw_input.strip() if stripped.startswith({) or stripped.startswith([): try: return json.loads(stripped) except json.JSONDecodeError: return {content: raw_input} # 已经是结构化数据比如直接传了dict if isinstance(raw_input, (dict, list)): return raw_input # 兜底作为纯文本处理 return {content: str(raw_input)}这个小函数解决了一个非常实际的问题Agent接到用户的输入时你没办法假设它一定是JSON还是纯文本。做成自动探测格式比自己猜好得多。5.3 输出侧让JSON成为Agent的“标准答案”前面说了Agent框架希望模型输出JSON。但用户不可能直接看JSON用户要的是自然语言或者一个可下载的文件。所以输出侧通常是一个“翻译”的过程内部用JSON传递对外展示用自然语言或文件。一个常见的设计模式是# 内部Agent处理后得到JSON结果 json_result { status: success, summary: [ {状态: 已付款, 订单数: 120, 总金额: 35880.5}, {状态: 待发货, 订单数: 35, 总金额: 10240.0} ] } # 对外转成可读文本 lines [] for item in json_result[summary]: lines.append( f{item[状态]}的订单有{item[订单数]}个 f总金额是{item[总金额]}元 ) final_answer \n.join(lines)这样用户看到的就不是干巴巴的JSON而是完整的自然语言回答。还有一个重要的输出场景Agent不只是返回文本还需要刷新文件或调用下游API。此时你输出的JSON必须严格匹配下游的Schema。写接口对接文档的时候我吃过不少亏后来总结出一个原则永远在你代码里做一次“输出前校验”而不是等到下游系统报错了再来排查。required_fields [status, summary] for field in required_fields: if field not in json_result: raise ValueError(f缺少关键字段: {field})5.4 从零到一一个最小可运行的Agent数据处理脚本最后放一个完整的最小脚本把JSON、CSV、数据结构全部串起来。这个脚本可以直接跑起来当模板用import json import csv import pandas as pd class MiniAgentDataProcessor: def __init__(self, csv_path): self.df pd.read_csv(csv_path, encodingutf-8-sig) self.records self.df.to_dict(orientrecords) def filter_by_status(self, status): 数据结构列表推导过滤 return [r for r in self.records if r.get(状态) status] def aggregate_by_key(self, key): 数据结构字典聚合统计 result {} for r in self.records: k r.get(key, 未知) result[k] result.get(k, 0) 1 return result def to_json(self, data): 输出JSON return json.dumps(data, ensure_asciiFalse, indent2) def to_csv(self, data, output_path): 输出CSV if not data: return headers list(data[0].keys()) with open(output_path, w, encodingutf-8-sig, newline) as f: writer csv.DictWriter(f, fieldnamesheaders) writer.writeheader() writer.writerows(data) # 使用示例 agent MiniAgentDataProcessor(orders.csv) paid agent.filter_by_status(已付款) print(agent.to_json(agent.aggregate_by_key(商品))) agent.to_csv(paid, paid_orders.csv)这个类虽然简陋但结构清晰你可以根据自己的业务往上加方法。它展示了一个核心思想Agent的数据处理本质就是把外部数据读进来变成内部结构处理完再变成外部格式发出去。6. 常见问题与排查技巧实录6.1 高频报错与解决方案速查表这几年在Agent开发里JSON/CSV相关的问题反复出现。整理成一张速查表方便直接对号入座。报错/现象原因解决方案json.JSONDecodeError: Expecting value输入的不是JSON文本或者带了杂质先打印原始字符串检查用parse_llm_json做兜底json.JSONDecodeError: Extra data字符串里有多个JSON对象拼接定位分隔符逐段解析或要求模型严格输出JSONKeyError: xxx直接取不存在的键改用.get()增加结构校验Excel打开CSV中文乱码编码用了utf-8而不是utf-8-sig写入时指定encodingutf-8-sig读取CSV第一列列名有\ufeff文件带BOM读取时指定encodingutf-8-sigValueError: could not convert string to floatCSV里金额列有非数字值用pd.to_numeric(errorscoerce)或清洗数据pandas读取时“干净”的列变成了object类型混合类型或空值用dtype参数强制指定CSV导出后多空行没加newline文件打开时加newlineTypeError: string indices must be integers拿字符串当字典用检查变量是不是JSON解析失败了打印类型确认6.2 排查思路先打印再定位遇到这类问题我第一反应永远是打印原始数据。很多人一报错就急着找解决方案其实最快的路径是先确认数据本身长什么样、类型是什么、编码是什么。几个快速诊断的写法import json text {status: success} print(type(text)) # class str print(repr(text)) # 看不可见字符 data json.loads(text) print(type(data)) # class dict print(data.get(status)) # 安全取值排查CSV问题也一样先看原始行再看字典长什么样with open(orders.csv, encodingutf-8-sig) as f: lines f.readlines() print(lines[:3]) # 看原始数据6.3 数据处理里的几个独家心得最后分享几个经验性的心得这些是踩过坑之后才慢慢悟出来的。心得一能用标准库解决的不要急着上框架。很多初学者一上来就pandas结果处理一个1万行的小文件也要挂着几百MB的内存。其实csv模块处理小文件完全够用pandas更应该用在真正的分析场景。Agent是一个需要快速响应的系统依赖越轻、启动越快体验越好。心得二永远对LLM输出保持“怀疑”。LLM输出JSON时大概率是规则的但小概率会出现各种诡异情况多余的逗号、缺失的引号、混入的注释、甚至干脆输出一大段文字。你自己写的解析函数要能处理这些异常但不能假设它永远不会发生。最好的方式是先试用一两个月把真实遇到的异常都记录到你的兜底逻辑里。心得三数据处理代码一定要单独拆出来。别把JSON解析、CSV清洗、数据转换的逻辑直接堆在Agent的主流程里。拆成一个独立的data_utils.py或者data_processor.py模块方便单独测试也方便复用。我现在的项目里数据解析模块的单元测试覆盖率比Agent逻辑本身还高因为数据出问题的影响面实在太大了。写在后面坦白说Agent开发最迷人的部分是“智能”但最考验工程能力的反而是这些不起眼的数据处理细节。一个JSON解析的边界没处理好可能让整个Agent在线上跑三天之后突然崩掉一个CSV的编码没管好用户的Excel里就是一团乱码。在我个人的实践中数据处理的代码往往是整个Agent项目里改动最少、但收益最高的部分。它们不炫技却在默默支撑着Agent的每一次输入和输出。希望这篇能帮你把Agent的地基打扎实一点少走几步弯路把时间真正花在Agent智能逻辑本身的打磨上。