Metadata-Version: 2.5
Name: qi-agent
Version: 1.0.0
Summary: Python-first extensible agent backend, server, and built-in adapters
Requires-Python: >=3.12
Requires-Dist: dashscope>=1.26.4
Requires-Dist: fastapi>=0.116
Requires-Dist: filelock>=3.18
Requires-Dist: httpx>=0.28
Requires-Dist: jsonschema>=4.23
Requires-Dist: keyring>=25.6
Requires-Dist: packaging>=24
Requires-Dist: platformdirs>=4.3
Requires-Dist: pydantic-settings>=2.9
Requires-Dist: pydantic>=2.10
Requires-Dist: qi-ai<2,>=1.0.0
Requires-Dist: typer>=0.16
Requires-Dist: uvicorn>=0.35
Requires-Dist: zstandard>=0.23
Description-Content-Type: text/markdown

# qi-pi / pi-agent-py v1

> Python Agent 微内核：Session Event Log 是唯一事实来源，Everything is a Plugin。

qi-pi v1 不再把 Agent Loop、工具、Provider、持久化或 Server 当作 Core 的固定组成。`qi_agent_core`
只提供五种原语：`ServiceKey`、`Context`、`Scope`、`Effect` 和 `EventSpec`。其余能力均由同进程插件
提供，可独立替换、组合和按 generation 热重载。

这是破坏性 v1：不兼容 API v1 插件，也不读取旧 Session 数据。

## 架构

```mermaid
flowchart LR
    Client["Web / Desktop / Headless"] --> Transport["HTTP/SSE 插件"]
    Transport --> Driver["AgentDriver 插件"]
    Driver --> Session["SessionService"]
    Driver --> Provider["LLMService 插件"]
    Driver --> Tools["Tool Pipeline 插件"]
    Session --> JSONL["Zstd JSONL Persistence 插件"]
    Session --> Projection["可重建 SQLite 投影"]
    Core["qi_agent_core 微内核"] --- Driver
    Core --- Session
    Core --- Provider
    Core --- Tools
    Plugins["Blog / Memory / Live2D / Voice / Telemetry"] --> Core
```

核心约束：

- `Session Event Log` 是消息历史、回放、Resume、Audit 和 Telemetry 的共同事实源。
- `derive_messages()` 从事件 surface 投影模型历史，不维护第二份 `Agent.messages`。
- `assistant/chunk` 保留流式过程，`assistant/message` 保存组装后的权威消息。
- Session Stream 保存可恢复事实；Agent Control Stream 只发送活跃状态。
- 活跃 Turn 租用创建时的 generation；热重载只影响新 Turn。
- 工具策略使用单调决策：`abstain < require_approval < deny`。
- Parallel 工具只并发执行 body；策略、post、实时通知和持久提交仍保持模型调用顺序。
- Tool Ledger 记录 `prepared → dispatched → external-result-recorded → committed`，未知副作用不会盲目重试。
- 不序列化协程栈；恢复只从显式事件和检查点重新进入。

## 安装与启动

Python 3.12+。用户只需管理两个公开 Distribution：`qi-ai` 与 `qi-agent`。

```bash
uv tool install qi-agent
export DASHSCOPE_API_KEY='...'
qi-agent serve
```

源码开发：

```bash
uv sync --all-packages
uv run pytest
uv run ruff check packages tests
uv run pyright
```

HTTP 的最小流程：

```bash
curl -X POST http://127.0.0.1:8765/api/v1/sessions \
  -H 'content-type: application/json' \
  -d '{"metadata":{"title":"demo"}}'

curl -X POST http://127.0.0.1:8765/api/v1/turns \
  -H 'content-type: application/json' \
  -d '{
    "session_id":"SESSION_ID",
    "prompt":"你好",
    "model":{"id":"qwen3-max","provider":"dashscope","display_name":"Qwen3 Max"}
  }'
```

事实流使用 `/api/v1/sessions/{session_id}/events?after_sequence=...`；活跃控制流使用
`/api/v1/turns/{turn_id}/control-events`。

