# ai-sdr -> dsh-sdr V2 迁移方案 ## 目标与兼容范围 首个兼容目标为 DeepSeek Harness `0.1.0-rc.6`。该版本的模式菜单仍以 agent preset roster 为准,因此继续使用受管 preset 安装器:bundle 激活时把 `presets/sdr` 安装到 `$DSH_HOME/.agent-presets/sdr`。安装后重启 `dsh web`,新建会话即可看到「SDR 数字员工」。 ## V2 推荐架构 ```text DSH Web / SDR preset | | native Cordis tools v Node.js SdrService | +-- server-owned 9-stage state machine +-- atomic JSON durable store +-- KnowledgeBase (versioned enterprise facts) +-- global Lead Registry / canonical dedupe +-- approval hash gate +-- append-only audit events +-- ConnectorRegistry +-- email (dry-run) +-- whatsapp (dry-run placeholder) +-- crm (dry-run placeholder) Legacy path remains available: Python app/ -> FastAPI / Feishu / Pydantic AI / old MCP ``` V1 的 Python MCP + `8765` 端口方案可以继续用于旧集成,但不再是 DSH 插件的运行时依赖。V2 使用 Node.js 原生 ESM,减少一层 HTTP 进程、端口、Python 环境和跨进程审批同步问题;状态、审批和审计仍然是持久化的。 ## 记忆框架选择 不把 Letta、Mem0 这类自编辑 Agent memory 直接作为核心。DSH 已经拥有 Agent loop 和会话记忆;再叠加一个 Agent runtime 会产生双 loop、双上下文和更难审计的审批边界。当前选择是插件内的 `KnowledgeRepository` / `KnowledgeBase`:结构化记录、来源、版本、approved 状态和引用关系全部由服务端掌握。 本地 adapter 使用原子 JSON,保证 Node 20+ 零额外服务即可演示;检索先使用元数据和关键词匹配,接口不绑定实现。生产替换路径是 PostgreSQL + pgvector/pg_trgm,可选 LlamaIndex TS 负责文档解析和混合检索。Letta 如果未来需要,只作为对话经历记忆的可选 adapter,不能写入产品事实、审批凭证或客户抑制状态。 ## 代码边界 保留且不删除:`app/state.py`、`app/agent/`、`app/tools/`、`app/rag/`、`app/guardrails.py`、FastAPI、飞书接入、原有 Python MCP 和测试。它们继续是 ai-sdr 的兼容路径。 新增/重写: - `packages/dsh-sdr/lib/domain.js`:Node 业务内核、阶段机、JsonStore、Lead Registry、哈希审批和 connector registry。 - `packages/dsh-sdr/lib/index.js`:DSH native tool 注册和受管 preset 安装器。 - `packages/dsh-sdr/presets/sdr/agent.cordis.yml`:只声明 persona、userQuestions 和原生工具,不加载 Python MCP。 - `packages/dsh-sdr/test/`:Node 离线回归测试。 暂不删除:Python MCP 仍可被外部旧客户端调用;后续可将其改为调用同一持久化服务或标记为 legacy,不影响 DSH 插件。 ## 工具契约 模型只看到受控高层动作: ```text sdr_create_task sdr_next_step sdr_review_drafts sdr_continue_after_approval sdr_get_task sdr_get_report sdr_audit_log sdr_connector_status sdr_knowledge_search sdr_knowledge_upsert sdr_knowledge_list ``` 不再暴露 `run_sdr_stage(stage=...)` 这种任意阶段参数。`sdr_next_step` 根据持久化状态决定合法阶段;每次只完成一个阶段。这样模型即使产生错误计划,也不能越过状态机。 ## 审批门控 第 5 阶段生成草稿并写入当前 `draft_hash`。第 6 阶段只能由 `sdr_review_drafts` 通过 DSH `userQuestions` 等待人类选择;选择结果写入 `approvals`,包括批准人、时间和草稿哈希。 `sdr_continue_after_approval` 在服务端重新计算所有草稿哈希,并要求每一封草稿都有匹配的人工批准凭证。任何未批准、草稿被修改、任务阶段不正确的情况都会失败关闭。插件没有 `send_email` 或通用 `send` 工具,默认 connector 的 `send()` 固定返回 `blocked-dry-run`。 ## 客户去重 Lead Registry 为每个客户生成 `canonical_lead_id`,并按以下字段去重: ```text canonical domain 标准化邮箱 E.164 电话 market + normalized company 同一 lead + product + campaign_version 幂等键 ``` 去重在任务幂等创建和客户发现时执行,之后的草稿、审批和 connector 仍携带 `canonical_lead_id`。客户被某一活动 claim 后,其他活动不会再次选择它。 KnowledgeBase 的写入需要 `DSH_SDR_AGENT_KNOWLEDGE=1` 显式放行;关闭后仍可检索。自动化流程只消费 approved 记录,草稿和结案报告会记录使用过的 `knowledge_id`,因此用户引导的信息可以跨任务复用但不会变成无来源的隐式记忆。 ## Connector 预留 ConnectorRegistry 接受统一对象接口: ```ts interface OutreachConnector { readonly channel: "email" | "whatsapp" | "crm"; readonly dryRun: boolean; validateRecipient(input: Recipient): Promise; createDraft(input: DraftInput): Promise; send(input: ApprovedMessage): Promise; syncStatus(input: SyncInput): Promise; } ``` 当前内置 `DryRunConnector`,后续可替换为 SMTP/SES、WhatsApp Business API、HubSpot、Salesforce 或飞书实现。真实 connector 必须由部署方显式注册,且仍要经过批准哈希、去重抑制和幂等键检查。 ### 配置权限 部署环境可以通过 `DSH_SDR_DEPLOYMENT_CONFIG_JSON` 提供非敏感基线。若部署流程需要 Agent 参与补全 connector 参数,显式设置 `DSH_SDR_AGENT_CONFIG=1` 后,Agent 可调用 `sdr_configure_connector` 写入运行时覆盖;基线配置保持不变,状态工具会分别返回 deployment config、runtime override 和 effective config。 工具只接受白名单字段和 `password_ref`、`api_key_ref`、`credential_ref` 等引用名,拒绝密码、token 和 API key 原文。`DSH_SDR_AGENT_LIVE_CONFIG=1` 是额外的 live 配置开关,默认关闭;即使开启,当前内置 connector 仍然是 dry-run,真实外发必须另行部署真实实现。 ## 持久化演进 本地首版使用原子写入 JSON 文件,零依赖即可演示。`JsonStore` 是独立边界,后续可实现 SQLite、PostgreSQL 或 Temporal adapter,而不修改工具契约。生产部署建议:PostgreSQL + pg_trgm/pgvector + Temporal TypeScript;本地开发继续使用 JSON/dry-run。 ## API 兼容与风险 - rc.6 的 native tool schema 必须是 `type: object`;所有工具已使用完整 JSON Schema,避免 `type: null`。 - rc.6 受管 preset 安装器不是正式 marketplace API;未来若 DSH 提供原生 preset 注册接口,可替换安装器而不动业务内核。 - preset 只对新建或空白会话生效;重新安装后必须重启 `dsh web` 并新建会话。 - 已安装的旧 V1 preset 可能仍包含 Python MCP 配置;重新执行本地 `dsh plugin ... add` 后,受管 preset 会被覆盖为 V2。遇到无管理标记的 `sdr` 目录时,安装器会拒绝覆盖。 - 默认状态文件位于 DSH 用户目录,测试可设置 `DSH_SDR_DATA_FILE` 使用隔离路径;不读取凭证文件。 - 合成客户域名使用 `.example`,无真实外部请求;真实服务接入前必须新增凭证管理、限流、重试和审计策略。 ## 当前完成度 已完成 Node 原生九阶段运行时、结构化审批、草稿哈希、全局去重、原子状态、审计、Email/WhatsApp/CRM connector 占位、rc.6 preset 和离线测试。未完成的是生产级 PostgreSQL/Temporal 适配、真实渠道 connector 和 DSH Web UI 实际点击录屏;无凭证 dry-run 演示已可运行。