Skip to content

feat: add OpenCode Zen AI provider - #372

Open
Jiandao303 wants to merge 9 commits into
JOYCEQL:mainfrom
Jiandao303:feat/opencode-zen-provider
Open

feat: add OpenCode Zen AI provider#372
Jiandao303 wants to merge 9 commits into
JOYCEQL:mainfrom
Jiandao303:feat/opencode-zen-provider

Conversation

@Jiandao303

Copy link
Copy Markdown

为了方便使用,我添加了opencode的提供商

Jiandao303 and others added 9 commits July 30, 2026 16:55
Land the native conversational resume agent plus the supporting routing
migration in one commit:

- OpenCode Zen provider with a tool-gated native resume agent runtime
  (resume-agent-runtime/): locked-down permission set, read-only research
  tool whitelist, triple budgets, fact gating, user-confirmation gates.
- Two-stage discovery: when no target company is given, the agent searches
  the web for real career directions and lets the user pick one before
  researching its latest JD.
- SSRF-safe JD fetching: redirects / manual-mode fetch degrade to a
  recoverable outcome instead of failing the whole job.
- Agent UI: thinking-chain timeline, resizable three-tab side panel,
  A4 CSS-zoom preview with a collapsible template rail, clarification
  question wizard with explicit step cursor and previous/next navigation.
- Field-path questions humanized to Chinese, questions de-duplicated,
  all seven resume sections covered by confirmation questions.
- Skills rendered grouped by category instead of one skill per line.
- Drop Next.js app-router / i18n-compat leftovers in favor of TanStack
  Start file routing; update docs, changelog, and roadmap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Use the two new portrait previews for the support section in both the
English and Chinese readmes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 技能:model 提示词要求按「类别名: 技能、技能」分组输出,skillContent
  渲染增强识别分组格式(类别名加粗 + 技能顿号分隔),education/experience
  等普通列表不受影响
- 续聊:新增 /continue 端点与 continueResumeAgentJob,首轮结束后直接发消息
  复用同一 Job,重跑 candidate_facts/tailoring/fact_gate 而保留岗位调研;
  candidate_facts 后加目标一致性检测,公司/职位/JD 链接变化时自动回退重新
  调研;前端续聊不重置时间线、从上次事件序列续读;时间线新增 continue-runtime
  里程碑替代重复的「已启动」欢迎语
- 7 板块确认:buildPendingQuestions 新增板块覆盖 pass,除基本信息外的 6 个
  空板块逐一询问(可自由填写或点「没有相关内容」/跳过),稳定 section-* id
  配合 answeredQuestions 避免重复追问;问题上限 12→16
- model-adapter 新增 classifyUserIntent:一次轻量模型调用判断最新消息是
  chat(闲聊/问候/道谢)还是 resume(提供简历素材或制作/修改请求),
  闲聊时顺带生成带引导的回复(介绍能力 + 引导发基本信息和目标岗位/JD)
- runner 在 candidate_facts 前插入意图分流层:chat 直接返回回复并进入
  waiting_user,不启动工作流、不调工具;resume 才继续完整流程。闲聊轮次
  不标记任何步骤,下一条消息会再次分流;简历做完后的纯聊天同样走此层,
  只回复、不动草稿
- 作答(/answer)与方向选择(/select-direction)是明确的简历操作,通过
  checkpoint.intentSkipped 跳过分流;意图分类失败保守按 resume 处理
- 前端 user.required 携带 mode:chat 时以 idle 状态呈现(不展开轨迹、不弹
  澄清卡片),聊天回复以普通气泡展示;timeline 新增 intake 里程碑 stage
原先的执行轨迹是流水账式 trace(一句话叙述一件事),既无法表达决策依据,
也支撑不了工具调度;工具调用散落在 runner 的多处 if 分支里,没有统一的
「规划-执行-校验」闭环,失败与信息不足时也缺少标准化的回退与降级。

思维链模块
- 新增 reasoning-chain.ts:ReasoningChain 构造器,节点强制携带判断依据、
  执行动作、预期产出,收尾必须给出校验结论(pass/degraded/blocked/skipped)
- 五阶段固定链路:需求拆解 → 路径规划 → 工具执行 → 结果校验 → 最终交付,
  节点编号按阶段递增(1.1、2.1、3.2…)
- 卡点必须同时声明原因与补全/降级方案,不允许只报错不给出路
- 节点通过 trace.updated 事件下发(stage="chain"),同时保留纯文本 detail
  兜底,旧渲染路径不受影响

