Rust Agent Runtime
src-tauri/src/agent/runtime.rs 是项目中最大的生产源文件之一。它把一次对话拆成可观察、可取消的执行过程:
识别显式网络、原文、图谱、写入或简单寒暄。
让模型决定是否需要工具,避免仅凭问号或句长强制检索。
最多 8 轮,观察结果进入下一轮上下文。
组装引用、工具事件、生成物与最终文本。
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:知识入口
- Manifest V3 扩展通过 activeTab 临时权限注入 Readability 与 Turndown。
- Readability 去掉广告、导航和侧栏;Turndown 转成 ATX 标题、围栏代码和表格 Markdown。
- 内容限制在 100 万字符,POST 不自动重试,避免响应丢失时重复保存。
- 扩展从 19827 获取项目列表,优先选择用户指定、当前或第一个项目。
- 服务保存来源文件并放入 pending clips;前端轮询后复用正常摄入流水线。
19827 与 19828 共享 API Token,优于完全无鉴权,但也让浏览器入口与完整 API 共享泄漏半径。更稳妥的做法是为剪藏签发仅能“创建待处理来源”的独立能力令牌。
生态设计的意义
薄适配能减少行为分叉,但每新增一个入口都增加身份、授权、速率、数据泄漏和版本兼容问题。核心能力应统一,安全上下文必须随入口显式传递,不能靠“同一台机器”推断。
src-tauri/src/agent/*、api_server.rs、clip_server.rs、mcp-server/src/*、extension/*。威胁模型和能力令牌方案是本次审计提出的通用改进,不代表项目已实现。