05 / Agent Security

工具越强
信任边界越窄

规划循环、路径校验和审批是必要基础,但文档、网页、Skill 与工具输出全部属于不可信输入。真正的安全边界必须在模型之外强制执行。

案例循环Bounded · Observable · Cancellable
本地服务19828 API · 19827 Clipper
外部协议JSON · SSE · MCP
安全原则Least Privilege · Server-side Enforcement

Rust Agent Runtime

src-tauri/src/agent/runtime.rs 是项目中最大的生产源文件之一。它把一次对话拆成可观察、可取消的执行过程:

01 / route意图提示

识别显式网络、原文、图谱、写入或简单寒暄。

02 / plan模型规划

让模型决定是否需要工具,避免仅凭问号或句长强制检索。

03 / loop执行工具

最多 8 轮,观察结果进入下一轮上下文。

04 / answer生成答复

组装引用、工具事件、生成物与最终文本。

Router 故意保守:它只给明显意图打标签,不因“像一个问题”就自动搜索 Wiki。真正的工具选择交给模型 Planner; 只有 Planner 不可用时才启用运行时兜底。这修复了诸如“你有哪些 Skill?”被误送入知识检索的问题。

工具目录

工具作用效果类型
wiki.search / read_page混合搜索 Wiki、读取指定 Markdown 页Read
source.search从 raw/sources 和有效解析缓存中找原文证据Read
graph.search查询邻居、反向链接、依赖和实体关系Read
web.search / anytxt.search搜索外部网络或 AnyTXT 本地索引Network + Read
deep_research.run组合 web、AnyTXT、Wiki、Source 证据完成研究Network + Read
wiki.write_page在 wiki/ 下创建页面;覆盖必须显式允许Write
workspace.write / append在 agent-workspace/ 生成 HTML、Markdown 等产物Write
skill.read_file / shell.exec读取 Skill 参考,执行被允许的项目命令Read / Process

工具返回的页面、来源与网络结果都被规范成 Reference,最终可在聊天消息中显示和持久化,而不是只留下不可追踪的模型文本。Reference 提高可解释性,但不会让内容自动可信;工具输出仍可能包含错误事实或针对模型的恶意指令。

上下文不是无限堆叠

Agent context 会组合 Purpose、Index、显式上下文文件、历史消息、Skill 指令、工具观察和引用。系统对各部分进行预算控制与截断,避免一个巨大来源挤掉用户问题。

  • 聊天历史深度可配置,消息持久化但不必全部送入模型。
  • 图片从消息和来源引用中单独解析,支持多模态模型。
  • 工具观察只保留后续决策需要的信息,避免每轮指数增长。
  • 最终回答与工具规划可使用不同的结构化模型配置和 Token 上限。
  • Reasoning 内容与最终答复分离,前端流式展示并在完成后折叠。

Agent Skills

系统扫描项目级和用户级 SKILL.md,在聊天输入中通过 /skill 选择。Skill 主文件可以引用自身目录下的参考文件, Agent 再通过 skill.read_file 按需加载,不必把整个技能包一次塞进上下文。

Auto 模式

把可用技能作为能力提示,由模型在需要时选择。

Explicit 模式

用户明确选择 Skill,运行时为其提供更大的工具迭代预算。

参考文件

限制在 Skill 根目录内,拒绝路径穿越和符号链接逃逸。

结构化交互

Skill 可请求单选、多选或自由文本,界面无需为每种技能硬编码。

写入与 Shell 安全

项目没有把“Agent 有工具”误解成“Agent 可以任意执行”。能力、路径和审批三层是正确基础,但仍需把 Prompt 注入、工具输出污染、子进程隔离和凭证范围纳入同一威胁模型。

  • 能力策略:ReadProject、SearchWiki、WriteWiki、Network、Process 等明确枚举。
  • Wiki 边界:写入只能位于项目 wiki/;现有文件需要 allowOverwrite=true
  • Workspace 边界:生成物只能放到公开可见的 agent-workspace/
  • 规范化与 canonicalize:拒绝 ../、绝对路径、隐藏路径和符号链接逃逸。
  • Shell 审批:Process 能力本身不等于许可;请求必须匹配用户单独批准的精确命令。
  • 取消:每轮规划、工具执行、网络生成和事件流都检查 cancellation token。
