GPT Researcher 日志体系全解析:读懂 research 过程的 events、用户日志与开发者日志
2026/9/10 2:16:20 网站建设 项目流程

GPT Researcher 日志体系全解析:读懂 research 过程的 events、用户日志与开发者日志

【免费下载链接】gpt-researcherAn autonomous agent that conducts deep research on any data using any LLM providers项目地址: https://gitcode.com/GitHub_Trending/gp/gpt-researcher

导读:gpt-researcher 在每次研究任务中都会沉淀三类日志——面向终端用户的 JSON 事件日志、面向开发者的.log文本日志与.json结构化日志。本文以仓库文档docs/docs/gpt-researcher/handling-logs/all-about-logs.md为骨架,结合 logging_config.py、server_utils.py、actions/utils.py 等源码,逐一拆解日志文件的存放位置、JSON 事件结构、26 类事件语义、日志的排障/透明化/复现价值,以及面向开发者的日志类型与真实调用链,帮助读者在排障、性能分析与流程追溯中准确使用这套日志体系。

一、日志体系总览:一份研究任务会产生哪些日志

gpt-researcher 的日志体系分为用户日志(User Logs)与开发者日志(Developer Logs)两大阵营,二者定位完全不同:

维度用户日志(User Logs)开发者日志(Developer Logs)
存放位置outputs目录logs目录
文件格式JSON(.json),带缩进、易读.log文本 +.json结构化
面向对象使用 Web 界面的终端用户开发者、调试者
内容特征含 emoji 与通俗描述,覆盖事件流、图片、来源、报告全文技术细节,无 emoji、无简化语言,聚焦子查询与数据量
获取方式outputs目录内直接查看,或报告页点击 "Download Logs" 按钮运行目录下的logs文件夹

用户日志的文件名格式为task_{时间戳}_{任务哈希}.json。从 server_utils.py 的sanitize_filename实现可以看到,任务哈希是任务文本的 MD5 前 10 位,配合int(time.time())时间戳,确保每次研究任务生成的日志文件名唯一且不含特殊字符,便于在outputs目录中按任务定位。

开发者日志的命名遵循research_{YYYYMMDD_HHMMSS}.logresearch_{YYYYMMDD_HHMMSS}.json,时间戳精确到秒,同一时刻启动的.log.json一一对应,见 logging_config.py 的setup_research_logging

需要特别说明:用户日志是"增量实时写入"的。Web 服务启动时会在 app.py 中os.makedirs("outputs", exist_ok=True)并挂载/outputs静态目录;每次研究任务开始时,CustomLogsHandler会先写入一个空的 JSON 骨架(含timestampeventscontent三部分),随后每产生一个事件就重写一次文件(server_utils.py)。这意味着研究进行中打开该 JSON 文件,就能看到实时推进的事件流

二、用户日志 JSON 结构深度拆解

用户日志是一个 JSON 文件,包含一次研究任务中发生的所有事件(events)的列表。顶层结构如下:

{ "timestamp": "2026-09-09T19:22:26.123456", "events": [], "content": { "query": "", "sources": [], "context": [], "report": "", "costs": 0.0 } }

这一骨架由 server_utils.py 初始化写入,五个content字段在事件流推进过程中会被逐步填充:query记录研究主问题,sources记录被采纳的来源 URL 列表,context记录最终聚合的研究上下文,report存放生成的报告文本,costs记录研究总成本。

2.1 顶层字段说明

  • timestamp:格式为YYYY-MM-DDTHH:MM:SS.ffffff,即 ISO 8601 格式。顶层timestamp表示日志文件本身的生成时间;而events数组中每个事件各自的timestamp表示该事件实际发生的时间,二者含义不同,用于还原研究全程的时间线。
  • events:包含该研究任务所有已记录事件的数组,每个事件对象结构如下:
{ "timestamp": "2026-09-09T19:22:26.456789", "type": "event", "data": { "type": "logs", "content": "scraping_content", "output": "📄 Scraped 12 pages of content", "metadata": null } }

2.2 事件对象(Event Object)字段

字段说明
timestamp事件发生的具体时间(ISO 格式),用于按顺序追踪动作序列
type目前恒为"event",是事件类型的占位标记
data.type事件的广义类型,目前主要为"logs"
data.content工具正在做什么的描述符,例如starting_researchrunning_subquery_researchscraping_content
data.output更详细的消息,通常包含 emoji 等可视化指示,会实时推送给用户
data.metadata事件的附加数据,可为null,或包含相关信息数组(如 URL 列表、子查询列表)

