跳转至

外部能力

一切不是仓库内代码的东西——事件 API、真实 persona 池——都是声明式的能力, 而不是藏起来的接线。能力统一收录在一个提交入库的公开文件里;端点和 key 住在 完全另一个地方;每个研究按名字记录自己实际用过哪些。

能力注册表

resources/capabilities.yaml 是那份公开索引。它被刻意提交入库、开源:一个条目 宣告某个能力存在、什么时候该用它、以及没有它时怎么继续走。

字段 含义
name 唯一 id。kind: mcp 时它必须等于 .mcp.json 里的 server 名
kind mcp | http_client | builtin
provides 一行话:它给研究带来什么
stages 哪些 sv-* skill 会咨询它(capability_view 按此过滤)
use_when skill 拿研究去比对的触发条件
confirm true = 调用会花掉协作者的预算、或把数据发出去 → 先征求同意
cost 计费方式(见下面的辨析)
available_when 静态可用性检查:{mcp_server: <name>}{env: [VARS…]}
health 真正依赖它之前要跑的实时探针
howto skill 怎么用它,含物化与溯源规则
fallback 不可用时怎么办——离线 / 开源路径
attach 有访问权的人如何注册那个私有端点

构建 skill 通过 skills.sv_workspace.capability_view(stage=…) 读它:原样返回 每个条目,再加两个解析出来的字段:

解析字段 类型 含义
available bool 在当前环境里 available_when 是否通过
availability_note str 原因——比如缺哪个环境变量、缺哪个 MCP server

confirmcost 是两个不同的信号

confirm: true 表示这次调用会花掉协作者的预算或把数据发出去,所以工作流 要停下来问。cost 描述的是计费。两者互相独立。内置的 event_toolconfirm: false 并且有成本:它便宜、只读,所以工作流直接用、在阶段闸门处 汇报——但在托管服务上,每一次抓取仍然计入你的配额。不要confirm: false 读成「免费」。

每个条目都遵守三条不变量:

  • 端点和 key 永不进注册表。 kind: mcp 通过 gitignore 的 .mcp.json 解析, kind: http_client 通过 gitignore 的 .env 里的环境变量解析。
  • 研究在自己的 resources.json 里按名字(NAME)钉住用过的能力McpServerDecl)——可复现的溯源,私有端点不会漏进提交的产物。
  • 接入一个新服务 = 加一条注册表条目、零 skill 修改。 每个 skill 只带一个 稳定、通用的能力检查钩子;skill 正文里从不出现某个具体服务的名字。

配置服务:.env 是唯一来源

服务 key 只住在一个 gitignore 的文件里:socioverse-beta/.env (从 .env.example 起步)。.mcp.json 是由它生成的:

python scripts/gen_mcp_json.py

生成脚本读 SV_EVENT_API_URL / SV_EVENT_API_KEYSV_USER_POOL_MCP_URL / SV_USER_POOL_MCP_KEY,写出对应的 mcpServers 条目。 改 .env、重跑脚本——不要手改 .mcp.json,两个文件都不要提交。安装路径见 安装

event_tool——给 E 的真实世界事件与宏观背景

event_tool 如何锚定 E:20 个结构化数据源 → MCP 的三个 tools → 逐月环境快照,全程带 grounding 溯源

kind http_client
stages sv-initsv-build-environment
provides E 的结构化宏观 / 新闻 / 市场背景(FRED、census、逐月新闻归档)
confirm false——但计费,见上面的辨析
配置 SV_EVENT_API_URLSV_EVENT_API_KEY,可选 SV_EVENT_LOCAL_CACHE

sv-init 的 grounding 引导会把结构化事实推迟给这个服务; sv-build-environment 把它们物化进研究。

三个最常踩错的点

  1. SV_EVENT_API_URL 是完整的 MCP 端点,含路径——例如 http://host:9997/event_mcp,不是裸主机名。生产服务在 MCP 线路上说 JSON-RPC(tools/call get_data),带 Authorization: Bearer 头。不需要 MCP SDK。
  2. 健康探针在主机根路径 /healthz不在 MCP 路径下。客户端会先探它 并 fail-fast,然后才逐 source 抓取。
  3. months= 是必填的。 生产服务没有 query 解析器(agent 层是被刻意去掉 的)。永远显式传 months= 列表,通常也传 sources=query 只用于溯源。

