openai-agents-python 沙箱实战:基于 SandboxAgent 构建带源码引用的 10-K 金融问答 Agent
2026/9/12 14:47:55 网站建设 项目流程

openai-agents-python 沙箱实战:基于 SandboxAgent 构建带源码引用的 10-K 金融问答 Agent

【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python

导读

本文围绕 openai-agents-python 仓库中的dataroom_qa沙箱教程示例,完整讲解如何构建一个"检索优先"(retrieval-first)的金融问答 Agent:它在一个有界的合成 10-K 年报语料包上运行,借助沙箱内的bash与文件检索能力查找证据,并以可点击的源码级引用(文本按行号、PDF 按页码)回答财务问题。读完本文,你将掌握沙箱 manifest 的加载方式、SandboxAgent的配置要点、Unix-local 与 Docker 两种运行模式,以及如何为自己的金融语料设计可验证的引用协议。

一、示例目标:在合成 10-K 数据包上做有依据的金融问答

examples/sandbox/tutorials/dataroom_qa/README.md开宗明义:本示例的目标是"Answer grounded financial questions over a synthetic 10-K packet",即基于一个合成 10-K 数据包回答有依据(grounded)的财务问题。

这里的"数据包"使用合成公司数据,但文档形态完全模仿真实年报节选:

  • MD&A(管理层讨论与分析)文本使用 10-K 的Part II, Item 7结构;
  • 财务报表 PDF 与脚注文本使用Part II, Item 8结构。

合成公司名为HelioCart, Inc.,财务口径保持一致性设计(例如 MD&A 中注明"净收入"与"收入"可互换使用、分部脚注注明"订阅与交易平台收入"与"平台分部收入"是同一指标),这些细节都用于考验 Agent 在交叉引用多个文件时是否能保持口径统一。

该模式的价值在于:在一个有界的金融语料上做检索优先的 Agent 工作流,让每一个指标和解释都与源文件保持绑定。相比直接依赖模型记忆作答,这种设计天然支持审计、追责与二次核验。

二、数据准备:运行 fixture 生成器

示例的输入数据并不是预置在仓库中的静态文件,而是由生成器脚本产出。从仓库根目录执行:

uv run python examples/sandbox/tutorials/data/dataroom/setup.py

examples/sandbox/tutorials/data/dataroom/setup.py负责生成全部 8 个 fixture 文件:

文件内容对应 10-K 章节
10-k-mdna-overview.txt收入、毛利率、营业利润的年度对比Part II, Item 7
10-k-mdna-liquidity.txt经营现金流、资本开支、自由现金流Part II, Item 7
10-k-note-segments.txt分部收入(Platform / Services)Part II, Item 8, Note 4
10-k-note-geography.txt地区收入(Americas / EMEA / APAC)Part II, Item 8, Note 5
10-k-note-balance-sheet.txt现金、递延收入等资产负债表指标Part II, Item 8, Note 7
10-k-statements-of-operations.pdf经营成果表(净收入/毛利/营业利润)单页 PDF
10-k-balance-sheets.pdf资产负债表(现金/应收/递延收入)单页 PDF
10-k-statements-of-cash-flows.pdf现金流量表(经营现金流/资本开支/自由现金流)单页 PDF

2.1 fixture 生成器的实现要点

生成器本身就是一个值得学习的小工具,它展示了如何在不依赖任何第三方 PDF 库的情况下手工构造可检索的合成 PDF:

  • write_plain_pdf()直接按 PDF 1.4 规范手工拼装对象(Catalog / Pages / Page / Contents / Font),用 Helvetica Type1 字体逐行写出文本,并生成合法的xref交叉引用表与trailer
  • write_financial_pdf()将表格行用" | ".join(row)拼成纯文本行写入 PDF,保证每行内容可被文本抽取工具读取;
  • pdf_escape()负责转义 PDF 字符串中的\()等特殊字符。

从源码结构看,这样设计的目的是让每个 PDF 恰好是单页纯文本,方便 Agent 用pypdf等工具按"页码"定位证据,与 README 中约定的n引用格式一一对应。

三、运行方式:Unix-local 与 Docker 双模式

3.1 前置条件

运行前需要在 shell 环境中设置OPENAI_API_KEY,并先生成 fixture 数据(见上一节)。main.py启动时会校验数据文件是否存在,若缺失会直接提示先运行setup.py

3.2 Unix-local 模式(默认)

uv run python examples/sandbox/tutorials/dataroom_qa/main.py

这是默认运行方式,直接在本机以 Unix-local 沙箱执行,无需 Docker。首次回答完成后,示例会保持沙箱会话打开,通过 Rich 渲染的交互提示符接收追问(终端会提示 "Enter follow-up prompts. Press Ctrl-D or Ctrl-C to finish.")。

3.3 一次性运行(非交互)

uv run python examples/sandbox/tutorials/dataroom_qa/main.py --no-interactive

传入--no-interactive后,脚本只执行预设的问答回合并退出,适合 CI、批量验证或自动化场景。

