定制 milpa/example-agent-ready-blog 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

milpa/example-agent-ready-blog

Composer 安装命令:

composer create-project milpa/example-agent-ready-blog

包简介

Runnable example of the Milpa loop — plugin → capability → tool → verification → event → result — as a tiny agent-ready blog.

README 文档

README

Milpa

Milpa Example: Agent-Ready Blog

The Milpa loop, live: plugin → capability → tool → verification → event → result — as a tiny agent-ready blog you can run in two commands.

CI Packagist PHP License

This repo doesn't teach you how to build a blog. It teaches you how to make a mutation agent-ready without losing human control.

Most examples let agents mutate state directly. This one does not. Every mutation enters through a declared tool, passes through a confirmation/verification seam, and only becomes application state through an event. The blog is just the smallest honest thing worth mutating.

flowchart TD
    A["Agent or human"] --> B["ToolRegistry"]
    B --> C["publish_post — mutating"]
    C --> D{"Confirm gate<br/>(registry)"}
    D -->|"confirm_token redeemed"| E["Verification seam<br/>(HumanVerifier)"]
    E -.->|"⚡ verification.requested"| F{"Human decides"}
    F -->|approve| G["⚡ verification.granted"]
    F -->|reject| H["⚡ verification.rejected"]
    G --> I["BlogPlugin event handler"]
    I --> J["⚡ post.published — state changed"]
    H --> K["post stays a draft"]
Loading

Prefer a guided tour? Read docs/walkthrough.md — seven files, in the order that makes the loop click.

Quickstart

composer create-project milpa/example-agent-ready-blog blog
cd blog
php bin/blog.php

You'll be asked to approve or reject a publish request interactively. Here's a real run (a typed at the prompt):

milpa · example-agent-ready-blog — the loop, live
plugin → capability → tool → verification → event → result

✔ Capability graph: StoragePlugin provides PostStorage → BlogPlugin requires it
✔ 3 plugins booted · tools: create_post, human_verify, list_posts, publish_post

