yier-agents
Python Agent 系统,参考 OpenCode 架构设计,支持工具调用、子 Agent 和基于 finish_reason 的循环控制。
版本更新与不兼容变更见 CHANGELOG · v0.4.0。changelog 随源码包发布,并包含在 wheel 的 yier_agents-0.4.0.data/data/share/yier_agents/CHANGELOG.md 中(安装后位于 Python 环境的 share/yier_agents/CHANGELOG.md)。
特性
- ✅ finish_reason 驱动循环 - 自动处理工具调用和 token 限制
- ✅ Pydantic 参数验证 - 类型安全的工具定义
- ✅ ToolMiddleware - 统一的工具管理和错误处理
- ✅ SubAgent 支持 - Agent 可以作为工具被其他 Agent 调用
- ✅ 并发工具执行 - 使用 asyncio.gather 并行执行
- ✅ OpenAI-compatible API - 支持任何兼容 OpenAI 格式的 LLM
- ✅ 内置 Todo 工具 - AI 自动管理任务列表和进度跟踪
- ✅ Verbose 调试模式 - 美观的彩色输出,实时查看执行过程
- ✅ MCP 支持(可选依赖)- 通过
MCPClient连接任意 MCP server,自动转换为 Tool 实例,支持 stdio 和 SSE 两种传输 - ✅ 会话记忆 - 自动保存和加载会话上下文,支持多轮对话
- ✅ Skill 支持 - 通过
Skill打包 system prompt 和 tools,复用 Agent 能力 - ✅ 上下文压缩 - 长对话可自动摘要旧消息,保留最近上下文
- ✅ 可持久化记忆 - 通过
JSONSessionStore将 session memory 持久化到磁盘 - ✅ 思考过程支持 - 支持 reasoning_content 字段,兼容 o1、Claude thinking 等模型
- ✅ 交互式 CLI 助手 - 内置
yier-cli,支持会话记忆、skills、MCP 和工作区工具 - ✅ PTY 终端与任务排队 - 长任务可后台执行,完成后自动提醒并触发后续 follow-up
宿主应用集成
已针对 Bubu 的集成反馈重构公共 API:MCP 内存配置与租约、公开协议模型和工具适配器、JSON Schema 本地校验、统一 Agent 执行循环、真实 SSE token 流、取消/deadline、中间件与动态工具,以及异步会话存储适配和原子文件写入。
本轮进一步统一了事件信封与结果载荷、宿主日志、显式模型配置和 token 预算压缩,迁移见 宿主契约加固。
完整示例、行为约定与兼容性变化见 宿主集成与迁移说明。其中 MCPManager() 默认空配置,文件模式需显式传 config_dir;同名工具默认报错;LLM 库层不再读取 .env 或弹出配置询问。
安装
# 使用 uv
uv pip install -e .
# 或使用 pip
pip install -e .
可选依赖:MCP
MCP 支持是可选的,需要额外安装:
pip install "yier_agents[mcp]"
之所以拆成 extra,是因为 mcp 包会带进 starlette、uvicorn、pyjwt、jsonschema 等一大串传递依赖,而它们经常和宿主应用已锁定的版本冲突。不装也不影响其它功能——import yier_agents 照常可用,只是真正去连接 MCP server 时会提示你安装。
打包与发版
推荐通过 Makefile 管理版本和构建产物,这样每次打包前都会先清掉旧的 dist/、build/ 和 *.egg-info,避免安装到旧 wheel,或者看到过期的 METADATA。
# 查看当前版本
make version
# 只在本地完成 patch 发版准备
make tag-release-patch
# 本地准备后顺序推送分支和 tag
make publish-release-patch
常用命令:
make build- 清理旧产物后重新生成 wheel 和 sdist,并执行twine checkmake metadata- 查看最新 wheel 中的METADATAmake pkg-info- 查看最新 sdist 中的PKG-INFOmake install-wheel- 使用uv pip install --reinstall安装最新 wheel,避免同版本安装时残留旧元数据make tag-release-patch- 顺序执行 bump、build、test、commit 和 tag,但不推送make publish-release-patch- 在tag-release-patch基础上继续 push 当前分支和版本 tag
如果你刚改了 README.md、依赖或项目描述,建议至少跑一遍:
make build
make metadata
如果你要正式发版,推荐直接走 publish-release-*,这样 commit、tag 和 push 都在同一个顺序化流程里,不容易再出现 tag 指到错误提交的问题。
快速开始
import asyncio
from pydantic import BaseModel, Field
from yier_agents import Agent, LLM, Tool, ToolContext, ToolOutput
# 1. 定义工具参数
class ReadParams(BaseModel):
path: str = Field(description="文件路径")
# 2. 定义工具执行函数
async def read(params: ReadParams, ctx: ToolContext) -> ToolOutput:
with open(params.path) as f:
content = f.read()
return ToolOutput(content=f"File content:\n{content}")
# 3. 创建工具
read_tool = Tool(
name="read",
description="读取文件内容",
parameters=ReadParams,
execute=read
)
# 4. 创建 Agent
llm = LLM(
base_url="https://api.openai.com/v1",
api_key="your-key",
model="gpt-4"
)
agent = Agent(
llm=llm,
tools=[read_tool],
system_prompt="你是一个文件助手",
verbose=True # 启用详细的调试输出
)
# 5. 运行
result = await agent.run("读取 README.md 并总结")
print(result.final_message)
模型接入仅使用 OpenAI-compatible Chat Completions 接口,配置为 base_url、api_key 和 model:
from yier_agents import LLM
llm = LLM(
base_url="https://gateway.example/v1",
api_key="your-key",
model="your-model-id",
)
# 或显式从环境变量加载:
llm = LLM.from_env()
from_env() 读取 YIER_BASE_URL、YIER_API_KEY、YIER_MODEL,显式参数优先于环境值。模型 ID 原样发送,不做厂商推断或前缀转换。使用完调用 await llm.aclose(),或使用 async with LLM(...)。
调试模式
使用 verbose=True 参数可以查看 Agent 执行的详细过程,方便调试:
agent = Agent(
llm=llm,
tools=[tool1, tool2],
verbose=True # 启用详细输出
)
result = await agent.run("执行任务", "session-id")
输出示例:
ℹ ============================================================
ℹ Starting Agent execution (session: session-i...)
ℹ ============================================================
ℹ Added system prompt
ℹ User: 执行任务
ℹ ────────────────────────────────────────────────────────────
ℹ Iteration 1/20
ℹ ────────────────────────────────────────────────────────────
ℹ Calling LLM...
✓ LLM response received (finish_reason: tool_calls, usage: 150 tokens)
⚙ Executing 2 tool call(s)...
⚙ [1/2] read({"path": "README.md"})
⚙ [2/2] grep({"pattern": "Agent"})
✓ Received 2 tool result(s)
⚙ [1] File content: ...
⚙ [2] Found 5 matches
ℹ ────────────────────────────────────────────────────────────
ℹ Iteration 2/20
ℹ ────────────────────────────────────────────────────────────
ℹ Calling LLM...
✓ LLM response received (finish_reason: stop, usage: 200 tokens)
ℹ Content: 任务已完成
✓ Agent completed successfully
ℹ ============================================================
详见 verbose 示例。
架构
Agent (执行循环)
├─> LLM (API 调用层)
├─> ToolMiddleware (工具管理和验证)
│ ├─> Tool (工具定义)
│ └─> Context (执行上下文)
├─> MCPClient (MCP server 连接)
│ ├─> stdio transport (子进程)
│ └─> SSE transport (HTTP)
└─> Message (消息管理)
Skills
Skill 用来打包一组工具和附加的 system prompt,适合构建你自己的可组合 Agent 能力层。
from yier_agents import Agent, LLM, Skill
network_skill = Skill(
name="network-basics",
description="Basic network utilities",
tools=[get_ip_tool],
system_prompt="Use get_ip when the user asks about the local machine IP."
)
agent = Agent(
llm=llm,
tools=[],
skills=[network_skill],
system_prompt="你是一个轻量但可靠的终端助手"
)
Lazy Skills
除了静态 Skill 之外,现在也支持 OpenCode 风格的按需加载 skills。它们从 SKILL.md 目录结构中发现,在系统提示里只暴露 <available_skills> 列表,真正需要时再通过内置 skill_load 工具加载完整内容。
支持的项目级目录:
.opencode/skill/<name>/SKILL.md.opencode/skills/<name>/SKILL.md.claude/skills/<name>/SKILL.md.agents/skills/<name>/SKILL.md
from pathlib import Path
from yier_agents import Agent, LLM, SkillCatalog
catalog = SkillCatalog.discover(
Path.cwd(),
include_project=True,
include_global=False,
)
agent = Agent(
llm=LLM.from_env(),
tools=[],
skill_catalog=catalog,
system_prompt="你是一个支持 lazy skills 的编程助手",
)
如果某个 skill 目录里包含 scripts/ 或 references/, skill_load 会把 skill 正文、base directory 和采样文件列表注入上下文。脚本不会自动执行,而是由后续普通工具去读取或运行。
内置文件/命令工具:
交互式 CLI 默认使用兼容 pi coding-agent 的短工具名,模型看到的是 read、bash、edit、write、grep、find 和 ls:
read- 读取文件内容并返回行号bash- 在工作区执行 shell 命令edit- 对文件做精确文本替换write- 创建或更新文件grep- 按文本或正则搜索文件内容,支持 glob、上下文行和大小写选项find- 按 glob 查找文件和目录ls- 浏览目录内容
Python API 使用同一套命名:create_read_tool、create_bash_tool、create_edit_tool、create_write_tool、create_grep_tool、create_find_tool、create_ls_tool。旧工厂和旧工具单例已移除,迁移对照见 CHANGELOG。
from pathlib import Path
from yier_agents import Agent, create_read_tool, create_grep_tool, create_find_tool
root = Path.cwd()
agent = Agent(
llm=llm,
tools=[factory([root], default_root=root) for factory in (
create_read_tool, create_grep_tool, create_find_tool,
)],
)
ToolOutput 结构
所有内置工具都会返回同一个 ToolOutput 结构:
content: 给模型和 CLI 看的可读摘要metadata: 轻量摘要字段,适合列表、状态栏和简单 UI 绑定raw: 给 UI 和外部调用方看的结构化原始载荷
推荐的消费顺序是:
- 用
raw驱动 UI 展示和交互 - 用
metadata做轻量状态判断或列表摘要 - 把
content当作日志、回退展示或给模型继续阅读的文本
result = await tool.execute(params, ctx)
print(result.content)
print(result.metadata)
print(result.raw)
raw.kind 是最重要的分流字段。当前内置工具大致会返回这些类型:
shell_command:bashterminal:terminal(窗口状态、增量输出、屏幕和等待结果)file_read:readfile_list:lsfile_write:writegrep:grepfind:findfile_replace:edittodo_list:todo_readtodo_update:todo_writeskill_load:skill_loadmcp_tool_result: 通过MCPClient代理的 MCP 工具subagent_result:AgentToolqueued_followup:queue_terminal_followupnetwork_lookup:get_ip
Shell / Terminal
bash 返回 raw.kind="shell_command",包含 request、process、events 和 streams。运行期间通过 ToolCallUpdateEvent 实时推送 stdout/stderr;支持取消、可选超时与进程组清理,CLI 可用 Ctrl-C 停止当前运行。输出截断保留尾部,完整日志路径见 full_output_path。默认无超时,非零退出和超时会标为工具错误。完整接入见 Bash。
terminal 返回 raw.kind="terminal"。窗口操作返回 window、name、running、state、exit_code、尺寸和标题;读取另有 output、绝对字符游标 offset、丢弃字符数 skipped、truncated、waited 和 timed_out。PTY 将 stdout/stderr 合并为同一个有序输出流。
完整使用说明与迁移见 Terminal。
File / Todo / Skill / MCP
其他工具也在 raw 中提供结构化结果:文件读取的 lines、目录列表的 entries、grep 的 matches、find 的 paths、Todo 的 todos、Skill 的 sampled_files、MCP 的 contents 和子 Agent 的 final_message。宿主 UI 可以直接使用这些字段。
会话消息调度
库提供 SessionController:steer() 在当前模型请求及工具批次完成后注入,不打断工具;queue() 在当前整轮完成后依次启动新回合。支持消息 ID、状态事件、撤回、清空和取消后的显式恢复。CLI 运行中普通输入默认 steer,/queue <消息> 排下一轮,/messages 查看,/withdraw <id> 撤回,/resume 恢复。终端完成触发改用 /after <窗口> <消息>。
宿主示例、边界及异常语义见 SESSION_CONTROL.md。
Session Memory
默认 memory 仍可直接使用,但现在也支持持久化 store 和上下文压缩。
from pathlib import Path
from yier_agents import Agent, CompactionConfig, JSONSessionStore, LLM
agent = Agent(
llm=LLM.from_env(),
tools=[],
session_store=JSONSessionStore(Path(".agent_storage/sessions")),
compaction_config=CompactionConfig(
enabled=True,
trigger_message_count=24,
preserve_recent_messages=8,
),
)
Interactive Assistant CLI
如果你想直接启动一个交互式个人助手,而不是自己写一层循环,可以直接运行包内 CLI:
python -m yier_agents.yier_cli
安装为可执行脚本后也可以直接运行:
yier-cli
CLI 读取 .env 和 YIER_BASE_URL、YIER_API_KEY、YIER_MODEL。交互式启动时仅询问缺失的地址、密钥或模型 ID;非交互模式缺少配置会立即报错。
这个入口默认启用:
- 会话持久化记忆
- 长上下文摘要压缩
- lazy skills 发现与
/skills浏览 - 受限的文件、搜索、写入与命令工具
- PTY 交互终端和 follow-up 排队
- MCP 配置热重载
常用交互命令:
/skills [query]- 查看或筛选已发现的 skills/ps [running|all]- 查看终端窗口/tail <session_id>- 查看终端窗口最近输出/wait <session_id> [seconds]- 等待终端退出一小段时间/stop <session_id>- 关闭终端窗口/after [list|<session_id> <message>]- 给终端挂一个完成后自动执行的 follow-up
一个典型流程是:
- 让 Agent 调用
terminal(command="new-window", shell_command="npm run dev", name="dev")。 - 通过
terminal的read、send-keys、capture-pane和kill-window继续控制窗口。 - 用
/after 1 总结结果并告诉我下一步,或者让 Agent 调用queue_terminal_followup。 - 终端退出后,CLI 将完成通知和输出摘要注入下一次 Agent 回合,并执行已排队的 follow-up。
也可以直接操作:/terminal new-window -n dev 'npm run dev'、/terminal send-keys -t dev C-c、/terminal capture-pane -t dev。/ps、/tail、/wait 和 /stop 继续作为终端窗口的快捷命令。
可以在 .yier.json 里为 CLI 覆盖这些默认值。默认行为和现在一致,只是搬进了配置层:
{
"assistant": {
"workspaceRoot": ".",
"sessionStoragePath": ".agent_storage/sessions",
"promptHistoryPath": ".agent_storage/prompt_history.txt",
"allowedRoots": [
".",
".agent_storage/sessions"
],
"includeSkillDirectoriesInAllowedRoots": true,
"maxIterations": 100,
"runCommand": {
"allowShell": false,
"shellProgram": "/bin/bash"
},
"compaction": {
"enabled": true,
"contextWindow": 32768,
"reserveTokens": 4096,
"keepRecentTokens": 8192,
"summaryMaxTokens": 1024
}
}
}
create_bash_tool() 默认允许 shell 语法;CLI 仍遵循配置,runCommand.allowShell 默认为 false,此时 bash 不支持 |、重定向和链式 shell 语法;这是为了避免把普通命令执行提升成完整 shell。只有在你明确开启它时,才会用 shell 模式执行。
shellProgram 是可选覆盖项:类 Unix 系统默认使用 /bin/bash,Windows 默认使用当前系统的 ComSpec。terminal 使用相同的 shellProgram,通过 PTY 始终运行真实 shell,独立于 runCommand.allowShell。终端支持 Linux、macOS 和 WSL;Windows 原生暂不提供 ConPTY 后端。
MCP 支持
使用前请先安装可选依赖:
pip install "yier_agents[mcp]"
通过 MCPClient 连接任意 MCP server,所有工具自动转换为 Tool 实例,与本地工具用法完全一致。
基于 mcp 2.x SDK:自动协商协议版本——server 支持 2026-07-28 新协议时走 server/discover 无握手连接,否则回退到传统 initialize 握手,stdio / SSE / Streamable HTTP 三种传输都兼容。
from yier_agents import MCPClient, Agent, LLM
llm = LLM.from_env()
# stdio 方式(本地子进程)
async with MCPClient.stdio("npx", ["-y", "@primevue/mcp"]) as client:
tools = await client.get_tools()
agent = Agent(llm=llm, tools=tools)
result = await agent.run("DataTable 有哪些常用 props?", session_id="s1")
# SSE 方式(HTTP 远程)
async with MCPClient.sse("http://localhost:8000/sse") as client:
tools = await client.get_tools()
agent = Agent(llm=llm, tools=tools)
result = await agent.run("执行任务", session_id="s2")
# 多个 MCP server 混合使用
async with (
MCPClient.stdio("npx", ["-y", "@primevue/mcp"]) as mcp1,
MCPClient.sse("http://localhost:8000/sse") as mcp2,
):
tools = await mcp1.get_tools() + await mcp2.get_tools()
agent = Agent(llm=llm, tools=tools)
一次连接,多个 Agent 共享:
async with MCPClient.stdio("npx", ["-y", "@primevue/mcp"]) as client:
tools = await client.get_tools() # 只连接一次
agent1 = Agent(llm=llm, tools=tools, system_prompt="你是前端专家")
agent2 = Agent(llm=llm, tools=tools, system_prompt="你是无障碍专家")
# 两个 agent 共享同一个 MCP 连接
r1, r2 = await asyncio.gather(
agent1.run("DataTable 怎么分页?", "session-1"),
agent2.run("Button 的无障碍属性?", "session-2"),
)
详见 MCP 示例。
SubAgent 示例
# 创建专门的子 agent
search_agent = Agent(
llm=llm,
tools=[grep_tool, read_tool],
system_prompt="你是代码搜索专家"
)
# 包装为工具
from yier_agents import AgentTool
search_tool = AgentTool(
name="search_codebase",
description="搜索和分析代码",
agent=search_agent
)
# 主 agent 可以调用
main_agent = Agent(
llm=llm,
tools=[search_tool, edit_tool]
)
result = await main_agent.run("重构这个模块")
Todo 工具
内置的任务跟踪工具,让 AI 自动管理多步骤任务的进度。
from yier_agents import Agent, LLM, TodoWriteTool, TodoReadTool
# 创建带 todo 工具的 agent
agent = Agent(
llm=llm,
tools=[
TodoWriteTool, # AI 可以创建和更新任务列表
TodoReadTool, # AI 可以查看当前进度
# ... 其他工具
],
system_prompt="对于多步骤任务,使用 todo_write 工具跟踪进度。"
)
# AI 会自动使用 todo 工具管理任务
result = await agent.run("实现用户认证系统,包括登录、注册和密码重置")
AI 自动管理 todos:
- 接收多步骤任务时自动创建 todo 列表
- 开始每个任务前标记为
in_progress - 完成后标记为
completed - 实时跟踪进度
Todo 状态:
pending⏸ - 待执行in_progress▶ - 执行中completed✓ - 已完成
开发
# 运行测试
pytest tests/ -v
# 运行特定测试
pytest tests/test_agent.py -v
# 查看覆盖率
pytest --cov=doc_agents tests/
设计文档
详细设计请参考:
License
MIT
Metadata
Release files for yier-agents 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| yier_agents-0.4.0.tar.gz | 198.2 kB | With provenance | Details for yier_agents-0.4.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| yier_agents-0.4.0-py3-none-any.whl | Python 3 | none | any | Details for yier_agents-0.4.0-py3-none-any.whl |
Total release size: 375.4 kB
Release files / yier_agents-0.4.0.tar.gz
| Download URL | yier_agents-0.4.0.tar.gz |
|---|---|
| Size | 198.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
73dcbc103a922d700b6ab8d3fb210d4783e789662eb9060693fa3db6c6206409
|
|
BLAKE2b-256 checksum How to use checksums |
b9f3c5ac7c2c04e54981319aae441c1c7efb0303f08991dabf29c35304b6ff1f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logRelease files / yier_agents-0.4.0-py3-none-any.whl
| Download URL | yier_agents-0.4.0-py3-none-any.whl |
|---|---|
| Size | 177.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
adfbc37c9b997738a8f60e2e62493cfbb40d9d6f6b24cf69f63d109210c27dc5
|
|
BLAKE2b-256 checksum How to use checksums |
a788d03c96384426a856a3fffa35d4404bfa8b1ed9dd03a1f154378755c57d63
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logSecurity for yier-agents 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for yier_agents-0.4.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.