Files
ylhan c37a968548 Phase 1 spike:Pipecat Flows 兼容性验证,7 项全通过
按 P1-Pipecat Flows Agent化与记忆改造交接.md 的 Phase 1 要求,
只做独立 spike,不改生产入口。

新增
- p1/flows_spike.py:探索脚本,带请求体抓取与逐项诊断
- app/tests/test_flows_spike.py:断言版,复用探索脚本的检查项避免漂移

验证结果(9 个断言全过)
- DeepSeek 在 Flows 中流式输出中文(14–22 个 token 级分块)
- 工具调用参数聚合完整、无重复拼接;回灌后能出最终回答
- worker.cancel() 能立即终止 LLM 流
- role_message 生效(模型自称"桌面陪伴小助手")
- context 里只有一条 system 消息

三条最重要的结论
1. **不要装 pipecat-ai-flows**。Pipecat 1.10 内置 Flows(pipecat.flows),
   装了会报 _warn_if_standalone_flows_installed;且独立包 1.4.0 会把
   pipecat 从 1.10.0 降到 1.4.0。requirements.txt 已加说明。
2. **必须用 PipelineRunner().run(worker)**。直接 worker.run() 时 runner
   没挂上,工具执行失败并抛 "runner is not set; call attach() first",
   该异常被转成给 LLM 的通用错误串,handler 从不执行,默认日志看不到。
3. **await flow.initialize(node)**。它是协程但签名显示 -> None,
   漏 await 不报错,只是 role_message/task_messages/工具全部静默失效——
   极易误判成"框架不支持这些特性"。

另两条
- respond_immediately 默认 True,initialize 会立刻触发一轮回复,
  做归因测试时必须清空观察窗口,否则会归因错。
- role_message(走 system_instruction)与 task_messages 各产生一条
  system 消息,两者不要同时用;切回本地 vLLM 时会 400。

LLMService 选择:复用 Pipecat 的 DeepSeekLLMService。它自带
DeepSeekLLMAdapter 处理 reasoning_content 回传约束(我们 P0 踩过的坑),
thinking 默认 disabled,与既有结论一致。代价是失去本地 vLLM 降级
(Flows 要求 LLMService 实例,FallbackProvider 不能直接用;且当前
本机 vLLM 已停用,fallback 实际无可用目标)。

环境:NLTK punkt_tab 本地也需预装(服务器已有),spike 加了 fail-fast
检查与拷贝命令提示。
2026-09-16 13:13:39 +08:00

55 lines
1.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Pipecat Flows 兼容性回归测试(Phase 1 spike 的断言版)。
对应交接文档 §九 Phase 1 的 7 项验证。**需要真实 DeepSeek 网络**,
所以和 test_search_live.py 一样属于"现场测试",不进离线回归集。
用法:
~/venvs/ai_assistant/bin/python app/tests/test_flows_spike.py
实现体在 p1/flows_spike.py——那里是探索脚本(带请求体抓取、逐项诊断),
这里只做断言与退出码,便于反复跑和接进 CI。
"""
from __future__ import annotations
import asyncio
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent.parent
sys.path.insert(0, str(ROOT))
# 复用探索脚本里的检查项,避免两处实现漂移
sys.path.insert(0, str(ROOT / "p1"))
import flows_spike as spike # noqa: E402
async def main() -> int:
print("=" * 64)
print("Pipecat Flows 兼容性回归(Phase 1 spike 断言版)")
print("=" * 64)
results: list[dict] = []
for key, (label, fn) in spike.ITEMS.items():
print(f"\n--- {key}. {label} ---")
try:
results.extend(await fn())
except Exception as e: # noqa: BLE001
import traceback
traceback.print_exc()
results.append(spike.check(f"{key}. 执行", False,
f"{type(e).__name__}: {e}"))
print("\n" + "=" * 64)
ok = sum(1 for r in results if r["ok"])
print(f"通过 {ok} / {len(results)}")
for r in results:
if not r["ok"]:
print(f" 未通过: {r['name']} — {r['detail']}")
return 0 if ok == len(results) else 1
if __name__ == "__main__":
sys.exit(asyncio.run(main()))