2.3 事件写入的源码依据

事件对象由CustomLogsHandler.send_json在收到type == "logs"的数据时追加写入,其余类型(如querysourcesreport)则更新到content区块(server_utils.py)。而事件数据的源头,来自研究流程中的stream_output调用。其签名与核心逻辑见 actions/utils.py:

async def stream_output( type, content, output, websocket=None, output_log=True, metadata=None ): if (not websocket or output_log) and type != "images": logger.info(f"{output}") if websocket: await websocket.send_json( {"type": type, "content": content, "output": output, "metadata": metadata} )

从源码可以推断:当websocket被传入CustomLogsHandler实例时,send_json会同时完成两件事——把数据实时推送给前端 WebSocket 显示,并把type == "logs"的事件落盘到outputs下的用户日志文件。这正是"事件日志 = 前端实时进度条 + 可下载 JSON 日志"双通道共享同一数据源的设计。

三、26 类事件类型全解(content 字段语义)

以下是文档中列出的全部content类型。按研究流程的阶段分组解读,括号内为对应output/metadata行为:

3.1 研究启动与代理选择阶段

content 值含义output / metadata
starting_research研究流程对给定任务正式启动output包含研究查询的完整文本
agent_generated指示本次任务使用了哪个代理(agent)output显示代理名称
planning_research工具先浏览以理解请求范围并开始规划output提示正在浏览或进行初始规划

源码佐证:在 researcher.py 中,这三个事件在conduct_research阶段被依次发出:starting_research携带用户查询原文,agent_generated携带代理名称;planning_research则由_plan_research_outlineget_search_results前后两次触发(首次为"Browsing the web...",随后为"Planning the research strategy...")。

3.2 子查询生成与执行阶段

content 值含义output / metadata
subqueries工具已生成将用于研究的子查询output列出全部子查询;metadata为子查询字符串数组
running_subquery_research正在执行某个特定子查询的研究output显示正在运行的子查询

源码佐证:子查询生成后,stream_output("logs", "subqueries", ..., True, sub_queries)会把子查询列表同时写入outputmetadata(researcher.py);随后对每个子查询调用_get_context_by_subquery,并在其中发出running_subquery_research事件(researcher.py)。

3.3 来源采集与抓取阶段

content 值含义output / metadata
added_source_url某 URL 被识别为相关信息来源output带勾选 emoji 的 URL;metadata包含实际添加的 URL
researching工具正在跨多个来源积极检索信息output为通用提示消息
scraping_urls开始从一组 URL 抓取内容output提示将要抓取的 URL 数量
scraping_content成功从 URL 抓取内容output显示成功抓取的页面数
scraping_images抓取过程中识别并选择了图片output显示新选图片数与图片总数;metadata为所选图片 URL 数组
scraping_complete对 URL 的抓取过程已完成output提示抓取完成

源码佐证:added_source_url_get_context_by_subquery中筛选出新 URL 后触发,metadata携带该 URL(researcher.py);scraping_urlsscraping_content则由抓取函数在 browser.py 中发出,scraping_content的 output 中即包含len(scraped_content)抓取成功页数。值得注意的是,scraping_images事件与stream_outputtype != "images"的条件相互配合——图片数据不会写入控制台日志,但仍会通过 WebSocket 推送并在用户日志中留下记录。

3.4 上下文构建阶段

content 值含义output / metadata
fetching_query_content正在基于特定查询获取内容output显示正在获取内容的查询
subquery_context_window为给定子查询创建上下文窗口,辅助更细致的研究output提示子查询上下文窗口已创建
research_step_finalized某一步骤的研究部分已定稿output提示研究完成,并附研究总成本
relevant_contents_context为相关内容创建了上下文窗口output提示相关内容上下文窗口已创建

源码佐证:research_step_finalized的 output 会附带💸 Total Research Costs: $xxx的成本信息(researcher.py),说明该事件同时承担了阶段成本汇报的功能。

3.5 报告撰写阶段

content 值含义output / metadata
generating_subtopics/subtopics_generated生成/已完成生成报告子主题output分别提示生成中/已完成
writing_introduction/introduction_written开始/完成撰写报告引言output分别提示开始/完成
generating_draft_sections/draft_sections_generated开始/完成生成报告草稿章节output分别提示生成中/已完成
fetching_relevant_written_content正在获取与报告相关的已写内容output提示正在获取相关内容
writing_report/report_written开始/完成将研究编译为报告output分别提示生成已开始/已结束
writing_conclusion/conclusion_written开始/完成撰写报告结论output分别提示撰写中/已完成

源码佐证:writing_report事件在 writer.py 中由write_report触发,携带用户查询文本;而在 agent.py 中,报告撰写前后还会通过_log_event("research", step="writing_report" / "report_completed", ...)记录existing_headerscontext_sourceavailable_images_count以及报告长度、嵌入图片数等开发者侧指标。

3.6 用事件流还原一次完整研究

结合时间戳,上述事件按如下顺序串联出完整的研究流水线:

starting_research → agent_generated → planning_research → subqueries → (对每个子查询) running_subquery_research → added_source_url → scraping_urls → scraping_content → scraping_images → scraping_complete → subquery_context_window → research_step_finalized → generating_subtopics → subtopics_generated → writing_introduction → introduction_written → generating_draft_sections → draft_sections_generated → fetching_relevant_written_content → relevant_contents_context → writing_report → report_written → writing_conclusion → conclusion_written

四、日志的四大实战用途

文档明确了用户日志的四种核心用途,对应不同角色与场景:

  • 故障排查(Troubleshooting):当研究结果不符合预期时,日志能帮你还原工具执行的确切步骤——用了哪些查询、访问了哪些来源、报告是如何生成的,快速定位"跑偏"环节。
  • 过程透明(Transparency):日志完整记录了访问过的 URL、选中的图片、报告构建方式,让每次研究行为可审计。
  • 流程理解(Understanding the Process):日志提供了工具整体工作流及各步骤形态的概览,是新人理解 gpt-researcher 研究管线最直接的素材。
  • 可复现性(Reproducibility):通过时间戳与事件序列,用户可以精确追溯整个研究过程,复现同样的问题设定与来源路径。

从 AccessReport.tsx 可确认,前端报告结果页确实提供了 "Download Logs" 按钮(<a>Download Logs</a>),点击即可下载本次研究对应的用户日志 JSON 文件。

五、开发者日志:.log.json双格式

除了面向用户的日志文件,应用还会为开发者生成两类日志,均位于logs目录。其初始化逻辑集中在setup_research_logging(logging_config.py):

