feat: 统一 AI 自动回复决策链路与发送安全控制 - #17
Merged
NingNing0111 merged 9 commits intoAug 17, 2026
Merged
Conversation
新增 agent 模块收口全部非流式大模型调用:任务只描述上下文与验收标准, 装参数、渲染、流式采集、输出净化、校验返工由统一循环负责。流式采集的 增量只在内部累积不外推,因此可以安全重试与降级;模拟面试那条流式路径 不并入,它的增量已推送到界面。 新增 rpa/conversation 作为两平台共享的会话契约:上下文、决策结构、 动作 trait 与安全护栏。此前两平台各写一套判断逻辑,行为已经分叉。 修复三个必现缺陷: - 提示词变量按条件注入导致渲染必然失败,猎聘自动回复在默认配置下 一条消息都发不出去,且失败表现为「未匹配到回复模板」难以排查 - BOSS 侧不读状态直接点工具栏第一个按钮,而该类名为发简历、换电话、 换微信共用,点中哪个取决于 DOM 顺序 - BOSS 侧两条分支都提前 continue,回复逻辑几乎不可达 猎聘首次落库聊天记录,两平台均合并本地历史后再判断;聊天记录主键从 岗位改为「平台+会话」,猎聘映射不到岗位的会话也能保留上下文。
演练模式让判断与生成照常进行但不实际发送,首次启用自动回复时可以先 跑一轮确认生成质量;另外开放是否允许自主投递简历、单会话回复条数上限、 单条回复字数上限三项边界配置。
此前只有自动求职链路走了 Agent 循环,命令层还有六处直连服务,各自手拼参数、 各自解析 JSON、没有输出净化也没有校验返工。调试页的生成回复因为漏填 chat_history 必然报错,等于调试看到的和实际跑的不是同一条链路。 现在除 provider 连接测试外的全部模型用途都走 agent::run: - 岗位筛选规则生成、简历追问预测、简历定向优化、岗位面试分析改用对应任务 - 调试页的回复与打招呼直接复用生产任务,不再另写一套 - 模拟面试并入循环的流式模式 流式不再游离在体系之外。循环新增流式模式:强制单轮,且只在一个字都还没吐出去 时才允许降级——增量一旦推到界面上,重发就会让用户看到重复内容。这条约束是循环 的一种模式,而不是某个用途的特例。 provider 连接测试是唯一保留直连的地方:它验证单个服务通不通,走降级链会让备用 服务的成功掩盖主用服务的故障,理由已写进代码。 内置提示词全部移入 agent/prompts 的 .md 资源,防注入声明与纯 JSON 输出约定收成 共享片段。模拟面试的两个提示词此前嵌入简历、JD 和历史对话却没有防注入声明, 一并补上。用户可编辑的打招呼与回复提示词仍留在配置文件。
实测事故:开启 LLM 打招呼后,模型判断出岗位不该投,但打招呼任务只返回纯文本, 它没有渠道表达这个结论,只能写进正文——「注意到您是猎头顾问,我暂时不考虑猎头 渠道」被当成开场白发了出去,紧跟着那条固定的简历图片照发不误,对方收到的是 「我不考虑你」外加一张简历截图。 两处根因各修一处: 一是打招呼任务改为返回结构化决策,与自动回复对称。模型现在可以选 skip 表示 不该投,理由写在 reason 里给求职者自己看,不会发出去。提示词里明确写了 「不投就选 skip,不要把婉拒写进 greeting,那个字段会原样发给对方」。 二是新增统一的对外发送闸门 vet_outbound,打招呼与自动回复共用。任何一条内容 不合格,整轮都不发,而不是只丢掉那一条——逐条过滤在这种场景下比不过滤更糟, 它把一段本该整体取消的动作退化成语义残缺的动作。图片无法体检,但去留跟着 整轮走,不会单独发出去。 闸门另加婉拒意图检测作为兜底,防模型不听话仍把结论写进正文。这条只用于打招呼: 回复里礼貌表达「这个岗位我可能不太合适」本身是合理消息,要终止对话有 Skip 可用, 差别已写进代码注释。 被闸门拦下的岗位不再记为已沟通,猎聘的本页统计也单列该项,避免统计骗人。
删掉三个零引用的方法:LlmService::generate_template、LlmChainService::generate 及其 generate_template。全部调用点改走 Agent 循环后它们已无人使用,留着只会让 后来者以为还有第二条可用路径。 vet_reply_text 与 vet_outbound 各写了一遍拒答、占位符、截断,差别其实只有 「要不要拦婉拒」一条。合并成带 OutboundKind 参数的单一实现,单条回复走 vet_reply 这层薄包装。改一处忘一处的隐患随之消失。
失败原因此前只存进任务状态给前端看,日志文件里一点痕迹没有,
事后回看只能看到任务戛然而止,查不出停在哪一步。用 {error:#}
带上完整 context 链,CDP 那类报错才定位得到具体动作。
原先把 .user-list-content 当常驻容器,实际它在分类没有会话时整个被卸载, 于是空分类下必然等待超时;而 .no-data 也不是权威空态,切分类等接口返回的 一秒多里它同样会出现,看见就断言没有未读正好撞进这个假空态窗口。 另有两处让同一个会话被处理两遍:「未读」是纯前端筛选,点开会话后列表 DOM 不会自己重算,停在该分类上又不会重新渲染,每轮读到的都是同一批卡片;去重 用的整卡片文本里混着未读徽标数字,徽标一消失文本就变,去重随之失效。 改为:卡片见到即认、空态需连续稳定 3 秒且标签无计数才认;每处理完一个会话 借道「全部」逼前端重新过滤;去重改用 .title-box 里不随已读状态变化的部分; 未读数改报标签计数,列表分页时才准。选择器与时序均连 CDP 实测得出。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
变更内容
影响
自动沟通链路更可控、可观测,并降低错误消息外发的风险。
验证