项目:离线工具代理骨架
项目目标
不安装 LangChain、不调用模型、不使用密钥,亲手验证模型—工具循环中不随框架版本变化的责任:模型提出结构化调用,执行器严格校验,工具只在白名单内运行,观察通过 call ID 返回,运行时限制步数、工具数和重复调用,最终输出保留可审计轨迹。
项目不是“自己重写 LangChain”。它提供一个确定性对照组:迁移到 create_agent 或 LangGraph 后,若权限、错误、预算和测试边界消失,说明框架接入破坏了工程合同。
文件结构
00-初学者路线/examples/
├── offline_agent_loop.py # 无依赖的模型桩、两个只读工具和有界循环
├── lcel_no_key.py # 需要 langchain-core、但不需要模型密钥的 LCEL 示例
└── test_offline_agent_loop.py # 63 项离线回归测试循环的数据流
user message
↓
deterministic model stub
├─ final ───────────────→ done
└─ tool_call
↓ exact schema + call ID
allowlist executor
↓ argument validation
calculator / local policy lookup
↓ ID-bound tool message
model stub ───────────→ final模型响应只允许 final 或 tool_call 两种精确形状。执行器不信任工具名和参数;工具消息必须带回相同 tool_call_id。默认 CLI 结果不输出原始 messages,避免把输入全文当作普通观测日志。
在 PowerShell 7 运行
以下每个代码块都从同时包含 docs/ 与 .website/ 的项目根目录运行;它们不依赖上一个代码块修改当前工作目录:
python -B .\docs\LangChain\00-初学者路线\examples\offline_agent_loop.py '计算 12 * (3 + 2)' # 触发受限计算器工具,验证一次正常 tool call。
python -B .\docs\LangChain\00-初学者路线\examples\offline_agent_loop.py '查询政策 privacy' # 触发只读本地政策检索,并检查返回的来源 ID。
python -B .\docs\LangChain\00-初学者路线\examples\offline_agent_loop.py '解释什么是工具调用' # 验证无需工具时走直接回答分支。
python -B .\docs\LangChain\00-初学者路线\examples\offline_agent_loop.py --self-test # 运行脚本内置的确定性自检,不访问网络或模型。预期分别出现:一次 calculator 调用与结果 60;带 offline-policy:privacy:v1 来源的本地政策;不调用工具的直接回答;5 项显式自检通过。脚本不写文件、不联网。
安全计算器验证
python -B .\docs\LangChain\00-初学者路线\examples\offline_agent_loop.py "计算 __import__('os').getcwd()" # 验证 AST allowlist 会拒绝函数调用,而不是执行系统访问。
python -B .\docs\LangChain\00-初学者路线\examples\offline_agent_loop.py '计算 2 ** 1000000' # 验证资源风险极高的幂运算会被结构化拒绝。计算器使用 Python AST allowlist,只接受有限数、加减乘除、整除、取模和一元正负。函数调用、属性、变量、幂运算、过长/过深表达式、除零、非有限数和过大结果都会变成结构化工具错误。它没有使用 eval。
LCEL 无密钥示例
lcel_no_key.py 演示 RunnableLambda | RunnableParallel。它不需要模型密钥,但需要项目实际安装 langchain-core。以下版本是 2026-07-19 的已验证快照,不是永久推荐值;新项目应重新查 PyPI、变更记录和 Python 约束。
# 一次性隔离运行,不在 vault 内创建 .venv
uv run --isolated --with 'langchain-core==1.4.9' python -B .\docs\LangChain\00-初学者路线\examples\lcel_no_key.py ' hello agent ' # 临时安装锁定 core 版本并运行无密钥 LCEL 示例。若依赖不存在,脚本以退出码 4 和 dependency_missing 明确结束;这表示该分支被跳过,不能声称 LCEL 已运行。2026-07-22 已用 Python 3.11.9 与隔离的 langchain-core==1.4.9 实际运行,输出规范化文本 hello agent、字符数 11、词数 2。若改用持久 venv,必须在 vault 外的练习项目中创建,固定同一依赖并运行 python -m pip check;团队项目还应保存锁文件,在升级后重跑此示例和相邻测试。
对照 LangChain 概念
| 离线骨架 | LangChain / LangGraph 对应 | 仍由应用负责 |
|---|---|---|
model_stub | chat model response | 模型选择、版本和质量评测 |
validate_model_response | 消息/工具调用 schema | 业务枚举、事实和来源校验 |
TOOLS / ToolSpec | @tool 与工具注册 | 对象级授权、网络和副作用 |
execute_tool | 工具执行器 / middleware | 超时、幂等、凭据和隔离 |
tool_call_id | AI tool call ↔ ToolMessage | 关联、审计和迟到结果 |
max_model_steps / max_tool_calls | recursion/运行预算 | 成本、deadline 和人工出口 |
trace | LangSmith 或自建观测 | 脱敏、保留和业务 outcome |
运行 63 项测试
$examples = '.\docs\LangChain\00-初学者路线\examples' # 集中保存测试发现目录,避免重复书写长路径。
python -B -m unittest discover -s $examples -p 'test_*.py' -v # 在普通模式发现并运行全部相邻测试。
python -B -O -m unittest discover -s $examples -p 'test_*.py' -v # 验证生产校验不依赖裸 assert。
python -B -W error -m unittest discover -s $examples -p 'test_*.py' -v # 把 warning 提升为失败,及早发现兼容性问题。
python -B -O -W error -m unittest discover -s $examples -p 'test_*.py' -v # 组合两种严格运行条件完成回归验证。测试覆盖:AST 资源限制、工具与参数 allowlist、消息精确形状、call ID 绑定、工具错误、直接回答、两种工具、步数和工具预算、重复调用停止、原始消息不进入默认 CLI 输出,以及 LCEL 依赖存在/缺失两种合法状态。-O 模式证明测试不依赖会被移除的裸 assert。基础环境中的 63 项绿灯可能包含 dependency_missing,因此必须把“核心离线循环通过”和“LCEL 在锁定依赖下通过”分开报告;后者已按上节命令独立验证。
综合扩展任务
增加一个只读 lookup_order 工具,但先写合同:
order_id的语法、长度和当前调用者授权如何验证?- 哪些字段返回模型,哪些只作为 artifact 留在应用?
- 未找到、无权、暂时错误如何分类?
- 单次结果和总轨迹的大小限制是什么?
- 如何防止模型用另一个用户的订单 ID 越权查询?
- 新增正例、无权、非法 ID、未知工具、重复调用和预算耗尽测试。
如果把它改成写工具,必须另增预览、审批、幂等键、结果未知查询和完成验证;不能只把函数名从 lookup 改成 update。
项目验收
- 三类示例路径产生预期工具数和终态。
- 危险计算不会执行任意 Python。
- 工具结果与原调用 ID 严格对应。
- 未知工具、错误参数、重复调用与预算耗尽都失败关闭。
- 63 项测试在 normal、
-O、-W error与-O -W error四种模式通过。 - LCEL 示例在锁定的
langchain-core环境中真实运行,而不是仅返回dependency_missing。 - 能说明离线骨架证明了什么,以及没有证明真实 LangChain API、模型质量或生产持久化。
下一步
先进入 Layer B:无密钥 create_agent 运行时合同,把框架无关工具循环映射到真实 LangChain harness;再进入 Layer C:LangGraph 可恢复审批流,把控制流映射到 StateGraph、SQLite checkpointer 与 interrupt。
资料基线
LangChain 概念文档核对日期:2026-07-14;langchain-core 包版本与 LCEL 实跑复核日期:2026-07-19。