→ create_post("Hello Milpa") … draft #1 created (not mutating-gated: no friction)
→ publish_post(#1) … INTERCEPTED by the registry confirm gate → confirm_token 867c9ca7…
  ⚡ verification.requested
→ token redeemed … the tool ran and asked the VERIFICATION seam (status: pending_verification)
? An agent wants to publish post #1 — [a]pprove / [r]eject: a
  ⚡ verification.granted
  ⚡ post.published (id 1)
✔ post #1 is now PUBLISHED — the result arrived via event, handled by BlogPlugin

See it: php -S localhost:8080 -t public   →   http://localhost:8080

Now look at it:

php -S localhost:8080 -t public

Prefer non-interactive? php bin/blog.php --auto-approve and php bin/blog.php --reject drive both paths without a prompt — that's exactly what this repo's own CI runs as its smoke test.

The loop, stage by stage

Every stage below is a real contract from a published package, not an abstraction invented for this example.

Stage What runs Published contract
plugin Kernel::boot() instantiates StoragePlugin, BlogPlugin, AgentToolsPlugin Milpa\Interfaces\Plugin\PluginInterface + #[Milpa\Attributes\PluginMetadata] (milpa/core)
capability CapabilityGraph::check() reads each plugin's #[PluginMetadata], builds the VOs, and fails before boot if a requires has no provides Milpa\ValueObjects\Capability\{CapabilityProvision,CapabilityRequirement} (milpa/core)
tool AgentToolsPlugin::registerTools() scans BlogTools's three #[Tool] methods into the registry Milpa\ToolRuntime\{Attributes\Tool,Attributes\Param,ToolScanner,ToolRegistry} + Milpa\Interfaces\Tooling\ToolProviderInterface (milpa/tool-runtime on milpa/core)
verification publish_post asks HumanVerifier::verify(); a human approves or rejects at the terminal prompt Milpa\Interfaces\Verification\VerifierInterface (milpa/core) + Milpa\ToolRuntime\Verification\HumanVerifier (milpa/tool-runtime)
event Every step above fires through one shared dispatcher — verification.requested / verification.granted / verification.rejected / post.published — printed live by name Milpa\Interfaces\Event\MilpaEventDispatcherInterface (milpa/core), implemented here by App\EventDispatcher
result BlogPlugin's verification.granted handler flips the post to published and dispatches post.published — the result arrives via event, not a return value src/Plugins/BlogPlugin/BlogPlugin.php (this repo)

What an agent sees

The tools are transport-agnostic: what follows is the registry's own getToolSummaries() output — the exact catalog an MCP host (or any other transport) would list. This is real output, not documentation prose:

[
  {
    "name": "publish_post",
    "description": "Publish a draft post (requires human verification)",
    "inputSchema": {
      "type": "object",
      "properties": { "id": { "type": "integer", "description": "Post id" } },
      "required": ["id"]
    }
  },
  {
    "name": "create_post",
    "description": "Create a draft post",
    "inputSchema": {
      "type": "object",
      "properties": {
        "title": { "type": "string", "description": "Post title" },
        "body":  { "type": "string", "description": "Post body" }
      },
      "required": ["title", "body"]
    }
  }
]

(list_posts and human_verify are also listed — run $kernel->registry()->getToolSummaries() to see the full catalog.)

From the agent's side, publishing is a two-call choreography — it never mutates on the first try:

  1. publish_post(id: 1) → the registry intercepts (the tool is mutating) and returns a confirm_token instead of running the tool.
  2. publish_post(id: 1, confirm_token: …) → the tool runs, asks the verification seam, and returns pending_verification with a request_id.
  3. The actual state change arrives by event (verification.grantedpost.published), never as a return value the agent can force.

About human_verify (the name comes from milpa/tool-runtime): it requests or resolves a verification — it does not make anyone human. Its principal argument is an opaque string, and the schema itself says who may resolve is the host's problem: the runtime trusts the host to authenticate principals. In this example the only resolution channel wired is the terminal prompt — an agent calling human_verify with decision: grant would still be an unauthenticated principal unless you build the policy layer that says otherwise. That is the seam doing its job: the framework hands you the gate; guarding it is explicitly your half of the contract.

What implements what

The three published packages define the seams; this repo implements the smallest possible host around them — ~940 lines of application code, of which ~440 implement every framework seam. On purpose, so you can read every line:

Unit Lines Implements Notes
App\Container 146 Milpa\Interfaces\Di\DIContainerInterface (milpa/core) Explicit registerService() plus honest constructor autowiring — exactly what the published docblocks promise, no more.
App\EventDispatcher 82 Milpa\Interfaces\Event\MilpaEventDispatcherInterface (milpa/core) Priority ordering, the documented wildcard grammar (* matches exactly one dot-segment), and handler error isolation.
App\CapabilityGraph 52 — (consumes core's CapabilityProvision/CapabilityRequirement VOs) The "A provides / B requires" edge of the loop, checked before any plugin boots.
App\Http\Router 71 Milpa\Http\Routing\RouterInterface (milpa/http) Exact segments plus single-segment {placeholder}s; never throws, never returns null — RouteResult carries the outcome.
App\Kernel 89 — (orchestrates the four above) Container → dispatcher → capability check → ordered plugin boot → tool registry wiring. A miniature of a real Milpa host.

You can implement the seams in an afternoon — this repo is the proof.

What this example is NOT

  • Not production. Storage is a plain JSON file (var/posts.json), there's no auth, and App\Container has no compiled/cached resolution — it's a from-scratch DI container that happens to satisfy the published interface.
  • Not a template to fork for a real blog. It's a template for understanding the loop.
  • Mutations enter via tools, not HTTP — that's the point. The web view (php -S localhost:8080 -t public) is read-only by design: publishing a post always goes through create_postpublish_post → human verification, whether the caller is a human running bin/blog.php or an agent calling the same tools — e.g. over MCP; not wired in this example, the tools are transport-agnostic.

The family

This example consumes three published Milpa packages, unmodified, from Packagist:

Contributing

Contributions are welcome — see CONTRIBUTING.md. Please report security issues via SECURITY.md, and note that this project follows a Code of Conduct.

License

Apache-2.0 © TeamX Agency.

Milpa is designed, built, and maintained by TeamX Agency.

统计信息

  • 总下载量: 10
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: Apache-2.0
  • 更新时间: 2026-07-07

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固