按原因、后果、解决思路、落地方案和回归测试复盘 10 类后端问题。 同步更新接口实现状态、Plugin 权限接口、真实 Streaming、Citation 偏移约定、第一阶段 Tool 列表和测试数量。
332 lines
9.4 KiB
Markdown
332 lines
9.4 KiB
Markdown
# AI Core 与 Agent Core 开发说明
|
||
|
||
> 本文档用于团队开发和模块联调,记录当前已经落地的核心边界与使用方式。
|
||
|
||
## 当前实现
|
||
|
||
当前已经建立第一条可运行链路:
|
||
|
||
```text
|
||
FastAPI
|
||
→ Provider Registry
|
||
→ Agent Runtime
|
||
→ Permission Manager
|
||
→ Tool Registry
|
||
→ Skill Runtime / Plugin Runtime
|
||
→ Agent Trace / SSE
|
||
```
|
||
|
||
对应代码:
|
||
|
||
```text
|
||
backend/app/
|
||
├── providers/
|
||
│ ├── base.py Provider Protocol 与统一 Turn
|
||
│ ├── registry.py Provider 注册、发现、模型列表和连接测试
|
||
│ └── mock.py 离线开发 Provider
|
||
├── agent/
|
||
│ ├── runtime.py Agent Loop、限制、取消、Trace 和 SSE
|
||
│ ├── tools.py Tool 注册、参数校验、隔离执行和结果转换
|
||
│ ├── permissions.py 权限策略、确认请求和会话授权
|
||
│ └── builtin_tools.py 无副作用的内置开发 Tool
|
||
├── extensions/
|
||
│ └── runtime.py Skill/Plugin Manifest、生命周期、依赖与 Tool Contribution
|
||
└── container.py AI Core 依赖组装
|
||
|
||
backend/extensions/
|
||
├── skills/knowledge-assistant/ 内置知识库 Skill
|
||
└── plugins/text-tools/ 内置示例 Plugin
|
||
```
|
||
|
||
Router 只负责 HTTP/SSE 与错误转换,不实现 Agent、Tool 或 Provider 业务逻辑。
|
||
|
||
## 模块边界
|
||
|
||
当前实现属于范涵宇负责的 AI Core / Agent Core:
|
||
|
||
- Provider 抽象与注册;
|
||
- Agent Run 生命周期;
|
||
- Tool Registry;
|
||
- Tool 参数校验与执行隔离;
|
||
- Permission;
|
||
- Step、Timeout、Token Budget、取消;
|
||
- Tool 并发上限与 run 级网络权限;
|
||
- 内存 Trace 与 SSE;
|
||
- Skill Manifest、Prompt、Tool/Permission/模型能力解析;
|
||
- Plugin Manifest、生命周期和 Tool Contribution;
|
||
- Skill 调用内置 Tool 与 Plugin Tool;
|
||
- 公共 Contract 和 API 接入。
|
||
|
||
以下内容保持接口,不在本模块实现:
|
||
|
||
- Note、NoteBlock、Markdown Parser:由 Knowledge Core 提供;
|
||
- FTS5、Vector、RRF、Reranker、Citation:由 Retrieval Core 提供;
|
||
- 文件系统和 API Key 明文读取:由 Rust Host 提供;
|
||
- MCP Plugin Host、Frontend Extension Slot:按技术基线放在第二阶段实现。
|
||
|
||
## Provider
|
||
|
||
默认注册离线 Provider:
|
||
|
||
```text
|
||
provider_id = mock
|
||
model = mock-1
|
||
```
|
||
|
||
它支持:
|
||
|
||
```text
|
||
chat
|
||
tool_calling
|
||
streaming
|
||
```
|
||
|
||
普通 Chat 请求:
|
||
|
||
```json
|
||
{
|
||
"provider_id": "mock",
|
||
"model": "mock-1",
|
||
"messages": [
|
||
{"role": "user", "content": "hello"}
|
||
]
|
||
}
|
||
```
|
||
|
||
`POST /api/chat` 返回 ModelEvent SSE。OpenAI-Compatible Adapter 直接消费上游 SSE,Ollama Adapter 直接消费 JSONL,`TextDelta` 是真实增量内容,不再等待整段回答完成。
|
||
|
||
另外已经实现以下可配置 Adapter:
|
||
|
||
```text
|
||
openai_chat / openai_compatible
|
||
ollama
|
||
```
|
||
|
||
创建 Ollama Provider:
|
||
|
||
```json
|
||
{
|
||
"provider_type": "ollama",
|
||
"name": "Local Ollama",
|
||
"base_url": "http://127.0.0.1:11434",
|
||
"default_model": "qwen3:latest"
|
||
}
|
||
```
|
||
|
||
创建 OpenAI-Compatible Provider:
|
||
|
||
```json
|
||
{
|
||
"provider_type": "openai_compatible",
|
||
"name": "OpenAI Compatible",
|
||
"base_url": "https://api.openai.com/v1",
|
||
"default_model": "<model>",
|
||
"credential_id": "openai-main"
|
||
}
|
||
```
|
||
|
||
凭证 ID `openai-main` 对应 Sidecar 进程中的临时环境变量 `AINOTE_CREDENTIAL_OPENAI_MAIN`。环境变量由 Rust Host 从 Stronghold 读取后注入,不写入 Provider Config、日志或前端 Store。
|
||
|
||
Provider 配置生命周期接口已经可用:
|
||
|
||
```text
|
||
GET /api/providers
|
||
POST /api/providers
|
||
GET /api/providers/{provider_id}
|
||
PATCH /api/providers/{provider_id}
|
||
DELETE /api/providers/{provider_id}
|
||
GET /api/providers/{provider_id}/models
|
||
POST /api/providers/test
|
||
```
|
||
|
||
## Agent Run
|
||
|
||
创建普通 Agent Run:
|
||
|
||
```json
|
||
{
|
||
"input": "hello",
|
||
"provider_id": "mock",
|
||
"model": "mock-1",
|
||
"max_steps": 10
|
||
}
|
||
```
|
||
|
||
请求:
|
||
|
||
```text
|
||
POST /api/agent/runs
|
||
```
|
||
|
||
创建后通过以下接口读取状态和事件:
|
||
|
||
```text
|
||
GET /api/agent/runs/{run_id}
|
||
GET /api/agent/runs/{run_id}/events
|
||
POST /api/agent/runs/{run_id}/cancel
|
||
```
|
||
|
||
当前 Run 与 Trace 保存在内存中,AI Core 重启后清空。Runtime 最多保留 200 个 Run,每个 Run 最多保留 2000 个事件,并限制单轮 Tool Call 数量,避免长时间运行时无界增长。后续数据库层接入时替换 Repository,不改变 API Contract。
|
||
|
||
## Tool Calling
|
||
|
||
当前注册以下内置 Tool:
|
||
|
||
```text
|
||
system.echo
|
||
math.add
|
||
notes.search
|
||
rag.search
|
||
notes.read
|
||
notes.create
|
||
notes.update
|
||
notes.list
|
||
notes.move
|
||
tasks.create
|
||
tasks.update
|
||
tasks.list
|
||
attachments.read
|
||
audio.transcribe
|
||
```
|
||
|
||
Mock Provider 使用下面的开发语法产生 Tool Call:
|
||
|
||
```text
|
||
/tool system.echo {"text":"hello tool"}
|
||
/tool math.add {"left":1,"right":2}
|
||
```
|
||
|
||
Agent Run 需要显式声明 `allowed_tools`:
|
||
|
||
```json
|
||
{
|
||
"input": "/tool math.add {\"left\":1,\"right\":2}",
|
||
"provider_id": "mock",
|
||
"model": "mock-1",
|
||
"allowed_tools": ["math.add"]
|
||
}
|
||
```
|
||
|
||
Tool 参数由独立 Pydantic Model 再次校验。Tool 的异常、非法参数、超时和权限拒绝统一转换为 `ToolResult`,不会直接打断 API 进程。
|
||
|
||
`notes.search` / `rag.search` 返回的 Citation 会由 Agent Runtime 收集到 `AgentRun.citations`,并产生 `Citation` Trace Event。Note 写操作调用 `note_service`,检索调用 Retrieval Engine,不直接访问 SQLite。
|
||
|
||
## Permission
|
||
|
||
Permission Policy 当前支持:
|
||
|
||
```text
|
||
allow
|
||
confirm
|
||
deny
|
||
```
|
||
|
||
需要确认时,Agent 状态进入 `waiting_permission`,并发出 `PermissionRequired` 事件。前端使用:
|
||
|
||
```text
|
||
POST /api/agent/runs/{run_id}/permissions/{request_id}
|
||
```
|
||
|
||
提交以下决策之一:
|
||
|
||
```json
|
||
{"decision":"allow_once"}
|
||
{"decision":"allow_session"}
|
||
{"decision":"deny"}
|
||
```
|
||
|
||
默认需要确认的高影响权限包括 `notes.write`、`notes.delete`、`tasks.write`、`network.request` 和 `secrets.use`。权限命名空间采用白名单,未知权限默认拒绝。
|
||
|
||
## Knowledge / Retrieval 接入
|
||
|
||
Knowledge Core 与 Retrieval Core 已通过 Tool Registry 接入 Agent。Agent Runtime 仍只依赖 Tool Contract,不直接依赖具体服务:
|
||
|
||
```python
|
||
tool_registry.register(
|
||
definition=tool_definition,
|
||
arguments_model=arguments_model,
|
||
executor=executor,
|
||
)
|
||
```
|
||
|
||
第一批已经接入:
|
||
|
||
```text
|
||
notes.search
|
||
notes.read
|
||
notes.create
|
||
notes.update
|
||
notes.list
|
||
rag.search
|
||
```
|
||
|
||
写操作 Executor 调用 Knowledge Core Service,不直接访问 SQLite;检索 Executor 调用 Retrieval Core Service,不直接拼接 FTS5 或 sqlite-vec SQL。
|
||
|
||
## Extension Core
|
||
|
||
### Skill Runtime
|
||
|
||
Skill Package 由 `skill.yaml` 和可选 `prompt.md` 组成。安装时使用 Pydantic 校验 Manifest,并解析:
|
||
|
||
```text
|
||
permissions
|
||
tools
|
||
retrieval
|
||
model.required_capabilities
|
||
```
|
||
|
||
Skill 启用前检查 Tool 是否已注册、Tool 所需权限是否已在 Manifest 声明。创建 Agent Run 时,Skill Runtime 生成 Agent Configuration,注入 System Prompt、允许的 Tool、权限和 Retrieval Config。模型缺少 `chat`、`tool_calling` 等必要 Capability 时拒绝启动。
|
||
|
||
生命周期接口:
|
||
|
||
```text
|
||
GET /api/skills
|
||
POST /api/skills/install
|
||
GET /api/skills/{skill_id}
|
||
POST /api/skills/{skill_id}/enable
|
||
POST /api/skills/{skill_id}/disable
|
||
DELETE /api/skills/{skill_id}
|
||
```
|
||
|
||
### Plugin Runtime
|
||
|
||
第一阶段 Plugin Runtime 完成 Manifest 校验、安装、启用、停用、卸载和 Tool Contribution。第三方代码不会直接 import 到 AI Core;当前 Declarative Plugin Host 只执行宿主实现的白名单 handler,MCP Host 留到第二阶段。
|
||
|
||
启用 Plugin 时将 Tool 注册到统一 Tool Registry,并标记 `source=plugin`;停用或异常时注销 Tool。启用中的 Skill 依赖某 Plugin Tool 时,Plugin 不能直接卸载。
|
||
|
||
生命周期接口:
|
||
|
||
```text
|
||
GET /api/plugins
|
||
POST /api/plugins/install
|
||
GET /api/plugins/{plugin_id}
|
||
POST /api/plugins/{plugin_id}/enable
|
||
POST /api/plugins/{plugin_id}/disable
|
||
PUT /api/plugins/{plugin_id}/permissions
|
||
DELETE /api/plugins/{plugin_id}
|
||
```
|
||
|
||
Plugin Manifest 中的权限只是声明,不代表已经授权。带权限的 Plugin 安装后进入 `permission_required`,Host 必须通过权限接口记录用户授权,之后才能启用。JSON Schema 在安装阶段校验,Tool 调用时再次校验实际参数。
|
||
|
||
内置示例 `text-tools` 注册 `text.uppercase`。内置 `knowledge-assistant` Skill 同时声明 `notes.search` 和 `text.uppercase`,用于验证完整链路:
|
||
|
||
```text
|
||
Skill Manifest
|
||
→ Agent Configuration
|
||
→ Tool Registry
|
||
→ Plugin Tool
|
||
→ Tool Result
|
||
→ Agent Loop
|
||
```
|
||
|
||
## 当前限制与下一步
|
||
|
||
- 已实现 Mock、OpenAI-Compatible Chat Completions 与 Ollama Adapter;OpenAI Responses 和 Anthropic Messages 尚未实现。
|
||
- Provider 配置暂存内存,后续通过 Repository 接入 SQLite;PATCH 已支持用显式 `null` 清空 base URL、默认模型和凭据引用。
|
||
- Run/Trace 暂存内存;下一步抽象 Repository 并接入 SQLite。
|
||
- Permission 已有核心等待/恢复机制,前端确认 UI 尚未联调。
|
||
- Task 已持久化到 SQLite;Attachment Tool 读取 Host 管理目录中的 UTF-8 文件。
|
||
- `audio.transcribe` 当前消费 Host 预生成的 transcript;faster-whisper 与说话人分离仍按技术基线在第二阶段接入。
|
||
- Extension 安装记录暂存内存;后续接入持久化 Registry 与版本升级流程。
|
||
- 当前 Plugin Host 只支持内置声明式白名单 handler;MCP Bridge、独立进程健康检查与 UI Contribution 在第二阶段实现。
|