5.1 基础日志文件(.log

  • 格式:纯文本,每行一条日志条目。
  • 内容
    • 毫秒级精度的时间戳
    • 日志级别:通常为INFO,复杂部署中可能包含DEBUGWARNINGERROR
    • 模块名(如research);
    • 各类流程的描述性消息,覆盖:研究任务开始与结束、正在执行的 Web 搜索、研究规划、生成的子查询及其结果、抓取数据的大小、子查询找到的内容大小、最终聚合的全部上下文大小。
  • 开发者用途
    • 实时监控:随时观察工具活动;
    • 调试:通过操作的时间顺序与收集内容的体量定位问题;
    • 性能分析:利用时间戳量化特定操作的耗时,识别瓶颈;
    • 高层概览:快速查看工具执行了哪些步骤及采集内容的规模。
  • 与用户日志的关键区别:结构更松散,适合开发者实时查看;包含非开发用户通常不需要的技术信息;没有 emoji 和简化语言;不包含图片采集信息。

源码佐证:.log文件由logging.FileHandler写入,格式化模板为'%(asctime)s - %(name)s - %(levelname)s - %(message)s',并同时挂载控制台StreamHandler实现"文件 + 终端"双输出,research_logger.propagate = False防止向根日志器重复传播(logging_config.py)。研究过程中的业务埋点通过logger.info_log_event中的兜底research_logger.info(...)(agent.py)写入。

5.2 JSON 日志文件(.json

  • 格式:结构化 JSON。
  • 内容
    • 与所有日志文件一致的时间戳;
    • type字段,取值包括:
      • sub_query:包含子查询字符串与scraped_data_size(抓取数据大小);
      • content_found:包含sub_querycontent_size(找到的内容大小);
    • content字段:给出整体研究的快照,可包含该任务研究得到的最终上下文与来源。
  • 开发者用途
    • 详细分析:查看工具运行的细节,尤其是子查询与其研究结果;
    • 过程理解:查看运行了哪些子查询、每个子查询生成了多少内容,助力调试与理解;
    • 数据检查:审阅生成的查询与内容大小。
  • 与用户日志的关键区别:高度结构化、聚焦子查询执行及其结果(特别是采集信息的体量);不包含简化语言、emoji 或高层解释;不包含整体上下文与图片信息,主要关注子查询过程。

源码佐证:JSONResearchHandler在初始化时即构建{"timestamp", "events", "content"}骨架,log_event将事件追加到events数组并调用_save_json落盘,update_content则更新content区(logging_config.py)。get_json_handler通过getattr(logging.getLogger('research'), 'json_handler', None)获取处理器(logging_config.py),供研究者模块在子查询完成后记录scraped_data_sizecontent_size等体量指标。

六、日志读取与解析速查(结合测试验证)

仓库的测试用例 test_logging.py 直接验证了用户日志的写入契约,可作为解析日志时的"官方参考":

handler = CustomLogsHandler(mock_websocket, "test_query") test_data = {"type": "logs", "message": "Test log message"} await handler.send_json(test_data) # 读取日志文件验证 with open(handler.log_file, 'r') as f: log_data = json.load(f) assert len(log_data['events']) == 1 assert log_data['events'][0]['data'] == test_data

测试同时验证了content更新行为:当发送非logs类型数据(如querysourcesreport)时,会更新log_data['content']对应字段(test_logging.py)。据此可以总结出解析规则:

  1. events数组= 时间线:遍历其中事件,按data.content分类统计各阶段执行情况;
  2. content区块= 结果快照:直接读取querysourcescontextreportcosts获取研究成果;
  3. 按时间戳排序= 流程还原:将events[].timestamp按序排列即可复现研究步骤与耗时。

七、日志体系常见问题与最佳实践

7.1 找不到outputs/logs目录?

outputslogs目录均在运行时按需创建:outputs由 Web 服务启动时的os.makedirs("outputs", exist_ok=True)CustomLogsHandler初始化创建(server_utils.py);logssetup_research_logging中的Path("logs").mkdir(exist_ok=True)创建(logging_config.py)。二者均为相对当前运行目录创建,因此日志文件会出现在启动服务/运行脚本时的工作目录下

7.2 何时选择用户日志 vs 开发者日志?

  • 排查"研究结论为什么与预期不符"、追溯访问了哪些 URL、确认图片选择情况 → 用outputs下的用户日志(或报告页 "Download Logs");
  • 量化子查询抓取/内容体量、分析阶段耗时瓶颈、查看原始技术细节 → 用logs目录下的开发者日志.log看时序与级别,.json看体量指标)。

7.3 实时监控技巧

  • Web 界面研究过程中,stream_output会同时把事件推送到前端并写入用户日志,因此研究未结束时打开outputs下的 JSON 文件即可看到实时事件流
  • 开发者.log文件为追加写入(FileHandler默认模式),配合tail -f即可实时观察INFO/WARNING/ERROR级别的流程输出。

7.4 关于文档与版本演进的提示

文档明确提示:随着新功能开发,报告与事件类型可能随版本演进而变化。因此本文的事件清单(26 类content)以当前仓库源码为准,在旧版本或未来版本中若遇到未收录的事件名,应优先以日志中实际的data.content和该版本源码中的stream_output调用点为准进行解读。

八、小结

gpt-researcher 的日志体系用"一份用户日志 + 两份开发者日志"覆盖了研究流程的可观测性需求:

  • 用户日志(outputs以 JSON 事件流的形式,把从starting_researchconclusion_written的全过程透明化,兼顾前端实时展示与事后下载追溯,是排查"研究为什么这样做"的一手证据;
  • 开发者日志(logs.log文本与.json结构化两种形态,提供毫秒级时间戳、日志级别、模块名与子查询数据体量指标,服务于实时监控、调试与性能分析;
  • 事件流、content快照与测试契约(test_logging.py)共同构成了可靠的解析基础,让研究者既能还原完整时间线,也能一键获取最终查询、来源、上下文、报告与成本。

理解这套日志体系后,无论你是想排查一次失败的研究、分析某类报告类型的时间开销,还是想复现某次调研的完整来源链,都能在日志中快速找到答案。

【免费下载链接】gpt-researcherAn autonomous agent that conducts deep research on any data using any LLM providers项目地址: https://gitcode.com/GitHub_Trending/gp/gpt-researcher

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询