Skip to content

fix(agent): allow tool responses to pass image content to next-round vision input - #7923

Open
lirenjie95 wants to merge 2 commits into
labring:mainfrom
lirenjie95:fix/agent-image-input-7827
Open

lirenjie95 wants to merge 2 commits into
labring:mainfrom
lirenjie95:fix/agent-image-input-7827

Conversation

@lirenjie95

@lirenjie95 lirenjie95 commented Oct 8, 2026 •

Copy link
Copy Markdown

Fixes #7827

背景

Agent 工具(如沙盒 get_file_url、workflow 工具节点、Agent V2 业务工具)返回图片后,下一轮模型请求无法把图片作为视觉输入,只能拿到文本链接。

改动内容

工具结果从「只有 response 字符串」扩展为「可选 text / image_url content parts」,并让整条工具链路支持主动产出图片:

  • 类型层:ChatCompletionToolMessageParamSchema.content 支持字符串或 ChatCompletionToolMessageContentPart[](text / image_url)。这是 FastGPT 内部扩展契约:供应商的 tool message 只接受字符串或文本 parts,出站时由 loadRequestMessages 把图片拆成这一轮 tool 结果之后的 user 视觉消息,不会把图片直接发给供应商。
  • 工具执行链路:AgentLoopToolExecutionResult / AgentLoopCoreToolRunResult / sandbox 工具结果新增可选 content 字段,逐层透传。
  • 工具入口:
    • workflow 工具节点(ToolCall)输出 { content: [{ type: 'image_url', image_url: { url } }] } 时提取为 tool content;
    • Agent V2 业务工具(插件 / MCP / HTTP)的输出同样提取,与 response 一起交给 agent-loop;
    • 沙盒 get_file_url 对图片文件在完整文件列表文本之外额外返回 image_url parts。
  • 提取规则:复用共享的 ChatCompletionToolMessageContentPartSchema 校验,任一 part 不合法(如 detail 不是 auto/low/high、URL 为空)整体回退为普通文本。
  • fastAgent:tool message 使用工具返回的 content parts;同一轮多个 tool call 的图片统一追加在全部 tool 结果之后;压缩后的文本写回 tool 消息,展示与实际发送一致,交互恢复路径复用同一套转换。
  • piAgent:tool content 转换为 pi 的 TextContent / ImageContent(图片取回 base64,失败降级占位文本),并按 useVision 做能力过滤。
  • 字符串响应不再做「整串恰好是图片链接」的后缀猜测:旧输出一律走文本路径,只有显式 image_url part 才进视觉。

测试

  • packages/global:chat adapt 相关用例
  • packages/service:tool 消息 content 加载(vision 开关、base64 归一化、空数组兜底 none、同一轮多工具结果的分组顺序)、workflow / Agent V2 工具 content 提取、fastAgent 压缩与交互恢复、piAgent 视觉能力过滤

本地 tsc -p tsconfig.json 与相关 vitest 用例全部通过。

兼容性

  • 工具未返回 content 时行为与之前一致(纯文本响应)
  • tool content 为数组时,对不支持 vision 的模型自动过滤图片、单文本折叠回字符串
  • 旧工具返回的字符串 / 业务对象保持原意,不会被改写成图片输入

联动 PR

插件侧配套实现:labring/fastgpt-plugin#563

lirenjie95 added a commit to lirenjie95/FastGPT that referenced this pull request Oct 8, 2026
…gent loop

Address code review feedback on labring#7923:

- convertToolResponseContentToPiContent accepts a useVision option and degrades
  image parts to [Image: url] placeholders when the model lacks vision support,
  matching the capability filtering applied by loadRequestMessages.
- run.ts and build.ts derive useVision from the llm params/vision config and
  pass it to every conversion site; empty content arrays now fall back to the
  text response instead of being dropped.
- promptToolCallMessageRewrite handles null tool content and preserves
  image-only tool messages as [Image: url] text instead of silently dropping.
- extractAgentLoopCoreToolContent rejects image_url parts with empty urls.
- Adds unit tests for the new behavior in piAgentMessage.test.ts,
  toolCall.test.ts, and workflowToolRunner.test.ts.
