# AGENTS.md 本文件给「后续接手这个仓库的人类或 AI Agent」看,说明项目约定、代码结构、改动时的注意事项。 如果你只想**使用**这个插件,请直接看 [README.md](./README.md)。 --- ## 1. 项目定位 `dsh-wechat-auto-reply` 是一个 **DeepSeek Harness(DSH)插件**: 通过 Python 的 `wxauto` 监听 Windows 微信私聊消息,交给 DSH 的 Agent(或 LLM 服务)生成回复, 再通过 wxauto 发回微信。属于**技术演示项目**,仅建议用小号测试。 核心约束(修改代码前务必知道): - **不引入重型依赖**。运行时只依赖 `yaml` 一个 npm 包;Python 侧只依赖 `wxauto`。 新增依赖前先想清楚是否真的必要。 - **桥接进程必须可崩溃**。wxauto 依赖微信 UI 自动化,随时可能因微改版失效; Node 侧必须能自动重启它(`src/wechat/bridge.js` 里的 `_onExit` / `_restart`), 不能因为桥接挂了就把 DSH 插件拖死。 - **协议只有 JSON 行**。`python/wxauto_bridge.py` 的 stdout **只允许**输出协议 JSON; wxauto / uiautomation 的 `print` 必须被重定向到 stderr(脚本开头已经把 `sys.stdout` 换成 stderr)。 改 Python 代码时不要直接用 `print()` 输出调试信息,用 `log()`。 - **插件启动失败不能崩 DSH**。`src/index.js` 的 `apply()` 内部自行捕获异常并写日志, 保证用户配置错误时 DSH 仍能正常启动。 --- ## 2. 目录结构 ``` dsh-wechat-auto-reply/ ├── package.json # npm 元数据 + dsh.bundle.patch 声明(DSH 插件入口) ├── cordis.patch.yml # DSH 的插件行 patch(insert 本插件) ├── config.yaml # 用户配置(白名单 / 延迟 / 日志路径 / 后端选择) ├── AGENTS.md # 本文件 ├── README.md # 用户文档(第一部分是免责声明,不要删改顺序) ├── LICENSE # MIT ├── scripts/ │ └── check-syntax.js # npm run check:node --check + py_compile ├── src/ │ ├── index.js # DSH 插件入口:export name / apply(ctx, config) │ ├── config.js # 读 config.yaml + 校验 + 归一化 │ ├── logger.js # 聊天 jsonl 日志 + 运行日志 │ ├── context.js # 单用户对话上下文(裁剪 + 持久化) │ ├── runtime.js # 业务编排:白名单/指令/暂停/延迟/生成/发送/落日志 │ ├── util.js # 通用小工具(延迟、超时、文本清洗、错误信息) │ ├── dev.js # npm run dev:MockBridge + MockEngine 全链路自测 │ ├── dsh/ │ │ ├── factory.js # 按 config.dsh.backend 选择引擎 │ │ ├── agent-engine.js# 后端 A:ctx.agents(真正的 DSH Agent 会话) │ │ └── llm-engine.js # 后端 B:ctx.llm.stream(直接调用模型) │ └── wechat/ │ ├── bridge.js # 拉起 Python 桥接进程,JSON 行协议双向通信 │ └── mock-bridge.js # 本地开发用的假微信 └── python/ ├── wxauto_bridge.py # 真实微信收发(wxauto) └── requirements.txt ``` --- ## 3. 模块边界(改代码时先找准位置) | 需求 | 改哪里 | | --- | --- | | 新增配置项 | `config.yaml` 注释 + `src/config.js` 的 `DEFAULT_CONFIG` 与校验 + README 配置表 | | 新增聊天指令(如 /help 之后再加 /clear) | `src/runtime.js` 的 `COMMAND_ALIASES` / `HELP_TEXT` / `handleCommand()` | | 换一种"怎么生成回复" | 实现一个 `{name, supportsReset, reply({contact,text}), reset(contact), dispose()}` 对象,在 `src/dsh/factory.js` 里注册 | | 微信收发行为(等待、重连、超时) | `src/wechat/bridge.js`;**消息内容解析**在 `python/wxauto_bridge.py` | | 日志字段 / 落盘格式 | `src/logger.js` 的 `chat()` | | 上下文策略(轮数、字符数、是否持久化) | `src/context.js` + `config.yaml` 的 `dsh.context` | `runtime.js` 是整个项目的"业务真相",它对 DSH 和微信都**没有硬依赖**: 桥接是注入的、引擎是注入的。所以 `npm run dev` 能用 Mock 跑通同一份逻辑—— **改动 `runtime.js` 后,至少跑一次 `npm run dev` 验证。** --- ## 4. 代码风格约定 - ESM(`"type": "module"`),Node.js >= 20.10,不使用 TypeScript。 - 四个空格 / 2 空格缩进沿用现有文件(2 空格),单引号,语句末尾带分号。 - **注释写"为什么"而不是"是什么"**。本项目要求"详细注释 + clone 后可部署运行", 新增复杂逻辑时请补齐中文注释,尤其是与 wxauto / DSH API 交互的地方 (这两块的版本差异是主要坑点)。 - 文件顶部保留一段模块职责说明;导出函数写 JSDoc。 - 不要在代码里写死密钥 / 微信号 / 模型名;一律走 `config.yaml`。 - 中文日志面向普通用户,说清"发生了什么 + 该怎么办"。 --- ## 5. DSH API 兼容性说明(重要) 本插件用到的 DSH 服务接口(若 DSH 升级后不兼容,优先检查这三处): 1. `ctx.effect(fn, name)` —— 插件生命周期,`fn` 返回清理函数。见 `src/index.js`。 2. `ctx.agents`(Agent 服务): - `ctx.agents.create({ sessionId, agentOptions })`、`ctx.agents.resume({ sessionId })`、`ctx.agents.get(id)` - `handle.agent.followup({ content: [{ type: 'text', text }], source: { kind: 'plugin', plugin } })` - `handle.agent.whenIdle()`、`handle.dispose()` - `agent.ctx.on('agent/assistant-stream', ({ frame }) => ...)`,`frame.type === 'chunk' && frame.chunk.type === 'text-delta'` 时拿到增量文本 - 见 `src/dsh/agent-engine.js` 3. `ctx.llm.stream({ provider, model, messages })` —— 异步迭代器, `chunk.type === 'text-delta'` 取文本,`chunk.type === 'finish'` 判断 `chunk.reason.kind`。见 `src/dsh/llm-engine.js`。 策略: - 优先 `backend: agent`(需求就是"接入 DSH Agent");`llm` 是备用降级路径。 - 两条路径都只累积 `text-delta` 文本,不依赖 DSH 内部的 BlockAssembler / 私有导出, 以免包解析路径变化导致插件加载失败。 - 任何可选 API(`agents.get`、`reset`、`dispose` 等)都先判断存在再调用。 --- ## 6. 验证清单(提交 PR 前) ```bash npm install # 只装 yaml npm run check # JS 语法 + Python 语法 npm run dev # Mock 全链路:应能看到指令回复、白名单拦截、聊天日志 ``` 手动确认: - [ ] `npm run dev` 输出里 `/status`、`/off`、`/on` 行为符合预期; - [ ] `logs/dev/chat-*.jsonl` 有 in / out 记录,`logs/dev/runtime-*.log` 无 ERROR; - [ ] 改动涉及真实微信时,用**小号**测试,且 `config.yaml` 白名单只填一个测试号; - [ ] README 的免责声明仍在文档最前面; - [ ] 没有把个人微信昵称、日志、token 提交进仓库(检查 `git status` 与 `git diff`)。 --- ## 7. 不要做的事 - 不要把这个项目用于营销群发、自动加好友、爬取通讯录等用途(README 已声明)。 - 不要把 `config.yaml` 的白名单默认值改成 `["*"]`(等于全量自动回复,风险极高)。 - 不要在日志里落完整聊天记录之外的敏感信息(如微信号、手机号);日志格式已按需求设计为 "消息 + 回复",不要额外扩展采集。 - 不要删掉 README 开头的免责声明,也不要把它挪到文末。 - 不要为了让某个版本能跑就 `try { ... } catch {}` 吞掉所有异常而不写日志。