关键原则

审批列表不能由模型输出或历史对话自动填充;文档里的“忽略规则”“调用 Shell”等文本也只能作为数据。授权必须由服务端策略与真实用户动作产生,并绑定具体工具、参数、作用域和有效期。

本地 HTTP API

端口 19828 的 /api/v1 把桌面能力开放给其他本地程序:

GET /projects列出已知项目与当前项目。
GET /projects/:id/files受限列出 wiki / sources 公共文件。
POST /projects/:id/search调用同一关键词、向量、图扩展检索。
GET /projects/:id/graph返回知识图谱节点与边。
POST /projects/:id/chat运行 Agent;支持 JSON 或 SSE 流式协议。
POST .../sources/rescan触发 Source Watch 重新扫描。
POST .../pages/embed为指定 Wiki 页重建向量。

服务带开关、Token、CORS、本地/LAN 绑定设置、每秒 120 次限流、64 个在途请求、8 个聊天流和 4 个页面向量任务上限。Chat 与 Embedding 写操作始终要求 Token。这些是已验证保护,但“监听回环”本身不构成身份认证。

把本地 Agent 当成真实服务审计

攻击面案例观察生产级约束
不可信内容Wiki、原始文件、网页和 Skill 都会进入模型上下文。内容与系统指令分层;工具参数由策略校验;高影响操作不因内容中的文字获得授权。
本地 HTTP支持 Token、CORS、限流与可选 LAN;代码还兼容从 query 读取 Token。禁止 URL Token,避免进入历史、日志和 Referer;严格 Origin/Host 校验,默认只绑定回环。
共享令牌API 与 Clipper 共享 Token,便于配置但扩大泄漏后的能力范围。按客户端、项目和能力签发短期令牌,可独立撤销,写操作使用更窄权限。
Shell / 进程有精确命令审批与路径限制,但子进程仍继承宿主环境。最小环境变量、资源上限、隔离工作目录、网络策略、输出上限和完整审计记录。
持久化污染Agent 可写 Wiki 与 workspace,错误可能进入后续检索和新一轮上下文。写入进入候选区,保留来源与 actor;高风险变更经 diff 审核后发布,可一键撤回。
安全不依赖模型听话

System Prompt、忠实模式和“不要执行恶意指令”只能降低概率,不能承担访问控制。可授权主体、能力令牌、服务端参数校验、隔离执行和审计日志才是安全机制。

MCP Server:薄适配,不复制核心

mcp-server/ 是独立 TypeScript 包,通过 stdio 暴露 10 个左右工具:状态、项目绑定、文件、读取、审核、搜索、聊天、图谱、重扫、向量化。 它不重新实现检索或 Agent,而是调用 19828 API。

MCP 进程可以“锁定”一个项目。锁定后其他工具不能偷偷切到另一个项目,除非显式调用 set_project;这是多项目桌面应用接入 AI Agent 时很重要的作用域保护。

Chrome Clipper:知识入口

  1. Manifest V3 扩展通过 activeTab 临时权限注入 Readability 与 Turndown。
  2. Readability 去掉广告、导航和侧栏;Turndown 转成 ATX 标题、围栏代码和表格 Markdown。
  3. 内容限制在 100 万字符,POST 不自动重试,避免响应丢失时重复保存。
  4. 扩展从 19827 获取项目列表,优先选择用户指定、当前或第一个项目。
  5. 服务保存来源文件并放入 pending clips;前端轮询后复用正常摄入流水线。

19827 与 19828 共享 API Token,优于完全无鉴权,但也让浏览器入口与完整 API 共享泄漏半径。更稳妥的做法是为剪藏签发仅能“创建待处理来源”的独立能力令牌。

生态设计的意义

一份核心,多种入口

薄适配能减少行为分叉,但每新增一个入口都增加身份、授权、速率、数据泄漏和版本兼容问题。核心能力应统一,安全上下文必须随入口显式传递,不能靠“同一台机器”推断。

Next Chapter06 · 生产化门槛与建设顺序
案例证据:src-tauri/src/agent/*api_server.rsclip_server.rsmcp-server/src/*extension/*。威胁模型和能力令牌方案是本次审计提出的通用改进,不代表项目已实现。