AI回答采集:原始数据存储与可追溯性设计

📅 2026/7/29 17:21:15
AI回答采集:原始数据存储与可追溯性设计
采集AI模型回答时如果只存储最终解析后的结果一旦发现数据异常或需要重新计算指标往往无法还原原始回答。例如模型返回了格式错误的JSON或者后续需要提取新的字段这时原始响应就是唯一可靠的依据。本文面向需要审计或长期维护的AI采集系统设计一种原始数据存储方案重点解决可追溯性问题。方案不涉及实时流处理或大规模分布式存储仅讨论单机或中小规模场景下的实现思路。存储方案设计存储格式原始回答通常以文本形式存储。对于结构化响应如OpenAI Chat Completion API返回的JSON包含choices、usage等字段直接存储完整JSON可保留所有字段便于后续解析。对于流式响应需在客户端拼接完整内容后存储为纯文本。以下是一个示例JSON结构{id:chatcmpl-abc123,object:chat.completion,created:1722163200,model:gpt-4-0613,choices:[{index:0,message:{role:assistant,content:原始回答内容},finish_reason:stop}],usage:{prompt_tokens:10,completion_tokens:20,total_tokens:30}}对于非结构化文本如Markdown存储为纯文本但建议同时记录原始请求和响应的完整内容。元数据字段每条记录需要包含以下元数据采集时间精确到毫秒的时间戳平台如OpenAI、Claude、本地模型等模型名称如gpt-4-0613、claude-3-opus问题用户输入的原始问题请求参数temperature、max_tokens等响应状态成功、超时、错误码等版本号用于追踪数据schema变更数据库表设计使用关系型数据库如PostgreSQL存储元数据原始回答内容存储在文件系统或对象存储中数据库只保存路径。表结构如下CREATETABLEraw_responses(id BIGSERIALPRIMARYKEY,request_id UUIDNOTNULLUNIQUE,collected_at TIMESTAMPTZNOTNULLDEFAULTNOW(),platformVARCHAR(50)NOTNULL,model_nameVARCHAR(100)NOTNULL,questionTEXTNOTNULL,request_params JSONB,response_statusVARCHAR(20)NOTNULL,raw_content_pathTEXTNOTNULL,content_formatVARCHAR(20)NOTNULLDEFAULTjson,schema_versionINTEGERNOTNULLDEFAULT1,created_at TIMESTAMPTZNOTNULLDEFAULTNOW());CREATEINDEXidx_raw_responses_collected_atONraw_responses(collected_at);CREATEINDEXidx_raw_responses_platformONraw_responses(platform);原始回答文件按日期和request_id组织例如/data/raw/2026-07-28/{request_id}.json。核心实现过程以下Python代码展示采集并存储原始回答的流程包含异常处理和重试逻辑importjsonimportuuidfromdatetimeimportdatetimefrompathlibimportPathimportpsycopg2frompsycopg2importpoolfromtenacityimportretry,stop_after_attempt,wait_fixedclassRawResponseStorage:def__init__(self,db_config,storage_root,max_retries3):self.poolpool.SimpleConnectionPool(1,10,**db_config)self.storage_rootPath(storage_root)self.max_retriesmax_retriesretry(stopstop_after_attempt(3),waitwait_fixed(2))defstore(self,platform,model,question,params,response,status):request_idstr(uuid.uuid4())collected_atdatetime.utcnow()date_strcollected_at.strftime(%Y-%m-%d)# 保存原始内容到文件file_dirself.storage_root/date_str file_dir.mkdir(parentsTrue,exist_okTrue)file_pathfile_dir/f{request_id}.jsonraw_data{request_id:request_id,collected_at:collected_at.isoformat(),platform:platform,model:model,question:question,params:params,response:response,status:status}try:withopen(file_path,w,encodingutf-8)asf:json.dump(raw_data,f,ensure_asciiFalse,indent2)exceptIOErrorase:raiseRuntimeError(fFailed to write raw content file:{e})# 插入元数据到数据库connself.pool.getconn()try:withconn.cursor()ascur:cur.execute( INSERT INTO raw_responses (request_id, collected_at, platform, model_name, question, request_params, response_status, raw_content_path, content_format) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) ,(request_id,collected_at,platform,model,question,json.dumps(params),status,str(file_path),json))conn.commit()exceptExceptionase:conn.rollback()# 清理孤儿文件iffile_path.exists():file_path.unlink()raiseRuntimeError(fDatabase insert failed:{e})finally:self.pool.putconn(conn)returnrequest_id# 调用示例# storage RawResponseStorage(# db_config{host: localhost, dbname: ai_collect, user: user, password: pass},# storage_root/data/raw# )# request_id storage.store(# platformopenai,# modelgpt-4-0613,# question什么是可追溯性,# params{temperature: 0.7, max_tokens: 100},# response{choices: [{message: {content: 可追溯性是指...}}]},# statussuccess# )代码说明使用连接池管理数据库连接避免频繁创建连接。通过tenacity库实现重试机制网络超时或临时故障时可自动重试。异常处理文件写入失败抛出异常数据库插入失败时回滚并删除已写入的文件防止孤儿文件。调用示例展示了如何从API获取response并传入store方法。版本管理当存储格式或元数据字段发生变化时通过schema_version字段区分。旧版本数据仍可读取但解析逻辑需兼容。建议在代码中维护一个版本映射表根据版本号选择对应的反序列化方法。例如VERSION_PARSERS{1:parse_v1,2:parse_v2,}defparse_raw(raw_path,version):parserVERSION_PARSERS.get(version)ifnotparser:raiseValueError(fUnsupported schema version:{version})returnparser(raw_path)验证结果正常情况下执行存储后数据库应出现一条记录且文件系统中存在对应的JSON文件。可通过以下SQL验证SELECTid,request_id,collected_at,platform,model_name,response_statusFROMraw_responsesWHEREcollected_at2026-07-28ORDERBYcollected_atDESCLIMIT10;同时检查文件路径是否存在ls-la/data/raw/2026-07-28/常见问题与避坑文件系统性能高并发时文件IO可能成为瓶颈可考虑使用对象存储如S3替代本地文件。数据一致性写入文件成功但数据库插入失败会导致孤儿文件。上述代码通过异常处理回滚并删除文件但更可靠的做法是使用两阶段提交或先写数据库再写文件并增加定期清理任务。存储空间原始回答可能很大需要设置保留策略例如只保留30天或归档到冷存储。敏感信息原始回答可能包含用户隐私存储前需脱敏或加密。总结本方案通过分离元数据和内容、记录完整请求上下文、引入版本号实现了AI回答的可追溯性。在中小规模采集场景下表现良好但高并发时文件IO可能成为瓶颈建议使用对象存储。版本管理通过schema_version实现但需注意旧版本数据的兼容性。实际部署时需根据并发量、存储成本和合规要求调整具体实现。