ai_co_discuss 0.2.70 → 0.2.71

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.
@@ -12,7 +12,7 @@ export const ROUTE_CONFIDENCE = 0.7;
12
12
  /** Waking everyone or no one costs more when wrong (N runs, or an ignored message), so it needs more certainty. */
13
13
  export const ROUTE_WIDE_CONFIDENCE = 0.85;
14
14
  /** Bump when the routing question or its option descriptions change, so ratings can be compared across wordings. */
15
- export const ROUTE_PROMPT_VERSION = 3;
15
+ export const ROUTE_PROMPT_VERSION = 4;
16
16
  const HOST_CHOICE = '__host__';
17
17
  const NOTE_CHOICE = '__note__';
18
18
  const ALL_CHOICE = '__all__';
@@ -95,6 +95,11 @@ function detail_of(questions, answers) {
95
95
  });
96
96
  }
97
97
 
98
+ /** The shown detail plus the exact input (state and full questions), kept so a rated-bad decision can be reviewed later. */
99
+ function judged_of(state, questions, answers) {
100
+ return { detail: detail_of(questions, answers), input: { state, questions } };
101
+ }
102
+
98
103
  /** Say which option the provider leaned to when it was not sure enough, so the transcript shows how close it was. */
99
104
  function unsure_label(lean) {
100
105
  return lean ? `倾向${lean},把握不足,交给主持人` : '拿不准,交给主持人';
@@ -108,8 +113,8 @@ function excerpt(message) {
108
113
  }
109
114
 
110
115
  /** The room's purpose in the user's words, so a decision can tell drifting from progress. */
111
- function topic_lines(title, goal, user_request, { brief, focus, arrangement, progress } = {}) {
112
- return [`房间:${title || ''}`, goal && `目标:${goal}`, user_request && `用户原文:${String(user_request).slice(0, MAX_MESSAGE_CHARS)}`,
116
+ function topic_lines(title, goal, user_request, { roster, brief, focus, arrangement, progress } = {}) {
117
+ return [`房间:${title || ''}`, roster && `房间成员(名字不区分大小写):${roster}`, goal && `目标:${goal}`, user_request && `用户原文:${String(user_request).slice(0, MAX_MESSAGE_CHARS)}`,
113
118
  focus && `当前焦点:${focus}`, brief && `主持简报(含分工与规则):${String(brief).slice(0, MAX_BRIEF_CHARS)}`,
114
119
  arrangement && `主持人最近的安排:${arrangement}`, progress].filter(Boolean).join('\n');
115
120
  }
@@ -136,21 +141,39 @@ export function certainty(answer) {
136
141
  */
137
142
  export async function route_free_message(home, scheduler, { title, goal, context, message, host, members }, fetch_impl) {
138
143
  const choices = [...members.map(member => ({ value: member.name, description: member.description })),
139
- { value: HOST_CHOICE, description: `主持人 ${host.description}:消息在延续主持人自己的回答,或对象不明确,或需要分工、排先后、互相回应` },
140
- { value: ALL_CHOICE, description: '全体成员(含主持人)各自回应一次,彼此不需要配合,如打招呼、自我介绍、各说一句看法' },
144
+ { value: HOST_CHOICE, description: `主持人 ${host.description}:没有指向任何人(如单独一句“hi”“你好”这类问候、闲聊,由主持人代表房间回应),或需要分工、排先后、互相回应` },
145
+ { value: ALL_CHOICE, description: '全体成员(含主持人)各自回应一次,彼此不需要配合;须是用户明确要求大家各自回应,如“大家/都出来打个招呼”“各自介绍一下”“各说一句看法”' },
141
146
  { value: NONE_CHOICE, description: '无需回应:只是确认、致谢或旁注,不需要任何人回答' }];
142
147
  const questions = [{ type: 'choice', name: 'handler', choices,
143
- instructions: '这条用户消息没有用 @ 点名成员。判断它的回应对象是否已经确定:正文叫了某位成员的名字或在向其提问;在延续某位成员的回答(追问、要求修改、引用,或“回复”指向其消息);在补充或纠正一条正由某位成员处理的消息。以上情况选该成员,是主持人就选主持人。要求所有成员各自回应一次选全体;只是确认、致谢或旁注选无需回应;对象不明确,或需要分工、排先后、互相回应时选主持人。' }];
144
- const answers = await decide(home, scheduler, scheduler_state(title, context, message, goal, '', {}, ROUTE_CONTEXT_MESSAGES), questions, fetch_impl);
145
- const detail = detail_of(questions, answers);
148
+ instructions: '这条用户消息没有用 @ 点名成员。判断它的回应对象,按下面的顺序:1)正文叫了某位成员的名字,或在向其提问(包括“X在吗”“X还在吗”“X你怎么看”),选该成员,即使上一条是别人的回答;2)正文没有指向任何人时,看它是否在延续某位成员的回答(追问、要求修改、引用,或“回复”指向其消息),或在补充、纠正一条正由某位成员处理的消息,是则选该成员,是主持人就选主持人;3)明确要求所有成员各自回应一次(用了“大家”“都”“各自”等)选全体;4)单独的问候或闲聊(如“hi”)既没有指向某位成员,也没有要求大家一起回应,选主持人;5)只是确认、致谢或旁注选无需回应;6)对象不明确,或需要分工、排先后、互相回应时选主持人。' }];
149
+ const roster = [`${host.name}(主持人)`, ...members.map(member => member.name)].join('、');
150
+ const state = scheduler_state(title, context, message, goal, '', { roster }, ROUTE_CONTEXT_MESSAGES);
151
+ const answers = await decide(home, scheduler, state, questions, fetch_impl);
152
+ const judged = judged_of(state, questions, answers);
146
153
  const choice = answers.handler?.choice ?? null;
147
154
  const confidence = certainty(answers.handler);
148
155
  const wide = choice === ALL_CHOICE || choice === NONE_CHOICE;
149
156
  const sure = confidence !== null && confidence >= (wide ? ROUTE_WIDE_CONFIDENCE : ROUTE_CONFIDENCE);
150
- if (sure && choice === ALL_CHOICE) return { action: 'all', choice, confidence, detail };
151
- if (sure && choice === NONE_CHOICE) return { action: 'none', choice, confidence, detail };
152
- if (sure && members.some(item => item.name === choice)) return { action: 'member', member: choice, choice, confidence, detail };
153
- return { action: 'host', choice, confidence, detail };
157
+ if (sure && choice === ALL_CHOICE) return { action: 'all', choice, confidence, ...judged };
158
+ if (sure && choice === NONE_CHOICE) return { action: 'none', choice, confidence, ...judged };
159
+ if (sure && members.some(item => item.name === choice)) return { action: 'member', member: choice, choice, confidence, ...judged };
160
+ return { action: 'host', choice, confidence, ...judged };
161
+ }
162
+
163
+ /**
164
+ * The user named everyone (@全体成员): decide whether they answer at once (parallel) or one after another (sequential).
165
+ * Anything but a confident sequential stays parallel, the cheaper and earlier behaviour of naming everyone.
166
+ */
167
+ export async function decide_group_order(home, scheduler, { title, goal, context, message, members }, fetch_impl) {
168
+ const questions = [{ type: 'choice', name: 'order', choices: [
169
+ { value: 'parallel', description: '同时回答:各自独立回应即可,不需要看到别人的回答,如打招呼、各自介绍、各说一句看法、各自投票' },
170
+ { value: 'sequential', description: `依次回答:后发言的需要看到前面的回答,如接龙、轮流补充或反驳、逐个评审上一位的结果、按顺序分工接力;按房间发言顺序(${members.join('、')})逐个回答` }],
171
+ instructions: '用户用 @全体成员 请房间里所有成员回应这条消息。判断大家应同时各自回答,还是需要按顺序依次回答(后一位看到前一位的回答再接着说)。' }];
172
+ const state = scheduler_state(title, context, message, goal, '', {}, ROUTE_CONTEXT_MESSAGES);
173
+ const answers = await decide(home, scheduler, state, questions, fetch_impl);
174
+ const confidence = certainty(answers.order);
175
+ const sequential = answers.order?.choice === 'sequential' && confidence !== null && confidence >= ROUTE_CONFIDENCE;
176
+ return { action: sequential ? 'sequential' : 'all', choice: answers.order?.choice ?? null, confidence, ...judged_of(state, questions, answers) };
154
177
  }
155
178
 
156
179
  /**
@@ -162,15 +185,16 @@ export async function decide_interjection(home, scheduler, { title, goal, user_r
162
185
  { value: HOST_CHOICE, description: `由主持人 ${host} 处理:留言要改变讨论目标、规则、分工、安排或结束讨论,或同时涉及多位专家` },
163
186
  { value: NOTE_CHOICE, description: '仅补充信息:留言只是旁注、信息或鼓励,不要求任何人回应,按当前安排继续' }];
164
187
  const questions = [{ type: 'choice', name: 'handling', instructions: '讨论进行中用户发来这条留言。对照当前焦点、主持简报和安排,判断它最适合怎样处理。', choices }];
165
- const answers = await decide(home, scheduler, scheduler_state(title, context, message, goal, user_request, extra), questions, fetch_impl);
166
- const detail = detail_of(questions, answers);
188
+ const state = scheduler_state(title, context, message, goal, user_request, extra);
189
+ const answers = await decide(home, scheduler, state, questions, fetch_impl);
190
+ const judged = judged_of(state, questions, answers);
167
191
  const choice = answers.handling?.choice;
168
192
  const confidence = certainty(answers.handling);
169
193
  const sure = confidence !== null && confidence >= ROUTE_CONFIDENCE;
170
- if (sure && choice === NOTE_CHOICE) return { action: 'note', confidence, detail, label: '仅补充信息,按原计划继续' };
171
- if (sure && experts.includes(choice)) return { action: 'ask', expert: choice, confidence, detail, label: `先请 ${choice} 回应这条留言` };
194
+ if (sure && choice === NOTE_CHOICE) return { action: 'note', confidence, ...judged, label: '仅补充信息,按原计划继续' };
195
+ if (sure && experts.includes(choice)) return { action: 'ask', expert: choice, confidence, ...judged, label: `先请 ${choice} 回应这条留言` };
172
196
  const lean = choice === NOTE_CHOICE ? '按原计划继续' : experts.includes(choice) ? `请 ${choice} 回应` : choice === HOST_CHOICE ? '由主持人处理' : '';
173
- return { action: 'host', confidence, detail, label: choice === HOST_CHOICE && sure ? '这条留言需要主持人处理' : unsure_label(lean) };
197
+ return { action: 'host', confidence, ...judged, label: choice === HOST_CHOICE && sure ? '这条留言需要主持人处理' : unsure_label(lean) };
174
198
  }
175
199
 
176
200
  /**
@@ -189,17 +213,17 @@ export async function decide_next_speaker(home, scheduler, { title, goal, user_r
189
213
  { type: 'predicate', name: 'obligation',
190
214
  instructions: `是否存在明确依据要求 ${candidates.join('、')} 之一接着发言:简报分工或流程规定轮到其人,或最近发言点名追问了其人?仅仅是“本轮还没发言”不算依据。` }];
191
215
  const answers = await decide(home, scheduler, state, questions, fetch_impl);
192
- const detail = detail_of(questions, answers);
216
+ const judged = judged_of(state, questions, answers);
193
217
  const choice = answers.speaker?.choice;
194
218
  const confidence = certainty(answers.speaker);
195
219
  const obligation = answers.obligation?.probability;
196
220
  if (candidates.includes(choice) && confidence !== null && confidence >= ROUTE_CONFIDENCE && obligation >= ROUTE_CONFIDENCE) {
197
- return { action: 'next', expert: choice, confidence, detail, label: `本轮继续,请 ${choice} 接着发言` };
221
+ return { action: 'next', expert: choice, confidence, ...judged, label: `本轮继续,请 ${choice} 接着发言` };
198
222
  }
199
223
  if (typeof obligation === 'number' && obligation < ROUTE_CONFIDENCE) {
200
- return { action: 'host', confidence, detail, label: `有明确依据的概率 ${obligation.toFixed(2)},未达 ${ROUTE_CONFIDENCE.toFixed(2)},交给主持人安排` };
224
+ return { action: 'host', confidence, ...judged, label: `有明确依据的概率 ${obligation.toFixed(2)},未达 ${ROUTE_CONFIDENCE.toFixed(2)},交给主持人安排` };
201
225
  }
202
- return { action: 'host', confidence, detail, label: choice === HOST_CHOICE && confidence >= ROUTE_CONFIDENCE ? '建议由主持人调整安排,交给主持人' : unsure_label(candidates.includes(choice) ? `请 ${choice} 发言` : choice === HOST_CHOICE ? '交给主持人' : '') };
226
+ return { action: 'host', confidence, ...judged, label: choice === HOST_CHOICE && confidence >= ROUTE_CONFIDENCE ? '建议由主持人调整安排,交给主持人' : unsure_label(candidates.includes(choice) ? `请 ${choice} 发言` : choice === HOST_CHOICE ? '交给主持人' : '') };
203
227
  }
204
228
 
205
229
  /**
@@ -216,13 +240,13 @@ export async function decide_round(home, scheduler, { title, goal, user_request,
216
240
  if (candidates.length > 1) questions.push({ type: 'choice', name: 'first', instructions: '下一轮应先请谁发言?按简报分工或接龙顺序判断;没有规定时选被其他专家点名追问、最需要回应的人。',
217
241
  choices: candidates.map(name => ({ value: name, description: `先请 ${name}` })) });
218
242
  const answers = await decide(home, scheduler, state, questions, fetch_impl);
219
- const detail = detail_of(questions, answers);
243
+ const judged = judged_of(state, questions, answers);
220
244
  const choice = answers.next?.choice;
221
245
  const confidence = certainty(answers.next);
222
246
  if (choice !== 'continue' || confidence === null || confidence < ROUTE_CONFIDENCE || !candidates.length) {
223
247
  const sure = confidence !== null && confidence >= ROUTE_CONFIDENCE;
224
- return { action: 'host', confidence, detail, label: choice === HOST_CHOICE && sure ? '建议收束或调整方向,交给主持人' : unsure_label(choice === 'continue' ? '继续下一轮' : choice === HOST_CHOICE ? '收束或调整方向' : '') };
248
+ return { action: 'host', confidence, ...judged, label: choice === HOST_CHOICE && sure ? '建议收束或调整方向,交给主持人' : unsure_label(choice === 'continue' ? '继续下一轮' : choice === HOST_CHOICE ? '收束或调整方向' : '') };
225
249
  }
226
250
  const first = candidates.includes(answers.first?.choice) ? answers.first.choice : candidates[0];
227
- return { action: 'continue', first, confidence, detail, label: `继续下一轮,先请 ${first} 发言` };
251
+ return { action: 'continue', first, confidence, ...judged, label: `继续下一轮,先请 ${first} 发言` };
228
252
  }