///|
/// LLM 上下文组装器:把检索结果按 token 预算拼成可直接喂给大模型的提示词。
pub struct ContextBuilder {
  max_tokens : Int
  tokenizer : Tokenizer
} derive(ToJson, FromJson)

///|
pub fn ContextBuilder::new(
  max_tokens? : Int = 800,
  tokenizer? : Tokenizer = Tokenizer::new(),
) -> ContextBuilder {
  { max_tokens, tokenizer }
}

///|
/// 组装上下文:先放问题,再按相关性放入带编号的片段,超出预算即停止。
pub fn ContextBuilder::build(
  self : ContextBuilder,
  query : String,
  results : Array[SearchResult],
) -> String {
  let sb = StringBuilder::new()
  sb.write_string(
    "请仅根据以下资料回答问题,不要编造资料中没有的内容。\n\n",
  )
  sb.write_string("问题:")
  sb.write_string(query)
  sb.write_string("\n\n资料:\n")
  let mut used = 0
  for i = 0; i < results.length(); i = i + 1 {
    let r = results[i]
    let est = self.tokenizer.estimate_tokens(r.text)
    if used + est > self.max_tokens {
      break
    }
    sb.write_string("\n[")
    sb.write_string((i + 1).to_string())
    sb.write_string("](来源:")
    sb.write_string(r.doc_id)
    sb.write_string(")\n")
    sb.write_string(r.text)
    sb.write_string("\n")
    used = used + est
  }
  sb.to_string()
}