LLM 接入与干跑¶
在 SocioVerse 里,LLM 只坐在一个位置:决策模型。
decide_batch(observations, memories) 是计算 B = f(P, E) 的地方,
这个计算是规则、统计模型还是 LLM 调用,由研究自己选择。其余一切——
环境、人群、存储、报告——两种方式下完全相同地运行。
配置¶
LLM 驱动的决策模型从环境变量或 gitignore 的 .env 读取设置(查找顺序见
安装)。
真实 LLM 运行需要 llm extra(pip install -e ".[llm]"),它带来 OpenAI 兼容客户端。
| 变量 | 含义 |
|---|---|
SV_LLM_API_KEY |
模拟中 agent 调用所用的 key(OPENAI_API_KEY 作为备选) |
SV_LLM_BASE_URL |
可选的端点覆写——任何 OpenAI 兼容网关(未设置时为 OpenAI 官方 API) |
SV_LLM_MODEL |
可选;由 scripts/demo_offline.py、芝加哥适配器以及 simulation.json 未指定模型的研究读取。随仓库提供的研究固定了 decision_args.model |
这些变量只配置模拟用的 LLM。运行 /sv-* skill 的编程智能体(Claude Code,或实验性的
Codex CLI 路径)用它自己的登录凭据——见
工作流 skill 由什么驱动。
调用方式¶
引擎把整步——每个 agent 的观察——一次交给 decide_batch,并期望为每个 agent
拿回一条 Action。「batch」描述的是这个接口,而不是 LLM 怎么被调用。一个行为良好的
LLM 决策模型会:
- 把每个观察的四象限视图渲染进 prompt
(
Observation.rendered是放置备好文本的惯例位置); - 以 agent 的
AgentMemory(它自己过往行为的滚动窗口)为条件—— 「我已经搬过两次家了,这次留下」; - 在从零构建的研究里,为每个 agent 各发一次 LLM 调用,把该步的调用放到有上限的
线程池里并发执行(
decision_args里的max_workers),并逐个 agent 解析或回退到规则 (见studies/opinion_diffusion/model.py)。绝不让模型用一条 JSON 回复覆盖全部人群: 长的结构化回复会被截断,一条坏回复就会让整步失败; - 在遗留适配器里,保留被包装引擎自己的调用方式(例如芝加哥模型按 archetype 分组的 调用),让它经过验证的动力学保持不变;
- 返回类型化的
Action,让apply()能折回 E。
确定性干跑¶
你应当能在不花一个 token 的情况下验证全部管线——循环、产物、存储、指标。
通用做法:llm_kind: "scripted"。 每个从零构建的模板都只注册一个决策模型,
带两种模式,在 simulation/simulation.json 里切换,不用改代码:
"scripted" 运行研究自己的确定性规则(免费,由种子完全复现);"openai" 通过
SV_LLM_* 通道让每个 agent 角色扮演,回复无法解析时只对该 agent 回退(opinion_diffusion
回退到规则;campus_dining_choice 维持该 agent 上一步的选择)。scripts/demo_offline.py
就是这样跑 opinion_diffusion 的,/sv-run 也把 scripted 模式作为免费干跑提供。
/sv-build-model 新建的研究沿用同一模式,所以规则始终留在那里当 fallback。
规则式决策模型什么都不需要:决策是观察的纯函数。
包装遗留引擎:替身客户端。 包装遗留模拟器的研究沿用那个引擎自己的 LLM 客户端,
所以它通过注入一个确定性替身来干跑。随仓库提供的实例是 chicago_schelling 的
DeterministicLLMClient(studies/chicago_schelling/adapter/fake_llm.py),以
llm_client=DeterministicLLMClient() 传给 build_chicago_simulator。它只存在于这个
适配器里,不是给其他研究用的通用客户端。一致性测试(parity test)用的就是它:被包装的
研究断言自己在同一种子下复现原版轨迹,替身消除了唯一的非确定性来源。
关于可复现性,诚实地说¶
真实 LLM 运行不可复现
真实 LLM 调用在非零温度下采样:同一研究连跑两次结果会不同。 可复现的对象是:(a) 干跑——端到端由种子决定;(b) 任何已记录的 运行——它的面板存储是永久的,而且分支重放 会精确重放已存储的行为,而不是重新询问 LLM。
成本控制¶
/sv-run在花钱之前展示运行配置并要求确认——花费闸门。- 迭代产物时优先用干跑;每个有意义的配置只上线跑一次。
- 分支重放继承父版本分叉步 之前的所有步数(零 LLM),一个反事实只为分叉步之后的部分花钱。