@pull-request-size pull-request-size Bot added size/XL and removed size/L labels Oct 8, 2026
@c121914yu c121914yu self-assigned this Oct 10, 2026
@c121914yu

Copy link
Copy Markdown
Collaborator

审查基于当前提交 dd0e31ee906a09c103b2da74f1caa244d40299b0,并联查了 plugin PR #563。

本次兼容要求是:工具响应在原有字符串基础上增加显式的 text / image_url 复合内容支持;旧字符串、旧插件业务输出及原有文本/文件链接信息应保留原意。按此要求,以下问题需要处理:

  1. [P1] FastAgent 把图片直接放在 role: tool 中发送,不符合 OpenAI Chat Completions 的消息契约。

    loadRequestMessages,750–778 行 保留了 tool 角色,并把 image_url parts 放回其 content;常规 tool-call 路径会继续把该结构传给模型接口。OpenAI 官方接口定义中的 tool content 仅支持字符串或文本 parts,不能直接包含 image_url;本地安装的 OpenAI SDK 类型也一致。内部可以支持复合结果,但 provider 适配时应保留 tool 文本,并将图片转为接口支持的视觉消息,例如在对应的 tool results 后追加 user 图片消息。此项基于接口契约和请求调用链验证,未进行真实模型 API 调用。

  2. [P1] 沙箱返回图片时,会覆盖原有文件列表,丢失其他文件及链接信息。

    getFileUrl.tool.ts,71–91 行 的 response 包含全部文件的 filename/fileUrl,而新增 content 只包含图片;FastAgent,633–637 行 又优先采用 content。实际调用代码并 mock 预览服务后,get_file_url(["/workspace/chart.png", "/workspace/data.csv"]) 的下一轮输入仅剩 chart 图片,CSV 和原文件列表均丢失;FastAgent 关闭视觉后还会将纯图片 content 过滤为 none。复合内容应包含承载原 response 的 text part,再追加图片,确保新增视觉信息不会替换旧内容。

  3. [P2] 自动把旧字符串猜成图片,会改变原有 tool response 的语义。

    getToolResponseContent,42–50 行 将图片 URL 字符串改为 image_url;因此旧工具原本返回的文本链接会切换成图片输入。现有检测也并非“整串恰好一个 URL”:实测 https://example.com/a.png?x=1\nThis is explanatory text(其中 \n 为实际换行)和两行图片 URL 都会通过 isExactImageUrl,最终整串作为图片地址。按本次兼容要求,应让旧字符串继续走文本路径,仅由显式 image_url part 开启视觉输入,而不是通过后缀猜测。

  4. [P2] 复合内容绕过了已经执行的工具响应压缩。

    FastAgent,579–637 行 先压缩 response 并记录压缩用量,随后只要 toolContent 非空就发送原 parts,丢弃 toolFinalResponse。内存复现中,压缩器执行一次,工具事件显示 SUMMARY,下一轮请求却仍包含 UNCOMPRESSED CONTENT。较大的复合文本会因此失去上下文控制,并可能产生无效压缩费用。应对最终发送的 text parts 应用压缩、保留图片,并让展示结果及计费记录与实际发送内容一致。

  5. [P2] 交互恢复分支没有接入复合工具结果。

    FastAgent,292–351 行 从 onRunInteractiveTool 只取 response,恢复 requestMessages 和 transcript 时也只回填该字符串。内存复现中,同一个包含 text/image_url 的返回值,普通路径保留 parts,恢复路径下一轮仅收到 RAW RESULT。需要将正常执行与交互恢复统一到相同的结果转换逻辑,覆盖恢复后的模型输入及 transcript。

  6. [P2] Pi 沙箱分支遗漏视觉开关参数,导致关闭视觉仍下载图片。

    Pi build.ts,336–338 行 调用 convertToolResponseContentToPiContent 时未传入本函数已计算的 useVision,而转换器默认开启视觉。实际构建工具并 mock 图片下载后,在 useVision=false、model.config.vision=false 时仍发生一次下载,生成了原生 image part,没有走其他分支的文本降级。即使后续 provider 再过滤图片,此处也已发生不必要下载并丢失文本兜底;应与其余分支一样传入 { useVision }。

