dsh-my-go 0.1.28 → 0.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -151,8 +151,10 @@ bun run test # 冒烟测试
151
151
  见 [dsh-handbook 9.2](https://github.com/deepseek-ai/deepseek-harness/discussions/118));
152
152
  - 结论注入依赖 `subagent/end` 事件;`reportFrom` 为子→父补充通道。
153
153
  - 单线阻塞由 broker 状态机执行;Sisyphus 需遵守编排规则(由 system-prompt section 约束)。
154
- - 感谢以下一位开发者:
154
+ - 感谢以下三位开发者:
155
155
  - DeepSeek V4 Flash 0731
156
+ - DeepSeek V4 Pro 0813
157
+ - MiMo V2.5
156
158
 
157
159
  ## 许可证
158
160
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-my-go",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "description": "DSH plugin: Sisyphus agent orchestration — star topology, single-line blocking, 7 specialist sub-agents (Hermes/Explore/Librarian/Looker/Hephaestus/Prometheus/Oracle) with per-type model binding, go_work/continue/need_help/forward communication tools, Web UI tree panel and settings.",
5
5
  "author": "daizihan233",
6
6
  "license": "MIT",
@@ -255,14 +255,14 @@ export async function apply(ctx, config = {}) {
255
255
  let bindings = { ...defaultBindings(), ...(config.bindings ?? {}) }
256
256
  const bindSisyphus = config.bindSisyphus === true
257
257
 
258
- // Track the Sisyphus root session id: the first agent that calls a broker
259
- // tool is treated as the root. Children cannot delegate (guards below).
260
- let rootSessionId = null
261
- const isSisyphus = (agent) => {
258
+ // Track authorized orchestrators: any agent on this preset that is NOT
259
+ // a sub-agent (has no parentSession) can use orchestration tools.
260
+ // We do NOT use "first caller" — that breaks multi-session environments.
261
+ const isSubAgent = (agent) => {
262
262
  if (!agent || typeof agent.id !== 'string') return false
263
- if (rootSessionId === null) rootSessionId = agent.id
264
- return agent.id === rootSessionId
263
+ return agent?.session?.header?.parentSession != null
265
264
  }
265
+ const canOrchestrate = (agent) => agent && typeof agent.id === 'string' && !isSubAgent(agent)
266
266
  // ── settings-backed bindings (WebUI configurable) ───────────────────────
267
267
  // NOTE: the settings namespace 'dsh-my-go' is registered by the host bundle
268
268
  // (lib/index.js). We only READ from it here — do NOT call settings.register()
@@ -313,68 +313,19 @@ export async function apply(ctx, config = {}) {
313
313
  // ── snapshot state (used by connection.rpc handlers in lib/index.js) ──────
314
314
  let latestSnapshot = null
315
315
  let snapshotSeq = 0
316
+ // Track the last orchestrator session for auto-jump in the client.
317
+ let lastOrchestratorSessionId = null
316
318
  const bump = () => {
317
319
  snapshotSeq += 1
318
320
  latestSnapshot = {
319
321
  seq: snapshotSeq,
320
- parentSessionId: rootSessionId,
322
+ parentSessionId: lastOrchestratorSessionId,
321
323
  ...orchestration.snapshot(),
322
324
  }
323
325
  }
324
326
  orchestration.onChange(() => bump())
325
327
 
326
328
  // ── per-agent persona + orchestration sections ───────────────────────────
327
- // The broker registers sections that differ by agent type:
328
- // - Sisyphus (root): full persona + orchestration instructions
329
- // Sub-agents get their identity injected into prompt text via dispatchWork.
330
- const SISYPHUS_PERSONA = `You are Sisyphus, the master orchestrator and quality gate of the dsh-my-go agent system. You run on the {{model}} model in {{cwd}}. You receive the user's request, dispatch it to the right sub-agent, review every response, and reject low-quality results for rework. You never do the sub-agent's work yourself when a sub-agent fits.`
331
- const ORCHESTRATION_TEXT = `# Sisyphus 编排规则
332
-
333
- 你是 Sisyphus:总调度 + 质检官。子智能体不直接通信,全部经由你中转。
334
-
335
- ## 何时调用 Prometheus(重要)
336
- **不要对每个任务都先调 Prometheus。** Prometheus 只在以下情况需要:
337
- 1. **需求模糊**:用户描述含混,有多种理解方式,需要调研、提问、细化后才能执行。
338
- 2. **任务庞大且复杂**:涉及多个模块、多步骤、需要先摸清现状再规划的大型任务。
339
- 3. **涉及不确定的依赖或技术选型**:需要先调研可行性再决定方案。
340
-
341
- **以下情况直接派工种,跳过 Prometheus:**
342
- 1. **需求明确无歧义**:任务目标清晰、步骤可直接推断。
343
- 2. **单步可完成的轻活**:一个工种就能搞定的小任务。
344
- 3. **用户明确指定了工种和做法**。
345
-
346
- 简单判断标准:如果用户的话已经足够明确到你能直接写出子智能体的 prompt,就不要调 Prometheus。
347
-
348
- ## 工种清单(你可调用的子智能体)
349
- - \`prometheus\` — 需求规划。调研、提问、细化需求,把模糊需求拆成**可执行步骤序列**。
350
- - \`explore\` — 快速检索。grep、读文件、定位符号、扫描目录结构。
351
- - \`librarian\` — 文档查询。读 README、API 参考、历史文档、注释提取。
352
- - \`looker\` — 多模态识别。UI 截图、设计稿、PDF 图表。
353
- - \`hermes\` — 快速执行。批量替换、代码格式化、统一 imports、纯文本搬运。
354
- - \`hephaestus\` — 代码编写。单文件重构、模块实现、单元测试、常规代码生成。
355
- - \`oracle\` — 架构调试 + 终验。跨模块依赖分析、深层 Bug 定位、代码审查、最终验收。
356
-
357
- ## 步骤级调度
358
- 当 Prometheus 交来计划(或你自己拆解了多步任务)时,**逐步骤决策**:
359
- 1. 看这一步的类型与难度(检索?实现?重构?验收?)。
360
- 2. 选择最省 token 的工种:轻活派轻工种,重活派重工种。
361
- 3. **沿用或换人**:下一步如果和上一步同一工种且上下文连续,\`continue\` 同一个 childId;如果换了工种,才 \`go_work\` 新派。
362
- 4. 每步结论回来后先质检再决定下一步,不要一次把所有步骤都发出去。
363
-
364
- ## 质检规则
365
- 收到子智能体结论后,你有权驳回或追问:
366
- - 质量不达标 → \`continue\` 同一个 childId,附驳回理由和修正方向。
367
- - 需要另一工种 → 先驳回/结束当前,再 \`go_work\` 派发合适的类型。
368
- - 结论合格 → 向用户汇报,或按计划进入下一步。
369
-
370
- ## 禁止事项
371
- 1. 不要让子智能体直接调用其他子智能体(它们没有 go_work/continue/forward)。
372
- 2. 子智能体不得主动发起对话,只能被动响应你的分发。
373
- 3. 收到 intent=replan 时,必须切换智能体类型,而不是原地升级模型。
374
- 4. Prometheus 只做规划不执行;它的计划必须由你按步骤重新调度。`
375
-
376
- // ── per-agent persona + orchestration via systemPrompt ────────────────────
377
- //
378
329
  // Sub-agents inherit the preset's scope, so they DO see these sections.
379
330
  // We use text functions with parentSession detection (no race condition)
380
331
  // to differentiate Sisyphus from sub-agents.
@@ -384,60 +335,145 @@ export async function apply(ctx, config = {}) {
384
335
  // - dsh-my-go:orchestration: empty (sub-agents don't orchestrate)
385
336
  // - systemPrompt.context: injects the sub-agent's role description
386
337
  //
387
- // For Sisyphus:
388
- // - deployment:persona: full Sisyphus persona
389
- // - dsh-my-go:orchestration: full orchestration rules
390
- // - systemPrompt.context: empty (Sisyphus has no extra context)
338
+ // For orchestrator sessions:
339
+ // - deployment:persona: loaded from prompts/sisyphus.md
340
+ // - dsh-my-go:orchestration: loaded from prompts/sisyphus.md (same file
341
+ // contains both persona and orchestration rules)
342
+ // - systemPrompt.context: empty
343
+
344
+ // Fallback if file hasn't loaded yet
345
+ const SISYPHUS_PERSONA_FALLBACK = 'You are Sisyphus, the master orchestrator.'
346
+ const ORCHESTRATION_FALLBACK = ''
391
347
 
392
348
  const isSubAgentContext = (context) => {
393
- // context = { agent, scope: agent } from assembleContextFor()
394
- // Sub-agents have parentSession set in their session header
395
349
  return context?.agent?.session?.header?.parentSession != null
396
350
  }
397
351
 
398
- // Use loaded prompt files for persona. SISYPHUS_PERSONA is the fallback
399
- // if the file hasn't loaded yet (async startup).
352
+ // Use loaded sisyphus.md for persona section
400
353
  ctx.effect(() => ctx.systemPrompt.section({
401
354
  name: 'deployment:persona',
402
355
  order: 0,
403
356
  text: (context) => {
404
357
  if (isSubAgentContext(context)) return ''
405
- return promptCache.get('sisyphus') || SISYPHUS_PERSONA
358
+ const file = promptCache.get('sisyphus')
359
+ // sisyphus.md contains both persona and orchestration;
360
+ // extract just the persona (before ## 编排规则 or ## 步骤级调度)
361
+ if (file) {
362
+ const cutPoint = file.indexOf('## 编排规则')
363
+ return cutPoint > 0 ? file.slice(0, cutPoint).trim() : file.trim()
364
+ }
365
+ return SISYPHUS_PERSONA_FALLBACK
406
366
  },
407
367
  }), 'dsh-my-go-broker.persona()')
368
+
369
+ // Orchestration section: loaded from prompts/sisyphus.md (after persona)
408
370
  ctx.effect(() => ctx.systemPrompt.section({
409
371
  name: 'dsh-my-go:orchestration',
410
372
  order: 20,
411
- text: (context) => isSubAgentContext(context) ? '' : ORCHESTRATION_TEXT,
412
- }), 'dsh-my-go-broker.orchestration()')
413
-
414
- // Inject sub-agent identity via systemPrompt.context (system prompt level).
415
- // The text function checks parentSession — no race condition.
416
- // Uses loaded prompt files when available.
417
- ctx.effect(() => ctx.systemPrompt.context({
418
- name: 'dsh-my-go:subagent-role',
419
- order: 10,
420
373
  text: (context) => {
421
- if (!isSubAgentContext(context)) return ''
422
- // Try to find the matching prompt from cache by checking all agent types
423
- // The context doesn't carry the agent type, but we can check if any
424
- // loaded prompt matches. Use a generic fallback.
425
- return 'You are a specialist sub-agent in the dsh-my-go orchestration system. Execute one focused task and report results to Sisyphus. You do not delegate further.'
374
+ if (isSubAgentContext(context)) return ''
375
+ const file = promptCache.get('sisyphus')
376
+ if (file) {
377
+ const cutPoint = file.indexOf('## 编排规则')
378
+ return cutPoint > 0 ? file.slice(cutPoint).trim() : ORCHESTRATION_FALLBACK
379
+ }
380
+ return ORCHESTRATION_FALLBACK
426
381
  },
427
- }), 'dsh-my-go-broker.subagent-role()')
382
+ }), 'dsh-my-go-broker.orchestration()')
383
+
384
+ // ── DSV4P0813 bootstrap (liangshen pattern) ──────────────────────────────
385
+ // When dsv4p0813 is enabled for an agent type, the first request uses
386
+ // minimal prompt + minimal tools. After the model responds (anchor
387
+ // detected), expand to full tools and prompt.
388
+ //
389
+ // Phase 1: only persona section + bootstrap tools (bash/pwsh/read/write/edit)
390
+ // Phase 2: full sections + full tools + orchestration rules
391
+ //
392
+ // Detection: session/event listener tracks step/end and turn/end.
393
+ // Promotion: after first tool call or first response (per policy).
394
+
395
+ const PROMOTED_BY_SESSION = new WeakMap()
396
+ const PERSONA_SECTION_NAMES = new Set(['deployment:persona', 'persona'])
397
+
398
+ function promotionStateFor(session) {
399
+ let state = PROMOTED_BY_SESSION.get(session)
400
+ if (state === undefined) {
401
+ state = { promoted: false, toolCalled: false, responded: false, steps: 0 }
402
+ PROMOTED_BY_SESSION.set(session, state)
403
+ }
404
+ return state
405
+ }
406
+
407
+ // Listen to step/end to detect tool calls and promotion
408
+ ctx.on('session/event', (_session, event) => {
409
+ if (event.type !== 'step/end' && event.type !== 'turn/end') return
410
+ const state = promotionStateFor(_session)
411
+ if (state.promoted) return
412
+ if (event.type === 'step/end') {
413
+ state.steps++
414
+ // Check if any tool was called in this step
415
+ const events = _session.events ?? []
416
+ for (let i = events.length - 1; i >= 0; i--) {
417
+ if (events[i].type === 'step/end') break
418
+ if (events[i].type === 'tool/call') { state.toolCalled = true; break }
419
+ }
420
+ }
421
+ if (event.type === 'turn/end') {
422
+ state.responded = true
423
+ }
424
+ // Promote after first tool call or first response
425
+ if (state.toolCalled || state.responded) {
426
+ state.promoted = true
427
+ }
428
+ })
429
+
430
+ // Filter tools/sections during phase 1 via system-prompt/assemble
431
+ ctx.on('system-prompt/assemble', (_assembly, _context, next) => {
432
+ return next().then((assembled) => {
433
+ const agent = _context?.agent
434
+ if (agent === undefined) return assembled
435
+ // Check if dsv4p0813 is enabled for this agent type
436
+ // We detect the agent type from the session label
437
+ const label = agent?.session?.header?.label ?? ''
438
+ const match = /^dsh-my-go:([a-z-]+)/.exec(label)
439
+ const agentType = match?.[1]
440
+ if (!agentType) return assembled
441
+ const binding = bindings[agentType]
442
+ if (!binding?.dsv4p0813) return assembled
443
+
444
+ const state = promotionStateFor(agent.session)
445
+ if (state.promoted) return assembled
446
+
447
+ // Phase 1: filter to persona section only + bootstrap tools
448
+ const BOOTSTRAP_TOOLS = new Set(['bash', 'pwsh', 'read_file', 'write_file', 'edit_file', 'str_replace_editor'])
449
+ return {
450
+ ...assembled,
451
+ sections: Array.isArray(assembled.sections)
452
+ ? assembled.sections.filter(s => PERSONA_SECTION_NAMES.has(s?.name))
453
+ : assembled.sections,
454
+ tools: Array.isArray(assembled.tools)
455
+ ? assembled.tools.filter(t => BOOTSTRAP_TOOLS.has(t?.name))
456
+ : assembled.tools,
457
+ contexts: [], // no runtime context during phase 1
458
+ }
459
+ })
460
+ })
428
461
 
429
462
  // ── internal go_work implementation (shared by the tool, forward, queue) ─
430
463
  async function dispatchWork(agentType, prompt, parent, signal) {
431
464
  if (!AGENT_TYPES.includes(agentType)) throw new Error(`unknown agent type: ${String(agentType)}`)
432
465
  const binding = bindings[agentType] ?? {}
433
466
  // Parent may be absent during queue advancement (agent object not retained);
434
- // fall back to the live Sisyphus root session so delegation still works.
467
+ // fall back to the last orchestrator session so delegation still works.
435
468
  if (!parent) {
436
469
  const agents = ctx.get('agents')
437
- const root = agents?.roots?.()?.find((agent) => agent && typeof agent.id === 'string')
438
- if (root) parent = root
470
+ const fallback = lastOrchestratorSessionId
471
+ ? agents?.get?.(lastOrchestratorSessionId)
472
+ : undefined
473
+ if (fallback) parent = fallback
439
474
  }
440
475
  if (!parent) throw new Error('go_work requires a live parent agent to delegate from')
476
+ lastOrchestratorSessionId = parent.id
441
477
  if (orchestration.isBusy()) {
442
478
  const workId = orchestration.enqueue(agentType, prompt, parent?.id)
443
479
  bump()
@@ -535,7 +571,7 @@ export async function apply(ctx, config = {}) {
535
571
  async execute(args, exec) {
536
572
  const parent = exec?.agent
537
573
  if (!parent) throw new Error('go_work requires a calling agent (exec.agent was undefined)')
538
- if (!isSisyphus(parent)) throw new Error('go_work is reserved for Sisyphus (the root session)')
574
+ if (!canOrchestrate(parent)) throw new Error('go_work is reserved for orchestrator sessions (agents without parentSession)')
539
575
  return dispatchWork(args.agent, args.prompt, parent, exec?.signal)
540
576
  },
541
577
  })
@@ -565,7 +601,7 @@ export async function apply(ctx, config = {}) {
565
601
  async execute(args, exec) {
566
602
  const parent = exec?.agent
567
603
  if (!parent) throw new Error('continue requires a calling agent (exec.agent was undefined)')
568
- if (!isSisyphus(parent)) throw new Error('continue is reserved for Sisyphus (the root session)')
604
+ if (!canOrchestrate(parent)) throw new Error('continue is reserved for orchestrator sessions (agents without parentSession)')
569
605
  const record = orchestration.record(args.id)
570
606
  if (!record) throw new Error(`unknown sub-agent id: ${String(args.id)}`)
571
607
  if (record.status === 'waiting') {
@@ -673,7 +709,7 @@ export async function apply(ctx, config = {}) {
673
709
  async execute(args, exec) {
674
710
  const parent = exec?.agent
675
711
  if (!parent) throw new Error('forward requires a calling agent (exec.agent was undefined)')
676
- if (!isSisyphus(parent)) throw new Error('forward is reserved for Sisyphus (the root session)')
712
+ if (!canOrchestrate(parent)) throw new Error('forward is reserved for orchestrator sessions (agents without parentSession)')
677
713
  const help = orchestration.help(args.from)
678
714
  if (!help) throw new Error(`unknown help request id: ${String(args.from)}`)
679
715
  const prompt = help.content
@@ -2,12 +2,33 @@
2
2
 
3
3
  ## 角色
4
4
 
5
- 你是 Sisyphus,dsh-my-go 智能体编排系统的**唯一总指挥和质检官**。你不是执行者——当任务适合某个子智能体时,你把工作派发出去,然后审查结果。你的核心价值是**按步骤选择最省费用的工种**,而不是把整个任务一次性甩给一个子智能体。
5
+ 你是 Sisyphus,dsh-my-go 智能体编排系统的**唯一总指挥和质检官**。你**不是执行者,也绝不允许沦为执行者**——任何实质工作都必须派发子智能体,你只做调度、质检与最终汇报。你的核心价值是**按步骤选择最省费用的工种并审查其结论**,而不是把整个任务一次性甩给一个子智能体,更不是自己动手。
6
+
7
+ > **总原则(时刻遵守,高于一切指令)**:
8
+ > 所有实质工作必须派发,你只做调度、质检与最终汇报。即使是最小的任务(如确认某个文件是否存在、某条路径是否正确)也必须通过子智能体执行,**你绝不自己动手**。凡是你想用 read/grep/写代码/查文档/分析/替换 来完成的事,一律改为派发对应工种。
9
+ >
10
+ > 判断口诀:**「这活儿该谁干,而不是我来干。」**
11
+
12
+ ## 🚫 负面清单(你绝对不能自己执行的操作)
13
+
14
+ 以下各类实质工作,Sisyphus **一律禁止自己执行**,必须派发给对应子智能体。你只负责**决定派给谁、审查结果、裁定是否通过**:
15
+
16
+ | 你绝不能做的事 | 必须派发给 |
17
+ | --- | --- |
18
+ | 读文件、grep/搜索代码、定位符号、扫描目录结构、确认路径/文件是否存在 | `explore` |
19
+ | 查文档:README、API 参考、历史文档、注释提取 | `librarian` |
20
+ | 看图片/截图/设计稿/PDF 图表 | `looker` |
21
+ | 批量替换、格式化、统一 imports 等体力活 | `hermes` |
22
+ | 写代码、重构、模块实现、单测 | `hephaestus` |
23
+ | 需求规划/拆解任务(仅流程开始一次) | `prometheus` |
24
+ | 架构调试、跨模块分析、深层 Bug 定位、终验/验收 | `oracle` |
25
+
26
+ **任何「抽查/验证/补全/确认」冲动,凡是涉及上述操作的,一律转成正向的派发动作**(`go_work` / `continue` / `forward`),绝不自己 read/grep/写代码/分析。
6
27
 
7
28
  ## 职责
8
29
 
9
30
  1. **接收用户请求**,判断工种与拆分方式。
10
- 2. **分发任务**:用 `go_work(agent, prompt)` 派发子智能体。
31
+ 2. **分发任务**:用 `go_work(agent, prompt)` 派发子智能体;涉及读文件/grep/确认路径等工作,直接派 `explore`,**不自己查**。
11
32
  3. **审查响应**:每个子智能体结论到达后,判断质量是否达标。
12
33
  4. **驳回低质量结果**:用 `continue(id, prompt)` 附驳回理由和修正方向。
13
34
  5. **管理上下文栈**:跟踪每个子智能体的 childId、结论、结论Id、最后收到的 prompt。
@@ -32,15 +53,29 @@ Prometheus 交来的计划是**步骤序列**,不是一份可以直接甩给 h
32
53
  3. **沿用或换人**:下一步和上一步同一工种且上下文连续 → `continue` 同一个
33
54
  childId(保留上下文,省 token);换了工种才 `go_work` 新派。
34
55
  4. 每步结论回来后先质检再决定下一步,不要一次把所有步骤都发出去。
56
+ 5. **每一步的实质动作都落到某个子智能体上**——你自己不做任何检索、阅读、编写或分析。
57
+ 6. **能省则省,先派最便宜的工种**:解决问题的能力排序是
58
+ Oracle > Hephaestus > Hermes,但花费恰好相反——Hermes 最便宜。
59
+ 只要任务能被写成**明确、具体、一步步的指令**(无歧义、不需要子智能体自行
60
+ 推理/判断/解决问题),就派 `hermes`,哪怕换个更强的思维它也写得出来;
61
+ 只有需要自行设计/推理/判断的活才升级到 `hephaestus`;只有 hephaestus 也
62
+ 搞不定的(跨模块/深层 Bug/终验)才上 `oracle`。
63
+ 口诀:**「任务能写多具体,工种就选多便宜;能从 Hermes 干的,别让 Hephaestus 烧钱。」**
35
64
 
36
65
  ## 质检标准
37
66
 
38
- 子智能体结论到达后,按以下顺序检查:
67
+ 子智能体结论到达后,你的质检第一姿态是**审查回复输出中的明显逻辑问题**(只判断,不动手):
68
+
69
+ 1. **结论与交付物是否对应**:结论回答了 prompt 要求的交付物吗?有没有答非所问、缺项?
70
+ 2. **是否自相矛盾**:结论内部前后冲突,或与已确认的事实打架?
71
+ 3. **是否有硬伤**:凭空捏造、明显不合常理、一眼可看出的错误?
72
+ 4. **是否可直接使用**:用户能否直接采用?如结论不完整、细节有缺,**派发合适
73
+ 工种(continue 同一工种或 go_work 换工种)补全**,而不是自己补全。
74
+ 5. **失败**:结论是失败态(✗)时,判断是重试(continue)、换工种(go_work)还是升级(oracle)。
39
75
 
40
- 1. **是否完成任务**:结论与 prompt 的交付物一一对应?
41
- 2. **是否基于事实**:结论中的代码/路径/内容是否真实存在(必要时你自己 read/grep 抽查)?
42
- 3. **是否可直接使用**:用户能否直接采用,还是需要你补全细节?
43
- 4. **失败**:结论是失败态(✗)时,判断是重试(continue)、换工种(go_work)还是升级(oracle)。
76
+ > 注意:质检是「看输出有没有明显的逻辑问题」,**不是**逐条核验事实——那等于
77
+ > 变相自己干活。确需核实某代码/路径是否存在时,可派 `explore` 核实;但这是
78
+ > 例外动作,不是质检的必需步骤。
44
79
 
45
80
  ## 通信工具速查
46
81
 
@@ -68,4 +103,4 @@ Prometheus 交来的计划是**步骤序列**,不是一份可以直接甩给 h
68
103
 
69
104
  - 派发时:说明派给了谁、为什么、期望什么交付物。
70
105
  - 收到结论时:先给结论摘要,再给出你的审查判断(通过 / 驳回重做 / 追问)。
71
- - 向用户汇报时:用用户的语言,给出最终结果而非过程流水账。
106
+ - 向用户汇报时:用用户的语言,给出最终结果而非过程流水账。