产品 Profile 通过稳定 ServiceKey 接入同一个 Transport：`live2d` Profile 提供受 Manifest
白名单约束的模型资产、Snapshot、Command 和 SSE；启用 `voice` 后，
`POST /api/v1/speech/synthesize` 为已存在 Session 返回私有、禁止缓存的 PCM 流。HTTP 层统一执行
Origin、请求体上限、Bearer/Cookie 与 CSRF 策略，产品插件不重复实现认证。

## 最小插件

```python
from typing import Any

from qi_agent_core import Context, ServiceKey
from qi_agent_runtime import TOOLS, ToolDefinition


class GreetPlugin:
    id = "greet"
    inject: tuple[ServiceKey[Any], ...] = (TOOLS,)

    async def apply(self, ctx: Context) -> None:
        tool = ToolDefinition(
            name="greet",
            description="Greet someone by name.",
            parameters={
                "type": "object",
                "properties": {"name": {"type": "string"}},
                "required": ["name"],
            },
            execute=lambda args, _ctx: f"Hello, {args['name']}!",
        )
        ctx.effect(ctx.require(TOOLS).register(tool))
```

`ctx.provide()`、`ctx.on()`、`ctx.effect()` 和服务的 `register()` 都产生 disposer；插件作用域退出时按
逆序自动释放监听器、后台任务、Provider Client 和连接。

插件不是由 Runtime 的 `if/else` 装配。`PluginCatalog` 注册工厂与 Manifest，`Bundle → Profile →
Patch` 选择最终 generation；未指定 Profile 时使用 `headless`。内置产品 Profile 为 `headless`、
`web`、`blog` 和 `live2d`。Patch 对对象递归合并、数组整段替换，`null` 删除字段。

本地/Git/PyPI 插件统一管理：

```bash
qi-agent install ./my-plugin
qi-agent install github:owner/my-plugin@v1.0.0
qi-agent install pypi:qi-agent-my-plugin@1.0.0
qi-agent update my-plugin
qi-agent update
qi-agent remove my-plugin
```

目录插件必须声明 Manifest API 2：

```toml
[extension]
id = "greet"
version = "1.0.0"
api_version = "2"
entrypoint = "plugin.py:create_plugin"
requires_agent = ">=1,<2"
provides = []
inject = ["tools"]
replaces = []
```

同 ID 或同 Service 替换必须通过 `replaces` 显式声明。Manifest 的服务声明、配置 Schema 与 Agent
版本范围会在候选 generation 装配前校验；失败时完整回滚。

## 内置插件与数据

| 插件 | 提供能力 |
| --- | --- |
| `session-persistence-jsonl` | 每 Session append-only Zstandard frame 日志 |
| `session` | 校验、surface、派生消息和事实订阅 |
| `providers` | Provider Registry |
| `command-inbox` / `compaction` | 持久输入队列与 surface 替换 |
| `tools` | Gate、审批、wrapper、执行、render 和结果冻结 |
| `react-agent-loop` | 默认可恢复状态机 |
| `telemetry-sqlite` | 可删除、可重建的管理投影 |
| `website` / `blog` | 安全网页动作与博客检索 Tool |
| `memory` | Tool、动态上下文、事件观察和管理动作 |
| `live2d` / `voice` | Avatar 与流式语音服务 |

默认 Session 位于新 `v1/sessions` 数据根。SQLite 只用于列表、搜索、统计等投影，不是模型历史真相
源。v1 不要求 PostgreSQL、Redis、ClickHouse 或 OTel Collector。

工具结果使用 `TextContent | ImageContent` 富内容块。Anthropic 转换为原生 tool-result 图片块；
Gemini 3 转换为原生 multimodal function response；不支持统一工具图片协议的
OpenAI-compatible endpoint 使用带类型信息的文本降级。

## 文档入口

- [v1 微内核 ADR](docs/adr/0014-everything-is-a-plugin-v1.md)
- [Session 格式与恢复](docs/protocols/session-format.md)
- [持久事件目录](docs/protocols/agent-events.md)
- [插件开发](docs/extensions/overview.md)
- [Manifest API 2](docs/protocols/extension-manifest.md)
- [Tool Contract](docs/protocols/tool-contract.md)
- [TypeScript UI SDK](sdk/typescript/src/index.ts)

正式 Web UI 不在本仓库。前端通过轻量 SDK 回放 Session 事件、按 sequence 去重，并使用稳定
`(kind, id)` 节点键进行 `start/update/end` 投影。