另外,plugin #563 将原本任意业务输出中的 content 字段收紧为 parts-only,会直接破坏旧插件返回值,具体复现和建议已提交到 plugin 审查评论。

验证范围:主库 5 个相关测试文件共 167 项通过;plugin 2 个相关测试文件共 26 项通过。上述边界问题另用实际函数及 mock 依赖的内存实验验证;现有通过的测试尚未覆盖这些回归路径。未运行全量测试,未修改源码。

@lirenjie95

Copy link
Copy Markdown
Author

按意见都改了一遍:

  • tool 消息里不再放图片了,loadRequestMessages 会把图片拆成 tool 结果后面的 user 视觉消息,文本留在 tool 消息里。
  • getFileUrl 的 content 现在固定带一个 text part(完整 response 的 JSON)+ 图片 part,非图片文件不会再丢,关掉视觉也不会变成 none。
  • 按后缀猜图片那段删掉了,字符串一律当文本,只有显式 image_url part 才进视觉。
  • fastAgent 压缩后的最终文本会写进 tool 消息的 text part,展示和实际发送一致,交互恢复路径也统一了。
  • piAgent 沙箱分支补上了 { useVision }。

测试都补了,本地 service/global 的 vitest 和 tsc 全过。

lirenjie95 added a commit to lirenjie95/FastGPT that referenced this pull request Oct 13, 2026
…gent loop

Address code review feedback on labring#7923:

- convertToolResponseContentToPiContent accepts a useVision option and degrades
  image parts to [Image: url] placeholders when the model lacks vision support,
  matching the capability filtering applied by loadRequestMessages.
- run.ts and build.ts derive useVision from the llm params/vision config and
  pass it to every conversion site; empty content arrays now fall back to the
  text response instead of being dropped.
- promptToolCallMessageRewrite handles null tool content and preserves
  image-only tool messages as [Image: url] text instead of silently dropping.
- extractAgentLoopCoreToolContent rejects image_url parts with empty urls.
- Adds unit tests for the new behavior in piAgentMessage.test.ts,
  toolCall.test.ts, and workflowToolRunner.test.ts.
@lirenjie95
lirenjie95 force-pushed the fix/agent-image-input-7827 branch 4 times, most recently from 3d9dcdf to 61287e3 Compare October 19, 2026 12:47
@lirenjie95
lirenjie95 force-pushed the fix/agent-image-input-7827 branch from 61287e3 to 7c303c8 Compare October 21, 2026 01:10
@lirenjie95
lirenjie95 force-pushed the fix/agent-image-input-7827 branch from 7c303c8 to 26b232e Compare October 25, 2026 11:52

@c121914yu c121914yu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex 自动审查:fix(agent): allow tool responses to pass image content to next-round vision input

重要发现

  1. [P1] 图片 user 消息打断同轮多个 tool call 的结果回填。 loadRequestMessages:831–840 对每条 tool 消息分别追加 user 图片消息。同一个 assistant 发起 A、B 两个工具调用,且 A 返回图片时,实际出站顺序变成 assistant(A,B) → tool A → user(image A) → tool B;旧版纯文本结果会连续回填 A、B。图片 user 消息出现时 B 尚未响应,破坏工具调用协议,严格校验的模型接口会拒绝下一轮请求。临时用例已复现该顺序;后续 mergeAssistantFieldMessages 不会修复它。应先连续写回同轮全部 tool 结果,再统一追加图片消息;可对照当前 pi-ai 0.67.3 对连续 toolResult 的分组处理。

  2. [P2] Agent V2 所选业务工具仍无法产出视觉输入。 新增提取仅在 workflowToolRunner:211 调用,覆盖 ToolCall 节点的 workflow 工具。Agent V2 的实际路径是 createWorkflowAgentToolProvider → getExecuteTool → dispatchTool / dispatchPlugin:插件返回的 { content: [image_url part] } 仍在 dispatchTool:237 整体 JSON.stringify,且 getExecuteTool:251 未返回 content。临时用例确认插件输出成功、response 中保留了图片 JSON,但结果没有独立 content;fastAgent 和 piAgent 最终都只收到字符串。这是本次新增能力遗漏的入口,Sandbox 路径的修复不能覆盖它。请在 Agent V2 业务工具/子工作流结果进入统一 runtime 前提取并逐层透传 content,同时保留原 response。

  3. [P2] 手写校验接受共享 schema 明确拒绝的图片 part。 extractAgentLoopCoreToolContent:123–135 只校验 type 和 URL,没有校验 image_url.detail,随后通过类型断言原样返回。例如 detail: 'ultra' 被共享 ChatCompletionToolMessageContentPartSchema 拒绝,却会被该函数接受;归一化又保留此字段,可能让下一轮请求被供应商拒绝。临时用例已确认这两种校验结果不一致。旧路径会把这类业务数据作为文本,本次识别后却将其提升为请求字段;应复用共享 schema 做 safeParse,失败时回退文本,并保留现有非空检查。

