运行前确保服务已启动:
python -m uvicorn app.main:app --host 127.0.0.1 --port 8000 --reload每个测试用 curl 或 httpie 发 POST 到
http://127.0.0.1:8000/api/v1/chat/explain-and-ask观察要点写在每条用例后面。
{
"text": "1加1等于几?",
"mode": "education",
"age_hint": "5-6",
"session_id": "test-edu-01"
}观察点:
react.selected_act应为directgrounding.used_rag应为falsemetadata.workflow_trace应包含plan→execute(不含tools)- 回答语气应童趣、有引导性追问
- LLM 主观评估: 回答是否用儿童能懂的语言解释了 1+1=2,语气是否轻松有趣,是否自然带出一个引导孩子思考的追问
{
"text": "蜗牛为什么背着一个壳?",
"mode": "education",
"age_hint": "5-6",
"session_id": "test-edu-02"
}观察点:
react.selected_act应为retrieve_knowledgegrounding.used_rag应为true,grounding.sources非空metadata.workflow_trace应包含plan→tools→observe→execute- 回答应基于《十万个为什么》内容,有具体解释
- LLM 主观评估: 解释蜗牛背壳的原因是否准确(保护/家/移动)、语言是否符合 5-6 岁理解水平,有无生动比喻
{
"text": "植物为什么要晒太阳?",
"mode": "education",
"age_hint": "4-5",
"session_id": "test-edu-03"
}观察点:
- 同 T02,验证植物类也走 RAG 路径(plan 节点有显式规则)
- 回答应提到光合作用,但用儿童友好的语言
- LLM 主观评估: 光合作用说明是否用了儿童能理解的比喻(如"植物做饭"),是否避免了照搬专业术语
{
"text": "小明有5个苹果,送给小红2个,还剩几个?",
"mode": "education",
"age_hint": "6-7",
"session_id": "test-edu-04"
}观察点:
grounding.used_rag应为false- plan_steps 应有步骤拆解(数苹果的逻辑)
- 有引导孩子自己算的追问
- LLM 主观评估: 是否引导孩子自己想(而非直接给出"剩3个"),语气是否鼓励、有趣,追问是否推动孩子自主思考
{
"text": "今天天气真好,我想去玩!",
"mode": "companion",
"age_hint": "5-6",
"session_id": "test-comp-01"
}观察点:
react.selected_act应为directmetadata.workflow_trace应只有reason→respond(无 tools)- 语气温暖自然,无说教感
follow_up_question非空(继续话题)- LLM 主观评估: 回复是否表现出真诚的陪伴感(而非机械应答),是否自然延伸话题(如问去哪玩、和谁玩),有无说教或灌输
先发 T05,再发这条,使用同一 session_id
{
"text": "你还记得我刚才说了什么吗?",
"mode": "companion",
"age_hint": "5-6",
"session_id": "test-comp-01"
}观察点:
react.selected_act应为read_memorymetadata.workflow_trace应包含reason→tools→observe→respond- 回答应正确引用上一轮说的"天气好想玩"
- LLM 主观评估: 记忆引用是否自然融入对话(不生硬复述),是否让孩子感受到"被记住"的温暖感
发送顺序:先 T07a,再 T07b
T07a(第一轮):
{
"text": "我最喜欢的动物是小猫咪!",
"mode": "companion",
"age_hint": "5",
"session_id": "test-comp-02"
}T07b(第二轮):
{
"text": "你知道我最喜欢什么动物吗?",
"mode": "companion",
"age_hint": "5",
"session_id": "test-comp-02"
}观察点:
- T07b 的
memory.session_updated应为true - 回答应提到"小猫咪"(working memory 生效)
- LLM 主观评估: 两轮对话衔接是否流畅,T07b 的回答是否感觉像真正记住了而非刻意搜索,有无借机展开猫咪相关话题
{
"text": "孩子不肯吃蔬菜怎么办?",
"mode": "parent",
"age_hint": "4-5",
"session_id": "test-parent-01"
}观察点:
- 语气专业,针对家长而非孩子
react.selected_act应为direct- 建议具体可操作
- LLM 主观评估: 建议是否实用(如分阶段引导、混入喜欢的食物等),是否站在家长视角而非教条说教,回答是否简洁不啰嗦
{
"text": "最近有没有关于儿童早教的新研究?",
"mode": "parent",
"age_hint": "3-5",
"session_id": "test-parent-02"
}观察点:
react.selected_act应为tavily_searchmetadata.workflow_trace应包含reason→tools→observe→respond- 回答应引用近期信息(非截断知识)
grounding.sources非空,有来源 URL- LLM 主观评估: 搜索结果是否被有效整合(非逐条粘贴),是否提炼出对家长有实际参考价值的关键信息,可信度标注是否清晰
使用已有记录的 session/profile,或先在 education 模式建立几轮对话后再问
{
"text": "我的孩子最近学了什么内容?",
"mode": "parent",
"session_id": "test-parent-03",
"profile_id": "default_child"
}观察点:
react.selected_act应为read_memory- 回答应汇总从 episodic/semantic memory 中检索到的孩子学习记录
- 若无记录,应给出礼貌提示
- LLM 主观评估: 记忆汇总是否有条理(按学科/时间整理),信息呈现是否让家长一目了然,是否附有建议性的后续行动
{
"text": "量子力学是什么?",
"mode": "education",
"age_hint": "6",
"session_id": "test-rag-01"
}观察点:
- 即使触发检索,
grounding.sources可能为空或低分 - 系统应降级到直接回答,而非崩溃或乱引用
metadata.confidence可能为medium或low- LLM 主观评估: 降级回答是否诚实承认知识边界("这个问题超出我现在的知识范围"),是否有引导性提示,而非捏造内容
{
"text": "鸟为什么会飞?",
"mode": "education",
"age_hint": "5-6",
"session_id": "test-rag-02"
}观察点:
grounding.used_rag应为truegrounding.sources[0].score应相对高(表示命中)- 回答内容应与《十万个为什么》中的鸟类相关内容一致
- LLM 主观评估: 解释鸟类飞行原理是否准确(翅膀/骨骼/肌肉/空气动力),内容是否源于知识库而非凭空生成,语言是否适合儿童
{
"text": "为什么?",
"mode": "education",
"age_hint": "5",
"session_id": "test-edge-01"
}观察点:
- 系统不应崩溃
- 应给出澄清引导,例如"你是想问什么的为什么?"
- LLM 主观评估: 澄清方式是否友好(不让孩子觉得自己问错了),是否给出 2-3 个具体的方向示例引导孩子继续表达
{
"text": "为什么天空是blue的?",
"mode": "education",
"age_hint": "6",
"session_id": "test-edge-02"
}观察点:
- 系统应正常处理混合语言输入
- 回答应用中文,保持风格一致
- LLM 主观评估: 是否正确理解了"blue"即"蓝色",解释瑞利散射是否用了儿童能接受的类比(如"太阳光是彩虹色,蓝光最爱跑")
{
"text": "今天是2026年4月11日,上周三是几月几日?",
"mode": "companion",
"age_hint": "7",
"session_id": "test-edge-03"
}观察点:
- ReAct 可能调用时间计算逻辑
- 最多 3 次迭代后应给出答案,不应死循环
metadata.workflow_trace中reason出现次数 ≤ 3- LLM 主观评估: 最终给出的日期是否计算正确(2026-04-04),若无时间工具是否诚实说明,回答风格是否仍保持轻松儿童友好
curl -s -X POST http://127.0.0.1:8000/api/v1/chat/explain-and-ask \
-H "Content-Type: application/json" \
-d '{"text":"蜗牛为什么背着一个壳?","mode":"education","age_hint":"5-6","session_id":"test-edu-02"}' \
| python3 -m json.tool或使用 SSE 流式接口:
curl -N -X POST http://127.0.0.1:8000/api/v1/chat/explain-and-ask-stream \
-H "Content-Type: application/json" \
-d '{"text":"蜗牛为什么背着一个壳?","mode":"education","age_hint":"5-6","session_id":"test-edu-02"}'| 编号 | 模式 | 核心特性 | 关键字段 |
|---|---|---|---|
| T01 | education | Plan 直接回答 | selected_act=direct, used_rag=false |
| T02 | education | Plan + RAG(动物) | selected_act=retrieve_knowledge, used_rag=true |
| T03 | education | Plan + RAG(植物) | 同上 |
| T04 | education | Plan 推理(数学) | used_rag=false, plan_steps 有拆解 |
| T05 | companion | ReAct 直接 | selected_act=direct, 无 tools |
| T06 | companion | ReAct + read_memory | selected_act=read_memory |
| T07 | companion | 多轮记忆连贯 | memory.session_updated=true |
| T08 | parent | 直接建议 | 语气专业 |
| T09 | parent | Tavily 搜索 | selected_act=tavily_search, sources 非空 |
| T10 | parent | 孩子记录查询 | selected_act=read_memory |
| T11 | education | RAG 未命中降级 | 不崩溃,confidence 可能低 |
| T12 | education | RAG 高置信命中 | sources[0].score 高 |
| T13 | education | 极短输入 | 不崩溃,引导澄清 |
| T14 | education | 混合语言 | 中文回答,逻辑正常 |
| T15 | companion | ReAct 迭代上限 | reason 出现次数 ≤ 3 |
背景: 原来跨会话历史全部注入上下文,导致
read_memory_bundle工具形同虚设。 现已改为:companion/parent模式启动时Memory={}空载,LLM 按需调用工具拉取跨会话记忆;education模式仍自动注入 profile 记忆。 以下用例使用独立 profile_idnm-test-child/nm-test-parent隔离数据。
{
"text": "我叫小明,我最喜欢的颜色是蓝色!",
"mode": "companion",
"age_hint": "6",
"session_id": "nm-sess-A",
"profile_id": "nm-test-child"
}观察点:
memory.session_updated == true(当前 session 写入成功)memory.written_types非空- 正常有回复
- LLM 主观评估: 回复是否热情回应了孩子的自我介绍(称呼小明、提到蓝色),语气是否像朋友而非问答机器人
必须在 NM-01a 之后执行,使用不同 session_id 但同一 profile_id
{
"text": "你知道我最喜欢什么颜色吗?",
"mode": "companion",
"age_hint": "6",
"session_id": "nm-sess-B",
"profile_id": "nm-test-child"
}观察点:
react.selected_act == 'read_memory'(主动调工具拉取跨会话记忆)'tools' in workflow_trace- 回答包含「蓝」(成功从 profile 记忆中找到颜色偏好)
- LLM 主观评估: 回答是否自然地说出"你最喜欢蓝色"(而非"根据记忆你喜欢蓝色"的机械措辞),是否顺势延伸(如问为什么喜欢蓝色)
{
"text": "我的名字叫小花,今天学会了画画!",
"mode": "companion",
"age_hint": "6",
"session_id": "nm-same-01",
"profile_id": "nm-test-child-2"
}观察点:
memory.session_updated == true- 正常有回复
- LLM 主观评估: 是否为孩子学会画画的成就感到真诚高兴,是否鼓励孩子继续画,有无提问她画了什么
与 NM-02a 使用完全相同的 session_id
{
"text": "你还记得我的名字吗?",
"mode": "companion",
"age_hint": "6",
"session_id": "nm-same-01",
"profile_id": "nm-test-child-2"
}观察点:
react.selected_act == 'direct'(无需调工具,short_Memory 已含当前 session 历史)'tools' not in workflow_trace- 回答包含「小花」
- LLM 主观评估: 是否自然地叫出"小花"(而非"你的名字是小花"),语气是否亲昵,是否有趣地延续了画画话题
沿用 nm-test-child profile(NM-01a 已写入数据),用全新 session_id
{
"text": "蜗牛是什么动物?",
"mode": "education",
"age_hint": "6",
"session_id": "nm-edu-new",
"profile_id": "nm-test-child"
}观察点:
'plan' in workflow_trace(走 education 路径)- workflow_trace 中无
chatbot(说明 education 模式不走 ReAct,Memory 已自动注入) - 正常有回复
- LLM 主观评估: 蜗牛解释是否准确(软体动物、壳的功能),语言是否适合 6 岁理解水平,有无激发好奇心的追问
先建立一些 parent profile 数据,再新 session 查询
NM-04a(写入,session P1):
{
"text": "我的孩子叫小宝,今年4岁,最近在学认字。",
"mode": "parent",
"age_hint": "4",
"session_id": "nm-parent-A",
"profile_id": "nm-test-parent"
}NM-04b(读取,session P2,新 session 同 profile):
{
"text": "你还记得我孩子叫什么名字吗?",
"mode": "parent",
"age_hint": "4",
"session_id": "nm-parent-B",
"profile_id": "nm-test-parent"
}观察点(NM-04a):
memory.session_updated == true- 正常有回复
- LLM 主观评估(NM-04a): 是否礼貌地确认了孩子信息(小宝、4岁、认字),是否给出符合认字阶段的一条简单建议或鼓励
观察点(NM-04b):
react.selected_act == 'read_memory'(parent 模式跨 session 也必须调工具)'tools' in workflow_trace- 回答包含「小宝」
- LLM 主观评估(NM-04b): 是否自然说出"小宝"(而非机械复述记忆),是否顺势关切孩子的认字进展
| 编号 | 模式 | 场景 | 预期行为 |
|---|---|---|---|
| NM-01a | companion | 写入跨 session 记忆 | session_updated=true |
| NM-01b | companion | 跨 session 召回 | selected_act=read_memory,回答含「蓝」 |
| NM-02a | companion | 写入当前 session | session_updated=true |
| NM-02b | companion | 同 session 回忆 | 无工具调用,直接从上下文回答「小花」 |
| NM-03 | education | profile 自动注入 | 走 plan 路径,无 chatbot 节点 |
| NM-04a | parent | 写入跨 session 记忆 | session_updated=true |
| NM-04b | parent | 跨 session 召回 | selected_act=read_memory,回答含「小宝」 |
背景: ChatRequest 支持
image_url/image_base64字段;视觉推理走 vision LLM(vllm_*配置)。 追问轮不再携带图片,视觉上下文由state_update从 working memory 的recent_turns中恢复。 以下两条用例共用session_id="im-sess-01"形成一个对话对。
{
"text": "这是什么?",
"image_url": "https://ts1.tc.mm.bing.net/th/id/OIP-C.Mykh0w4k5mpqd4xGDKBgPQHaE7?w=193&h=135&c=8&rs=1&qlt=90&o=6&dpr=1.3&pid=3.1&rm=2",
"mode": "companion",
"age_hint": "6",
"session_id": "im-sess-01"
}观察点:
message非空,包含「长颈鹿」(图片内容为长颈鹿)metadata.workflow_trace含chatbot→respond(companion 模式 ReAct 路径)- 视觉 LLM 被调用(请求携带 image_url)
- LLM 主观评估: 图片描述是否生动准确(不仅说"这是长颈鹿",还能描述颜色/场景/动作),语言是否让 6 岁孩子感到惊喜和好奇
必须在 IM-01a 之后、同一测试进程中执行,同 session_id,不携带图片字段
{
"text": "那么图中具体有几只长颈鹿呢?站位情况怎么样?",
"mode": "companion",
"age_hint": "6",
"session_id": "im-sess-01"
}观察点:
message非空,包含数量描述(如「一只」「两只」「三只」或具体数字)- 模型能根据历史中的
image_url继续分析图片(_build_image_content_from_request_or_history生效) - 不应答非所问或说"没有图片"
- LLM 主观评估: 是否清楚说明了长颈鹿的数量和相对站位(如"一只在前、两只在后"),描述是否符合图片实际内容,语言是否儿童友好
| 编号 | 场景 | 关键字段 | 预期行为 |
|---|---|---|---|
| IM-01a | 首轮带图识别 | image_url,message含「长颈鹿」 | 视觉 LLM 描述图片内容 |
| IM-01b | 追问轮(无图) | 同 session,历史含 image_url | 从 working memory 恢复图片继续分析 |
背景:
parent_summaryskill 在家长询问孩子学习情况时触发,调用generate_parent_summary工具, 从 episodic/semantic memory 中检索孩子记录并生成结构化摘要。 以下先写入一条孩子学习数据(education 模式,profile_id默认为default_child),再由家长查询。
{
"text": "今天学了加法,1+1=2,1+2=3,老师还教我们数数到20!",
"mode": "education",
"age_hint": "6",
"session_id": "sk-edu-01"
}观察点:
memory.session_updated == true(写入 working + episodic 记忆,profile_id="default_child")metadata.workflow_trace含plan→execute(education 路径)- 正常有回复
- LLM 主观评估: 是否为孩子学会加法感到鼓励,是否继续引导做更多加法练习,语气是否有趣而非枯燥
必须在 SK-01a 之后执行(或已有 default_child 历史数据)
{
"text": "帮我看看孩子最近的学习情况,有什么进展吗?",
"mode": "parent",
"age_hint": "6",
"session_id": "sk-parent-01"
}观察点:
react.selected_act == 'skill'(触发 skill 路由)'tools' in workflow_trace(generate_parent_summary 工具被调用)metadata.workflow_trace含chatbot→tools→observe→respondmessage非空,内容应汇总孩子的学习记录(如加法、数数等)- LLM 主观评估: 摘要是否结构清晰(按学科分类或时间排列),内容是否准确反映 SK-01a 输入(加法 1+1=2、数数到20),对家长是否有参考价值,有无给出后续学习建议
| 编号 | 模式 | 场景 | 预期行为 |
|---|---|---|---|
| SK-01a | education | 写入孩子学习数据 | session_updated=true,走 plan 路径 |
| SK-01b | parent | 查询孩子学习进展(skill) | selected_act=skill,调用 generate_parent_summary |