API

from socioverse import materialize_external_events, month_range

path, ev = materialize_external_events(
    study_dir,
    query="US inflation and consumer sentiment, 2024",   # 仅用于溯源
    months=month_range("2024-01", "2024-12"),            # 必填
    sources=["fred", "nyt"],
)

materialize_external_events(...) 写出 environment/external_events.json, 返回 (path, bundle)month_range(start, end) 生成闭区间的 "YYYY-MM" 列表 (上限 48 个月)。

值得记住的 bundle 字段:

字段 含义
provider remote | local_cache | unavailable
available property:provider != "unavailable"
results 数据本体,键为 "<source>_<YYYY-MM>"
requested_months / requested_sources 你请求了什么
errors 每一层的失败记录——降级发生时读它
summaryweb_evidence 遗留字段。不要据此撰写内容。

广播只能写自 ev.results,绝不能写自 ev.summary

summary(以及 web_evidence)装的是另一个模型写的散文。把那段文字注入 E,意味着你观察到的行为部分是在回应第三方 LLM 对世界的表述,而不是在回应 世界本身——它污染了 B = f(P, E) 的归因,悄悄让整个研究失效。请从 ev.results 里读出结构化数字,广播由你自己撰写。

降级链

客户端逐层降级,且永不抛异常:

  1. remote——事件服务,前提是配了 SV_EVENT_API_URL/healthz 正常;
  2. local_cache——SV_EVENT_LOCAL_CACHE,目录布局 <source>/<YYYY-MM>.json,离线用;
  3. unavailable——此时就是第三层:手工网络检索、手写广播,并把每一个 数字都记进 grounding 侧车via="web_search",附 url 与访问日期。

没有事件服务的研究同样是有锚定的——台账只是如实记录更朴素的依据。这些事实怎么 记录,见真实世界锚定与溯源

user_pool_survey_mcp——给 P 的真实平台 persona

user_pool 如何锚定 P:真实平台人口池 → 按目标分布采样 → 并行 LLM 角色扮演作答,以 MCP 服务化

kind mcp
stages sv-build-populationsv-report
provides 按目标边缘分布采样的真实 X/Twitter 与小红书 persona,外加基于它们的 LLM 问卷作答
confirm true——每次调用都花掉协作者的 LLM 预算,并把问卷发出去
配置 SV_USER_POOL_MCP_URL + SV_USER_POOL_MCP_KEY(bearer 鉴权)→ .mcp.json

P 应当锚定在真实平台用户上(而不是合成出来的),或者研究想在纵向面板旁边 再要一份横截面问卷基线时,用它。

仅限构建时,通过会话内的 MCP 工具:

  1. 先用 get_user_pool_simulation_capabilities 做健康检查 (索引状态用 get_user_pool_index_status)。
  2. run_user_pool_distribution_simulation(platform_id, distributions, user_count, questionnaire, seed, max_workers)——max_workers ≤ 128。 platform id:x_mode_1(X/Twitter)、rednote_v2(小红书)。

allow_virtual_users 默认为 true

如果某个目标分布格子没有匹配到真实用户,服务会静默地用 VIRTUAL_* persona 补上。需要真实用户就传 allow_virtual_users=false,并检查返回的 sample 有没有缺口——覆盖不到的格子是关于你人群的一项发现,不是要糊过去的 麻烦。

物化返回结果:

  • 把返回的 output_jsonl 写进 population/ 产物(personas + roster),P 侧接缝用 provider_ref="mcp.socioverse_pool";或写进报告的基线表;
  • 每个承重分布记一条 grounding 事实via="provider",source = 平台 + 查询;
  • 把返回的 llm_model 留在溯源里——答案是依赖模型的。

成本随 user_count 线性增长

一次分布模拟会为每个被模拟的受访者跑一次 LLM 问卷作答。成本大致是 user_count × 单人费率,在托管服务上计入你的 sim-plane 配额,见 配额与计费。因为 confirm: true,调用前要 说清预计开销(user_count 是多少、会扣余额),拿到明确同意再调。

降级: 按「先锚定再发明」的规则合成 persona(检索到的锚点,或明确声明的 假设),或加载用户上传的文件(provider_ref="file.personas"),并在溯源里注明 persona 池当时不可用。

延伸阅读