Skip to content
English

选择策略目录、保留额度及可行性筛选 ​

策略的具体实现如下。

ts
contextSelectionCatalog = {
  sequence: { validate: validateSequence, choose: ordered },
  fixed: { validate: validateFixed, choose: fixed },
  auto: { validate: validateAuto, choose: automatic },
  model: { validate: validateModel, choose: model },
} satisfies Record<ContextSelection['mode'], SelectionHandler>

export async function selectContextSteps(input: SelectionInput): Promise<SelectionResult> {
  const selection = input.selection;
  const modelId = selection.mode === 'model' ? selection.handlerId : undefined;
  const selector = input.selectors.find((handler) => handler.id === modelId);
  const selectorCalls = selector ? contextHandlerCalls(selector) : 0;
  const reserve = reservedContextCalls(selection);
  // The selector may call the model itself, so its allowance is unavailable to the steps it picks.
  const available = input.remainingCalls - reserve - selectorCalls;
  if (selection.mode === 'model' && available < 0)
    throw new BudgetError('No calls available for context selection');
  const candidates: ContextSelectionCandidate[] = input.candidates.filter(
    (candidate) => candidate.requiredCalls <= available,
  );
  if (selection.mode === 'model' && !candidates.length)
    throw new BudgetError('No feasible context operations after reserving calls');
  const result = await contextSelectionCatalog[selection.mode].choose({ ...input, candidates });
  if (selection.mode === 'model') {
    const required = result.stepIds.reduce(
      (sum, id) => sum + candidates.find((candidate) => candidate.id === id)!.requiredCalls,
      0,
    );
    breakWhile(required > available, invalid('Selected operations exceed context call allowance'));
  }
  return result;
}

async function automatic({ selection, candidates }: SelectionInput): Promise<SelectionResult> {
  if (selection.mode !== 'auto') throw invalid('Invalid automatic context selection');
  const ratio = selection.maxTrimLossRatio ?? 0.25;
  const trims = candidates
    .filter(
      (candidate) =>
        candidate.kind === 'trim' &&
        (candidate.trimLossRatio ?? 1) <= ratio &&
        candidate.projectedChars! < candidate.chars,
    )
    .sort((a, b) => a.trimLossRatio! - b.trimLossRatio!);
  const chosen = trims[0] ?? candidates.find((candidate) => candidate.kind === 'compress');
  return {
    stepIds: chosen ? [chosen.id] : [],
    reason:
      chosen?.kind === 'trim'
        ? 'bounded-trim-loss'
        : chosen
          ? 'preserve-through-compression'
          : 'no-safe-candidate',
  };
}

reservedContextCalls = (selection: ContextSelection) =>
  selection.mode === 'auto' || selection.mode === 'model' ? (selection.reserveCalls ?? 1) : 0

contextHandlerCalls = (handler: { maxCalls?: number }) => handler.maxCalls ?? 1

接口参考、使用示例与开发指南。