工具调度模块
- 新增 execution-planner.ts:执行前先推导本轮步骤序列、工具清单与依赖关系,
  跳步原因(已完成 / 依赖不满足)成为可展示结论;首轮目标未知的下游步骤
  标「待定」而不是谎报跳过
- 新增 tool-orchestrator.ts:runToolStep 完成统一闭环
  adapt(取参)→ validateInput → execute(瞬时错误重试 1 次)
  → validateOutput → collect(回填参数袋)→ degrade(降级)
- ToolResultBag 实现工具间参数自动传递:搜索结果 URL 由 collect 写入队列,
  抓取工具的 adapt 直接取用,不再由调用方手工搬运中间结果
- 九个工具的调度契约集中声明(调用原因、入参逻辑、预期结果、校验规则、
  降级方案);新增工具只加一条 spec,闭环自动生效
- 事实门禁显式声明无降级方案:门禁被跳过等于放行未核验内容

前端渲染
- AgentTimelineStep 新增 chain kind 与 ReasoningNode 载荷
- 新增 AgentChainNode:按字段渲染节点(依据/动作/产出/工具四要素/卡点),
  折叠态标题带阶段编号与校验结论徽标
- 默认展开规则:pass / skipped 折叠,degraded / blocked / pending 展开
- zh/en 补齐 steps.chain 文案

业务逻辑与 SSRF 边界未改动:调研三条子路径、事实门禁、澄清计划、意图分流
的判定条件与顺序保持原样,只是改为经调度闭环执行并在思维链上留痕。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
实测一个 Job 在 39 分钟内追问 6 轮、用户重复作答 45 次(18 个唯一问题、
其中两题各答 6 次)、消耗 13 次模型调用,最终以上游「Insufficient Balance」
告终。余额耗尽只是终点,根因是澄清环节永不收敛。

死循环机制:模型把「用户已确认 X」写进 draft.assumptions,事实门禁又无条件
把每条 assumption 变成"待本人确认"反问用户;用户一答,模型再写一条新的
「用户已确认…」。currentDraft 每轮回灌且 prompt 要求保留已确认事实,
assumptions 只增不减,门禁产出逐轮上升(3→4→7→7→7)永不归零。

切断循环
- career-ops: 门禁跳过已闭环的过程记录(引用用户答复 / 描述草稿增删两类模式)。
  用真实失控草稿回放:7 条 assumptions → 0 条待确认项,且"根据经历推断…"
  "假设候选人愿意…"这类真推断不被误杀
- model-adapter: SYSTEM_PROMPT 明确 assumptions 只写待用户确认的推断,
  禁止写成变更日志;用户答复过的必须落进草稿字段并从 assumptions 移除
- clarification: 问题 ID 从位置编号 q-${index}-${field} 改为内容哈希。
  位置编号会随列表变化漂移,导致"已作答"无法判定;改后同一句问题在任何
  轮次都是同一 ID,已作答项豁免从仅 section-* 扩展到全部问题

断路器与成本护栏
- clarification: 澄清轮次超过软上限 3 轮后只保留阻塞入库的 error 项,
  warning 项如实标记为待补充。模型换个措辞就能绕过内容哈希,需要轮次兜底
- runner: 新增 Job 级模型调用上限(默认 8 次,checkpoint.modelCallCount 跨轮
  累计)。JOB_BUDGET_MS 只管单轮时长,管不住多轮累计失控。输入框新消息
  重置计数,作答路径不重置——那正是要收敛的循环

错误分类与提示
- model-adapter: 新增 classifyUpstreamFailure,把计费/鉴权/限流类上游错误
  归类并中文化,附带"草稿可直接入库 / 去 AI 设置处理账户"两条出路
- runner: 余额与鉴权类失败在意图分流即中止上抛,不再白跑一次事实提取撞
  同一个错误;job.failed 携带 failureKind 与 draftUsable
- page: 账户类失败隐藏「重试」按钮(重试必然撞同一错误),改为提示草稿仍可保存

观测
- 澄清节点记录"新增 N 项 / 与上轮重复 M 项",循环失控时一眼可见

验证:容器内 tsc 通过;用 402 mock 实测 failureKind=billing、中文提示正确、
余额类失败只产生 1 个思维链节点(原为 3 个);内容哈希 ID 稳定性与断路器
阈值行为均已单独验证。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant