按 P1-Pipecat Flows Agent化与记忆改造交接.md 的 Phase 4 完成。 新增 p1/phase4_acceptance.py:跑真实服务端验 7 条,每条之间查数据库。 实测结果(2026-09-16) 1. "记住我不吃香菜" → confirmed 落库 ✅ 2. **新连接** "我不吃什么?" → "你不吃香菜。" ✅ 连接开始时加载档案生效 3. "我今天吃了香菜" → 不新增 confirmed,模型还追问了是否口味变了 ✅ 4. "其实我现在吃香菜了" → 同键仍只一条 confirmed,值已更新 ✅ 5. "忘掉我的饮食偏好" → 已删除 ✅ 6/7. 访客与跨用户隔离 → 由单测覆盖(客户端不支持切身份) 实现:会话开始时加载稳定档案 - load_stable_memories():取已确认、未过期记忆(身份未确认返回空) - _conversation_node 改用 build_system_prompt,与 legacy 共用同一套 人格与记忆注入格式;记忆进**同一条** system 消息,仍不用 role_message 修掉一个真实缺陷:Flows 路径误用了语音的 max_tokens - 症状:一句"忘掉我的饮食偏好",服务端日志 "Failed to parse function call arguments: {"query": "饮食"(JSON 被截断), 回复为空、记忆没删掉,且**没有任何报错** - 根因:Flows 路径沿用了语音的 max_tokens=200(为"回复要短"而设)。 原始 API 对照实测:max_tokens=200 时模型 finish=stop、根本不调工具; 600 时正常产生 tool_calls 且参数 JSON 完整 - 修法:新增 AgentConfig.max_tokens(AI_AGENT_MAX_TOKENS,默认 600), Flows 路径用它,语音路径仍用 200 新增 client/test_client.py 能力(为验收所需) - RTVIReplies:收服务端 RTVI 文本消息(bot-output / bot-transcription) - 打印 BOT_REPLY 供验收脚本断言 回归:test_agent_tools 45/45、test_flows_runtime 24/24、 test_memory 30/30、test_identity 19/19 服务器已改回 AI_AGENT_RUNTIME=legacy(改默认是 Phase 7 的事)
62 lines
2.6 KiB
Python
62 lines
2.6 KiB
Python
"""Pipecat LLMService 工厂。
|
||
|
||
Phase 1 spike 的选型结论固化在这里:**复用 Pipecat 自带的 `DeepSeekLLMService`**,
|
||
不自写 `FallbackProvider → LLMService` 适配器。
|
||
|
||
理由(详见笔记库 `P1-Flows兼容性Spike结论.md`):
|
||
|
||
1. 它自带 `DeepSeekLLMAdapter`,处理"思考模式下必须把 reasoning_content
|
||
原样回传"这个约束——**正是本项目 P0 阶段踩过的坑**,框架已经做了。
|
||
2. `thinking` 默认就是 disabled,与本项目既有结论一致。
|
||
3. 支持自定义 `base_url`,将来要接本地 vLLM 也能用。
|
||
4. Flows 硬要求 `LLMService` 实例,而 `pipecat.flows` 的 `LLMAdapter`
|
||
**只管摘要生成**,不是 provider 适配器——想保留 `FallbackProvider`
|
||
必须自己写 `LLMService` 子类,成本不划算。
|
||
|
||
**代价**:失去 `FallbackProvider` 的本地 vLLM 降级。当前本机 vLLM 已停用,
|
||
fallback 实际没有可用目标,所以先只用 DeepSeek。将来若需双后端,
|
||
Pipecat 有 `LLMSwitcher` 可用,不必自写。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import logging
|
||
|
||
from ..config import LLMConfig
|
||
|
||
log = logging.getLogger(__name__)
|
||
|
||
|
||
def create_llm_service(cfg: LLMConfig, *, max_tokens: int | None = None):
|
||
"""按配置造 Pipecat LLMService。
|
||
|
||
目前只支持 DeepSeek(OpenAI 兼容)。其它后端返回 None,调用方应当
|
||
回退到 legacy 路径,而不是带着一个坏 service 继续。
|
||
"""
|
||
if cfg.name != "deepseek":
|
||
log.warning("Flows 路径暂不支持后端 %r,请继续使用 legacy", cfg.name)
|
||
return None
|
||
|
||
from pipecat.services.deepseek.llm import DeepSeekLLMService
|
||
|
||
# **必须显式覆盖模型名。**
|
||
# Pipecat 的默认是 `deepseek-v4-flash`,该模型不存在,用默认值会 400。
|
||
# 见 P0-DeepSeek验证报告.md:真实清单只有 deepseek-flash 与 deepseek-v4-pro。
|
||
# max_tokens 由调用方指定(Flows 路径用 AgentConfig.max_tokens,
|
||
# 不是语音的 200)。见 create_llm_service 的 max_tokens 参数。
|
||
settings = DeepSeekLLMService.Settings(
|
||
model=cfg.model,
|
||
# 显式关闭思考。Pipecat 的默认恰好也是 disabled,但写出来更安全:
|
||
# 将来默认值变化时不会悄悄改变行为,也不至于把思考内容送进 TTS。
|
||
thinking=DeepSeekLLMService.ThinkingConfig(type="disabled"),
|
||
max_tokens=max_tokens or cfg.max_tokens,
|
||
)
|
||
return DeepSeekLLMService(
|
||
api_key=cfg.api_key,
|
||
base_url=cfg.base_url,
|
||
settings=settings,
|
||
)
|
||
|
||
|
||
__all__ = ["create_llm_service"]
|