import { accountName, downloadStatusName, healthName, humanNow, humanRelative, kindName, subscriptionStatusName, } from "../format.ts"; import type { EnvironmentFacts, PromptSection } from "../types.ts"; // 第 2 层:环境事实。会话开始时服务端从业务表读出的摘要,让模型知道"有什么"再去查"怎么样": // 反例(docs/design/agent-report-sample-bad.md)里模型只看了站点列表,就汇报"系统里没有下载器"。 // 只写名称、类型、状态和计数,不写地址、ID 与凭证;数字以会话开始时为准,细节仍以工具结果为准。 const MAX_NAMED = 6; function names(items: readonly T[], render: (x: T) => string): string { const shown = items.slice(0, MAX_NAMED).map(render); const more = items.length - shown.length; return more > 0 ? `${shown.join(";")};另有 ${more} 个` : shown.join(";"); } /** 计数表 → "追更中 3、已取消 2";全 0 返回空串 */ function counts(m: Record, label: (k: string) => string): string { return Object.entries(m) .filter(([, n]) => n > 0) .sort((a, b) => b[1] - a[1]) .map(([k, n]) => `${label(k)} ${n}`) .join("、"); } const total = (m: Record) => Object.values(m).reduce((a, b) => a + b, 0); export function renderEnvironment(f: EnvironmentFacts): string[] { const now = new Date(f.now); const tz = f.timeZone; const rel = (iso: string | null) => humanRelative(iso, now, tz); const lines: string[] = [`现在:${humanNow(now, tz)}。`]; const dl = f.downloaders.filter((d) => d.enabled); if (dl.length === 0) lines.push( f.downloaders.length > 0 ? `下载器:${f.downloaders.length} 个都已停用。` : "下载器:还没有接入(设置 → 下载器)。", ); else lines.push( `下载器 ${dl.length} 个:${names(dl, (d) => { const seen = rel(d.lastSeenAt); const t = d.tasks; const tasks = t.total > 0 ? `,本工具管理 ${t.total} 个任务(进行中 ${t.active}、失败 ${t.failed})` : ",没有本工具管理的任务"; return `「${d.name}」${kindName(d.kind)},${healthName(d.health)}${seen ? `(${seen})` : ""}${tasks}`; })}。`, ); const ms = f.mediaServers.filter((m) => m.enabled); lines.push( ms.length === 0 ? "媒体服务器:还没有接入。" : `媒体服务器 ${ms.length} 个:${names(ms, (m) => { const seen = rel(m.lastSeenAt); return `「${m.name}」${kindName(m.kind)},${healthName(m.health)}${seen ? `(${seen})` : ""}`; })}。`, ); const sites = f.sites.filter((s) => s.enabled); lines.push( sites.length === 0 ? "站点:还没有启用的站点。" : `站点 ${sites.length} 个:${names(sites, (s) => { const at = rel(s.lastHealthAt); const health = s.health === "unknown" ? "还没检查过" : `上次检查${healthName(s.health)}${at ? `(${at})` : ""}`; return `「${s.name}」${kindName(s.kind)},${health}${s.account ? `,${accountName(s.account)}` : ""}`; })}。`, ); const roots = f.libraryRoots.filter((r) => r.enabled); lines.push( roots.length === 0 ? "媒体库目录:还没有登记。" : `媒体库目录 ${roots.length} 个:${names(roots, (r) => `「${r.name}」${healthName(r.health)}`)}。`, ); const subs = total(f.subscriptions); lines.push(subs === 0 ? "订阅:没有。" : `订阅 ${subs} 个:${counts(f.subscriptions, subscriptionStatusName)}。`); const dls = total(f.downloads); lines.push( dls === 0 ? "下载任务(本工具管理):没有。" : `下载任务(本工具管理)${dls} 个:${counts(f.downloads, downloadStatusName)}。`, ); lines.push(f.openDecisions === 0 ? "待处理事项:没有。" : `待处理事项:${f.openDecisions} 条未关闭。`); if (f.webSearch !== undefined) lines.push(f.webSearch ? "网页搜索:已开启。" : "网页搜索:未开启。"); return lines; } export function environmentLayer(f: EnvironmentFacts | null | undefined): PromptSection { if (!f) return { layer: "environment", title: "当前环境", lines: [] }; return { layer: "environment", title: "当前环境", lines: [ "会话开始时的快照,只有名称、状态和计数;要细节或最新状态用下面的能力查。这里写了「还没有接入」的就是真的没有,不用再向用户确认。", ...renderEnvironment(f).map((l) => `- ${l}`), ], }; }