3.4 Docker 模式

要复用同一个 manifest 在 Docker 中运行,需要先构建共享教程镜像,再传入--docker

docker build --tag sandbox-tutorials:latest examples/sandbox/tutorials uv run python examples/sandbox/tutorials/dataroom_qa/main.py --docker

examples/sandbox/tutorials/Dockerfile揭示了镜像的依赖构成:

  • 基础镜像python:3.14-slim,内置uv
  • 安装ca-certificatesgitpoppler-utils(PDF 文本抽取)、ripgrep(证据检索);
  • 通过uv pip install安装pypdf,用于解析 PDF 文本。

这些工具正是沙箱 Agent 在运行时执行检索命令所需的运行时依赖。

3.5 命令行参数速查

main.py通过argparse暴露了 4 个参数:

参数默认值说明
--modelgpt-5.4-mini使用的模型名称
--question见下文默认问题发送给 Agent 的提示词
--dockerFalse使用 Docker 沙箱替代 Unix-local
--imagesandbox-tutorials:latest--docker搭配使用的镜像名
--no-interactiveFalse只跑脚本回合,跳过终端追问

默认问题(DEFAULT_QUESTION)本身就是一份很好的"追问设计"范例,覆盖了利润表与现金流两个维度的交叉验证:

"How did revenue, gross margin, operating income, and operating cash flow change in FY2025 versus FY2024, and which segment contributed the most revenue?"

要回答这个问题,Agent 至少需要检索 MD&A 概览(收入/毛利率/营业利润)、流动性 MD&A(经营现金流)、分部脚注(收入贡献最大的分部),并核对 PDF 中的经营成果表,体现了多文件交叉检索的设计意图。

四、核心实现拆解:main.py 的五层结构

examples/sandbox/tutorials/dataroom_qa/main.py是示例的全部实现,其结构可以拆解为五个关键环节:

4.1 第 1 层:指令加载

README 中 "How instructions are loaded" 一节说明:启动时,wrapper 将本文件夹的AGENTS.md加载进 Agent 指令。在代码中,AGENTS.md的内容以dedent字符串硬编码在main.py中(AGENTS_MD常量),内容为:

# AGENTS.md Answer the user's financial question using only the synthetic 10-K packet in `data/`. ## Evidence & citations - Cite every material claim with markdown links in these formats (no bare links): - `1` for text sources - `2` for PDF sources (each synthetic PDF is one page) - Use `rg` and `sed` to find and quote exact evidence; do not use `data/setup.py`. Keep the final answer direct and finance-oriented.

这段指令有三个关键约束:

  1. 只用data/目录内的合成 10-K 数据包作答,防止模型凭记忆编造;
  2. 每一条实质性主张都必须带引用,且规定了两种精确格式(文本按行号、PDF 按页码),禁止裸链接;
  3. 明确工具使用方式:用rgsed查找并引用精确证据,且禁止调用data/setup.py(防止 Agent 重新生成或篡改语料)。

4.2 第 2 层:manifest 构建

manifest = Manifest( entries={ "AGENTS.md": File(content=AGENTS_MD.encode("utf-8")), "data": LocalDir(src=DATAROOM_DATA_DIR), } )

这对应 README 中 "builds a hard-coded manifest that maps the shared SEC packet ... into the sandbox asdata/..." 的描述:将共享的 SEC 数据包从examples/sandbox/tutorials/data/dataroom/映射进沙箱的data/目录,同时在沙箱根目录注入一份AGENTS.md

其中LocalDir来自agents.sandbox.entries,表示把宿主机目录作为整体挂载进沙箱;File用于在沙箱内直接创建文本文件。Manifest类型定义在 src/agents/sandbox/manifest.py,它支持文件、目录、挂载、快照等多种条目类型,并在校验失败时抛出InvalidManifestPathError

4.3 第 3 层:SandboxAgent 配置

agent = SandboxAgent( name="Dataroom Analyst", model=model, instructions=AGENTS_MD, capabilities=[Shell()], )

SandboxAgent定义在 src/agents/sandbox/sandbox_agent.py,是Agent的沙箱专用子类。从源码注释可以确认一个重要设计原则:沙箱的传输细节(client、client options、会话)不存储在 Agent 上,而是在运行时通过RunConfig(sandbox=...)注入。这使 Agent 定义与沙箱环境解耦,同一个 Agent 可以复用于不同后端。

capabilities=[Shell()]为该 Agent 启用了 bash shell 能力(Shell来自agents.sandbox.capabilities)。README 的 "Demo shape" 一节明确示例的运行时原语就是sandbox-local bash / file search——即模型通过沙箱内的 bash 工具执行rg/sed等命令来完成证据检索,而非依赖云端 RAG 工具。

4.4 第 4 层:沙箱客户端与会话

client, sandbox = await create_sandbox_client_and_session( manifest=manifest, use_docker=use_docker, image=image, )

