feat(consultation): unify server-distributed agents without fixed roles

This commit is contained in:
2026-09-24 11:56:42 +08:00
parent 248776d855
commit 8a157be4f6
35 changed files with 625 additions and 668 deletions

View File

@@ -42,7 +42,7 @@ export async function prepareTeacherModel(
throw new TeacherError(
422,
'teacher_model_unavailable',
'老师所用模型暂不可用,请联系运营调整。'
'智能体所用模型暂不可用,请联系运营调整。'
);
}
const savedChoice = definition.model.reasoning_choice;
@@ -61,7 +61,7 @@ export async function prepareTeacherModel(
throw new TeacherError(
422,
'teacher_model_unavailable',
'老师所用思考选项已不可用,请联系运营调整。'
'智能体所用思考选项已不可用,请联系运营调整。'
);
}
const outputLimit = Math.min(
@@ -110,7 +110,7 @@ export async function streamTeacherReply(
reads.shift();
}
if (options && estimateTeacherTokens(build()) + toolBudget > options.inputLimit) {
throw new TeacherError(422, 'teacher_context_too_long', '老师读取的内容超过上下文预算,请缩小问题范围或联系运营增加预算。');
throw new TeacherError(422, 'teacher_context_too_long', '智能体读取的内容超过上下文预算,请缩小问题范围或联系运营增加预算。');
}
const result = await streamTeacherTurn(config, modelId, reasoningFields, outputLimit,
build(), signal, options?.finalOnly ? () => undefined : onText, fetchImpl, tools, round === 6);
@@ -126,7 +126,7 @@ export async function streamTeacherReply(
return usage;
}
if (!tools || round === 6) {
throw new TeacherError(502, 'teacher_tools_unavailable', '老师未能完成本次读取,请缩小问题范围后重试。');
throw new TeacherError(502, 'teacher_tools_unavailable', '智能体未能完成本次读取,请缩小问题范围后重试。');
}
const batch: TeacherModelMessage[] = [{ role: 'assistant', content: result.text, tool_calls: result.calls,
...(result.reasoning ? { reasoning_content: result.reasoning } : {}) }];
@@ -136,7 +136,7 @@ export async function streamTeacherReply(
- toolBudget - estimateTeacherTokens([...messages, ...batch]) - 64 * result.calls.length)
* TEACHER_ESTIMATED_BYTES_PER_TOKEN / result.calls.length));
if (resultBudget < 128) {
throw new TeacherError(422, 'teacher_context_too_long', '老师读取的内容超过上下文预算,请缩小问题范围或联系运营增加预算。');
throw new TeacherError(422, 'teacher_context_too_long', '智能体读取的内容超过上下文预算,请缩小问题范围或联系运营增加预算。');
}
for (const call of result.calls) {
signal.throwIfAborted();
@@ -184,10 +184,10 @@ async function streamTeacherTurn(
? '词元点数不足,请补充后再提问。'
: response.status === 401
? '模型凭据已失效,请重新提问。'
: '老师暂时无法回复,请稍后重试。'
: '智能体暂时无法回复,请稍后重试。'
);
}
if (!response.body) throw new TeacherError(502, 'teacher_stream_missing', '未收到老师回复。');
if (!response.body) throw new TeacherError(502, 'teacher_stream_missing', '未收到智能体回复。');
const reader = response.body.getReader(),
decoder = new TextDecoder();
let buffer = '',
@@ -211,13 +211,13 @@ async function streamTeacherTurn(
usage?: { prompt_tokens: number; completion_tokens: number };
};
if (event.error)
throw new TeacherError(502, 'teacher_model_failed', '老师回复中断,请保留当前内容后重试。');
throw new TeacherError(502, 'teacher_model_failed', '智能体回复中断,请保留当前内容后重试。');
const choice = event.choices?.[0];
if (!tools && (choice?.delta?.tool_calls || choice?.finish_reason === 'tool_calls'))
throw new TeacherError(
502,
'teacher_tools_unavailable',
'老师只能提供文字建议,本次回复未完成。'
'智能体只能提供文字建议,本次回复未完成。'
);
if (typeof choice?.delta?.content === 'string') {
text += choice.delta.content;
@@ -227,7 +227,7 @@ async function streamTeacherTurn(
if (typeof choice?.delta?.reasoning_content === 'string') reasoning += choice.delta.reasoning_content;
for (const delta of choice?.delta?.tool_calls ?? []) {
if (!Number.isSafeInteger(delta.index) || delta.index < 0 || delta.index >= 8)
throw new TeacherError(502, 'teacher_stream_invalid', '老师读取请求格式无效。');
throw new TeacherError(502, 'teacher_stream_invalid', '智能体读取请求格式无效。');
const call = calls.get(delta.index) ?? { id: '', type: 'function', function: { name: '', arguments: '' } };
if (delta.id) call.id = delta.id;
if (delta.function?.name) call.function.name += delta.function.name;
@@ -262,7 +262,7 @@ async function streamTeacherTurn(
if (data) frame(data);
}
if (buffer.length > 1_048_576)
throw new TeacherError(502, 'teacher_stream_invalid', '老师回复格式无效。');
throw new TeacherError(502, 'teacher_stream_invalid', '智能体回复格式无效。');
if (chunk.done) break;
}
if (signal.aborted) throw signal.reason;
@@ -270,7 +270,7 @@ async function streamTeacherTurn(
throw new TeacherError(502, 'teacher_stream_interrupted', '回复中断,以下内容可能不完整。');
if ((calls.size && finishReason !== 'tool_calls') || (finishReason === 'tool_calls' && !calls.size)
|| [...calls.values()].some(call => !call.id || !call.function.name))
throw new TeacherError(502, 'teacher_stream_interrupted', '老师读取请求未完整收到,请重试。');
throw new TeacherError(502, 'teacher_stream_interrupted', '智能体读取请求未完整收到,请重试。');
return { usage, text, reasoning, calls: [...calls.values()] };
} finally {
await reader.cancel().catch(() => undefined);