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 .

快速开始

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)

调试模式

使用 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 文本搜索

Shell 相关工具现在会同时返回两层数据:

  • content: 给模型和 CLI 看的可读摘要
  • raw: 给 UI 和外部调用方看的结构化原始载荷

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.streams.stdout / raw.streams.stderr: 当前聚合输出快照
result = await run_tool.execute(
    run_tool.parameters(command="python3 demo.py", cwd="."),
    ctx,
)

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

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

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

这个入口默认启用:

  • 会话持久化记忆
  • 长上下文摘要压缩
  • 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.5.tar.gz (81.7 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.5-py3-none-any.whl (65.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: yier_agents-0.1.5.tar.gz
  • Upload date:
  • Size: 81.7 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.5.tar.gz
Algorithm Hash digest
SHA256 ccc4147766b8df6319ae12fff56ab8f2d9333673ef5c7e2fb32097291262e83d
MD5 c46e46e1e5249638923d51f6a3361ec9
BLAKE2b-256 c8e6a0e367e4dfe87437ae451b5df84737f7ec640e0fcf0dd91f215bc05eefc5

See more details on using hashes here.

Provenance

The following attestation bundles were made for yier_agents-0.1.5.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.5-py3-none-any.whl.

File metadata

  • Download URL: yier_agents-0.1.5-py3-none-any.whl
  • Upload date:
  • Size: 65.7 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.5-py3-none-any.whl
Algorithm Hash digest
SHA256 d4dda3a1fc76aa04089e2c3d9da3883416ef9827ca895fd6615cdf0c13b01c2b
MD5 d60f02ebb4b4d5a744692877c5065deb
BLAKE2b-256 cb1ba56ed7683ab37d1688e08279e507c3b3f18f0c50ff1291128944ef6b3777

See more details on using hashes here.

Provenance

The following attestation bundles were made for yier_agents-0.1.5-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