按 P1-Pipecat Flows Agent化与记忆改造交接.md 的 Phase 2 完成。
legacy 路径行为不变,只是改为复用新工具层。
新增 app/agent/
- memory_policy.py:记忆写入/删除的裁决层。模型只提议,策略决定。
不依赖 LLM/网络/真实存储,可纯单元测试。
- 身份未确认 → 一律 reject,不碰私人记忆
- 模型传 owner_user_id/scope 等越权字段 → reject(不静默忽略)
- 只有用户明确说"记住/以后/别忘了/我的偏好是"才写 confirmed
- "记住我今天吃了香菜"这类一次性事件 → 降级 candidate
- 不采信模型自称的"用户明确要求"
- 模糊/多条删除 → needs_confirmation,不猜目标
- tools.py:工具 schema 与执行器,legacy 与 flows 共用。
- 身份由 ToolContext 注入,**不在工具参数里**
- 身份未确认时不暴露任何记忆工具(比暴露后拒绝更干净)
- 写入结果如实回报:candidate 明确告知"尚未生效",
删除如实返回"已删除/没有找到/需要确认",不把"请求已收到"说成"已做到"
- 工具失败转成给模型的文本,不拖垮本轮对话
- llm_service.py:Pipecat LLMService 工厂,固化 Phase 1 spike 的选型结论
(复用 DeepSeekLLMService;必须显式覆盖模型名为 deepseek-flash,
因为 Pipecat 默认的 deepseek-v4-flash 不存在)
改造 app/engine.py
- _execute_tool 改为委托 app/agent/tools.execute(),删除重复实现
- 本轮用户原话挂到实例上,供策略层判定语义等级
- 移除不再使用的 SearchError 导入
新增 app/tests/test_agent_tools.py:45 项断言,**不依赖 LLM/网络/SQLite**
(用 fake store)。覆盖交接文档 §十 的清单:越权参数、未确认身份、
明确记住 vs 推断、同 key 幂等、模糊删除、精确删除、跨用户隔离。
回归:test_agent_tools 45/45、test_memory 30/30、test_identity 19/19、
test_search 12/12、test_transport 4/4、test_profile_memory 22/22,无退化。
60 lines
2.4 KiB
Python
60 lines
2.4 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):
|
||
"""按配置造 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。
|
||
settings = DeepSeekLLMService.Settings(
|
||
model=cfg.model,
|
||
# 显式关闭思考。Pipecat 的默认恰好也是 disabled,但写出来更安全:
|
||
# 将来默认值变化时不会悄悄改变行为,也不至于把思考内容送进 TTS。
|
||
thinking=DeepSeekLLMService.ThinkingConfig(type="disabled"),
|
||
max_tokens=cfg.max_tokens,
|
||
)
|
||
return DeepSeekLLMService(
|
||
api_key=cfg.api_key,
|
||
base_url=cfg.base_url,
|
||
settings=settings,
|
||
)
|
||
|
||
|
||
__all__ = ["create_llm_service"]
|