590 字
3 分钟
系统架构与回调流程
系统架构与回调流程
本篇承接 01_项目概览与技术栈,重点看这个项目的运行骨架:模块如何分层、Gradio 为什么采用两步回调,以及
history数据结构为什么会成为调试关键点。
1 模块分层
项目的分层可以概括为:
- 输入层:收集文字、图片、语音
- 预处理层:把三种输入转换成统一可组装格式
- LangChain 层:组织 Prompt、注入历史、调用模型
- 存储层:把多轮对话历史持久化
2 两步回调机制
Gradio 的事件链设计是本项目的一个关键点。它不是“一次提交做完所有事”,而是分成两步:
add_message():立即更新 UI,处理用户输入submit_messages():真正调用模型,等待 AI 回复
为什么不一步做完?因为模型调用和外部 API 都是耗时操作。先让 add_message() 把界面更新出来,用户会明显感觉“消息已经发出”,而不是一直卡在输入阶段。
3 Gradio history 数据结构
理解这个格式是调试的关键:
# history 是一个列表,每条消息是一个字典history = [ # 文字消息(旧格式 - 纯字符串) {"role": "user", "content": "你好"},
# 文字消息(新格式 - 列表) {"role": "user", "content": [{"type": "text", "text": "你好"}]},
# 图片消息(dict 格式,type='messages' 模式下必须这样) {"role": "user", "content": {"path": "C:/Temp/gradio/xxx.png"}},
# AI 回复 {"role": "assistant", "content": "你好!有什么可以帮你的?"},]常见坑:tuple 格式已废弃旧版 Gradio 用 (file_path,) 元组存图片,新版 type='messages' 模式下会报 ValueError,必须改用 {'path': file_path} 字典格式。
4 常量设计
源代码里把 URL 和轮询参数抽成常量,不只是代码风格问题,而是为了让“接口地址”和“重试策略”不散落在业务逻辑里。
# 为什么把 URL 提取为常量?# 1. 阿里云接口地址偶尔会调整(如国际版 dashscope-intl.aliyuncs.com)# 2. 集中管理,改一处全局生效# 3. 代码可读性更高
DASHSCOPE_MULTIMODAL_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"DASHSCOPE_ASR_SUBMIT_URL = "https://dashscope.aliyuncs.com/api/v1/services/audio/asr/transcription"DASHSCOPE_TASK_QUERY_URL = "https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}"
POLL_MAX_RETRIES = 20 # 最多等 20 × 2 = 40 秒POLL_INTERVAL_SECONDS = 2相关笔记
- 前置:01_项目概览与技术栈
- 后续:03_语音识别与降级链路
- 后续:05_图像处理与多模态消息组装