feat(agent): 集数待确认的智能体兜底——查看上下文 / 模拟 / 写规则三个能力,写入前强制模拟,接后台调查与授权内恢复
能力 ID 的域名允许单个下划线分词(episode_mapping)。 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
45a114263b
commit
7793c7b3bc
27 files changed
+1747
-48
No files matched your search
@@ -6,8 +6,25 @@ import { BrowserController, BrowserDownloadVerification, BrowserSnapshot } from
|
||||
import { CAPABILITY_CATALOG, type CapabilitySpec, type KnownCapabilityId } from "./capability.ts";
|
||||
import { Id, JsonObject, NonNegInt } from "./common.ts";
|
||||
import { MediaEntity, MediaKind } from "./content.ts";
|
||||
import {
|
||||
EpisodeMappingRuleSpec,
|
||||
EpisodeMappingRuleStatus,
|
||||
EpisodeMappingTableEntry,
|
||||
EpisodeReresolveResult,
|
||||
EpisodeResolutionMethod,
|
||||
EpisodeSpecialKind,
|
||||
EpisodeSuggestion,
|
||||
EpisodeValidationState,
|
||||
} from "./episode-mapping.ts";
|
||||
import { AcquisitionPlan, DownloadStatus, DownloadTask, Task, TaskStatus } from "./execution.ts";
|
||||
import { EnrichmentStatus, EnrichSuggestion, EnrichUsage, ExternalEntityType, ExternalIdentity } from "./identity.ts";
|
||||
import {
|
||||
EnrichmentStatus,
|
||||
EnrichSuggestion,
|
||||
EnrichUsage,
|
||||
EvidenceLevel,
|
||||
ExternalEntityType,
|
||||
ExternalIdentity,
|
||||
} from "./identity.ts";
|
||||
import { OrganizeMetadataInput, OrganizeMetadataOutput } from "./local-metadata.ts";
|
||||
import { ServerMapping } from "./ops.ts";
|
||||
import { OrganizationAction, OrganizationRecord } from "./organization.ts";
|
||||
@@ -909,6 +926,168 @@ export const WebSearchTestResult = z.object({
|
||||
});
|
||||
export type WebSearchTestResult = z.infer<typeof WebSearchTestResult>;
|
||||
|
||||
// ── episode_mapping.*:集数映射的智能体兜底(docs/plan/episode-resolution.md 批次 5) ──
|
||||
// inspect 给上下文包(有上限),simulate 试草稿规则,apply_rule 服务端强制先模拟、通过才写规则并重算。
|
||||
|
||||
const EpisodeRange = z.object({ start: z.number().int(), end: z.number().int() });
|
||||
const EpisodeSourceView = z.object({
|
||||
season: z.number().int().optional(),
|
||||
episodes: EpisodeRange.optional(),
|
||||
airDate: z.string().optional(),
|
||||
});
|
||||
/** 解析结果摘要:方式、规范季集、特别篇去向 */
|
||||
export const EpisodeResultBrief = z.object({
|
||||
method: EpisodeResolutionMethod,
|
||||
canonical: z.object({ season: z.number().int(), episodes: EpisodeRange }).nullable(),
|
||||
special: EpisodeSpecialKind.nullable(),
|
||||
});
|
||||
export type EpisodeResultBrief = z.infer<typeof EpisodeResultBrief>;
|
||||
|
||||
export const EpisodeMappingInspectInput = z.object({
|
||||
/** 集数待确认事项 ID;给了就按事项的作品 / 来源季 / 发布组取上下文 */
|
||||
decisionId: Id.optional(),
|
||||
/** 不给 decisionId 时必填 */
|
||||
mediaEntityId: Id.optional(),
|
||||
/** 来源季;不填 = 任意季 */
|
||||
season: z.number().int().min(0).max(999).nullable().optional(),
|
||||
/** 发布组;不填 = 没有发布组的资源 */
|
||||
releaseGroup: z.string().trim().max(200).nullable().optional(),
|
||||
});
|
||||
export const EpisodeInspectRelease = z.object({
|
||||
sourceReleaseId: Id,
|
||||
title: z.string(),
|
||||
publishedAt: z.string(),
|
||||
releaseGroup: z.string().nullable(),
|
||||
/** 发布者声称的季集 */
|
||||
source: EpisodeSourceView,
|
||||
/** 当前结果与依据 */
|
||||
current: EpisodeResultBrief.nullable(),
|
||||
evidence: z.array(z.string()),
|
||||
});
|
||||
export type EpisodeInspectRelease = z.infer<typeof EpisodeInspectRelease>;
|
||||
export const EpisodeMappingInspectOutput = z.object({
|
||||
entity: z.object({ id: Id, title: z.string(), kind: MediaKind, year: z.number().int().nullable() }),
|
||||
scope: z.object({ season: z.number().int().nullable(), releaseGroup: z.string().nullable() }),
|
||||
decision: z
|
||||
.object({
|
||||
id: Id,
|
||||
status: DecisionStatus,
|
||||
title: z.string(),
|
||||
summary: z.string().nullable(),
|
||||
/** 算出越界被停用的规则 */
|
||||
failedRules: z.array(z.string()),
|
||||
})
|
||||
.nullable(),
|
||||
/** 事项内资源(没有事项时 = 这个范围里当前待确认的资源),最多 50 条 */
|
||||
releases: z.array(EpisodeInspectRelease),
|
||||
/** 同作品同发布组近期的其他资源(含已对上 / 已换算的),最多 50 条 */
|
||||
neighbors: z.array(EpisodeInspectRelease),
|
||||
/** 季表:每季总集数、已知每集播出日期与标题(每季最多 100 集) */
|
||||
seasons: z.array(
|
||||
z.object({
|
||||
season: z.number().int(),
|
||||
total: z.number().int().nullable(),
|
||||
episodes: z.array(
|
||||
z.object({ episode: z.number().int(), airDate: z.string().nullable(), title: z.string().nullable() }),
|
||||
),
|
||||
}),
|
||||
),
|
||||
latestSeason: z.number().int().nullable(),
|
||||
ended: z.boolean(),
|
||||
/** 这部作品的映射规则(含停用及原因);byAgent = 智能体写的 */
|
||||
rules: z.array(
|
||||
z.object({
|
||||
id: Id,
|
||||
season: z.number().int().nullable(),
|
||||
releaseGroup: z.string().nullable(),
|
||||
kind: z.enum(["expression", "table", "special"]),
|
||||
expression: z.string().nullable(),
|
||||
targetSeason: z.number().int().nullable(),
|
||||
tableEntries: z.array(EpisodeMappingTableEntry).nullable(),
|
||||
special: EpisodeSpecialKind.nullable(),
|
||||
status: EpisodeMappingRuleStatus,
|
||||
disabledReason: z.string().nullable(),
|
||||
evidenceLevel: EvidenceLevel,
|
||||
byAgent: z.boolean(),
|
||||
/** 智能体写规则时的理由 */
|
||||
reasons: z.array(z.string()),
|
||||
}),
|
||||
),
|
||||
/** 确定性建议(按发布时间对播出日期,全部样本成立且唯一才有) */
|
||||
suggestion: EpisodeSuggestion.nullable(),
|
||||
truncated: z.object({ releases: z.boolean(), neighbors: z.boolean() }),
|
||||
});
|
||||
|
||||
export const EpisodeMappingSimulateInput = z.object({
|
||||
/** 集数待确认事项 ID:样本取事项内资源;规则的季 / 发布组不填时用事项的 */
|
||||
decisionId: Id.optional(),
|
||||
/** 不给 decisionId 时必填 */
|
||||
mediaEntityId: Id.optional(),
|
||||
/** 草稿规则(expression / table / special + 作用域) */
|
||||
rule: EpisodeMappingRuleSpec,
|
||||
/** 理由:模拟通过时作为智能体建议留在事项上(后台调查没有写规则授权时) */
|
||||
reasons: z.array(z.string().trim().min(1).max(300)).max(10).optional(),
|
||||
});
|
||||
export const EpisodeSimulationItem = z.object({
|
||||
sourceReleaseId: Id,
|
||||
title: z.string(),
|
||||
releaseGroup: z.string().nullable(),
|
||||
/** 是否是事项内样本 */
|
||||
sample: z.boolean(),
|
||||
source: EpisodeSourceView,
|
||||
before: EpisodeResultBrief.nullable(),
|
||||
after: EpisodeResultBrief,
|
||||
validation: EpisodeValidationState,
|
||||
changed: z.boolean(),
|
||||
});
|
||||
export type EpisodeSimulationItem = z.infer<typeof EpisodeSimulationItem>;
|
||||
export const EpisodeSimulationSummary = z.object({
|
||||
/** 事项内样本数 */
|
||||
samples: NonNegInt,
|
||||
/** 其中套用后解决的(非待确认、校验非 fail) */
|
||||
resolved: NonNegInt,
|
||||
/** 其中校验结果为「对得上」的 */
|
||||
passed: NonNegInt,
|
||||
/** 样本不在模拟范围内(太旧)的条数 */
|
||||
missing: NonNegInt,
|
||||
/** 当前已对上 / 已换算、套用后变成待确认的条数 */
|
||||
regressions: NonNegInt,
|
||||
/** 套用后结果有变化的条数 */
|
||||
changed: NonNegInt,
|
||||
/** 参与模拟的资源数 */
|
||||
scanned: NonNegInt,
|
||||
/** 事项内样本全部解决且没有让已对上的资源变成待确认 */
|
||||
ok: z.boolean(),
|
||||
});
|
||||
export type EpisodeSimulationSummary = z.infer<typeof EpisodeSimulationSummary>;
|
||||
export const EpisodeMappingSimulateOutput = z.object({
|
||||
rule: EpisodeMappingRuleSpec,
|
||||
summary: EpisodeSimulationSummary,
|
||||
/** 没通过的原因(通过时为空) */
|
||||
failures: z.array(z.string()),
|
||||
/** 样本 + 有变化的资源,最多 100 条 */
|
||||
items: z.array(EpisodeSimulationItem),
|
||||
/** 已作为智能体建议留在事项上 */
|
||||
suggestionRecorded: z.boolean(),
|
||||
});
|
||||
|
||||
export const EpisodeMappingApplyRuleInput = z.object({
|
||||
/** 集数待确认事项 ID */
|
||||
decisionId: Id,
|
||||
/** 草稿规则;季 / 发布组不填时用事项的 */
|
||||
rule: EpisodeMappingRuleSpec,
|
||||
/** 为什么这样映射(依据),至少一条 */
|
||||
reasons: z.array(z.string().trim().min(1).max(300)).min(1).max(10),
|
||||
});
|
||||
export const EpisodeMappingApplyRuleOutput = z.object({
|
||||
ruleId: Id,
|
||||
evidenceLevel: EvidenceLevel,
|
||||
summary: EpisodeSimulationSummary,
|
||||
reresolve: EpisodeReresolveResult,
|
||||
/** 写入后事项的状态(资源都解决时已自动关闭) */
|
||||
decisionStatus: DecisionStatus,
|
||||
});
|
||||
|
||||
/** 能力 ID → 输入/输出 schema(server 注册处理器、agent 生成工具定义时共用) */
|
||||
// ── schedule.draft_check:按自然语言需求起草巡检(建成未启用,用户在计划页检查后启用) ──
|
||||
|
||||
@@ -985,6 +1164,9 @@ export const CAPABILITY_IO = {
|
||||
"metadata.resolve_organize": { input: OrganizeMetadataInput, output: OrganizeMetadataOutput },
|
||||
"metadata.enrich": { input: MetadataEnrichInput, output: MetadataEnrichOutput },
|
||||
"schedule.draft_check": { input: ScheduleDraftCheckInput, output: ScheduleDraftCheckOutput },
|
||||
"episode_mapping.inspect": { input: EpisodeMappingInspectInput, output: EpisodeMappingInspectOutput },
|
||||
"episode_mapping.simulate": { input: EpisodeMappingSimulateInput, output: EpisodeMappingSimulateOutput },
|
||||
"episode_mapping.apply_rule": { input: EpisodeMappingApplyRuleInput, output: EpisodeMappingApplyRuleOutput },
|
||||
} as const satisfies Record<KnownCapabilityId, { input: z.ZodType; output: z.ZodType }>;
|
||||
|
||||
type CapabilityIo = typeof CAPABILITY_IO;
|
||||
@@ -1076,3 +1258,9 @@ export type WebFetchInput = z.infer<typeof WebFetchInput>;
|
||||
export type WebFetchOutput = z.infer<typeof WebFetchOutput>;
|
||||
export type MetadataEnrichInput = z.infer<typeof MetadataEnrichInput>;
|
||||
export type MetadataEnrichOutput = z.infer<typeof MetadataEnrichOutput>;
|
||||
export type EpisodeMappingInspectInput = z.infer<typeof EpisodeMappingInspectInput>;
|
||||
export type EpisodeMappingInspectOutput = z.infer<typeof EpisodeMappingInspectOutput>;
|
||||
export type EpisodeMappingSimulateInput = z.infer<typeof EpisodeMappingSimulateInput>;
|
||||
export type EpisodeMappingSimulateOutput = z.infer<typeof EpisodeMappingSimulateOutput>;
|
||||
export type EpisodeMappingApplyRuleInput = z.infer<typeof EpisodeMappingApplyRuleInput>;
|
||||
export type EpisodeMappingApplyRuleOutput = z.infer<typeof EpisodeMappingApplyRuleOutput>;
|
||||
@@ -24,8 +24,11 @@ export const MutationPlan = z.object({
|
||||
});
|
||||
export type MutationPlan = z.infer<typeof MutationPlan>;
|
||||
|
||||
/** 能力 ID:`域.动作`,如 download.submit */
|
||||
export const CapabilityId = z.string().regex(/^[a-z][a-z0-9]*\.[a-z][a-z0-9_]*$/);
|
||||
/**
|
||||
* 能力 ID:`域.动作`,如 download.submit;域名可用单个下划线分词(episode_mapping.inspect),
|
||||
* 不以下划线结尾、不含连续下划线,工具名(`域__动作`)按第一个 `__` 反向切分仍无歧义
|
||||
*/
|
||||
export const CapabilityId = z.string().regex(/^[a-z][a-z0-9]*(?:_[a-z0-9]+)*\.[a-z][a-z0-9_]*$/);
|
||||
export type CapabilityId = z.infer<typeof CapabilityId>;
|
||||
|
||||
// ── 能力描述符与首批能力(任务书 §3.1-9;REQ §10.3) ──
|
||||
@@ -94,6 +97,9 @@ export const CAPABILITY_IDS = [
|
||||
"metadata.enrich",
|
||||
"metadata.resolve_organize",
|
||||
"schedule.draft_check",
|
||||
"episode_mapping.inspect",
|
||||
"episode_mapping.simulate",
|
||||
"episode_mapping.apply_rule",
|
||||
] as const;
|
||||
export const KnownCapabilityId = z.enum(CAPABILITY_IDS);
|
||||
export type KnownCapabilityId = z.infer<typeof KnownCapabilityId>;
|
||||
@@ -629,6 +635,39 @@ export const CAPABILITY_CATALOG: Record<KnownCapabilityId, CapabilityDescriptor>
|
||||
false,
|
||||
"safe",
|
||||
),
|
||||
"episode_mapping.inspect": d(
|
||||
"episode_mapping.inspect",
|
||||
"看集数映射上下文",
|
||||
"集数待确认时用:按事项(或作品 + 来源季 + 发布组)取上下文——事项内资源的原标题、发布时间、原始季集与当前结果,同组近期其他资源,季表(每季总集数、每集播出日期与标题),现有规则(含停用原因),确定性建议规则。写规则前先看这个",
|
||||
"read",
|
||||
"EpisodeMappingInspect",
|
||||
"read",
|
||||
"low",
|
||||
true,
|
||||
false,
|
||||
),
|
||||
"episode_mapping.simulate": d(
|
||||
"episode_mapping.simulate",
|
||||
"模拟映射规则",
|
||||
"试一条草稿映射规则(expression 偏移表达式 / table 对照表 / special 特别篇·花絮·忽略),逐条给出套用后的季集与校验,并汇总事项内样本是否全部解决、有没有让已对上的资源变成待确认。不写规则;智能体会话里带 decisionId 且模拟通过时,规则和理由会作为智能体建议留在事项上,由用户确认",
|
||||
"preview",
|
||||
"EpisodeMappingSimulate",
|
||||
"read",
|
||||
"low",
|
||||
true,
|
||||
false,
|
||||
),
|
||||
"episode_mapping.apply_rule": d(
|
||||
"episode_mapping.apply_rule",
|
||||
"写映射规则",
|
||||
"把草稿映射规则写进作品并按作品重算资源的集数。服务端先强制模拟:事项内样本必须全部解决、已对上的资源不能变成待确认,否则拒绝不写。先用 episode_mapping.simulate 确认通过再调用,每个事项只写一次;拿不准就不要写",
|
||||
"execute",
|
||||
"EpisodeMappingApplyRule",
|
||||
"organize.execute",
|
||||
"medium",
|
||||
false,
|
||||
true,
|
||||
),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -691,6 +730,9 @@ export const CAPABILITY_ACTIVITY: Record<KnownCapabilityId, CapabilityActivity>
|
||||
"web.fetch": act("读", "网页", "个"),
|
||||
"metadata.resolve_organize": act("补全", "整理元数据"),
|
||||
"metadata.enrich": act("补全", "元数据"),
|
||||
"episode_mapping.inspect": act("查", "集数映射"),
|
||||
"episode_mapping.simulate": act("模拟", "映射规则", "次"),
|
||||
"episode_mapping.apply_rule": act("写入", "映射规则", "条"),
|
||||
};
|
||||
|
||||
function d(
|
||||
|
||||
Reference in new issue
Block a user