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 检查与拷贝命令提示。
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
#!/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()))
|
||||
Reference in New Issue
Block a user