杠精派
派聪明
0/512

热门评论

polarwinnie 2026年07月20日 14:18
引用原文:所以我们的策略是:给 Message 加一个 contentParts 字段,和原来的 content 字段并存。普通文本消息继续走 content,只有带图片的消息才用 contentParts
@派聪明 为什么不全部走contentParts,而要进行区分
点赞
回复1 取消回复
派聪明 2026年07月20日 14:18
这是一个非常经典且务实的"兼容性演进"设计决策,原因主要有三点: --- ## 1. 兼容不同 API 协议 参考资料明确提到:**不同的 API 端点对 content 格式的支持完全不同**。 - 纯文本模型(如 GLM-5.1、DeepSeek V4)走的是 **Coding API**,其 `content` 字段只接受纯文本字符串。 - 多模态模型(如 GLM-5V)走的是 **Multimodal API**,才支持 `content` 数组(text + image_url 混排)。 如果全部统一成 `contentParts`,那么在序列化发给纯文本模型时,还得再套一层判断和转换逻辑;而保留两个字段并存,可以在序列化时**根据模型类型按需选择**: ```java private void appendMessageContent(ObjectNode msgNode, Message msg) { if (!msg.hasContentParts()) { msgNode.put("content", msg.content()); // 纯文本模型:直接走 content return; } // 多模态模型:构建 content 数组 ArrayNode contentArray = msgNode.putArray("content"); } ``` ## 2. 规避协议限制(关键原因) 参考资料明确指出,**某些角色的消息只支持纯文本 content**。比如 **tool role** 的 message,OpenAI API 规范约定其 content 只能是字符串,不支持 content array。如果强行把图片塞进 tool message,API 会返回 **400 错误**。 因此 PaiCLI 的处理策略是:tool message 里放文本 fallback,再追加一条 **user message** 放真图。如果没有 `content` 字段作为 fallback,这类场景就无法优雅处理。 ## 3. 平滑演进,不改历史代码 原有的消息创建、日志打印、文本分析等逻辑都依赖于 `content` 字段(字符串)。如果一刀切改成 `contentParts`,意味着**所有历史代码都要跟着改**,且容易引入 bug。 保留双字段的设计,本质上是一种 **"渐进式改造"**:新增的图片场景走 `contentParts`,原有的纯文本场景零改动,各自的逻辑互不干扰,如下图所示: ``` 普通文本消息:content = "帮我写段代码" ,contentParts = null 带图片的消息: content = "帮我看看这张图"(纯文本fallback),contentParts = [text, image_url] ``` --- **一句话总结**:留 `content` 是给旧协议和纯文本场景走"老路",加 `contentParts` 是给多模态场景开"新路",两条路并存,互不阻塞,也互不污染。
点赞
回复 取消回复

7 条评论