需求变更

为 #7827 补齐工具取图后的多模态输入:工具结果由仅有 response 字符串扩展为可选 text/image_url parts;Sandbox get_file_url 对图片额外生成 image_url。fastAgent 组合最终文本与图片,piAgent 下载图片并转换为 ImageContent;不支持视觉时过滤或降级。对外共享消息 schema 扩展,未修改 API 路由、数据库结构、权限判断或计费公式。

关键实现路径

  • Sandbox:get_file_url → createToolRunner → runSandboxTools → fastAgent/piAgent → 下一轮模型输入。
  • ToolCall:workflow 工具输出 → workflowToolRunner 提取 content → normalizeAgentLoopCoreToolRunResult → provider。fastAgent 出站时由 loadRequestMessages 将图片移到 user 消息;piAgent 将图片转为 base64。
  • 展示与历史:GPTMessages2Chats 只回折 text parts;图片本身不通过该链路持久化。prompt tool 重写保留文本及图片占位;真实请求会先经过 loadRequestMessages。
  • pro:指针未变;核查 workflowBuilder runner、CLI result 对 AgentLoopToolExecutionResult 的调用,现有仅返回 response 的调用保持兼容。

影响范围与越界判断

22 个文件的改动均围绕工具多模态结果、两种 provider、消息适配及局部测试,属于合理扩散。未发现无关依赖升级、部署配置修改、数据库迁移或权限/计费逻辑调整。主要风险是公共请求格式化引入的消息顺序回归,以及 Agent V2 入口覆盖不完整。

代码质量

结构化字段沿 domain/runtime 透传的方向清晰,Sandbox 下载失败与模型能力过滤有兜底。必须修复上述分组、入口覆盖与 schema 校验问题;现有新增测试主要验证单工具和 helper,未覆盖真实多工具消息序列及 Agent V2 插件入口。

代码风格一致性

新增类型使用 Zod 推导,核心 helper 有职责注释,测试位置与相邻实现一致。手写 content 校验应复用已有业务 schema,避免两套契约漂移。

非阻塞建议:type.ts:131–145 的注释把 image_url tool parts 描述为 OpenAI SDK 原生契约,与已安装 openai 6.34.0 的 string | ChatCompletionContentPartText[] 不符。建议明确这是 FastGPT 内部扩展、出站时转换为 user 图片消息;官方 SDK 定义 可供对照。

测试与验证缺口

  • 在指定 worktree 安装锁定依赖、构建 SDK;11 个相关原有测试文件共 382 个用例通过。覆盖 chat adapt、LLM utils、prompt tool、fastAgent/base loop、piAgent message/provider、Sandbox tools/get_file_url、workflowToolRunner、Agent V2 dispatchTool。
  • 首轮 workflowToolRunner 因跳过安装脚本而缺少 sandbox-adapter 构建产物,补齐 SDK 构建后该文件 17 个用例通过;该环境准备失败不计为 PR 缺陷。
  • 三个临时回归用例均在预期断言处失败,分别证实上述三项发现;复现代码与原始日志已保留在任务目录,临时测试文件已清理。未修改业务代码。
  • 完整阅读主仓库 diff;pro 固定版本可用、指针未变,核查相关消费者。未运行全量测试、全库类型检查、真实模型请求或完整 Sandbox 网络集成,因此供应商拒绝影响来自契约和调用链分析,并非实测线上返回。
  • 当前 CI:build/test 均 SKIPPED;report-coverage、CLA 为 SUCCESS,不能据此认定运行时链路通过。

