# PRD:dsh-plugin-log-forwarder 实时日志转发插件 > 在 DeepSeek-Harness 会话中将 Agent 运行时的全部事件实时转发到外部日志系统(WebSocket / Loki / 本地文件),支持外部页面实时监控 Agent 每一步思考与工具执行,适合调试、监控、演示场景。 元信息 - 作者:自定义 - 版本:V1.0 - 状态:开发中 - 更新日期:2026-09-06 --- ## 1. 背景与痛点 ### 1.1 用户痛点 - 调试 Agent 时只能在 Harness 界面里看输出,无法在外部终端 / 网页 / IDE 中实时观察 Agent 的思考过程与工具调用。 - 长任务运行时离开 Harness 窗口,回来后不知道 Agent 进行到哪一步、有没有报错,缺少实时监控能力。 - 做 Agent 演示时,想在大屏幕 / 独立网页上展示 Agent 实时工作流,Harness 原生界面不适合投屏展示。 - 日志分散在 Harness 内部,无法统一接入公司已有的日志平台(Loki / ELK)做集中分析与告警。 - 多会话并行运行时,无法在一个面板中同时监控多个 Agent 的运行状态。 - 会话复盘导出插件(session-export)是**事后导出**,缺少**实时流式推送**能力。 ### 1.2 目标用户 Agent 开发者(需要实时调试)、运维人员(需要集中监控)、技术博主(需要实时演示 Agent 工作流)、需要将 Agent 日志接入现有可观测性平台的团队。 ### 1.3 插件定位 一句话定位:Harness 实时日志管道插件,**插件负责事件采集与多通道转发**;不干预 Agent 正常运行,仅做旁路实时推送;支持 WebSocket 实时推送、Loki 远程写入、本地文件追加三种输出通道,可同时启用多个。 --- ## 2. 范围界定 ### 2.1 ✅ V1.0 必须实现(P0) - [ ] 监听 Harness 全生命周期事件:`turn:before` / `turn:after` / `user:message` / `tool:before` / `tool:after` / `plugin:unload` - [ ] 事件标准化:将不同事件统一为标准 JSON 格式(含时间戳、会话ID、轮次、事件类型、payload) - [ ] 输出通道1:WebSocket Server,外部客户端可连接接收实时事件流 - [ ] 输出通道2:本地文件追加,将事件写入 JSONL 格式日志文件 - [ ] 输出通道3:Loki 远程写入(HTTP API),将事件推送到 Loki 日志系统 - [ ] 多通道可同时启用,互不影响 - [ ] 事件过滤:可配置只转发特定事件类型(如只转发 tool_call 和 error) - [ ] 事件脱敏:可配置对 payload 中的敏感字段(api_key、password、token)自动脱敏 - [ ] 断线重连:Loki 通道网络失败时自动退避重试,不丢失事件(内存缓冲) - [ ] WebSocket 通道提供简单的状态页面(连接数、已转发事件数) - [ ] 侧边 UI 面板:展示各通道状态(已连接/断开)、已转发事件数、速率 - [ ] 插件卸载时关闭所有通道连接、清空缓冲、无残留 ### 2.2 ⭕ V1.1 后续迭代(P1,本期不做) - [ ] 输出通道:Elasticsearch 直接写入 - [ ] 输出通道:飞书 / 企业微信 Webhook 推送(仅报错和关键事件) - [ ] 输出通道:MQTT / Kafka 消息队列 - [ ] 内置实时监控 Dashboard(HTML 页面,展示事件时间轴、工具调用分布、报错告警) - [ ] 多会话聚合转发(所有会话事件带 session_id 区分,统一推送到一个通道) - [ ] 事件规则引擎:满足条件的事件触发额外动作(如报错时发通知) - [ ] 日志文件按大小 / 日期自动切割轮转 ### 2.3 ❌ 不在本版本做(明确边界) - ❌ 不做日志存储与查询(仅转发,不做数据库) - ❌ 不做日志可视化 Dashboard(V1.0 仅提供原始事件流,Dashboard 由外部工具实现) - ❌ 不修改模型输出内容,仅做旁路采集与转发 - ❌ 不做日志的长期持久化(本地文件仅追加,不做索引与查询) --- ## 3. 功能详细需求 ### 3.1 用户触发方式 - 插件加载后根据配置自动启动转发,无需用户手动触发 - 自然语言触发:`查看日志转发状态`、`暂停日志转发`、`恢复日志转发` - 工具调用触发:`log_forwarder_status()`、`log_forwarder_pause()`、`log_forwarder_resume()` - 侧边 UI 面板:展示各通道状态 + 暂停/恢复按钮 - 事件自动触发:插件加载即开始采集事件并转发 ### 3.2 全部功能列表 #### 功能1:全量事件采集与标准化 订阅 Harness 事件,将每种事件转换为标准 JSON 格式: ```json { "id": "evt_abc123", "timestamp": "2026-09-06T10:30:00.123Z", "session_id": "sess_xyz", "turn_index": 3, "type": "tool_call", "payload": { "tool_name": "shell", "args": {"command": "ls -la"} } } ``` 事件类型包括:`session_start`、`user_input`、`turn_start`、`model_output`、`reasoning`、`tool_call`、`tool_result`、`tool_error`、`turn_end`、`session_end`。 #### 功能2:WebSocket 输出通道 - 插件启动一个本地 WebSocket Server(默认端口 18765,可配置) - 外部客户端连接 `ws://localhost:18765` 后,实时接收 JSON 格式事件流 - 支持多客户端同时连接,事件广播给所有连接的客户端 - 提供 HTTP 状态端点 `http://localhost:18765/status`,返回 JSON:当前连接数、已转发事件数、运行时长 - 客户端断开后自动清理连接资源 - 端口被占用时自动尝试下一个端口,或报错提示 #### 功能3:本地文件输出通道 - 将事件以 JSONL 格式(每行一个 JSON 对象)追加写入本地文件 - 默认路径:`/logs/session-.jsonl` - 可配置自定义输出路径 - 文件不存在时自动创建,目录不存在时自动创建 - 每次写入后 flush,确保数据不落内存 - 可配置最大文件大小,超出后停止写入并提示(V1.0 不做自动切割) #### 功能4:Loki 输出通道 - 通过 Loki HTTP API(`/loki/api/v1/push`)将事件推送到远程 Loki 实例 - 事件作为日志行,标签包括:`session_id`、`event_type`、`turn_index` - 支持配置 Loki 地址、租户 ID(X-Scope-OrgID)、认证 Token - 网络失败时自动退避重试(指数退避,最大重试 5 次) - 重试期间事件存入内存缓冲队列,缓冲有上限(默认 1000 条),超出后丢弃最旧事件并记录警告 - Loki 恢复后自动将缓冲中的事件批量推送 #### 功能5:事件过滤 - 可配置 `includeEventTypes` 白名单:只转发列表中的事件类型 - 可配置 `excludeEventTypes` 黑名单:不转发列表中的事件类型 - 白名单优先级高于黑名单 - 默认转发全部事件类型 #### 功能6:敏感信息脱敏 - 可配置 `sensitiveFields` 列表(默认包含:api_key、apikey、password、token、secret、authorization) - 事件 payload 中匹配到这些字段名时,值自动替换为 `[REDACTED]` - 支持嵌套对象中的敏感字段递归脱敏 - 脱敏在转发前执行,所有输出通道统一生效 #### 功能7:通道状态监控 - 实时跟踪每个输出通道的状态:运行中 / 已暂停 / 已断开 / 报错 - 统计每个通道的:已转发事件数、转发失败数、当前缓冲队列长度 - 侧边 UI 面板展示各通道状态卡片 - 通道异常时在面板显示红色告警 #### 功能8:暂停与恢复 - 支持全局暂停转发(所有通道停止接收新事件,但 WebSocket Server 保持运行) - 暂停期间事件不采集、不缓冲 - 恢复后继续采集新事件 - 可通过工具调用或侧边面板按钮操作 ### 3.3 配置项(对应 cordis.yml schema) | 配置key | 类型 | 默认值 | 说明 | |---|---|---|---| | enable | boolean | true | 插件总开关 | | channels.websocket.enable | boolean | true | 是否启用 WebSocket 通道 | | channels.websocket.port | number | 18765 | WebSocket Server 端口 | | channels.file.enable | boolean | false | 是否启用本地文件通道 | | channels.file.path | string | "" | 自定义文件路径,为空则使用默认路径 | | channels.loki.enable | boolean | false | 是否启用 Loki 通道 | | channels.loki.url | string | "" | Loki 地址,如 http://localhost:3100 | | channels.loki.tenantId | string | "" | Loki 租户 ID(X-Scope-OrgID) | | channels.loki.token | string | "" | Loki 认证 Token | | channels.loki.bufferSize | number | 1000 | Loki 内存缓冲队列上限 | | filter.includeEventTypes | string[] | [] | 事件白名单,为空表示全部 | | filter.excludeEventTypes | string[] | [] | 事件黑名单 | | redaction.enable | boolean | true | 是否启用敏感信息脱敏 | | redaction.sensitiveFields | string[] | ["api_key","password","token","secret"] | 脱敏字段名列表 | | autoStart | boolean | true | 插件加载后是否自动开始转发 | ### 3.4 UI 表现 - 侧边面板:各通道状态卡片(WebSocket/文件/Loki),显示运行状态、已转发事件数、失败数 - 全局统计:总事件数、总转发数、总丢弃数、运行时长 - 按钮:【暂停转发】【恢复转发】【清空统计】 - WebSocket 通道显示连接客户端数 - Loki 通道显示缓冲队列长度 - 不污染主聊天流 --- ## 4. 状态与数据设计 ### 4.1 内存状态结构 ```typescript interface StandardEvent { id: string; timestamp: string; // ISO 8601 sessionId: string; turnIndex: number; type: string; payload: Record; } interface ChannelStats { forwarded: number; // 已转发事件数 failed: number; // 转发失败数 dropped: number; // 丢弃事件数(缓冲溢出) bufferSize: number; // 当前缓冲队列长度 } interface ChannelState { name: "websocket" | "file" | "loki"; enabled: boolean; status: "running" | "paused" | "disconnected" | "error"; stats: ChannelStats; error?: string; } interface PluginState { running: boolean; // 全局运行状态 startTime: number; // 启动时间戳 totalEvents: number; // 总采集事件数 channels: Map; // 各通道状态 lokiBuffer: StandardEvent[]; // Loki 缓冲队列 wsClients: Set; // WebSocket 客户端连接集合 } ``` - 会话隔离:事件带 `sessionId` 区分,多会话事件统一转发 - 持久化:仅内存保存运行状态;文件通道将事件持久化到磁盘 ### 4.2 生命周期行为 1. **插件加载 apply(ctx)** - 初始化各输出通道(启动 WebSocket Server、打开文件句柄、测试 Loki 连接) - 订阅 Harness 事件 - 注册状态查询/暂停/恢复工具 - 注册侧边 UI 面板 - 根据 `autoStart` 配置决定是否立即开始转发 2. **插件卸载 plugin:unload** - 停止采集事件 - 关闭 WebSocket Server,断开所有客户端连接 - 关闭文件句柄 - 推送 Loki 缓冲队列中剩余事件(有超时上限,最多等 5 秒) - 清空所有状态与缓冲 - 移除事件监听与 UI 面板 - 卸载后无任何残留进程或连接 ### 4.3 自动休眠逻辑 - 全局暂停时停止采集事件,但 WebSocket Server 保持运行(客户端可连接但收不到数据) - 单个通道报错时自动标记为 `error` 状态,不影响其他通道继续运行 - Loki 通道连续失败超过 10 次时自动标记为 `disconnected`,停止重试,需用户手动恢复 --- ## 5. 工具与事件清单 ### 5.1 注册给大模型调用的 Tool 列表 | tool名称 | 入参 | 返回 | 用途 | |---|---|---|---| | log_forwarder_status | 无入参 | {running:boolean, totalEvents:number, channels: ChannelState[]} | 查询日志转发器全局状态 | | log_forwarder_pause | 无入参 | {success:boolean} | 暂停全局转发 | | log_forwarder_resume | 无入参 | {success:boolean} | 恢复全局转发 | | log_forwarder_channel_status | channel:"websocket"\|"file"\|"loki" | ChannelState | 查询单个通道详细状态 | ### 5.2 监听 Harness 事件列表 | 事件名 | 用途 | |---|---| | `turn:before` | 采集轮次开始事件 | | `turn:after` | 采集模型输出、轮次结束事件 | | `user:message` | 采集用户输入事件 | | `tool:before` | 采集工具调用事件 | | `tool:after` | 采集工具返回/报错事件 | | `plugin:unload` | 资源清理,关闭所有通道连接 | --- ## 6. 安全约束 & 异常处理 ### 6.1 安全规则 - WebSocket Server 仅绑定 `127.0.0.1`,不监听公网,防止外部未授权访问 - Loki 通道的 Token 等敏感配置在 UI 面板中掩码显示 - 事件 payload 中的敏感字段自动脱敏后才转发 - 本地文件通道写入路径做校验,禁止写入系统关键目录 - 插件不执行任何 shell 命令,不读取用户文件(除日志文件写入外) ### 6.2 异常场景处理 1. **WebSocket 端口被占用**:自动尝试端口+1,最多尝试 5 个端口;全部失败则标记 WebSocket 通道为 error,其他通道正常运行 2. **WebSocket 客户端异常断开**:自动从连接集合移除,不影响其他客户端 3. **本地文件写入失败(磁盘满/权限不足)**:标记文件通道为 error,打印警告,不崩溃 4. **Loki 网络失败**:指数退避重试,事件入缓冲;缓冲满后丢弃最旧事件并记录 5. **Loki 连续失败超阈值**:自动断开,停止重试,等待手动恢复 6. **事件 JSON 序列化失败**:跳过该事件,记录错误计数,不影响后续事件 7. **插件卸载时 Loki 缓冲还有数据**:尝试推送(最多 5 秒超时),超时后丢弃并记录 --- ## 7. 手工测试用例 - [ ] 用例1:插件加载,验证 WebSocket Server 启动,外部客户端可连接并接收事件 - [ ] 用例2:多轮对话含工具调用,验证 WebSocket 客户端实时收到全部事件,JSON 格式正确 - [ ] 用例3:启用文件通道,验证日志文件以 JSONL 格式写入,每行一个 JSON - [ ] 用例4:启用 Loki 通道,验证事件推送到 Loki,标签正确(session_id、event_type) - [ ] 用例5:Loki 地址不可达,验证自动退避重试、事件入缓冲、不崩溃 - [ ] 用例6:工具入参包含 api_key,验证转发事件中该字段被脱敏为 [REDACTED] - [ ] 用例7:配置 includeEventTypes 只包含 tool_call,验证其他事件不被转发 - [ ] 用例8:暂停转发,验证新事件不被采集;恢复后继续采集 - [ ] 用例9:侧边面板实时展示各通道状态与统计数据 - [ ] 用例10:插件卸载,验证 WebSocket Server 关闭、文件句柄关闭、Loki 缓冲清空、无残留连接 --- ## 8. 风险与待解决问题 | 风险 | 缓解方案 | |---|---| | WebSocket 端口冲突 | 自动端口探测 + 可配置端口 | | Loki 缓冲队列内存溢出 | 设置 bufferSize 上限 + 溢出丢弃策略 | | 高频事件导致文件写入 IO 瓶颈 | 批量写入(每 100ms 或满 50 条 flush 一次) | | 敏感信息脱敏不彻底(嵌套对象/数组) | 递归遍历所有嵌套层级,匹配字段名不区分大小写 | | Harness 事件 payload 结构随版本变化 | 事件标准化做容错,未知字段原样保留,不硬依赖 | --- ## 9. 交付物清单 - cordis.yml - src/index.ts(插件入口,事件订阅与通道管理) - src/event-normalizer.ts(事件标准化器) - src/redaction.ts(敏感信息脱敏器) - src/channels/websocket.ts(WebSocket Server 通道) - src/channels/file.ts(本地文件通道) - src/channels/loki.ts(Loki 远程写入通道) - src/channels/base.ts(通道基类接口) - src/types.ts(类型定义) - package.json / tsconfig.json - docs/PRD.md(本文档) - docs/api.md(接口文档) - README.md 用户文档(含 WebSocket 客户端接入示例、Loki 配置说明) - CHANGELOG.md - LICENSE