阿斯顿 2026年07月28日 14:34
引用原文:注意 Message.user() 工厂方法里的 plainText(contentParts),它会把 contentParts 里所有文本块拼接成一个纯文本 fallback 存到 content 字段。这样即使后续序列化逻辑走到不支持 content array 的分支,消息也不会丢失文本信息。
这是啥意思@派聪明
点赞
回复1 取消回复
派聪明 2026年07月28日 14:34
小派正在努力回答中, 耐心等待一下吧...
点赞
回复 取消回复
17377927899 2026年07月20日 21:50
引用原文:多模态
@派聪明 简述PaiCLI的多模态是怎么实现的
点赞
回复1 取消回复
派聪明 2026年07月20日 21:50
根据参考资料,PaiCLI 的多模态能力核心是通过**接入多模态模型(GLM-5V)** 实现的,具体链路如下: ## 1. 模型层:从纯文本到多模态 纯文本模型(如 GLM-5.1)只能处理文本 token,而多模态模型 **GLM-5V** 多了一个 **Vision Encoder**(视觉编码器)组件。它会将图片切成固定大小的 patch(如 14×14 像素),每个 patch 经 ViT 处理后变成与文本 embedding 维度对齐的"视觉 token",与文本 token 一起参与 Transformer 的注意力计算。 ## 2. 协议层:ContentPart 升级 将 `Message` 的 content 从单一的 `String` 扩展为 **`List<ContentPart>`** 数组,支持 text 和 image_url 两种 block 混排。序列化时根据是否有图片决定走纯文本还是带 image block 的 JSON 数组格式。 ## 3. 输入层:@image 与 @clipboard - **`@image:路径`**:支持相对路径、绝对路径、`file://` 协议、尖括号包裹(处理空格/中文路径) - **`@clipboard`**:macOS 通过 AppleScript 获取剪贴板 PNG/TIFF 数据,Linux/Windows 走 AWT Clipboard API ## 4. MCP 图片注入 Chrome DevTools MCP 截图后,将 `type: "image"` 的 MCP 工具结果提取为 base64,通过 **tool message(文本 fallback)+ user message(真图 block)** 的消息序列注入对话,解决 tool role 不支持 content array 的协议限制。 ## 5. 图片预处理 三级处理决策树:5MB 以内直通 → 带 alpha 的做白底 flatten → 超 5MB 等比缩放到 2000×2000,必要时 JPEG 逐级降质(0.85→0.25)。同时有 **历史图片裁剪机制**,每轮 ReAct 前只保留最近一轮的图片实体,减少上下文膨胀。
点赞
回复 取消回复
polarwinnie 2026年07月20日 14:18
引用原文:所以我们的策略是:给 Message 加一个 contentParts 字段,和原来的 content 字段并存。普通文本消息继续走 content,只有带图片的消息才用 contentParts
@派聪明 为什么不全部走contentParts,而要进行区分
点赞
回复1 取消回复
派聪明 2026年07月20日 14:18
这是一个非常经典且务实的"兼容性演进"设计决策,原因主要有三点: --- ## 1. 兼容不同 API 协议 参考资料明确提到:**不同的 API 端点对 content 格式的支持完全不同**。 - 纯文本模型(如 GLM-5.1、DeepSeek V4)走的是 **Coding API**,其 `content` 字段只接受纯文本字符串。 - 多模态模型(如 GLM-5V)走的是 **Multimodal API**,才支持 `content` 数组(text + image_url 混排)。 如果全部统一成 `contentParts`,那么在序列化发给纯文本模型时,还得再套一层判断和转换逻辑;而保留两个字段并存,可以在序列化时**根据模型类型按需选择**: ```java private void appendMessageContent(ObjectNode msgNode, Message msg) { if (!msg.hasContentParts()) { msgNode.put("content", msg.content()); // 纯文本模型:直接走 content return; } // 多模态模型:构建 content 数组 ArrayNode contentArray = msgNode.putArray("content"); } ``` ## 2. 规避协议限制(关键原因) 参考资料明确指出,**某些角色的消息只支持纯文本 content**。比如 **tool role** 的 message,OpenAI API 规范约定其 content 只能是字符串,不支持 content array。如果强行把图片塞进 tool message,API 会返回 **400 错误**。 因此 PaiCLI 的处理策略是:tool message 里放文本 fallback,再追加一条 **user message** 放真图。如果没有 `content` 字段作为 fallback,这类场景就无法优雅处理。 ## 3. 平滑演进,不改历史代码 原有的消息创建、日志打印、文本分析等逻辑都依赖于 `content` 字段(字符串)。如果一刀切改成 `contentParts`,意味着**所有历史代码都要跟着改**,且容易引入 bug。 保留双字段的设计,本质上是一种 **"渐进式改造"**:新增的图片场景走 `contentParts`,原有的纯文本场景零改动,各自的逻辑互不干扰,如下图所示: ``` 普通文本消息:content = "帮我写段代码" ,contentParts = null 带图片的消息: content = "帮我看看这张图"(纯文本fallback),contentParts = [text, image_url] ``` --- **一句话总结**:留 `content` 是给旧协议和纯文本场景走"老路",加 `contentParts` 是给多模态场景开"新路",两条路并存,互不阻塞,也互不污染。
点赞
回复 取消回复
粗心的超短裙 2026年05月11日 23:33
打卡
1
回复 取消回复

目录