Skip to main content

Lightweight Python agents with tools, memory, MCP, and lazy skills.

Project description

yier-agents

Python Agent 系统,参考 OpenCode 架构设计,支持工具调用、子 Agent 和基于 finish_reason 的循环控制。

特性

  • 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 和工作区工具
  • 后台命令与任务排队 - 长任务可后台执行,完成后自动提醒并触发后续 follow-up

安装

# 使用 uv
uv pip install -e .

# 或使用 pip
pip install -e .

打包与发版

推荐通过 Makefile 管理版本和构建产物,这样每次打包前都会先清掉旧的 dist/build/*.egg-info,避免安装到旧 wheel,或者看到过期的 METADATA

# 查看当前版本
make version

# 只在本地完成 patch 发版准备
make tag-release-patch

# 本地准备后顺序推送分支和 tag
make publish-release-patch

常用命令:

  • make build - 清理旧产物后重新生成 wheel 和 sdist,并执行 twine check
  • make metadata - 查看最新 wheel 中的 METADATA
  • make pkg-info - 查看最新 sdist 中的 PKG-INFO
  • make 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-*,这样 committagpush 都在同一个顺序化流程里,不容易再出现 tag 指到错误提交的问题。

快速开始

import asyncio
from pydantic import BaseModel, Field
from yier_agents import Agent, LLM, Tool, ToolContext, ToolOutput


# 1. 定义工具参数
class ReadFileParams(BaseModel):
    path: str = Field(description="文件路径")


# 2. 定义工具执行函数
async def read_file(params: ReadFileParams, 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_file",
    description="读取文件内容",
    parameters=ReadFileParams,
    execute=read_file
)

# 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)

如果你用的是 Z.AI,也可以直接使用内置 provider preset,不必自己记接口地址:

from yier_agents import LLM


llm = LLM(
    provider="zai",
    model="glm-4.7-flash",
)

# 如果你订阅了 Z.AI Coding Plan:
llm = LLM(
    provider="zai-coding-plan",
    model="glm-4.7-flash",
)

默认会读取这些环境变量:

  • YIER_PROVIDER - 可选,支持 zaizai-coding-plan
  • YIER_MODEL - 模型名,例如 glm-4.7-flash
  • ZHIPU_API_KEY - Z.AI / 智谱 API key
  • YIER_BASE_URLYIER_API_KEY - 通用 OpenAI-compatible 覆盖项

如果你历史上已经在用 z-ai/glm-4.7-flash 这种写法,现在也兼容,发送请求时会自动归一化为 Z.AI 原生 model id。

调试模式

使用 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_file({"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(model="gpt-4"),
    tools=[],
    skill_catalog=catalog,
    system_prompt="你是一个支持 lazy skills 的编程助手",
)

如果某个 skill 目录里包含 scripts/references/, skill_load 会把 skill 正文、base directory 和采样文件列表注入上下文。脚本不会自动执行,而是由后续普通工具去读取或运行。

内置文件/命令工具:

  • list_files - 浏览 skill 目录或项目目录
  • read_file - 读取脚本、参考资料和配置文件
  • replace_in_file - 做精确文本替换式编辑
  • write_file - 在允许的工作区内创建或更新文件
  • run_command - 执行 skill 中提到的脚本或命令
  • search_files - 进行 grep-like 文本搜索

ToolOutput 结构

所有内置工具都会返回同一个 ToolOutput 结构:

  • content: 给模型和 CLI 看的可读摘要
  • metadata: 轻量摘要字段,适合列表、状态栏和简单 UI 绑定
  • raw: 给 UI 和外部调用方看的结构化原始载荷

推荐的消费顺序是:

  1. raw 驱动 UI 展示和交互
  2. metadata 做轻量状态判断或列表摘要
  3. content 当作日志、回退展示或给模型继续阅读的文本
result = await tool.execute(params, ctx)

print(result.content)
print(result.metadata)
print(result.raw)

raw.kind 是最重要的分流字段。当前内置工具大致会返回这些类型:

  • shell_command: run_command
  • background_command: start_background_commandread_background_commandwait_background_commandstop_background_commandsend_background_command_input
  • file_read: read_file
  • file_list: list_files
  • file_write: write_file
  • file_search: search_files
  • file_replace: replace_in_file
  • todo_list: todo_read
  • todo_update: todo_write
  • skill_load: skill_load
  • mcp_tool_result: 通过 MCPClient 代理的 MCP 工具
  • subagent_result: AgentTool
  • queued_followup: queue_background_followup
  • network_lookup: get_ip

Shell / Background Command

run_commandstart_background_commandread_background_commandwait_background_commandstop_background_commandsend_background_command_input 都会在 raw 里暴露统一结构:

  • raw.kind: shell_commandbackground_command
  • raw.request: 命令、工作目录、shell 配置等请求参数
  • raw.process: 状态、退出码、时间戳、运行时长
  • raw.events: 终端事件流,包含 startedstdoutstderrstdinstate_changedexit
  • raw.latest_event_index: 最新事件索引,适合增量轮询
  • raw.streams.stdout / raw.streams.stderr: 当前聚合输出快照
  • raw.events_truncated / raw.dropped_event_count: 事件缓冲是否发生截断

如果你在 UI 里想做更像真实 shell 的展示,优先消费 raw["events"]; content 更适合直接展示给模型或做日志摘要。

result = await run_tool.execute(
    run_tool.parameters(command="python3 demo.py", cwd="."),
    ctx,
)

print(result.content)
print(result.metadata)
print(result.raw["events"])
print(result.raw["streams"]["stdout"]["text"])

File / Todo / Skill / MCP

除了 shell 类工具,其他工具也会在 raw 里给出稳定结构,避免 UI 再去解析 content:

  • file_read.raw.lines: 带行号的读取结果
  • file_list.raw.entries: 目录项列表
  • file_search.raw.matches: 搜索命中,包含路径、行号和文本
  • todo_list.raw.todos / todo_update.raw.todos: 完整 todo 列表
  • skill_load.raw.sampled_files: skill 采样文件
  • mcp_tool_result.raw.contents: MCP 原始内容项,按 text / binary / resource / unknown 归类
  • subagent_result.raw.final_message: 子 agent 的最终消息和摘要信息

这些 raw 结构会随着工具类型变化,但都遵循同一个原则: content 面向模型, metadata 面向轻量状态, raw 面向 UI。

Session Memory

默认 memory 仍可直接使用,但现在也支持持久化 store 和上下文压缩。

from pathlib import Path
from yier_agents import Agent, CompactionConfig, JSONSessionStore, LLM

agent = Agent(
    llm=LLM(model="gpt-4"),
    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

如果启动时还没有配置 YIER_PROVIDERYIER_BASE_URL,CLI 会先给一个快捷选择器,默认就是 zai-coding-plan。这时只需要输入模型名,例如 glm-4.7-flash,再提供 ZHIPU_API_KEY 即可。

这个入口默认启用:

  • 会话持久化记忆
  • 长上下文摘要压缩
  • lazy skills 发现与 /skills 浏览
  • 受限的文件、搜索、写入与命令工具
  • 后台命令管理和 follow-up 排队
  • MCP 配置热重载

常用交互命令:

  • /skills [query] - 查看或筛选已发现的 skills
  • /ps [running|all] - 查看后台任务
  • /tail <session_id> - 查看后台任务最近输出
  • /wait <session_id> [seconds] - 等待某个后台任务一小段时间
  • /stop <session_id> - 停止后台任务
  • /queue [list|<session_id> <message>] - 给后台任务挂一个完成后自动执行的 follow-up

一个典型流程是:

  1. 让 Agent 用 start_background_command 在后台启动构建、测试、抓取或同步任务。
  2. 继续和 CLI 聊别的事情,不阻塞当前对话。
  3. /queue bg-1 总结结果并告诉我下一步,或者让 Agent 调用 queue_background_followup
  4. 后台任务完成后,CLI 会提示它已结束,并把输出摘要注入到下一次 Agent 回合里;如果已经排了 follow-up,会自动继续执行。

对应的内置后台工具有:

  • start_background_command
  • list_background_commands
  • read_background_command
  • wait_background_command
  • stop_background_command
  • send_background_command_input
  • queue_background_followup

可以在 .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,
      "triggerMessageCount": 24,
      "preserveRecentMessages": 8,
      "summaryMaxTokens": 512
    }
  }
}

runCommand.allowShell 默认为 false,所以 run_command 默认不支持 |、重定向和链式 shell 语法;这是为了避免把普通命令执行提升成完整 shell。只有在你明确开启它时,才会用 shell 模式执行。

shellProgram 是可选覆盖项:类 Unix 系统默认使用 /bin/bash,Windows 默认使用当前系统的 ComSpec。同样地,start_background_command 也遵循这套配置。

MCP 支持

通过 MCPClient 连接任意 MCP server,所有工具自动转换为 Tool 实例,与本地工具用法完全一致。

from yier_agents import MCPClient, Agent, LLM

llm = LLM(model="gpt-4")

# 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 ✓ - 已完成

详见 Todo 工具文档示例

开发

# 运行测试
pytest tests/ -v

# 运行特定测试
pytest tests/test_agent.py -v

# 查看覆盖率
pytest --cov=doc_agents tests/

设计文档

详细设计请参考:

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

yier_agents-0.1.7.tar.gz (91.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

yier_agents-0.1.7-py3-none-any.whl (71.3 kB view details)

Uploaded Python 3

File details

Details for the file yier_agents-0.1.7.tar.gz.

File metadata

  • Download URL: yier_agents-0.1.7.tar.gz
  • Upload date:
  • Size: 91.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for yier_agents-0.1.7.tar.gz
Algorithm Hash digest
SHA256 ed90d43beb09074096f398bdb6780e41d97ce8fa7f5cabc4753f7a2f71e5009b
MD5 c5521d4a6d4fe224be3773fcba8aebf5
BLAKE2b-256 191a19a93f5b26cfc1475f8f9c1480ce33b5f45f918aefd826c3bfc087c7dafc

See more details on using hashes here.

Provenance

The following attestation bundles were made for yier_agents-0.1.7.tar.gz:

Publisher: publish.yml on Sube-py/yier_agents

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file yier_agents-0.1.7-py3-none-any.whl.

File metadata

  • Download URL: yier_agents-0.1.7-py3-none-any.whl
  • Upload date:
  • Size: 71.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for yier_agents-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 7867feb98a7017ffb6ddd476043768ab3275678b1925fd6d3ba7a9b2bc988eed
MD5 954f0ec37e15d48d0b4d9432791e1cc5
BLAKE2b-256 a010064b84ace38d5c7156d165a66b379ddf7441f07ae4e9409b69040eb6e945

See more details on using hashes here.

Provenance

The following attestation bundles were made for yier_agents-0.1.7-py3-none-any.whl:

Publisher: publish.yml on Sube-py/yier_agents

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page