该辅助函数定义在 examples/sandbox/tutorials/misc.py:

  • 非 Docker 模式:UnixLocalSandboxClient()(来自agents.sandbox.sandboxes.unix_local),在宿主机本地创建沙箱;
  • Docker 模式:通过docker.from_env()构造DockerSandboxClient,并传入DockerSandboxClientOptions(image=image)
  • build_docker_environment()会探测当前 Docker 上下文(包括 Docker Desktop、Colima 等),自动设置DOCKER_HOST,避免硬编码某个具体守护进程提供商。

会话创建后进入async with sandbox:生命周期块,finally中调用await client.delete(sandbox)确保清理。

4.5 第 5 层:流式运行与交互循环

result = Runner.run_streamed( agent, conversation, max_turns=20, run_config=RunConfig( sandbox=SandboxRunConfig(session=sandbox), tracing_disabled=True, workflow_name="Dataroom Q&A example", ), ) return await print_streamed_result(result)

要点:

  • 使用Runner.run_streamed进行流式运行,max_turns=20限制最大工具调用轮数,防止检索循环失控;
  • 通过RunConfig(sandbox=SandboxRunConfig(session=sandbox))已创建的沙箱会话注入本次运行,对应第 4.3 节提到的"运行时注入"设计;
  • tracing_disabled=True关闭 tracing,workflow_name标记工作流名;
  • print_streamed_result遍历result.stream_events(),用print_event(定义于misc.py,基于 Rich 面板渲染)逐条展示 reasoning、工具调用(含exec_command的 bash 语法高亮)、工具输出、消息输出,最后以绿色面板打印最终回答;
  • 首轮完成后,run_interactive_loop进入追问循环(除非--no-interactive),每次追问都会带着完整对话历史再次走run_turn

五、预期产物:流式回答中的源码级引用

README 的 "Expected artifacts" 一节给出了可验证的验收标准:

  • 在流式 Agent 回答中得到带直接引用的答案
  • 引用遵循固定格式:
    • 文本摘录:n—— 精确到行号
    • 合成 PDF(单页):n—— 精确到页码

这种"每个数字背后都有源文件坐标"的输出形态,正是金融问答场景最看重的可审计性:读者可以把回答中的任何一个指标(如 FY2025 收入 1,284 百万美元、毛利率 71.4%、营业利润 186 百万美元)直接回溯到对应文件的对应行/页,快速完成人工核验。

六、深入理解:沙箱检索机制与镜像依赖

6.1 为什么用沙箱内 bash 检索而非预置 RAG

main.py可以看到,示例没有使用file_search等云端检索工具,而是刻意把检索能力限定为sandbox-local bash/file searchcapabilities=[Shell()])。结合 examples/sandbox/tutorials/Dockerfile 中预装ripgreppoppler-utilspypdf的事实可以推断,设计意图是:

  1. 让检索逻辑完全透明、可复现——rg/sed是确定性的命令行工具;
  2. 让 Agent 的检索行为在受控沙箱内执行,与宿主机隔离;
  3. 让 PDF 文本抽取能力(pypdf)与纯文本检索(rg)在同一声明式镜像内就绪,Docker 与 Unix-local 两种模式行为一致。

6.2 相关源码入口

深入理解本示例可继续阅读以下文件:

  • examples/sandbox/tutorials/dataroom_qa/main.py:示例主程序(manifest、Agent、流式运行、参数解析);
  • examples/sandbox/tutorials/data/dataroom/setup.py:合成 10-K 语料生成器;
  • examples/sandbox/tutorials/misc.py:沙箱客户端/会话创建、Rich 事件渲染、交互循环;
  • examples/sandbox/tutorials/Dockerfile:教程共享 Docker 镜像(ripgrep / poppler-utils / pypdf);
  • src/agents/sandbox/sandbox_agent.py:SandboxAgent定义与"运行时注入沙箱"的设计说明;
  • src/agents/sandbox/manifest.py:Manifest类型与校验逻辑;
  • src/agents/sandbox/sandboxes/unix_local.py 与 src/agents/sandbox/sandboxes/docker.py:两种沙箱后端实现。

七、小结:从示例迁移到自己的金融语料

dataroom_qa虽然针对合成数据,但其模式可直接迁移到真实场景:

  1. 语料准备:把任意有界的金融文档集(年报、招股书、季报)整理为文本或单页 PDF,保证可被rgpypdf检索;
  2. manifest 映射:用Manifest把宿主机语料目录映射进沙箱的data/,并注入一份约束明确的AGENTS.md
  3. 指令约束:在指令中强制"只用data/作答 + 每条主张必须带n引用";
  4. 能力裁剪:只开启Shell()能力,让 Agent 用确定性命令检索证据;
  5. 运行与验收:Unix-local 快速调试、Docker 保证环境一致,用--no-interactive接入自动化回归。

当你的业务需要"每一个数字都能被追溯到源文件"的可审计问答时,这个检索优先 + 沙箱执行 + 源码级引用的组合,就是一个开箱即用的参考实现。

【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python

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

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

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

立即咨询