last30days-skill 的 Agent JSON 导出契约:为下游 Agent 与脚本构建版本化机器可读研究数据
【免费下载链接】last30days-skillAI agent skill that researches any topic across Reddit, X, YouTube, HN, Polymarket, and the web - then synthesizes a grounded summary项目地址: https://gitcode.com/GitHub_Trending/la/last30days-skill
本篇技术指南解析 last30days-skill 的 Agent JSON 导出契约:如何从 CLI 或斜杠命令触发版本化 JSON 输出,逐项理解schema_version1.2 顶层字段、source_status十种状态、集群与结果字段的生成规则,以及 Discovery 导出、本地语料隐私边界与版本演进策略。读完后可在脚本、cron 任务、仪表盘或 Agent 工作流中安全消费该 JSON,而不必依赖易变的人类可读渲染。
触发方式:斜杠命令与 CLI 双通道
Agent JSON profile 是该项目面向下游 Agent、脚本、仪表盘与工作流工具的稳定机器可读研究契约。有两种触发方式。
在支持斜杠命令的宿主 Agent 中,直接要求返回版本化导出:
/last30days AI coding agents — return the versioned agent JSON export在脚本、cron 任务或开发场景中直接使用引擎:
python3 skills/last30days/scripts/last30days.py "AI coding agents" --emit=json python3 skills/last30days/scripts/last30days.py "AI coding agents" --emit=json --output results.json从源码看,--emit接受compact、json、context、md、html、brief六种渲染形式,默认为compact;--output是可选的精确输出文件路径,两者在 CLI 参数定义 中声明。
--json-profile:agent 与 raw 两种剖面
--emit=json默认使用--json-profile=agent,即带版本号的稳定契约:
# 默认 agent profile(可省略 --json-profile=agent) python3 skills/last30days/scripts/last30days.py "AI coding agents" --emit=json # 完整内部报告,面向调试与高级用户 python3 skills/last30days/scripts/last30days.py "AI coding agents" --emit=json --json-profile=raw--json-profile只接受agent与raw两个取值,默认agent(见 参数声明)。两者的本质区别:
- agent profile:由 to_agent_export 序列化,输出固定形状的版本化契约,当前版本为
1.2(常量AGENT_EXPORT_SCHEMA_VERSION,见 schema.py)。 - raw profile:直接对整个内部
Reportdataclass 做 JSON 序列化,有意不做版本化——当管线内部结构变化时它可能随之变化。它保留了 agent profile 引入之前的 JSON 序列化形态,适合调试但不适合依赖。
顶层字段
agent profile 的顶层字段始终全部存在;空运行(无结果)会输出空数组,而不是省略字段。
| 字段 | 类型 | 含义 |
|---|---|---|
schema_version | string | Agent 导出契约版本。当前版本为1.2。 |
query | string | 提交给引擎的研究主题。 |
generated_at | string | UTC 生成时间戳,RFC 3339 格式。 |
window_days | integer | 报告起止日期之间的天数。 |
source_status | object | 源名称到本次运行中观察到的结果的映射。 |
freshness_verdicts | array | --verify-freshness产生的按声明计的行为时判定;未请求验证或无可提取的保守声明时为空。 |
clusters | array | 按排名排序的关联结果分组。 |
results | array | 供下游处理的已排名扁平证据结果。 |
源名称仅在该次运行记录了其结果时才出现在source_status中。
实现层面,to_agent_export 按上述键序构造返回字典;generated_at经由 _agent_generated_at 统一转换为以Z结尾的 UTC 表示,window_days由报告起止日期相减得到(_window_days),因此window_days可直接用于判断时间窗宽度。
source_status:区分"干净的空结果"与"覆盖不完整"
每个源的取值是本次运行中观察到的结果状态,共十种,在源码中由RunOutcomeState字面量集合固定(schema.py):
| 状态 | 含义 |
|---|---|
ok | 源完成并返回了一个或多个条目。 |
no-results | 源成功完成但未发现匹配条目。 |
partial | 源在后续失败前已返回部分条目。 |
rate-limited | 检索被提供方速率限制中止。 |
auth-failed | 检索过程中凭据缺失、被拒绝或已过期。 |
unreachable | 源或网络端点不可达。 |
timeout | 检索超过时间上限。 |
schema-drift | 提供方响应不再匹配预期形状。 |
skipped-unconfigured | 因缺少必需配置而有意跳过该源。 |
error | 因其他原因检索失败。 |
消费方不得将失败状态解读为"该源没有相关讨论"——只有no-results表示源干净完成且零匹配。
从源码结构看,状态由SourceOutcomedataclass 承载(schema.py),其构造器对状态值与items_returned做校验,非法状态会直接抛出ValueError。状态流转逻辑集中在 RetrievalBundle.record_failure:当某个源已返回部分条目后失败,状态会被记为partial而非笼统的失败;而AUTH_FAILED即使在有条目存在时也被保留,因为它是一个可操作的信号(需要重新登录),不应被降级为通用partial。
集群字段与engagement_total的生成规则
clusters数组的顺序即排名顺序;每个结果中的cluster值是该数组的零基索引,未入簇的结果会省略该字段。
| 字段 | 类型 | 含义 |
|---|---|---|
title | string | 集群标题。 |
summary | string | 来自集群代表结果的摘要。 |
sources | array of strings | 集群覆盖的源。 |
engagement_total | number | 每条结果取一个标题级原生互动计数并求和。已知源使用其主计数(例如 Digg 用postCount);否则使用最大的计数类字段。排名、比率、评分与计算分数元数据被排除。 |
实现上,engagement_total由 _headline_engagement 逐条计算:
- 优先查 _HEADLINE_ENGAGEMENT_FIELDS_BY_SOURCE 中的首选字段——
digg用postCount,reddit用score,stocktwits用likes/reshares; - 未命中时回退到最大的"计数类"字段,而 _is_counter_field 明确排除
rank、rating、score、trustscore、followers、subscribers以及以_rank/_score/_ratio/_rate/_followers结尾的字段,防止把排名或评分误当互动量累加。
集群导出时若某个被剔除的候选参与了原标题推导,集群标题会用新的代表结果重建——这是隐私剔除不残留私有文本的保证之一(见下文without_sources)。
结果字段:results中每条证据的约定
| 字段 | 类型 | 含义 |
|---|---|---|
candidate_id | string | 稳定标识符,用于把该结果与freshness_verdicts[].candidate_id关联。1.2新增。 |
title | string | 结果标题。 |
source | string | 主源名称,如reddit、x、youtube、grounding。 |
url | string | 规范化结果 URL;提供方未给链接时可能为空字符串。 |
published_at | string | 主源条目的发布日期或时间戳;未知时省略。 |
summary | string | 规范化摘要,相关性解释或正文作为回退。 |
engagement | object | 主源条目的原生互动计数,如 Reddit 的score与num_comments,X 的likes与reposts。 |
relevance_score | number | 引擎最终分数,归一化到含端点的0.0–1.0区间。 |
cluster | integer | 指向clusters的零基索引;未入簇时省略。 |
未知值一律省略,而不是输出 JSONnull;字符串与集合字段即使为空(空字符串、空对象、空数组)也会保留。这条"省略而非 null"的规则由 _drop_none 递归实现,它作用于整棵导出结构。
两个实现细节值得消费方注意:
relevance_score在 to_agent_export 中由final_score / 100计算,先裁剪到[0.0, 1.0]再四舍五入到 4 位小数,因此跨源可直接比较;summary的回退链为:候选摘要 → 主条目摘要 → 相关性解释 → 主条目正文(_agent_summary),保证summary在绝大多数情况下非空。
freshness_verdicts:行为时声明校验
每个条目标识一条接地声明与其候选,附带主源条目、类型化verdict、适用时的原始值与重新推导值,以及源/证据 URL 和时间戳。verdict取四种值(由 FreshnessVerdictState 限定):
current:重新检查后声明仍成立;stale:成功的定点重取返回了已变化的值;contradicted:报告窗口内的更新条目明确与声明矛盾;unsupported:该数据无法重新核验,包括源状态降级的情况。
判定结构由 FreshnessVerdict 承载,包含claim_id、candidate_id、claim、source、source_item_id、verdict、checked_at及source_url/evidence_url/original_value/current_value/detail等可选字段。candidate_id正是1.2在results中新增的字段,消费方可据此把判定关联回它所标注的结果。消费方可以仅对verdict == "current"的声明采取行动,而不必把"源不可达"当作"声明已变化"的证据——这正是unsupported与stale的区别所在。该数组仅在请求--verify-freshness或存在可提取的保守声明时非空。
本地语料隐私边界
来自--corpus/LAST30DAYS_CORPUS_DIRS的证据默认排除在版本化 agent profile 之外。该剔除会移除:corpus 结果、仅含 corpus 的集群、corpus 源状态、corpus 相关的新鲜度判定,以及由 corpus 代表推导出的标题。
仅在"本次运行的 JSON 被有意允许包含本地文件内容"时才设置LAST30DAYS_CORPUS_IN_EXPORT=1。该 opt-in不改变 schema 形状或版本——它只是允许现有结果字段中出现source: "corpus"条目。未版本化的rawprofile 是完整的本地调试转储,可能包含 corpus 路径与文本。
从源码看,这是整个导出管线的"发布边界":to_agent_export 在未显式 opt-in 时调用 without_sources 对报告做深拷贝并剔除corpus源——包括items_by_source、source_status、freshness_verdicts、相关 artifacts,以及被剔除候选所影响的集群成员与标题重建。env 开关在 CLI 侧经 _config_truthy 读取后注入报告 artifacts。隐私剔除与 opt-in 的行为由 tests/test_corpus_source.py 覆盖:默认导出中不出现 corpus 条目,而corpus_in_export=True时结果允许携带source: "corpus"。
Discovery 导出:独立演进的版本化契约
Discovery 模式(--discover)有独立的版本化契约,使其主题结果不会改变常规研究导出的形状:
python3 skills/last30days/scripts/last30days.py --discover "AI agents" --emit=json顶层包含(版本1.1,常量DISCOVERY_EXPORT_SCHEMA_VERSION见 schema.py):
schema_version(1.1)、kind("discovery")、domain(全局无域趋势运行为"")、generated_at、window_days、source_status、feeds、results、warnings;outcome:"ok",或当没有任何主题越过置信度底线时为"nothing-solid"——这是"诚实的空结果",而不是排名噪声;weak_signal:nothing-solid 运行时最接近底线但未过的主题名,否则为null。
每个排名结果(由 to_discovery_export 序列化,与 DiscoveryTopic 一一对应)包含:
| 字段 | 含义 |
|---|---|
rank、topic | 排名与主题名。 |
why_spiking | 引擎对主题加速原因的解释。 |
momentum | new-this-week或building。 |
velocity_score | 速度分。 |
sources、engagement | 覆盖源与按源分组的原生互动计数。 |
command | 一条可直接执行的后续研究命令。 |
evidence_urls | 证据链接列表。 |
top_comment | 主题研究阶段中最强的逐字社区评论(含署名);浅层运行(--discover-shallow等)为null。 |
corroboration_count | 独立佐证源数量。 |
podcast_angle、x_article_angle | 引擎生成的播客/X 文章内容钩子;无推理提供方产出时为null。 |
previously_surfaced_count | 主题队列注记:此前扫描中出现该主题的次数;队列关闭时为0。 |
last_surfaced | 主题队列注记:主题上次出现日期;队列关闭时为null。 |
covered | 主题队列注记:主题是否已被覆盖;队列关闭时为false。 |
Discovery 契约遵循下文相同的版本策略,但与常规 agent 导出独立演进。--json-profile=raw在 discovery 场景下返回未版本化的内部DiscoveryReportdataclass 序列化(CLI 分支)。
对比运行:envelope 包装
对比查询使用 envelope,让每个实体保留各自的契约:
{ "schema_version": "1.2", "comparison": true, "entities": ["OpenAI", "Anthropic"], "reports": [ {"entity": "OpenAI", "report": {"schema_version": "1.2", "query": "OpenAI"}}, {"entity": "Anthropic", "report": {"schema_version": "1.2", "query": "Anthropic"}} ] }上述缩写报告仅示意 envelope 结构;真实报告中每个report包含全部已记录的顶层字段。消费方应按entity遍历reports,对每个内层报告独立套用 agent profile 契约。
远程 API 模式的限制
当LAST30DAYS_API_KEY与LAST30DAYS_API_BASE把运行路由到已配置的远程 API 时,服务器不会返回构建 agent profile 所需的本地Report。在该模式下,--json-profile=agent会以状态码 2 退出,而不是输出一个有误导性的形状;请使用--json-profile=raw保留远程后端的既有服务器响应 JSON 契约。该行为在 CLI 的 hosted 分支 中实现:错误信息写入 stderr 后直接return 2,便于脚本区分"远程后端不支持 agent profile"与其他运行错误。
版本演进策略与快照测试
schema_version使用major.minor编号。- 任何破坏性的字段删除、重命名、类型变更、语义变更或 envelope 变更都要求 major 版本递增。
- 向后兼容的字段新增可以使用 minor 版本递增。消费方应忽略无法识别的字段。
- 仓库内签入的黄金快照测试锁定当前完整形状;契约变更必须同时、刻意地更新版本号与快照。
1.2为每条results条目新增candidate_id,使判定可以关联到其所标注的结果。- Discovery
1.1为每条 discoveryresults新增podcast_angle、x_article_angle、previously_surfaced_count、last_surfaced、covered——向后兼容的 minor 递增;这些字段在角度生成器或主题队列填充前携带默认值(null/null/0/null/false)。 --json-profile=raw在该兼容策略之外,因为它镜像内部管线 dataclass。
快照测试位于 tests/test_agent_export.py:test_agent_export_matches_v1_2_golden_contract断言to_agent_export的完整输出与签入的黄金期望逐字段相等——这是"契约变更必须刻意更新快照"的落点。新鲜度判定与导出关联由 tests/test_freshness.py 覆盖,评估框架(tests/eval/harness.py)同样以 agent profile 的results作为评测输入,进一步印证它是下游工具的事实数据面。
与--preflightJSON 的边界
最后注意一条容易混淆的边界:--preflight --emit=json是另一个机器契约,用于权限与配置检查;--json-profile不改变 preflight 输出。换言之,--json-profile只对研究运行(含--discover)的--emit=json生效,消费 preflight 输出时应按它自己的形状解析,而不是套用本文的 agent 契约。
小结
last30days-skill 把"给 Agent 看的数据"与"给人看的渲染"明确分离:agent profile 以1.2契约提供顶层字段恒定、未知值省略、失败状态语义精确的 JSON,配合独立的 Discovery1.1契约、对比 envelope、本地语料隐私边界和以退出码 2 表达的能力边界,使脚本、cron 任务与下游 Agent 可以在不解析人类可读文本的前提下,可靠地消费研究结果、判定新鲜度并驱动后续行动。
【免费下载链接】last30days-skillAI agent skill that researches any topic across Reddit, X, YouTube, HN, Polymarket, and the web - then synthesizes a grounded summary项目地址: https://gitcode.com/GitHub_Trending/la/last30days-skill
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考