需要作者确认的问题

PR 描述仍提到 isExactImageUrl/getToolResponseContent 和“整串图片链接兜底”,但固定 head 没有这些函数,普通 response 字符串保持文本。请确认该兜底已从本次范围移除,并同步描述。该文案差异不改变上述已证实问题的结论。

审查结论

needs_changes。本次以 COMMENT 发布,不执行批准、请求修改或合并操作。

  • 主仓库 head:26b232ef8646d3913dbfd90dc2d1ac350c148e66
  • base 分支:main;固定 base / merge-base:749809070b75278e11a1576a8ef05ca90acb2d08
  • pro merge-base / head:均为 b2a36080f37af4582c09f4ca1ea36bc85cf3c01d,指针未变。
  • worktree:/Users/yjl/.codex/experiments/pr-review-client-probe/worktrees/pr-7923-83b2fac2e66b(复用宿主目录并保留;主仓库/pro 干净)。

Comment thread packages/service/core/ai/llm/utils.ts Outdated
- 同一轮多个 tool call 的图片消息统一追加在全部 tool 结果之后,
  避免 assistant(A,B) 变成 tool A -> user(image) -> tool B 打断回填顺序
- Agent V2 业务工具(插件/MCP/HTTP)输出的结构化 content 逐层透传到 agent-loop
- content parts 提取复用共享 schema 校验,拒绝 detail 等非法字段并回退文本
- 修正 tool content part 注释:属于 FastGPT 内部扩展,出站转成 user 图片消息
@lirenjie95

Copy link
Copy Markdown
Author

三个问题都改了:

  1. [P1] 多 tool call 分组:loadRequestMessages 不再逐条在 tool 消息后面插图片消息,而是先把工具图片登记下来,等这一轮全部 tool 结果连续回填完再统一追加一条 user 视觉消息。assistant(A,B) 现在是 tool A → tool B → user(images)。新增用例 should append tool images after every tool result of the same assistant turn 锁这个顺序。

  2. [P2] Agent V2 入口:dispatchTool(插件 / MCP / HTTP 三个分支)在保留原 response 的前提下提取结构化 content,DispatchSubAppResponse 与 getExecuteTool 逐层透传到 agent-loop,最终和 ToolCall 路径共用同一份 AgentLoopCoreToolRunResult.content。补了两条用例:有 content 时透传、纯文本时不产生 content。

  3. [P2] 校验复用共享 schema:提取逻辑挪到 agentLoopCore/domain/toolContent,改为逐项 ChatCompletionToolMessageContentPartSchema.safeParse,任一 part 不合法(detail: 'ultra'、空 URL 等)整体返回 undefined 回退文本,保留原有非空检查。补了 detail 非法的用例。

另外按非阻塞建议改了 ChatCompletionToolMessageContentPartSchema 的注释,明确这是 FastGPT 内部扩展、出站会转成 user 图片消息,并同步更新了 PR 描述——isExactImageUrl / getToolResponseContent 那段兜底已从本次范围移除,描述里的旧文案也删了。

本地 tsc -p tsconfig.json 通过,相关 vitest(llm/utils、workflowToolRunner、agent utils、toolCall、fastAgentLoop、piAgentMessage、getFileUrl)共 201 项通过。

@c121914yu c121914yu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex 自动审查:fix(agent): allow tool responses to pass image content to next-round vision input

