Skip to content

Repository files navigation

local-llm-agent

本地大模型(llama.cpp + Qwen-7B)工具调用 Agent 的基础设施:在按会话隔离的 workspace 沙箱之上,把 Agent 做成可服务化、可观测、可恢复、可审计的工程系统。

English (condensed) | 中文完整版(当前)

本仓库的全部数值都来自随仓库提供的原始日志、CSV 与 JSONL 产物;无法证明或未执行的部分单列在「已知限制」一节。

解决什么问题

直接用 open(path) 读写文件的 Agent 有三个真实问题,本仓库逐项给出了实现与实测:

  1. 没有文件边界:工具能读 .env、项目源码、系统文件,也能往任意路径写。 → 白名单解析(Path.resolve() + is_relative_to)+ 会话级沙箱根。
  2. 多会话互相污染:同一个进程里不同用户的对话历史与文件会串。 → 会话存储(内存字典 + SQLite)+ thread_id = session_id + 独立会话目录。
  3. 小模型工具调用不可靠:格式错乱、"假装执行"、参数缺失。 → GBNF 约束解码把动作锁进枚举,格式非法输出无法生成;语义安全交给工具白名单兜底。

关键实测结果

能力 实测
路径白名单沙箱 同一批 8 条越权路径:改造前允许 7/8 → 改造后允许 1/8(唯一允许项为正向对照)
多会话隔离 5/5 通过(跨会话不串历史、不串文件;重启后按 session_id 恢复 thread_id
安全网关 14/14 通过(越权拒绝、跨会话绝对路径拒绝、搜索超时 5s + 重试 2 次 + 兜底 JSON 降级)
真实 GBNF 工具调用 llama-server 真实联调 29/30 = 96.67% 综合合法率;校验器精确率/召回率 100%
全链路可观测 369 事件 / 85 trace / 25 会话;总体 P95 28.93 ms
检查点中断恢复 两个独立进程:PHASE-1 中断退出(pid 62548)→ PHASE-2 从 SQLite 恢复完成(pid 40980)
单元/集成测试 pytest -q agent/tests2 passed(仓库自包含,克隆后可直接跑)

架构

flowchart TB
    subgraph C["调用方"]
        CLI["CLI: agent_core/run.py"]
        HC["HTTP 客户端"]
    end
    subgraph I["Agent Infra (agent/)"]
        MW["TraceSessionMiddleware (纯 ASGI)<br/>trace_id / session_id / X-Trace-ID"]
        API["FastAPI<br/>/v1/agent/run | /v1/session/{id} | /healthz"]
        SS["SessionStore<br/>内存字典 + SQLite"]
        G["LangGraph 主图 + SqliteSaver"]
        GW["安全网关 call_tool<br/>白名单校验 + 审计日志 + 降级"]
        TR["Trace JSONL"]
    end
    subgraph K["基础 Agent (agent_core/)"]
        SP["safe_path.py<br/>Path.resolve + is_relative_to"]
        TL["tools.py<br/>read_file / write_file / search_web"]
    end
    LLM["llama-server (GBNF 约束解码)"]
    WS["会话文件沙箱<br/>workspace/sessions/{session_id}/"]
    HC --> MW --> API --> G
    API --> SS
    G --> GW --> SP --> WS
    GW --> TL
    G -.-> LLM
    MW --> TR
    G --> TR
Loading

快速开始

前置:Python 3.9+;可选一个已编译的 llama.cpp(用于真实模型;没有则走确定性 mock)。

pip install -r agent/requirements.txt

# 配置:复制模板(本地开发用 127.0.0.1,容器内由 Dockerfile 的 ENV 提供)
copy agent_core\.env.example agent_core\.env

# 启动服务(默认 mock 模式也能跑通全部编排/隔离/日志/恢复链路)
cd agent
uvicorn app.main:app --host 127.0.0.1 --port 8000

调用:

curl -X POST http://127.0.0.1:8000/v1/agent/run `
     -H "Content-Type: application/json" -H "X-Session-ID: demo-1" `
     -d "{\"input\": \"把\\\"hello\\\"写入文件 a.txt\"}"

接真实模型时,先把 llama-server 起在本机 8080(示例:RTX 4060 8GB,显存占用约 6.3 GB):

llama-server.exe -m <model>.gguf --host 127.0.0.1 --port 8080 `
  -ngl 99 -c 4096 -np 1 -ctk q8_0 -ctv q8_0 -fa on --metrics

AGENT_LLM_MODE 取值 auto(默认,探测到服务就用真实模型)/ llama / mock

目录结构

├── README.md / README.en.md      本文件与英文精简版
├── README_all.md                 实现说明(架构、核心实现、实测数据、接口、复现命令)
├── audit_before.md               改造前文件访问边界审计(含真实越权证据)
├── agent/                        Agent Infra 主体
│   ├── app/                      config / tracing / sessions / security / llm / graph / main
│   ├── tests/                    会话隔离、安全网关、GBNF 合法率(含 30 条用例)
│   ├── scripts/                  gen_traffic / analyze_trace / run_resume_demo / gen_grammar
│   ├── grammar/                  手写 grammar + llama.cpp 官方转换器生成的 GBNF + JSON Schema
│   ├── logs/                     实测 trace 与安全审计日志(样本)
│   └── README_agent.md           模块说明、接口、已知边界
├── agent_core/                   基础 Agent(被 app 引用的运行时依赖)
├── audit/                        前后对比探针与真实对比数据
├── backup/                       改造前原始实现(仅作对照证据,内含警示,请勿用于生产)
├── docker/                       Dockerfile(非 root、只挂载 workspace)+ 安装指引
├── figures/                      图表与生成/校验脚本、清单
├── llama_server/                 启动/就绪/联调/关闭日志
├── shared/                       测试与验证记录
└── workspace/                    workspace 隔离设计决策

安全模型

  • 白名单根AGENT_WORKSPACE_ROOT/sessions/{session_id},工具只能在此子树内读写;
  • 判定方式Path.resolve() 解析 .. 与符号链接后,is_relative_to(会话根) 校验;
  • 可审计原因码PROJECT_SOURCE_DENIEDENV_CONFIG_FILEWINDOWS_SYSTEM_DIRSYSTEM_ACCOUNT_FILEOUT_OF_WORKSPACEDRIVE_RELATIVE_PATHNOT_FOUNDCONTROL_CHAR
  • 审计日志:每次工具调用(放行与拒绝)都写 agent/logs/security_gateway.log(JSON Lines);
  • 失败可区分:路径拒绝返回原因码,而不是依赖解码失败等偶然因素。

部署注意:本服务默认只监听 127.0.0.1没有鉴权,请勿直接暴露到公网; llama-server 自身也会打印 CORS is set to allow all origins 警告。若要在内网使用,请自行在前置代理上增加认证与来源限制。

已实现能力

  • 服务化POST /v1/agent/runGET|DELETE /v1/session/{id}GET /healthz
  • 多会话隔离X-Session-ID 头 > session_id Cookie > 自动生成 uuid4 并 Set-Cookie
  • 持久化:会话元数据/历史/工具调用记录入 SQLite,重启后按 session_id 恢复 thread_id
  • 全链路 trace:JSONL 事件(trace_id, session_id, thread_id, node_name, input, output, start_time, end_time, elapsed_ms, error)+ P95/错误率统计脚本;
  • 中断恢复:LangGraph interrupt() + SqliteSaver,两阶段两进程可复现;
  • GBNF 工具调用:grammar 由 llama.cpp 官方 json_schema_to_grammar.py 生成,配 30 条用例的合法率测试与自实现子集匹配器。

图表

节点耗时 节点错误率
latency error
越权路径改造前后 GBNF 合法率
security gbnf

session isolation

图表全部由 figures/make_figures.py 从仓库内真实 CSV/JSONL 生成,清单见 figures/manifest.md

文档索引

文档 内容
README_all.md 实现说明:架构、核心实现(沙箱/隔离/网关/日志/恢复/GBNF)、实测数据、接口、复现命令
agent/README_agent.md Agent Infra 模块说明、接口、测试与已知边界
workspace/workspace_decision.md workspace 隔离设计决策(直接读写项目根 → 收敛白名单)
audit_before.md 改造前文件访问边界审计(含真实越权证据)
shared/validation.md 测试与验证结果
docker/DOCKER_SETUP_GUIDE.md Docker 安装指引(Docker Desktop 或 WSL2 + docker-ce 两条路径)

已知限制

  • Docker 沙箱未实测:开发机未安装 Docker 且未启用 WSL,容器内验证待环境具备(Dockerfile 与指引已随仓库提供,构建上下文已就绪)。等价的本机路径白名单与会话隔离已实测。
  • 真实模型合法率为 96.67%:唯一失败用例是一次 max_tokens 截断(模型重复生成导致 JSON 未闭合),不是 GBNF 失效。
  • mock 模式的合法率不代表模型能力:mock 用例集按设计含 11 条非法输出,该模式用于验证校验链路(校验器精确率/召回率 100%);真实模型指标以 llama-server 联调为准。
  • agent/logs/ 下的 trace 与审计日志是实测样本,重新运行会追加内容。

第三方与许可

本仓库不重分发任何第三方源码,只把它们作为运行时依赖:

本项目以 MIT 许可发布。

About

Local LLM tool-calling agent infrastructure: session-isolated workspace sandbox, path whitelist, FastAPI service, tracing, checkpoint resume and GBNF-constrained tool calls

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages