Perlica v1:单目标群任务助手可实施规格 #1
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
feniota/perlica#1
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Perlica v1:单目标群任务助手可实施规格
版本:1;整理日期:2026-10-15;追踪 Issue:#1。
Problem Statement
Perlica 需要在受控 Kubernetes 集群中为一个可信 QQ 群执行开发与运维任务。当前资料已经确定了目标群边界、消息保护、单活动任务、持久根文件系统沙箱、策略网关、审批和审计等决定,但还缺少一份按实现顺序组织、可由工程任务直接执行并可验收的规格。
第一版的核心风险是边界被隐式扩大:群外消息进入模型、凭据样内容进入持久层或出站服务、审批被自然语言复用、沙箱只持久化工作区、Kubernetes MCP 绕过策略网关,或 rootfs 的未验证实现被当成已完成能力。本规格将这些边界转换为模块契约、状态、验收条件和测试证据。
Solution
实现一个 TypeScript/Node.js 单实例服务,运行于 Kubernetes,并通过 NapCatQQ 连接一个配置指定的目标 QQ 群。消息入口先执行群边界、去重和内容保护,再写入 SQLite;只有明确
@Perlica的目标群消息创建 Agent 任务。任务协调器维护一个持久状态机和唯一活动任务,使用 Pi Agent SDK 运行受限 Session,只注册 Perlica 的结构化工具。所有工具调用都经过 Perlica 工具网关和执行代理。执行代理把动作规范化为动作计划,按
allow/ask/block判定,执行或发出一次性审批请求,并记录实际参数、身份、目标和保护后的结果。沙箱、Kubernetes、Tavily、Web Fetch、Spectra、附件和记忆组件都位于该边界之后。沙箱由可替换的
SandboxBackend提供,最多三个,创建时继承唯一的全局沙箱镜像配置。沙箱目标语义是可恢复的整个根文件系统;节点绑定、持久 upper/work、重挂载、崩溃恢复和清理必须先通过真实 Linux 节点 rootfs 实测闸门。闸门未通过时不声称沙箱能力完成,也不把只持久化工作区作为等价实现。第一版只读 Web 控制台复用任务、审批、沙箱和审计查询,不直接执行动作。QQ 只发送普通文本;长 Markdown 只有在模型明确调用
spectra_upload且内容完成保护后才上传,QQ 最终收到普通文本短链接。User Stories
@Perlica发起任务,以便只有我明确请求时才消耗 Agent 资源。.approve和.deny在模型上下文之外解析,以便审批消息不会成为可被模型解释的自然语言资料。MemoryAdapter接入并可替换,以便其 Pi 生命周期耦合不会破坏任务边界。Implementation Decisions
边界与角色
targetGroupId;入口第一步比较群 ID。非目标群事件在内存丢弃,不写业务日志、数据库或模型。dot-command-router解析,不进入 Agent Session。第一版至少支持.approve <approval-id> <short-code>、.deny <approval-id>,以及 Owner 的沙箱、镜像介绍和扩展管理命令。NapCatAdapter内转换为IncomingGroupMessage;领域模块不依赖 OneBot/QQ 事件类型。内容保护与数据流
ContentProtection出站入口执行结构化抽取、凭据/私钥/Token/kubeconfig 模式检测、不可逆替换、最终序列化和再次扫描。content、status(redacted、scanned_no_findings、scan_incomplete、not_scanned)、policyVersion和findingCount。scan_incomplete不得作为安全通过。任务与 Session
received → contextualizing → running → waiting_approval → completing → completed/failed/cancelled,状态持久化并通过数据库唯一约束保证一个活动任务。@Perlica创建任务。活动任务期间后续提及仍可脱敏入库,但不创建任务,立即通过 Outbox 回复忙碌提示。group_search_history获取。Pi 与工具网关
@earendil-works/pi-coding-agent,关闭默认bash、read、write、edit,只注册结构化 Perlica 工具。sandbox_exec、sandbox_read、sandbox_write、group_recent_context、group_search_history、get_attachment、web_search、web_fetch、spectra_upload、sandbox_manage、受限cluster_*、memory_recall和memory_candidate。MemoryAdapter隔离不带 scope 的jayzeng/pi-memory。先验证初始化、工具注册、Session 边界、上下文注入、删除/恢复和 qmd 失败降级;若不能安全隔离,复用其 Markdown 记忆、daily log、scratchpad、qmd 和删除恢复语义而自行实现。群消息、历史检索、Session 摘要和审计由 Perlica 自己拥有。执行策略与审批
Low:沙箱内受资源、网络和运行时限制的文件/命令、测试和构建,自动执行并审计。Medium:已配置明确范围的允许域名 GET/HEAD、依赖安装、集群只读查询、创建允许类型沙箱、修改描述,自动执行并告知。High:集群写入/删除、沙箱删除、上传、Secret/凭据相关动作、MCP/Skill 安装、节点/RBAC/网络策略变化,逐项 Owner 审批。Block:无法判定影响范围、宿主访问、绕过执行代理、向模型或沙箱提供长期凭据、策略自修改,直接拒绝。groupId、Owner QQ ID、taskId、计划哈希、目标摘要、10 分钟有效期和一次消费。SQLite 与数据模型
MessageStore、TaskStore、ApprovalStore、SandboxStore和AuditStore,不把 SQLite API 暴露给领域模块。group_messages、attachments、tasks、task_events、session_summaries、approvals、sandbox_image_config、sandboxes、sandbox_operations、audit_events和outbox_messages。group_messages强制目标群 ID、QQ message ID 唯一、保护状态和保留期;FTS5 只索引脱敏文本。group_search_history后端强制目标群、时间跨度、结果数和字符上限,模型不提交 SQL,结果按相关性/时间排序后再次保护。SandboxBackend 与 rootfs 闸门
currentImage()、create(spec)、inspect(name)、execute(name, action)、stop(name)、delete(name)、reconcile()。SandboxImageConfig包含镜像引用、固定 digest、介绍和 revision。SandboxSpec包含不可变名称、用途描述、硬节点名、资源限制、网络策略和生命周期策略,不接受每沙箱镜像。d_type的本地文件系统和一个固定 digest;必须验证 layer 解包、创建/覆盖/删除、whiteout、目录 rename、权限、硬链接、大文件 copy-up、卸载重挂载、runner/containerd 重启、kill/fsync、最小断电一致性、节点 pin、local PV RWO 冲突、节点离线与恢复清理。image-switching:暂停创建、停止并销毁全部现有沙箱、确认清理后激活新配置;任一清理失败进入image-switch-blocked。只改介绍只递增 revision。lastActiveAt仅由命令、文件读写、安装等实际触碰刷新,元数据观察不刷新。连续七天无活跃触碰进入删除;删除失败进入告警与孤儿资源表。节点故障标记lost/deleted,不迁移,恢复后只清理残留。Kubernetes、Web 与扩展
containers/kubernetes-mcp-server位于独立 ClusterToolProvider 和 Perlica 策略网关之后,Pi 不直连 MCP。MCP confirmation fallback 不能作为 Perlica 授权。read_only=true、工具白名单、禁用config、Secret、pods_exec、pods_run和写工具,独立 ServiceAccount、窄 namespace RoleBinding、NetworkPolicy。工具结果再次保护。交付顺序与启用条件
Testing Decisions
测试边界
主要在最高的应用边界测试:输入标准化
IncomingGroupMessage或控制命令,通过真实内容保护、SQLite、任务协调器、策略、执行代理和 Outbox,观察 QQ 回复、Session 输入、后端调用和持久化结果。模型、时钟、NapCat 传输、外部 API 和基础设施后端使用可控替身;只在契约和发布阶段做窄范围真实连接测试。rootfs 是例外的真实环境闸门,必须在真实 Linux 节点、真实目标文件系统和实际 runner/containerd 组合上验证。fake backend、普通 PVC 或单元测试不能证明 rootfs 闸门通过。
必须覆盖的行为
当前仓库只有需求、计划、术语、ADR 和 research,没有现成代码或测试可复用。首批使用 Vitest 在上述应用边界建立行为测试,真实 SQLite 使用临时数据库,可控时钟覆盖期限和重启;后续沿用该边界,不为每个内部模块另建同义测试层。
.approve/.deny的群、Owner、任务、计划哈希、短码、过期和一次性消费;修改任一计划字段必须使旧票据失效;审批消息不得进入 Session。验收证据
第一版发布前必须提供自动化测试报告、迁移/恢复记录、rootfs 原型报告、策略与逃逸面报告、MCP/RBAC 配置快照、脱敏回归样本和一次真实 NapCat 群 E2E。以下每项均需可观察证据:
@Perlica不创建任务,活动任务期间只返回 busy。测试只断言外部行为和安全边界,不锁定具体 SQLite 查询、Pi 扩展内部函数、MCP 实现细节或 runner 私有结构。没有对应真实环境证据的能力只能标记为未启用或实验性,不能纳入已通过验收的第一版。
Out of Scope
/workspace持久化或模型记忆文件宣称为整个 rootfs 持久化。pods_exec、pods_run、Helm、Tekton、KubeVirt 或 Secret 操作。Further Notes
GLOSSARY.md的“目标群”“控制命令”“Agent 任务”“Session”“内容保护”“沙箱”“全局沙箱镜像”“动作计划”“执行代理”“审批票据”“集群工具适配器”和“扩展”等术语。ready-for-agent表示本文规格已具备实现入口,不表示 rootfs 实测闸门、真实 QQ 连接、真实外部 API 或生产发布已经通过。