将请求(prompt、image_urls 和 audio_urls)包装成统一消息格式。
(self)
| 184 | return "\n".join(result_parts) |
| 185 | |
| 186 | async def assemble_context(self) -> dict: |
| 187 | """将请求(prompt、image_urls 和 audio_urls)包装成统一消息格式。""" |
| 188 | # 构建内容块列表 |
| 189 | content_blocks = [] |
| 190 | |
| 191 | # 1. 用户原始发言(OpenAI 建议:用户发言在前) |
| 192 | if self.prompt and self.prompt.strip(): |
| 193 | content_blocks.append({"type": "text", "text": self.prompt}) |
| 194 | elif self.image_urls: |
| 195 | # 如果没有文本但有图片,添加占位文本 |
| 196 | content_blocks.append({"type": "text", "text": "[图片]"}) |
| 197 | elif self.audio_urls: |
| 198 | # 如果没有文本但有音频,添加占位文本 |
| 199 | content_blocks.append({"type": "text", "text": "[音频]"}) |
| 200 | |
| 201 | # 2. 额外的内容块(系统提醒、指令等) |
| 202 | if self.extra_user_content_parts: |
| 203 | for part in self.extra_user_content_parts: |
| 204 | content_blocks.append(part.model_dump_for_context()) |
| 205 | |
| 206 | # 3. 图片内容 |
| 207 | if self.image_urls: |
| 208 | for image_url in self.image_urls: |
| 209 | image_data = await MediaResolver( |
| 210 | image_url, |
| 211 | media_type="image", |
| 212 | ).to_base64_data() |
| 213 | if not image_data: |
| 214 | logger.warning("图片预处理结果为空,将忽略。") |
| 215 | continue |
| 216 | content_blocks.append( |
| 217 | { |
| 218 | "type": "image_url", |
| 219 | "image_url": {"url": image_data.to_data_url()}, |
| 220 | }, |
| 221 | ) |
| 222 | |
| 223 | # 4. 音频内容 |
| 224 | if self.audio_urls: |
| 225 | for audio_url in self.audio_urls: |
| 226 | try: |
| 227 | audio_data = await MediaResolver( |
| 228 | audio_url, |
| 229 | media_type="audio", |
| 230 | default_suffix=".wav", |
| 231 | ).to_base64_data( |
| 232 | strict=True, |
| 233 | target_format="wav", |
| 234 | ) |
| 235 | except Exception as exc: |
| 236 | logger.warning("音频预处理失败,将忽略。错误: %s", exc) |
| 237 | continue |
| 238 | if not audio_data: |
| 239 | logger.warning("音频预处理结果为空,将忽略。") |
| 240 | continue |
| 241 | content_blocks.append( |
| 242 | { |
| 243 | "type": "audio_url", |