Metadata-Version: 2.4
Name: yier-agents
Version: 0.1.0
Summary: Add your description here
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Requires-Dist: environs>=14.6.0
Requires-Dist: httpx>=0.28.1
Requires-Dist: mcp>=1.0.0
Requires-Dist: pydantic>=2.12.5
Requires-Dist: questionary>=2.1.1
Requires-Dist: rich>=14.3.3

# 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 两种传输

## 安装

```bash
# 使用 uv
uv pip install -e .

# 或使用 pip
pip install -e .
```

## 快速开始

```python
import asyncio
from pydantic import BaseModel, Field
from doc_agents.src 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 执行的详细过程,方便调试:

```python
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 示例](examples/verbose_example.py)。

## 架构

```
Agent (执行循环)
  ├─> LLM (API 调用层)
  ├─> ToolMiddleware (工具管理和验证)
  │     ├─> Tool (工具定义)
  │     └─> Context (执行上下文)
  ├─> MCPClient (MCP server 连接)
  │     ├─> stdio transport (子进程)
  │     └─> SSE transport (HTTP)
  └─> Message (消息管理)
```

## MCP 支持

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

```python
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 共享：**

```python
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 示例](examples/mcp_primevue_example.py)。

## SubAgent 示例

```python
# 创建专门的子 agent
search_agent = Agent(
    llm=llm,
    tools=[grep_tool, read_tool],
    system_prompt="你是代码搜索专家"
)

# 包装为工具
from doc_agents.src 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 自动管理多步骤任务的进度。

```python
from doc_agents.src 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 工具文档](docs/TODO_TOOLS.md) 和 [示例](examples/todo_example.py)。

## 开发

```bash
# 运行测试
pytest tests/ -v

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

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

## 设计文档

详细设计请参考:
- [设计文档](docs/plans/2026-02-08-python-agent-design.md)
- [实现计划](docs/plans/2026-02-08-python-agent-implementation.md)

## License

MIT