重要发现

  1. [P2] MCP 原生图片结果没有转换,仍无法进入视觉链路。 dispatchTool:294 将 MCPClient 返回值直接交给只接受 text/image_url 的提取器;当前 MCP SDK 的图片格式为 { type: 'image', data, mimeType }。合法图片会导致整组提取失败,回退为 JSON 文本。已用当前 SDK 的 CallToolResultSchema 校验并复现。应在 MCP 边界先转换为 data URI image_url,再按共享 schema 校验。

  2. [P2] piAgent 在新增图片时丢失 response 中的其他业务信息。 executeOrdinaryTool:151–155 在 content 非空时直接采用 parts。对 { jobId: 'task-123', content: [image_url part] } 这类合法工具输出,旧路径会把完整 response 发给模型;新路径只剩图片,jobId 丢失,影响后续查询或编辑。已实际执行构建出的 AgentTool 复现:事件 response 包含 jobId,模型 content 中没有。应保留规范化 response 文本并追加图片,与 fastAgent 保持一致,交互恢复路径也需同步。

  3. [P2] 内网沙箱预览取图被公共 SSRF 检查拦截。 inlineSandboxPreviewImage:330 使用的 getImageBase64 最终经过 shared axios。当 CHECK_INTERNAL_IP=true 且预览代理为内网地址时,配置中的合法预览 URL 会被拒绝;fastAgent 丢弃图片,piAgent 降级文本,核心取图能力失效。已实际调用取图 helper,确认错误为 Request to private network not allowed,并验证出站图片被删除。应提供基于当前 sandbox 或精确配置地址的受限读取通道,继续保护任意工具 URL。

需求变更

解决 #7827 中 Agent 通过工具取得图片后只能把链接作为文本传给下一轮模型的问题。工具执行结果新增可选 text/image_url parts,Sandbox get_file_url 对图片追加 image_url;fastAgent 出站将图片移到全部 tool 结果之后的 user 消息,piAgent 将图片下载为 ImageContent。普通 response 字符串继续按文本处理,共享消息 schema 支持内部图片扩展。

关键实现路径

  • Sandbox:get_file_url → createToolRunner → runSandboxTools → provider → 下一轮模型请求。
  • 工作流 ToolCall:workflowToolRunner 提取 content → normalizeAgentLoopCoreToolRunResult → agent-loop。
  • Agent V2 业务工具:dispatchTool(插件/MCP/HTTP)→ getExecuteTool → WorkflowAgentToolProvider → 同一 runtime/provider。
  • fastAgent:buildToolMessageContent 保留最终文本及图片 → createLLMResponse → loadRequestMessages 按模型能力过滤、归一化并分组追加图片。
  • piAgent:buildPiAgentTools / 交互恢复 → convertToolResponseContentToPiContent → pi-agent-core;recordToolResult 保存标准 transcript。
  • 展示与历史:GPTMessages2Chats 提取 tool 的 text parts;核查固定 pro 中 chatAgentHelper 的 formatAIResponse、workflowBuilder handler 等消费者。

影响范围与越界判断

28 个文件均围绕工具结果、多模态请求转换、两种 provider、展示适配和对应测试,属于合理扩散。没有无关依赖升级、部署配置、数据库迁移、API 路由、权限或计费公式改动。公共消息适配的影响范围较大,多工具分组已有覆盖;上述三个入口/数据边界问题仍需修复。

代码质量

共享 schema 校验和单独的 toolContent 模块减少了契约重复;结构化字段已穿过主要 runtime 层。上述问题来自协议边界未转换、provider 组合规则不一致,以及配置可信地址与公共下载策略未衔接。建议分别补充真实 MCP 图片、带额外业务字段的 piAgent 工具结果、内网预览代理用例。

代码风格一致性

新增类型沿用 Zod 推导,核心 helper 有职责和回退说明;未引入动态 i18n key。非阻塞建议:workflowToolRunner.ts:107 和 agentLoopCore/interface/index.ts:44 新增了转导,违反当前 .agents/code/syntax.md 的禁止 re-export 规则;消费者及测试应直接从 domain/toolContent 导入。

