项目实训开发日志(六):BabyMind:基于多Agent和RAAG的科学育儿辅助平台
进入第六周后,团队的开发重点进一步从"让系统具备智能问答能力"转向"让系统真正可用、可演示"。前五周我们完成了三大核心Agent的基础能力建设、RAG知识库的初步接入、统一问答入口的打通,以及健康、时间轴、营养模块之间数据联动的基础链路。而进入这一阶段,我们主要聚焦两件事:一是完成语音交互功能,使系统具备多模态输入输出能力;二是进一步完善RAG知识库内容,保证问答能够真正基于知识检索而非模型直接生成。与此同时,我们也借助这一阶段的联调机会,修复了若干此前遗留的模块对接问题。
一、本周整体完成内容
1、完成语音识别(ASR)与语音合成(TTS)服务接入
语音交互是BabyMind项目的重要设计目标之一。对于正在抱娃或喂奶的家长来说,双手被占用的场景非常常见,文字输入的交互方式在这类场景下并不友好。因此,本周我们优先完成了语音交互链路的后端服务接入。
语音识别部分,我们通过调用SiliconFlow平台的ASR接口,实现了音频文件到文字的转换。接口接收前端上传的M4A或WAV格式音频,完成识别后返回文字内容。
async def transcribe_audio(audio_file: UploadFile, settings: Settings) -> str:
audio_bytes = await audio_file.read()
response = httpx_client.post(
settings.siliconflow_asr_url,
headers={"Authorization": f"Bearer {settings.llm_api_key}"},
files={"file": (audio_file.filename, audio_bytes, audio_file.content_type)},
)
result = response.json()
text = result.get("text", "").strip()
if not text:
raise SpeechNotRecognizedException()
return text
语音合成部分,我们接入了SiliconFlow的TTS服务,支持三种音色(中文女声小萱、中文男声小明、英文女声Emma)和三档语速(0.75倍、1.0倍、1.5倍),生成结果以Base64编码形式返回给前端。
相关配置项已同步补充至app/core/config.py和.env.example,便于部署时统一管理。
2、完成端到端语音问答接口
在ASR和TTS基础上,本周我们进一步封装了端到端语音问答接口POST /api/v1/voice/ask。该接口接收前端上传的音频文件,内部依次完成语音识别、问题路由与回答生成、语音合成三个环节,最终将识别文字、回答文字和语音数据统一返回。
@router.post("/ask")
async def voice_ask(
file: UploadFile = File(...),
baby_id: int = Form(...),
voice: str = Form("中文女"),
speed: float = Form(1.0),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
transcribed_text = await voice_service.transcribe_audio(file, settings)
baby = get_baby_profile_for_user(db, current_user, baby_id)
qa_result = answer_question_with_router(
db=db, user=current_user, baby=baby,
question=transcribed_text, session_id=None, session_title=None,
)
answer_text = voice_service.build_answer_text(qa_result)
audio_base64 = await voice_service.synthesize_speech(answer_text, voice, speed, settings)
return VoiceAskResponse(
transcribed_text=transcribed_text,
answer_text=answer_text,
audio_base64=audio_base64,
total_ms=int((time.time() - start_time) * 1000),
)
端到端接口内部任一环节失败时均有独立容错,TTS合成失败时仍会返回文字回答,不影响问答主流程。此外,接口通过限流装饰器控制每用户每分钟最多20次请求,避免API调用成本失控。
3、完成RAG知识库内容系统性补充与检索优化
前几周RAG知识库虽然已经完成基础接入,但知识条目数量偏少,内容覆盖不够完整,导致部分健康类问答无法有效检索到相关资料。本周我们对知识库进行了系统性补充。
我们新增了scripts/build_knowledge_base.py脚本,用于生成结构化的中文育儿知识内容,按健康照护、疫苗接种、辅食营养、成长发育四个方向组织。知识文档生成后,通过scripts/ingest_kb.py完成文本切分和向量入库。
python scripts/build_knowledge_base.py
# health: wrote 27 condition files
# vaccine: wrote 16 vaccine files + faq/schedule/myths
# nutrition: wrote 5 stages + ingredients/allergens/skills/recipes/faq/nutrients
# development: wrote 37 month files (0-36) + 6 domain overviews
python scripts/ingest_kb.py
# babymind_knowledge_health: 455 chunks
# babymind_knowledge_timeline: 191 chunks
# babymind_knowledge_nutrition: 202 chunks
# TOTAL embeddings ingested: 848
# PASS
经过补充,知识库总条目达到848条,覆盖27种常见病症、16种疫苗说明、5个辅食阶段和0-36月龄全周期发育内容,基本满足项目对RAG知识库的量化要求。
同时,本周我们还修复了一个此前遗留的关键问题:知识库入库时使用了本地离线嵌入模型(384维),但RAG检索时调用了SiliconFlow嵌入接口(1024维),两者维度不一致导致检索失败,问答服务返回500错误。本周通过统一嵌入函数来源解决了这一问题。
4\完成营养Agent过敏原自动识别与档案同步
本周在营养Agent问答流程中,我们新增了过敏原自动提取能力。当家长在与营养Agent的对话中明确提及宝宝对某种食物过敏时,系统会在生成回答后,通过轻量级LLM调用对用户消息进行过敏原提取:
ALLERGEN_EXTRACT_PROMPT = """
从以下用户消息中提取宝宝食物过敏信息。
只提取明确提到"过敏"、"不能吃"、"不耐受"、"吃了会过敏"的食物名称。
不要提取"不爱吃"、"不想吃"等非过敏表述。
以JSON格式返回,只返回JSON不要其他内容:
{{"add": ["食物1"], "remove": []}}
如果没有明确过敏信息,返回: {{"add": [], "remove": []}}
用户消息: {question}
"""
提取结果会附加在问答响应的allergen_changes字段中返回给前端。前端收到后展示确认弹窗,由用户确认后通过新增的PATCH /api/v1/babies/{baby_id}/allergies接口将过敏原写入宝宝档案。过敏原提取逻辑采用静默容错设计,提取失败时不影响主问答流程。
5、完成Android端语音交互页面
前端部分,本周完成了语音交互的Android端实现,主要包含以下内容:
- VoiceButton.kt:带四种状态的麦克风按钮(待机/录音/处理中/播放中),录音时有红色脉冲动画和音量波形显示;
- VoiceViewModel.kt:管理录音流程,使用MediaRecorder录制音频,最长30秒自动停止,录完后自动上传调用语音问答接口;
- VoiceRepository.kt:封装语音相关API调用,包括voiceAsk、synthesizeSpeech和fetchVoices;
- VoiceSettingsScreen.kt:语音设置页面,支持选择音色和语速,设置持久化到SharedPreferences。
语音按钮已集成至首页输入栏,识别完成后答案自动注入聊天记录,并触发TTS播报,形成完整的语音交互闭环。
6、补充语音模块相关测试
本周围绕语音识别、语音合成和端到端语音问答补充了对应测试用例,位于tests/test_voice.py:
- test_voice_asr_valid_audio:上传真实WAV音频,验证识别结果非空且响应时间在2秒内;
- test_voice_tts_returns_audio:验证TTS接口返回非空音频数据;
- test_voice_ask_end_to_end:验证端到端接口返回完整字段且总耗时在5秒内;
- test_voice_rate_limit:连续发送21次请求,验证第21次返回429;
- test_voice_empty_audio:上传静音文件,验证返回422和speech_not_recognized错误。
二、本周核心代码与模块
语音交互模块:
- app/services/voice_service.py
- app/api/routers/voice.py
- app/schemas/voice.py
知识库与RAG优化:
- app/services/rag_service.py
- scripts/build_knowledge_base.py
- scripts/ingest_kb.py
- data/knowledge_base/
过敏原自动识别:
- app/services/agent_router_service.py
- app/api/routers/babies.py
- app/schemas/qa.py
Android端语音交互:
- frontend/.../ui/screens/VoiceButton.kt
- frontend/.../ui/screens/VoiceSettingsScreen.kt
- frontend/.../ui/viewmodel/VoiceViewModel.kt
- frontend/.../data/VoiceRepository.kt
测试验证:
- tests/test_voice.py
三、本周遇到的问题与解决思路
1、模拟器麦克风质量导致语音识别失败
在模拟器环境下测试语音输入时,发现ASR接口频繁返回speech_not_recognized,但同样的音频文件通过curl直接上传可以正常识别。排查后确认是模拟器录制的音频质量过差,信噪比低,ASR模型无法有效识别。
解决方式是在测试阶段通过tests/fixtures/sample_question.wav直接测试后端接口,绕过模拟器录音环节。真机测试时语音识别正常,这一问题在实际使用场景中不会出现。
2、知识库嵌入维度不一致
前几周入库时为了快速验证,在没有配置API Key的情况下使用了ChromaDB本地ONNX嵌入模型(384维)。而RAG检索时调用了SiliconFlow嵌入接口(1024维),两者维度不一致,导致问答请求直接返回500错误。
这一问题在联调阶段暴露,修复方式是清空旧的向量数据库,配置好API Key后重新运行入库脚本,统一使用SiliconFlow的嵌入模型完成入库和检索。
3、端到端语音接口响应时间偏长
ASR识别加上LLM问答再加TTS合成,三步串行调用总耗时容易超过5秒的目标。测试时发现主要瓶颈在LLM问答阶段,因为RAG检索和回答生成本身耗时较长。
当前的处理方式是在前端增加了等待动画,让用户感知到系统正在处理,减少等待时的困惑感。后续可以考虑将TTS合成异步化,先返回文字回答再生成语音,进一步降低感知延迟。
4、过敏原提取误识别问题
在调试过敏原自动提取时,发现如果用户说"宝宝不爱吃花生",系统有时会误识别为过敏原。通过在提示词中明确限定只提取含有"过敏""不耐受""不能吃""吃了会过敏"等字眼的表述,并将轻量LLM的max_tokens控制在100以内,有效减少了误识别的情况。即便出现误识别,也需要用户手动确认才会写入档案,设计上保留了人工审核的环节。
四、阶段性成果
经过第六周开发,BabyMind项目在前五周已完成三大核心Agent基础能力的基础上,进一步补齐了多模态语音交互和知识库完整性两个重要短板。当前系统已能够支持从语音输入到语音播报的完整交互闭环,知识库内容也扩充到848条,RAG检索链路恢复稳定。
五、下周计划
下一阶段我们计划继续推进以下内容:
- 继续完善Android端各功能页面的交互细节和界面设计;
- 优化健康、时间轴、营养三类问答的回答质量和结构;
- 修复联调过程中暴露的前后端对接问题;
- 开始整理项目文档,包括README补充和接口说明更新;
- 准备项目演示流程,确保核心功能在答辩环境下可以稳定运行。
总体来看,第六周是项目从"功能开发"转向"系统整合与打磨"的过渡阶段。核心新增能力已完成,接下来的重点将从"做出来"转向"做好用"。
更多推荐


所有评论(0)