mirror of
https://github.com/tvytlx/ai-agent-deep-dive.git
synced 2026-04-03 07:34:50 +08:00
Add teaching Python agent CLI with Poetry and CI
This commit is contained in:
26
.github/workflows/ci.yml
vendored
Normal file
26
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Install Poetry
|
||||||
|
run: pip install poetry
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: poetry install
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: poetry run pytest
|
||||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.venv/
|
||||||
|
__pycache__/
|
||||||
|
.pytest_cache/
|
||||||
|
*.pyc
|
||||||
105
docs/00-product-overview.md
Normal file
105
docs/00-product-overview.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# 00. 产品总览需求文档
|
||||||
|
|
||||||
|
## 1. 产品定义
|
||||||
|
|
||||||
|
这是一款面向软件工程任务的交互式 AI 执行系统。它不是单纯的聊天机器人,也不是只会调用几个工具的脚本外壳,而是一套把推理、工具、权限、任务拆解、记忆、扩展机制与用户交互统一起来的产品系统。
|
||||||
|
|
||||||
|
## 2. 产品目标
|
||||||
|
|
||||||
|
产品需要满足以下目标:
|
||||||
|
|
||||||
|
1. 帮助用户完成真实的软件工程任务,而不只是提供建议
|
||||||
|
2. 在执行过程中保持安全、可控、可恢复
|
||||||
|
3. 让复杂任务可以分解、委派、验证、追踪
|
||||||
|
4. 让系统可以扩展新的技能、插件与外部工具能力
|
||||||
|
5. 让长期使用形成可积累的记忆和工作习惯
|
||||||
|
|
||||||
|
## 3. 核心用户
|
||||||
|
|
||||||
|
### 3.1 主要用户
|
||||||
|
- 独立开发者
|
||||||
|
- 工程师
|
||||||
|
- 技术产品经理
|
||||||
|
- 有代码任务但希望借助 AI 提升效率的操作者
|
||||||
|
|
||||||
|
### 3.2 用户的核心诉求
|
||||||
|
- 我不只想问问题,我想让系统帮我做事
|
||||||
|
- 我不只想生成代码,我想让它真正改动项目并验证结果
|
||||||
|
- 我不只想要一次回答,我想让它持续推进任务
|
||||||
|
- 我不只想在一个固定产品里工作,我希望它能接入我自己的工具和工作流
|
||||||
|
|
||||||
|
## 4. 产品要解决的核心问题
|
||||||
|
|
||||||
|
### 4.1 普通聊天模型的问题
|
||||||
|
普通聊天模型的核心局限是:
|
||||||
|
- 只做一次性回答
|
||||||
|
- 没有稳定执行能力
|
||||||
|
- 没有工具治理
|
||||||
|
- 没有任务状态
|
||||||
|
- 没有长期上下文管理
|
||||||
|
- 无法形成可扩展工作流
|
||||||
|
|
||||||
|
### 4.2 简单 Agent 的问题
|
||||||
|
简单 Agent 虽然能调用工具,但通常会遇到:
|
||||||
|
- 行为发散
|
||||||
|
- 工具滥用
|
||||||
|
- 缺乏权限约束
|
||||||
|
- 上下文污染
|
||||||
|
- 任务过程不可追踪
|
||||||
|
- 做完后不验证
|
||||||
|
- 无法优雅扩展
|
||||||
|
|
||||||
|
因此,本产品的需求本质上是在解决:
|
||||||
|
|
||||||
|
> 如何把“模型 + 工具”升级成一个可用、可控、可扩展、可产品化的软件工程执行系统。
|
||||||
|
|
||||||
|
## 5. 顶层产品能力
|
||||||
|
|
||||||
|
根据源码结构反推,这套产品至少需要以下一级能力:
|
||||||
|
|
||||||
|
1. 系统提示词编排能力
|
||||||
|
2. 工具发现、执行与治理能力
|
||||||
|
3. 多 Agent 调度能力
|
||||||
|
4. Skills / Plugins / MCP 扩展能力
|
||||||
|
5. Memory / Session 管理能力
|
||||||
|
6. 命令系统与交互界面能力
|
||||||
|
7. 任务与后台执行能力
|
||||||
|
8. 验证与质量保证能力
|
||||||
|
9. Telemetry / Transcript / 可追溯能力
|
||||||
|
|
||||||
|
## 6. 顶层非功能需求
|
||||||
|
|
||||||
|
### 6.1 安全性
|
||||||
|
- 危险操作必须可拦截
|
||||||
|
- 外部工具结果必须被视为潜在不可信输入
|
||||||
|
- 用户必须能控制权限边界
|
||||||
|
|
||||||
|
### 6.2 可恢复性
|
||||||
|
- 会话可恢复
|
||||||
|
- 任务状态可追踪
|
||||||
|
- 子任务生命周期可清理
|
||||||
|
|
||||||
|
### 6.3 可扩展性
|
||||||
|
- 外部工具可接入
|
||||||
|
- 自定义技能可接入
|
||||||
|
- 插件可注入命令、技能和行为约束
|
||||||
|
|
||||||
|
### 6.4 成本控制
|
||||||
|
- 提示词拼装要考虑缓存
|
||||||
|
- 上下文使用要考虑预算
|
||||||
|
- 大任务要支持压缩和摘要
|
||||||
|
|
||||||
|
## 7. 产品价值主张
|
||||||
|
|
||||||
|
如果从需求层面概括,这套产品的价值主张不是“回答更聪明”,而是:
|
||||||
|
|
||||||
|
- 更稳定地执行任务
|
||||||
|
- 更安全地调用能力
|
||||||
|
- 更可控地使用 AI
|
||||||
|
- 更容易把 AI 接入真实工程工作流
|
||||||
|
|
||||||
|
## 8. 一个产品经理视角下的总需求句
|
||||||
|
|
||||||
|
可以用一句话总结:
|
||||||
|
|
||||||
|
> 用户需要的不是一个会聊天的模型,而是一个能够在真实工程环境中持续推进任务、遵守约束、调用工具、拆分工作、保留上下文并可被验证的 AI 软件工程操作系统。
|
||||||
143
docs/01-system-prompt-and-orchestration.md
Normal file
143
docs/01-system-prompt-and-orchestration.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# 01. 系统提示词与 Agent 编排需求文档
|
||||||
|
|
||||||
|
## 1. 系统提示词为什么不是一段固定文案
|
||||||
|
|
||||||
|
从源码结构可以反推,这个产品要求系统提示词具备“动态拼装”能力,而不是固定模板。
|
||||||
|
|
||||||
|
### 需求原因
|
||||||
|
用户环境、工具集、语言、输出风格、会话状态、MCP 连接状态都可能变化。如果系统提示词不能动态组装,就会出现:
|
||||||
|
- 规则不匹配当前会话
|
||||||
|
- 工具说明失效
|
||||||
|
- 记忆无法注入
|
||||||
|
- token 成本失控
|
||||||
|
|
||||||
|
## 2. 系统提示词层的核心需求
|
||||||
|
|
||||||
|
### 2.1 基础身份定义
|
||||||
|
系统需要明确告诉模型:
|
||||||
|
- 你是执行型协作者
|
||||||
|
- 你的主要任务是软件工程支持
|
||||||
|
- 你的输出直接给用户看
|
||||||
|
|
||||||
|
### 2.2 做任务规范
|
||||||
|
系统需要内建一套工程行为规范,例如:
|
||||||
|
- 不要乱加功能
|
||||||
|
- 不要过度抽象
|
||||||
|
- 不要假装验证过
|
||||||
|
- 先读代码再改代码
|
||||||
|
- 不要随意创建文件
|
||||||
|
|
||||||
|
这不是“风格偏好”,而是稳定性需求。
|
||||||
|
|
||||||
|
### 2.3 风险动作规范
|
||||||
|
系统需要明确哪些操作有 blast radius,需要额外确认。例如:
|
||||||
|
- 删除
|
||||||
|
- 推送
|
||||||
|
- 外部可见动作
|
||||||
|
- 修改共享状态
|
||||||
|
|
||||||
|
### 2.4 工具使用语法
|
||||||
|
系统不仅要告诉模型“有什么工具”,还要告诉它:
|
||||||
|
- 什么时候读文件
|
||||||
|
- 什么时候搜索
|
||||||
|
- 什么时候编辑
|
||||||
|
- 什么时候用 shell
|
||||||
|
- 什么时候并行调用
|
||||||
|
|
||||||
|
## 3. 动态区块需求
|
||||||
|
|
||||||
|
系统提示词至少需要支持以下动态区块:
|
||||||
|
|
||||||
|
1. 环境信息
|
||||||
|
2. 当前语言偏好
|
||||||
|
3. 输出风格
|
||||||
|
4. 会话局部规则
|
||||||
|
5. 记忆内容
|
||||||
|
6. MCP 指令
|
||||||
|
7. scratchpad / 临时工作区规则
|
||||||
|
8. token budget 提示
|
||||||
|
|
||||||
|
## 4. 为什么要有 Prompt cache boundary
|
||||||
|
|
||||||
|
如果每次请求都完全重造提示词,会带来两个问题:
|
||||||
|
- 成本上升
|
||||||
|
- 缓存命中下降
|
||||||
|
|
||||||
|
因此产品需要把系统提示词分成:
|
||||||
|
- 稳定前缀
|
||||||
|
- 动态后缀
|
||||||
|
|
||||||
|
这样才能兼顾灵活性与成本控制。
|
||||||
|
|
||||||
|
## 5. Agent 编排的需求本质
|
||||||
|
|
||||||
|
系统需要支持把复杂任务拆给不同角色,而不是只依赖一个万能主 agent。
|
||||||
|
|
||||||
|
### 必须支持的角色至少包括
|
||||||
|
- 通用执行角色
|
||||||
|
- 探索角色
|
||||||
|
- 规划角色
|
||||||
|
- 验证角色
|
||||||
|
|
||||||
|
### 需求原因
|
||||||
|
不同任务阶段对行为模式要求不同:
|
||||||
|
- 探索需要只读
|
||||||
|
- 规划需要结构化输出
|
||||||
|
- 实施需要可执行
|
||||||
|
- 验证需要对抗性检查
|
||||||
|
|
||||||
|
如果用一个 agent 混合承担所有角色,稳定性会下降。
|
||||||
|
|
||||||
|
## 6. 子 Agent 设计需求
|
||||||
|
|
||||||
|
### 6.1 fork 子任务
|
||||||
|
系统需要支持:
|
||||||
|
- 子任务继承上下文
|
||||||
|
- 子任务尽量共享缓存前缀
|
||||||
|
- 子任务减少主线程污染
|
||||||
|
|
||||||
|
### 6.2 background 子任务
|
||||||
|
系统需要支持:
|
||||||
|
- 后台执行
|
||||||
|
- 进度跟踪
|
||||||
|
- 结果通知
|
||||||
|
- 可恢复输出
|
||||||
|
|
||||||
|
### 6.3 isolation 子任务
|
||||||
|
系统需要支持:
|
||||||
|
- worktree 隔离
|
||||||
|
- remote 隔离(如果启用)
|
||||||
|
|
||||||
|
## 7. Prompt 写给子 Agent 的需求
|
||||||
|
|
||||||
|
主 agent 必须能够给子 agent 交接充分背景,不能只丢一个模糊命令。产品层面要明确要求:
|
||||||
|
- 写清任务目标
|
||||||
|
- 写清已知背景
|
||||||
|
- 写清约束
|
||||||
|
- 写清期望输出
|
||||||
|
|
||||||
|
## 8. 伪代码表达
|
||||||
|
|
||||||
|
```python
|
||||||
|
class SystemPromptBuilder:
|
||||||
|
def build(self, env, tools, memory, output_style, language, mcp_info):
|
||||||
|
static_sections = [
|
||||||
|
intro(),
|
||||||
|
system_rules(),
|
||||||
|
task_rules(),
|
||||||
|
action_safety(),
|
||||||
|
tool_usage_rules(),
|
||||||
|
]
|
||||||
|
dynamic_sections = [
|
||||||
|
env_section(env),
|
||||||
|
language_section(language),
|
||||||
|
output_style_section(output_style),
|
||||||
|
memory_section(memory),
|
||||||
|
mcp_section(mcp_info),
|
||||||
|
]
|
||||||
|
return static_sections + dynamic_sections
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. 产品经理视角下的总需求句
|
||||||
|
|
||||||
|
> 系统提示词层的核心需求,是把产品规则、工具语法、会话状态和环境约束组装成一套可动态生成、可缓存优化、可支持多角色协作的运行时控制平面。
|
||||||
119
docs/02-tools-permissions-and-execution.md
Normal file
119
docs/02-tools-permissions-and-execution.md
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# 02. 工具、权限与执行链需求文档
|
||||||
|
|
||||||
|
## 1. 为什么工具系统是产品核心
|
||||||
|
|
||||||
|
如果模型不能操作环境,它只是建议生成器。这个产品的目标是帮助用户推进真实工程任务,因此必须具备正式的工具系统。
|
||||||
|
|
||||||
|
## 2. 工具系统需求
|
||||||
|
|
||||||
|
### 2.1 基础工具能力
|
||||||
|
产品至少需要以下工具类别:
|
||||||
|
- 读文件
|
||||||
|
- 改文件
|
||||||
|
- 写文件
|
||||||
|
- 搜索文件
|
||||||
|
- 搜索内容
|
||||||
|
- shell / 命令执行
|
||||||
|
- todo / 任务管理
|
||||||
|
- 用户追问
|
||||||
|
- 启动子 agent
|
||||||
|
- 调用外部 MCP 工具
|
||||||
|
|
||||||
|
### 2.2 工具使用规范
|
||||||
|
系统必须对模型明确规定工具使用优先级,避免:
|
||||||
|
- 用 shell 替代专用文件工具
|
||||||
|
- 误删或误改文件
|
||||||
|
- 低效重复操作
|
||||||
|
|
||||||
|
## 3. 权限系统需求
|
||||||
|
|
||||||
|
### 3.1 用户控制边界
|
||||||
|
用户必须能控制哪些工具自动允许,哪些要询问,哪些禁止。
|
||||||
|
|
||||||
|
### 3.2 权限决策来源
|
||||||
|
权限决策至少可能来自:
|
||||||
|
- 当前模式
|
||||||
|
- 用户规则
|
||||||
|
- 项目规则
|
||||||
|
- Hook 决策
|
||||||
|
- 特殊工具安全策略
|
||||||
|
|
||||||
|
### 3.3 被拒后的行为要求
|
||||||
|
如果某次工具调用被拒绝,系统不能机械重试,而应:
|
||||||
|
- 理解拒绝信号
|
||||||
|
- 调整方案
|
||||||
|
- 必要时向用户澄清
|
||||||
|
|
||||||
|
## 4. 执行链路需求
|
||||||
|
|
||||||
|
工具执行不能是“模型决定 -> 直接运行”。产品需要一条正式执行链:
|
||||||
|
|
||||||
|
1. 找到工具
|
||||||
|
2. 校验输入结构
|
||||||
|
3. 做额外 validateInput
|
||||||
|
4. 执行 PreToolUse hooks
|
||||||
|
5. 做权限决策
|
||||||
|
6. 真正执行工具
|
||||||
|
7. 记录 telemetry
|
||||||
|
8. 执行 PostToolUse hooks
|
||||||
|
9. 格式化结果回流给模型
|
||||||
|
|
||||||
|
## 5. 为什么需要 Hook
|
||||||
|
|
||||||
|
Hook 的需求本质是:
|
||||||
|
- 让组织规则进入运行时
|
||||||
|
- 让系统可以插入额外检查
|
||||||
|
- 让工具调用具备动态治理能力
|
||||||
|
|
||||||
|
### Hook 至少要支持的行为
|
||||||
|
- 返回消息
|
||||||
|
- 阻断执行
|
||||||
|
- 修改输入
|
||||||
|
- 提供 allow / ask / deny 建议
|
||||||
|
- 注入额外上下文
|
||||||
|
|
||||||
|
## 6. 为什么输入校验是必需的
|
||||||
|
|
||||||
|
模型本身会生成错误参数,因此产品必须在执行层拦住:
|
||||||
|
- schema 不合法
|
||||||
|
- 参数越界
|
||||||
|
- 缺字段
|
||||||
|
- 类型错误
|
||||||
|
|
||||||
|
## 7. shell 类工具的特殊需求
|
||||||
|
|
||||||
|
shell 工具的风险高于读写文件类工具,因此需要:
|
||||||
|
- 更严格的权限策略
|
||||||
|
- 可能的前置分类器检查
|
||||||
|
- 更强的审计能力
|
||||||
|
|
||||||
|
## 8. 工具执行结果的产品要求
|
||||||
|
|
||||||
|
工具执行结果不仅要“返回成功/失败”,还要满足:
|
||||||
|
- 可读
|
||||||
|
- 可追踪
|
||||||
|
- 能被后续 Hook 处理
|
||||||
|
- 能成为 transcript 的一部分
|
||||||
|
|
||||||
|
## 9. 伪代码表达
|
||||||
|
|
||||||
|
```python
|
||||||
|
def execute_tool(tool_name, raw_input, context):
|
||||||
|
tool = find_tool(tool_name)
|
||||||
|
validated = schema_validate(tool, raw_input)
|
||||||
|
validated = run_custom_validation(tool, validated)
|
||||||
|
|
||||||
|
hook_result = run_pre_hooks(tool, validated, context)
|
||||||
|
decision = resolve_permission(hook_result, context)
|
||||||
|
if decision == 'deny':
|
||||||
|
return denied_result()
|
||||||
|
|
||||||
|
final_input = maybe_update_input(validated, hook_result)
|
||||||
|
output = tool.call(final_input)
|
||||||
|
run_post_hooks(tool, final_input, output, context)
|
||||||
|
return output
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. 产品经理视角下的总需求句
|
||||||
|
|
||||||
|
> 工具系统必须从“可调用”升级到“可治理”:既要让模型拥有执行能力,也要在执行前后经过校验、权限、Hook、审计与结果回流,确保整个过程安全、稳定、可追踪。
|
||||||
103
docs/03-skills-plugins-mcp.md
Normal file
103
docs/03-skills-plugins-mcp.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# 03. Skills、Plugins 与 MCP 需求文档
|
||||||
|
|
||||||
|
## 1. 为什么产品不能只靠内置能力
|
||||||
|
|
||||||
|
如果产品所有能力都硬编码在主程序里,会遇到几个问题:
|
||||||
|
- 难以扩展
|
||||||
|
- 难以适配不同团队
|
||||||
|
- 难以承载领域知识
|
||||||
|
- 难以形成生态
|
||||||
|
|
||||||
|
因此,这套产品必须支持可扩展能力面。
|
||||||
|
|
||||||
|
## 2. Skills 的需求本质
|
||||||
|
|
||||||
|
Skill 不是普通帮助文档,而是一种可复用的工作流能力包。
|
||||||
|
|
||||||
|
### 2.1 Skill 需要承载什么
|
||||||
|
- 某类任务的使用规则
|
||||||
|
- 某类任务的上下文说明
|
||||||
|
- 某类任务的执行 SOP
|
||||||
|
- 该任务适用的工具边界
|
||||||
|
|
||||||
|
### 2.2 为什么 Skill 必须是 first-class primitive
|
||||||
|
因为产品需要让模型在遇到特定任务时,优先加载相应能力,而不是每次都重新即兴发挥。
|
||||||
|
|
||||||
|
## 3. Skill 的产品需求
|
||||||
|
|
||||||
|
1. 系统要能列出当前可用技能
|
||||||
|
2. 模型要能在合适时调用技能
|
||||||
|
3. skill 内容要能注入会话
|
||||||
|
4. skill 要能带 frontmatter 元信息
|
||||||
|
5. skill 可以约束 allowed-tools
|
||||||
|
6. skill 需要避免重复加载
|
||||||
|
|
||||||
|
## 4. Plugin 的需求本质
|
||||||
|
|
||||||
|
Plugin 的角色不是给程序员加脚本,而是为模型注入新的行为表面。
|
||||||
|
|
||||||
|
### Plugin 至少要支持
|
||||||
|
- 新命令
|
||||||
|
- 新技能目录
|
||||||
|
- frontmatter 配置
|
||||||
|
- 运行时变量替换
|
||||||
|
- 工具约束
|
||||||
|
- 用户可调用与否的声明
|
||||||
|
- effort / model 等提示
|
||||||
|
|
||||||
|
## 5. 为什么要有 MCP
|
||||||
|
|
||||||
|
MCP 的需求本质是:
|
||||||
|
- 用统一协议接入外部工具
|
||||||
|
- 让产品获得更多外部能力
|
||||||
|
- 让工具与说明一起进入运行时
|
||||||
|
|
||||||
|
### MCP 需要满足
|
||||||
|
1. 接入外部 server
|
||||||
|
2. 拉取工具定义
|
||||||
|
3. 注入使用说明
|
||||||
|
4. 在 agent 级别支持额外 server
|
||||||
|
5. 在生命周期结束时清理资源
|
||||||
|
|
||||||
|
## 6. 为什么模型需要“知道扩展能力存在”
|
||||||
|
|
||||||
|
很多系统扩展做不起来,不是因为没有插件,而是模型根本不知道:
|
||||||
|
- 有哪些技能
|
||||||
|
- 什么时候该用
|
||||||
|
- 扩展工具怎么使用
|
||||||
|
|
||||||
|
因此产品必须把这些扩展能力转化成模型可感知的提示信息。
|
||||||
|
|
||||||
|
## 7. Plugin / Skill / MCP 三者关系
|
||||||
|
|
||||||
|
### Skill
|
||||||
|
解决“某类任务应该怎么做”
|
||||||
|
|
||||||
|
### Plugin
|
||||||
|
解决“系统可以新增什么能力面”
|
||||||
|
|
||||||
|
### MCP
|
||||||
|
解决“系统如何连接外部工具与外部能力”
|
||||||
|
|
||||||
|
三者叠加后,产品才能具备生态能力。
|
||||||
|
|
||||||
|
## 8. 伪代码表达
|
||||||
|
|
||||||
|
```python
|
||||||
|
class ExtensionRuntime:
|
||||||
|
def load_skills(self, cwd):
|
||||||
|
return discover_skill_packages(cwd)
|
||||||
|
|
||||||
|
def load_plugins(self):
|
||||||
|
return discover_plugins()
|
||||||
|
|
||||||
|
def connect_mcp_servers(self, configs):
|
||||||
|
return [connect(server) for server in configs]
|
||||||
|
|
||||||
|
def expose_capabilities_to_model(self, skills, plugins, mcp_servers):
|
||||||
|
return build_runtime_capability_listing(skills, plugins, mcp_servers)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. 产品经理视角下的总需求句
|
||||||
|
|
||||||
|
> 产品必须提供一套可扩展运行时:Skill 负责封装工作流知识,Plugin 负责扩展命令与能力表面,MCP 负责接入外部工具与说明,三者共同让系统具备持续生长的能力。
|
||||||
152
docs/04-memory-and-session.md
Normal file
152
docs/04-memory-and-session.md
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# 04. 记忆与会话系统需求文档
|
||||||
|
|
||||||
|
## 1. 为什么需要记忆系统
|
||||||
|
|
||||||
|
如果产品只靠当前窗口上下文工作,会出现几个问题:
|
||||||
|
|
||||||
|
- 用户长期偏好无法沉淀
|
||||||
|
- 项目背景每次都要重新解释
|
||||||
|
- 复杂任务容易因上下文压缩而丢失关键事实
|
||||||
|
- 系统无法逐步形成“协作连续性”
|
||||||
|
|
||||||
|
因此,这个产品需要一个多层级记忆系统。
|
||||||
|
|
||||||
|
## 2. 记忆系统的产品目标
|
||||||
|
|
||||||
|
1. 保存长期有效的用户偏好和协作约束
|
||||||
|
2. 保存项目级背景信息
|
||||||
|
3. 在上下文压缩后仍能恢复关键事实
|
||||||
|
4. 支持会话恢复与后续继续执行
|
||||||
|
5. 为子任务和后台任务提供必要的上下文继承
|
||||||
|
|
||||||
|
## 3. 记忆层级反推
|
||||||
|
|
||||||
|
从目录结构与调用链可以推断,产品至少需要以下记忆层级:
|
||||||
|
|
||||||
|
### 3.1 用户级记忆
|
||||||
|
保存:
|
||||||
|
- 用户语言偏好
|
||||||
|
- 输出风格偏好
|
||||||
|
- 常见工作习惯
|
||||||
|
- 可长期生效的协作规则
|
||||||
|
|
||||||
|
### 3.2 项目级记忆
|
||||||
|
保存:
|
||||||
|
- 项目目录背景
|
||||||
|
- 常用命令
|
||||||
|
- 测试 / 构建约定
|
||||||
|
- 项目规范文件中的持续性要求
|
||||||
|
|
||||||
|
### 3.3 会话级记忆
|
||||||
|
保存:
|
||||||
|
- 当前任务目标
|
||||||
|
- 当前已知上下文
|
||||||
|
- 本轮执行中的中间状态
|
||||||
|
- 当前阶段的局部决策
|
||||||
|
|
||||||
|
### 3.4 子任务级记忆
|
||||||
|
保存:
|
||||||
|
- 子 agent 的任务目标
|
||||||
|
- 子 agent 的局部上下文
|
||||||
|
- fork 继承的父上下文
|
||||||
|
- 验证 / 探索 / 规划等专职子任务的专属状态
|
||||||
|
|
||||||
|
## 4. 记忆系统的核心需求
|
||||||
|
|
||||||
|
### 4.1 持久化需求
|
||||||
|
系统需要能保存以下内容:
|
||||||
|
- 用户长期偏好
|
||||||
|
- 项目常识
|
||||||
|
- 会话摘要
|
||||||
|
- 可恢复的任务元数据
|
||||||
|
|
||||||
|
### 4.2 注入需求
|
||||||
|
系统需要在合适的时机把记忆重新注入到模型上下文中,而不是永久塞在一次请求里。
|
||||||
|
|
||||||
|
### 4.3 压缩需求
|
||||||
|
当上下文接近限制时,系统必须支持:
|
||||||
|
- 自动压缩历史
|
||||||
|
- 保留关键事实
|
||||||
|
- 降低 token 成本
|
||||||
|
|
||||||
|
### 4.4 恢复需求
|
||||||
|
用户中断后再次回来时,系统要支持:
|
||||||
|
- 恢复当前任务状态
|
||||||
|
- 恢复关键上下文
|
||||||
|
- 继续之前的执行链
|
||||||
|
|
||||||
|
## 5. Session 管理需求
|
||||||
|
|
||||||
|
### 5.1 会话身份
|
||||||
|
每个会话都需要唯一标识,便于:
|
||||||
|
- 恢复
|
||||||
|
- 归档
|
||||||
|
- 分析
|
||||||
|
- 分享
|
||||||
|
- 跟踪任务生命周期
|
||||||
|
|
||||||
|
### 5.2 会话摘要
|
||||||
|
系统需要能够自动生成摘要,用于:
|
||||||
|
- 历史压缩
|
||||||
|
- 后续恢复
|
||||||
|
- 快速理解上下文
|
||||||
|
|
||||||
|
### 5.3 会话转录
|
||||||
|
系统需要持久记录关键消息流,至少包括:
|
||||||
|
- 用户输入
|
||||||
|
- 模型输出
|
||||||
|
- 工具调用摘要
|
||||||
|
- 子任务结果
|
||||||
|
- 失败信息
|
||||||
|
|
||||||
|
## 6. 为什么记忆不能等于“把所有历史都塞进去”
|
||||||
|
|
||||||
|
产品层面必须避免一种错误思路:把完整历史无脑拼进上下文。
|
||||||
|
|
||||||
|
这样会导致:
|
||||||
|
- 成本上升
|
||||||
|
- 注意力稀释
|
||||||
|
- 有效信息被噪声淹没
|
||||||
|
- 子任务污染主线程
|
||||||
|
|
||||||
|
更合理的需求是:
|
||||||
|
|
||||||
|
> 记忆应该是可选择地提取、压缩、恢复和注入,而不是无节制堆积。
|
||||||
|
|
||||||
|
## 7. fork 与记忆的需求关系
|
||||||
|
|
||||||
|
从多 agent 设计看,fork 子任务有两个特殊需求:
|
||||||
|
|
||||||
|
1. 继承足够的上下文来完成任务
|
||||||
|
2. 又不能把中间噪声带回主线程
|
||||||
|
|
||||||
|
因此系统需要:
|
||||||
|
- 支持父子上下文有边界地继承
|
||||||
|
- 支持子任务输出被总结,而不是原样回灌
|
||||||
|
- 支持 prompt cache 友好的上下文复用
|
||||||
|
|
||||||
|
## 8. 记忆系统的伪代码表达
|
||||||
|
|
||||||
|
```python
|
||||||
|
class MemorySystem:
|
||||||
|
def load_user_memory(self, user_id):
|
||||||
|
...
|
||||||
|
|
||||||
|
def load_project_memory(self, project_root):
|
||||||
|
...
|
||||||
|
|
||||||
|
def load_session_summary(self, session_id):
|
||||||
|
...
|
||||||
|
|
||||||
|
def build_runtime_context(self, user_memory, project_memory, session_summary):
|
||||||
|
return merge_relevant_context(user_memory, project_memory, session_summary)
|
||||||
|
|
||||||
|
def compact_if_needed(self, messages):
|
||||||
|
if context_budget_low(messages):
|
||||||
|
return summarize(messages)
|
||||||
|
return messages
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. 产品经理视角下的总需求句
|
||||||
|
|
||||||
|
> 这套产品必须拥有一个分层记忆系统:它既能保存长期偏好与项目背景,又能在上下文预算受限时压缩历史,并在任务恢复、子任务继承和长期协作中重新注入关键事实。
|
||||||
86
docs/05-commands-ui-and-operator-experience.md
Normal file
86
docs/05-commands-ui-and-operator-experience.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# 05. 命令系统、界面与操作者体验需求文档
|
||||||
|
|
||||||
|
## 1. 为什么命令系统是一级产品能力
|
||||||
|
|
||||||
|
这类产品不是单轮对话工具,而是一个长期运行的操作者界面。因此,命令系统不是附属功能,而是操作面板。
|
||||||
|
|
||||||
|
## 2. 命令系统需求
|
||||||
|
|
||||||
|
产品需要支持用户快速控制以下对象:
|
||||||
|
- memory
|
||||||
|
- permissions
|
||||||
|
- hooks
|
||||||
|
- mcp
|
||||||
|
- skills
|
||||||
|
- tasks
|
||||||
|
- review / plan / status
|
||||||
|
- 输出风格
|
||||||
|
- model
|
||||||
|
- sandbox
|
||||||
|
- 插件管理
|
||||||
|
|
||||||
|
## 3. 命令系统的产品目标
|
||||||
|
|
||||||
|
1. 降低复杂能力的学习成本
|
||||||
|
2. 提供清晰的系统控制入口
|
||||||
|
3. 把高级功能从自然语言里解耦出来
|
||||||
|
4. 为插件和技能提供统一入口面
|
||||||
|
|
||||||
|
## 4. UI / TUI 的核心需求
|
||||||
|
|
||||||
|
### 4.1 任务可见性
|
||||||
|
用户需要看到:
|
||||||
|
- 当前在做什么
|
||||||
|
- 后台任务是否运行中
|
||||||
|
- 子 agent 在做什么
|
||||||
|
- 哪些动作在等待权限
|
||||||
|
|
||||||
|
### 4.2 状态反馈
|
||||||
|
系统需要及时反馈:
|
||||||
|
- 进度
|
||||||
|
- 错误
|
||||||
|
- 被阻断原因
|
||||||
|
- 工具运行状态
|
||||||
|
|
||||||
|
### 4.3 结构化展示
|
||||||
|
系统需要把复杂状态结构化展示出来,例如:
|
||||||
|
- memory 面板
|
||||||
|
- permissions 面板
|
||||||
|
- skills 列表
|
||||||
|
- hooks 状态
|
||||||
|
- mcp 状态
|
||||||
|
- tasks 状态
|
||||||
|
|
||||||
|
## 5. 为什么操作者体验是核心需求
|
||||||
|
|
||||||
|
如果系统很强,但用户看不懂当前状态,就会产生:
|
||||||
|
- 不信任
|
||||||
|
- 不敢授权
|
||||||
|
- 不知道何时干预
|
||||||
|
- 不知道下一步怎么控制
|
||||||
|
|
||||||
|
因此产品必须让操作者感觉:
|
||||||
|
- 任务是透明的
|
||||||
|
- 系统是可控的
|
||||||
|
- 能力是可发现的
|
||||||
|
|
||||||
|
## 6. 后台任务与通知需求
|
||||||
|
|
||||||
|
一旦支持 background agents,系统就必须支持:
|
||||||
|
- 后台任务注册
|
||||||
|
- 进度更新
|
||||||
|
- 完成通知
|
||||||
|
- 输出文件查看
|
||||||
|
- 必要时 kill / cancel
|
||||||
|
|
||||||
|
## 7. 命令系统的扩展需求
|
||||||
|
|
||||||
|
命令系统还必须允许:
|
||||||
|
- 内建命令
|
||||||
|
- 插件命令
|
||||||
|
- skill 入口
|
||||||
|
- 条件启用的功能命令
|
||||||
|
|
||||||
|
## 8. 产品经理视角下的总需求句
|
||||||
|
|
||||||
|
> 这套产品不仅要有能力,还要有可操作性。命令系统与界面层的目标,是把复杂的 AI 运行时变成用户可发现、可控制、可追踪的操作者体验。
|
||||||
92
docs/06-verification-and-quality.md
Normal file
92
docs/06-verification-and-quality.md
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# 06. 验证与质量保证需求文档
|
||||||
|
|
||||||
|
## 1. 为什么“做完”不等于“完成”
|
||||||
|
|
||||||
|
在 AI 编程产品里,最大的风险之一是:模型会把“代码改了”误当成“任务完成了”。
|
||||||
|
|
||||||
|
因此,这个产品必须把“验证”设计成一个独立能力,而不是可有可无的附属步骤。
|
||||||
|
|
||||||
|
## 2. 验证系统的产品目标
|
||||||
|
|
||||||
|
1. 独立检查实现是否真的可用
|
||||||
|
2. 防止只读代码就宣称完成
|
||||||
|
3. 防止 happy path 偏见
|
||||||
|
4. 让验证结果带证据而不是口头判断
|
||||||
|
|
||||||
|
## 3. 验证 Agent 的需求
|
||||||
|
|
||||||
|
### 3.1 独立角色
|
||||||
|
验证角色需要与实施角色分离,避免实现者偏见。
|
||||||
|
|
||||||
|
### 3.2 默认心智模型
|
||||||
|
验证角色的工作不是“帮实现找理由通过”,而是主动尝试发现问题。
|
||||||
|
|
||||||
|
### 3.3 必须禁止的行为
|
||||||
|
验证角色不能:
|
||||||
|
- 修改项目文件
|
||||||
|
- 安装依赖
|
||||||
|
- 用写操作掩盖问题
|
||||||
|
|
||||||
|
### 3.4 必须支持的检查类型
|
||||||
|
- build
|
||||||
|
- test suite
|
||||||
|
- lint / type-check
|
||||||
|
- 接口调用验证
|
||||||
|
- UI 自动化验证
|
||||||
|
- CLI 输入输出验证
|
||||||
|
- migration 验证
|
||||||
|
- adversarial probe
|
||||||
|
|
||||||
|
## 4. 输出格式需求
|
||||||
|
|
||||||
|
验证结果必须满足:
|
||||||
|
- 有检查项标题
|
||||||
|
- 有实际执行命令
|
||||||
|
- 有真实输出
|
||||||
|
- 有 PASS / FAIL / PARTIAL 结果
|
||||||
|
- 最后有统一 verdict
|
||||||
|
|
||||||
|
## 5. 为什么需要 adversarial probe
|
||||||
|
|
||||||
|
只验证 happy path 会导致大量问题漏检。因此系统要要求验证阶段主动尝试:
|
||||||
|
- 边界输入
|
||||||
|
- 并发场景
|
||||||
|
- 空输入 / 非法输入
|
||||||
|
- 重复请求
|
||||||
|
- 不存在资源引用
|
||||||
|
|
||||||
|
## 6. 为什么验证必须可追溯
|
||||||
|
|
||||||
|
如果验证没有命令和输出,用户无法判断:
|
||||||
|
- 到底测没测
|
||||||
|
- 测了什么
|
||||||
|
- 失败在哪
|
||||||
|
|
||||||
|
因此,质量系统必须要求“证据化验证”。
|
||||||
|
|
||||||
|
## 7. 质量保证不只是测试
|
||||||
|
|
||||||
|
这套产品的质量保证包括:
|
||||||
|
- 提示词中对诚实汇报的要求
|
||||||
|
- 验证角色的独立存在
|
||||||
|
- 执行链的日志与 transcript
|
||||||
|
- 失败可追踪
|
||||||
|
- 结果可复盘
|
||||||
|
|
||||||
|
## 8. 伪代码表达
|
||||||
|
|
||||||
|
```python
|
||||||
|
class VerificationRunner:
|
||||||
|
def verify(self, task, changed_files, context):
|
||||||
|
checks = build_verification_plan(task, changed_files)
|
||||||
|
results = []
|
||||||
|
for check in checks:
|
||||||
|
cmd = check.command
|
||||||
|
output = run(cmd)
|
||||||
|
results.append(evaluate(output, check.expectation))
|
||||||
|
return summarize_verdict(results)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. 产品经理视角下的总需求句
|
||||||
|
|
||||||
|
> 产品必须把验证设计成独立、对抗性、证据化的质量系统:它不依赖实施者自我声明,而是通过命令、输出与统一 verdict 来证明任务是否真正完成。
|
||||||
112
docs/07-architecture-map.md
Normal file
112
docs/07-architecture-map.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# 07. 架构能力地图
|
||||||
|
|
||||||
|
## 1. 顶层能力区
|
||||||
|
|
||||||
|
### A. 入口层
|
||||||
|
对应需求:
|
||||||
|
- 提供 CLI 入口
|
||||||
|
- 提供初始化流程
|
||||||
|
- 提供 SDK 接入方式
|
||||||
|
- 提供 MCP 入口
|
||||||
|
|
||||||
|
### B. Prompt 编排层
|
||||||
|
对应需求:
|
||||||
|
- 动态生成系统提示词
|
||||||
|
- 注入环境信息
|
||||||
|
- 注入语言与输出风格
|
||||||
|
- 注入记忆、MCP 说明、会话局部规则
|
||||||
|
|
||||||
|
### C. 工具执行层
|
||||||
|
对应需求:
|
||||||
|
- 工具发现
|
||||||
|
- 工具输入校验
|
||||||
|
- 权限判断
|
||||||
|
- Hook 拦截
|
||||||
|
- 执行记录
|
||||||
|
- 输出回流
|
||||||
|
|
||||||
|
### D. Agent 调度层
|
||||||
|
对应需求:
|
||||||
|
- 启动子 agent
|
||||||
|
- 支持 fork / background / remote / teammate 模式
|
||||||
|
- 管理 agent 生命周期
|
||||||
|
- 管理 agent 上下文边界
|
||||||
|
|
||||||
|
### E. 扩展生态层
|
||||||
|
对应需求:
|
||||||
|
- Skills
|
||||||
|
- Plugins
|
||||||
|
- MCP
|
||||||
|
- 命令扩展
|
||||||
|
- 插件变量替换与配置注入
|
||||||
|
|
||||||
|
### F. Memory / Session 层
|
||||||
|
对应需求:
|
||||||
|
- 项目记忆
|
||||||
|
- 用户记忆
|
||||||
|
- 会话摘要
|
||||||
|
- transcript
|
||||||
|
- resume
|
||||||
|
|
||||||
|
### G. 任务与后台层
|
||||||
|
对应需求:
|
||||||
|
- 本地任务
|
||||||
|
- 后台 agent 任务
|
||||||
|
- 远程 agent 任务
|
||||||
|
- shell 任务
|
||||||
|
- 进度追踪与通知
|
||||||
|
|
||||||
|
### H. 质量保证层
|
||||||
|
对应需求:
|
||||||
|
- verification agent
|
||||||
|
- 构建 / 测试 / lint / 类型检查
|
||||||
|
- adversarial probe
|
||||||
|
- FAIL / PASS / PARTIAL 判定
|
||||||
|
|
||||||
|
### I. 界面与操作者体验层
|
||||||
|
对应需求:
|
||||||
|
- TUI 展示
|
||||||
|
- 状态栏
|
||||||
|
- 权限提示
|
||||||
|
- 任务进度
|
||||||
|
- 命令系统
|
||||||
|
- agent / skills / memory / hooks 可视化
|
||||||
|
|
||||||
|
## 2. 跨层系统性要求
|
||||||
|
|
||||||
|
### 2.1 安全要求跨层存在
|
||||||
|
- Prompt 层要提醒风险
|
||||||
|
- Tool 层要校验与限权
|
||||||
|
- Hook 层要能阻断
|
||||||
|
- UI 层要能提示用户
|
||||||
|
|
||||||
|
### 2.2 上下文管理跨层存在
|
||||||
|
- Prompt 组装要考虑动态边界
|
||||||
|
- Session 要考虑压缩与恢复
|
||||||
|
- Agent 要考虑上下文隔离
|
||||||
|
- Skills / MCP 要考虑按需注入
|
||||||
|
|
||||||
|
### 2.3 产品化要求跨层存在
|
||||||
|
- 每个子系统不仅要能工作,还要:
|
||||||
|
- 可追踪
|
||||||
|
- 可恢复
|
||||||
|
- 可扩展
|
||||||
|
- 可治理
|
||||||
|
|
||||||
|
## 3. 反推出来的组织设计
|
||||||
|
|
||||||
|
从架构地图可以反推出,这不是一个“以模型为中心”的产品,而是一个“以运行时操作系统为中心”的产品。
|
||||||
|
|
||||||
|
模型只是其中一个核心部件,真正的产品能力来自这些部分的组合:
|
||||||
|
|
||||||
|
- prompt assembly
|
||||||
|
- tool execution pipeline
|
||||||
|
- permission governance
|
||||||
|
- agent orchestration
|
||||||
|
- extension surface
|
||||||
|
- memory/session management
|
||||||
|
- verification and traceability
|
||||||
|
|
||||||
|
## 4. 产品经理视角下的总需求句
|
||||||
|
|
||||||
|
> 该产品的架构应被理解为一张能力地图:每一层都不是独立存在的功能点,而是在共同支撑一个目标——让 AI 在真实工程环境中成为一个可控、可扩展、可追踪的执行系统。
|
||||||
162
docs/08-agent-runtime-loop.md
Normal file
162
docs/08-agent-runtime-loop.md
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
# 08. Agent 运行时主循环规格
|
||||||
|
|
||||||
|
> 目标:定义 Python 版本实现时最核心的运行时循环。该文档不是源码解释,而是实现规格。
|
||||||
|
|
||||||
|
## 1. 设计目标
|
||||||
|
|
||||||
|
主循环必须满足以下目标:
|
||||||
|
|
||||||
|
1. 能持续推进任务,而不是只做一次回答
|
||||||
|
2. 能在每轮后根据工具结果重新决策
|
||||||
|
3. 能在权限阻断、上下文压缩、工具失败时继续可控运行
|
||||||
|
4. 能触发子 agent、后台任务、验证任务
|
||||||
|
5. 能生成可追踪的消息流与 transcript
|
||||||
|
|
||||||
|
## 2. 主循环的输入
|
||||||
|
|
||||||
|
一次主循环至少需要以下输入:
|
||||||
|
|
||||||
|
- `messages`: 当前消息序列
|
||||||
|
- `system_prompt`: 已组装完成的系统提示词
|
||||||
|
- `tool_registry`: 当前可用工具集合
|
||||||
|
- `tool_use_context`: 执行上下文(cwd、权限、session、任务状态)
|
||||||
|
- `memory_context`: 注入后的记忆内容
|
||||||
|
- `user_context`: 用户态上下文
|
||||||
|
- `system_context`: 系统态上下文
|
||||||
|
- `can_use_tool`: 权限判断函数
|
||||||
|
- `max_turns`: 最大回合数
|
||||||
|
- `task_budget`: 本轮任务预算(可选)
|
||||||
|
|
||||||
|
## 3. 主循环的输出
|
||||||
|
|
||||||
|
主循环每轮可能产出:
|
||||||
|
|
||||||
|
- 普通 assistant 消息
|
||||||
|
- tool_use 请求
|
||||||
|
- tool_result 回写
|
||||||
|
- progress 消息
|
||||||
|
- compact / summary 边界消息
|
||||||
|
- 子任务启动事件
|
||||||
|
- terminal state(完成 / 失败 / 中断)
|
||||||
|
|
||||||
|
## 4. 循环状态
|
||||||
|
|
||||||
|
实现时必须维护显式状态,而不是散落在局部变量中。
|
||||||
|
|
||||||
|
建议状态字段:
|
||||||
|
|
||||||
|
```python
|
||||||
|
class QueryState:
|
||||||
|
messages: list
|
||||||
|
turn_count: int
|
||||||
|
auto_compact_tracking: dict | None
|
||||||
|
pending_tool_summary: object | None
|
||||||
|
stop_hook_active: bool
|
||||||
|
max_output_recovery_count: int
|
||||||
|
has_attempted_reactive_compact: bool
|
||||||
|
transition_reason: str | None
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 标准单轮流程
|
||||||
|
|
||||||
|
每轮执行顺序建议固定为:
|
||||||
|
|
||||||
|
1. 预处理当前消息
|
||||||
|
2. 计算 token / budget 状态
|
||||||
|
3. 调用模型
|
||||||
|
4. 解析返回内容
|
||||||
|
5. 如有工具调用,进入工具执行链
|
||||||
|
6. 将工具结果追加回消息
|
||||||
|
7. 如需压缩,执行 compact
|
||||||
|
8. 决定是否继续下一轮
|
||||||
|
9. 达到终止条件则返回 terminal state
|
||||||
|
|
||||||
|
## 6. 终止条件
|
||||||
|
|
||||||
|
至少支持以下终止条件:
|
||||||
|
|
||||||
|
- 模型明确结束任务
|
||||||
|
- 达到 `max_turns`
|
||||||
|
- 用户中断
|
||||||
|
- 权限阻断且无可行替代方案
|
||||||
|
- 发生不可恢复异常
|
||||||
|
- 任务被后台化或移交
|
||||||
|
|
||||||
|
## 7. 工具调用处理要求
|
||||||
|
|
||||||
|
如果模型返回一个或多个工具调用:
|
||||||
|
|
||||||
|
- 必须按顺序或编排策略执行
|
||||||
|
- 必须将每个 tool_result 写回消息流
|
||||||
|
- 如果工具失败,失败信息也必须结构化回写
|
||||||
|
- 严禁只在 UI 层显示,不回写到模型上下文
|
||||||
|
|
||||||
|
## 8. 压缩与恢复点
|
||||||
|
|
||||||
|
主循环必须内建以下钩子点:
|
||||||
|
|
||||||
|
- 调用模型前检查是否需要压缩
|
||||||
|
- 工具结果过长时应用 result budget
|
||||||
|
- prompt 过长时触发 compact / reactive compact
|
||||||
|
- resume 时从 compact boundary 后恢复有效消息
|
||||||
|
|
||||||
|
## 9. 子 agent 与后台任务接入点
|
||||||
|
|
||||||
|
主循环必须允许以下事件打断常规路径:
|
||||||
|
|
||||||
|
- 启动 foreground subagent
|
||||||
|
- 启动 background subagent
|
||||||
|
- 接收 task notification
|
||||||
|
- 继续 resume 某个已有 agent
|
||||||
|
|
||||||
|
## 10. Python 版推荐伪代码
|
||||||
|
|
||||||
|
```python
|
||||||
|
def query_loop(params):
|
||||||
|
state = init_state(params)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if should_stop(state, params):
|
||||||
|
return terminal_result(state)
|
||||||
|
|
||||||
|
state = maybe_compact(state, params)
|
||||||
|
request = build_model_request(state, params)
|
||||||
|
response = call_model(request)
|
||||||
|
state.messages.append(response.assistant_message)
|
||||||
|
|
||||||
|
if response.tool_calls:
|
||||||
|
tool_events = run_tools(response.tool_calls, params)
|
||||||
|
state.messages.extend(tool_events)
|
||||||
|
state.transition_reason = 'tool_round'
|
||||||
|
continue
|
||||||
|
|
||||||
|
if response.should_launch_subagent:
|
||||||
|
launch_subagent(response, params)
|
||||||
|
state.transition_reason = 'subagent'
|
||||||
|
continue
|
||||||
|
|
||||||
|
if response.is_terminal:
|
||||||
|
return terminal_result(state)
|
||||||
|
|
||||||
|
state.turn_count += 1
|
||||||
|
```
|
||||||
|
|
||||||
|
## 11. 实现边界
|
||||||
|
|
||||||
|
Python 第一版务必保证:
|
||||||
|
- 单主循环是清晰、可测试的
|
||||||
|
- 每轮状态可序列化
|
||||||
|
- 每轮输出可回放
|
||||||
|
- 每轮失败可定位
|
||||||
|
|
||||||
|
不要把主循环写成大量隐式副作用的脚本式逻辑。
|
||||||
|
|
||||||
|
## 12. 验收标准
|
||||||
|
|
||||||
|
程序员实现完成后,应满足:
|
||||||
|
|
||||||
|
1. 可以连续多轮处理任务
|
||||||
|
2. 工具结果能回流并影响下一轮
|
||||||
|
3. 超长上下文会压缩而不是直接崩溃
|
||||||
|
4. 子任务可以插入主流程
|
||||||
|
5. transcript 可完整记录轮次
|
||||||
130
docs/09-message-model-and-state.md
Normal file
130
docs/09-message-model-and-state.md
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# 09. 消息模型与状态规格
|
||||||
|
|
||||||
|
## 1. 为什么消息模型必须先定义
|
||||||
|
|
||||||
|
这类 Agent 系统的核心不是“函数调用”,而是“结构化消息驱动状态变化”。如果消息模型不稳定,后面的 memory、resume、工具执行、agent 任务都会混乱。
|
||||||
|
|
||||||
|
## 2. 顶层消息类型
|
||||||
|
|
||||||
|
Python 版本建议至少定义以下消息类型:
|
||||||
|
|
||||||
|
- `system`
|
||||||
|
- `user`
|
||||||
|
- `assistant`
|
||||||
|
- `tool_use`
|
||||||
|
- `tool_result`
|
||||||
|
- `progress`
|
||||||
|
- `attachment`
|
||||||
|
- `summary`
|
||||||
|
- `compact_boundary`
|
||||||
|
- `notification`
|
||||||
|
- `tombstone`(可选,用于删除/隐藏历史消息后的链修复)
|
||||||
|
|
||||||
|
## 3. 核心消息字段
|
||||||
|
|
||||||
|
所有消息建议共享:
|
||||||
|
|
||||||
|
```python
|
||||||
|
class BaseMessage:
|
||||||
|
id: str
|
||||||
|
type: str
|
||||||
|
created_at: float
|
||||||
|
parent_id: str | None
|
||||||
|
session_id: str
|
||||||
|
```
|
||||||
|
|
||||||
|
### assistant 消息
|
||||||
|
```python
|
||||||
|
class AssistantMessage(BaseMessage):
|
||||||
|
content_blocks: list
|
||||||
|
usage: dict | None
|
||||||
|
stop_reason: str | None
|
||||||
|
```
|
||||||
|
|
||||||
|
### user 消息
|
||||||
|
```python
|
||||||
|
class UserMessage(BaseMessage):
|
||||||
|
content_blocks: list
|
||||||
|
source: str | None
|
||||||
|
```
|
||||||
|
|
||||||
|
### tool_use 消息块
|
||||||
|
```python
|
||||||
|
class ToolUseBlock:
|
||||||
|
id: str
|
||||||
|
name: str
|
||||||
|
input: dict
|
||||||
|
```
|
||||||
|
|
||||||
|
### tool_result 消息块
|
||||||
|
```python
|
||||||
|
class ToolResultBlock:
|
||||||
|
tool_use_id: str
|
||||||
|
content: str | list
|
||||||
|
is_error: bool = False
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 为什么 parent_id 很重要
|
||||||
|
|
||||||
|
消息链必须可追踪。这样才能支持:
|
||||||
|
- transcript 回放
|
||||||
|
- resume
|
||||||
|
- 历史修复
|
||||||
|
- compact boundary 后的有效链重建
|
||||||
|
|
||||||
|
## 5. progress 消息的处理原则
|
||||||
|
|
||||||
|
高频 progress 消息通常是 UI 态,而不是核心 transcript。实现时建议:
|
||||||
|
|
||||||
|
- 可以显示在界面中
|
||||||
|
- 但默认不参与核心 parent chain
|
||||||
|
- 不应污染 resume 后的模型上下文
|
||||||
|
|
||||||
|
## 6. attachment / notification 的用途
|
||||||
|
|
||||||
|
这类消息用于携带结构化系统事件,例如:
|
||||||
|
- hook 阻断
|
||||||
|
- task notification
|
||||||
|
- permission 说明
|
||||||
|
- MCP 附加上下文
|
||||||
|
|
||||||
|
不要把所有系统事件都挤进普通文本消息中。
|
||||||
|
|
||||||
|
## 7. 状态对象要求
|
||||||
|
|
||||||
|
除消息外,还要有显式状态对象:
|
||||||
|
|
||||||
|
```python
|
||||||
|
class SessionState:
|
||||||
|
session_id: str
|
||||||
|
cwd: str
|
||||||
|
current_task_id: str | None
|
||||||
|
memory_summary: str | None
|
||||||
|
active_agent_ids: list[str]
|
||||||
|
current_permission_mode: str
|
||||||
|
token_budget_state: dict | None
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. compact boundary 的需求
|
||||||
|
|
||||||
|
压缩前后必须有清晰边界,方便:
|
||||||
|
- 知道哪些历史被总结了
|
||||||
|
- resume 时只加载必要部分
|
||||||
|
- 保持消息链清晰
|
||||||
|
|
||||||
|
## 9. transcript 存储要求
|
||||||
|
|
||||||
|
transcript 至少要支持:
|
||||||
|
- 顺序追加
|
||||||
|
- 按 session 加载
|
||||||
|
- 按 agent 侧链加载
|
||||||
|
- 读取尾部摘要
|
||||||
|
- 限制超大文件读取风险
|
||||||
|
|
||||||
|
## 10. 验收标准
|
||||||
|
|
||||||
|
1. 任一轮对话都能序列化为结构化消息
|
||||||
|
2. tool_use 和 tool_result 可一一对应
|
||||||
|
3. progress 不污染核心历史链
|
||||||
|
4. 会话可按消息链恢复
|
||||||
|
5. 子 agent transcript 能独立存储
|
||||||
98
docs/10-context-management.md
Normal file
98
docs/10-context-management.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# 10. 上下文管理与压缩规格
|
||||||
|
|
||||||
|
## 1. 目标
|
||||||
|
|
||||||
|
上下文管理的目标不是“保留一切”,而是让模型在有限预算内持续拿到最关键的信息。
|
||||||
|
|
||||||
|
## 2. 必须保留的信息
|
||||||
|
|
||||||
|
优先级最高的信息包括:
|
||||||
|
|
||||||
|
1. 当前用户任务目标
|
||||||
|
2. 系统硬规则与安全约束
|
||||||
|
3. 用户明确偏好
|
||||||
|
4. 最近关键工具结果
|
||||||
|
5. 当前活跃子任务状态
|
||||||
|
6. 记忆摘要
|
||||||
|
7. 最近 compact 后的摘要
|
||||||
|
|
||||||
|
## 3. 可压缩的信息
|
||||||
|
|
||||||
|
以下信息应优先被压缩或裁剪:
|
||||||
|
|
||||||
|
- 旧的长日志
|
||||||
|
- 重复解释
|
||||||
|
- 已完成步骤的冗余细节
|
||||||
|
- 大量相似 read/search 结果
|
||||||
|
- 旧的 progress 消息
|
||||||
|
|
||||||
|
## 4. 上下文预算机制
|
||||||
|
|
||||||
|
Python 版建议同时维护:
|
||||||
|
- 粗略 token 估计
|
||||||
|
- 工具结果字符预算
|
||||||
|
- 每轮输出预算
|
||||||
|
- 全任务预算(可选)
|
||||||
|
|
||||||
|
## 5. 压缩触发条件
|
||||||
|
|
||||||
|
建议在以下情况触发:
|
||||||
|
|
||||||
|
- 请求前 token 估计超过阈值
|
||||||
|
- 工具结果总量过大
|
||||||
|
- 模型返回 prompt too long
|
||||||
|
- resume 重建会话时
|
||||||
|
|
||||||
|
## 6. 压缩策略层级
|
||||||
|
|
||||||
|
### 6.1 轻量裁剪
|
||||||
|
先裁剪:
|
||||||
|
- 重复 progress
|
||||||
|
- 冗长工具输出尾部
|
||||||
|
- 无关附件
|
||||||
|
|
||||||
|
### 6.2 摘要压缩
|
||||||
|
把旧消息浓缩成 summary message。
|
||||||
|
|
||||||
|
### 6.3 边界标记
|
||||||
|
插入 compact boundary,标记压缩点。
|
||||||
|
|
||||||
|
## 7. 子任务上下文要求
|
||||||
|
|
||||||
|
### fork 子任务
|
||||||
|
- 继承必要父上下文
|
||||||
|
- 尽量维持 cache-friendly prefix
|
||||||
|
- 子任务输出不要原样全部灌回主线程
|
||||||
|
|
||||||
|
### verification 子任务
|
||||||
|
- 需要任务目标、改动文件、实现摘要
|
||||||
|
- 不需要完整噪声过程
|
||||||
|
|
||||||
|
## 8. 工具结果预算
|
||||||
|
|
||||||
|
工具结果必须经过 budget 控制。否则:
|
||||||
|
- 长 grep
|
||||||
|
- 长 read
|
||||||
|
- 长 shell 输出
|
||||||
|
会迅速污染上下文。
|
||||||
|
|
||||||
|
## 9. Python 版伪代码
|
||||||
|
|
||||||
|
```python
|
||||||
|
def manage_context(messages, budget):
|
||||||
|
messages = drop_ephemeral_progress(messages)
|
||||||
|
messages = trim_large_tool_results(messages, budget.tool_result_chars)
|
||||||
|
|
||||||
|
if estimate_tokens(messages) > budget.max_input_tokens:
|
||||||
|
summary = summarize_old_messages(messages)
|
||||||
|
messages = build_post_compact_messages(summary, messages)
|
||||||
|
|
||||||
|
return messages
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. 验收标准
|
||||||
|
|
||||||
|
1. 长任务不会因上下文无限增长而崩溃
|
||||||
|
2. resume 后仍可恢复关键事实
|
||||||
|
3. 子任务不会把噪声大规模回灌主线程
|
||||||
|
4. 工具长输出会被预算裁剪
|
||||||
85
docs/11-task-model.md
Normal file
85
docs/11-task-model.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# 11. 任务模型与后台执行规格
|
||||||
|
|
||||||
|
## 1. 目标
|
||||||
|
|
||||||
|
任务系统负责把“一个 agent 在做什么”变成可追踪对象,而不是只存在于对话文本里。
|
||||||
|
|
||||||
|
## 2. 任务类型
|
||||||
|
|
||||||
|
Python 版建议至少支持:
|
||||||
|
|
||||||
|
- `main_session_task`
|
||||||
|
- `local_agent_task`
|
||||||
|
- `background_agent_task`
|
||||||
|
- `shell_task`
|
||||||
|
- `verification_task`
|
||||||
|
- `remote_task`(可后置)
|
||||||
|
|
||||||
|
## 3. 核心任务字段
|
||||||
|
|
||||||
|
```python
|
||||||
|
class TaskRecord:
|
||||||
|
task_id: str
|
||||||
|
type: str
|
||||||
|
session_id: str
|
||||||
|
parent_task_id: str | None
|
||||||
|
agent_id: str | None
|
||||||
|
description: str
|
||||||
|
status: str
|
||||||
|
created_at: float
|
||||||
|
started_at: float | None
|
||||||
|
finished_at: float | None
|
||||||
|
output_path: str | None
|
||||||
|
error: str | None
|
||||||
|
result_summary: str | None
|
||||||
|
progress: dict | None
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 状态机
|
||||||
|
|
||||||
|
建议统一状态:
|
||||||
|
- `pending`
|
||||||
|
- `running`
|
||||||
|
- `waiting_permission`
|
||||||
|
- `backgrounded`
|
||||||
|
- `completed`
|
||||||
|
- `failed`
|
||||||
|
- `cancelled`
|
||||||
|
- `killed`
|
||||||
|
|
||||||
|
## 5. 背景任务需求
|
||||||
|
|
||||||
|
background agent 至少要支持:
|
||||||
|
- 注册
|
||||||
|
- 更新进度
|
||||||
|
- 存储输出文件路径
|
||||||
|
- 完成时通知主线程
|
||||||
|
- 失败时通知主线程
|
||||||
|
- 支持 kill / cancel
|
||||||
|
|
||||||
|
## 6. 进度跟踪需求
|
||||||
|
|
||||||
|
任务进度至少记录:
|
||||||
|
- 工具调用次数
|
||||||
|
- token 使用量
|
||||||
|
- 最近活动
|
||||||
|
- 最近摘要
|
||||||
|
|
||||||
|
## 7. 输出文件需求
|
||||||
|
|
||||||
|
后台任务建议将结果落盘到 output file,用于:
|
||||||
|
- 主线程查看
|
||||||
|
- resume 后读取
|
||||||
|
- 审计
|
||||||
|
|
||||||
|
## 8. 通知机制需求
|
||||||
|
|
||||||
|
任务结束时,系统必须向主线程注入结构化 notification,而不是只静默完成。
|
||||||
|
|
||||||
|
## 9. 验收标准
|
||||||
|
|
||||||
|
1. 每个子 agent 都能注册为任务
|
||||||
|
2. 背景任务可查询状态
|
||||||
|
3. 任务可输出结果文件
|
||||||
|
4. 任务完成/失败会通知主线程
|
||||||
|
5. 任务可被停止
|
||||||
59
docs/12-workspace-and-isolation.md
Normal file
59
docs/12-workspace-and-isolation.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# 12. 工作区与隔离策略规格
|
||||||
|
|
||||||
|
## 1. 目标
|
||||||
|
|
||||||
|
不同 agent 不能默认拥有相同的文件系统权限。产品必须允许根据角色与场景控制工作区隔离。
|
||||||
|
|
||||||
|
## 2. 隔离模式
|
||||||
|
|
||||||
|
Python 版建议至少定义:
|
||||||
|
|
||||||
|
### 2.1 shared workspace
|
||||||
|
主线程默认工作区,共享项目目录。
|
||||||
|
|
||||||
|
### 2.2 read-only workspace
|
||||||
|
用于探索、规划、验证等只读角色。
|
||||||
|
|
||||||
|
### 2.3 temp workspace
|
||||||
|
用于临时脚本、临时测试产物。
|
||||||
|
|
||||||
|
### 2.4 worktree workspace
|
||||||
|
用于隔离修改型子任务,避免污染主分支。
|
||||||
|
|
||||||
|
### 2.5 remote workspace
|
||||||
|
用于远程执行环境(第一版可不做)。
|
||||||
|
|
||||||
|
## 3. 为什么隔离必须存在
|
||||||
|
|
||||||
|
没有隔离会导致:
|
||||||
|
- 子任务互相污染
|
||||||
|
- 验证角色破坏项目
|
||||||
|
- fork 实验影响主线程
|
||||||
|
- 风险动作边界模糊
|
||||||
|
|
||||||
|
## 4. 角色与隔离建议
|
||||||
|
|
||||||
|
- Explore Agent -> read-only
|
||||||
|
- Plan Agent -> read-only
|
||||||
|
- Verification Agent -> read-only + temp writable
|
||||||
|
- General Agent -> shared 或 worktree
|
||||||
|
- 高风险实现任务 -> worktree
|
||||||
|
|
||||||
|
## 5. 路径翻译需求
|
||||||
|
|
||||||
|
如果子任务运行在 worktree 中,而继承的上下文引用的是父工作区路径,系统需要能做路径翻译或重新读取。
|
||||||
|
|
||||||
|
## 6. 清理需求
|
||||||
|
|
||||||
|
隔离工作区结束后必须支持:
|
||||||
|
- 清理临时文件
|
||||||
|
- 清理临时目录
|
||||||
|
- 清理 worktree(如适用)
|
||||||
|
- 清理孤儿进程
|
||||||
|
|
||||||
|
## 7. 验收标准
|
||||||
|
|
||||||
|
1. 只读角色不能修改项目文件
|
||||||
|
2. 验证角色只能在 temp 目录写测试脚本
|
||||||
|
3. worktree 任务不污染主项目目录
|
||||||
|
4. 任务结束后能清理隔离资源
|
||||||
80
docs/13-failure-recovery.md
Normal file
80
docs/13-failure-recovery.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# 13. 失败处理与恢复规格
|
||||||
|
|
||||||
|
## 1. 目标
|
||||||
|
|
||||||
|
Agent 系统不能把失败当成异常边缘情况。失败是常态,系统必须有明确恢复策略。
|
||||||
|
|
||||||
|
## 2. 失败类型
|
||||||
|
|
||||||
|
建议至少区分:
|
||||||
|
|
||||||
|
- `tool_input_error`
|
||||||
|
- `permission_denied`
|
||||||
|
- `hook_blocked`
|
||||||
|
- `shell_runtime_error`
|
||||||
|
- `model_api_error`
|
||||||
|
- `prompt_too_long`
|
||||||
|
- `mcp_connect_error`
|
||||||
|
- `task_killed`
|
||||||
|
- `resume_load_error`
|
||||||
|
- `session_storage_error`
|
||||||
|
|
||||||
|
## 3. 各类失败处理原则
|
||||||
|
|
||||||
|
### tool_input_error
|
||||||
|
- 直接回写错误给模型
|
||||||
|
- 不执行真实工具
|
||||||
|
|
||||||
|
### permission_denied
|
||||||
|
- 不重复原样调用
|
||||||
|
- 引导模型调整方案
|
||||||
|
|
||||||
|
### hook_blocked
|
||||||
|
- 将阻断原因结构化返回
|
||||||
|
- 允许模型或用户后续处理
|
||||||
|
|
||||||
|
### prompt_too_long
|
||||||
|
- 触发 compact / reactive compact
|
||||||
|
- 必要时重建消息序列后重试
|
||||||
|
|
||||||
|
### model_api_error
|
||||||
|
- 保留错误记录
|
||||||
|
- 可尝试有限恢复
|
||||||
|
|
||||||
|
## 4. resume 恢复需求
|
||||||
|
|
||||||
|
系统必须支持:
|
||||||
|
- 读取 transcript
|
||||||
|
- 找到 compact boundary 后有效历史
|
||||||
|
- 恢复 session_id 与 project dir
|
||||||
|
- 恢复活跃任务或至少恢复摘要
|
||||||
|
|
||||||
|
## 5. 子任务失败需求
|
||||||
|
|
||||||
|
子任务失败后不能静默消失,必须:
|
||||||
|
- 写入任务状态
|
||||||
|
- 通知主线程
|
||||||
|
- 保留错误摘要
|
||||||
|
|
||||||
|
## 6. 恢复策略级别
|
||||||
|
|
||||||
|
### 轻恢复
|
||||||
|
- 调整输入
|
||||||
|
- 重新请求权限
|
||||||
|
- 简短重试
|
||||||
|
|
||||||
|
### 中恢复
|
||||||
|
- 压缩上下文后重试
|
||||||
|
- 重建子任务上下文
|
||||||
|
|
||||||
|
### 重恢复
|
||||||
|
- resume 会话
|
||||||
|
- 用户介入
|
||||||
|
- 终止并保留证据
|
||||||
|
|
||||||
|
## 7. 验收标准
|
||||||
|
|
||||||
|
1. 常见失败都有明确处理路径
|
||||||
|
2. 子任务失败不会丢失
|
||||||
|
3. prompt too long 可进入压缩恢复
|
||||||
|
4. 会话中断后可恢复
|
||||||
59
docs/14-configuration-system.md
Normal file
59
docs/14-configuration-system.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# 14. 配置系统规格
|
||||||
|
|
||||||
|
## 1. 目标
|
||||||
|
|
||||||
|
配置系统负责把产品的默认行为、用户偏好、项目约束和扩展能力统一管理,而不是散落在代码里。
|
||||||
|
|
||||||
|
## 2. 配置来源
|
||||||
|
|
||||||
|
Python 版建议支持以下来源:
|
||||||
|
|
||||||
|
1. 全局用户配置
|
||||||
|
2. 项目级配置
|
||||||
|
3. session 级配置
|
||||||
|
4. plugin / skill frontmatter 配置
|
||||||
|
5. 环境变量
|
||||||
|
6. CLI 参数覆盖
|
||||||
|
|
||||||
|
## 3. 配置优先级
|
||||||
|
|
||||||
|
建议优先级从高到低:
|
||||||
|
|
||||||
|
1. runtime override / CLI 参数
|
||||||
|
2. session 配置
|
||||||
|
3. 项目配置
|
||||||
|
4. 用户全局配置
|
||||||
|
5. 默认配置
|
||||||
|
|
||||||
|
## 4. 必须可配置的项目
|
||||||
|
|
||||||
|
- 默认模型
|
||||||
|
- 语言
|
||||||
|
- 输出风格
|
||||||
|
- permission mode
|
||||||
|
- hook 开关与 hook 配置
|
||||||
|
- MCP server 配置
|
||||||
|
- plugin 路径
|
||||||
|
- skill 路径
|
||||||
|
- token / task budget
|
||||||
|
- 自动 compact 开关
|
||||||
|
- transcript 持久化开关
|
||||||
|
|
||||||
|
## 5. Agent 级配置需求
|
||||||
|
|
||||||
|
每个 agent 定义建议支持:
|
||||||
|
- agent_type
|
||||||
|
- when_to_use
|
||||||
|
- allowed_tools / disallowed_tools
|
||||||
|
- model
|
||||||
|
- memory scope
|
||||||
|
- mcp_servers
|
||||||
|
- background capability
|
||||||
|
- isolation mode
|
||||||
|
|
||||||
|
## 6. 配置系统验收标准
|
||||||
|
|
||||||
|
1. 用户可在不改代码的情况下调整运行行为
|
||||||
|
2. 项目可定义局部约束
|
||||||
|
3. session 可临时覆盖配置
|
||||||
|
4. plugin / skill 可附带配置元信息
|
||||||
74
docs/15-mvp-scope.md
Normal file
74
docs/15-mvp-scope.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# 15. Python 版本 MVP 范围
|
||||||
|
|
||||||
|
## 1. 目标
|
||||||
|
|
||||||
|
避免 Python 版本一开始就无限扩张。MVP 只做能跑通核心闭环的最小系统。
|
||||||
|
|
||||||
|
## 2. MVP 必须包含
|
||||||
|
|
||||||
|
### 2.1 主循环
|
||||||
|
- 多轮 query loop
|
||||||
|
- tool call -> tool result -> next turn
|
||||||
|
|
||||||
|
### 2.2 基础工具
|
||||||
|
- read file
|
||||||
|
- write file
|
||||||
|
- edit file
|
||||||
|
- grep/search
|
||||||
|
- bash
|
||||||
|
|
||||||
|
### 2.3 基础权限系统
|
||||||
|
- allow / ask / deny
|
||||||
|
- 被拒后不原样重试
|
||||||
|
|
||||||
|
### 2.4 基础 transcript / session
|
||||||
|
- 消息持久化
|
||||||
|
- resume 会话
|
||||||
|
|
||||||
|
### 2.5 基础 memory
|
||||||
|
- 用户 / 项目记忆注入
|
||||||
|
- 简单记忆文件结构
|
||||||
|
|
||||||
|
### 2.6 基础 agent orchestration
|
||||||
|
- main agent
|
||||||
|
- 至少一个 verification agent
|
||||||
|
- 可选一个 explore / plan agent
|
||||||
|
|
||||||
|
### 2.7 基础 compact
|
||||||
|
- 超长消息时压缩历史
|
||||||
|
- 工具结果 budget 裁剪
|
||||||
|
|
||||||
|
## 3. MVP 可以后置的能力
|
||||||
|
|
||||||
|
- 完整 TUI
|
||||||
|
- remote execution
|
||||||
|
- 多人 teammate / swarm
|
||||||
|
- 高级 telemetry
|
||||||
|
- 高级 tracing
|
||||||
|
- 复杂 MCP delta 更新
|
||||||
|
- 高级 proactive / coordinator 模式
|
||||||
|
- 完整插件生态
|
||||||
|
|
||||||
|
## 4. 推荐实现顺序
|
||||||
|
|
||||||
|
1. 消息模型
|
||||||
|
2. 主循环
|
||||||
|
3. 工具执行链
|
||||||
|
4. transcript / resume
|
||||||
|
5. memory
|
||||||
|
6. verification agent
|
||||||
|
7. skills / plugin / MCP 里的最小一项
|
||||||
|
|
||||||
|
## 5. 成功标准
|
||||||
|
|
||||||
|
MVP 成功不等于“功能很多”,而是下面闭环能跑通:
|
||||||
|
|
||||||
|
> 用户给任务 -> 系统多轮推进 -> 调用工具 -> 保留上下文 -> 必要时压缩 -> 完成后给结果 -> 可恢复会话 -> 可做基本验证
|
||||||
|
|
||||||
|
## 6. 不建议在 MVP 阶段做的错误方向
|
||||||
|
|
||||||
|
- 先做复杂 UI
|
||||||
|
- 先做很多 agent 类型
|
||||||
|
- 先做大而全插件系统
|
||||||
|
- 没有 transcript / resume 就上 background task
|
||||||
|
- 没有权限系统就直接开放 bash
|
||||||
69
docs/16-python-implementation-notes.md
Normal file
69
docs/16-python-implementation-notes.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# 16. Python 实现注意事项
|
||||||
|
|
||||||
|
## 1. 实现原则
|
||||||
|
|
||||||
|
目标不是逐字复刻原实现,而是用 Python 重建同样的产品能力结构。
|
||||||
|
|
||||||
|
## 2. 模块建议
|
||||||
|
|
||||||
|
建议 Python 项目按以下模块拆:
|
||||||
|
|
||||||
|
- `runtime/`:query loop, orchestration
|
||||||
|
- `messages/`:message schemas
|
||||||
|
- `tools/`:tool registry and execution
|
||||||
|
- `permissions/`:permission engine
|
||||||
|
- `memory/`:memory loading and retrieval
|
||||||
|
- `tasks/`:background task model
|
||||||
|
- `storage/`:transcript and session persistence
|
||||||
|
- `agents/`:agent definitions
|
||||||
|
- `extensions/`:skills/plugins/mcp
|
||||||
|
- `verification/`:verification runner
|
||||||
|
|
||||||
|
## 3. 推荐先用的数据模型
|
||||||
|
|
||||||
|
优先用:
|
||||||
|
- `pydantic` / `dataclasses` 定义消息与状态
|
||||||
|
- `sqlite` 或 JSONL 先做 transcript
|
||||||
|
- 明确的 service 层代替隐式全局状态
|
||||||
|
|
||||||
|
## 4. 不要过早做的事
|
||||||
|
|
||||||
|
- 不要先优化 UI
|
||||||
|
- 不要先做复杂并发
|
||||||
|
- 不要先支持十几种 agent
|
||||||
|
- 不要把 memory 做成向量库重系统
|
||||||
|
|
||||||
|
## 5. 第一阶段的最佳目标
|
||||||
|
|
||||||
|
先做出一个:
|
||||||
|
- 结构清晰
|
||||||
|
- transcript 可回放
|
||||||
|
- message 模型稳定
|
||||||
|
- verification 能跑
|
||||||
|
- compact 能工作
|
||||||
|
|
||||||
|
的 Python core runtime。
|
||||||
|
|
||||||
|
## 6. 推荐里程碑
|
||||||
|
|
||||||
|
### Milestone 1
|
||||||
|
- message model
|
||||||
|
- query loop
|
||||||
|
- basic tools
|
||||||
|
- transcript
|
||||||
|
|
||||||
|
### Milestone 2
|
||||||
|
- permissions
|
||||||
|
- memory
|
||||||
|
- resume
|
||||||
|
- compact
|
||||||
|
|
||||||
|
### Milestone 3
|
||||||
|
- verification agent
|
||||||
|
- simple skill loading
|
||||||
|
- background local agent tasks
|
||||||
|
|
||||||
|
### Milestone 4
|
||||||
|
- MCP / plugin minimal support
|
||||||
|
- worktree isolation
|
||||||
|
- richer UI
|
||||||
97
docs/README.md
Normal file
97
docs/README.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# 产品需求文档反推总览
|
||||||
|
|
||||||
|
> 目标:基于现有源码结构,反推出这套 AI 编程产品的核心需求与产品设计,而不是复述实现细节。
|
||||||
|
|
||||||
|
## 文档原则
|
||||||
|
|
||||||
|
- 只写需求、目标、交互、约束、边界条件
|
||||||
|
- 不直接泄露原始源码实现
|
||||||
|
- 如需描述机制,只用自然语言或 Python 风格伪代码
|
||||||
|
- 文档站在产品经理 / 系统设计者视角,回答“为什么需要这个能力”
|
||||||
|
|
||||||
|
## 文档结构
|
||||||
|
|
||||||
|
1. `00-product-overview.md`
|
||||||
|
- 产品定位
|
||||||
|
- 核心用户
|
||||||
|
- 核心问题
|
||||||
|
- 顶层系统能力
|
||||||
|
|
||||||
|
2. `01-system-prompt-and-orchestration.md`
|
||||||
|
- 系统提示词层的需求
|
||||||
|
- 为什么要做动态拼装
|
||||||
|
- 为什么要做角色化 agent orchestration
|
||||||
|
|
||||||
|
3. `02-tools-permissions-and-execution.md`
|
||||||
|
- 工具系统需求
|
||||||
|
- 权限系统需求
|
||||||
|
- Hook / 执行链路 / 安全要求
|
||||||
|
|
||||||
|
4. `03-skills-plugins-mcp.md`
|
||||||
|
- Skills 需求
|
||||||
|
- Plugins 需求
|
||||||
|
- MCP 集成需求
|
||||||
|
|
||||||
|
5. `04-memory-and-session.md`
|
||||||
|
- 记忆系统需求
|
||||||
|
- Session 管理需求
|
||||||
|
- 压缩、归档、恢复、摘要需求
|
||||||
|
|
||||||
|
6. `05-commands-ui-and-operator-experience.md`
|
||||||
|
- 命令系统需求
|
||||||
|
- TUI / 状态栏 / 任务可视化需求
|
||||||
|
- 操作者体验
|
||||||
|
|
||||||
|
7. `06-verification-and-quality.md`
|
||||||
|
- 验证 agent 需求
|
||||||
|
- 质量保证需求
|
||||||
|
- 失败报告与可追溯性需求
|
||||||
|
|
||||||
|
8. `07-architecture-map.md`
|
||||||
|
- 按模块汇总产品能力地图
|
||||||
|
- 用于快速定位需求归属
|
||||||
|
|
||||||
|
9. `08-agent-runtime-loop.md`
|
||||||
|
- 主循环规格
|
||||||
|
- 多轮执行与终止条件
|
||||||
|
|
||||||
|
10. `09-message-model-and-state.md`
|
||||||
|
- 消息模型
|
||||||
|
- 会话与状态对象
|
||||||
|
|
||||||
|
11. `10-context-management.md`
|
||||||
|
- 上下文预算
|
||||||
|
- 压缩与恢复
|
||||||
|
|
||||||
|
12. `11-task-model.md`
|
||||||
|
- 任务模型
|
||||||
|
- 后台执行与通知
|
||||||
|
|
||||||
|
13. `12-workspace-and-isolation.md`
|
||||||
|
- 工作区隔离策略
|
||||||
|
- 角色与写权限边界
|
||||||
|
|
||||||
|
14. `13-failure-recovery.md`
|
||||||
|
- 失败处理
|
||||||
|
- 恢复机制
|
||||||
|
|
||||||
|
15. `14-configuration-system.md`
|
||||||
|
- 配置来源与优先级
|
||||||
|
- Agent / Session 配置项
|
||||||
|
|
||||||
|
16. `15-mvp-scope.md`
|
||||||
|
- Python MVP 范围
|
||||||
|
- 哪些先做,哪些后置
|
||||||
|
|
||||||
|
17. `16-python-implementation-notes.md`
|
||||||
|
- Python 版实现建议
|
||||||
|
- 模块划分与里程碑
|
||||||
|
|
||||||
|
## 阅读建议
|
||||||
|
|
||||||
|
如果你想快速理解这套产品:
|
||||||
|
|
||||||
|
1. 先看 `00-product-overview.md`
|
||||||
|
2. 再看 `04-memory-and-session.md`
|
||||||
|
3. 再看 `02-tools-permissions-and-execution.md`
|
||||||
|
4. 最后看 `03-skills-plugins-mcp.md` 和 `06-verification-and-quality.md`
|
||||||
96
poetry.lock
generated
Normal file
96
poetry.lock
generated
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# This file is automatically @generated by Poetry 2.3.3 and should not be changed by hand.
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorama"
|
||||||
|
version = "0.4.6"
|
||||||
|
description = "Cross-platform colored terminal text."
|
||||||
|
optional = false
|
||||||
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||||
|
groups = ["dev"]
|
||||||
|
markers = "sys_platform == \"win32\""
|
||||||
|
files = [
|
||||||
|
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||||
|
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iniconfig"
|
||||||
|
version = "2.3.0"
|
||||||
|
description = "brain-dead simple config-ini parsing"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.10"
|
||||||
|
groups = ["dev"]
|
||||||
|
files = [
|
||||||
|
{file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"},
|
||||||
|
{file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packaging"
|
||||||
|
version = "26.0"
|
||||||
|
description = "Core utilities for Python packages"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
groups = ["dev"]
|
||||||
|
files = [
|
||||||
|
{file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"},
|
||||||
|
{file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pluggy"
|
||||||
|
version = "1.6.0"
|
||||||
|
description = "plugin and hook calling mechanisms for python"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
groups = ["dev"]
|
||||||
|
files = [
|
||||||
|
{file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"},
|
||||||
|
{file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["pre-commit", "tox"]
|
||||||
|
testing = ["coverage", "pytest", "pytest-benchmark"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pygments"
|
||||||
|
version = "2.20.0"
|
||||||
|
description = "Pygments is a syntax highlighting package written in Python."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
groups = ["dev"]
|
||||||
|
files = [
|
||||||
|
{file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"},
|
||||||
|
{file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
windows-terminal = ["colorama (>=0.4.6)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest"
|
||||||
|
version = "8.4.2"
|
||||||
|
description = "pytest: simple powerful testing with Python"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
groups = ["dev"]
|
||||||
|
files = [
|
||||||
|
{file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"},
|
||||||
|
{file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""}
|
||||||
|
iniconfig = ">=1"
|
||||||
|
packaging = ">=20"
|
||||||
|
pluggy = ">=1.5,<2"
|
||||||
|
pygments = ">=2.7.2"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
lock-version = "2.1"
|
||||||
|
python-versions = ">=3.11,<4.0"
|
||||||
|
content-hash = "3970d1c7a3ef6cf46d8cd1b9b926406243dd4d6996d6ec81549cbcbf747d6b96"
|
||||||
20
pyproject.toml
Normal file
20
pyproject.toml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
[tool.poetry]
|
||||||
|
name = "ai-agent-deep-dive"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Teaching-oriented Python coding agent inspired by modern agent runtimes"
|
||||||
|
authors = ["Xiao Tan <no-reply@example.com>"]
|
||||||
|
readme = "README.md"
|
||||||
|
packages = [{ include = "agt", from = "src" }]
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = ">=3.11,<4.0"
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
pytest = "^8.3.5"
|
||||||
|
|
||||||
|
[tool.poetry.scripts]
|
||||||
|
agt = "agt.cli:main"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=1.9.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
3
src/agt/__init__.py
Normal file
3
src/agt/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from .agent import Agent, Message, Tool, ToolResult
|
||||||
|
|
||||||
|
__all__ = ["Agent", "Message", "Tool", "ToolResult"]
|
||||||
107
src/agt/agent.py
Normal file
107
src/agt/agent.py
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Message:
|
||||||
|
role: str
|
||||||
|
content: str
|
||||||
|
meta: dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ToolResult:
|
||||||
|
ok: bool
|
||||||
|
content: str
|
||||||
|
meta: dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
class Tool:
|
||||||
|
def __init__(self, name: str, description: str, handler: Callable[[dict[str, Any]], ToolResult]):
|
||||||
|
self.name = name
|
||||||
|
self.description = description
|
||||||
|
self.handler = handler
|
||||||
|
|
||||||
|
def call(self, payload: dict[str, Any]) -> ToolResult:
|
||||||
|
return self.handler(payload)
|
||||||
|
|
||||||
|
|
||||||
|
class Agent:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.messages: list[Message] = []
|
||||||
|
self.tools: dict[str, Tool] = {}
|
||||||
|
self.memory: list[str] = []
|
||||||
|
self.max_turns: int = 20
|
||||||
|
|
||||||
|
def register_tool(self, tool: Tool) -> None:
|
||||||
|
self.tools[tool.name] = tool
|
||||||
|
|
||||||
|
def add_message(self, role: str, content: str, **meta: Any) -> None:
|
||||||
|
self.messages.append(Message(role=role, content=content, meta=meta))
|
||||||
|
|
||||||
|
def remember(self, text: str) -> None:
|
||||||
|
self.memory.append(text)
|
||||||
|
|
||||||
|
def can_use_tool(self, tool_name: str) -> bool:
|
||||||
|
return tool_name in self.tools
|
||||||
|
|
||||||
|
def load_skills(self, skills_dir: str | Path) -> list[str]:
|
||||||
|
skills_path = Path(skills_dir)
|
||||||
|
if not skills_path.exists():
|
||||||
|
return []
|
||||||
|
|
||||||
|
loaded: list[str] = []
|
||||||
|
for path in sorted(skills_path.rglob("SKILL.md")):
|
||||||
|
loaded.append(path.parent.name)
|
||||||
|
return loaded
|
||||||
|
|
||||||
|
def model_step(self) -> dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"type": "message",
|
||||||
|
"content": "这是一个教学用 Agent 骨架。下一步请在 model_step() 中接入你的模型逻辑。",
|
||||||
|
}
|
||||||
|
|
||||||
|
def run(self, user_input: str) -> str:
|
||||||
|
self.add_message("user", user_input)
|
||||||
|
|
||||||
|
for turn in range(self.max_turns):
|
||||||
|
step = self.model_step()
|
||||||
|
|
||||||
|
if step["type"] == "message":
|
||||||
|
content = step["content"]
|
||||||
|
self.add_message("assistant", content, turn=turn)
|
||||||
|
return content
|
||||||
|
|
||||||
|
if step["type"] == "tool_call":
|
||||||
|
tool_name = step["tool"]
|
||||||
|
tool_input = step.get("input", {})
|
||||||
|
|
||||||
|
if not self.can_use_tool(tool_name):
|
||||||
|
error = f"Tool not allowed or not found: {tool_name}"
|
||||||
|
self.add_message("tool_result", error, ok=False, tool=tool_name)
|
||||||
|
continue
|
||||||
|
|
||||||
|
result = self.tools[tool_name].call(tool_input)
|
||||||
|
self.add_message(
|
||||||
|
"tool_result",
|
||||||
|
result.content,
|
||||||
|
ok=result.ok,
|
||||||
|
tool=tool_name,
|
||||||
|
tool_input=tool_input,
|
||||||
|
tool_meta=result.meta,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
raise ValueError(f"Unknown step type: {step['type']}")
|
||||||
|
|
||||||
|
final_text = "Agent stopped because it reached max_turns."
|
||||||
|
self.add_message("assistant", final_text)
|
||||||
|
return final_text
|
||||||
|
|
||||||
|
|
||||||
|
def echo_tool(payload: dict[str, Any]) -> ToolResult:
|
||||||
|
text = str(payload.get("text", ""))
|
||||||
|
return ToolResult(ok=True, content=f"echo: {text}")
|
||||||
47
src/agt/cli.py
Normal file
47
src/agt/cli.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .agent import Agent, Tool, echo_tool
|
||||||
|
|
||||||
|
|
||||||
|
def build_parser() -> argparse.ArgumentParser:
|
||||||
|
parser = argparse.ArgumentParser(prog="agt", description="Teaching-oriented AI agent CLI")
|
||||||
|
parser.add_argument("prompt", nargs="?", default="请开始", help="User prompt")
|
||||||
|
parser.add_argument(
|
||||||
|
"--skills-dir",
|
||||||
|
default="skills",
|
||||||
|
help="Directory containing skill folders with SKILL.md",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--list-skills",
|
||||||
|
action="store_true",
|
||||||
|
help="List discovered skills and exit",
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = build_parser()
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
agent = Agent()
|
||||||
|
agent.register_tool(Tool("echo", "Echo input text", echo_tool))
|
||||||
|
|
||||||
|
skills = agent.load_skills(Path(args.skills_dir))
|
||||||
|
if args.list_skills:
|
||||||
|
for skill in skills:
|
||||||
|
print(skill)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if skills:
|
||||||
|
agent.remember(f"loaded_skills={','.join(skills)}")
|
||||||
|
|
||||||
|
reply = agent.run(args.prompt)
|
||||||
|
print(reply)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
23
tests/test_agent.py
Normal file
23
tests/test_agent.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
from agt.agent import Agent, Tool, echo_tool
|
||||||
|
|
||||||
|
|
||||||
|
def test_agent_returns_default_message() -> None:
|
||||||
|
agent = Agent()
|
||||||
|
agent.register_tool(Tool("echo", "Echo input text", echo_tool))
|
||||||
|
|
||||||
|
result = agent.run("hello")
|
||||||
|
|
||||||
|
assert "教学用 Agent 骨架" in result
|
||||||
|
assert agent.messages[0].role == "user"
|
||||||
|
assert agent.messages[-1].role == "assistant"
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_skills_finds_skill_md(tmp_path) -> None:
|
||||||
|
skill_dir = tmp_path / "skills" / "writing"
|
||||||
|
skill_dir.mkdir(parents=True)
|
||||||
|
(skill_dir / "SKILL.md").write_text("# writing", encoding="utf-8")
|
||||||
|
|
||||||
|
agent = Agent()
|
||||||
|
skills = agent.load_skills(tmp_path / "skills")
|
||||||
|
|
||||||
|
assert skills == ["writing"]
|
||||||
30
tests/test_cli.py
Normal file
30
tests/test_cli.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
from subprocess import run
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
|
|
||||||
|
def test_cli_lists_skills(tmp_path) -> None:
|
||||||
|
skill_dir = tmp_path / "skills" / "analysis"
|
||||||
|
skill_dir.mkdir(parents=True)
|
||||||
|
(skill_dir / "SKILL.md").write_text("# analysis", encoding="utf-8")
|
||||||
|
|
||||||
|
result = run(
|
||||||
|
[
|
||||||
|
sys.executable,
|
||||||
|
"-m",
|
||||||
|
"agt.cli",
|
||||||
|
"--skills-dir",
|
||||||
|
str(tmp_path / "skills"),
|
||||||
|
"--list-skills",
|
||||||
|
],
|
||||||
|
cwd=ROOT,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.returncode == 0
|
||||||
|
assert "analysis" in result.stdout
|
||||||
Reference in New Issue
Block a user