测试与验证缺口

  • 在指定独立 worktree 安装锁定依赖并构建 3 个 SDK;所选 9 个测试文件、349 个用例全部通过。覆盖 global chat adapt、LLM utils/prompt tool、fastAgent、piAgent message/provider、get_file_url、Agent V2 utils 和 workflowToolRunner。
  • 首次运行有 3 个文件因 sandbox-adapter 尚未构建而无法加载,补齐 SDK 构建后仅重跑这 3 个文件,54 个用例通过。环境准备问题已排除。
  • 额外以真实函数调用复现上述三项问题;MCP 使用已安装 SDK 的结果 schema,piAgent 执行实际构建的工具,内网取图使用实际下载 helper 和 SSRF 检查。复现脚本及输出保留在任务目录,未修改业务代码。
  • 完整阅读主仓库 diff;pro 固定提交可用、指针未变,检查相关共享消息消费者。没有执行全量测试、全库类型检查、真实模型请求或完整 Sandbox 网络集成。
  • 当前 CI 的 build/test 为 SKIPPED,report-coverage 与 CLA 为 SUCCESS。

需要作者确认的问题

无额外需求歧义;三个问题均有代码和执行证据。

审查结论

needs_changes

  • 主仓库 head:75abcfab6c30bb2d84d3ade9ff17e3b40ac103ec
  • base 分支:main;固定 base / merge-base:749809070b75278e11a1576a8ef05ca90acb2d08
  • pro merge-base / head:均为 b2a36080f37af4582c09f4ca1ea36bc85cf3c01d,指针未变化,递归子模块状态已核对。
  • worktree:/Users/yjl/.codex/experiments/pr-review-client-probe/worktrees/pr-7923-e766002e6ffc,复用并保留宿主准备目录。
  • 复现材料:/Users/yjl/.codex/experiments/pr-review-client-probe/runs/7923-e766002e6ffc3998/tool-image-probe.mts 及同名 .log。

toolName,
params
});
const content = extractAgentLoopCoreToolContent(result);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex 自动审查 · [P2] 先转换 MCP 原生图片格式。 MCPClient.toolCall 原样返回 SDK 的 CallToolResult,其图片 part 是 { type: 'image', data, mimeType }。这里直接交给只接受 text/image_url 的提取器,遇到标准 MCP 图片时会整组返回 undefined,最终仍把 base64 图片 JSON 当作文本,下一轮模型没有视觉输入。已用当前 SDK 的 CallToolResultSchema 校验合法图片结果并复现。请在 MCP 边界将 image 转为 image_url: { url: 'data:<mimeType>;base64,<data>' } 后再校验,并覆盖真实 MCP 返回格式。

Comment on lines +151 to +155
// 工具返回结构化 content 时优先透传;否则用规范化后的 response 字符串兜底
const toolContent = result.content?.length
? result.content
: normalizeToolResponseContent(result.response);
onToolResult({ call, response: normalizedResponse, assistantMessages, content: toolContent });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex 自动审查 · [P2] 保留 response 中的业务字段。 有 content 时这里直接替换完整 response。例如 HTTP/插件输出 { jobId: 'task-123', content: [image_url part] },上游仍把整个对象序列化为 response,但本分支给 piAgent 的实际工具结果只剩图片,模型无法获知 jobId,后续查询/编辑操作因而缺少必要参数。实际调用 buildPiAgentTools(...)[0].execute(...) 已复现;工具事件仍持有 jobId,实际模型 content 却没有。请按 fastAgent 的组合规则保留规范化 response 文本并追加图片,同步处理交互恢复路径。

}

try {
const { completeBase64: base64 } = await getImageBase64(part.image_url.url);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex 自动审查 · [P2] 让受信任的内网预览地址能够取图。 当部署开启 CHECK_INTERNAL_IP=true,且 AGENT_SANDBOX_PREVIEW_PROXY_URL 使用内网地址时,getImageBase64 内部的 shared axios 会在请求前报 Request to private network not allowed。这里捕获后删除图片,所以有效的 get_file_url 结果仍无法成为下一轮视觉输入;piAgent 调用同一 helper 也只能降级文本。已用配置前缀 http://10.23.45.67:3006 复现实际拒绝及图片被移除。应通过当前 sandbox 的受控读取能力,或为配置中的精确 origin/路径设计受限取图通道,同时保留普通工具 URL 的 SSRF 防护。

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Agent V2 工具取得圖片後,無法在下一輪模型請求中作為圖像輸入

2 participants