Design covers: agent loop, LLM provider abstraction, transport layer, context management, skill system, tool registry. P0 plan targets the minimum viable loop: user input → LLM streaming → tool call → output. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
15 KiB
15 KiB
AI Coding Assistant 设计文档
日期:2026-05-12 状态:已批准(2026-05-12)
1. 背景与目标
构建一个可扩展的 AI 编程助手,具备以下核心能力:
- 统一多 LLM 提供者 API(OpenAI 兼容 + Anthropic 原生)
- 工具调用、状态管理、上下文压缩、技能系统
- 多种交互模式:TUI、RPC、打印模式、Web UI
- 差分渲染、Markdown、xterm.js 渲染引擎
- Web Components 页面,支持渲染 markdown、html、图片等
目标
- 可扩展:LLM 提供者、工具、技能均支持插件式扩展
- 流式优先:从 LLM 调用到终端渲染全链路流式
- 双语言协作:Python 后端(agent 逻辑)+ TypeScript 前端(渲染交互)
- 多模式交互:同一 agent 支持不同前端接入(TUI / Web / RPC)
非目标
- 不做模型训练 / 微调
- 不做多用户 / 多租户
- 不做云端部署(初期仅本地运行)
- 不做 VS Code / JetBrains 插件集成(后续扩展)
2. 技术栈
| 层 | 技术 | 说明 |
|---|---|---|
| Agent 核心 | Python 3.12+ | asyncio,type hints |
| LLM 调用 | httpx (async) | 统一 HTTP 客户端 |
| 状态持久化 | SQLite (aiosqlite) | 消息、会话、技能状态 |
| Transport - stdio | asyncio streams | TUI 直接 spawn |
| Transport - SSE/WS | aiohttp + aiohttp-sse | Web 前端接入 |
| TUI | TypeScript + Ink / Blessed | 通过 stdio JSON-RPC 通信 |
| Web UI | TypeScript + Web Components | 通过 SSE/WS 通信 |
| 渲染 | Markdown-it / xterm.js / 差分渲染 | Web Components 封装 |
3. 核心架构
3.1 Agent Loop
┌─────────────────────────────────────────────────────────────┐
│ agentLoop() / agentLoopContinue() │
│ ├── 创建 EventStream │
│ └── 调用 runLoop() / runAgentLoopContinue() │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ runLoop() — 主循环 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 外层 while(true) — 处理 follow-up 消息 │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ 内层 while(hasMoreToolCalls || pendingMsg) │ │ │
│ │ │ 1. streamAssistantResponse() → LLM 调用 │ │ │
│ │ │ 2. executeToolCalls() → 工具执行 │ │ │
│ │ │ 3. prepareNextTurn() → 可切换模型/思考级别 │ │ │
│ │ │ 4. shouldStopAfterTurn() → 决定是否退出 │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
3.2 消息流转
AgentMessage[] (内部格式)
↓ LLMProvider.convert_messages()
Message[] (LLM API 格式)
↓ LLM 调用
AssistantMessage (含 tool_calls)
↓ executeToolCalls()
ToolResultMessage[]
↓ 回到 runLoop() 继续
3.3 Transport 抽象
class Transport(ABC):
@abstractmethod
async def start(self, handler: RequestHandler) -> None: ...
@abstractmethod
async def send_event(self, event: AgentEvent) -> None: ...
@abstractmethod
async def stop(self) -> None: ...
# RequestHandler = Callable[AgentRequest, Awaitable[None]]
# AgentEvent = 流式事件(token / tool_call / tool_result / done / error)
stdio 实现:stdin 读取 JSON-RPC 请求,stdout 写入 JSON-RPC 响应/通知。 SSE/WS 实现:aiohttp 启动 HTTP server,POST 接收请求,SSE 推送事件,WS 双向通信。
3.4 LLM Provider 抽象
class LLMProvider(ABC):
@abstractmethod
async def stream_chat(
self,
messages: list[Message],
tools: list[ToolDef] | None = None,
**kwargs,
) -> AsyncIterator[StreamChunk]: ...
@abstractmethod
def convert_messages(self, agent_messages: list[AgentMessage]) -> list[Message]: ...
@abstractmethod
def convert_tools(self, tools: list[ToolDef]) -> list[dict]: ...
OpenAI 兼容 adapter:覆盖 OpenAI / Azure / 通义 / 智谱 / DeepSeek 等。 Anthropic adapter:原生 Messages API,保留 tool_use / extended_thinking 能力。
3.5 上下文管理
┌──────────────────────────────────────────────────┐
│ LayeredContext │
│ ┌────────────────────────────────────────────┐ │
│ │ System Layer(系统提示词,不可压缩) │ │
│ ├────────────────────────────────────────────┤ │
│ │ Task Layer(当前任务摘要,低压缩优先级) │ │
│ ├────────────────────────────────────────────┤ │
│ │ Conversation Layer(对话消息,可压缩) │ │
│ │ └── SlidingWindow + Summarizer │ │
│ └────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
- 滑动窗口:保留最近 N 轮完整消息
- 摘要压缩:超出窗口的旧消息通过 LLM 生成摘要,替换原文
- 分层优先级:System > Task > Conversation,压缩只作用于 Conversation 层
3.6 状态持久化(SQLite)
-- 会话表
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
model TEXT,
metadata JSON
);
-- 消息表
CREATE TABLE messages (
id TEXT PRIMARY KEY,
session_id TEXT REFERENCES sessions(id),
role TEXT NOT NULL, -- system / user / assistant / tool
content TEXT,
tool_calls JSON,
tool_call_id TEXT,
token_count INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 上下文摘要表
CREATE TABLE context_summaries (
id TEXT PRIMARY KEY,
session_id TEXT REFERENCES sessions(id),
layer TEXT NOT NULL, -- system / task / conversation
content TEXT NOT NULL,
message_range_start INTEGER,
message_range_end INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 技能状态表
CREATE TABLE skill_states (
session_id TEXT,
skill_name TEXT,
state JSON,
PRIMARY KEY (session_id, skill_name)
);
3.7 技能系统
class Skill(ABC):
"""技能基类:提示词模板 + 工具绑定 + 触发规则"""
name: str
description: str
trigger: TriggerRule # 自动 / 关键词 / LLM 选择
system_prompt_fragment: str # 注入到系统提示词
tools: list[ToolDef] # 技能关联的工具
@abstractmethod
async def on_activate(self, ctx: SkillContext) -> None: ...
@abstractmethod
async def on_deactivate(self, ctx: SkillContext) -> None: ...
class SkillRegistry:
"""技能注册中心,支持动态加载"""
def __init__(self, skill_dirs: list[Path]):
self._skills: dict[str, Skill] = {}
self._skill_dirs = skill_dirs
async def load_all(self) -> None:
"""扫描目录,动态导入技能模块"""
async def load_skill(self, name: str) -> None:
"""热加载单个技能"""
def get_active_skills(self, context: AgentContext) -> list[Skill]:
"""根据触发规则返回当前应激活的技能"""
动态加载:从配置的目录扫描 .py 文件,每个文件导出 Skill 子类实例,运行时可热加载。
3.8 Hooks 配置点
| Hook | 时机 | 用途 |
|---|---|---|
get_steering_messages |
每轮开始前 | 注入上下文消息 |
get_follow_up_messages |
Agent 停止前 | 检查后续消息 |
prepare_next_turn |
轮次间 | 切换模型/思考级别 |
should_stop_after_turn |
每轮结束 | 决定是否退出 |
before_tool_call |
工具执行前 | 拦截/修改参数 |
after_tool_call |
工具执行后 | 增强/修改结果 |
4. 模块边界与数据流
┌─────────┐ JSON-RPC/SSE ┌──────────────┐ StreamChunk ┌───────────┐
│ TUI │ ←──────────────→ │ Transport │ ←─────────────→ │ LLM │
│ (TS) │ │ (stdio/WS) │ │ Provider │
└─────────┘ └──────┬───────┘ └───────────┘
│
┌─────────┐ SSE/WS ┌──────┴───────┐
│ Web UI │ ←──────────────→│ Agent Loop │
│ (TS) │ │ (Python) │
└─────────┘ └──────┬───────┘
│
┌──────────────┼──────────────┐
│ │ │
┌─────┴─────┐ ┌────┴─────┐ ┌──────┴─────┐
│ Context │ │ Skills │ │ State │
│ Manager │ │ Registry │ │ (SQLite) │
└───────────┘ └────┬─────┘ └────────────┘
│
┌─────┴─────┐
│ Tools │
│ Registry │
└───────────┘
5. 错误处理
| 场景 | 策略 |
|---|---|
| LLM API 调用失败 | 指数退避重试(最多 3 次),返回错误事件给前端 |
| 工具执行超时 | 可配置超时(默认 60s),超时后返回 timeout 错误结果 |
| 工具执行异常 | 捕获异常,转为 ToolResultMessage(is_error=True) |
| Transport 断连 | stdio: 进程退出;SSE/WS: 自动重连 + 会话恢复 |
| 上下文压缩失败 | 回退到简单截断,保留最近 N 条消息 |
| 技能加载失败 | 跳过该技能,记录警告日志,不影响其他技能 |
6. 测试策略
| 层 | 测试类型 | 工具 |
|---|---|---|
| Python 核心 | 单元测试 | pytest + pytest-asyncio |
| LLM Provider | 集成测试(mock HTTP) | pytest + respx |
| Transport | 单元测试 + 集成测试 | pytest,stdio 用 subprocess 测试 |
| 上下文管理 | 单元测试 | pytest,SQLite 用内存数据库 |
| 技能系统 | 单元测试 | pytest,动态加载用 importlib |
| 工具 | 单元测试 | pytest |
| TypeScript 前端 | 单元测试 | vitest |
| Web Components | 组件测试 | vitest + @open-wc/testing |
| 端到端 | 集成测试 | pytest + Playwright(Web)/ subprocess(TUI) |
7. 实现优先级(MVP)
-
P0 — 核心 agent loop + OpenAI 兼容 provider + stdio transport
- 跑通最小闭环:用户输入 → LLM 流式响应 → 工具调用 → 输出
-
P1 — Anthropic provider + SSE/WS transport + SQLite 状态
- 多 provider 支持 + Web 接入 + 会话持久化
-
P2 — 上下文压缩 + 技能系统 + Web Components 基础渲染
- 长对话支持 + 可扩展能力 + 前端渲染
-
P3 — TUI + 差分渲染 + xterm.js + 高级渲染
- 完整交互体验
8. 项目结构
ai-coding-assistant/
├── pyproject.toml
├── packages/
│ ├── core/ # Python: agent 核心
│ │ ├── agent/
│ │ │ ├── loop.py # agentLoop / runLoop
│ │ │ ├── messages.py # AgentMessage 消息模型
│ │ │ └── hooks.py # Hook 配置点
│ │ ├── llm/
│ │ │ ├── base.py # LLMProvider 抽象接口
│ │ │ ├── openai_compat.py
│ │ │ └── anthropic.py
│ │ ├── transport/
│ │ │ ├── base.py # Transport 抽象接口
│ │ │ ├── stdio.py
│ │ │ └── sse_ws.py
│ │ ├── context/
│ │ │ ├── manager.py # 分层上下文管理
│ │ │ ├── compressor.py # 滑动窗口 + 摘要压缩
│ │ │ └── state.py # SQLite 状态持久化
│ │ ├── skills/
│ │ │ ├── registry.py # SkillRegistry 动态加载
│ │ │ ├── base.py # Skill 基类
│ │ │ └── builtin/ # 内置技能
│ │ └── tools/
│ │ ├── base.py # Tool 基类
│ │ └── builtin/ # 内置工具
│ └── web/ # TypeScript: 前端
│ ├── package.json
│ ├── src/
│ │ ├── components/ # Web Components
│ │ ├── renderers/ # 差分渲染、Markdown、xterm.js
│ │ ├── tui/ # TUI 模式
│ │ └── rpc/ # RPC 客户端
│ └── ...
├── docs/
│ └── plans/
└── memory/