LifeOS LocalIntelligence:面向任意美国城市的公民情报聚合技能全解析
【免费下载链接】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
导读
LocalIntelligence 是 LifeOS 中的一项通用公民情报聚合技能(skill),它不针对任何单一城市做硬编码,而是从主理人(principal)的身份文件运行时解析出家乡城市(Hometown),并行抓取建筑许可、犯罪、新开业商家、公职人员、立法、选举、逮捕与本地新闻八类数据,汇成一份每日 JSON 摘要,供 Pulse 的 LOCAL 标签页消费。读完本文,你将掌握该技能的整体架构、Fetcher 契约、Refresh.ts编排流程、用户自定义源(sources.json)的完整 Schema,以及--fillAI 补全、无覆盖保护(no-clobber guard)等关键工程细节,并能直接在 LifeOS 中配置和运行它。
一、它解决什么问题
你居住地真正发生的事情分散在十几个几乎没人查看的网站上:城市的许可门户、市议会议程系统、治安官日志、本地报纸、选举办公室。没有任何单一信息流会告诉你:一条新法令正在表决、街对面正在盖楼、或一场选举即将到来。大多数"本地新闻"工具要么只覆盖单一城市,要么硬编码端点导致频繁失效,要么把好内容放在付费墙后面。
LocalIntelligence 的设计目标正是解决这三类痛点:跨全部美国城市保持通用(运行时解析目标城市,无每城市专属配置、无硬编码端点)、优雅降级(某个源无数据时返回空态而不是让整个摘要白屏)、数据可追溯(每条 item 都携带真实来源链接)。
技能定义位于 LifeOS/install/skills/LocalIntelligence/SKILL.md,其元信息声明:适用于本地新闻、家乡新闻、市议会会议、建筑许可、市长动态、选票提案、法令、近期逮捕、公民情报、本地摘要等场景;不用于全国新闻或任意城市犯罪查询。
二、核心设计:Hometown 永不硬编码
技能的每条工作流和每个工具都在运行时通过Tools/Hometown.ts解析家乡城市:
import { readHometown } from "./Tools/Hometown.ts" const { city, state, zip, county } = await readHometown()从源码 Tools/Hometown.ts 可以看到完整的解析逻辑:
- 身份文件路径可用环境变量
LIFEOS_PRINCIPAL_IDENTITY覆盖,默认值为~/.claude/LIFEOS/USER/PRINCIPAL/PRINCIPAL_IDENTITY.md; - 使用严格正则
HOMETOWN_RE匹配 Quick Reference 中的- **Hometown:** <City>, <ST> (ZIP <zip>, <County> County)行,<ST>可以是两位 USPS 州代码或完整州名,括号部分可选但推荐; - 内置了完整的 50 州 + DC 的州名→州代码映射表
STATE_NAME_TO_CODE,两字母州代码直接通过STATE_CODES集合校验; - 括号内容解析出
zip(支持12345或12345-6789格式)与county; - 派生
citySlug/stateSlug(kebab-case 化),用于 Patch RSS 这类模板化 URL; - 未找到
Hometown:行时抛出NoHometownError,CLI 运行会打印明确引导信息并以退出码 2 结束——没有备选城市。
这正是"技能保持城市无关、具体城市只存在于用户身份文件"的关键机制。
三、工作流路由
SKILL.md 定义了 9 条工作流及其触发短语(位于 LifeOS/install/skills/LocalIntelligence/Workflows/):
| 工作流 | 触发示例 | 对应文件 |
|---|---|---|
| DailyBrief | "daily local digest"、"what's happening in my city"、"refresh local intel" | Workflows/DailyBrief.md |
| Construction | "new construction"、"building permits"、"what's being built" | Workflows/Construction.md |
| Crime | "crime stats"、"is my city safer"、"crime trend" | Workflows/Crime.md |
| Business | "new businesses"、"business openings"、"business closures" | Workflows/Business.md |
| Officials | "city council"、"mayor"、"school board"、"public officials" | Workflows/Officials.md |
| Legislation | "pending laws"、"council agenda"、"ordinance vote"、"new laws in effect" | Workflows/Legislation.md |
| Elections | "upcoming election"、"ballot measures"、"who's running"、"polling location" | Workflows/Elections.md |
| Arrests | "recent arrests"、"police blotter"、"sheriff blotter" | Workflows/Arrests.md |
| News | "local news"、"hometown news"、"headlines from my city" | Workflows/News.md |
四、Fetcher 契约:所有抓取器的统一接口
所有内置抓取器遵循同一函数签名,定义于 Tools/Types.ts:
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>关键约定:
- 不抛异常,返回空态:抓取器遇到无数据情况返回
unavailable或empty而非抛出,Refresh.ts通过Promise.allSettled并行运行全部八个抓取器,单个源失效绝不会让整份摘要白屏; source_status: "empty"与"unavailable"语义不同:empty表示源返回 200 但零条匹配(小城镇常见),unavailable表示 4xx/5xx 或 DNS 失败,Pulse 仪表盘对两种状态渲染不同的空态;- 错误信息落入
meta.errors,带失败的源标签。
五、Refresh 编排器与双路径持久化
Tools/Refresh.ts 是整套流水线的核心编排器,运行流程如下:
readHometown()解析城市;refresh(home)通过Promise.allSettled并行执行八个 fetcher(FetchConstruction、FetchCrime、FetchBusiness、FetchOfficials、FetchLegislation、FetchElections、FetchArrests、FetchNews),构建含meta(城市、州、县、邮编、生成时间、sources_used、sources_failed、errors)与八个分区的 Digest;applyUserSources(digest)合并用户确定性源;- 若带
--fill参数,执行claudeFill(digest, home)补全仍为空的分区; persist(digest)写盘。
每次刷新的抓取顺序
内置 fetcher →UserSources.ts(确定性、用户配置)→ClaudeFill.ts(仅补空分区)。确定性数据永远优先于模型调研结果——用户源已填充的分区绝不会再被 AI 补全覆盖。
输出路径与 no-clobber 保护
每次 persist 会写:
- 带日期历史文件:
~/.claude/LIFEOS/MEMORY/DATA/LocalIntelligence/<YYYY-MM-DD>_<city>_<state>_digest.json(周/月/年视图聚合这些历史文件); latest.json同时写入两处:LIFEOS/USER/CUSTOMIZATIONS/SKILLS/LocalIntelligence/(Pulse 模块主读路径)与MEMORY/DATA/LocalIntelligence/(遗留回退路径)。
SKILL.md 的 Gotchas 记录了一个真实事故:2026-05-03 至 2026-07-16 期间Refresh.ts只写遗留路径,导致标签页静默展示了 2.5 个月前的摘要,而每天 6 点的任务却"成功"运行。修复后persist()双写,两条路径都不允许删除。
no-clobber 保护是承重墙:若一次全空运行(fetcher 全挂、fill 失败)生成了带日期的文件,但绝不会覆盖已填充的latest.json;否则一次故障的 6 点运行就会清空整个仪表盘。实现见 Tools/Refresh.ts:当totalItems(digest) === 0且已存在非空 latest 时,latestSkipped置真,仅落盘 dated 文件用于历史/调试。
六、用户自定义源:sources.json 完整 Schema
城市专属 URL 属于用户配置,永不进入公开技能。配置文件位于~/.claude/LIFEOS/USER/CUSTOMIZATIONS/SKILLS/LocalIntelligence/sources.json,由 Tools/UserSources.ts 在每次刷新时读取,Schema 详见 Tools/UserSources.help.md:
{ "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 } ] }支持的字段(取自 Tools/UserSources.ts 的类型定义):
| 字段 | 说明 |
|---|---|
section | 归属分区,必须是八类之一(construction/crime/business/officials/legislation/elections/arrests/news) |
name | 源名称,出现在 item 的source字段 |
type | "rss"(RSS 2.0 / Atom)或"json"(任意 JSON GET 端点) |
url | 源地址 |
items_path | JSON 模式下数组的 dot-path(默认响应根) |
map | JSON 模式的{field}模板:title/date/summary |
link | 每条 item 的链接:固定 URL 或{field}模板 |
backfill_url | 可选的更深时间窗 URL,供 Tools/Backfill.ts 使用 |
max_items | 每源最大条数(默认 7) |
title_case | 标题首字母大写(原始 API 枚举值读起来生硬) |
strip_title_suffix | 按最后一个" - "拆分聚合源标题并把后缀提升为 source(针对 Google News 的"Headline - Publication"模式) |
filter_title | 标题必须包含的不区分大小写子串(区域源过滤) |
max_age_days | 丢弃早于 N 天的条目(聚合搜索源按相关性排序,可能返回十年前旧闻;这是确定性兜底——仅靠when:操作符仍会漏进旧条目) |
enabled | 置false可禁用某源 |
实现细节:
- RSS 解析:正则切出
<item>/<entry>块,逐个提取 title、link(含 Atom 的<link href>单独处理)、pubDate/published/updated/dc:date;摘要若只是标题回声加刊物名(聚合源常见噪音)会被丢弃; - JSON 解析:通过 dot-path 取数组,
map.*模板用{field}占位符替换(支持嵌套字段),link支持固定值或逐条模板; - 合并语义:用户条目前插进分区并按标题去重,分区置为
ok,因此 ClaudeFill 会跳过该分区;失败源落入meta.errors,绝不导致整份摘要失败; - 容错:配置文件缺失=静默无用户源(非错误);JSON 损坏只打日志并返回空列表,不让整个刷新失败;单源 20 秒超时(
FETCH_TIMEOUT_MS),User-Agent 为LifeOS-LocalIntelligence/1.0 (+personal civic digest); - 配置位于
LIFEOS/USER/**(受管控删除区),城市专属 URL 永不进入公开技能。
七、--fill 模式:AI 补全与确定性校验
bun run Tools/Refresh.ts --fill在跑完 fetcher 与用户源后,由 Tools/ClaudeFill.ts 启动单个启用 WebSearch/WebFetch 的claude --print子进程,一次性调研所有空/不可用分区并返回严格 JSON。每日 Pulse cron 与仪表盘 Refresh 按钮都使用--fill;裸调用保持纯确定性。
各分区的调研窗口(SECTION_ASKS):construction 最近 14 天、crime 最近 7 天、business 最近 30 天、officials 最近 14 天、legislation 最近 30 天、elections 即将到来、arrests 最近 7 天、news 最近 7 天。
关键工程约束:
- 模型输出绝不未经校验进入摘要:
validateSection()做字段检查(title/source 非空、URL 必须匹配^https?://...形态)、URL 形状校验与上限截断(每分区最多 10 条);无效条目被丢弃并在meta.errors记录数量; - fill 只能增,不能减:fill 失败时退化为 fetcher 的摘要;
ok分区永不覆盖; - 计费与嵌套会话安全:子进程不传
ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN(走订阅 OAuth),并 unsetCLAUDECODE防止嵌套会话检测拒绝启动,镜像LIFEOS/TOOLS/Inference.ts的做法;LIFEOS_NOTIFICATION_CHANNEL置为headless,杜绝桌面语音通道泄漏; - 8 分钟默认超时(
DEFAULT_TIMEOUT_MS = 480_000)。
八、八类数据源:通用源目录
References/DataSources.md 是全部通用公民数据源的目录,所有源都基于{city, state}(偶尔含 county)推导,无每城市配置:
- Construction:美国人口普查局建筑许可调查(月度、MSA+place 级、最统一的来源)、城市开放数据许可门户(许多城市用 Accela
apo/...端点)、规划委员会议程(Granicus/Legistar 发现); - Crime:委托给专门的犯罪统计技能,本技能不直接抓取;
- Business:城市开放数据营业执照数据集、县书记官 DBA 备案、商会会员公告;
- Officials:Ballotpedia API(按城市+州)、Google News 官员话题搜索、城市新闻稿 feed;
- Legislation:OpenStates API(
https://v3.openstates.org/,州级待决+已通过法案)、Granicus/Legistar(市议会议程,通过知名 URL 模式)、市议会日历(iCal/RSS);条目携带metadata.status = "pending" | "enacted"; - Elections:Ballotpedia API(选举、候选人、选票提案)、Vote.gov(州注册+投票信息链接)、县选民登记处(投票站 URL 尽力发现);
- Arrests:县治安官登记日志、市警局每日 blotter、Patch crime 标签软回退;仅公开数据,无付费人肉搜索聚合器、无绕过 CAPTCHA;
- News:Patch RSS(
https://patch.com/<state-slug>/<city-slug>/feed)、Google News 话题搜索("<city>, <state>")、可选区域媒体(经PREFERENCES.md)。
九、Pulse 集成:JSON 写与读的耦合点
技能写 JSON,Pulse 读 JSON,耦合只存在于两处(SKILL.md 明确说明):
- Pulse 模块
~/.claude/LIFEOS/PULSE/modules/local-intelligence.ts:只读MEMORY/DATA/LocalIntelligence/latest.json,暴露GET /api/local-intelligence与POST /api/local-intelligence/refresh; - Pulse 仪表盘标签页
~/.claude/LIFEOS/PULSE/Observability/src/app/local/page.tsx:拉取 JSON 渲染九个分区卡片,导航入口位于AppHeader.tsx的lifeNav中、LIFE与WORK之间。
每日刷新由PULSE.toml中的[[job]]以 cron 表达式0 6 * * *驱动,运行bun run skills/LocalIntelligence/Tools/Refresh.ts。
三个典型使用示例
示例 1:运行每日摘要
User: "What's happening in my city today?" → 调用 DailyBrief 工作流 → 从 PRINCIPAL_IDENTITY.md 读取家乡城市 → 运行 Tools/Refresh.ts 编排器 → 写入 latest.json → 在对话中按类别汇总每类 Top-3 条目示例 2:检查议会日程
User: "Anything on the council agenda this week?" → 调用 Legislation 工作流 → 为家乡城市调用 Tools/FetchLegislation.ts → 返回带来源链接的待决议会条目示例 3:从仪表盘刷新
User 点击 LOCAL 标签页的 "Refresh now" → Pulse POST /api/local-intelligence/refresh → Pulse 模块派生 Tools/Refresh.ts 子进程 → 重新生成 latest.json,标签页重渲染十、真实世界的坑(Gotchas 精读)
SKILL.md 用一整节记录了实战中踩过的坑,这些是使用本技能最值得注意的约束:
- 没有 Hometown 行 = 不抓取:
PRINCIPAL_IDENTITY.md缺少Hometown:行时,每条工作流返回清晰的引导消息并以零码退出,不得凭空捏造城市; - 各城市 API 质量差异巨大:有的城市有丰富的 Granicus/OpenStates 覆盖,有的只发布 PDF;每个 fetcher 必须在通用源无数据时返回
unavailable而非失败; - 人口普查建筑许可调查是月度而非每日:建设信号天然是中延迟的,不要承诺"今天的许可";
- OpenStates 覆盖州议会而非市议会:市议会待决/已通过法案靠 Granicus/Legistar 的知名 URL 模式尽力发现,覆盖是 best-effort;
- Patch RSS 路径因州而异:
https://patch.com/<state-slug>/<city-slug>/feed对大多数城市有效,少数使用遗留 slug;News fetcher 先试规范路径,失败则回退到以"<city>, <state>"为关键词的 Google News 话题搜索; - 治安官日志抓取因司法辖区而异:可发现则抓县治安官 blotter 页,否则返回
unavailable;v1 不绕过 CAPTCHA、不用付费抓取服务; - 犯罪数据绝不与专门犯罪统计技能重复:
FetchCrime.ts调用该技能并把结果塑形进摘要;禁止在本技能内直接调用 CitizenRIMS、FBI UCR 或 AreaVibes(见设计 ISA 的 ISC-12); - 每日 JSON 文件会累积:旧摘要保留在
MEMORY/DATA/LocalIntelligence/供趋势检索,只有latest.json是 Pulse 的读取目标,定期清理由用户决定; - 本地报纸把好故事放在付费墙后:RSS 通常只露出标题+摘要,仪表盘链接到原文,技能从不绕过付费墙;
- 两个 latest.json 路径都必须写(见第五节);
- 聚合搜索源按相关性而非日期排序:Google News RSS 查询可能返回十年前旧闻;
sources.json中每个聚合源同时需要查询里的when:Nd操作符与max_age_days(确定性兜底,仅when:仍会漏旧条目); - Google News item 链接是 news.google.com 重定向、标题以 "Headline - Publication" 结尾:设置
strip_title_suffix: true把刊物名提升进 source 字段;重定向 URL 在浏览器中可正常解析,不要改写; - 市政 CivicPlus/城市网站常硬屏蔽爬虫(即使带浏览器头也返回 Akamai 403):不要直接接入,其内容通过 Google News 查询获取(2026-07-16 已对某城市站点验证);
- 本地报纸头版/聚焦故事与摘要的取舍、以及无 Hometown 的提示,共同构成"优雅降级"的完整闭环。
十一、自定义层与公开发布检查
自定义覆盖
执行前检查~/.claude/LIFEOS/USER/CUSTOMIZATIONS/SKILLS/LocalIntelligence/目录:若存在,加载并应用其中的PREFERENCES.md、可选源列表覆盖、按源 API 密钥(如 OpenStates、Google News topic ID),这些覆盖默认值;目录不存在则使用技能默认值(仅通用源)。sources.json是同一目录下的确定性每城市源列表,Schema 见前文。
发布前自检
技能主体是通用设计,发布前用 grep 预检:
rg -i "<your-city>|<your-zip>|<your-county>|/Users/[a-z]+/" ~/.claude/skills/LocalIntelligence/要求零匹配才算可发布;主理人的真实家乡只存在于PRINCIPAL_IDENTITY.md,永不写入技能本体。
十二、执行日志
完成任何工作流后,向执行日志追加一条 JSONL 记录(便于 LifeOS 追踪每次运行):
echo '{"ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","skill":"LocalIntelligence","workflow":"WORKFLOW_USED","input":"8_WORD_SUMMARY","status":"ok|error","duration_s":SECONDS}' >> ~/.claude/LIFEOS/MEMORY/SKILLS/execution.jsonl结语
LocalIntelligence 的价值不在于它抓了多少个源,而在于它的工程结构:Hometown 单一解析点、统一的 Fetcher 契约、确定性优先的抓取顺序、双路径持久化 + no-clobber 保护、以及"AI 只能补充不能覆盖"的补全边界。这套设计让一个技能可以服务于任意美国城市,且单点故障永远不会清空用户的每日本地摘要。想要深入源码,可从 SKILL.md 出发,依次阅读 Tools/Hometown.ts、Tools/Refresh.ts、Tools/UserSources.ts 与 Tools/ClaudeFill.ts,即可完整复现整条流水线。
【免费下载链接】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),仅供参考