LifeOS LocalIntelligence 数据源体系解析:基于{city, state}的通用美国城市政务情报聚合方案
【免费下载链接】LifeOS⛰️ The Life Operating System — an intent engineering platform that moves you from your current state to your ideal state, in life and work.项目地址: https://gitcode.com/GitHub_Trending/pe/LifeOS
本篇技术指南以 DataSources.md 为核心骨架,系统讲解 LifeOS 中 LocalIntelligence 技能的"通用数据源(Universal Data Sources)"设计:它如何仅凭{city, state}(偶尔含county)这一个键,驱动施工许可、犯罪、商业、官员、立法、选举、逮捕、新闻八个类目的日常情报聚合。读完你将掌握每个类目的公开数据源与访问模式、source_status优雅降级契约、以及通过sources.json与PREFERENCES.md做用户级源覆盖的完整做法,并能在源码层面印证其实现原理。
一、设计前提:一切数据源都围绕{city, state}键展开
LocalIntelligence 是一个"任意美国城市的通用政务情报聚合器"(generic civic intelligence aggregator)。它的核心设计约束写在 SKILL.md 与 DataSources.md 中:
- 不做逐城市配置(no per-city configuration):技能体内不硬编码任何城市名、端点或 API 密钥;唯一的数据键是用户身份文件中的
{city, state}(偶尔扩展到county)。 - 城市在运行时解析:所有工作流与工具统一通过 Hometown.ts 的
readHometown()读取PRINCIPAL_IDENTITY.md中**Hometown:**一行的值,见 SKILL.md 的 "Default Hometown — Always Dynamic" 一节:
import { readHometown } from "./Tools/Hometown.ts" const { city, state, zip, county } = await readHometown()- 数据源不可用时优雅降级:当某个通用数据源对当前城市没有数据时,抓取器返回
source_status: "unavailable",而不是抛出异常拖垮整个摘要(DataSources.md 开篇明示:"When a source is unavailable for a given city, the fetcher returnssource_status: "unavailable"rather than failing.")。
这一契约在 Types.ts 中有类型级定义:SourceStatus = "ok" | "unavailable" | "empty",其中"empty"表示源返回 200 但匹配条目为零(小城市的常见情况),"unavailable"表示源 4xx/5xx 或 DNS 失败。两者的差异会被 Pulse 仪表盘渲染为不同的空状态,详见 SKILL.md 的 Gotchas 一节。
每个抓取器(fetcher)导出统一签名的函数:
type Item = { title: string; source: string; url: string; date: string; summary?: string } type FetchResult = { items: Item[]; source_status: "ok" | "unavailable" | "empty"; errors?: string[] } export async function fetch(home: Hometown): Promise<FetchResult>八个抓取器(construction、crime、business、officials、legislation、elections、arrests、news)在 Refresh.ts 中通过Promise.allSettled并行执行,"一个死源不会清空整个摘要",失败的源以标签形式落入meta.errors。
二、Construction:施工许可与建设信号(FetchConstruction)
| 数据源 | 访问模式 | 说明 |
|---|---|---|
| 美国人口普查局建筑许可调查(US Census Building Permits Survey) | https://www2.census.gov/econ/bps/Place/... | 月度更新;覆盖 MSA 与 place 两级;全美最统一的源 |
| 城市开放数据许可门户(City open-data permits portal) | <city-domain>/permits.json、<city-domain>/api/permits | 尽力发现(best-effort);许多城市使用 Accela 的apo/...端点 |
| 规划委员会议程(Planning commission agendas) | Granicus / Legistar 发现模式,通常形如https://<city>.granicus.com/... | 用于捕获大型项目与分区变更信号 |
关于该源的三个工程要点,来自源码与 SKILL.md 的 Gotchas:
- 月度而非日更:Census BPS 是月度数据,"construction signal is medium-latency by nature",因此不要承诺"今日许可"。
- 实现优先级:FetchConstruction.ts 的注释明确建议"先实现 Census BPS API 路径,它是全国最统一、基线信号最好的路径"。
- 规范降级:当前版本该抓取器以
unavailable()桩实现返回 TODO 标记,说明实现的推荐起点与通用源的失败语义已经冻结。
三、Crime:犯罪数据委托而非直连(FetchCrime)
犯罪类目的数据源策略与其余七类截然不同——它不维护任何直接数据源。DataSources.md 原文:
Delegates to a dedicated crime-stats skill. No direct sources from this skill.
原因记录在 FetchCrime.ts 的 ISA 约束ISC-12中:该抓取器禁止直接调用 CitizenRIMS、FBI UCR、AreaVibes 等任何犯罪数据源,所有犯罪数据必须经由专用 crime-stats 技能路由,再由本技能把结果塑形进 digest。也就是说,犯罪部分的"数据源"只有一个:the dedicated crime-stats skill's workflow output。
四、Business:商业活动信号(FetchBusiness)
| 数据源 | 访问模式 |
|---|---|
| 城市开放数据营业执照数据集(City open-data business-license dataset) | <city-data-portal>/business-licenses发现 |
| 县书记官 DBA 备案(County clerk DBA filings) | 县书记官(county clerk recorder)网站页面 |
| 商会成员公告(Chamber of Commerce member announcements) | 当地商会 RSS(如有) |
该类的信号目标是"新开张的生意 / 关闭的生意",为 DailyBrief 提供本地商业活力视图。与所有类目一致,城市级 URL 不允许进入技能体,只能放在用户自定义层(见第七节)。
五、Officials:民选与任命官员动态(FetchOfficials)
| 数据源 | 访问模式 |
|---|---|
| Ballotpedia API | https://ballotpedia.org/api/v3/...,以 city + state 为键 |
| Google News 主题搜索 | 按每位官员(per officeholder)检索 |
| 城市新闻稿(City press releases) | <city-domain>/news/feed发现 |
FetchOfficials.ts 注释将该类目定义为"elected/appointed officials 的动向与相关新闻",覆盖市长、市议会、学区委员会等角色。它同样给出三个通用源:Ballotpedia API(官员名单 + 近期报道)、每位官员的 Google News 主题搜索、以及城市 RSS 新闻稿。
六、Legislation:立法与议会议程(FetchLegislation)
| 数据源 | 覆盖范围 |
|---|---|
OpenStates API(https://v3.openstates.org/) | 州级待决与已生效法案 |
| Granicus / Legistar | 通过通用 URL 模式发现市议会议程项 |
| 市议会会议日历(City council meeting calendar) | 以 iCal 或 RSS 形式暴露时 |
数据项携带metadata.status = "pending" | "enacted"(FetchLegislation.ts 与 DataSources.md 均确认)。
需要注意的边界(SKILL.md Gotchas):OpenStates 覆盖的是州立法机构,不覆盖市议会。因此市议会级别的待决/已生效法案要靠 Granicus/Legistar 的通用 URL 模式尽力发现,覆盖是 best-effort 的。当用户问"本周市议会议程上有什么",对应的工作流是 Legislation.md,它调用Tools/FetchLegislation.ts并返回带源链接的待决市议会项目。
七、Elections:选举与投票信息(FetchElections)
| 数据源 | 访问模式 |
|---|---|
| Ballotpedia API | 即将举行的选举、候选人、选票措施 |
| Vote.gov | 各州选民注册与投票信息链接 |
| 县选民登记官(County registrar of voters) | 投票站(polling places)URL 的尽力发现 |
该类的信号包括"谁在参选""票上有哪些 ballot measures""去哪儿投票"。同样遵循"公共数据优先"原则,Vote.gov 提供的是官方注册信息入口链接而非抓取内容。
八、Arrests:逮捕与警务记录(FetchArrests)
| 数据源 | 访问模式 |
|---|---|
| 县警长关押日志(County sheriff booking log) | <sheriff-domain>/booking-log发现 |
| 市警察局每日记事簿(City PD daily blotter) | <pd-domain>/blotter发现 |
| Patch 犯罪标签 | 软回退(soft fallback) |
DataSources.md 在本节特别划出红线:Public-data only. No paid people-search aggregators, no bypassing CAPTCHAs.(仅使用公共数据;不使用付费人肉搜索聚合器,不绕过 CAPTCHA。)SKILL.md 的 Gotchas 进一步说明:警长记事簿抓取因辖区而异(jurisdiction-specific),若无法发现则返回unavailable;v1 不做 CAPTCHA 绕过、不使用付费抓取服务。
九、News:本地新闻头条(FetchNews)
| 数据源 | 访问模式 |
|---|---|
| Patch RSS | https://patch.com/<state-slug>/<city-slug>/feed |
| Google News 主题搜索 | 以"<city>, <state>"为查询键 |
| 可选区域媒体 | 通过LIFEOS/USER/CUSTOMIZATIONS/SKILLS/LocalIntelligence/PREFERENCES.md配置 |
News 是当前实现最完整的抓取器,FetchNews.ts 展示了从 DataSources.md 表格到可运行代码的完整落地:
- 以
home.stateSlug与home.citySlug(由 Hometown.ts 的slugify()生成 kebab-case slug)拼出 Patch RSS URL; - 8 秒超时(
AbortSignal.timeout(8000)),HTTP 非 2xx 返回unavailable,解析到 0 条返回EMPTY_RESULT; - 解析
<item>块提取 title/link/pubDate/description,剥离 HTML 取前 240 字符作摘要,最多取 7 条。
关于 Patch 的一个已知坑(SKILL.md Gotchas):<state-slug>/<city-slug>路径对大多数城市有效,但少数城市存在历史遗留 slug;News 抓取器先尝试规范路径,失败后回退到以"<city>, <state>"为键的 Google News 主题搜索。另外,聚合搜索源按相关性而非日期排序,可能返回十年前的旧文——这正是下一节max_age_days与when:Nd存在的理由。
十、可选定制层:sources.json 与 PREFERENCES.md
DataSources.md 的最后一节定义了用户级定制机制,它是整个"通用源 + 用户覆盖"架构的收尾:
Per-user source overrides go in
~/.claude/LIFEOS/USER/CUSTOMIZATIONS/SKILLS/LocalIntelligence/PREFERENCES.md. Examples: an OpenStates API key for higher rate limits, a Google News topic ID, additional regional newspaper RSS feeds. The skill body never hardcodes any of this.
也就是说:OpenStates API key(换取更高限流)、Google News topic ID、区域报纸 RSS 等全部属于用户定制层,技能体永不硬编码。对应关系在 SKILL.md 的 Customization 一节中有完整定义:
PREFERENCES.md:加载并应用偏好与按源的 API key;sources.json:确定性(deterministic)的逐城市源清单(RSS 订阅、本地 JSON API),每次刷新都由Tools/UserSources.ts消费,城市级 URL 只允许出现在这里,绝不进入技能体。
sources.json 完整 schema
以 UserSources.help.md 与 UserSources.ts 为准,配置位于~/.claude/LIFEOS/USER/CUSTOMIZATIONS/SKILLS/LocalIntelligence/sources.json:
{ "sources": [ { "section": "news", "name": "Local Paper", "type": "rss", "url": "https://example.com/feed", "max_items": 7 }, { "section": "crime", "name": "City Crime API", "type": "json", "url": "https://example.org/api/crimes?days=2", "items_path": "items", "map": { "title": "{offense_type} — {location}", "date": "{reported_date}" }, "link": "https://example.org", "title_case": true } ] }各字段语义与源码中的默认行为:
| 字段 | 类型 | 说明 |
|---|---|---|
section | string | 目标摘要区段,必须是八个SectionKey之一(construction/crime/business/officials/legislation/elections/arrests/news),非法值会被过滤 |
name | string | 源名称,最终进入 item 的source字段 |
type | "rss"|"json" | rss同时处理 RSS 2.0 与 Atom;json需配合items_path/map |
url | string | 抓取地址;请求带User-Agent: LifeOS-LocalIntelligence/1.0,20 秒超时(FETCH_TIMEOUT_MS),跟随重定向 |
items_path | string | json类型下指向数组的点路径(dot-path),缺省视为响应根 |
map | object | json类型下{field}模板:title、date、summary |
link | string | 每条目的链接:固定 URL 或{field}模板;缺省回退到源url |
backfill_url | string | 供Tools/Backfill.ts拉取更深时间窗口用的可选 URL,缺省用url |
max_items | number | 每源最多条目,默认 7(DEFAULT_MAX_ITEMS) |
title_case | boolean | 标题首字母大写(原始 API 枚举字段通常很粗糙) |
strip_title_suffix | boolean | 对 Google News 式"Headline - Publication"标题,在最后一个" - "处拆分并把后缀提升为 item 的source |
filter_title | string | 标题必须包含的大小写不敏感子串(区域订阅过滤) |
max_age_days | number | 丢弃可解析日期早于 N 天的条目(聚合搜索源的确定性兜底;when:Nd查询操作符单独使用仍会漏进旧文) |
enabled | boolean | false时跳过该源 |
合并语义(UserSources.help.md 与applyUserSources()源码双重确认):用户条目前插进对应区段、按标题去重,区段状态变为ok,于是 ClaudeFill 会跳过该区段——确定性数据永远优先于模型研究。配置缺失 = 没有用户源(静默而非报错);单源失败只落入meta.errors,不清空摘要。
十一、刷新链路与双路径落盘:从数据源到 digest
理解数据源如何在系统中流动,需要看 Refresh.ts 的编排顺序(SKILL.md 明示):
- 内置抓取器(上述八个 Fetch* 工具)经
Promise.allSettled并行运行; UserSources.ts合并用户确定性源;ClaudeFill.ts(仅--fill模式)对仍然为空的区段做一次带确定性输出校验的 web 研究补全——填充只能"加",不能覆盖确定性数据。
落盘路径有两条且两条都必须写(SKILL.md Gotchas 用一个真实事故强调此事):USER/CUSTOMIZATIONS/SKILLS/LocalIntelligence/latest.json(Pulse 模块主读取路径)与MEMORY/DATA/LocalIntelligence/latest.json(遗留回退路径)。历史上曾出现只写遗留路径、Pulse 标签页静默服务 2.5 个月旧摘要的故障,修复后persist()同时写两份。
此外还有两个负载关键(load-bearing)的守卫:
- No-clobber 守卫:全空运行(抓取器挂掉 + fill 失败)仍会写入带日期的文件,但绝不覆盖已有内容的
latest.json——否则一次糟糕的凌晨 6 点运行就会清空仪表盘; --fill模式:bun run Tools/Refresh.ts --fill才触发 ClaudeFill;日常 Pulse cron 与仪表盘刷新按钮都使用--fill,裸调用则保持纯确定性。
按 DailyBrief.md 的 intent-to-flag 映射,刷新命令支持--force(已有今日摘要也强制重跑)、--summary(跳过编排直接读现有 latest.json)、--json(只输出原始 JSON):
bun run ~/.claude/skills/LocalIntelligence/Tools/Refresh.ts [--force] [--summary] [--json]十二、架构总览与进一步阅读
DataSources.md 是 LocalIntelligence 技能 "References" 层的唯一文档,SKILL.md 给出了它在一棵技能树中的位置——每个 Fetch* 工具对应一个 Workflow(DailyBrief、Construction、Crime、Business、Officials、Legislation、Elections、Arrests、News),输出一份 JSON digest 供 Pulse 的 LOCAL 标签页渲染九张区段卡片。
关键实现文件速查(均为仓库内相对路径):
- 数据源目录(本文主体)
- 技能主文档:架构、路由、Gotchas
- 类型契约:Item / FetchResult / Digest
- 城市解析:readHometown 与 NoHometownError
- 编排器:八抓取器并行、双路径落盘、no-clobber
- 用户源合并:sources.json schema 与模板映射
- News 抓取器:Patch RSS 完整实现
- Crime 抓取器:ISC-12 委托约束
- 每日摘要工作流:意图映射与输出
若要在自己的环境中启用这套数据源体系,前提是在PRINCIPAL_IDENTITY.md中写好**Hometown:**行(缺失时 Hometown.ts 会抛出NoHometownError并提示正确格式,如- **Hometown:** Austin, TX (ZIP 78701, Travis County)),然后运行bun run Tools/Refresh.ts --fill生成当日 digest。数据源表列出的所有通用端点(Census BPS、Ballotpedia、OpenStates、Vote.gov、Patch RSS、Granicus/Legistar 等)均为公共数据入口,可用性因城市而异,这正是source_status三态契约存在的意义:任何单一源都不得决定整个摘要的生死。
【免费下载链接】LifeOS⛰️ The Life Operating System — an intent engineering platform that moves you from your current state to your ideal state, in life and work.项目地址: https://gitcode.com/GitHub_Trending/pe/LifeOS
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考